EasyX实战存档

五子棋棋盘
#include <graphics.h>
#include <iostream>
int main() {
	initgraph(400, 400); //400*400的画布
	setbkcolor(RGB(210,125,40)); //棋盘颜色
	cleardevice();

	setlinecolor(BLACK); //黑线
	for (int i = 25; i < 400; i += 25) line(i, 25, i, 375); //竖线
	for (int i = 25; i < 400; i += 25) line(25, i, 375, i); //横线

	//设置字体格式
	settextstyle(20, 0, _T("")); 
	settextcolor(BLACK);
	setbkmode(TRANSPARENT);
	TCHAR s[5];
	for (int i = 25; i < 400; i += 25) {
		_stprintf_s(s, _T("%d"), 16 - i / 25); //数字1-15
		outtextxy(0, i-10, s);
	}
	char c = 'A';
	for (int i = 25; i < 400; i += 25) {
		outtextxy(i-5, 375, c); //字母A-Z
		c++;
	}
	//5个黑点
	setfillcolor(BLACK);
	solidcircle(100, 100, 5);
	solidcircle(100, 300, 5);
	solidcircle(300, 100, 5);
	solidcircle(300, 300, 5);
	solidcircle(200, 200, 5);
	system("pause");
	closegraph();
	return 0;
}

动态时钟
#include <graphics.h>
#include <iostream>
#include <time.h>
#include <math.h>
#include <conio.h>
const double pi = 6.283;
int main(){
    initgraph(500, 500);
    //显示钟表图片
    IMAGE picture;
    loadimage(&picture, "clk.png", 500, 500);
    //将中心设为原点,y轴朝上为正
    setorigin(250, 255);
    setaspectratio(1, -1);
    //solidcircle(-250, -250, 5);
    BeginBatchDraw();
    SYSTEMTIME time;
    int hour, minute, second,x,y;
    while (!_kbhit()) {
        cleardevice();
        //表盘
        putimage(-250, -255, &picture);
        //获取当前时间
        GetLocalTime(&time);
        hour = time.wHour;
        minute = time.wMinute;
        second = time.wSecond;
        if (hour >= 12) hour -= 12;
        //找角度画三个指针
        //指针末端坐标
        //秒针
        //std::cout << "秒:" << second << " 角度" << second * 6 << " cos" << std::endl;
        x = 180 * cos((450 - second * 6) / 360.0 * pi);
        y = 180 * sin((450 - second * 6) / 360.0 * pi);
        setlinestyle(PS_SOLID, 5);
        setlinecolor(RED);
        line(0, 0, x, y);
        //分针
        x = 150 * cos((450 - (minute * 6 + 6.0 * second / 60.0)) / 360.0 * pi);
        y = 150 * sin((450 - (minute * 6 + 6.0 * second / 60.0)) / 360.0 * pi);
        setlinestyle(PS_SOLID, 7);
        setlinecolor(BLACK);
        line(0, 0, x, y);
        //时针
        x = 100 * cos((450 - (hour * 30 + 30.0 * minute / 60.0)) / 360.0 * pi);
        y = 100 * sin((450 - (hour * 30 + 30.0 * minute / 60.0)) / 360.0 * pi);
        setlinestyle(PS_SOLID, 12);
        line(0, 0, x, y);
        FlushBatchDraw();
    }
    EndBatchDraw();
    closegraph();
    return 0;
}

image

处理鼠标信息
#include <graphics.h>
int main() {
	initgraph(600, 600);
	BeginBatchDraw();
	ExMessage msg;
	bool running = true;
	bool ctrl = false;
	while (running) {
		while (peekmessage(&msg)) {
			if (msg.message == WM_LBUTTONDOWN) {
				if(!ctrl) rectangle(msg.x - 5, msg.y - 5, msg.x + 5, msg.y + 5);
				else rectangle(msg.x - 10, msg.y - 10, msg.x + 10, msg.y + 10);
				FlushBatchDraw();
				break;
			}else if (msg.message == WM_RBUTTONDOWN) {
				if (!ctrl) circle(msg.x, msg.y, 10);
				else circle(msg.x, msg.y, 20);
				FlushBatchDraw();
				break;
			}
			else if (msg.message == WM_KEYDOWN) {
				switch (msg.vkcode) {
					case VK_CONTROL:
						ctrl = true;
						break;
					case 'c':
					case 'C':
						cleardevice();
						FlushBatchDraw();
						break;
					case 'R':
					case 'r':
						setlinecolor(RED);
						break;
					case 'g':
					case 'G':
						setlinecolor(GREEN);
						break;
					case 'B':
					case 'b':
						setlinecolor(BLUE);
						break;
					case 'W':
					case 'w':
						setlinecolor(WHITE);
						break;
					case VK_ESCAPE:
						running = false;
						break;
				}
			}
			else if (msg.message == WM_KEYUP) {
				if (msg.vkcode == VK_CONTROL) { ctrl = false; break; }
			}
		}
	}
	EndBatchDraw();
	closegraph();
	return 0;
};

