#include <iostream>
#include <conio.h>
#include <windows.h>
#include <vector>
#include <cstdlib>
#include <ctime>
using namespace std;

enum C
{
	BLACK = 0,
	BLUE = 1,
	GREEN = 2,
	CYAN = 3,
	RED = 4,
	MAGENTA = 5,
	YELLOW = 6,
	LIGHTGRAY = 7,
	DARKGRAY = 8,
	LIGHTBLUE = 9,
	LIGHTGREEN = 10,
	LIGHTCYAN = 11,
	LIGHTRED = 12,
	LIGHTMAGENTA = 13,
	LIGHTYELLOW = 14,
	WHITE = 15
};
void sc ( int c)
{
	HANDLE h=GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(h,c); 
}


const int w = 40;//宽 
const int h = 20;//高 
int x = w / 2, y = h - 1;
vector<int> bx, by, ex, ey;//b子弹 e敌机 
int score = 0;
bool ending = false;
int cd = 0;
int maxcd;
int starttime, timeleft;
bool warning = false;

void begin()
{
	
	
	sc(WHITE) ;
	cout << "请输入难度(3级最难,1级最简单):";
	cin >>  maxcd;
	system("cls");
	sc(LIGHTGRAY);
	cout << "规则:"  << '\n';
	cout << "躲避敌机and攻击敌机"  << '\n';
	cout << "消灭一架敌机加10分" << '\n';
	cout << "最后的分数会有彩蛋,拼运气和人品的时候到了。" << '\n';
	sc(BLACK);
	cout << "猜猜这句话写的是啥?" << ' ';
	sc(WHITE);
	cout << "猜猜这里写的什么?" << '\n';
	sc(LIGHTBLUE);
	cout << "操作:	向左:A    向右:D    发射子弹:空格"  << '\n'; 
	sc(LIGHTMAGENTA);
	cout << "限时 180秒" << '\n' ;
	sc(LIGHTRED);
	cout << "注意不要离敌机太近才释放子弹" << '\n';
	sc(LIGHTYELLOW);
	cout << "游戏5s后开始";
	Sleep(5000);
}

void diji (int n) //初始化敌机 
{
	for ( int i = 0; i < n; i ++)
	{
		ex.push_back(rand()%w);
		ey.push_back(rand()%(h/2));
	}
}

int gettime()//获取当前时间 
{
	return GetTickCount()/1000;
}




void draw() //绘制界面 
{
	system("cls");
	
	sc(DARKGRAY);
	for ( int i = 0; i < w+2; i ++) cout << '#';//顶部 
	cout << '\n';
	
	for ( int r = 0; r < h; r ++)//行 ,纵坐标 
	{
		sc(DARKGRAY);
		cout << '#';
		
		for ( int c = 0; c < w; c ++)//列 , 横坐标 
		{
			if ( r == y && c == x)
			{
				sc(LIGHTYELLOW);
				cout << 'A';
			}
			else
			{
				bool f = false;
				for ( size_t i = 0; i < bx.size(); i ++)//子弹 b
				{
					if ( bx[i] == c && by[i] == r) 
					{
						sc(LIGHTCYAN);
						cout << '|' ;
						f = true;
						break;
					}
				}
				if (!f)
				{
					for ( size_t i = 0; i < ex.size(); i ++)//敌机e 
					{
						if ( ex[i] == c && ey[i] == r)
						{
							sc(LIGHTRED);
							cout << 'V';
							f = true;
							break;
						}
					}
				}
				if (!f)
				{
					sc(BLACK);
					cout << ' ';
				}	
			}
			
		}
		sc(DARKGRAY);
		cout << '#' << '\n';
	}
	sc(DARKGRAY) ;
	for ( int i = 0; i < w+2; i ++) cout << '#';//底部 
	cout << '\n';
	
	sc(LIGHTGREEN);
	cout << "分数:" << score << '\n' ;//分数 
	
	if ( timeleft > 30) sc(GREEN);
	else if ( timeleft <= 30 && timeleft > 10) sc(YELLOW);
	else sc(RED);
	cout << "剩余时间:" << timeleft << 's' << '\n';//时间 
	
	if ( timeleft <= 10) 
	{
		if ( timeleft % 2 == 1) sc(LIGHTRED);
		if ( timeleft % 2 == 0) sc(LIGHTYELLOW);
		cout << "最后"  << timeleft << "秒!" << "加油!!!" << '\n' ;//鼓励&&加油 
	}
	
	sc(LIGHTGRAY);
	cout << "规则:"  << '\n';
	cout << "躲避敌机and攻击敌机" << '\n' ;
	cout << "操作:		向左:A    向右:D    发射子弹:空格"  << '\n'; 
	cout << "限时 180秒" << '\n' ;
	sc(YELLOW);
	cout << "如果你是全班分数最高的人,那么..." << '	' << "啥也没有" << '\n' ;
	sc(WHITE);
	
}

