我正在试着做一个简单的游戏:在屏幕底部的太空船拍摄小行星从屏幕顶部坠落。
我正在学习ECS和GameplayKit,并且一直在尝试把防护罩变成一个组件。我在很大程度上依赖于苹果的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和来自DemoBots的PhysicsComponent,那就太好了,我的船和小行星GKEntity子类已经有了,但是组件不能有组件。我将ship设置为ContactNotifiableType,但因为ship节点实际上并不属于ship实体。
我知道我来这里显然是错的,我不知道如何纠正这一点。我希望得到一个如何制作一个屏蔽组件的例子。
发布于 2017-10-03 01:12:00
您必须了解组件只能处理一种行为。因此,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。(当心。这是一个可选参数,因此请将其解包。
一旦有了实体引用,就可以在其中搜索其他组件并检索using component(ofType:_)属性。例如ship?.component(ofType: RenderComponent.self)
除此之外,我认为你有一个很好的屏蔽组件
https://stackoverflow.com/questions/46472328
复制相似问题