golang 二维矢量模拟玩家移动
今天拜读徐波老师的《golang从入门到实践》书籍中看到一个小例子,使用golang来实现二维矢量模拟玩家移动。其中介绍在游戏中,一般使用二维矢量保存玩家的位置 。 使用矢量运算计算玩家移动的位置。 觉得很有趣,和大家分享一下。
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) Sub(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.Y - 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))) return Vec2{v.X * oneOverMag, v.Y * oneOverMag} } return Vec2{0, 0} }
2 .实现玩家对象
(1) 使用矢量减法,将目标位置( targetPos )减去当前位置( currPos )即可计算出位于两个位置之间的新矢量。
(2)使用 Nonnalize() 方法将方向矢量变为模(长度)为1的单位化矢量。 这里需要将矢量单位化后才能进行后续计算。
(3)获得方向后,将单位化方向矢量根据速度进行等比缩放,速度越快,速度数值越大,乘上方向后生成的矢量就越长(模很大)。
(4) 将缩放后的方向 添加到 当前位置后形成新的位置 。
// 玩家 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.Sub(p.currPos).Normalize() // 添加速度矢量生成新的位置 newPos := p.currPos.Add(dir.Scale(p.speed)) // 移动完成后,更新当前位置 p.currPos = newPos } } // 创建新玩家 func NewPlayer(speed float32) *Player { return &Player{ speed: speed, } } func main() { p := NewPlayer(0.5) p.MoveTo(Vec2{3, 5}) for !p.IsArrived() { p.Update() fmt.Println(p.Pos()) } }
运行结果:
{0.2572479 0.4287465}
{0.5144958 0.8574929}
{0.77174366 1.2862394}
{1.0289915 1.7149858}
{1.2862394 2.1437323}
{1.5434873 2.5724788}
{1.8007352 3.0012252}
{2.0579832 3.4299717}
{2.315231 3.8587182}
{2.572479 4.2874646}
{2.8297267 4.7162113}
总结:
首先我们实现一个矢量结构 Vec2 来保存玩家的位置坐标,并为这个结构定义了加、减、乘、单位化方法。使用这一系列矢量运算计算出玩家移动的位置。
然后我们实现一个玩家对象,定义玩家的当前位置 、目标位置和速度属性,为这个对象实现操作她的方法,比如设置目的地,更新位置等来模拟玩家的移动。
在更新玩家位置逻辑中计算当前位置指向目标的朝向时,先将两矢量相减获得指向被减矢量的新矢量。然后将新矢量使用 Normalize()方法单位化 。最终返回的 dir 矢量就是移动方向(因为单位化后模为1)。这个dir矢量和速度属性配合计算玩家移动后的新位置。
在IsArrived方法中判断玩家是否到达目标点。玩家每次移动的半径就是速度( speed ),因此,如果当前位置与目标点的距离小于速度,表示己经非常靠近目标,可以视为到达目标。