void input()//输入 
{
	if (GetAsyncKeyState('A')&0x8000 && x > 0) x--;
	if (GetAsyncKeyState('D')&0x8000 && x < w-1) x++;
	if (GetAsyncKeyState(VK_SPACE)&0x8000 && cd<=0)//添加子弹b 
	{
		bx.push_back(x);
		by.push_back(y-1);
		cd = maxcd;
	}
	if (cd > 0) cd --;
}


void update ()//更新 
{
	for ( size_t i = 0; i < bx.size(); i ++)//移动子弹 b
	{
		by[i] --;
		if ( by[i] < 0) 
		{
			bx.erase(bx.begin()+i);
			by.erase(by.begin()+i) ;
			i --;
		}
	}
	
	for ( size_t i = 0; i < ex.size(); i ++)//移动敌机 e
	{
		ey[i] ++;
		if (ey[i] >= h)
		{
			ex.erase(ex.begin()+i);
			ey.erase(ey.begin()+i);
			i --;
			ex.push_back(rand()%w);
			ey.push_back(0);
		}
	}
	
	for ( size_t i = 0; i < bx.size(); i ++)//子弹b敌机e碰撞检测 
	{
		for ( size_t j = 0; j < ex.size(); j ++)
		{
			if ( bx[i] == ex[j] && by[i] == ey[j] || bx[i] == ex[j] && by[i] == ey[j]+1)
			{
				bx.erase(bx.begin()+i);
				by.erase(by.begin()+i);
				
				ex.erase(ex.begin()+j);
				ey.erase(ey.begin()+j);
				
				score += 10;
				
				ex.push_back(rand()%w);
				ey.push_back(0);
				
				i --;
				break;
			}
		}
	}
	
	
	for ( size_t i = 0; i < ex.size(); i ++)//玩家与敌机 e
		if ( ex[i] == x && ey[i] == y)
		{
			ending = true;
			break;
		}
	
	int nowtime = gettime();//更新时间 
	timeleft = 180 - (nowtime - starttime);
	if ( timeleft <= 0) ending = true;
}


void gameover ()
{
	system("cls");
	
	sc(LIGHTRED);
	cout << "==== Game Over ====" << '\n';
	sc(WHITE);
	cout << "存活时间:";
	sc(LIGHTMAGENTA) ;
	cout << (180-timeleft) << 's' << '\n';
	
	Sleep(500);
	
	sc(WHITE);
	cout << "你的分数是:";
	Sleep (1000);
	cout << "... ..." << '\n';
	Sleep (1000);
	sc(LIGHTCYAN);
	cout << "期待一下吧" << '\n';
	Sleep(1000) ;
	sc (WHITE);
	cout << "你的分数是:";
	sc(LIGHTRED);
	cout <<  score << '\n';
	
	Sleep(500);
	
	if ( score == 430 || score == 390 || score == 130 || score == 170 || score == 90 || score == 180 || score == 50) 
	{
		score *= 2;
		sc(LIGHTMAGENTA);
		cout << "恭喜你!运气不错 开到盲盒了,总分数翻倍" << '\n';
		cout << "你最终的分数是:";
		sc(LIGHTRED);
		cout << score << '\n';
	}
	
	sc(RED);
	if ( score <= 50) cout << "再接再厉!!!" << '\n' ;
	else if ( score > 50 && score <= 200) cout << "还不错,下次继续加油!" << '\n';
	else if ( score > 200 && score <= 500) cout << "太有实力了!!!" << '\n';
	else if ( score > 500) cout << "太太太太太有实力了!!! 可谓是古希腊掌管游戏的神" << '\n';
	sc(WHITE);
}

int main () 
{
	begin();
	
	srand(time(0));//随机数种子 
	diji(6);//初始化敌机
	
	starttime = gettime();
	timeleft = 180;
	
	HANDLE h=GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO ci;
	GetConsoleCursorInfo(h,&ci);
	ci.bVisible=0;
	SetConsoleCursorInfo(h,&ci); //光标 
	
	
	while(!ending)
	{
		draw();
		input();
		update();
		Sleep(50);//数字 = 1000 / 帧率 
	}
	
	gameover();
	Sleep(5000);
	return 0;
}