弹球
#include <graphics.h>
#include <time.h>
const int width = 800;
const int height = 600;
const int width_b = 100;
const int height_b = 16;
const int speed = 13;
const int FRAME = 60;
struct Board {int x, y;}board;
class Ball {
private:
	int r, x, y, dx, dy, to_x, to_y;
public:
	Ball():x(width/2),y(height/2),r(10){
		dx = rand() % 3 + 2;//速度
		dy = rand() % 3 + 2;
		to_x = (rand() % 2 == 0) ? 1 : -1;//向左-1向右1
		to_y = 1;//向下
	}
	int getX() { return x; }
	int getY() { return y; }
	int getR() { return r; }
	bool move() {
		x = x + to_x * dx;
		y = y + to_y * dy;
		if (x - r < 0) { x = r; to_x *= -1; }
		if (x + r > width) { x = width-r; to_x *= -1; }
		if (y - r < 0) { y = r;  to_y *= -1; }
		if (y + r > height) return false; 
		if (y + r >= board.y - height_b/2 && y + r <= board.y + height_b/2) {
			if (x >= board.x - width_b / 2 && x < board.x + width_b / 2) {
				to_y *= -1;
				y = board.y - height_b / 2 - r;
			}
		}
		return true;
	}
}ball;
int main() {
	srand(time(NULL));
	initgraph(width, height);
	BeginBatchDraw();
	board = { width / 2,height - 100 };
	ExMessage msg;
	bool running = true;
	bool pause = false;
	settextcolor(RED);
	settextstyle(50,25,_T("宋体"));
	while (running) {
		DWORD begin = GetTickCount();
		while (peekmessage(&msg)) {
			if (msg.message == WM_KEYDOWN) {
				switch (msg.vkcode) {
					case 'a':
					case 'A':
					case VK_LEFT:
						board.x -= speed;
						break;
					case 'd':
					case 'D':
					case VK_RIGHT:
						board.x += speed;
						break;
					case 'p':
					case 'P':
						pause = !pause;
						break;
					case VK_ESCAPE:
						running = false;
						break;
				}
			}
		}
		if (!pause) {
			if (!ball.move()) { running = false; break; }
			if (board.x - width_b / 2 < 0) board.x = width_b / 2;
			if (board.x + width_b / 2 > width) board.x = width - width_b / 2;
		}
		cleardevice();
		solidcircle(ball.getX(), ball.getY(), ball.getR());
		rectangle(board.x - width_b / 2, board.y - height_b / 2, board.x + width_b / 2, board.y + height_b / 2);
		if (pause) outtextxy(300, 300, _T("暂停游戏"));
		FlushBatchDraw();
		DWORD end = GetTickCount();
		DWORD dt = end - begin;
		if (dt < 1000 / FRAME) Sleep(1000 / FRAME - dt);
	}
	EndBatchDraw();
	closegraph();
	return 0;
}

飞机大战和图案记忆写在一起了,比较乱,可以只看主要函数

点击查看代码
#include <graphics.h>
#include <cstdio>
#include <vector>
#include <ctime>
#include <iostream>
#pragma comment(lib, "MSIMG32.LIB")
#pragma comment(lib,"Winmm.lib")
using namespace std;

const int width = 400;//画布的宽
const int height = 600;//画布的高
IMAGE back; //背景图
TCHAR s[50];
void loadIMG();//加载所需图片
void putimage_alpha(int x, int y, IMAGE* img) { //呈现透明底图片
	int w = img->getwidth();
	int h = img->getheight();
	AlphaBlend(GetImageHDC(NULL), x, y, w, h, GetImageHDC(img), 0, 0, w, h, { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA });
}

//飞机大战
const int ball_r = 8; //子弹半径
const int ball_speed = 15; //子弹速度
const int dx = 8; //玩家飞机移动速度
const int FRAME = 60; //帧数
const int ENEMY_SPAWN_INTERVAL = 30; // 敌机生成间隔(帧数)
bool LEFT, RIGHT; //飞机左右移动状态
int score, combo; //分数,连击数
int cnt,tmp; //记录帧数
int level; //每1000帧升一级,难度加1
class Plane {public: int x, y, life;IMAGE img;bool alive;}plane; //玩家飞机
class Enemies {public: int x, y, speed, rank_img, die; bool alive; }; vector<Enemies>enemy; //敌机
class Ball {public: int x, y; bool alive; }; vector<Ball>ball; //子弹
IMAGE heart; //飞机生命标识
IMAGE explosion; //敌机爆炸效果
IMAGE great[6];//获得多次连击时的鼓励图片 6张
IMAGE end_img;//结算分数时的图片
IMAGE enemy_img[5]; //五张敌机的图片
void init();
void plane_play();
void produce_enemy();
void produce_ball();
void solve_input();
void move();
bool check();
void draw();
void gameover();

//图案记忆
IMAGE find_img[6];//6张记忆图片
IMAGE find_back;//图片背面
IMAGE succ;
int xlocation_roll[3] = { 0,135,270 }; //第i列图片的x坐标
int ylocation_line[4] = { 0,155,310,465 };//第j列图片的y坐标
int belong[12]; //每张牌是第几张图
bool find_success[6]; //哪张图已经成功被找到了
int success;//已经找到了几张
int cnt_rand[6]; //桶,用于随机图片
void GuoDu();
void init_find();
void show(int, IMAGE*);
int findid(int, int);
void find_play();

