物體在二維空間中的移動
2013-11-09 20:25
309 查看
遊戲裡移動的原理很簡單,就是經圖從舊的地方刪除,重新畫在新的地方,只要速度夠快,看起來就會像是在移動。而
XNA 在每一次呼叫繪圖函數時都會有一行 GraphicsDevice.Clear (Color.CornflowerBlue);就是負責把畫面清空用的,所以我們只要改變圖的位置就可以了,話雖如此,但是不斷地改變位置對於需要不斷移動或是移動路徑複雜的物體,實在也很難知道下一次要出現在哪裡,所以必須用算的。於是乎,數學就重要啦!
相信大家在國中的時候就學過幾何數學了,當時或許覺得有趣,也可能認為不知所云!不過現在可能需要稍微回想一下了。在二維空間中物體的移動會有速度,而速度有方向性,在 xna 中可以用 Vector2 記錄,Vector2 支援許多向量的運算。假設有一顆紅色的球往右上角移動,他的速度是 V,此速度可以分解成 x 和 y 的分量,分別是 Vx 以及 Vy,如下圖:
此時我們可以用 Vector2 中的 X 與 Y 來記錄 Vx 和 Vy。若我們要讓物體往定點移動,假設由 A 點到 B 點速率為 s(速率 s 是純量),速度可以由下面的算式得出:
而當物體在向著目的地移動時,BA向量與速度的內積會是正數(如下圖左上),當物體到達目的地時,BA向量與速度的內積會是零,當物體超過目的地時,BA向量與速度的內積會是負數,(如下圖右下):
因此我們可以由此關係知道是否已經到達目的地,該停止移動了!
知道以上簡單的幾何數學後,就可以開始設計我們的程式了。首先設計一個飛機物件,他是我們的主角,程式碼如下:
public class Airplane{ private Vector2 _Origin; private Vector2 _Velocity; private Vector2 _Position; private Vector2 _Destination; private float _Speed; private bool _IsArrive; public Texture2D Image { get; private set; } public Vector2 Origin { get { return _Origin; } } //速度 public Vector2 Velocity { get { return _Velocity; } } //目前位置 public Vector2 Position { get { return _Position; } } //目標位置 public Vector2 Destination { get { return _Destination; } } //速率 public float Speed { get { return _Speed; } set { _Speed = value; } } //是否已經到達 public bool IsArrive { get { return _IsArrive; } } public Airplane(Texture2D image, Vector2 defaultPosition) { Image = image; _Origin = new Vector2(image.Width / 2, image.Height / 2); _Velocity = Vector2.Zero; _Position = defaultPosition; _Destination = defaultPosition; _IsArrive = true; _Speed = 200; } /// <summary> /// 計算前往目標位置所需要的速度 /// </summary> /// <param name="destination">目標位置</param> public void MoveTo(Vector2 destination) { _Destination = destination; Vector2 vector = Vector2.Subtract(destination, Position); float length = vector.Length(); if (length == 0) { _IsArrive = true; } else { float percent = _Speed / length; _Velocity = Vector2.Multiply(vector, percent); _Velocity /= 1000f; _IsArrive = false; } } public void Update(float time) { if (_IsArrive == false) { _Position += _Velocity * time; if (Vector2.Dot(Vector2.Subtract(_Destination, _Position), _Velocity) <= 0) { _IsArrive = true; _Position = _Destination; } } } public void Draw(SpriteBatch sprite) { sprite.Draw(Image, _Position, null, Color.White, 0, _Origin, 1, SpriteEffects.None, 0); } }
讓我稍微介紹一下 MoveTo 函式,此函式的參數是要到達的目標位置。在寫之前,應該先決定「單位」,一般生活中常用的速率單位是公尺每秒,但我想螢幕可以用公尺算的人應該不多!這裡我們就用像素每秒來當速率的單位!表示每一秒物體移動多少像素。因此 MoveTo 的內容就是一些簡單的幾何數數學了~先求出我與目標的向量,在乘以縮放比率,最後除以 1000 是因為我們輸入的速率是以秒為單位,但遊戲時間是以毫秒為單位,所以除以一千,這樣算出來的速度就是像素每毫秒。 Update 函式傳入毫秒,然後速度乘以時間就是距離,用來計算飛機下一個應該出現在哪裡。再來是利用向量內積來計算有沒有跑過頭,如果過頭了就表示已經到了,就要把飛機的位置設定成目標位置,如果不這樣做,飛機會因為速度太快而跑過頭,越快越明顯。Draw
函式就是讓飛機畫出自己。 在 Game 裡面加入 Airplane 物件,然後當使用者點畫面後,飛機就會移動到那個地方,關鍵程式碼如下:
Airplane Airplane; protected override void Initialize() { TouchPanel.EnabledGestures = GestureType.Tap; base.Initialize(); } protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); float time = (float)gameTime.ElapsedGameTime.TotalMilliseconds; while (TouchPanel.IsGestureAvailable) { var gs = TouchPanel.ReadGesture(); Airplane.MoveTo(gs.Position); } Airplane.Update(time); base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); // TODO: Add your drawing code here spriteBatch.Begin(); Airplane.Draw(spriteBatch); spriteBatch.End(); base.Draw(gameTime); } _Position = defaultPosition; _Destination = defaultPosition; _IsArrive = true; _Speed = 200; } /// <summary> /// 計算前往目標位置所需要的速度 /// </summary> /// <param name="destination">目標位置</param> public void MoveTo(Vector2 destination) { _Destination = destination; Vector2 vector = Vector2.Subtract(destination, Position); float length = vector.Length(); if (length == 0) { _IsArrive = true; } else { float percent = _Speed / length; _Velocity = Vector2.Multiply(vector, percent); _Velocity /= 1000f; _IsArrive = false; } } public void Update(float time) { if (_IsArrive == false) { _Position += _Velocity * time; if (Vector2.Dot(Vector2.Subtract(_Destination, _Position), _Velocity) <= 0) { _IsArrive = true; _Position = _Destination; } } } public void Draw(SpriteBatch sprite) { sprite.Draw(Image, _Position, null, Color.White, 0, _Origin, 1, SpriteEffects.None, 0); } }
讓飛機隨著點選的位置移動,但是飛機卻不會轉頭,看起來怪怪的。接著就來試著讓它跟著轉向吧!程式裡的角度轉向和我們以前念的數學也有點差異,主要是起始位置不同,以前數學通常都是由 X 軸逆時針方向旋轉 (下圖左),但是程式裡是 Y 軸順時針方向旋轉 (下圖右),這點要特別注意。另外一個要注意的就是遊戲內的角度是弧度,介於 0 到 2π 之間。
要轉向,就要先知道角度,大家是不是又把高中數學還給老師了呢?讓我們複習一下。
用這公式就可以求出兩個向量的夾角。由於我們要計算 Y 軸和目前速度 (V) 的夾角,而Y軸向量是 (0,1),其長度是 1,所以公式可以簡化如下
看一下程式碼:
public void Update(float time) { if (_IsArrive == false) { _Position += _Velocity * time; _Rotation = (float)Math.Acos((double)(-_Velocity.Y / _Velocity.Length())); if (_Velocity.X < 0) _Rotation = (float)MathHelper.TwoPi - _Rotation; System.Diagnostics.Debug.WriteLine(string.Format("{0}:{1}", _Velocity.Y / _Velocity.Length(), _Rotation)); if (Vector2.Dot(Vector2.Subtract(_Destination, _Position), _Velocity) <= 0) { _IsArrive = true; _Position = _Destination; } } } /// </summary> /// <param name="destination">目標位置</param> public void MoveTo(Vector2 destination) { _Destination = destination; Vector2 vector = Vector2.Subtract(destination, Position); float length = vector.Length(); if (length == 0) { _IsArrive = true; } else { float percent = _Speed / length; _Velocity = Vector2.Multiply(vector, percent); _Velocity /= 1000f; _IsArrive = false; } } public void Update(float time) { if (_IsArrive == false) { _Position += _Velocity * time; if (Vector2.Dot(Vector2.Subtract(_Destination, _Position), _Velocity) <= 0) { _IsArrive = true; _Position = _Destination; } } } public void Draw(SpriteBatch sprite) { sprite.Draw(Image, _Position, null, Color.White, 0, _Origin, 1, SpriteEffects.None, 0); } }
其中重要的兩行需要稍微講解一下
VB
_Rotation = (float)Math.Acos((double)(-_Velocity.Y / _Velocity.Length()));
注意這裡是負的 _Velocity.Y,這跟坐標系有關,根據上面角度的算法是由 Y 軸順時針起算,這裡 Y 軸是往上為正的笛卡兒座標系,但是螢幕的 Y 軸卻是往下為正,因此當我們的速度是正的,在螢幕上是往下跑,轉換成笛卡兒座標系就必須加個負號。
再來是下面一行
VB
if (_Velocity.X < 0) _Rotation = (float)MathHelper.TwoPi - _Rotation;
當我們用Math.Acos計算出來的角度,會是最小夾角,也就是不論我們的方向是右上還是左上,如下圖,得到的角度都是πD4 (45度),所以我們要分辨速度的X軸,如果是負的,就要用計算補角,也就是 2π-π/4=7π/4 (315度)
這樣計算的角度才會是我們要的,程式執行後,小飛機就會往我們點擊的地方跑,也會正常地轉頭了!
相关文章推荐
- 物體的移動
- 中移動新政策 幾家歡喜幾家憂
- 2012移動營銷論壇:3年後移動廣告收入達72億
- 移動設備開發注意點
- div 移動小示例
- 移動設備開發注意點
- 簡單的內容移動展示
- C#中怎樣利用VBA控制word中的某張圖片使 之從左上角移動到右下角(或說從原來的位置向左移動200個單位)
- CABasicAnimation用法和一些簡單的動畫效果,移動,旋轉,縮放
- フォルダの作成、削除、移動、タイムスタンプの取得と設定
- 物体移動
- 網頁內的對象可以移動
- 移動互聯網20大酷模式
- asp.net中鼠標移動圖片
- 實現樹樁類型結構及其相應的操作【增刪查改和移動】
- 移動的業務辦理辦法。人工服務或者是發短信qxgj -> 10086.網站是沒辦法辦理的。
- 學習 React.js:用 ReactJS 30 分鐘打造一個移動應用
- zz: 如何移動已存在的SQL Server 2005中的user database
- HOWTO--如何移動Oracle的所有數據文件
- 移動內存塊move