1 comments

  • @ 2026-2-5 16:46:02

    同学你好,很高兴看到你分享自己编写的游戏代码。这是一个用控制台实现的飞行射击小游戏,整体结构清晰,包含了游戏循环、绘制、输入、更新和结束逻辑。作为竞赛教练,我会从代码质量、算法效率和可维护性的角度给你一些建议,帮助你提升编程水平。


    一、代码结构分析

    你的代码将游戏逻辑(draw, update, input)与界面绘制、状态管理混合在一起。在较大项目中,这会导致维护困难。建议采用模块化设计

    • 将游戏状态(玩家位置、子弹、敌机、分数等)封装为一个结构体或类。
    • 将不同功能(如碰撞检测、敌机生成)拆分为独立函数。

    思考题:如果让你添加“敌机类型”(有的需要击中多次),现在的代码结构容易修改吗?


    二、算法效率问题

    1. 碰撞检测复杂度

      for (size_t i = 0; i < bx.size(); i++) // O(b)
        for (size_t j = 0; j < ex.size(); j++) // O(e)
      

      当前复杂度为 O(b×e)O(b \times e),在子弹和敌机数量较多时可能成为瓶颈。虽然当前游戏规模不大,但养成优化意识很重要。

      优化思路:能否用空间换时间?例如维护一个二维数组记录每个位置是否有敌机,将碰撞检测降至 O(b)O(b)

    2. 容器删除操作

      bx.erase(bx.begin()+i);  // 线性时间复杂度
      

      vector 中间删除元素会导致后续元素移动,当容器较大时影响性能。考虑:

      • 使用“标记-清除”策略,先标记待删除元素,再统一删除。
      • 或用 list 存储动态变化的数据(但需注意缓存不友好)。

    三、游戏逻辑细节

    1. 敌机生成算法

      ex.push_back(rand()%w);  // 可能生成在同一列
      

      这可能导致敌机堆叠,降低游戏性。可以记录每列最近生成的敌机位置,避免过于密集。

    2. 时间管理

      • 使用 GetTickCount() 在长时间运行后可能溢出(约49.7天),虽然对游戏影响不大,但在工程中建议使用 std::chrono
      • 帧率控制 Sleep(50) 在不同机器上可能不一致,考虑计算每帧耗时动态调整。

    四、代码规范与可读性

    1. 魔法数字:代码中多次出现 180(时间)、10(分数)等字面量。应定义为常量,如:

      const int TOTAL_TIME = 180;
      const int SCORE_PER_ENEMY = 10;
      
    2. 函数职责update() 函数同时处理子弹移动、敌机移动、碰撞检测、时间更新,过于臃肿。建议拆分为:

      • moveBullets()
      • moveEnemies()
      • checkCollisions()
      • updateTime()
    3. 全局变量:大量全局变量增加了耦合度。尝试将它们封装到类或结构体中。


    五、扩展思考(算法角度)

    如果这是一个算法题,可能会这样描述:

    在一个 w×hw \times h 的网格中,玩家位于底部。每帧玩家可以左右移动或发射子弹(子弹垂直向上)。敌机从顶部随机位置生成并垂直向下移动。子弹与敌机在同一格或相邻格时发生碰撞。求 TT 帧后的最大得分。

    考点

    1. 状态表示:如何用数据结构高效维护动态对象?
    2. 碰撞检测:如何优化到 O(n)O(n)O(nlogn)O(n \log n)
    3. 动态规划:如果子弹有冷却时间,如何规划发射时机最大化收益?

    六、建议改进步骤

    1. 重构代码结构,将数据与逻辑分离。
    2. 实现更高效的碰撞检测,尝试用哈希表存储位置到敌机的映射。
    3. 添加测试用例,例如模拟大量敌机测试性能。
    4. 考虑异常情况:如输入非法字符、窗口大小变化等。

    最后:你已经掌握了游戏开发的基本循环,这是很好的起点。下一步可以尝试:

    • 用面向对象的方式重写代码。
    • 实现更复杂的功能(如多种武器、Boss战)。
    • 学习使用游戏引擎(如Unity)理解更系统的游戏架构。

    如果对某个优化点有疑问,或想讨论具体实现细节,可以继续提问。记住,优秀的程序员不仅是写出能运行的代码,更要写出高效、可维护、可扩展的代码。

    • 1