int main() {
	srand(time(NULL));
	initgraph(width, height);
	loadIMG();//加载图片
	mciSendString(_T("open game\\bkmusic.mp3 alias bkmusic"), 0, 0, 0); //背景音乐
	mciSendString(_T("play bkmusic repeat"), 0, 0, 0); //循环播放

	//飞机大战
	plane.x = width / 2; plane.y = height - 50; plane.life = 3;/*三条命*/ plane.alive = true; //加载玩家

	BeginBatchDraw();
	init(); //开始游戏界面
	setbkmode(TRANSPARENT); settextstyle(30, 0, _T("Noto Sans SC Black")); //字体格式设置
	plane_play();
	gameover();
	mciSendString("close bkmusic", 0, 0, 0);
	GuoDu();

	//图案记忆
	init_find();
	find_play();

	putimage_alpha(width / 2 - succ.getwidth() / 2, height / 2 - succ.getheight() / 2, &succ); //游戏结束图片
	FlushBatchDraw();
	mciSendString(_T("play game\\end.mp3"), 0, 0, 0); //游戏成功音频

	EndBatchDraw();
	system("pause");
	closegraph();
	return 0;
}
void loadIMG() {
	loadimage(&back, "game\\background1.jpg", width, height); //背景图
	loadimage(&heart, "game\\heart.png", 20, 20); //生命值标识
	loadimage(&explosion, "game\\explosion1.png", 60, 60); //爆炸效果
	loadimage(&plane.img, _T("game\\player.png"), 60, 60); //玩家形象
	loadimage(&end_img, _T("game\\end.png"), 100, 75);//游戏结束的图片
	loadimage(&find_back, _T("game\\background3.jpg"), 130, 130);//图案记忆的背景图
	loadimage(&succ, _T("game\\success.png"), width-100, 100);//游戏结束的图片congratulations
	for (int i = 0; i < 5; ++i) {//加载敌机,5张
		_stprintf_s(s, _T("game\\enemy%d.png"), i + 1);
		loadimage(&enemy_img[i], s, 40, 40);
	}
	for (int i = 0; i < 6; ++i) {//打出combo会有鼓励图片,6张
		_stprintf_s(s, _T("game\\great%d.png"), i + 1);
		loadimage(&great[i], s, 100, 100);
	}
	for (int i = 0; i < 6; ++i) {//图案记忆中的6张图片
		_stprintf_s(s, _T("game\\find%d.jpg"), i + 1);
		loadimage(&find_img[i], s, 130, 130);
	}
}
void init() {
	//绘制开始界面
	putimage_alpha(0, 0, &back);
	rectangle(width / 2 - 200, height / 2 - 50, width / 2 + 200, height / 2 + 50);
	setbkmode(TRANSPARENT);
	TCHAR ss[10] = _T("开始游戏"); 
	settextstyle(50, 0, _T("宋体"));
	outtextxy(width / 2 - textwidth(ss) / 2, height / 2 - textheight(ss) / 2, ss);
	settextstyle(20, 0, _T("宋体"));
	outtextxy(width / 2 - 200, height / 2 + 50, _T("方向键控制飞机,空格发射子弹")); //操作说明
	FlushBatchDraw();
	//鼠标点击“开始游戏”按钮才能继续游戏
	ExMessage m;
	while (true) {
		m = getmessage(EX_MOUSE);
		if (m.message == WM_LBUTTONDOWN) { //鼠标左键
			if (m.x >= width / 2 - 200 && m.x <= width / 2 + 200 && m.y >= height / 2 - 50 && m.y <= height / 2 + 50) //按钮范围
				break;
		}
	}
}
void produce_enemy() {
	//产生敌机
	Enemies e = { rand() % 20 * 40 + 20,30,rand() % 3 + 2 + level,rand() % 5, 0,true };
	//速度随level增加,越来越快
	enemy.push_back(e);
}
void produce_ball() {
	//产生子弹
	Ball b = { plane.x,plane.y,true };
	ball.push_back(b);
}
void solve_input() {
	//处理输入
	ExMessage msg;
	while (peekmessage(&msg)) {
		if (msg.message == WM_KEYDOWN) {
			switch (msg.vkcode) {
				case 'a':
				case 'A':
				case VK_LEFT:
					LEFT = true; //向左
					break;
				case 'd':
				case 'D':
				case VK_RIGHT:
					RIGHT = true; //向右
					break;
				case ' ': //攻击
					produce_ball();
					break;
			}
		}
		else if (msg.message == WM_KEYUP) {
			switch (msg.vkcode) {
			case 'a':
			case 'A':
			case VK_LEFT:
				LEFT = false;
				break;
			case 'd':
			case 'D':
			case VK_RIGHT:
				RIGHT = false;
				break;
			}
		}
	 }
}
void move() {
	//处理飞机移动和子弹发射
	for (int i = 0; i < enemy.size(); ++i) if (enemy[i].alive) {
		enemy[i].y += enemy[i].speed;
		//敌机超出屏幕视为死亡
		if (enemy[i].y + enemy_img[0].getheight() / 2 > height) enemy[i].alive = false;
	}
	for (int i = 0; i < ball.size(); ++i) if (ball[i].alive) {
		ball[i].y -= ball_speed;
		if (ball[i].y - ball_r < 0) {
			//子弹超出屏幕视为死亡
			ball[i].alive = false;
			combo = 0; //子弹落空,连击清空
		}
	}
	//使用LEFT RIGHT记录状态 比 处理按键时进行移动 会更流畅,不会卡顿
	if (LEFT) plane.x = max(30, plane.x - dx);
	if (RIGHT)plane.x = min(width - 30, plane.x + dx);
}
bool check_rec(int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2) {
	//矩形检测碰撞
	//分别是 a左上角 a右下角 b左上角 b右下角 的坐标
	return (ax1<bx2 && ax2>bx1 && ay1<by2 && ay2>by1);
}
bool check() {
	//敌机和子弹碰撞
	for (int i = 0; i < ball.size(); ++i) if (ball[i].alive) {
		for (int j = 0; j < enemy.size(); ++j) if (enemy[j].alive) {
			if (check_rec(ball[i].x-ball_r,ball[i].y-ball_r,ball[i].x+ball_r,ball[i].y+ball_r,
				enemy[j].x-20,enemy[j].y-20,enemy[j].x+20,enemy[j].y+20)) {
				ball[i].alive = false;
				enemy[j].alive = false;
				enemy[j].die = cnt; //记录在第几帧被攻击
				combo++;//记录连击
				//分数:每架敌机10分,连击数越多附加分数越高
				//连击1-10每架敌机增益2分
				//连击11-20每架敌机增益4分
				//依次类推
				score = score + 10 + (combo / 10 + 1) * 2;
			}
		}
	}
	//玩家和敌机碰撞
	for (int i = 0; i < enemy.size(); ++i) if(enemy[i].alive) {
		if (check_rec(enemy[i].x - 20, enemy[i].y - 20, enemy[i].x + 20, enemy[i].y + 20,
			plane.x - 30, plane.y - 30, plane.x + 30, plane.y + 30)) {
			plane.life--; //一共三条命
			enemy[i].alive = false;
			enemy[i].die = cnt;
			combo++;
			score = score + 10 + (combo / 10 + 1) * 2;
			if (plane.life <= 0) return false;
		}
	}
	return true;
}
void draw() {
	//绘制飞机大战
	putimage(0, 0, &back);
	for (int i = 0; i < enemy.size(); ++i) {
		if (enemy[i].alive) putimage_alpha(enemy[i].x - 20, enemy[i].y - 20, &enemy_img[enemy[i].rank_img]);
		else {
			if (abs(enemy[i].die - cnt) < 5) putimage_alpha(enemy[i].x - 30, enemy[i].y - 30, &explosion);
			//爆炸效果维持9帧,太短则看不清楚爆炸效果
		}
	}
	for (int i = 0; i < ball.size(); ++i) if(ball[i].alive) {
		//子弹是实心圆
		solidcircle(ball[i].x, ball[i].y, ball_r);
	}
	putimage_alpha(plane.x - 30, plane.y - 30, &plane.img); 
	//绘制生命值
	int x = plane.x - 30; //x是飞机的左边界
	for (int i = 1; i <= plane.life; ++i) {
		putimage_alpha(x, plane.y + 30, &heart);
		x += 20; //生命值图片大小20*20
	}
	TCHAR s[30]; _stprintf_s(s, _T("score %d"), score); outtextxy(0, 15, s);//显示分数
	_stprintf_s(s, _T("combo %d"), combo);  outtextxy(0, 40, s);//显示连击数
	if (combo % 10 == 7) putimage_alpha(width - 100, 0, &great[tmp]);//连击每10帧出现一次,每次随机若干张显示
	if ((cnt + 1) / 100 != cnt / 100) tmp = rand() % 6;
	FlushBatchDraw();
}
void plane_play() {
	while (plane.alive) {
		DWORD begin = GetTickCount();
		cnt++; if (cnt % 1000 == 0) level++; //每100帧难度+1
		if (cnt % (max(10, ENEMY_SPAWN_INTERVAL - level * 2)) == 0) produce_enemy(); //定期生成敌机
		solve_input();
		move(); //更新敌机和子弹位置
		plane.alive = check(); //碰撞检测
		draw();//绘制
		//帧延时
		DWORD end = GetTickCount();
		DWORD dt = end - begin;
		if (dt < 1000 / FRAME) Sleep(1000 / FRAME - dt);
	}
}
void gameover() {
	//飞机大战结束界面
	cleardevice();
	putimage(0, 0, &back); //小图案
	_stprintf_s(s, _T("Great! You got %d scores!"), score); //分数
	outtextxy(width / 2 - textwidth(s) / 2, height / 2 - textheight(s) / 2, s);
	putimage_alpha(0, height / 2 + textheight(s) / 2, &end_img);
	FlushBatchDraw();
	Sleep(2000);
}
void GuoDu() {
	//过渡到图案记忆
	cleardevice();
	putimage(0, 0, &back);
	_stprintf_s(s, _T("Do you want to play a hidden game?"));
	outtextxy(width / 2 - textwidth(s) / 2, height / 2 - textheight(s) / 2, s);
	_stprintf_s(s, _T("YES (CLICK ME!)"));
	outtextxy(width / 2 - textwidth(s) / 2, height / 2 + textheight(s), s);
	FlushBatchDraw();
	ExMessage m;
	while (true) {
		m = getmessage(EX_MOUSE);
		if (m.message == WM_LBUTTONDOWN) {
			if (m.x >= width / 2 - textwidth(s) / 2 && m.x <= width / 2 + textwidth(s) / 2 && m.y >= height / 2 + textheight(s) && m.y <= height / 2 + textheight(s)*2)
				break;
		}
	}
	mciSendString(_T("play game\\start.mp3"), 0, 0, 0); //开始游戏音频
}
void show(int id, IMAGE* img) {
	//id从0-11,一共12张
	//显示卡牌id的图案
	putimage(xlocation_roll[id%3], ylocation_line[id/3], img);
}
int findid(int x, int y) {
	//(x,y)坐标处是几号卡牌
	int res = -1;
	for (int i = 0; i < 4; ++i)
		for (int j = 0; j < 3; ++j) {
			res++;
			if (x >= xlocation_roll[j] && x <= xlocation_roll[j] + 130 && y >= ylocation_line[i] && y <= ylocation_line[i] + 130) {
				//printf("%d %d %d %d %d %d %d\n",x, y,xlocation_roll[j],ylocation_line[i],i, j, res);
				return res;
			}
	}
	return -1;
}
void init_find() {
	cleardevice();
	loadimage(&back, _T("game\\background4.jpg"), width, height); 
	putimage(0, 0, &back);
	_stprintf_s(s, _T("接下来你每次可以翻两张牌,直到全部配对"));
	outtextxy(width / 2 - textwidth(s) / 2, height / 2 - textheight(s) / 2, s);
	FlushBatchDraw();
	Sleep(2000); //游戏规则显示两秒后自动进入游戏
	for (int i = 0, x; i < 12; ++i) { //随机分配图案
		x = rand() % 6; while (cnt_rand[x] >= 2) x = rand() % 6;
		belong[i] = x; cnt_rand[x]++;
	}
}
void find_play() {
	//开始游戏
	//每次翻两张牌
	int fir, sec; ExMessage m;
	while (success < 6) { //success记录已经成功配对的数量,一共6对
		cleardevice();
		putimage(0, 0, &back);
		//未配对的显示卡牌背面,已配对的显示卡牌正面
		for (int i = 0; i < 12; ++i) if (!find_success[belong[i]]) show(i, &find_back); else show(i, &find_img[belong[i]]);
		FlushBatchDraw();
		//第一张牌
		while (true) {
			m = getmessage(EX_MOUSE);
			if (m.message == WM_LBUTTONDOWN) {
				//点中卡牌并且是卡牌背面
				if ((fir = findid(m.x, m.y)) >= 0 && !find_success[belong[fir]]) break;
			}
		}
		show(fir, &find_img[belong[fir]]); FlushBatchDraw();
		//第二张牌
		while (true) {
			m = getmessage(EX_MOUSE);
			if (m.message == WM_LBUTTONDOWN) {
				//点中卡牌并且不和第一张相同并且是卡牌背面
				if ((sec = findid(m.x, m.y)) >= 0 && sec != fir && !find_success[belong[sec]]) break;
			}
		}
		show(sec, &find_img[belong[sec]]); FlushBatchDraw(); Sleep(500);
		
		//成功配对
		if (belong[fir] == belong[sec]) {
			success++;
			find_success[belong[fir]] = true;
			if(belong[fir] == 0) mciSendString(_T("play game\\binli.mp3"), 0, 0, 0);
			else mciSendString(_T("play game\\right.mp3"), 0, 0, 0); //正确配对音频
		}
		else mciSendString(_T("play game\\wrong.mp3"), 0, 0, 0); //错误配对音频
	}
}

