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 篇原創文章 · 獲贊 1 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章