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;
}

处理鼠标信息
#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;
}

浙公网安备 33010602011771号