C语言游戏开发实践
初始C语言 (7.8)
- 学习本文前需要掌握的语法知识:标识符,变量,常量,运算符与表达式,以及printf,scanf,if-else,while,for语句的用法。
- 原书中环境VS2010,笔者使用的是Dev-C++。
弹跳小球
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main()
{
int i,j;
int x = 0;//横坐标
int y = 5;//纵坐标
//记录速度的变量 velocity
//当v_x 碰到上下边界后改变正负号
//当v_y 碰到左右边界后改变正负号
int velocity_x = 1;
int velocity_y = 5;
int left = 0; //左
int right = 20; //右
int top = 0; //上
int bottom = 10; //下
while (1)
{
//小球的新位置 x=旧位置x+速度velocity
//通过两个方向改变影响小球的坐标,来完成斜方向的移动
x = x + velocity_x;
y = y + velocity_y;
//清屏函数
system("cls");
// 输出小球前的空行
for(i=0;i<x;i++)
printf("\n");
for (j=0;j<y;j++)
printf(" ");
printf("o");
printf("\n");
Sleep(50); // 等待若干毫秒 //windos编程S大写,延时50ms,实际时长茶瓯共50ms
//当小球到达上下边界时改变方向,
//编程通过改变速度变量velocity正负号,(物理学中速度是矢量)
if ((x==top)||(x==bottom))
velocity_x = -velocity_x;
if ((y==left)||(y==right))
velocity_y = -velocity_y;
}
return 0;
}
思考题:
- 1.如果不用Sleep函数,能否利用循环语句实现小球速度变慢的效果?
- 2.尝试利用printf("\a")实现小球碰到边界时响铃的效果。
- 3.尝试为反弹球游戏绘制边框。
最简单的飞机游戏
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
int main()
{
int i,j;
int x = 5;
int y = 10;
char input;
int isFire = 0;
int ny = 5; // 一个靶子,放在第一行,ny列上
int isKilled = 0;
while (1)
{
system("cls"); // 清屏函数
if (!isKilled) // 输出靶子
{
for (j=0;j<ny;j++)
printf(" ");
printf("+\n");
}
if (isFire==0) // 输出飞机上面的空行
{
for(i=0;i<x;i++)
printf("\n");
}
else // 输出飞机上面的激光竖线
{
for(i=0;i<x;i++)
{
for (j=0;j<y;j++)
printf(" ");
printf(" |\n");
}
if (y+2==ny) // +2是因为激光在飞机的正中间,距最左边2个坐标
isKilled = 1; // 击中靶子
isFire = 0;
}
// 下面输出一个复杂的飞机图案
for (j=0;j<y;j++)
printf(" ");
printf(" *\n");
for (j=0;j<y;j++)
printf(" ");
printf("*****\n");
for (j=0;j<y;j++)
printf(" ");
printf(" * * \n");
//kbhit()函数在用户有键盘输入时返回1,否则返回0
//写在这里功能就是没有键盘输入时下面语句不运行
//避免出现用户不输入游戏就暂停的情况。
if(kbhit())
{
//最初使用的是scanf(),但是每输入一次必须回车一次,交互效果不好
//getch()不需要回车就能得到输入的控制字符,头文件为非标准库<conio.h>
input = getch();//getch函数的返回值为读取的字符,getch = getchar
if (input == 'a')
y--; // 位置左移
if (input == 'd')
y++; // 位置右移
if (input == 'w')
x--; // 位置上移
if (input == 's')
x++; // 位置下移
if (input == ' ')
isFire = 1;
}
}
return 0;
}
思考题
- 1.如何让靶子移动起来?
- 2.如何统计和现实击中得分
程序调试方法
- 记录常见错误原因,因为大多都是英语语句,个人建议不要汉化软件而是自己主动查询意思并做笔记。
- 逻辑错误:程序编译通过但运行结果不对叫逻辑错误,以下为常见的逻辑错误:
1.算法思想错误;2.再判断表达式时忽略=与==的区别;3.逻辑运算符的优化问题;4.循环语句内有多条语句但没用{}花括号包含。 - 处理逻辑错误的方法是在编译器中设断点跟踪调试,如下是VS2010的方式:
- 选择需要设置断点的程序行,然后选择Insert/Remove BreakPoint命令,或按F9键设置断点。按F5键盘后程序运行道断电处暂停,在VC中有一个黄色的小箭头,指示将要执行的代码,按F10键继续单步运行。在变量观察窗口中输入要观察的变量和表达式,可以实时看到其对应的数值。通过单步运行和变量跟踪可以有效地分析逻辑错误。
- 简单总结一下上文,就是针对逻辑错误要利用好自身使用的软件,最好是在使用前百度出断点调试的方法,这点无论在面试还是学习,都是很好的。
- 程序代码较长时,浏览代码可以通过Ctrl+F2键在不同的代码行设定书签,按F2键快速切换道对应的代码行
- 代码排版不规范时,按Ctrl+A键全选后按Alt+F8键将自动排版。
- 活用查找和替换可以进行变量的批量重命名,程序的快速定位等功能。
- 利用注释,二分法可以实现快速排错。
简单总结
-
首先这是较为简单的两个小游戏,不需要较真和大量测试去更改它的代码,完成思考题就行。
-
再来是从这两个编程获得了什么? getch()不回显函数(非标准库<conio.h>),kbhit()判断键盘是否输入函数(非标准库<conio.h>),Sleep延迟函数(windos编程s大写,函数库为<windows.h>),坐标轴思想,原代码的思想图如下:
图一:弹跳小球
图二:最简单的飞机游戏
-
先让程序跑起来,再去想为什么,这是根本。
-
第一章到此结束。
函数封装:(7.10)
- 学习本内容需要掌握的语法知识:函数的定义与使用,全局变量,静态变量。
- 原书中环境VS2010,笔者使用的是Dev-C++。
飞机游戏
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
// 全局变量
int position_x,position_y; // 飞机位置
int bullet_x,bullet_y; // 子弹位置
int enemy_x,enemy_y; // 敌机位置
int high,width; // 游戏画面尺寸
int score; // 得分
void gotoxy(int x,int y) //光标移动到(x,y)位置
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(handle,pos);
}
void HideCursor() // 用于隐藏光标
{
CONSOLE_CURSOR_INFO cursor_info = {1, 0}; // 第二个值为0表示隐藏光标
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
void startup() // 数据初始化
{
high = 20;
width = 30;
position_x = high/2;
position_y = width/2;
bullet_x = -2;
bullet_y = position_y;
enemy_x = 0;
enemy_y = position_y;
score = 0;
HideCursor(); // 隐藏光标
}
void show() // 显示画面
{
gotoxy(0,0); // 光标移动到原点位置,以下重画清屏
int i,j;
for (i=0;i<high;i++)
{
for (j=0;j<width;j++)
{
if ((i==position_x) && (j==position_y))
printf("*"); // 输出飞机*
else if ((i==enemy_x) && (j==enemy_y))
printf("@"); // 输出敌机@
else if ((i==bullet_x) && (j==bullet_y))
printf("|"); // 输出子弹|
else
printf(" "); // 输出空格
}
printf("\n");
}
printf("得分:%d\n",score);
}
void updateWithoutInput() // 与用户输入无关的更新
{
if (bullet_x>-1)
bullet_x--;
if ((bullet_x==enemy_x) && (bullet_y==enemy_y)) // 子弹击中敌机
{
score++; // 分数加1
enemy_x = -1; // 产生新的飞机
enemy_y = rand()%width;
bullet_x = -2; // 子弹无效
}
if (enemy_x>high) // 敌机跑出显示屏幕
{
enemy_x = -1; // 产生新的飞机
enemy_y = rand()%width;
}
// 用来控制敌机向下移动的速度。每隔几次循环,才移动一次敌机
// 这样修改的话,用户按键交互速度还是保持很快,但我们NPC的移动显示可以降速
static int speed = 0;
if (speed<20)
speed++;
if (speed == 20)
{
enemy_x++;
speed = 0;
}
}
void updateWithInput() // 与用户输入有关的更新
{
char input;
if(kbhit()) // 判断是否有输入
{
input = getch(); // 根据用户的不同输入来移动,不必输入回车
if (input == 'a')
position_y--; // 位置左移
if (input == 'd')
position_y++; // 位置右移
if (input == 'w')
position_x--; // 位置上移
if (input == 's')
position_x++; // 位置下移
if (input == ' ') // 发射子弹
{
bullet_x = position_x-1; // 发射子弹的初始位置在飞机的正上方
bullet_y = position_y;
}
}
}
int main()
{
startup(); // 数据初始化
while (1) // 游戏循环执行
{
show(); // 显示画面
updateWithoutInput(); // 与用户输入无关的更新
updateWithInput(); // 与用户输入有关的更新
}
return 0;
}
思考题
1.随着积分的增加加快敌机的下落速度。
2.防止玩家操控飞机飞出边界。
3.增加按Esc键后游戏暂停的功能。
反弹球
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <cwindow.h>
// 全局变量
int high,width; // 游戏画面大小
int ball_x,ball_y; // 小球的坐标
int ball_vx,ball_vy; // 小球的速度
int position_x,position_y; // 挡板中心坐标
int ridus; // 挡板半径大小
int left,right; // 挡板左右位置
int ball_number; // 反弹小球的次数
int block_x,block_y; // 方块的位置
int score; // 消掉方块的个数
void gotoxy(int x,int y) //光标移动到(x,y)位置
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(handle,pos);
}
void startup() // 数据初始化
{
high = 13;
width = 17;
ball_x = 0;
ball_y = width/2;
ball_vx = 1;
ball_vy = 1;
ridus = 6;
position_x = high;
position_y = width/2;
left = position_y - ridus;
right = position_y + ridus;
ball_number = 0;
block_x = 0;
block_y = width/2+1;
score = 0;
}
void show() // 显示画面
{
gotoxy(0,0); // 光标移动到原点位置,以下重画清屏
int i,j;
for (i=0;i<=high+1;i++)
{
for (j=0;j<=width;j++)
{
if ((i== ball_x) && (j== ball_y))
printf("0"); // 输出小球
else if (j==width)
printf("|"); // 输出右边框
else if (i==high+1)
printf("-"); // 输出下边框
else if ( (i==high) && (j>left) && (j<right) )
printf("*"); // 输出挡板
else if ((i== block_x) && (j== block_y))
printf("B"); // 输出方块
else
printf(" "); // 输出空格
}
printf("\n");
}
printf("反弹小球数:%d\n",ball_number);
printf("消掉的方块数:%d\n",score);
}
void updateWithoutInput() // 与用户输入无关的更新
{
if (ball_x==high-1)
{
if ( (ball_y>=left) && (ball_y<=right) ) // 被挡板挡住
{
ball_number++;
printf("\a"); // 响铃
//ball_y = ball_y + rand()%4-2;
}
else // 没有被挡板挡住
{
printf("游戏失败\n");
system("pause");
exit(0);
}
}
if ((ball_x==block_x) && (ball_y==block_y)) // 小球击中方块
{
score++; // 分数加1
block_y = rand()%width; // 产生新的方块
}
ball_x = ball_x + ball_vx;
ball_y = ball_y + ball_vy;
if ((ball_x==0)||(ball_x==high-1))
ball_vx = -ball_vx;
if ((ball_y==0)||(ball_y==width-1))
ball_vy = -ball_vy;
Sleep(80);
}
void updateWithInput() // 与用户输入有关的更新
{
char input;
if(kbhit()) // 判断是否有输入
{
input = getch(); // 根据用户的不同输入来移动,不必输入回车
if (input == 'a')
{
position_y--; // 位置左移
left = position_y - ridus;
right = position_y + ridus;
}
if (input == 'd')
{
position_y++; // 位置右移
left = position_y - ridus;
right = position_y + ridus;
}
}
}
int main()
{
startup(); // 数据初始化
while (1) // 游戏循环执行
{
show(); // 显示画面
updateWithoutInput(); // 与用户输入无关的更新
updateWithInput(); // 与用户输入有关的更新
}
return 0;
}
思考题
1.增加砖块,使得击中概率增大
2.实现对小球更多的操控,从而可以调整击中砖块。
扫描二维码关注公众号,回复:
11624878 查看本文章
flappy bird
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <cwindow.h>
// 全局变量
int high,width; // 游戏画面大小
int bird_x,bird_y; // 小鸟的坐标
int bar1_y,bar1_xDown,bar1_xTop; // 障碍物1的相关坐标
int score; // 得分,经过障碍物的个数
void gotoxy(int x,int y) //光标移动到(x,y)位置
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(handle,pos);
}
void startup() // 数据初始化
{
high = 20;
width = 20;
bird_x = high/2;
bird_y = 3;
bar1_y = width;
bar1_xDown = high/3;
bar1_xTop = high/2;
score = 0;
}
void show() // 显示画面
{
gotoxy(0,0); // 光标移动到原点位置,以下重画清屏
int i,j;
for (i=0;i<high;i++)
{
for (j=0;j<width;j++)
{
if ((i==bird_x) && (j==bird_y))
printf("@"); // 输出小鸟
else if ((j==bar1_y) && ((i<bar1_xDown)||(i>bar1_xTop)) )
printf("*"); // 输出墙壁
else
printf(" "); // 输出空格
}
printf("\n");
}
printf("得分:%d\n",score);
}
void updateWithoutInput() // 与用户输入无关的更新
{
bird_x ++;
bar1_y --;
if (bird_y==bar1_y)
{
if ((bird_x>=bar1_xDown)&&(bird_x<=bar1_xTop))
score++;
else
{
printf("游戏失败\n");
system("pause");
exit(0);
}
}
if (bar1_y<=0) // 再新生成一个障碍物
{
bar1_y = width;
int temp = rand()%int(high*0.8);
bar1_xDown = temp - high/10;
bar1_xTop = temp + high/10;
}
Sleep(150);
}
void updateWithInput() // 与用户输入有关的更新
{
char input;
if(kbhit()) // 判断是否有输入
{
input = getch(); // 根据用户的不同输入来移动,不必输入回车
if (input == ' ')
bird_x = bird_x - 2;
}
}
int main()
{
startup(); // 数据初始化
while (1) // 游戏循环执行
{
show(); // 显示画面
updateWithoutInput(); // 与用户输入无关的更新
updateWithInput(); // 与用户输入有关的更新
}
return 0;
}
思考题:
1.实现小鸟受重力影响下落的效果,即加速下落。
2.模拟原版flappy bird游戏,在游戏画面中同时显示多个柱子。