【问题标题】:How best to code an Undo function in SpriteKit?如何最好地在 SpriteKit 中编写 Undo 函数?
【发布时间】:2018-08-31 23:05:16
【问题描述】:

我正在创建一个基于 sprite-kit 的游戏,允许玩家建造建筑物。我想创建一个撤消按钮,如果玩家放置一个块并使整个建筑物分崩离析,它会后退一步。

我正在考虑一种方法,在他们放置某些东西之前保存 sprite-kit 场景,如果他们点击“撤消”,它将用之前保存的场景替换场景。

这是最好的使用方法,也是最小化内存使用的最佳方法吗?是否有可以使用的恶意套件预置的东西?

【问题讨论】:

    标签: swift sprite-kit


    【解决方案1】:

    sprite-kit 中没有预置特定的 UNDO 技术。我对节省内存和性能的建议是构建用户操作堆栈。你可以做这样的事情:

    public struct Stack<T> {
        fileprivate var array = [T]()
    
        public var count: Int {
            return array.count
        }
    
        public var isEmpty: Bool {
            return array.isEmpty
        }
    
        public mutating func push(_ element: T) {
            array.append(element)
        }
    
        public mutating func pop() -> T? {
            return array.popLast()
        }
    
        public var top: T? {
            return array.last
        }
    }
    enum ActionType:String {
        case remove, add, moveTo, scale, rotate // add all the available user actions
    }
    struct UserAction {
        var type: ActionType
        var objectName: String
    }
    

    因此,首先您可以创建 LIFO(后进先出 顺序),其中您最后推送的元素是第一个在下一次弹出时出现的元素。然后,您可以创建在此示例中称为 UserAction 的自定义 LIFO 元素,您可以在其中保存操作类型(删除、添加、旋转..)和所涉及对象的 name 属性(例如例如,如果您有一个名为box1SKSpriteNode,您还应该实现box1.name = "box1",以识别您所涉及的节点。

    用法:

    var undoArray = Stack(array:[])
    let currentAction = UserAction.init(type: .remove, objectName: "box1")
    print(" - User has \(currentAction.type.rawValue) the \(currentAction.objectName)")
    undoArray.push(currentAction)
    

    显然,这只是UNDO数组的实现。您的下一步应该是构建每一个操作,例如删除、添加、旋转,以复制用户操作。

    换句话说,当用户按下 UNDO 按钮时,你应该从堆栈中“弹出”一个元素:

    // ...
    
    if let lastEl = undoArray.pop() {
        let lastUserAction = lastEl as! UserAction
        switch lastUserAction.type {
        case .remove: print("replicate remove action")
        case .add: print("replicate add action")
        case .moveTo : print("replicate moveTo action")
        // ... add the other user actions
        default:break
        }
    }
    

    【讨论】:

    • 我感觉他说话的方式是在使用物理,所以它不仅仅是保存动作。我的猜测是它就像一个积木塔,当你从积木塔中移除一个块时,塔可能会倒下。他想撤消坠落,以便叠叠乐塔再次直立。
    • 哦,当然,我明白你的意思,我同意你的看法。如果你在塔上的东西涉及到 phisic 法则,可能如果你移除一个块,你的塔挂在一侧并且那个物体掉到地上:此时 UNDO 动作是不够的,但这只是一个例子,你也可以构建而不是 lastUserAction 之类的 lastObjectState 在用户开始新操作之前存储名称、位置和物理参数,我的意思是有关当前状态的更多信息。仅当有人希望避免像您正确显示的那样重复场景时,该解决方案才可以使用
    • 如何在场景中创建一个本地 SKNode 数组,并且每次用户执行某项操作时,场景中的所有节点都会作为数组添加到该数组中。当你点击 undo 时,它会删除数组中最后添加的项,并将所有 self.children 替换为节点数组中的最后一个数组?
    • 是的,这可能是一个解决方案,取决于您的游戏。如果这些操作必须在游戏中多次发生,通常您应该避免“保存”大部分数据。最好将字符串或“指针”存储到您的数据,而不是完整的对象(假设您有一个具有不同纹理的节点,atlas,它可能会占用大量内存)。您的情况是什么?
    【解决方案2】:

    我只会复制您的 GameScene,然后当您需要撤消时,只需重新加载副本即可。

    let copy = scene.copy() as? GameScene
    
    
    view.presentScene(copy)
    

    除非您开始发现问题,否则我不会过于关注内存消耗。我们有时倾向于做的事情是过度优化到没有投资回报 (ROI) 的程度。只要您没有对保留或创建僵尸的对象进行管理不善,使用此方法就不会出现太多问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-29
      • 1970-01-01
      • 2019-02-17
      • 1970-01-01
      • 1970-01-01
      • 2011-09-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多