一、First of ALL
学习完了EGE库的所有函数后,今天我们就来综合实践一下。
今天我们将写一个数字华容道来简单练一练手。
准备好了吗,让我们开始。
二、材料准备
1.音乐
我们可以为我们的游戏增加音乐,这里我找了一首MP3来做背景音乐。
2.按键侦测
虽然ege库内有自带的按键侦测函数,但我还是习惯使用windows.h库里的按键侦测函数。
#define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000)? 1 : 0)
例如,KEY_DOWN(VK_RETURN)会侦测在这一时刻,你是否按下了回车键。
三、代码实现
1.main函数
那么首先,我们得让画面看起来是这样的
首先就是initgraph和setbkcolor,这一部分很简单,不用多说。
initgraph(640, 480);
setcaption(L"数字华容道"); //设置标题
setbkcolor(WHITE); //背景颜色
MUSIC mus; //音乐初始化
mus.OpenFile(L"Piano.mp3"); //打开音乐
mus.Play(); //播放音乐
然后就是初始化地图,绘制窗口。里面有些函数我们等一下会讲。
init_map(); //这些函数等一下会讲
draw_map();
draw_number();
setfont(20, 10, L"Lucida Console");//字体
outtext(L"按N静音"); //提示信息
outtextxy(0, 20, L"按M重置");
outtextxy(0, 40, L"按B退出");
然后就开始游戏循环了。
for (;;) {
move_map(); //移动数字后
draw_number(); //重新绘制地图(数字)
if (KEY_DOWN('N')) {
if (mus_state == 0) {
mus_state = 1; //muw_state:记录音乐在播放还是停止
mus.Pause(); //如果在播放时按下N,停止。
} else {
mus_state = 0; //下同
mus.Play();
}
}
if (KEY_DOWN('M')) { //M代表重开,再更新一遍地图
init_map();
draw_map();
draw_number();
}
if (KEY_DOWN('B')) { //B则是结束
return 0;
}
if (is_win()) { //如果胜利了就退出
break;
}
if (mus.GetLength() == mus.GetPosition()) {
mus.Close();
mus.OpenFile(L"Piano.mp3");
mus.Play();
} //如果音乐播完了再播一次
Sleep(100); //延迟
}
最后如果胜利了,输出提示信息,结束。
win();
closegraph();
return 0;
main函数:
int main() {
initgraph(640, 480);
setcaption(L"数字华容道");
setbkcolor(WHITE);
MUSIC mus;
mus.OpenFile(L"Piano.mp3");
mus.Play();
init_map();
draw_map();
draw_number();
setfont(20, 10, L"Lucida Console");
outtext(L"按N静音");
outtextxy(0, 20, L"按M重置");
outtextxy(0, 40, L"按B退出");
for (;;) {
move_map();
draw_number();
if (KEY_DOWN('N')) {
if (mus_state == 0) {
mus_state = 1;
mus.Pause();
} else {
mus_state = 0;
mus.Play();
}
}
if (KEY_DOWN('M')) {
init_map();
draw_map();
draw_number();
}
if (KEY_DOWN('B')) {
return 0;
}
if (is_win()) {
break;
}
if (mus.GetLength() == mus.GetPosition()) {
mus.Close();
mus.OpenFile(L"Piano.mp3");
mus.Play();
}
Sleep(100);
}
win();
closegraph();
return 0;
}
2.position结构体
这个结构体将记录每个数字的位置。
typedef struct {
int x;
int y;
} position; //不是坐标轴的x,y,而是行和列
3.initmap函数
这个函数将初始化地图(也就是3×3的矩阵),具体操作见下。
void init_map() {
bool b[9] = {0}; //记录每个数字是否已被选中过
randomize(); //初始化随机数
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
do {
MAP[i][j] = random(9); //取随机数
} while (b[MAP[i][j]] != 0); //如果这个数已经在矩阵里就再取一遍
b[MAP[i][j]] = 1; //标记这个数取过了
} //这样就保证里面的数是012345678而不会有
//两个2这样的事情发生。
}
if (is_win()) init_map(); //如果出来的矩阵已经是排列好的,就再init一遍
}
4.draw_map函数
init完之后就应该绘制框框来填数字了。
void draw_map() {
int x[9] = {200, 280, 360, 200, 280, 360, 200, 280, 360};//记录每个框框的x
int y[9] = {120, 120, 120, 200, 200, 200, 280, 280, 280};//记录每个框框的y
setcolor(BLACK); //设置颜色
for (int i = 0; i < 9; i++) {
rectangle(x[i], y[i], x[i] + 80, y[i] + 80); //绘制框框,长宽各为80
}
}
这样,我们就得到了3×3的一个矩形了。
5.draw_number函数
绘制好框框后,就该往里面填数字了。
void draw_number() {
int x[9] = {200, 280, 360, 200, 280, 360, 200, 280, 360};//数字的x坐标
int y[9] = {120, 120, 120, 200, 200, 200, 280, 280, 280};//数字的y坐标
setfont(70, 35, L"Lucida Console");//字体
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (MAP[i][j] != 0) print_score(MAP[i][j], x[i * 3 + j] + 2, y[i * 3 + j] + 2);
//如果不是0就在框框内写入数字。
else outtextxy(x[i * 3 + j] + 2, y[i * 3 + j] + 2, L" ");
//如果是0就代表这里是空白
}
}
}
6.move_map函数
这个函数就是我们操作的核心了。
void move_map() {
position p;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (MAP[i][j] == 0) { //记录哪个地方是空白的
p.x = i;
p.y = j;
}
}
}
if (KEY_DOWN(VK_RIGHT)) { //如果按下右键就让空白处左边的数移到空白处
if (p.y != 0) { //而且空白处左边必须有数(y坐标不为0)
swap(MAP[p.x][p.y], MAP[p.x][p.y - 1]);//移动(交换)
}
}
if (KEY_DOWN(VK_LEFT)) {
if (p.y != 2) { //下同
swap(MAP[p.x][p.y], MAP[p.x][p.y + 1]);
}
}
if (KEY_DOWN(VK_DOWN)) {
if (p.x != 0) {
swap(MAP[p.x][p.y], MAP[p.x - 1][p.y]);
}
}
if (KEY_DOWN(VK_UP)) {
if (p.x != 2) {
swap(MAP[p.x][p.y], MAP[p.x + 1][p.y]);
}
}
}
7.is_win函数
判断是否已经胜利。
bool is_win() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (MAP[i][j] != i * 3 + j + 1 && MAP[i][j] != 0) return 0;//如果这个数字不在它该在的位置上的话返回0
}
}
return 1;
}
8.win函数
当判断胜利后,我们要输出生胜利信息.
void win() {
cleardevice();
setfont(80, 40, L"Lucida Console");
outtextxy(200, 200, L"WIN!!!");
Sleep(1000);
getch();
}
这个就没什么好说的了。
9.int_to_string函数
将int转换为string,也没什么好说的。
string DX(string s) {
string res = "";
for (int i = s.size() - 1; i >= 0; i--)
res += s[i];
return res;
}
string int_to_string(int i) {
string res = "";
while (i % 10 != i) {
res += i % 10 + '0';
i /= 10;
}
res += i + '0';
return DX(res);
}
10.print_score函数
前面的int_to_string就用在这里了,输出数字。
void print_score(int score, int x = 0, int y = 0) { //x,y:坐标
string s = int_to_string(score); //将score转为string型
for (int i = 0; i < s.size(); i++) {
switch (s[i]) { //输出第i位
case '0':
outtextxy(x + 10 * i, y, L"0");
break;
case '1':
outtextxy(x + 10 * i, y, L"1");
break;
case '2':
outtextxy(x + 10 * i, y, L"2");
break;
case '3':
outtextxy(x + 10 * i, y, L"3");
break;
case '4':
outtextxy(x + 10 * i, y, L"4");
break;
case '5':
outtextxy(x + 10 * i, y, L"5");
break;
case '6':
outtextxy(x + 10 * i, y, L"6");
break;
case '7':
outtextxy(x + 10 * i, y, L"7");
break;
case '8':
outtextxy(x + 10 * i, y, L"8");
break;
case '9':
outtextxy(x + 10 * i, y, L"9");
break;
default:
outtextxy(x + 10 * i, y, L"Error"); //如果不是0123456789中的一个就报错
break;
}
}
}
这个函数是在draw_number里用到的,用来输出数字。
四、结束
那么现在我们的数字华容道就写完了,下面是代码:
#include<graphics.h>
#include<bits/stdc++.h>
#define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000)? 1 : 0)
using namespace std;
int MAP[3][3];
bool mus_state = 0;
typedef struct {
int x;
int y;
} position; //不是坐标轴的x,y,而是行和列
string DX(string s) {
string res = "";
for (int i = s.size() - 1; i >= 0; i--)
res += s[i];
return res;
}
string int_to_string(int i) {
string res = "";
while (i % 10 != i) {
res += i % 10 + '0';
i /= 10;
}
res += i + '0';
return DX(res);
}
void print_score(int score, int x = 0, int y = 0) {
string s = int_to_string(score);
for (int i = 0; i < s.size(); i++) {
switch (s[i]) {
case '0':
outtextxy(x + 10 * i, y, L"0");
break;
case '1':
outtextxy(x + 10 * i, y, L"1");
break;
case '2':
outtextxy(x + 10 * i, y, L"2");
break;
case '3':
outtextxy(x + 10 * i, y, L"3");
break;
case '4':
outtextxy(x + 10 * i, y, L"4");
break;
case '5':
outtextxy(x + 10 * i, y, L"5");
break;
case '6':
outtextxy(x + 10 * i, y, L"6");
break;
case '7':
outtextxy(x + 10 * i, y, L"7");
break;
case '8':
outtextxy(x + 10 * i, y, L"8");
break;
case '9':
outtextxy(x + 10 * i, y, L"9");
break;
default:
outtextxy(x + 10 * i, y, L"Error");
break;
}
}
}
bool is_win();
void init_map() {
bool b[9] = {0};
randomize();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
do {
MAP[i][j] = random(9);
} while (b[MAP[i][j]] != 0);
b[MAP[i][j]] = 1;
}
}
if (is_win()) init_map();
}
void draw_map() {
int x[9] = {200, 280, 360, 200, 280, 360, 200, 280, 360};
int y[9] = {120, 120, 120, 200, 200, 200, 280, 280, 280};
setcolor(BLACK);
for (int i = 0; i < 9; i++) {
rectangle(x[i], y[i], x[i] + 80, y[i] + 80);
}
}
void draw_number() {
int x[9] = {200, 280, 360, 200, 280, 360, 200, 280, 360};
int y[9] = {120, 120, 120, 200, 200, 200, 280, 280, 280};
setfont(70, 35, L"Lucida Console");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (MAP[i][j] != 0) print_score(MAP[i][j], x[i * 3 + j] + 2, y[i * 3 + j] + 2);
else outtextxy(x[i * 3 + j] + 2, y[i * 3 + j] + 2, L" ");
}
}
}
void move_map() {
position p;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (MAP[i][j] == 0) {
p.x = i;
p.y = j;
}
}
}
if (KEY_DOWN(VK_RIGHT)) {
if (p.y != 0) {
swap(MAP[p.x][p.y], MAP[p.x][p.y - 1]);
}
}
if (KEY_DOWN(VK_LEFT)) {
if (p.y != 2) {
swap(MAP[p.x][p.y], MAP[p.x][p.y + 1]);
}
}
if (KEY_DOWN(VK_DOWN)) {
if (p.x != 0) {
swap(MAP[p.x][p.y], MAP[p.x - 1][p.y]);
}
}
if (KEY_DOWN(VK_UP)) {
if (p.x != 2) {
swap(MAP[p.x][p.y], MAP[p.x + 1][p.y]);
}
}
}
bool is_win() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (MAP[i][j] != i * 3 + j + 1 && MAP[i][j] != 0) return 0;
}
}
return 1;
}
void win() {
cleardevice();
setfont(80, 40, L"Lucida Console");
outtextxy(200, 200, L"WIN!!!");
Sleep(1000);
getch();
}
int main() {
initgraph(640, 480);
setcaption(L"数字华容道");
setbkcolor(WHITE);
MUSIC mus;
mus.OpenFile(L"Piano.mp3");
mus.Play();
init_map();
draw_map();
draw_number();
setfont(20, 10, L"Lucida Console");
outtext(L"按N静音");
outtextxy(0, 20, L"按M重置");
outtextxy(0, 40, L"按B退出");
for (;;) {
move_map();
draw_number();
if (KEY_DOWN('N')) {
if (mus_state == 0) {
mus_state = 1;
mus.Pause();
} else {
mus_state = 0;
mus.Play();
}
}
if (KEY_DOWN('M')) {
init_map();
draw_map();
draw_number();
}
if (KEY_DOWN('B')) {
return 0;
}
if (is_win()) {
break;
}
if (mus.GetLength() == mus.GetPosition()) {
mus.Close();
mus.OpenFile(L"Piano.mp3");
mus.Play();
}
Sleep(100);
}
win();
closegraph();
return 0;
}
五、程序下载
今天写的游戏和音乐我都会放到这里,需要的可以下载。
六、Conclusion
今天我们用ege库写出了我们的第一个游戏,下期,我们将会用ege库写出更好的游戏。
敬请期待!