图片音频在这里,按自己的文件路径修改上述程序中的路径名称
图案记忆的音频比较好玩

UPD:被制裁了,因为没有用类写,还写的特别乱

让我们感谢deepseek
#include <graphics.h>
#include <cstdio>
#include <vector>
#include <ctime>
#include <iostream>
#pragma comment(lib, "MSIMG32.LIB")
#pragma comment(lib,"Winmm.lib")
using namespace std;

const int width = 400;//画布的宽
const int height = 600;//画布的高

// 基类:游戏资源加载器
class GameResource {
protected:
    IMAGE back;//背景图
    TCHAR s[50];

public:
    virtual void loadIMG() = 0;//加载所需图片

    void putimage_alpha(int x, int y, IMAGE* img) {//呈现透明底图片
        int w = img->getwidth();
        int h = img->getheight();
        AlphaBlend(GetImageHDC(NULL), x, y, w, h, GetImageHDC(img), 0, 0, w, h, { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA });
    }

    IMAGE& getBackImage() { return back; }
};

// 飞机大战资源
class PlaneResource : public GameResource {
private:
    IMAGE heart;//飞机生命标识
    IMAGE explosion;//敌机爆炸效果
    IMAGE player_img;//玩家的图片
    IMAGE end_img;//结算分数时的图片
    IMAGE enemy_img[5];//五张敌机的图片
    IMAGE great[6];//获得多次连击时的鼓励图片 6张

public:
    void loadIMG() override {//加载所需图片
        loadimage(&back, _T("game\\background1.jpg"), width, height);//背景图
        loadimage(&heart, _T("game\\heart.png"), 20, 20);//生命值标识
        loadimage(&explosion, _T("game\\explosion1.png"), 60, 60);//爆炸效果
        loadimage(&player_img, _T("game\\player.png"), 60, 60);//玩家形象
        loadimage(&end_img, _T("game\\end.png"), 100, 75);//游戏结束的图片

        for (int i = 0; i < 5; ++i) {//加载敌机,5张
            _stprintf_s(s, _T("game\\enemy%d.png"), i + 1);
            loadimage(&enemy_img[i], s, 40, 40);
        }

        for (int i = 0; i < 6; ++i) {//打出combo会有鼓励图片,6张
            _stprintf_s(s, _T("game\\great%d.png"), i + 1);
            loadimage(&great[i], s, 100, 100);
        }
    }

