使用Spritekit为2d平台游戏添加移动平台的便利性

时间:2017-07-24 14:03:28

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

嗯你好,

我正在使用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

只是为了澄清,这很有用。如果有更好的方法可以做到这一点,我会全力以赴。但是这会产生锯齿状的停顿并转动一种感觉。我希望这种轻松进出感觉让平台感觉更自然。

感谢您的帮助!!!

2 个答案:

答案 0 :(得分:2)

缓出功能

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

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

此答案时间为t,位置是时间的函数,f(t)是时间t的平台位置。

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

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

请原谅我从未使用过的快捷方式(我相信你可以纠正任何错误的语法)。

// 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的幂。在swift中,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的正常速度的一半,并且在中途点速度将是一半。为了让一切工作在开始和结束时都变得轻松,快速涌入并冲出去。 (也适用于反向)

帮助提供视觉表现

enter image description here

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

然后我们通过仅查看一段运动来规范化运动和时间。

enter image description here

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

红线代表线性运动f(t)=t(恒定速度)。在任何时候你移动到击中线向下移动,你可以找到行进的距离。

绿线表示缓动函数f(t)=t*t/(t*t+(1-t)*(1-t)),它的工作原理相同。在任何时间点扫描以找到绿线并向下移动以获得距离。函数f(t)为你做了。

使用maxSpeed时,在dist 0.5处的线的陡度发生变化,陡峭的斜率代表更快的行程。

答案 1 :(得分:1)

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

你可以做点什么:

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

基本上,当velocity.dx <1时,它会产生摩擦力。 10,你可能想要调整你想要的数字