大一假期几天的实训认识课,用 C++ 做了两个小游戏,一个是推箱子,一个是贪吃蛇,让我对编程实现二维游戏有了点基础认识,下面是贪吃蛇游戏的一点简要认识。
运行环境:vs2012
用到结构体,枚举enum, if条件语句等
首先,我们要用到地图Map当然指的是游戏地图,我们使用0,1来构造,1为墙(障碍物),0为可通过空间,大小随心,如图:
当然,0,1也用于后面的墙的图案定义以及蛇的撞墙判定,这里方块墙(也可以其它图案)我们使用搜狗输入法里的符号大全:
enum用来定义几个选项,如游戏菜单界面,游戏控制区,游戏输赢判定,enum枚举c++学习过程中大家都学过,例如:
enum enumType {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
枚举量Monday、Tuesday等的值默认分别为0-6,我们可以显式的设置枚举量的值:
enum enumType {Monday=1, Tuesday=2, Wednesday=3, Thursday=4, Friday=5, Saturday=6, Sunday=7};但指定的值必须是整数。
还有结构体struct:结构体可以将原本意义属于一个整体的数据组合在一起。
唯一有一个新知识:按键控制
函数GetAsyncKeyState确定在调用它时某个按键处于弹起还是按下的,以及此按键是否在上一次调用GetAsyncKeyState之后(“又”)按下过(重复也算按下)。
这句话的完整意思是:预定义了一个KEYDOWN参数为vk_code 他的定义的含义是判断一个键是否被按下(GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0如果按下了就是1,没有按下就是0
然后其它地方用的时候直接用KEYDOWN(vk_code)判断这个键是否按下,相反弹起来是1按下是0。
清屏函数:system(“cls”);执行c++的一些比较大的程序时,可能会需要执行很多功能,但一次一次功能执行过后,会留下以前的字符占据着屏幕,看起来特别乱,也影响接下来的功能调用,这个函数可以在每执行完一次功能,选择回到一开始的初始界面。
作用是清除程序先前显示的内容,但不会清除先前调用程序功能而实现的对内存的改变.而由于是清除先前显示的内容,所以对此函数的写入位置要考虑一下,避免连初始界面也给清空了。
若想调用此函数,在程序代码的头文件上,要加上#include <stdlib.h> 才可以使用。
我们把它放在循环中,最合适不过,不断把循环出的界面清屏,利用视觉暂留,造成动画效果。
延时函数:Sleep(1);//延时函数,减慢蛇的速度,便于控制,我的代码只有一关,所以直接自己随意改时间,觉得若是好多关卡,可以写一个函数,每关改变括号内的数值,改变游戏难度。
要加上头文件 #include<time.h>
最后就是不知为何,笔记本运行会出现好多莫名其妙的点闪啊闪,在学校机房运行却没有。
运行部分截图如下:
菜单界面:
游戏开始界面:
游戏中界面:这张截图考手速
撞墙之后:
食物必须随机出现,所以使用随机函数生成食物坐标,蛇头控制,蛇的移动是核心,还有蛇的长度增加,蛇移动时身体要跟着蛇头,所以我们总是要备份前一个点的位置,将其赋给后一点,实现蛇的移动,还有用判断语句实现吃完食物蛇的长度加一。
接下来附上代码(含注释):
// GREED Snake.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <Windows.h>
#include <iostream>
#include<time.h>//蛇走的太快,利用此头文件的函数让他慢下来
using namespace std;
#define KEY_DOWN(vk_code) GetAsyncKeyState(vk_code)&0x8000?1:0
int g_arrMap1[15][20]={
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};
enum
{
E_MENU_START,
E_MENU_SETTING,
E_MENU_EXIT
};
enum
{
E_GAME_MENU,
E_GAME_MAP,
E_GAME_WIN,
E_GAME_LOSE
};
enum
{
E_DIR_NONE,
E_DIR_DOWN,
E_DIR_UP,
E_DIR_LEFT,
E_DIR_RIGHT
};
struct SData
{
SData()
{
nRow = 0;
nCol = 0;
nRowBk = 0;
nColBk = 0;
}
SData(int nInRow,int nInCol)
{
nRow = nInRow;
nCol = nInCol;
nRowBk = nInRow;
nColBk = nInCol;
}
void backUp()//备份函数
{
nRowBk = nRow;
nColBk = nCol;
}
void restore()//坐标释放函数
{
nRow = nRowBk;
nCol = nColBk;
}
int nRow;
int nCol;
int nRowBk;
int nColBk;
};
int _tmain(int argc, _TCHAR* argv[])
{ Sleep(1);//延时函数,减慢蛇的速度
int nMenuState = E_MENU_START;//菜单状态
int nGameState = E_GAME_MENU;//游戏状态
//定义蛇:
SData arrSnake[15]={};
//蛇头坐标初始化
arrSnake[0] = SData(2,2);
//蛇长度初始化
int nlength = 1;
int foodnumber=1;
//定义蛇头的移动方向,
int nDir = E_DIR_NONE;
//食物坐标
SData sFood = SData( rand()%13+1, rand()%18+1) ; //食物随机出现
while(true)
{
system("cls");
if(E_GAME_MENU == nGameState)
{
if(KEY_DOWN(VK_RETURN))//菜单数据更新按下enter键进入游戏地图
{
if(E_MENU_START == nMenuState)/*按下enter键时要判断当前箭头是否指示在游戏开始的位置
判断nMenuState的值是否是0,是则切换,不是则不管*/
{nGameState = E_GAME_MAP;}
else if(E_MENU_EXIT==nMenuState)//退出程序运行框
{
exit(0);
}
}
if(KEY_DOWN(VK_UP))
{
nMenuState--;
if(nMenuState<E_MENU_START)
{
nMenuState = E_MENU_EXIT;
}
}
else if(KEY_DOWN(VK_DOWN))
{
nMenuState++;
if(nMenuState > E_MENU_EXIT)
{
nMenuState = E_MENU_START;
}
}
//菜单渲染
if(E_MENU_START == nMenuState)
{
cout <<"->游戏开始"<< endl;
cout <<" 游戏设置"<< endl;
cout <<" 游戏结束"<< endl;
}
else if(E_MENU_SETTING == nMenuState)
{
cout <<" 游戏开始"<< endl;
cout <<"->游戏设置"<< endl;
cout <<" 游戏结束"<< endl;
}
else if(E_MENU_EXIT == nMenuState)
{
cout <<" 游戏开始"<< endl;
cout <<" 游戏设置"<< endl;
cout <<"->游戏结束"<< endl;
}
}
//切地图
else if(nGameState == E_GAME_MAP)
{
if(KEY_DOWN(VK_ESCAPE))//按下esc键返回菜单
{
nGameState = E_GAME_MENU;
}
//蛇头的坐标的备份
arrSnake[0].nRowBk = arrSnake[0].nRow;
arrSnake[0].nColBk = arrSnake[0].nCol;
//控制蛇头的自动
if(KEY_DOWN(VK_UP))
{
nDir = E_DIR_UP;
}
if(KEY_DOWN(VK_DOWN))
{
nDir = E_DIR_DOWN;
}
if(KEY_DOWN(VK_LEFT))
{
nDir = E_DIR_LEFT;
}
if(KEY_DOWN(VK_RIGHT))
{
nDir = E_DIR_RIGHT;
}
if(nDir == E_DIR_UP)
{
arrSnake[0].nRow--;
}
if(nDir == E_DIR_DOWN)
{
arrSnake[0].nRow++;
}
if(nDir == E_DIR_LEFT)
{
arrSnake[0].nCol--;
}
if(nDir == E_DIR_RIGHT)
{
arrSnake[0].nCol++;
}
//吃食物
if(arrSnake[0].nRow == sFood.nRow && arrSnake[0].nCol == sFood.nCol)
{
nlength++;
sFood = SData(rand()%13+1 ,rand()%18+1);
//foodnumber++;
}
//身体跟随蛇头
for(int i = 1;i < nlength;i++)
{
//备份当前这一节坐标
arrSnake[i].backUp();
//arrSnake[i].nRowBk = arrSnake[i].nRow;
//arrSnake[i].nColBk = arrSnake[i].nCol;
//把这一节的坐标给下一节
arrSnake[i].nRow = arrSnake[i-1].nRowBk;
arrSnake[i].nCol = arrSnake[i-1].nColBk;
//在这一节坐标中输出
}
//游戏胜负的判断
//撞墙则失败
if(1 == g_arrMap1[arrSnake[0].nRow][arrSnake[0].nCol] )
{
nGameState = E_GAME_LOSE;
}
for(int i=1;i<15;i++)
{
if(arrSnake[0].nRow == arrSnake[1].nRow && arrSnake[0].nCol == arrSnake[1].nCol)
{
nGameState = E_GAME_LOSE;
break;
}
}
//地图渲染
for(int i=0;i<15;i++)
{
for(int j=0;j<20;j++)
{
bool bDrawSnake = false;
for(int m=0; m<15; m++)
{
if(i == arrSnake[m].nRow && j == arrSnake[m].nCol)
{
bDrawSnake = true;
break;
}
}
if(1 == g_arrMap1[i][j])
{
cout<<"■";
}
else if(bDrawSnake)
{
cout<<"□";
}
else if(i == sFood.nRow && j == sFood.nCol) //输出食物的位置
{
cout<<"●";
}
else
{
cout<<" ";
}
}
cout<<endl;
}
}
else if ( nGameState == E_GAME_LOSE )
{
if(KEY_DOWN(VK_ESCAPE))
{
nGameState = E_GAME_MENU; //按向下键 返回游戏菜单
}
cout<<" 很遗憾,你输了这场游戏!"<<endl;
cout<<"按Esc键退至菜单并进行新游戏吧!";
}
}
return 0;
}
假期实训认识之贪吃蛇。其实一开始不会蛇头自动走时,是仿照推箱子写的手动贪吃蛇,牵着蛇鼻子走,哈哈。