如何在 Sprite Kit 中为敌人设置 ID 系统?
How to set up an ID system for enemies in Sprite Kit?
我正在为 iOS 制作一款 2d Space 射击类游戏,但在为游戏元素(尤其是敌人)创建 ID 系统时遇到了问题。
我使用的教程只展示了如何让敌人一击即爆。它使用一个名为 didBegin(_ contact: SKPhysicsContact) 的函数。
根据我的理解,此函数评估每个接触实例,为两个主体分配两个不同的角色,然后决定如何处理每个主体。我假设让一个敌人受到三击,将使用一个包含他们剩余生命值的变量。然后,每接触一次,变量就会减一,直到为零,敌人就被移除了。
但是,这里面有一个很深的问题。 SincedidBegin(_ contact: SKPhysicsContact) 仅评估一个接触实例,它不知道其他先前的接触实例。本质上,当一个敌人被击中时,无法知道这个敌人是之前被击中过、根本没有被击中过还是被击中过多次。如果只有一个敌人,那么我可以使用一个变量来跟踪它的健康状况。然而,情况并非如此,因为屏幕上同时出现多个敌人,并且每次接触时,都无法知道上一次击中的是这个敌人还是另一个敌人。
如果屏幕上只有一个敌人,这很简单,因为每次接触都必须是那个敌人。但是如果屏幕上有多个实体,则无法知道接触适用于哪个敌人。
我知道我必须设置一个 ID 系统,以便在检测到接触后调用,我只是不知道该怎么做。
我试过建立一个字典和一个class结构(我知道你可以这样解决,但我一直没弄明白)。
可能最简单的方法是继承 SKNode
并将其用于你的敌人。您可以将 health
属性 添加到您的 EnemyNode
并将其初始化为合适的值。
然后在didBegin(_ contact: SKPhysicsContact)
你可以找到敌人,减少它的生命值并在适当的时候摧毁它。
func didBegin(_ contact: SKPhysicsContact) {
var enemy: EnemyNode? = nil
if contact.bodyA.node is EnemyNode {
enemy = contact.bodyA.node
} else if contact.bodyB.node is EnemyNode {
enemy = contact.bodyB.node
}
if let enemy = enemy {
enemy.health -= 1
if enemy.health == 0 {
//TODO: explode enemy
}
}
}
您不必在 didBegin
中填写您的联系代码,在 didFinisheUpdate
中没有什么能阻止您这样做:
这是您可以执行的操作的示例:
var contactedNodes = [SKNode]()
func didBegin(_ contact: SKPhysicsContact) {
func storeContact(nodeA:SKNode,nodeB:SKNode){
nodeA.userData = nodeA.userData ?? [:]()
nodeA.userData["nodesHit"] += [nodeB]
contactedNodes.append(nodeA)
}
storeContact(contact.bodyA.node,contact.bodyB.node)
storeContact(contact.bodyB.node,contact.bodyA.node)
}
func didFinishUpdate(){
for node in contactedNodes{
guard let userData = node.userData, let nodesHit = userData["nodesHit"] else {continue}
//do any logic here with nodes hit
}
}
就ID而言,您也可以使用userData来分配一个userID,而不必担心额外的class
我正在为 iOS 制作一款 2d Space 射击类游戏,但在为游戏元素(尤其是敌人)创建 ID 系统时遇到了问题。
我使用的教程只展示了如何让敌人一击即爆。它使用一个名为 didBegin(_ contact: SKPhysicsContact) 的函数。
根据我的理解,此函数评估每个接触实例,为两个主体分配两个不同的角色,然后决定如何处理每个主体。我假设让一个敌人受到三击,将使用一个包含他们剩余生命值的变量。然后,每接触一次,变量就会减一,直到为零,敌人就被移除了。
但是,这里面有一个很深的问题。 SincedidBegin(_ contact: SKPhysicsContact) 仅评估一个接触实例,它不知道其他先前的接触实例。本质上,当一个敌人被击中时,无法知道这个敌人是之前被击中过、根本没有被击中过还是被击中过多次。如果只有一个敌人,那么我可以使用一个变量来跟踪它的健康状况。然而,情况并非如此,因为屏幕上同时出现多个敌人,并且每次接触时,都无法知道上一次击中的是这个敌人还是另一个敌人。
如果屏幕上只有一个敌人,这很简单,因为每次接触都必须是那个敌人。但是如果屏幕上有多个实体,则无法知道接触适用于哪个敌人。
我知道我必须设置一个 ID 系统,以便在检测到接触后调用,我只是不知道该怎么做。
我试过建立一个字典和一个class结构(我知道你可以这样解决,但我一直没弄明白)。
可能最简单的方法是继承 SKNode
并将其用于你的敌人。您可以将 health
属性 添加到您的 EnemyNode
并将其初始化为合适的值。
然后在didBegin(_ contact: SKPhysicsContact)
你可以找到敌人,减少它的生命值并在适当的时候摧毁它。
func didBegin(_ contact: SKPhysicsContact) {
var enemy: EnemyNode? = nil
if contact.bodyA.node is EnemyNode {
enemy = contact.bodyA.node
} else if contact.bodyB.node is EnemyNode {
enemy = contact.bodyB.node
}
if let enemy = enemy {
enemy.health -= 1
if enemy.health == 0 {
//TODO: explode enemy
}
}
}
您不必在 didBegin
中填写您的联系代码,在 didFinisheUpdate
中没有什么能阻止您这样做:
这是您可以执行的操作的示例:
var contactedNodes = [SKNode]()
func didBegin(_ contact: SKPhysicsContact) {
func storeContact(nodeA:SKNode,nodeB:SKNode){
nodeA.userData = nodeA.userData ?? [:]()
nodeA.userData["nodesHit"] += [nodeB]
contactedNodes.append(nodeA)
}
storeContact(contact.bodyA.node,contact.bodyB.node)
storeContact(contact.bodyB.node,contact.bodyA.node)
}
func didFinishUpdate(){
for node in contactedNodes{
guard let userData = node.userData, let nodesHit = userData["nodesHit"] else {continue}
//do any logic here with nodes hit
}
}
就ID而言,您也可以使用userData来分配一个userID,而不必担心额外的class