    IMAGE& getHeart() { return heart; }
    IMAGE& getExplosion() { return explosion; }
    IMAGE& getPlayerImg() { return player_img; }
    IMAGE& getEndImg() { return end_img; }
    IMAGE& getEnemyImg(int index) { return enemy_img[index]; }
    IMAGE& getGreatImg(int index) { return great[index]; }
};

// 图案记忆资源
class MemoryResource : public GameResource {
private:
    IMAGE find_img[6];//6张记忆图片
    IMAGE find_back; // 图片背面
    IMAGE succ;//结算图片

public:
    void loadIMG() override {
        loadimage(&back, _T("game\\background4.jpg"), width, height);//图案记忆的背景图
        loadimage(&find_back, _T("game\\background3.jpg"), 130, 130);//卡片背面
        loadimage(&succ, _T("game\\success.png"), width - 100, 100);//游戏结束的图片congratulations

        for (int i = 0; i < 6; ++i) {//图案记忆中的6张图片
            _stprintf_s(s, _T("game\\find%d.jpg"), i + 1);
            loadimage(&find_img[i], s, 130, 130);
        }
    }

    IMAGE& getFindImg(int index) { return find_img[index]; }
    IMAGE& getFindBack() { return find_back; }
    IMAGE& getSucc() { return succ; }
};

// 游戏基类
class Game {
public:
    virtual void init() = 0;
    virtual void play() = 0;
    virtual ~Game() {}
};

