游戏开发——让我们从反弹球开始

咳咳,新博客建立后的第一篇学习笔记,反弹球。(图文无关)


前置知识:程序的三大基本结构,常量,变量,基本的运算符和表达式,函数(C语言)

可选的前置知识:二维数组

由于本节内容不需要使用easyX,所以画面会显示在控制台上,小球我就用字符“O”代替了。

屏幕坐标

我们要让一个小球在一个给定的平面上弹跳,第一步是什么?

当然是要创造一个平面啊……在控制台上,这个平面的“建立”就可以由一个二重for循环来完成。两个循环变量i和j,走到哪个地方,这里的“坐标”就可以由相应的i,j的值来代替。看一下这张图片,相信您就能大致的明白小球的坐标应该如何表示了。

可能有人会问,我们要坐标干什么?其实,小球在平面上的移动,本质上是小球坐标的改变,我们只需要改变小球的坐标,就可以改变小球的位置。

这个所谓的“坐标系”,严格来说并不可以称之为坐标系,只能说它可以方便我们描述物体在屏幕上的位置。

思考:坐标原点在哪?

· 打印一个静止的小球

我们使用printf()函数打印小球,利用for循环找到小球的位置。

下面给出一个简单的代码示例:

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
const int x = 15;
const int y = 20;
int main() {
for (int i = 0; i < x; i++)
printf("\n");
for (int j = 0; j < y; j++)
printf(" ");
printf("O\n");
return 0;
}

把它改成一个二重循环也可以,但是要通过if else来换行,不如这样实现的简洁一些。执行程序,发现在一个大黑框中出现了一个静止的小球(字母O),就算大功告成。更改x和y的值,就可以改变小球的位置。

当然,如果使用二维数组会让更简单。想一想,怎么做?

让小球动起来

我们已经有了一个静止的小球,现在我们要尝试让这个球上下弹跳。

(暂时不考虑落体和弹跳的物理规律)
先来考虑一下,要让小球一直不停地跳,要怎么办?使用for循环只能显示小球静止,那要让小球动起来,要怎么办?

一个显然的想法是一直不停地使用for循环。可以在打印的for循环之外再加一层管理“时间”的循环。可是那样的程序小球也只能跳动一定时间。

想让小球一直跳动,外面得加一层“死循环”——while(1)。

与此同时,需要注意的是,使用for循环打印换行符或者空格后,那些位置已经有字符存在了,如果直接去打印下一帧的小球肯定是不行的,此时我们需要一个清除屏幕的操作,只需要把以前打印的符号全部删除,然后重新画就可以了。

放心,电脑运行速度很快。这个操作听起来麻烦,但是电脑却可以在几乎一瞬间完成。可以使用位于stdlib.h里的system函数来清屏,只需调用system(“cls”);即可。

好,下面解决最主要的问题,处理下落,上升和碰撞。

我们先考虑单独处理下落。由坐标变化可以得知,下落的本质是x坐标的增加(想一想,为什么)。那么在外层管理“时间”的循环只需要写成管理x的循环就可以啦,每次循环x都要增加,小球看起来就像是“下落的”。

hint:只需要将上一份示例代码的打印部分外面套一层x递增的循环,然后在里面调用一下清屏函数就可以了。

下面,我们要让小球在这个平面的一条直线上来回运动。既然考虑到碰撞,碰撞所需要的边界就是我们需要考虑的问题。需要设置一个“地板”和一个“天花板”,也就是下界和上界。这一点很好实现,设置好常量即可。

再来想下一个问题,如何处理碰撞?我们不考虑现实的物理规律,只需要知道,当球碰到地板或者天花板时,速度的方向应该和原来的方向相反,大小是不需要改变的。(实际碰撞并非如此,我们只是简化了一下)

这里牵扯到了速度,我们设速度为v就好。
那么,坐标的变化,就可以由一个公式来表达。
设原来的x坐标是x old,该时刻变化后的坐标是x new,那么有:

x new = x old + v

