Go从入门到精通——结构体(struct)——示例:二维矢量模拟玩家移动
示例:二维矢量模拟玩家移动
在游戏中,一般使用二维矢量保存玩家的位置。使用矢量运算可以计算出玩家移动的位置。本例子中,首先实现二维矢量对象,接着构造玩家对象,最后使用矢量对象和玩家对象共同模拟玩家移动的过程。
1、实现二维矢量结构
矢量是数学中的概念,二维矢量拥有两个方向的信息,同时可以进行加、减、乘(缩放)、距离、单位化等计算。
在计算机中,使用拥有 X 和 Y 两个分量的 Vec2 结构体实现数学中二维向量的概念。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | 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) 获得方向后,将单位化方向矢量根据速度进行等比缩放,速度越快,速度数值越大,乘上方向后生成的矢量就越大(模很大),如下图:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | 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 实例化后,设定玩家移动的最终目标点。之后开始进行移动的过程,这是一个不断更新位置的循环过程,每次检测玩家是否靠近目标点附近,如果还没有到达,则不断地更新位置,让玩家朝着目标点不停的修改当前位置,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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()) } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具