14.像巴士一样行驶:在位置基础上添加摩擦力

14.Driving – Like A Bus: Adding friction based on location, and that differs sideways to forwards.

This post is about adding friction, or: rotating, splitting and manipulating vectors.

这篇帖子讨论添加摩擦力,或者:旋转,分解以及操作向量。

We recently changed our spaceship into a bus when developing our scenario, but it still handles more like a spaceship than a bus. The reason is friction. Our bus still uses the asteroids-style handling from the spaceship, where it slides around without ever slowing down and where you can spin round with no effect on your current speed. Let’s change that.

我们最近的游戏剧本里将飞船改为了巴士,但是处理起来更像飞船而不是巴士。原因是摩擦力。我们的巴士仍然使用星球大战风格中的飞船处理方式,它一直滑行但是从不慢下来,而且在当前速度下回旋时没有任何效果。让我们改变一下。

Different Friction

各式各样的摩擦力

If we wanted to simply slow down over time, we could take the baking code in previous post, and simply make the bus be always braking a little bit: this is what friction effectively does.

如果我们希望巴士随着时间简单地慢下来,我们可以使用前面帖子里介绍的巴士减速代码,简单地让巴士始终减速一点点:这就是摩擦力的作用效果。

However, I want to go one step better than that. When dealing with cars, buses, etc, there is a markedly different effect on forwards speed to sideways speed. Ignoring the possibility of rolling over, if you shoot a car out of a cannon along the ground at 20mph going forwards, it will take a long time to come to a complete halt (because the wheels will turn, with little friction). But if you do the same with the car going sideways, it will stop much faster (because the wheels won’t turn, so there’s lots of friction). And so it should be in our little simulation: sideways speed should reduce much faster than forwards speed.

但是,我希望更进一步。每当处理汽车、巴士之类的物体时,正面的速度和侧面的速度有一个显著的不同效果。如果用炮弹沿着地面以20英里每小时的速度向前射击一辆汽车,假如忽略翻转的可能,那么等它完全停止下来需要经历很长的时间(因为车轮会滚动,摩擦力很小)。但是如果向汽车的侧面这样做得话,它停下来将快得多(因为车轮不会滚动,所以摩擦力很大)。同样在我们的模拟中应该使用这样的原理:侧面速度应该比正面速度下降得更快。

Rotating Motion

旋转运动

So the first thing to do is to separate our motion into forwards and sideways speed. This is a case where there are several mathematical techniques available to produce the same effect: dot product, matrix rotation, etc. We will choose an approach that re-uses some of our previous code. What we’re going to do is take our motion vector, and rotate it counter-clockwise by our current direction, so that the forwards speed will lie along the X-axis and the sideways speed on the Y-axis. Here’s a diagram to help clarify:

因此首先要做的是将运动分解为正面的和侧面的速度。这种情况可以使用不同的数学技巧处理来达到相同的效果:点积,矩阵旋转,等等。我们将采用一种办法使得能够重新利用之前的一些代码。我们将要做的是获取运动向量,并且将其按当前的方向逆时针旋转,从而使得正面速度沿着x轴而侧面速度沿着y轴。下图可以帮助理解:

On the left is a bus that is heading at 34 degrees. Its current motion is the strong black arrow (so the bus is drifting slightly to its left, rather than heading straight). If we take the motion and rotate it in the opposite direction by 34 degrees, the X component of the motion is the “straight ahead” part (the horizontal dashed arrow), and the Y component is the “sideways” part (the vertical dashed arrow) — effectivesplitting the vector.

左边图中有一个巴士面向34度的方位。它的当前运动向量是黑色的实线箭头(于是这个巴士正向左边轻微地漂移,而不是朝正前方运行)。如果我们获取其运动向量并且将其按相反的方向旋转34度,运动向量的x分量便是“笔直向前”的部分(水平虚线箭头),y分量便是“侧面”部分(垂直虚线箭头)——有效的分解了该向量

Here’s the corresponding code to do this rotation:

这儿是执行该旋转的相应代码:

        double dir = calculateDirection(speedX, speedY);
        double speed = calculateMagnitude(speedX, speedY);
        
        // ... not showing braking and skidding code from previous posts
        
        double speedForwards = calculateX(dir - getRotation(), speed);
        double speedSideways = calculateY(dir - getRotation(), speed);

Once we have our forwards and sideways speeds, we can add friction (which we will make different for forwards than sideways) by slowing them down by different factors usingour proportional braking technique:

