我尝试在 SceneKit 中进行角色跳跃。我目前有一个解决方案,但我对此并不满意。
角色移动基于 updateAtTime 渲染器函数。不涉及 SCNActions。 我希望它仅由 updateAtTime 函数移动。
我的场景完全基于 Apple 的 Fox 演示游戏。但在这个演示游戏中,狐狸角色只能从高处坠落,而不能跳跃。所以我做了一个改编,将fall函数反转为jump函数。结果有点像沃克,但不准确。看看:
正如该图所示,角色当前以违反自然法则的方式跳跃。它缓慢加速,然后变得越来越快。 (摔倒也没关系)。我想让我的角色在一个漂亮的正弦波中跳跃,就像我的图像右侧所示的那样。就像在现实世界中一样。
这是一些代码,使我的角色当前上下跳跃。
// For Jumping (Falling)
if groundAltitude < position.y - threshold || self.isJumping {
if self.isJumpingUp {
accelerationY -= SCNFloat(deltaTime) * gravityAcceleration // approximation of acceleration for a delta time. UP MOVEMENT
} else {
accelerationY += SCNFloat(deltaTime) * gravityAcceleration // approximation of acceleration for a delta time. DOWN MOVEMENT (Orig Apple)
}
} else {
accelerationY = 0
}
// Set Position
position.y -= accelerationY // ORIG
// reset acceleration if we touch the ground
if groundAltitude > position.y {
accelerationY = 0
position.y = groundAltitude
}
如您所见,我使用变量 isJumping
和 isJumpingUp
来控制/定义角色应移动的 Y 方向。所以我要做的就是将 isJumping
变量设置为 true,并将 isJumpingUp
设置为 true,这会使角色向上移动。然后,在一半的时间,我将变量 isJumpingUp
设置为 false,这将反转方向,并将角色带到地面。
这或多或少都会导致图像可视化时不准确的跳跃运动。
然后我在SO上找到了这篇文章: How to make my character jump with gravity? (它甚至能够在浏览器中运行此代码片段。)
这非常接近,即使不是我正在寻找的。 (但我只是在寻找Y方向运动的东西)
但是每次尝试对此进行 Swift/SceneKit 改编都会导致一团糟。我无法将其以与网站上的行为相同的方式实现到我的更新功能中。而且我不知道我做错了什么。
一些尝试让角色跳得非常非常快,然后就再也没有下来。或者角色根本没有跳跃。我只是不知道...我希望我的角色跳到大约 1m 到 2m 的高度,然后掉下来。
非常感谢任何帮助我的角色准确跳跃的帮助。
出于可视化目的 - 这是我当前的整个运动函数:
func moveCharacter(time: TimeInterval) {
// Delta time since last update
if previousUpdateTime == 0.0 { previousUpdateTime = time }
let deltaTime = Float(min(time - previousUpdateTime, 1.0 / 60))
previousUpdateTime = time // GOOD
groundType = GroundType.inTheAir // always make it in the Air, later change, seems to crash the app ??? oder zufall? - RE-ENABLED for testing
// Speed Control
var characterSpeed = deltaTime * self.speedFactor
let characterRunSpeed = deltaTime * self.speedFactor * 3.4
// Remember initial position
let initialPosition = self.node.position
// Move Character Left or Right
if self.isWalkingLeft { self.node.position = self.node.position - SCNVector3(1.0,0.0,0.0) * characterSpeed}
if self.isWalkingRight { self.node.position = self.node.position + SCNVector3(1.0,0.0,0.0) * characterSpeed}
if self.isRunningLeft { self.node.position = self.node.position - SCNVector3(1.0,0.0,0.0) * characterRunSpeed}
if self.isRunningRight { self.node.position = self.node.position + SCNVector3(1.0,0.0,0.0) * characterRunSpeed}
// Character height positioning
var position = self.node.position
var p0 = position
var p1 = position
let maxRise = SCNFloat(1.0) // orig 0.08
let maxJump = SCNFloat(50.0) // orig 20.0
p0.y -= maxJump
p1.y += maxRise
// Do a vertical ray intersection
let results = gameScene.physicsWorld.rayTestWithSegment(from: p1, to: p0, options:[.collisionBitMask: BitMasks.BitmaskCollision, .searchMode: SCNPhysicsWorld.TestSearchMode.closest])
if let result = results.first {
guard (result.node.geometry != nil) else {return}
let groundAltitude = result.worldCoordinates.y
// can the following if statement be made in other way, because of the new guard?
if (result.node.geometry!.firstMaterial) != nil { groundType = groundTypeFromMaterial(material: result.node.geometry!.firstMaterial!) } else { groundType = .rock }
// MARK: Handle Y Position
let threshold = SCNFloat(1e-5)
let gravityAcceleration = SCNFloat(0.18) // 0.18
if groundAltitude < position.y - threshold || self.isJumping {
if self.isJumpingUp {
accelerationY -= SCNFloat(deltaTime) * gravityAcceleration // approximation of acceleration for a delta time. UP
} else {
accelerationY += SCNFloat(deltaTime) * gravityAcceleration // approximation of acceleration for a delta time. DOWN (Orig)
}
}
else {
accelerationY = 0
}
// Set Position
position.y -= accelerationY // orig.
// reset acceleration if we touch the ground
if groundAltitude > position.y {
accelerationY = 0
position.y = groundAltitude
}
// Finally, update the position of the character.
self.node.position = position
// if not touching the ground, we are in the air.
if groundAltitude < position.y - 0.2 {
groundType = .inTheAir
}
} else {
// no result, we are probably out the bounds of the level -> revert the position of the character.
self.node.position = initialPosition
}
}
最佳答案
好吧,经过一番 sleep 和反射(reflection)后,我得出了这个解决方案:
(而且效果很好)
将此变量添加到类中:
let gravityDrag : SCNFloat = 0.9999 // kind of slowing down the jump
let jumpPower : SCNFloat = -0.09 // jumps between 1,5m to 2.0m
然后更改 moveCharacter
函数,如下所示:
// Calc Y Position with acceleration and gravity
if groundAltitude < position.y - threshold || self.isJumping {
accelerationY += SCNFloat(deltaTime) * gravityAcceleration // approximation of acceleration for a delta time. DOWN (Orig)
accelerationY *= gravityDrag
}
else {
accelerationY = 0
}
// Set Position
position.y -= accelerationY // orig.
// reset acceleration if we touch the ground
if groundAltitude > position.y {
isJumping = false
accelerationY = 0
position.y = groundAltitude
}
像这样触发跳转:
isJumping = true
accelerationY = jumpPower
希望这能在某个时候对某人有所帮助。
关于swift - 如何使用 updateAtTime 在 SceneKit 中使角色跳跃准确,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70548154/