Как отслеживать изменения и сохранять предыдущие значения свойства унаследованного объекта?

Настройка:

Я создаю подкласс SKSpriteNode() для создания пользовательского подкласса с именем SubNode().

Класс SKSPriteNode() имеет свойство physicsBody, в котором хранится объект SKPhysicsBody(). SKPhysicsBody имеет свойство под названием "скорость", которое является CGVector()

Цель:

В моем подклассе я хотел бы иметь переменную, которая всегда сохраняет предыдущую CGVector(). То есть, как только я позвоню, например, subNode.physicsBody?.velocity = CGVector(dx: 10, dy: 10). subNode.previousVelocity следует установить на предыдущее значение subNode.physicsBody?.velocity.

Проблема

Если бы velocity было просто унаследованным свойством суперкласса, я мог бы просто override свойство и прикрепить didSet наблюдателя. Но поскольку velocity является свойством SKPhysicsBody(), а объект SKPhysicsBody() не изменяется при изменении одного из его свойств, didSet не срабатывает, и я не могу прослушать его изменения.

Пожалуйста, попросите дополнительные разъяснения, если мое объяснение неясно.

Мой подкласс:

class SubNode: SKSpriteNode {
    
    var previousVelocity: CGVector = .zero

    // This doesn't get triggered because the physiceBody object doesn't change when one of its properties changes.
    override var physicsBody: SKPhysicsBody? {
        didSet {
            previousVelocity = oldValue?.velocity
        }
    }
    
    // If velocity would be a property of the superclass, this would trigger.
    override var velocity: CGVector {
        didSet {
            previousVelocity = oldValue
        }
    }
}

Создание подузла объекта из подкласса SubNode(), установка физического тела, а затем скорости

let subNode = SubNode()
subNode.physicsBody = SKPhysicsBody()
subNode.physicsBody?.velocity = CGVector(dx: 10, dy: 10)
subNode.physicsBody?.velocity = CGVector(dx: 0, dy: 0)
//should be true: subnode.previousVelocity == CGVector(dx: 10, dy: 10)

person Marco Boerner    schedule 22.01.2021    source источник


Ответы (1)


Это только частичный ответ! Всего лишь попытка найти решение проблемы.

Я пытался как-то найти обходной путь. Я заметил, что как только свойство объекта изменяется, геттер объекта срабатывает раньше. Я создал следующий файл игровой площадки, и, похоже, он работает. Как только я меняю текст объекта, предыдущее имя сохраняется в другой переменной. Но, судя по моим испытаниям, это работает. Я не уверен, что моя логика верна или я что-то упускаю, возможно, это слишком сложно для достижения чего-то, что должно быть проще.

Ниже я добавил код для своего подкласса и из того, что я смог проверить, он действительно работает. Однако я был бы признателен за некоторые отзывы о том, где могут быть подводные камни в этом решении.

import UIKit

class ObjectClass {
    var name: String = "Initial Name"
}

class SuperClass {
    var object: ObjectClass?
}

class SubClass: SuperClass {
    
    var _oldObjectName: [String?] = [nil]
    
    var oldObjectName: String? {
        _ = object?.name
        return _oldObjectName.first ?? nil
    }
    
    override var object: ObjectClass? {
        get {
            // Is also being called before the object has been written to.
            if !_oldObjectName.contains(super.object?.name) {
                _oldObjectName.append(super.object?.name)
                _oldObjectName = _oldObjectName.suffix(2)
            }
            return super.object
        }
        set {
            super.object = newValue
        }
    }
}

let subClass = SubClass()

subClass.object = ObjectClass()

Используя приведенное выше решение с моим подклассом:

class SubNode: SKSpriteNode {
    
    private var _previousVelocity: [CGVector?] = []
    var previousVelocity: CGVector {
        _ = physicsBody?.velocity
        return (_previousVelocity.first ?? CGVector.zero)!
    }
    
    override var physicsBody: SKPhysicsBody? {
        get {
            if !_previousVelocity.contains(super.physicsBody?.velocity) {
                _previousVelocity.append(super.physicsBody?.velocity)
                _previousVelocity = _previousVelocity.suffix(2)
            }
            return super.physicsBody
        }
        set {
            super.physicsBody = newValue
        }
    }
}

Из моего первоначального тестирования это пока работает:

let subNode = SubNode()
subNode.physicsBody = SKPhysicsBody()
subNode.physicsBody?.velocity = CGVector(dx: 10, dy: 10)
subNode.physicsBody?.velocity = CGVector(dx: 0, dy: 0)
//true: subnode.previousVelocity == CGVector(dx: 10, dy: 10)

Обновление:

Одна проблема с этим решением, которую я обнаружил, заключается в том, что когда объект из ObjectClass() сначала создается как собственная переменная, а затем изменяется напрямую через переменную, геттер подкласса не будет запущен до тех пор, пока к нему не будет получен доступ через подкласс. В моем случае это не проблема, но определенно не идеально.

let subClass = SubClass()

let object = ObjectClass()

subClass.object = object

// This won't trigger the getter obviously since the subclass is not accessed.
object.name = "New Name"
object.name = "Another Name"
person Marco Boerner    schedule 24.01.2021