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())
    }
posted @   左扬  阅读(149)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
levels of contents
点击右上角即可分享
微信分享提示