swift - 如何使用 updateAtTime 在 SceneKit 中使角色跳跃准确

标签 swift xcode scenekit

我尝试在 SceneKit 中进行角色跳跃。我目前有一个解决方案,但我对此并不满意。

角色移动基于 updateAtTime 渲染器函数。不涉及 SCNActions。 我希望它仅由 updateAtTime 函数移动。

我的场景完全基于 Apple 的 Fox 演示游戏。但在这个演示游戏中,狐狸角色只能从高处坠落,而不能跳跃。所以我做了一个改编,将fall函数反转为jump函数。结果有点像沃克,但不准确。看看:

Current Jumping and Expected Jumping

正如该图所示,角色当前以违反自然法则的方式跳跃。它缓慢加速,然后变得越来越快。 (摔倒也没关系)。我想让我的角色在一个漂亮的正弦波中跳跃,就像我的图像右侧所示的那样。就像在现实世界中一样。

这是一些代码,使我的角色当前上下跳跃。

// 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
}

如您所见,我使用变量 isJumpingisJumpingUp 来控制/定义角色应移动的 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/

相关文章:

ios - 如何检索现有场景中节点的位置?

iOS - 如何在前置摄像头顶部添加 sceneKit 节点对象。不是 ArKit

ios - 附加PathComponent后路径不同(_ :)

ios - 如何在SwiftUI中汇总过滤后的核心数据?

ios - Xcode 在 View 可见后加载缓慢的东西

objective-c - 单元测试失败,那就不要

ios - 如何使用 Collection View Swift 3 创建 'sticky' 水平滚动效果

arrays - 数组扩展,对成员 'subscript' 的引用不明确

iphone - UIWebView 里面的 UIScrollView 内容大小

3d - iOS : How to simulate default camera controls in SceneKit?