小游戏就以飞机大战为例吧
首先说一下我对游戏的理解,游戏主要就是由动画、交互两大部分组成,
而动画则是由一帧一帧的图片组成。
所以要想在控制台中实现动画,那就要实现屏幕的刷新,就是把上一帧
的图片给清除掉,清除的方法有很多,在这里就用最简单的实现方法
system(“cls”);
首先实现屏幕的自动刷新
while (1)
{
Show(); //屏幕显示画面
InputWithData(); //执行与用户有关的数据
InputWithoutData(); //执行与用户无关的数据
system("cls"); //清屏函数,每次调用都将
} //上次的画面清除
将整个程序的主要函数放在一个死循环中,不停的调用结合清屏函数, 实现动画的显示
InputWithData();
InputWithoutData();
这两个函数后序再添加内容
显示画面
也即是Show()里面的内容
for (int i = 0; i < Heigh; i++) //Height 为定义的高度
{
for (int j = 0; j < Width; j++) //Width 宽度
{ //PlayerPos为玩家坐标 X、Y轴
if (i == PlayerPosY&&j == PlayerPosX)
{
cout << "*";
}
{
cout << " ";
}
}
cout << endl;
}
以空格打印一个界面,在打印空格时,当横纵坐标等于玩家坐标时则打印* (这个就暂且当做玩家吧。。。)
输出之后是这样的一个界面
然后就是和用户的交互了
1.首先是实现用户的移动
直接上代码
void InputWithData()
{
Movement();
}
void Movement(){
char input;
if (_kbhit())
{
input = _getch();
switch (input)
{
case'W':
case'w':
PlayPosY--;
break;
case'S':
case's':
PlayPosY++;
break;
case'A':
case'a':
PlayPosX--;
break;
case'D':
case'd':
PlayPosX++;
break;
default:
break;
}
}
}
通过按 WASD 键来改变玩家坐标的数值,进而实现飞机的移动。
_getch()这个函数是一个不回显函数,当用户按下某个字符时,
函数自动读取,无需按回车。
_kbhit()这个是用来检测用户是否按下键盘的函数,当有敌机移动时,
没有这个函数画面将会暂停等待你的输入了,显然这不是我们想要的
2.接下来就是玩家发射子弹了
空格键的ASCII值是 32
还是直接贴代码
//将BulletX,BulletY初始化为-1,-1 使在屏幕不可见
//当发射子弹时再执行下列代码
case 32:
BulletX = PlayPosX;
BulletY = PlayPosY - 1;
isShoot = true;
break;
发射子弹无非就是在上面移动代码里添加个空格触发事件(也可直接看到下述优化后的子弹代码)
for (int i = 0; i < Heigh; i++)
{
for (int j = 0; j < Width; j++)
{
if (i == PlayPosY&&j == PlayPosX)
{
cout << "*";
}
/
else if (i == BulletY && j == BulletX)
{
cout << "|";
}
///
else
{
cout << " ";
}
}
cout << endl;
}
这是Show()函数,仔细观察多了个上面标注的地方,用于子弹的显示,现在只是将子弹放在玩家前面,而没有发射移动,可以这样实现
void BulletMove()
{
if (BulletY > 0)
{
BulletY--;
}
}
将BulletMove( ) inputWithoutData()函数中,表示与用户输入无关的数据
下面是运行后的画面
这样玩家的基本功能就实现了,同样你也可以自己添加其他元素,如多发子弹、激光等高级武器
有人要看,我又回来了~
下面是对子弹的优化,上述写的子弹会发现,连续发射子弹的话,上一个子弹会被下一个子弹所“顶替”,这是因为这里用的是一个子弹的坐标,所以我们定义一个数组存储多个子弹坐标 代码如下
// int BulletX[10], BulletY[10]; 这里将子弹改为10个数组!!!
case 32:
for (int i = 0; i < 10; i++)
{
if (BulletY[i] < 0) //检测哪个子弹没有“发射”
{ //也即是 y 坐标跑到了屏幕上方
BulletX[i] = PlayPosX;
BulletY[i] = PlayPosY-1;
break;
}
}
break;
//然后是遍历需要上升的子弹
for (int i = 0; i < 10; i++)
{
if (BulletY[i] >= 0)
BulletY[i]--;
}
//Show显示这里就是遍历子弹的显示了 z表示遍历子弹个数
for (int z = 0; z < 10; z++)
{
if (i == player.BulletY[z] && j == player.BulletX[z])
{
cout << "|";
}
}
这样就可以连发了。
接下来是敌人的显示
.敌人的显示和子弹差不多
//定义敌人位置,建议定义为全局变量便于在各个函数调用
int EnemyPosX, EnemyPosY;
//这个也是添加到Show()函数里
if (i == enemy.EnemyPosY && j == enemy.EnemyPosX)
{
cout << "!";
}
下面是敌人的交互
bool isEnemyDeath
//检测与子弹的碰撞
for (int i = 0; i < 10; i++)
if (enemy.EnemyPosX == player.BulletX[i]
&& enemy.EnemyPosY == player.BulletY[i])
{
//敌人回到屏幕上方
//rand() % Width表示在屏幕上方随机x轴位置出现
enemy.EnemyPosX = rand() % Width;
enemy.EnemyPosY =- rand() % 6;
player.BulletX[i] = -1; //子弹也 “销毁”
player.BulletY[i] = -1;
isEnemyDeath=true;
break;
}
if (!isEnemyDeath)
{
//检测与玩家的碰撞
if (enemy.EnemyPosX == player.PlayPosX
&& enemy.EnemyPosY == player.PlayPosY)
{
enemy.EnemyPosX = rand() % Width;
enemy.EnemyPosY =- rand() % 6;
}
//这个是当敌人移动没有超出屏幕范围
else if (enemy.EnemyPosY < Heigh)
{
timer = 0;
//敌人y坐标一直向下移动
enemy.EnemyPosY++;
}
//超出范围
else
{
enemy.EnemyPosX = rand() % Width;
enemy.EnemyPosY =- rand() % 6;
}
}
这个是一直要执行的放在InputWithoutData()中就行了。
这里是一个敌人实现,如果想加入多个敌人的话,也可以用类似子弹的方法用个数组来实现。
到这基本就结束了,这里基本详细给出了这个小游戏制作的基本流程,如果能看懂,对着代码能按自己的实现思路写出来是最好的了。
如有错误和改进,还请不吝指出
2021- 11- 27 再次更新,最近用C语言又重写了一遍, 大概思路和上面一样就不再介绍了,直接上源码
#include "stdio.h"
#include // 双缓冲技术
#include // console input output
#include
#define amount 4
int Width = 40, Heigh = 10;
int BulletX[amount], BulletY[amount];
int PosX = 7, PosY = 5;
int EnemyX, EnemyY;
int shoot[amount];
int score = 0;
int tem = 0;
void gotoxy(int x, int y) // 定位输入的坐标位置
{
COORD c;
c.X = x;
c.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), c);
}
void HideCursor() // 用于隐藏光标
{
CONSOLE_CURSOR_INFO cursor_info = { 1, 0 }; // 第二个值为0表示隐藏光标
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
void Input()
{
if (_kbhit()) // ctrl+ k + d k+ c 注释 k+ u 解除注释
{
char tem = _getch();
switch (tem)
{
case 'W':
case 'w':
if (PosY > 1)
{
PosY--;
}
break;
case 'S':
case 's':
if (PosY < Heigh - 2)
{
PosY++;
}
break;
case 'A':
case 'a':
if (PosX > 1)
PosX--;
break;
case 'D':
case 'd':
if (PosX < Width - 2)
PosX++;
break;
case 32:
// amount 5
for (int i = 0; i < amount; i++)
{
if (shoot[i] == 0)
{
BulletX[i] = PosX + 1;
BulletY[i] = PosY;
shoot[i] = 1;
break;
}
}
break;
default:
break;
}
}
}
void Instance()
{
EnemyX = Width;
EnemyY = rand() % (Heigh - 2) + 1;
}
void Update()
{
while (1) // true, false
{
tem++;
for (int i = 0; i < amount; i++)
{
if (BulletX[i] != -1) // 子弹的移动
{
BulletX[i] += 1;
}
if (BulletX[i] >= Width) // 检测是否超出界面
{
BulletX[i] = -1;
shoot[i] = 0;
}
if (EnemyX == BulletX[i] && EnemyY == BulletY[i])
{
score++;
Instance();
}
if (BulletX[i] != -1)
{
gotoxy(BulletX[i], BulletY[i]);
printf("0");
}
}
if (tem > 3)
{
tem = 0;
EnemyX--;
}
if (EnemyX < 0)
{
Instance();
}
// 绘制屏幕
gotoxy(PosX, PosY);
printf("*");
gotoxy(EnemyX, EnemyY);
printf("@");
// 得分情况
gotoxy(0, 0);
printf(" Score is : %d", score);
Input();
// 如果还有闪屏的话, 在清屏函数前加个延迟函数
Sleep(70); // 延迟函数, 延迟 50ms 可自己调数值
system("cls"); //清屏函数
}
}
int main()
{
// 初始化子弹
for (int i = 0; i < amount; i++)
{
BulletX[i] = -1;
BulletY[i] = 0;
shoot[i] = 0;
}
HideCursor();
Instance(); // 0 - Heigh-1 1- (heigh-1)
Update();
return 0;
}
代码的结构写的不是太好,就懒得改了0.0