一旦我们掌握了正面的侧面的速度,我们便可以添加摩擦力(我们将对正面和侧面进行不同处理),这可通过按比例减速方式给予其不同的因子来实现:

        speedSideways = speedSideways * 0.96;
        speedForwards = speedForwards * 0.995;

        if (Math.abs(speedSideways) > 1)
        {
            layDownRubber();
        }

For an extra flourish, I’ve added some code above to producetyre marks when our sideways speed is high enough, so that we skid when we turn sharply.

作为额外的特效,我们还在以上代码里加入了轮胎印迹,当侧面速度值足够高的时候便会产生,于是急剧转向时便有打滑效果。

Now that we’ve altered our forwards and sideways speeds, we need to turn them back into X and Y speeds, by using our old methods:

现在已经改变了正面和侧面的速度值,我们需要将它们转回到x和y速度,可使用如下方式:

        speedX = calculateX(getRotation(), speedForwards)
               + calculateX(getRotation() + 90, speedSideways);
        speedY = calculateY(getRotation(), speedForwards)
               + calculateY(getRotation() + 90, speedSideways);

To explain what this code is doing, we’ll look at a reverse version of our previous diagram:

为了解释代码所做的事情,我们看一下之前示意图的反转版本:

We have the forwards and sideways components of our motion (the horizontal and vertical dashed arrows, respectively, on the left-hand side). We need to rotate both of these andadd them together to get the rotated motion (the solid arrow on the right-hand side). Our calculateX function can already work out the X-component of a rotation and speed, so we pass it our rotation and our forwards speed (the first line above). But we need to add on to that the X component of our sideways speed, which is at 90 degrees to our heading (the second line above). The calculation for the Y coordinate is nearly identical (third and fourth lines), but uses calculateY instead of calculateX.

我们拥有了运动向量的正面和侧面分量(在图左边分别用水平和垂直的虚线箭头表示)。我们需要旋转它们两者并且将其相加来获取旋转后的运动向量(图右边的实线箭头)。我们的calculateX 方法已经可以求出一个向量(包括角度和速度)的x分量,于是将其用到我们的角度和正面速度上(上面的第一行代码)。但是我们需要将侧面速度的x分量加上去,它与我们的朝向呈90度角(上面的第二行代码)。对于y坐标的计算几乎是一致的(第三和第四行代码),但是使用calculateY 方法替代calculateX方法。

事实上,上面的代码可以用如下代码来替代,它们的效果是一样的:

        speed =calculateMagnitude(speedForwards,speedSideways);
        dir=calculateDirection(speedForwards,speedSideways)+getRotation();        
        speedX=calculateX(dir,speed);
        speedY=calculateY(dir, speed);

Off The Beaten Track

As a final touch for this post, we can change the scenario so that we have a track to race the bus on, and the bus slows down more on grass than on the tarmac. Here’s a slightly hacky function to detect if we’re on the track (the grey part of the background image):

作为这篇帖子的最后一个话题,我们可以改变一下游戏剧本,添加一个轨道让巴士能够快速行驶,而巴士在草地上比之在路面上减速更快。以下是一个稍微有点奇特的方法来探测巴士是否行驶在路面上(背景图片的灰色部分):

    private boolean onGrass()
    {
        Color c = getWorld().getBackground().getColorAt(getX(), getY());
        if (c.getRed() == c.getGreen() && c.getRed() == c.getBlue())
            return false; // Grey/white
        else
            return true; // Not grey/white
    }

We can use that to decide how much to slow down our speed. Adjusting our earlier code:

我们可以使用它来决定究竟减速多少。调整我们之前的代码:

if (onGrass())
{
    speedSideways = speedSideways * 0.8;
    speedForwards = speedForwards * 0.9;
}
else
{
    speedSideways = speedSideways * 0.96;
    speedForwards = speedForwards * 0.995;
}

This code counts you as on the track if the centre of the bus is on the track, and off the track if the centre of the bus is on the grass. That’s not ideal: really, it’s whether your wheels are on or off the track that matters. You’ve now seen enough, between the skidding code in the last post, and the rotation code above, to modify the scenario to fix that and slow down depending on how many wheels are on the grass — so that’s your homework! You can findthe scenario in its current state on the Greenfoot site to have a play, ordownload it into Greenfoot and get fixing the wheels issue.

这段代码里,如果巴士的中点在轨道上则看做该巴士在轨道上,如果中点在草地上则巴士脱离了轨道。这不是很理想:实际上,问题在于车轮是否在轨道上。通过上一篇帖子的滑行代码以和上面的旋转代码,你现在已经足够知道如何去修改剧本去改进它,以及根据有多少车轮在草地上来减速——那是你的家庭作业!你可以在Greenfoot网站上试玩当前的游戏剧本,或者下载到Greenfoot里来改进车轮问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值