可能有人会说,这个公式是错误的。为什么?你怎么能直接把坐标和速度相加呢?实际上,这里的“速度”是已经“乘”上了“时间”的。换句话说,这里的v虽然我们设成了“速度”,但它实际上表示的是坐标的改变量,也算作是坐标。这个公式是在死循环下使用的,也就是说每执行一次循环都会使用一次这个公式,这样x就可以不断改变了。

但是,v并不是保持不变的。当小球触碰天花板或地板时,根据我们简化的碰撞规律,速度要变为反向。那么如何体现速度为反向呢?学过高中物理的同学应该知道,速度是一个矢量,它既有大小又有方向。表示速度时所带的正负号不参与比较大小,只代表速度的方向是与给定的正方向相同还是相反。所以,要让速度反向,取一个相反数就好啦。

下面给出一个简单的代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>
#define Top 20
#define Bottom 0
int x = 5; //这里就不能加const了
const int y = 10;
int v = 1;
int main() {
while (1) {
x = x + v;
system("cls");

for (int i = 0; i < x; i++)
printf("\n");
for (int j = 0; j < y; j++)
printf(" ");
printf("O\n");

if (x == Top || x == Bottom)
v = -v;

}
return 0;
}

运行该程序,会发现有一个小球以超级快的速度上下运动。如果想让它慢下来,可以使用Sleep()函数。这个函数可以让程序在执行至某位置时暂停一定时间,该时间由函数的参数决定,参数的单位是毫秒。这个函数在windows.h里,使用函数时不要忘了#include <windows.h>。

斜向弹跳

其实和在一条直线上来回弹跳是一样的,把上下的速度设为vx,左右的速度设为vy就可以。实现方式和上文所述完全相同。设定边界的时候除了Top和Bottom外还要加上左边和右边的“墙”——Left和Right。

代码实现留给读者您来完成。

绘制边框和添加撞击音效

绘制边框在打印换行时一并完成。此时打印部分需要做少许修改。

我们不能只打印到球为止,而是应该要把整个画布打印出来。所以此时需要二重循环+if判断再输出的结构。

我们来看一下代码:

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
   
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#define Top 20
#define Bottom 0
#define Left 0
#define Right 30
int x = 5;
int y = 10;
int vx = 1;
int vy = 1;
int main() {
while (1) {
x = x + vx;
y = y + vy;
system("cls");

for (int i = Bottom; i < Top; i++) {
for (int j = Left; j < Right; j++) {
if ((i == Bottom) || (i == Top - 1))
printf("-");
else if ((j == Left) || (j == Right - 1))
printf("|");
else if ((i == x) && (j == y))
printf("O");
else
printf(" ");
//没有东西的地方一定要打印空格,不能什么也不做,这点一定注意
}
printf("\n");
}

if (x == Top - 1 || x == Bottom + 1)
vx = -vx;
if (y == Left + 1 || y == Right - 1)
vy = -vy;

Sleep(100);
}
return 0;
}

可以看出,在打印部分使用了if else结构来判断到某个位置应该打印什么。有个比较细节的地方需要说一下。在打印后的碰撞检测里,我们检测的边界不再是Top、Bottom、Left、Right,而是Top - 1、Bottom + 1、Left + 1、Right - 1,这是为什么呢?很简单,本来要检测的边界变成了边框,小球的活动范围就缩小了一圈,不然会出现小球进墙的bug。

碰撞音效?其实就是printf(“\a”)啦。在碰撞检测成功后,变换速度的同时加一句这个就可以啦。这其实是系统的提示声,如果想播放其他声音……emmmm后续会说到的。


-------------本文结束,感谢您的阅读转载请注明原作者及出处-------------


本文标题:游戏开发——让我们从反弹球开始

文章作者:Shawn Zhou

发布时间:2018年08月02日 - 10:08

最后更新:2018年12月09日 - 13:12

原始链接:http://yoursite.com/2018/08/02/游戏开发——让我们从反弹球开始/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

知识无价,码字不易。对您有用,敬请打赏。金额随意,感谢关心。