我正在尝试制作一个简单的游戏:屏幕底部的太空飞船射击小行星从屏幕顶部“掉落”。
我正在学习ECS和GameplayKit,并一直试图将盾牌变成一个组件。我非常依赖Apple的DemoBots示例应用程序,并从示例代码中解除了PhysicsComponent
,ColliderType
和ContactNotifiableType
。
盾牌需要渲染与之相关的资产(一个用于完全盾牌,一个用于半盾牌),一个与船舶不同的物理体,因为它的半径明显大于船舶,并跟踪它的状态。为此,我写道:
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
初始化程序中,我这样做:
let shieldComponent = ShieldComponent()
renderComponent.node.addChild(shieldComponent.node)
如果我可以重用RenderComponent
,并且我已经使用了我的船和小行星PhysicsComponent
子类,那么来自DemoBots的GKEntity
会很棒,但组件不能有组件。我已将ShieldComponent
设为ContactNotifiableType
,但因为屏蔽节点实际上并不属于船舶实体。
我知道我明显错了,我不知道如何纠正这个问题。我希望得到一个如何制作屏蔽组件的例子。
答案 0 :(得分:1)
您必须了解组件只能处理一种行为。所以git摆脱了init()
函数中的物理代码,而是构建了一个类似于DemoBots中的物理组件。
根据自己的喜好调整渲染组件。使用DemoBots代码的问题在于它并不完全适合。所以我们来调整一下
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
}
}
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?
属性。 (小心。它是可选的,所以打开它。
获得实体引用后,您可以搜索其他组件并检索使用component(ofType:_)
属性。
例如ship?.component(ofType: RenderComponent.self)
除此之外,我认为你有一个不错的盾牌组件