Go從入門到精通——結構體(struct)——示例:二維向量模擬玩家移動
阿新 • • 發佈:2022-05-30
示例:二維向量模擬玩家移動
在遊戲中,一般使用二維向量儲存玩家的位置。使用向量運算可以計算出玩家移動的位置。本例子中,首先實現二維向量物件,接著構造玩家物件,最後使用向量物件和玩家物件共同模擬玩家移動的過程。
1、實現二維向量結構
向量是數學中的概念,二維向量擁有兩個方向的資訊,同時可以進行加、減、乘(縮放)、距離、單位化等計算。
在計算機中,使用擁有 X 和 Y 兩個分量的 Vec2 結構體實現數學中二維向量的概念。
package main import "math" type Vec2 struct { X, Y float32 } //使用向量加上另外一個向量,生成新的向量 func (v Vec2) Add(other Vec2) Vec2 { return Vec2{ v.X + other.X, v.Y + other.Y, } } //使用向量減去另外一個向量,生成新的向量 func (v Vec2) subtraction(other Vec2) Vec2 { return Vec2{ v.X - other.X, v.Y - other.Y, } } //使用向量乘以另外一個向量,生成新的向量 func (v Vec2) Scale(s float32) Vec2 { return Vec2{ v.X * s, v.Y * s, } } //計算兩個向量的距離 func (v Vec2) DistanceTo(other Vec2) float32 { dx := v.X - other.X dy := v.X - other.Y return float32(math.Sqrt(float64(dx*dx + dy*dy))) } //返回當前向量的標準化向量 func (v Vec2) Normalize() Vec2 { mag := v.X*v.X + v.Y*v.Y if mag > 0 { oneOverMag := 1 / float32(math.Sqrt(float64(mag))) //math.Sqrt()開方函式 return Vec2{v.X * oneOverMag, v.Y * oneOverMag} } return Vec2{0, 0} }
2、實現玩家物件
玩家物件負責儲存玩家的當前位置、目標位置和速度。使用 MoveTo() 方法為玩家設定移動的目標,使用 Update() 方法更新玩家位置。在 Update() 方法中,通過一系列的向量計算獲得玩家移動後的新位置,步驟如下:
(1) 使用向量減法,將目標位置(targetPos)減去當前位置(currPos)即可計算出位於兩個位置之間的新向量,如下圖:
(2) 使用 Normalize() 方法將方向向量變為模為 1 的單位化向量。這裡需要將向量單位化後才能進行後續計算,如下圖:
(3) 獲得方向後,將單位化方向向量根據速度進行等比縮放,速度越快,速度數值越大,乘上方向後生成的向量就越大(模很大),如下圖:
(4) 獲得方向後,將單位化方向向量根據速度進行等比縮放,速度越快,速度數值越大,乘上方向後生成的向量就越大(模很大),如下圖:
package main type Player struct { currPos Vec2 //當前位置 targetPos Vec2 //目標位置 speed float32 //移動速度 } //設定玩家移動的目標位置 func (p *Player) MoveTo(v Vec2) { p.targetPos = v } //獲取當前的位置 func (p *Player) Pos() Vec2 { return p.currPos } //判斷是否到達目的地 func (p *Player) IsArrived() bool { //通過計算當前玩家位置與目標位置的距離不超過移動的不長,判斷已經到達目標點 return p.currPos.DistanceTo(p.targetPos) < p.speed } //更新玩家的位置 func (p *Player) Update() { if !p.IsArrived() { //計算出當前位置指向目標位的朝向 dir := p.targetPos.subtraction(p.currPos).Normalize() //新增速度向量生成新的位置 newPos := p.currPos.Add(dir.Scale(p.speed)) //移動完成後,更新當前位置 p.currPos = newPos } } //建立新玩家 func NewPlayer(speed float32) *Player { return &Player{ speed: speed, } }
3、處理移動邏輯
將 Player 例項化後,設定玩家移動的最終目標點。之後開始進行移動的過程,這是一個不斷更新位置的迴圈過程,每次檢測玩家是否靠近目標點附近,如果還沒有到達,則不斷地更新位置,讓玩家朝著目標點不停的修改當前位置,程式碼如下:
package main
import "fmt"
func main() {
//例項化玩家物件,並設速度為 0.5
p := NewPlayer(0.5)
//讓玩家移動到 3,1 點
p.MoveTo(Vec2{3, 1})
//如果沒有到達就一直迴圈
for !p.IsArrived() {
//更新玩家位置
p.Update()
//列印每次移動後的玩家位置
fmt.Println(p.Pos())
}
}