首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用GKComponent制作可重用的盾牌

使用GKComponent制作可重用的盾牌
EN

Stack Overflow用户
提问于 2017-09-28 22:58:13
回答 1查看 131关注 0票数 0

我正在试着做一个简单的游戏:在屏幕底部的太空船拍摄小行星从屏幕顶部坠落。

我正在学习ECS和GameplayKit,并且一直在尝试把防护罩变成一个组件。我在很大程度上依赖于苹果的DemoBots示例应用程序,并从示例代码中删除了PhysicsComponentColliderTypeContactNotifiableType

盾牌需要渲染与其关联的资产(一个用于全盾,另一个用于半盾),与船不同的物理实体,因为它的半径明显大于船的半径,并跟踪其状态。为此,我写道:

代码语言:javascript
复制
final class ShieldComponent: GKComponent {
    enum ShieldLevel: Int {
        case full = 0, half, none
    }

    var currentShieldLevel: ShieldLevel = .full {
        didSet {
            switch currentShieldLevel {
            case .full:
                node.isHidden = false
                node.texture = SKTexture(image: #imageLiteral(resourceName: "shield"))
            case .half:
                node.isHidden = false
                node.texture = SKTexture(image: #imageLiteral(resourceName: "damagedShield"))
            case .none:
                node.isHidden = true
            }
        }
    }

    let node: SKSpriteNode

    override init() {
        node = SKSpriteNode(imageNamed: "shield")
        super.init()

        node.physicsBody = {
            let physicsBody = SKPhysicsBody(circleOfRadius: node.frame.size.width / 2)
            physicsBody.pinned = true
            physicsBody.allowsRotation = false
            physicsBody.affectedByGravity = false

            ColliderType.definedCollisions[.shield] = [
                .obstacle,
                .powerUp
            ]

            physicsBody.categoryBitMask = ColliderType.shield.rawValue
            physicsBody.contactTestBitMask = ColliderType.obstacle.rawValue
            physicsBody.collisionBitMask = ColliderType.obstacle.rawValue
            return physicsBody
        }()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func loseShields() {
        if let newShieldLevel = ShieldLevel(rawValue: self.currentShieldLevel.rawValue + 1) {
            self.currentShieldLevel = newShieldLevel
        }
    }

    func restoreShields() {
        self.currentShieldLevel = .full
    }
}

在我的Ship初始化器中,我这样做:

代码语言:javascript
复制
    let shieldComponent = ShieldComponent()
    renderComponent.node.addChild(shieldComponent.node)

如果我可以重用RenderComponent和来自DemoBots的PhysicsComponent,那就太好了,我的船和小行星GKEntity子类已经有了,但是组件不能有组件。我将ship设置为ContactNotifiableType,但因为ship节点实际上并不属于ship实体。

我知道我来这里显然是错的,我不知道如何纠正这一点。我希望得到一个如何制作一个屏蔽组件的例子。

EN

回答 1

Stack Overflow用户

发布于 2017-10-03 01:12:00

您必须了解组件只能处理一种行为。因此,git去掉了init()函数中的物理代码,转而构建一个类似于DemoBots中的物理组件。

根据自己的喜好调整渲染组件。使用DemoBots代码的问题是它并不完全适合。所以让我们调整一下吧

代码语言:javascript
复制
class RenderComponent: GKComponent {
// MARK: Properties

// The `RenderComponent` vends a node allowing an entity to be rendered in a scene.
@objc let node = SKNode()

var sprite = SKSpriteNode

// init
init(imageNamed name: String) {
    self.sprite = SKSpriteNode(imageNamed: name)
}
// MARK: GKComponent

override func didAddToEntity() {
    node.entity = entity
}

override func willRemoveFromEntity() {
    node.entity = nil
}

}

代码语言:javascript
复制
final class ShieldComponent: GKComponent {


    var node : SKSpriteNode
    //add reference to ship entity
    weak var ship: Ship?


    enum ShieldLevel: Int {
        case full = 0, half, none
    }

    var currentShieldLevel: ShieldLevel = .full {
        didSet {
            switch currentShieldLevel {
            case .full:
                node.isHidden = false
                node.texture = SKTexture(image: #imageLiteral(resourceName: "shield"))
            case .half:
                node.isHidden = false
                node.texture = SKTexture(image: #imageLiteral(resourceName: "damagedShield"))
            case .none:
                node.isHidden = true
            }
        }
    }

    // Grab the visual component from the entity. Unwrap it with a Guard. If the Entity doesnt have the component you get an error.
    var visualComponentRef : RenderComponent {
        guard let renderComponent = ship?.component(ofType: RenderComponent.self) else {
            fatalError("entity must have a render component")
        }
    }

    override init(shipEntity ship: Ship) {
    let visualComponent = RenderComponent(imageNamed: "imageName")
    node = visualComponent.sprite
        self.ship = ship
    super.init()

       // get rid of this. Use a Physics Component for this, Kep your components to one behaviour only. Make them as dumb as possible.
//    node.physicsBody = {
//        let physicsBody = SKPhysicsBody(circleOfRadius: node.frame.size.width / 2)
//        physicsBody.pinned = true
//        physicsBody.allowsRotation = false
//        physicsBody.affectedByGravity = false
//
//        ColliderType.definedCollisions[.shield] = [
//            .obstacle,
//            .powerUp
//        ]
//
//        physicsBody.categoryBitMask = ColliderType.shield.rawValue
//        physicsBody.contactTestBitMask = ColliderType.obstacle.rawValue
//        physicsBody.collisionBitMask = ColliderType.obstacle.rawValue
//        return physicsBody
//    }()
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

func loseShields() {
    if let newShieldLevel = ShieldLevel(rawValue: self.currentShieldLevel.rawValue + 1) {
        self.currentShieldLevel = newShieldLevel
    }
}

func restoreShields() {
    self.currentShieldLevel = .full
    }

};

确保查看我是如何更改组件与实体的交互的。您可以直接创建对Ship实体的引用对象。或者,您可以检查ShieldComponent是否具有带有entity?属性的entity。(当心。这是一个可选参数,因此请将其解包。

一旦有了实体引用,就可以在其中搜索其他组件并检索using component(ofType:_)属性。例如ship?.component(ofType: RenderComponent.self)

除此之外,我认为你有一个很好的屏蔽组件

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46472328

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档