手把手教你写蛇蛇大作战(三)

这篇我们的主要实现控制蛇的移动和加速效果

首先我们看看实现的效果图吧
snake

设计思路

要控制蛇的转向 首先要定义蛇当前的角度和需要转向的角度,然后计算蛇是向顺时针还是逆时针转弯。

2d 游戏要实现平滑转向移动需要使用三角函数

首先我们的坐标系,坐下角为原点坐标(0,0) 右边为x的正半轴 向上为y的正半轴 运动可以通过角度计算x和y方向的加速度 这里就要用到高中时候的三角函数了

1
2
vx = sin(angle);
vy = cos(angle);

我们来看下三角函数的正弦和余弦波

x-o-y

有没有很熟悉。

要实现蛇的转向问题,我们需要了解如何从A角度到B角度的最快转向,逆时针还是顺时针。

  • 顺时针从角度A到角度B

  • 逆时针从角度A到角度B

    顺时针: 旋转角度为A-B(A>B时,如从180到90度 需要顺时针旋转90度)或者360+A-B(A<B时,如30度到270度 需要顺时针旋转120度)

    逆时针:旋转角度为B-A(B>A时,如从50度到80度为 逆时针旋转30度)或者360+B-A(B<A时,如从315度到90度时逆时针旋转135度)

所以不论如何,角度A到角度B 顺时针旋转角度为C或者逆时针旋转角度为D ,都有C+D=360度。
所以我们计算|A-B|,如果当|A-B|<180时可以分为两种情况

  • A<B 时,逆时针旋转B-A度
  • A>B 时,顺时针旋转A-B度

如果|A-B|>180度,我们也可以分为两种情况

  • A<B 时,顺时针旋转360+A-B
  • A>B 时, 逆是在旋转360+B-A

如果是|A-B|=180度 那么逆时针和顺时针都一样,都是180度

代码实现

首先我们需要修改Snake.java
添加以下三个属性

1
2
3
private double angle;//蛇当前运动的角度0-360度
private double toAngle;//将要转向的角度
private double turnSpeed = Math.toRadians(2);//转弯速度;

然后修改init()方法

1
2
3
4
5
6
7
8
9
10
11
12
private void init() {
dimension.set(20, 20);
origin.set(dimension.x / 2, dimension.y / 2);
terminalVelocity.set(100, 100);
position.set(50, 120);
rotation = -90;
scale.set(0.7f, 0.7f);
bounds.set(0, 0, dimension.x, dimension.y);
movements = new Array<Movement>();
angle = 0;//当前角度设置为0度 默认向左运行
toAngle = angle;
}

然后修改update()方法

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
@Override
public void update(float deltaTime) {
movements.add(new Movement(new Vector2(position), speed));
if (movements.size > length) {
movements.removeValue(movements.first(), true);
}
angle = (Math.PI * 2 + angle) % (Math.PI * 2);//把负度数转为正度数
if (Math.abs(angle - toAngle) <= turnSpeed) {
toAngle = angle = toAngle % (Math.PI * 2);
} else {
if (Math.abs(angle - toAngle) < Math.PI) { //如果度数差小于 180度
if (angle < toAngle) { //如果转向度数大于当前度数 则 逆时针旋转 toAngle-angle 度数(如 30-80度
angle += turnSpeed;
} else {
angle -= turnSpeed;
}
} else {
if (angle < toAngle) {
angle -= turnSpeed;
} else {
angle += turnSpeed;
}
}

}
angle = angle % (Math.PI * 2);
float vx = (float) (speed * Math.cos(angle));
float vy = (float) (speed * Math.sin(angle));
velocity.set(vx, vy);
rotation = (float) (360 + Math.atan2(velocity.y, velocity.x)/ (Math.PI / 180 - 90);
super.update(deltaTime);
}

然后需要控制方向 添加方法

1
2
3
public void setDirection(double angle) {
toAngle = angle;
}

Snake.java 基本已经完成了
然后我们修改WorldController.java
添加方法

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
private void handleInput(float deltaTime) {
if (Gdx.app.getType() != Application.ApplicationType.Desktop) return;
int[][] angleArray = new int[][]{
{-1, 270, 90},
{180, 225, 135},
{0, 315, 45}
};//定义二维数组分别代表 上下左右 点击的角度
int i = 0, j = 0;
if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
i = 1;
} else if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
i = 2;
}

if (Gdx.input.isKeyPressed(Input.Keys.UP)) {
j = 2;
} else if (Gdx.input.isKeyPressed(Input.Keys.DOWN)) {
j = 1;
}
double angle = angleArray[i][j];
if (angle == -1) {
return;
}
snake.setDirection(Math.toRadians(angle));
}

然后我们修改WorldController中的update()方法

1
2
3
4
public void update(float deltaTime) {
handleInput(deltaTime);
snake.update(deltaTime);
}

##实现加速
Snake.java 添加两个属性

1
2
private boolean isSpeedUp; // 加速
private float oldSpeed;

然后添加加速方法和减速方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 加速
*/
public void speedUp() {
if (isSpeedUp) return;
isSpeedUp = true;
oldSpeed = speed;
speed *= 2;
}

public void speedDown() {
if (!isSpeedUp) return;
isSpeedUp = false;
this.speed = oldSpeed;
}

然后修改WorldController.java添加空格键监听 点击空格键加速,放开减速

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public boolean keyDown(int keycode) {
switch (keycode) {
case Input.Keys.SPACE:
snake.speedUp();
break;
}
return true;
}

@Override
public boolean keyUp(int keycode) {
switch (keycode) {
case Input.Keys.SPACE:
snake.speedDown();
break;
}
return true;
}

然后就可以运行查看效果了。

代码已经放在GITHUB
可以切换到tag2 来查看本篇文章代码

使用 git checkout tag2

文章作者: zhangman523
文章链接: http://blog.zhangman523.cn/2018/09/21/snake_thrid/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 zhangman523
支付宝打赏
微信打赏