// 飞机类
class Plane {
private:
    int x, y;//坐标
    int life;//生命值
    bool alive;//是否存活

public:
    Plane(int x, int y) : x(x), y(y), life(3), alive(true) {}

    int getX() const { return x; }
    int getY() const { return y; }
    int getLife() const { return life; }
    bool isAlive() const { return alive; }

    void setX(int newX) { x = newX; }
    void setY(int newY) { y = newY; }
    void setAlive(bool status) { alive = status; }//更新存活状态
    void decreaseLife() { life--; }//每次减一
};

// 敌机类
class Enemy {
private:
    int x, y;//坐标
    int speed;//速度
    int rank_img;//敌机图片
    int die_frame;//死亡帧数
    bool alive;//是否存活

public:
    Enemy(int x, int y, int speed, int rank_img) :
        x(x), y(y), speed(speed), rank_img(rank_img), die_frame(0), alive(true) {
    }

    int getX() const { return x; }
    int getY() const { return y; }
    int getSpeed() const { return speed; }
    int getRankImg() const { return rank_img; }
    int getDieFrame() const { return die_frame; }
    bool isAlive() const { return alive; }

    void setY(int newY) { y = newY; }
    void setAlive(bool status) { alive = status; }
    void setDieFrame(int frame) { die_frame = frame; }
    void move() { y += speed; }//移动
};

// 子弹类
class Bullet {
private:
    int x, y;//坐标
    bool alive;//是否存在

public:
    Bullet(int x, int y) : x(x), y(y), alive(true) {}

    int getX() const { return x; }
    int getY() const { return y; }
    bool isAlive() const { return alive; }

    void setY(int newY) { y = newY; }
    void setAlive(bool status) { alive = status; }
};

// 飞机大战游戏
class PlaneGame : public Game {
private:
    PlaneResource res;//资源
    Plane plane;//玩家
    vector<Enemy> enemies;//敌机
    vector<Bullet> bullets;//子弹

    bool LEFT, RIGHT;//左右移动状态
    int score, combo;//分数,连击数
    int frame_count, great_index;//记录帧数,连击显示的图片
    int level;//难度等级,每1000帧增加一
    TCHAR s[50];

    const int ball_r = 8;//子弹半径
    const int ball_speed = 15;//子弹速度
    const int dx = 6;//玩家飞机移动速度
    const int FRAME = 60;//帧数
    const int ENEMY_SPAWN_INTERVAL = 30;// 敌机生成间隔(帧数)

public:
    PlaneGame() : plane(width / 2, height - 50), score(0), combo(0),
        frame_count(0), great_index(0), level(0),
        LEFT(false), RIGHT(false) {
        res.loadIMG();
    }

    void init() override {
        //绘制开始界面
        res.putimage_alpha(0, 0, &res.getBackImage());
        rectangle(width / 2 - 200, height / 2 - 50, width / 2 + 200, height / 2 + 50);
        setbkmode(TRANSPARENT);
        TCHAR ss[10] = _T("开始游戏");
        settextstyle(50, 0, _T("宋体"));
        outtextxy(width / 2 - textwidth(ss) / 2, height / 2 - textheight(ss) / 2, ss);
        settextstyle(20, 0, _T("宋体"));
        outtextxy(width / 2 - 200, height / 2 + 50, _T("方向键控制飞机,空格发射子弹"));//操作说明
        FlushBatchDraw();
        //鼠标点击“开始游戏”按钮才能继续游戏
        ExMessage m;
        while (true) {
            m = getmessage(EX_MOUSE);
            if (m.message == WM_LBUTTONDOWN) {//鼠标左键
                if (m.x >= width / 2 - 200 && m.x <= width / 2 + 200 &&
                    m.y >= height / 2 - 50 && m.y <= height / 2 + 50)
                    break;
            }
        }
    }

    void produce_enemy() {//产生敌机
        enemies.push_back(Enemy(rand() % 20 * 40 + 20, 30, rand() % 3 + 2 + level, rand() % 5));
        //速度随level增加,越来越快
    }

    void produce_ball() {//产生子弹
        bullets.push_back(Bullet(plane.getX(), plane.getY()));
    }

    void solve_input() {//处理输入
        ExMessage msg;
        while (peekmessage(&msg)) {
            if (msg.message == WM_KEYDOWN) {
                switch (msg.vkcode) {
                case 'a':
                case 'A':
                case VK_LEFT:
                    LEFT = true;//向左
                    break;
                case 'd':
                case 'D':
                case VK_RIGHT:
                    RIGHT = true;//向右
                    break;
                case ' ':
                    produce_ball();//攻击
                    break;
                }
            }
            else if (msg.message == WM_KEYUP) {
                switch (msg.vkcode) {
                case 'a':
                case 'A':
                case VK_LEFT:
                    LEFT = false;
                    break;
                case 'd':
                case 'D':
                case VK_RIGHT:
                    RIGHT = false;
                    break;
                }
            }
        }
    }

    void move() {
        //处理飞机移动和子弹发射
        for (auto& enemy : enemies) if (enemy.isAlive()) {
            enemy.move();
            //敌机超出屏幕视为死亡
            if (enemy.getY() + res.getEnemyImg(0).getheight() / 2 > height)
                enemy.setAlive(false);
        }

        for (auto& bullet : bullets) if (bullet.isAlive()) {
            bullet.setY(bullet.getY() - ball_speed);
            //子弹超出屏幕视为死亡
            if (bullet.getY() - ball_r < 0) {
                bullet.setAlive(false);
                combo = 0;//子弹落空,连击清空
            }
        }

        //使用LEFT RIGHT记录状态 比 处理按键时进行移动 会更流畅,不会卡顿
        if (LEFT) plane.setX(max(30, plane.getX() - dx));
        if (RIGHT) plane.setX(min(width - 30, plane.getX() + dx));
    }

