【问题标题】:Adding ease in and out to moving platforms with spritekit for 2d platformer使用用于 2d 平台游戏的 spritekit 为移动平台添加轻松进出
【发布时间】:2017-07-24 14:03:28
【问题描述】:

你好,

我正在使用 spritekit 为 iOS 制作 2d 平台游戏。我有移动平台,让我的角色可以随着平台移动。

我不能只使用 skactions 来移动我的平台,因为角色不会随着平台移动。

问题:
为了拥有平台,我将如何添加缓入和缓出功能???模拟:SKactionTimeMode.easeInEaseOut

当前解决方案:
我面前没有代码,但对于左/右移动平台,这几乎就是我正在做的事情。这将在平台 update() 方法中运行。

If platform.position.x < xPositionIWantNodeToStopGoingLeft {
    velAmount = -velAmount
}
else if platform.position.x > xPositionIWantNodeToStopGoingRight {
    velAmount = -velAmount
}
platform.physicsBody?.velocity = SKVector(dx: velAmount, dy: velAmount
platform.position.y = staticYPosition

澄清一下,这很好用。如果有更好的方法可以做到这一点,我会全力以赴。但这会产生锯齿状的停止和转弯的感觉。我想要那种轻松进出的感觉,让平台感觉更自然。

感谢您的帮助!!!

【问题讨论】:

    标签: ios swift sprite-kit 2d game-physics


    【解决方案1】:

    缓出功能

    如果我们将平台从一侧移动到另一侧的时间视为一个单位(可能是 10 秒或 17 帧,没关系,我们现在以单位工作)。

    我们对距离做同样的事情。平台必须在一个单位时间内移动一个单位距离。

    这个回答时间是t,位置是时间的函数,写成f(t)是时间t的平台位置。

    对于简单的线性运动,函数就是f(t)=t。因此,在时间t=0 移动的距离为 0,在时间 0.5(中途)时,距离为 0.5(中途),依此类推。

    所以让我们把它变成更实用的东西。

    请原谅我之前从未使用过它的swift(我相信你可以纠正我弄错的任何语法)。

    // First normalise the distance and time (make them one unit long)
    // get the distance
    let distance = Double(xPositionStopGoingLeft - xPositionStopGoingRight);
    
    // use that and the velocity to get the time to travel
    let timeToTravel = distance / Double(velAmountX);  
    
    // first we have a frame ticker
    gameTick += 1; // that ticks for every frame
    
    // We can assume that the platform is always moving back and forth
    
    // Now is the unit time where at now = 2 the platform has move there and back 
    // at 3 it has move across again and at 4 back again.
    let now = Double(gameTick) / timeToTravel; // normalize time.
    
    // get the remainder of 2 as from 0-1 is moving forward and 1-2 is back
    let phase = now % 2.0;
    
    // We also need the unit time for the function f(t)=t
    let t = abs(phase - 1);
    if phase >= 1 { t = 1 - t } // reverse for return 
    
    // implement the function f(t) = t where f(t) is dist
    let dist = t
    
    // and convert back to pixel distance
    platform.position.x = Int(dist * distance + Double(xPositionStopGoingLeft));
     
    

    这就是线性平台。要改变运动,我们需要做的就是改变函数f(t)=?,在上面它的行let dist = t

    为了便于使用,有一个方便的功能可用于大多数简单的应用程序f(t) = t * t / ((t * t) + (1 - t) * ( 1 - t))

    有一些 t*t 是幂,t 是 2 或 t^2 的幂。迅速将其pow(t,2) 重写为代码

    let dist = pow(t,2) / (pow(t,2) + pow((1-t),2);
    

    这在开始和结束时提供了很好的轻松因为经过的距离和时间是恒定的,所以中间点t = 0.5 的速度必须更大才能赶上缓慢的开始和结束。 (旁注,获取上述函数的导数可以让你锻炼每个时间点的速度f'(t) = speed(t) = 2(-(t-1)t)^(2-1) /(t^2+(1-t)^2)^2

    这个函数太好了,0.5 时刻的速度为 2,与功率相同(对于直线行程,它是 1)。该函数的一个方便属性是中间点的速度始终与功率相同。如果你想让它在中点移动得非常快,比如快 4 倍,那么你可以使用 4 的力量

    让 dist = pow(t,4) / (pow(t,4) + pow((1-t),4);

    如果你只想让它加速一点,比如中心速度的 1.2 倍,那么功率是 1.2

    let dist = pow(t,1.2) / (pow(t,1.2) + pow((1-t),1.2);   
    

    所以现在我们可以引入另一个术语,maxSpeed,它是归一化的 maxSpeed(更准确地说,它是 t=0.5 时的速度,因为它可能比 1 慢,但我们需要最大速度就可以了)

    let maxSpeed = Double(velAmountX + 3) / Double(velAmountX); // 3 pixels per frame faster
    

    还有函数f(t) = t^m / (t^m + (1-t)^m),其中m是maxSpeed

    作为代码

    让 dist = pow(t,maxSpeed ) / (pow(t,maxSpeed ) + pow((1-t),maxSpeed);

    把这些放在一起

    // the next 3 lines can be constats
    let distance = Double(xPositionStopGoingLeft - xPositionStopGoingRight);
    let timeToTravel = distance / Double(velAmountX);  
    let maxSpeed = Double(velAmountX + 3) / Double(velAmountX);
    
    gameTick += 1; // that ticks for every frame
    
    let now = Double(gameTick) / timeToTravel; // normalize time.    
    let phase = now % 2.0;
    let t = abs(phase - 1);
    if phase >= 1 { t = 1 - t } // reverse for return 
    
    // the next line is the ease function
    let dist = pow(t, maxSpeed) / (pow(t, maxSpeed) + pow((1-t) ,maxSpeed);
    
    // position the platform
    platform.position.x = Int(dist * distance + Double(xPositionStopGoingLeft));
    

    现在您可以随时计算平台的位置。如果您想减慢整个游戏的速度并将帧步进到一半,它仍然可以工作。如果你加快游戏速度gameTick += 2 它仍然有效。

    最大速度也可以低于线速度。如果您希望平台在中心t=0.5 设置为maxSpeed = 0.5 的正常速度的一半,并且在中点处速度将是一半。为了让一切工作在开始和结束时轻松自如,冲进冲出会更快。 (也适用于反向)

    也许是一种视觉表现形式的帮助

    图片显示了平台随着时间的推移来回移动。距离约为60像素,时间可以是1分钟。所以在 1 分钟时,它会是右边的 2 分钟在左边,依此类推。

    然后我们通过只查看运动的一个部分来标准化运动和时间。

    图表表示从左到右的移动,距离为1,时间为1。它刚刚被缩放以适合单位框(1乘1框)。

    红线代表直线运动f(t)=t(恒速)。在任何时间点你越过这条线向下移动,你可以找到行进的距离。

    绿线代表缓动函数f(t)=t*t/(t*t+(1-t)*(1-t)),它的工作原理是一样的。在任何时间点扫描以找到绿线并向下移动以获得距离。函数 f(t) 会为你做这件事。

    使用 maxSpeed 时,距离 0.5 处的线的陡度会发生变化,更陡的坡度代表更快的行驶。

    【讨论】:

    • 感谢您的深入回答...我将把它扔在那里,这肯定是我的头,哈哈。我将花一些时间来研究一下以了解发生了什么......
    • @Discoveringmypath 我添加了一个可视化表示,可以帮助您了解解决方案。
    • 这个答案太棒了。
    【解决方案2】:

    对于物理,玩弄摩擦和身体的线性阻尼。您甚至可以使用 SKAction 运行块来减少或增加摩擦。

    你可以这样做:

    physicsBody.friction = (10 - physicsBody.velocity.dx) > 0 ? (10 - physicsBody.velocity.dx) / 10 : 0
    

    基本上当velocity.dx

    【讨论】:

    • 我更新了我的问题,我忘记了一行代码并更新了另外两行代码。基本上每次更新,我都会应用速度,因此它是一个恒定的速度。我喜欢线性阻尼的想法,我可以将其视为越接近停止点就可以减慢速度的好方法。作为一种选择,我绝对忽略了这一点......
    • 好奇摩擦在这种情况下有何帮助?因为摩擦更多的是与其他节点的接触......而不是减慢平台的速度......
    • 如果您的身体漂浮在空中,则使用线性阻尼,如果您的平台彼此重叠,则使用摩擦
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-06
    相关资源
    最近更新 更多