使用固定的 SKPhysicsJoint 将作为孩子的平台检测命中框附加到玩家精灵会更改玩家的碰撞和检测位掩码

Posted

技术标签:

【中文标题】使用固定的 SKPhysicsJoint 将作为孩子的平台检测命中框附加到玩家精灵会更改玩家的碰撞和检测位掩码【英文标题】:Using a fixed SKPhysicsJoint to attach a platform detection hitbox as a child to a player sprite changes player's collision and detection bit masks 【发布时间】:2017-12-22 23:43:25 【问题描述】:

我正在尝试在我的播放器底部添加一个碰撞箱,以仅检测平台。经过大量的试验和错误后,我发现将一个额外的物理体作为一个孩子连接到另一个物理体的固定位置,我需要使用 SKPhysicsJoint。我这样做了,它似乎产生了无法预料的后果。当我以这种方式将 hitbox 添加到玩家时,它会稍微改变玩家的碰撞/接触位掩码。

在我的游戏中,你点击屏幕跳转,它可以无缝运行,没有延迟:

Player jumping

但是当我用关节添加这个 hitbox 时,我必须用手指按住屏幕,最终玩家会跳跃,但大多数时候输入被忽略。几乎就好像在读取输入时存在某种严重的延迟一样。在这个 gif 中,我不断地点击屏幕试图让我的玩家跳跃,并且很多输入被忽略或阻止:

Player hitbox not behaving


这个函数在我的 gamescene.swift 中设置播放器,并通过固定的 SKPhysicsJoint 将 hitbox 添加到播放器到场景中:

func playerSetup()
    //setups player
    addChild(player)
    player.addChild(playerPlatformHitbox)
    let myCGPoint = player.position // sets joint position

    let myJoint = SKPhysicsJointFixed.joint(withBodyA: playerPlatformHitbox.physicsBody!, bodyB: player.physicsBody!, anchor: myCGPoint)

    scene?.physicsWorld.add(myJoint)

这是我为仅检测玩家平台而创建的 hitbox 类。你会看到它应该忽略所有期望来自位掩码的平台。出于某种原因,它不允许玩家接受与它的位掩码的接触,即使这个 hitbox 和玩家没有接触:

import Foundation
import SpriteKit

class PlayerPlatformHitbox: SKSpriteNode

init() 

    super.init(texture: nil, color: SKColor.blue, size: CGSize(width: playerTexture.size().width, height: 10))


  physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.size.width, height: self.size.height))

    position = CGPoint(x:0, y: (-playerTexture.size().height * 0.6))
    physicsBody?.categoryBitMask = CollisionTypes.jumpHitBox.rawValue
    physicsBody?.contactTestBitMask = CollisionTypes.platform.rawValue
    physicsBody?.collisionBitMask =  CollisionTypes.platform.rawValue
    physicsBody?.restitution = 0.0
    physicsBody?.friction = 0.0
    zPosition = 20
    physicsBody?.linearDamping = 0.0
    physicsBody?.angularDamping = 0.0


required init?(coder aDecoder: NSCoder) 
    super.init(coder: aDecoder)

这是我的播放器类

class Player: SKSpriteNode 

init() 

    super.init(texture: nil, color: SKColor.clear, size: playerTexture.size())

    //starts accelerameter
    motionManager = CMMotionManager()
    motionManager.startAccelerometerUpdates()
    physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: playerTexture.size().width,
                                                    height: playerTexture.size().height))

    physicsBody?.categoryBitMask = CollisionTypes.player.rawValue
    physicsBody?.contactTestBitMask =     CollisionTypes.memoryModule.rawValue | CollisionTypes.spikes.rawValue | CollisionTypes.finish.rawValue | CollisionTypes.enemy.rawValue | CollisionTypes.ground.rawValue

    physicsBody?.collisionBitMask = CollisionTypes.ground.rawValue | CollisionTypes.sceneEdge.rawValue
    physicsBody?.affectedByGravity = true
    physicsBody?.restitution = 0.0
    physicsBody?.friction = 0.3
    physicsBody?.isDynamic = true
    //physicsBody?.friction = 0.0
    physicsBody?.allowsRotation = false
    setScale(0.65)
    zPosition = 1
    physicsBody?.linearDamping = 0.0
    physicsBody?.angularDamping = 0.0

    animateWalk()


所有这一切的目标是使用 hitbox 来检测平台,而不是在我的更新方法中使用的这个函数中的播放器:

  //jump through platform check
    if let body = player.physicsBody 
        let dy = body.velocity.dy

            if dy > 0
                // Prevent collisions if the hero is jumping
                body.collisionBitMask &= ~CollisionTypes.platform.rawValue
                body.contactTestBitMask &= ~CollisionTypes.platform.rawValue
                        
                        else 
                // Allow collisions if the hero is falling
                body.collisionBitMask |= CollisionTypes.platform.rawValue
                body.contactTestBitMask |= CollisionTypes.platform.rawValue
                        
                    

我需要这样做的唯一原因是因为当玩家坠落时,如果他坠落并撞到平台的一侧,他会停下并滑下平台,因为碰撞又重新开始了,玩家的物理体是一个正方形。

如果我可以使用平台检测命中框来检测平台而不是玩家庞大的命中框,它将摆脱这个问题。

感谢您的任何建议。

编辑:这是孩子的行为,有物理身体,没有关节:

Odd behavior

【问题讨论】:

如果你的人需要 2 个物理体,我会添加一个带有第二个体的子节点。对于你需要的东西来说,关节似乎有点过分了 顺便说一句,接触发生在碰撞之前,所以如果我没看错的话,你甚至不需要物理体,只需检查接触点以确定是否激活或停用平台检查你的碰撞掩码 @Knight0fDragon 谢谢,添加一个没有物理主体的子节点可以正常工作,并且符合我的预期。但是没有物理实体,我无法检测到碰撞。一旦我在没有关节的情况下为孩子添加物理体,一旦玩家开始四处移动,孩子的位置就会发生巨大变化。它看起来几乎像一个错误,但关节是在保持物理身体的同时防止疯狂运动的唯一方法。 我想我要纠正一个关于这个的教程,所以很多人都在为这个而苦恼 那将不胜感激。我在 OP 中添加了一个 Edit,当它有一个物理体但没有关节时,它显示了奇怪的孩子行为。我通过改变它的 x 速度来移动玩家。也许这与它有关? 【参考方案1】:

我找到了解决办法。

似乎有一些固定关节的错误,答案是改用“销”关节。 This StackO question 详细介绍了固定关节的旋转错误。一时兴起,我决定把我的固定关节换成别针,现在孩子的行为就像你所期望的那样,无论如何从位置上讲。

所以看起来固定关节有一些需要解决的错误。

【讨论】:

以上是关于使用固定的 SKPhysicsJoint 将作为孩子的平台检测命中框附加到玩家精灵会更改玩家的碰撞和检测位掩码的主要内容,如果未能解决你的问题,请参考以下文章

SKPhysicsJoint:接触和碰撞不起作用

写 给 我 的 孩 子

将固定大小的特征矩阵作为参数传递给调用动态大小矩阵的函数

Chrome 扩展:插入固定 div 作为 UI

为啥将地址右移三位作为固定大小哈希表的哈希函数?

宽带篇(7.0) ❀ 06. 怎么将4G作为备用宽带使用?