1. 程式人生 > 其它 >Go從入門到精通——結構體(struct)——示例:二維向量模擬玩家移動

Go從入門到精通——結構體(struct)——示例:二維向量模擬玩家移動

示例:二維向量模擬玩家移動

  在遊戲中,一般使用二維向量儲存玩家的位置。使用向量運算可以計算出玩家移動的位置。本例子中,首先實現二維向量物件,接著構造玩家物件,最後使用向量物件和玩家物件共同模擬玩家移動的過程。

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())
	}
}