我使用swift开发了一款游戏,在我将系统更新到适用于iPhone的IOS 11.3之前,它运行得非常好。在我的游戏中,当子弹与敌人接触时,两个SKSpriteNode将立即被移除,而变量" gameScore"将按预期添加1。但现在,每当子弹与敌人接触时,游戏分数"将被大于1的数字添加(取决于SKSpriteNode和#34的速度;
因此,我猜想删除SKSpriteNode后仍然会调用didBegin函数。似乎didBegin功能有一段时间延迟。每个人都遇到同样的问题吗?
func didBegin(_ contact: SKPhysicsContact) {
var body1 = SKPhysicsBody()
var body2 = SKPhysicsBody()
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
body1 = contact.bodyA
body2 = contact.bodyB
} else {
body1 = contact.bodyB
body2 = contact.bodyA
}
if body1.categoryBitMask == physicscategories.Player && body2.categoryBitMask == physicscategories.Bullet {
gameScore += 1
gameLabel1.text = "SCORE: \(gameScore)"
body1.node?.removeFromParent()
body2.node?.removeFromParent()
}
}
答案 0 :(得分:1)
我无法找到我的答案,我认为这是在文件中,所以如果有人发现副本,请随时将其标记为。
首先,让我们尝试了解联系人的工作方式。
在物理阶段,为您的节点创建一个池,列出它接触的所有接触点。该池将保留所有节点。
E.G。
let pool : [SKPhysicsContact] = [{node1.side1,node2.side1},{node1.side1,node2.side2}]
然后我们遍历所有联系点并调用didBegin
函数。
for contact in pool
{
scene.didBegin(contact)
}
现在我们输入您提供的代码:
func didBegin(_ contact: SKPhysicsContact) {
var body1 = SKPhysicsBody()
var body2 = SKPhysicsBody()
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
body1 = contact.bodyA
body2 = contact.bodyB
} else {
body1 = contact.bodyB
body2 = contact.bodyA
}
if body1.categoryBitMask == physicscategories.Player && body2.categoryBitMask == physicscategories.Bullet {
gameScore += 1
gameLabel1.text = "SCORE: \(gameScore)"
body1.node?.removeFromParent()
body2.node?.removeFromParent()
}
}
如果我要将代码内联到for循环中,它最终会看起来像这样:
for contact in pool
{
var body1 = SKPhysicsBody()
var body2 = SKPhysicsBody()
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
body1 = contact.bodyA
body2 = contact.bodyB
} else {
body1 = contact.bodyB
body2 = contact.bodyA
}
if body1.categoryBitMask == physicscategories.Player && body2.categoryBitMask == physicscategories.Bullet {
gameScore += 1
gameLabel1.text = "SCORE: \(gameScore)"
body1.node?.removeFromParent()
body2.node?.removeFromParent()
}
}
现在你可以看到,调用body1.node?.removeFromParent()
不会做任何事情来阻止for循环发生两次。所有这一切都是将parent
设置为nil,但是联系人和节点都仍然存在,使下一个循环成功。
所以我们需要做的是如何防止循环再次处理。
现在有几种方法可以做到这一点:
1)检查父母是否为零:
func didBegin(_ contact: SKPhysicsContact) {
guard let _ = contact.bodyA.node.parent else {return}
这很有效,但是如果bodyA.node的某个地方变为零,那么我们的代码会崩溃。
2)检查节点或父节点是否为零:
func didBegin(_ contact: SKPhysicsContact) {
guard let _ = contact.bodyA.node?.parent else {return}
现在我们知道我们的代码是安全的。哦不,我们有2个不同的节点同时发生碰撞,节点被从场景中移除,我如何处理这两个节点,我只是删除了节点?
3)将节点移动到临时位置,检查节点是否在删除节点中,并在最终更新时进行清理:
let removalNode = SKNode()
func didBegin(_ contact: SKPhysicsContact) {
var body1 : SKPhysicsBody!
var body2 : SKPhysicsBody!
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
body1 = contact.bodyA
body2 = contact.bodyB
} else {
body1 = contact.bodyB
body2 = contact.bodyA
}
if body1.categoryBitMask == physicscategories.Player &&
body2.categoryBitMask == physicscategories.Bullet &&
body1.node?.parent != removalNode{
gameScore += 1
gameLabel1.text = "SCORE: \(gameScore)"
removalNode.addChild(body1)
removalNode.addChild(body2)
}
}
func didFinishUpdate(){
removalNode.removeAllChildren()
}
现在让我们看看我们在游戏中放置了一个令牌,而我们的游戏规则是如果玩家同时击中令牌和子弹,则玩家得分仍然会上升10.使用当前设置,我们可以现在这样做:
let removalNode = SKNode()
func didBegin(_ contact: SKPhysicsContact) {
var body1 : SKPhysicsBody!
var body2 : SKPhysicsBody!
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
body1 = contact.bodyA
body2 = contact.bodyB
} else {
body1 = contact.bodyB
body2 = contact.bodyA
}
if body1.categoryBitMask == physicscategories.Player &&
body2.categoryBitMask == physicscategories.Bullet &&
body1.node?.parent != removalNode{
gameScore += 1
gameLabel1.text = "SCORE: \(gameScore)"
removalNode.addChild(body1)
removalNode.addChild(body2)
}
if body1.categoryBitMask == physicscategories.Player &&
body2.categoryBitMask == physicscategories.Token &&
body2.node?.parent != removalNode{
gameScore += 10
gameLabel1.text = "SCORE: \(gameScore)"
removalNode.addChild(body2)
}
}
func didFinishUpdate(){
removalNode.removeAllChildren()
}
现在让我们来看看我们首先击中子弹,然后是令牌的示例。
contact-&gt;玩家击中子弹
确实开始了:
球员还活着
玩家击中bullet = true
比赛得分增加了
球员已经死了
子弹已经死了
玩家命中令牌=假
结束开始
contact-&gt;玩家命中令牌
确实开始了:
球员已经死了
玩家击中bullet = false
玩家命中令牌=真
gamescore增加10
结束开始
游戏结束了,游戏核心增加了11,这是我们的游戏规则。
但是,如果游戏规则是如果玩家击中子弹并且同时击中了令牌,那么该分数是否会被添加呢?你可能会认为&#34;让我们检查玩家是否在removalNode
中不添加分数。那么你就错了,因为如果管道碰巧是令牌然后是子弹。
contact-&gt;玩家点击令牌
确实开始了:
球员还活着
玩家击中bullet = false
玩家命中令牌=真
gamescore增加10
结束开始
联系 - >玩家击中子弹
确实开始了:
球员还活着
玩家击中bullet = true
比赛得分增加了
球员已经死了
子弹已经死了
玩家命中令牌=假
结束开始
分数现在是11,当它应该是1,违反了我们的游戏规则。
我们如何解决这个问题?
我们将评分移至didFinishUpdate
方法,我们使用userData
标记代币:
let removalNode = SKNode()
func didBegin(_ contact: SKPhysicsContact) {
var body1 : SKPhysicsBody!
var body2 : SKPhysicsBody!
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
body1 = contact.bodyA
body2 = contact.bodyB
} else {
body1 = contact.bodyB
body2 = contact.bodyA
}
if body1.categoryBitMask == physicscategories.Player &&
body2.categoryBitMask == physicscategories.Bullet &&
body1.node?.parent != removalNode{
removalNode.addChild(body1)
removalNode.addChild(body2)
}
if body1.categoryBitMask == physicscategories.Player &&
body2.categoryBitMask == physicscategories.Token &&
body2.node.parent != removalNode
//Make sure userData is allocated
if body1.userData = nil {body1.userData = [String:NSObject]()}
// If body.userData[tokenScore] is nil, default to 0 then add 10
body1.userData[tokenScore] = (body1.userData[tokenScore] ?? 0) + 10
removalNode.addChild(body2)
}
}
func didFinishUpdate(){
if player.parent = removalNode
{
gameScore += 1
}
else
{
//if userdata exists and token score has a value, then add it, otherwise add 0
gameScore += player.usedData?["tokenScore"] ?? 0
}
gameLabel1.text = "SCORE: \(gameScore)"
removalNode.removeAllChildren()
}
现在我们的游戏规则表明只有当玩家不在removeNode中时才会添加令牌。
或者,如果您不想使用userData
,您可以随时查看&#34; removalNode&#34;中的令牌数量。孩子们,并相应地添加分数。