    bool check_rec(int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2) {
        //矩形检测碰撞
        //分别是 a左上角 a右下角 b左上角 b右下角 的坐标
        return (ax1<bx2 && ax2>bx1 && ay1<by2 && ay2>by1);
    }

    bool check_collision() {
        // 敌机和子弹碰撞
        for (auto& bullet : bullets) if (bullet.isAlive()) {
            for (auto& enemy : enemies) if (enemy.isAlive()) {
                if (check_rec(bullet.getX() - ball_r, bullet.getY() - ball_r,
                    bullet.getX() + ball_r, bullet.getY() + ball_r,
                    enemy.getX() - 20, enemy.getY() - 20,
                    enemy.getX() + 20, enemy.getY() + 20)) {
                    bullet.setAlive(false);
                    enemy.setAlive(false);
                    enemy.setDieFrame(frame_count);//记录在第几帧被攻击
                    combo++;//记录连击
				    //分数:每架敌机10分,连击数越多附加分数越高
				    //连击1-10每架敌机增益2分
				    //连击11-20每架敌机增益4分
				    //依次类推
                    score = score + 10 + (combo / 10 + 1) * 2;
                }
            }
        }

        //玩家和敌机碰撞
        for (auto& enemy : enemies) if (enemy.isAlive()) {
            if (check_rec(enemy.getX() - 20, enemy.getY() - 20,
                enemy.getX() + 20, enemy.getY() + 20,
                plane.getX() - 30, plane.getY() - 30,
                plane.getX() + 30, plane.getY() + 30)) {
                plane.decreaseLife(); //一共三条命
                enemy.setAlive(false);
                enemy.setDieFrame(frame_count);
                combo++;
                score = score + 10 + (combo / 10 + 1) * 2;
                if (plane.getLife() <= 0) {
                    plane.setAlive(false);
                    return false;
                }
            }
        }
        return true;
    }

    void draw() {//绘制飞机大战
        putimage(0, 0, &res.getBackImage());
        for (auto& enemy : enemies) {
            if (enemy.isAlive()) {
                res.putimage_alpha(enemy.getX() - 20, enemy.getY() - 20,
                    &res.getEnemyImg(enemy.getRankImg()));
            }
            else {
                if (abs(enemy.getDieFrame() - frame_count) < 5) {
                    res.putimage_alpha(enemy.getX() - 30, enemy.getY() - 30,
                        &res.getExplosion());//爆炸效果维持9帧,太短则看不清楚爆炸效果
                }
            }
        }

        for (auto& bullet : bullets) if (bullet.isAlive()) {
            //子弹是实心圆
            solidcircle(bullet.getX(), bullet.getY(), ball_r);
        }

        res.putimage_alpha(plane.getX() - 30, plane.getY() - 30,
            &res.getPlayerImg());

        int x = plane.getX() - 30;//x是飞机的左边界
        for (int i = 1; i <= plane.getLife(); ++i) {//绘制生命值
            res.putimage_alpha(x, plane.getY() + 30, &res.getHeart());
            x += 20;//生命值图片大小20*20
        }

        TCHAR s[30];
        _stprintf_s(s, _T("score %d"), score); // 显示分数
        outtextxy(0, 15, s);
        _stprintf_s(s, _T("combo %d"), combo);//显示连击数
        outtextxy(0, 40, s);

        if (combo % 10 == 7)//连击每10帧出现一次,每次随机若干张显示
            res.putimage_alpha(width - 100, 0, &res.getGreatImg(great_index));
        if ((frame_count + 1) / 100 != frame_count / 100)
            great_index = rand() % 6;

        FlushBatchDraw();
    }

    void gameover() {//飞机大战结束界面
        cleardevice();
        putimage(0, 0, &res.getBackImage()); // 小图案
        _stprintf_s(s, _T("Great! You got %d scores!"), score);//分数
        outtextxy(width / 2 - textwidth(s) / 2, height / 2 - textheight(s) / 2, s);
        res.putimage_alpha(0, height / 2 + textheight(s) / 2, &res.getEndImg());
        FlushBatchDraw();
        Sleep(2000);
    }

    void play() override {
        init();
        setbkmode(TRANSPARENT);
        settextstyle(30, 0, _T("Noto Sans SC Black"));

        while (plane.isAlive()) {
            DWORD begin = GetTickCount();
            frame_count++;
            if (frame_count % 1000 == 0) level++;//每1000帧难度+1
            if (frame_count % (max(10, ENEMY_SPAWN_INTERVAL - level * 2)) == 0)
                produce_enemy();

            solve_input();
            move();//更新敌机和子弹位置
            check_collision();//碰撞检测
            draw(); // 绘制

            //帧延时
            DWORD end = GetTickCount();
            DWORD dt = end - begin;
            if (dt < 1000 / FRAME)
                Sleep(1000 / FRAME - dt);
        }

        gameover();
    }
};

// 卡牌类
class Card {
private:
    int id; //从左到右,从上到下,0-11的编号
    int pattern_id;//图片编号
    bool found;//是否被配对

public:
    Card(int id, int pattern_id) : id(id), pattern_id(pattern_id), found(false) {}

    int getId() const { return id; }
    int getPatternId() const { return pattern_id; }
    bool isFound() const { return found; }

    void setFound(bool status) { found = status; }
};

