C 游戏编程笔记:《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.实现对小球更多的操控,从而可以调整击中砖块。

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游戏,在游戏画面中同时显示多个柱子。

猜你喜欢

转载自blog.csdn.net/GameStrategist/article/details/107193139
今日推荐