试图建立平台,我可以从下面跳过,但降落在上面。难以微调逻辑
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了试图建立平台,我可以从下面跳过,但降落在上面。难以微调逻辑相关的知识,希望对你有一定的参考价值。
我的目标是在.sks文件中设置我的所有平台,以便更轻松地设计我的关卡。
这是在didMove之前在游戏scene.swift的顶部声明的:
private var JumpThroughPlatformObject = SKSpriteNode()
这是在DidMove中:
if let JumpThroughPlatformObjectNode = self.childNode(withName: "//jumpThroughPlatform1") as? SKSpriteNode {
JumpThroughPlatformObject = JumpThroughPlatformObjectNode}
我引用平台从.sks获得它的高度,因为我的所有平台都将达到相同的高度,我只需要从一个平台获得它。
下面是我试图在我的更新方法中使用的关闭冲突,直到我的播放器完全在平台之上。仅检查我的球员速度是否大于零的主要问题是:如果球员处于跳跃的峰值(他的速度减慢到零)。如果发生这种情况且玩家在平台内,他会立即弹到平台顶部或向下发射。
我不希望我的平台必须是1像素高的线。我还需要让玩家拥有一个完整的碰撞盒,因为他将与其他类型的环境进行交互。这让我相信我不知何故只需要将平台的顶部注册为碰撞盒而不是整个平台。
我写的if语句应该取平台的y位置并将其高度的一半加到它上面,因为y位置是基于精灵的中心我认为这会将平台的碰撞置于其顶部边界上。
我为玩家做了同样的事,但反之亦然。将球员碰撞只放在边界的底部。但它不能很好地工作,我不知道为什么在这一点上。
if (JumpThroughPlatformObject.position.y + (JumpThroughPlatformObject.size.height / 2)) > (player.position.y - (player.size.height / 2))
以下功能给出了3个主要问题:
- 我的球员跳跃总是dy = 80.如果我跳到一个位置.y = 90的平台,跳跃的球员高峰停在平台的中间,但他传送到它的顶部而不是继续落到地上。
- 如果我摔倒,平台的左右边缘仍然与玩家完全碰撞
- 如果我的玩家在一个平台上并且在我上面还有另一个玩家,那么玩家就无法跳过它。
设零:CGFloat = 0
if let body = player.physicsBody { let dy = player.physicsBody?.velocity.dy // when I jump dy is greater than zero else I'm falling if (dy! >= zero) { if (JumpThroughPlatformObject.position.y + (JumpThroughPlatformObject.size.height / 2)) > (player.position.y - (player.size.height / 2)) { print(" platform y: (JumpThroughPlatformObject.position.y)") print ("player position: (player.position.y)") // Prevent collisions if the hero is jumping body.collisionBitMask = CollisionTypes.saw.rawValue | CollisionTypes.ground.rawValue } } else { // Allow collisions if the hero is falling body.collisionBitMask = CollisionTypes.platform.rawValue | CollisionTypes.ground.rawValue | CollisionTypes.saw.rawValue } }
任何建议将不胜感激。我已经把头发撕了几天了。
编辑didBegin和didEnd:
func didBegin(_ contact: SKPhysicsContact) {
if let body = player.physicsBody {
let dy = player.physicsBody?.velocity.dy
let platform = JumpThroughPlatformObject
let zero:CGFloat = 0
if contact.bodyA.node == player {
// playerCollided(with: contact.bodyB.node!)
if (dy! > zero || body.node!.intersects(platform)) && ((body.node?.position.y)! - player.size.height / 2 < platform.position.y + platform.size.height / 2) {
body.collisionBitMask &= ~CollisionTypes.platform.rawValue
}
} else if contact.bodyB.node == player {
// playerCollided(with: contact.bodyA.node!)
isPlayerOnGround = true
if (dy! > zero || body.node!.intersects(platform)) && ((body.node?.position.y)! - player.size.height / 2 < platform.position.y + platform.size.height / 2) {
body.collisionBitMask &= ~CollisionTypes.platform.rawValue}
}
}
}
func didEnd(_ contact: SKPhysicsContact) {
if let body = player.physicsBody {
// let dy = player.physicsBody?.velocity.dy
// let platform = JumpThroughPlatformObject
if contact.bodyA.node == player {
body.collisionBitMask |= CollisionTypes.platform.rawValue
}else if contact.bodyB.node == player {
body.collisionBitMask |= CollisionTypes.platform.rawValue
}
}
}
添加我所做的,玩家无法再跳过平台。
这是我为macOS和ios目标制作的项目的链接: https://github.com/fluidityt/JumpUnderPlatform
基本上,这一切都与此有关
- 检测平台的碰撞
- 然后确定您的播放器是否在平台下
- 让你的玩家通过平台(然后登陆它)
--
SK Physics让这有点复杂:
- 在碰撞检测中,您的玩家的
.position.y
或.velocity.dy
可能已经变为“假”状态,以满足上面的#2检查(意味着#3永远不会发生)。此外,您的播放器将在第一次接触时从平台上弹回。 - 没有“自动”方式来确定玩家何时完成穿过对象(从而允许玩家降落在平台上)
--
因此,为了使一切工作,必须使用一点创造力和独创性!
1: Detecting collision of a platform:
因此,解决1是最简单的:我们只需要使用内置的didBegin(contact:)
我们将在很大程度上依赖于3大比特马斯,联系人,类别和碰撞:
(fyi,我不喜欢使用enum和bitmath进行物理,因为我是一个反叛的白痴):
struct BitMasks {
static let playerCategory = UInt32(2)
static let jupCategory = UInt32(4) // JUP = JumpUnderPlatform
}
override func didBegin(_ contact: SKPhysicsContact) {
// Crappy way to do "bit-math":
let contactedSum = contact.bodyA.categoryBitMask + contact.bodyB.categoryBitMask
switch contactedSum {
case BitMasks.jupCategory + BitMasks.playerCategory:
// ...
}
--
现在,你说你想使用SKSEditor,所以我接待了你:
// Do all the fancy stuff you want here...
class JumpUnderPlatform: SKSpriteNode {
var pb: SKPhysicsBody { return self.physicsBody! } // If you see this on a crash, then WHY DOES JUP NOT HAVE A PB??
// NOTE: I could not properly configure any SKNode properties here..
// it's like they all get RESET if you put them in here...
required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
}
--
Now for the player:
class Player: SKSpriteNode {
// If you see this on a crash, then WHY DOES PLAYER NOT HAVE A PB??
var pb: SKPhysicsBody { return self.physicsBody! }
static func makePlayer() -> Player {
let newPlayer = Player(color: .blue, size: CGSize(width: 50, height: 50))
let newPB = SKPhysicsBody(rectangleOf: newPlayer.size)
newPB.categoryBitMask = BitMasks.playerCategory
newPB.usesPreciseCollisionDetection = true
newPlayer.physicsBody = newPB
newPlayer.position.y -= 200 // For demo purposes.
return newPlayer
}
}
2. (and dealing with #4): Determining if under platform on contact:
有很多方法可以做到这一点,但我选择使用KOD提到的player.pb.velocity.dy
方法来跟踪玩家的位置...如果你的dy超过0,那么你是跳(在平台下),如果没有,那么你要么站着不动要么摔倒(需要与平台接触并坚持下去)。
为了实现这一点,我们必须更加技术化,因为物理系统和SK在其循环中的工作方式并不总是100%与我们认为应该如何工作。
基本上,我必须为玩家制作一个initialDY
属性,在update
的每一帧都不断更新
这个initialDY
将为我们提供第一次接触平台时所需的正确数据,允许我们告诉我们更改碰撞掩码,并将我们的玩家的CURRENT dy重置为最初的dy(因此玩家不会反弹)关闭)。
3. (and dealing with #5): Allow player to go through platform
要通过平台,我们需要玩collisionBitMasks
。我选择让玩家的碰撞面具=玩家的categoryMask,这可能不是正确的方法,但它适用于这个演示。
你在didBegin
最终得到了这样的魔法:
// Check if jumping; if not, then just land on platform normally.
guard player.initialDY > 0 else { return }
// Gives us the ability to pass through the platform!
player.pb.collisionBitMask = BitMasks.playerCategory
现在,处理#5将需要我们向我们的玩家类添加另一个状态..我们需要临时存储所联系的平台,以便我们可以检查玩家是否已成功完成通过平台(因此我们可以重置碰撞面具)
然后我们只检查didFinishUpdate
,如果玩家的框架高于该平台,如果是,我们重置掩码。
以下是所有文件,并再次指向github的链接: https://github.com/fluidityt/JumpUnderPlatform
Player.swift:
class Player: SKSpriteNode {
// If you see this on a crash, then WHY DOES PLAYER NOT HAVE A PB??
var pb: SKPhysicsBody { return self.physicsBody! }
// This is set when we detect contact with a platform, but are underneath it (jumping up)
weak var platformToPassThrough: JumpUnderPlatform?
// For use inside of gamescene's didBeginContact (because current DY is altered by the time we need it)
var initialDY = CGFloat(0)
}
// MARK: - Funkys:
extension Player {
static func makePlayer() -> Player {
let newPlayer = Player(color: .blue, size: CGSize(width: 50, height: 50))
let newPB = SKPhysicsBody(rectangleOf: newPlayer.size)
newPB.categoryBitMask = BitMasks.playerCategory
newPB.usesPreciseCollisionDetection = true
newPlayer.physicsBody = newPB
newPlayer.position.y -= 200 // For demo purposes.
return newPlayer
}
func isAbovePlatform() -> Bool {
guard let platform = platformToPassThrough else { fatalError("wtf is the platform!") }
if frame.minY > platform.frame.maxY { return true }
else { return false }
}
func landOnPlatform() {
print("resetting stuff!")
platformToPassThrough = nil
pb.collisionBitMask = BitMasks.jupCategory
}
}
// MARK: - Player GameLoop:
extension Player {
func _update() {
// We have to keep track of this for proper detection of when to pass-through platform
initialDY = pb.velocity.dy
}
func _didFinishUpdate() {
// Check if we need to reset our collision mask (allow us to land on platform again)
if platformToPassThrough != nil {
if isAbovePlatform() { landOnPlatform() }
}
}
}
JumpUnderPlatform & BitMasks.swift (respectively:)
// Do all the fancy stuff you want here...
class JumpUnderPlatform: SKSpriteNode {
var pb: SKPhysicsBody { return self.physicsBody! } // If you see this on a crash, then WHY DOES JUP NOT HAVE A PB??
required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
}
struct BitMasks {
static let playerCategory = UInt32(2)
static let jupCategory = UInt32(4)
}
GameScene.swift:
-
-
// MARK: - Props:
class GameScene: SKScene, SKPhysicsContactDelegate {
// Because I hate crashes related to spelling errors.
let names = (jup: "jup", resetLabel: "resetLabel")
let player = Player.makePlayer()
}
// MARK: - Physics handling:
extension GameScene {
private func findJup(contact: SKPhysicsContact) -> JumpUnderPlatform? {
guard let nodeA = contact.bodyA.node, let nodeB = contact.bodyB.node else { fatalError("how did this happne!!??") }
if nodeA.name == names.jup { return (nodeA as! JumpUnderPlatform) }
else if nodeB.name == names.jup { return (nodeB as! JumpUnderPlatform) }
else { return nil }
}
// Player is 2, platform is 4:
private func doContactPlayer_X_Jup(platform: JumpUnderPlatform) {
以上是关于试图建立平台,我可以从下面跳过,但降落在上面。难以微调逻辑的主要内容,如果未能解决你的问题,请参考以下文章