// 图案记忆游戏
class MemoryGame : public Game {
private:
    MemoryResource res;//资源
    vector<Card> cards;//卡牌
    int xlocation_roll[3] = { 0, 135, 270 };//每一列左边界
    int ylocation_line[4] = { 0, 155, 310, 465 };//每一行上边界
    int success;//已成功配对几张
    int cnt_rand[6];//用于随机分布图片的桶
    TCHAR s[50];

public:
    MemoryGame() : success(0) {
        res.loadIMG();
        for (int i = 0; i < 6; ++i) cnt_rand[i] = 0;
        for (int i = 0, x; i < 12; ++i) {//随机分配图案
            x = rand() % 6;
            while (cnt_rand[x] >= 2) x = rand() % 6;
            cards.push_back(Card(i, x));
            cnt_rand[x]++;
        }
    }

    void init() override {
        cleardevice();
        putimage(0, 0, &res.getBackImage());
        _stprintf_s(s, _T("接下来你每次可以翻两张牌,直到全部配对"));
        outtextxy(width / 2 - textwidth(s) / 2, height / 2 - textheight(s) / 2, s);
        FlushBatchDraw();
        Sleep(2000);//游戏规则显示两秒后自动进入游戏
    }

    void show(int id, IMAGE* img) {
        //id从0-11,一共12张
        //显示卡牌id的图案
        putimage(xlocation_roll[id % 3], ylocation_line[id / 3], img);
    }

    int findid(int x, int y) {
        //(x,y)坐标处是几号卡牌
        for (int i = 0; i < 4; ++i)
            for (int j = 0; j < 3; ++j) {
                int id = i * 3 + j;
                if (x >= xlocation_roll[j] && x <= xlocation_roll[j] + 130 &&
                    y >= ylocation_line[i] && y <= ylocation_line[i] + 130) {
                    return id;
                }
            }
        return -1;
    }

    void play() override {
        init();

        //开始游戏
        //每次翻两张牌
        int fir, sec;
        ExMessage m;

        while (success < 6) {//success记录已经成功配对的数量,一共6对
            cleardevice();
            putimage(0, 0, &res.getBackImage());

            for (const auto& card : cards) {//未配对的显示卡牌背面,已配对的显示卡牌正面
                if (!card.isFound())
                    show(card.getId(), &res.getFindBack());
                else
                    show(card.getId(), &res.getFindImg(card.getPatternId()));
            }

            FlushBatchDraw();

            //第一张牌
            while (true) {
                m = getmessage(EX_MOUSE);
                if (m.message == WM_LBUTTONDOWN) {
                    //点中卡牌并且是卡牌背面
                    if ((fir = findid(m.x, m.y)) >= 0 && !cards[fir].isFound())
                        break;
                }
            }

            show(fir, &res.getFindImg(cards[fir].getPatternId()));
            FlushBatchDraw();

            //第二张牌
            while (true) {
                m = getmessage(EX_MOUSE);
                if (m.message == WM_LBUTTONDOWN) {
                    //点中卡牌并且不和第一张相同并且是卡牌背面
                    if ((sec = findid(m.x, m.y)) >= 0 && sec != fir && !cards[sec].isFound())
                        break;
                }
            }

            show(sec, &res.getFindImg(cards[sec].getPatternId()));
            FlushBatchDraw();
            Sleep(500);

            //成功配对
            if (cards[fir].getPatternId() == cards[sec].getPatternId()) {
                success++;
                cards[fir].setFound(true);
                cards[sec].setFound(true);

                if (cards[fir].getPatternId() == 0)
                    mciSendString(_T("play game\\binli.mp3"), 0, 0, 0);
                else
                    mciSendString(_T("play game\\right.mp3"), 0, 0, 0);//正确配对音频
            }
            else
                mciSendString(_T("play game\\wrong.mp3"), 0, 0, 0);//错误配对音频
        }

        res.putimage_alpha(width / 2 - res.getSucc().getwidth() / 2,
            height / 2 - res.getSucc().getheight() / 2, &res.getSucc());
        FlushBatchDraw();
        mciSendString(_T("play game\\end.mp3"), 0, 0, 0); // 游戏成功音频
    }
};

// 过渡界面
void GuoDu() {
    cleardevice();
    IMAGE back;
    loadimage(&back, _T("game\\background1.jpg"), width, height);
    putimage(0, 0, &back);

    TCHAR s[50];
    _stprintf_s(s, _T("Do you want to play a hidden game?"));
    outtextxy(width / 2 - textwidth(s) / 2, height / 2 - textheight(s) / 2, s);

    _stprintf_s(s, _T("YES (CLICK ME!)"));
    outtextxy(width / 2 - textwidth(s) / 2, height / 2 + textheight(s), s);

    FlushBatchDraw();

    ExMessage m;
    while (true) {
        m = getmessage(EX_MOUSE);
        if (m.message == WM_LBUTTONDOWN) {
            if (m.x >= width / 2 - textwidth(s) / 2 && m.x <= width / 2 + textwidth(s) / 2 &&
                m.y >= height / 2 + textheight(s) && m.y <= height / 2 + textheight(s) * 2)
                break;
        }
    }
    mciSendString(_T("play game\\start.mp3"), 0, 0, 0);//开始游戏音频
}

int main() {
    srand(time(NULL));
    initgraph(width, height);

    mciSendString(_T("open game\\bkmusic.mp3 alias bkmusic"), 0, 0, 0);
    mciSendString(_T("play bkmusic repeat"), 0, 0, 0);

    BeginBatchDraw();

    // 创建飞机游戏并运行
    PlaneGame planeGame;
    planeGame.play();

    mciSendString(_T("close bkmusic"), 0, 0, 0);
    GuoDu();

    // 创建记忆游戏并运行
    MemoryGame memoryGame;
    memoryGame.play();

    EndBatchDraw();
    system("pause");
    closegraph();
    return 0;
}
posted @ 2025-07-22 22:05  弗阴  阅读(3)  评论(0)    收藏  举报