2025年C++入门——实现“祖玛”游戏

C++入门——实现“祖玛”游戏参考 C 和 C 游戏趣味编程 童晶 祖玛游戏 游戏的思路是 各种颜色的小球沿着轨道移动 玩家必须阻止小球进入轨道终点的城堡 玩家移动鼠标控制炮台旋转 按下鼠标右键更换小球颜色 点击鼠标左键发射小球 发射的小球进入轨道 如果周围有连续 3 个相同颜色的小球即可消除 顶点类

大家好,我是讯享网,很高兴认识大家。

参考

  1. 《C和C++游戏趣味编程》 童晶

祖玛游戏

游戏的思路是,各种颜色的小球沿着轨道移动,玩家必须阻止小球进入轨道终点的城堡。玩家移动鼠标控制炮台旋转,按下鼠标右键更换小球颜色,点击鼠标左键发射小球。发射的小球进入轨道,如果周围有连续3个相同颜色的小球即可消除
在这里插入图片描述
讯享网

顶点类

游戏中小球会沿着固定的轨迹移动,而轨迹可由一些离散的顶点组成,定义顶点类:

class Point { 
    public: float x, y; Point() { 
    } Point(float ix, float iy) { 
    x = ix; y = iy; } }; 

讯享网

轨迹类

在Point的基础上,定义轨迹类,并添加draw()成员函数基于关键点绘制圆和连线:

讯享网class Path { 
    public: vector<Point> keyPoints; void draw() { 
    setlinecolor(RGB(0, 0, 0)); setfillcolor(RGB(0, 0, 0)); for (int i = 0; i < keyPoints.size(); i++) { 
    fillcircle(keyPoints[i].x, keyPoints[i].y, 8); } for (int i = 0; i < keyPoints.size() - 1; i++) { 
    line(keyPoints[i].x, keyPoints[i].y, keyPoints[i + 1].x, keyPoints[i + 1].y); } } }; 

进一步,在关键点的连线上进行等间隔采样,小球可以在这些稠密的采样点上移动,从而实现运动的效果:

class Path { 
    public: vector<Point> keyPoints; float sampleInterval; // 采样间隔 vector<Point> allPoints; // 所有采样得到的点 void getAllPoints() { 
    int i; for (i = 0; i < keyPoints.size() - 1; i++) { 
    float xd = keyPoints[i + 1].x - keyPoints[i].x; float yd = keyPoints[i + 1].y - keyPoints[i].y; float length = sqrt(xd * xd + yd * yd); int num = length / sampleInterval; for (int j = 0; j < num; j++) { 
    float x_sample = keyPoints[i].x + j * xd / num; float y_sample = keyPoints[i].y + j * yd / num; allPoints.push_back(Point(x_sample, y_sample)); } } } void draw() { 
    setlinecolor(RGB(0, 0, 0)); setfillcolor(RGB(0, 0, 0)); for (int i = 0; i < keyPoints.size() - 1; i++) // 将关键点依次连接形成多条线段 { 
    line(keyPoints[i].x, keyPoints[i].y, keyPoints[i + 1].x, keyPoints[i + 1].y); } for (int i = 0; i < allPoints.size(); i++) // 在所有采样点处画一个小圆点 { 
    fillcircle(allPoints[i].x, allPoints[i].y, 3); } } 

绘制出所有的采样点:

讯享网#include <graphics.h> #include <conio.h> #include <vector> using namespace std; #define WIDTH 1000 #define HEIGHT 700 class Point { 
    public: float x, y; Point() { 
    } Point(float ix, float iy) { 
    x = ix; y = iy; } }; class Path { 
    public: vector<Point> keyPoints; float sampleInterval; // 采样间隔 vector<Point> allPoints; // 所有采样得到的点 void getAllPoints() { 
    int i; for (i = 0; i < keyPoints.size() - 1; i++) { 
    float xd = keyPoints[i + 1].x - keyPoints[i].x; float yd = keyPoints[i + 1].y - keyPoints[i].y; float length = sqrt(xd * xd + yd * yd); int num = length / sampleInterval; for (int j = 0; j < num; j++) { 
    float x_sample = keyPoints[i].x + j * xd / num; float y_sample = keyPoints[i].y + j * yd / num; allPoints.push_back(Point(x_sample, y_sample)); } } } void draw() { 
    setlinecolor(RGB(0, 0, 0)); setfillcolor(RGB(0, 0, 0)); for (int i = 0; i < keyPoints.size() - 1; i++) // 将关键点依次连接形成多条线段 { 
    line(keyPoints[i].x, keyPoints[i].y, keyPoints[i + 1].x, keyPoints[i + 1].y); } for (int i = 0; i < allPoints.size(); i++) // 在所有采样点处画一个小圆点 { 
    fillcircle(allPoints[i].x, allPoints[i].y, 3); } } }; Path path; void startup() { 
    initgraph(WIDTH, HEIGHT); setbkcolor(WHITE); cleardevice(); path.keyPoints.push_back(Point(100, 600)); path.keyPoints.push_back(Point(900, 600)); path.keyPoints.push_back(Point(900, 100)); path.keyPoints.push_back(Point(100, 100)); path.sampleInterval = 10; path.getAllPoints(); BeginBatchDraw(); } void show() { 
    cleardevice(); path.draw(); FlushBatchDraw(); Sleep(1); } int main() { 
    startup(); while (1) { 
    show(); } return 0; } 

在这里插入图片描述

小球类

小球一共有5种颜色,定义数组存储:

#define ColorNum 5 COLORREF colors[ColorNum] = { 
    RED, BLUE, GREEN, YELLOW, MAGENTA }; 

定义小球类,添加成员函数movetoIndexInPath()让小球移动到轨迹相应位置:

讯享网class Ball { 
    public: Point center; float radius; int colorId; int indexInPath; // 小球位置在Path的allPoints中的对应序号 void draw() { 
    setlinecolor(colors[colorId]); setfillcolor(colors[colorId]); fillcircle(center.x, center.y, radius); } void movetoIndexPath(Path path) { 
    center = path.allPoints[indexInPath]; } void initiate(Path path) { 
    radius = Radius; indexInPath = 0; movetoIndexPath(path); colorId = rand() % ColorNum; } }; 

修改startup(),向轨迹中放置30个小球:

path.sampleInterval = Radius / 5; path.getAllPoints(); for (int i = 30; i >= 0; i--) { 
    Ball ball; ball.initiate(path); ball.indexInPath = i * (2 * ball.radius / path.sampleInterval); ball.movetoIndexPath(path); balls.push_back(ball); } 

在这里插入图片描述

小球自动运动

在Ball类中新增成员变量direction,设定小球沿着轨迹线的移动方向,1向终点运动,-1向起点运动,0暂停。initiate()中让小球初始状态为静止:

讯享网int direction; void initiate(Path path) { 
    radius = Radius; indexInPath = 0; direction = 0; movetoIndexPath(path); colorId = rand() % ColorNum; } 

定义changeIndexbyDirection()成员函数,用于判断移动方向并移动:

void changeIndexbyDirection(Path path) { 
    if (direction == 1 && indexInPath + 1 < path.allPoints.size()) { 
    indexInPath++; } else if (direction == -1 && indexInPath - 1 >= 0) { 
    indexInPath--; } } 

小球运动的源动力来自于最后一个在起点处添加的小球。将最接近起点的小球的direction设为1,如果终点方向前面一个小球和这个小球相切,则其direction也等于1,否则为0。在updateWithoutInput()中实现:

讯享网void updateWithoutInput() { 
    if (balls[0].indexInPath >= path.allPoints.size() - 1) // 第一个求到了终点,游戏失败 { 
    return; } int i; for (i = 0; i < balls.size(); i++) { 
    balls[i].direction = 0; } i = balls.size() - 1; // 最后一个小球 balls[i].direction = 1; // 最后一个小球向前运动 while (i > 0) { 
    if (balls[i - 1].indexInPath - balls[i].indexInPath <= 2 * Radius / path.sampleInterval) // 前后两个小球相切 { 
    balls[i - 1].direction = 1; balls[i - 1].indexInPath = balls[i].indexInPath + 2 * Radius / path.sampleInterval; i--; } else { 
    break; } } for (int i = 0; i < balls.size(); i++) // 小球根据direction更新位置 { 
    balls[i].movetoIndexPath(path); balls[i].changeIndexbyDirection(path); } Sleep(50); } 

在这里插入图片描述

小球的插入与消除

遍历balls中所有小球,找到被鼠标点击的小球,然后将当前小球复制一份,插入到被点击的小球的位置,前面的小球依次向轨迹线终点方向移动:

void updateWithInput() { 
    int i, j; ExMessage e; while (peekmessage(&e)) { 
    if (e.message == WM_LBUTTONDOWN) { 
    for (i = 0; i < balls.size(); i++) { 
    float distance = Distance(balls[i].center.x, balls[i].center.y, e.x, e.y); if (distance < Radius) { 
    Ball fireball = balls[i]; balls.insert(balls.begin() + i, fireball); // 复制一个小球插入 for (j = i; j >= 0; j--) { 
    if (balls[j].indexInPath - balls[j + 1].indexInPath <= 0) { 
    balls[j].indexInPath = balls[j + 1].indexInPath + 2 * Radius / path.sampleInterval; } else { 
    break; } } return; } } } } } 

对被鼠标点击的小球周围进行判断,如果有位置连续、颜色相同且数目大于等于3的小球,就将这些小球删除:

讯享网int eraseSameColorBalls(int i, Ball fireball, Path& path, vector<Ball>& balls) { 
    vector<int> sameColorIndexes; // 记录与插入小球颜色一样的序号 int forward = i; int backward = i; sameColorIndexes.push_back(i); while (forward > 0 && balls[forward].colorId == fireball.colorId) // 向终点方向寻找 { 
    sameColorIndexes.push_back(forward); if (balls[forward - 1].indexInPath - balls[forward].indexInPath > 2 * Radius / path.sampleInterval) { 
    break; } forward--; } if (forward == 0 && balls[0].colorId == fireball.colorId) // 临界情况 { 
    sameColorIndexes.push_back(forward); } while (backward < balls.size() - 1 && balls[backward].colorId == fireball.colorId) // 向起点方向寻找 { 
    sameColorIndexes.push_back(backward); if (balls[backward].indexInPath - balls[backward + 1].indexInPath > 2 * Radius / path.sampleInterval) { 
    break; } backward++; } if (backward == balls.size() - 1 && balls[balls.size() - 1].colorId == fireball.colorId) { 
    sameColorIndexes.push_back(backward); } sort(sameColorIndexes.begin(), sameColorIndexes.end()); vector<int>::iterator ite = unique(sameColorIndexes.begin(), sameColorIndexes.end()); // 去重 sameColorIndexes.erase(ite, sameColorIndexes.end()); int NumSameColors = sameColorIndexes.size(); if (NumSameColors >= 3) { 
    int minIndex = sameColorIndexes[0]; int maxIndex = sameColorIndexes[NumSameColors - 1]; balls.erase(balls.begin() + minIndex, balls.begin() + maxIndex + 1); // 删除同颜色小球 return NumSameColors; } return 0; } 

炮台类

定义炮台类:

class Cannon { 
    public: IMAGE im; IMAGE im_rotate; float x, y; Ball ball; float angle; void draw() { 
    rotateimage(&im_rotate, &im, angle, RGB(160, 211, 255), false, false); putimage(x - im.getwidth() / 2, y - im.getheight() / 2, &im_rotate); ball.draw(); } void setBallPosition() { 
    ball.center.x = x + 100 * cos(angle); ball.center.y = y + 100 * sin(angle); } }; 

在startup()中初始化背景、炮台等:

讯享网loadimage(&im_bk, _T("bk.jpg")); // 导入背景图片 loadimage(&im_role, _T("role.jpg")); // 导入角色图片 loadimage(&im_house, _T("house.jpg")); // 导入家图片 cannon.im = im_role; // 炮台角色图片 cannon.angle = 0; // 初始角度 cannon.x = 500; // 中心坐标 cannon.y = 350; cannon.ball.radius = Radius; // 炮台带的小球的半径 cannon.ball.colorId = rand() % ColorNum; // 炮台小球颜色 cannon.setBallPosition(); // 设置炮台小球的坐标 

在这里插入图片描述

炮台旋转与更改小球颜色

为炮台类添加成员函数updateWithMouseMOVE(),实现炮台跟着鼠标旋转:

void updateWithMouseMOVE(int mx, int my) { 
    float xs = mx - x; float ys = my - y; float length = sqrt(xs * xs + ys * ys); if (length > 4) { 
    angle = atan2(-ys, xs); ball.center.x = x + 100 * xs / length; ball.center.y = y + 100 * ys / length; } } 

添加成员函数updateWithButtonDown(),实现鼠标点击时,改变小球颜色:

讯享网void updateWithRButtonDown() { 
    ball.colorId += 1; if (ball.colorId == ColorNum) { 
    ball.colorId = 0; } } 

然后在updateWithInput()中调用即可

炮台发射小球

当点击鼠标左键时,炮台小球沿着朝向鼠标的射线方向进行移动。判断每帧移动后和balls中的所有小球是否相交,如果没有则继续移动;如果有则将炮台小球插入到当前位置:

if (e.message == WM_LBUTTONDOWN) { 
    cannon.updateWithMouseMOVE(e.x, e.y); // 先更新炮台旋转角度、小球坐标 float vx = (cannon.ball.center.x - cannon.x) / 5; // 小球移动速度 float vy = (cannon.ball.center.y - cannon.y) / 5; int isCollider = 0; while (isCollider == 0 && cannon.ball.center.y > 0 && cannon.ball.center.y < HEIGHT && cannon.ball.center.x > 0 && cannon.ball.center.x < WIDTH) // 没有碰撞和越界 { 
    cannon.ball.center.x += vx; cannon.ball.center.y += vy; show(); // 显示移动轨迹 for (i = 0; i < balls.size(); i++) { 
    float distance = Distance(balls[i].center.x, balls[i].center.y, cannon.ball.center.x, cannon.ball.center.y); if (distance < Radius) // 找到与炮台小球碰撞的小球 { 
    isCollider = 1; cannon.updateWithMouseMOVE(e.x, e.y); // 恢复炮台小球位置 Ball fireball = balls[i]; fireball.colorId = cannon.ball.colorId; balls.insert(balls.begin() + i, fireball); // 复制一个小球插入 int count = eraseSameColorBalls(i, fireball, path, balls); if (count == 0) { 
    for (j = i; j >= 0; j--) { 
    if (balls[j].indexInPath - balls[j + 1].indexInPath <= 0) { 
    balls[j].indexInPath = balls[j + 1].indexInPath + 2 * Radius / path.sampleInterval; } else { 
    break; } } } return; } } } } 

连续出球与胜负判断

定义全局变量gameStatus描述游戏状态,在show()中根据游戏状态显示对应文字:

讯享网setbkmode(TRANSPARENT); settextcolor(RGB(255, 0, 0)); settextstyle(60, 0, _T("宋体")); if (gameStatus == 1) { 
    outtextxy(WIDTH * 0.35, HEIGHT * 0.35, _T("游戏胜利 :)")); } else if (gameStatus == -1) { 
    outtextxy(WIDTH * 0.35, HEIGHT * 0.35, _T("游戏失败 :(")); } 

每隔10秒产生一批小球。如果balls中没有小球且运行时间超过100秒,游戏胜利;如果有球跑到终点,游戏失败:

static clock_t start = clock(); // 第一次运行时刻 clock_t now = clock(); // 当前时刻 int nowSencond = (int(now - start) / CLOCKS_PER_SEC); // 程序目前运行了多少秒 if (nowSencond % 10 == 0 && nowSencond <= 100 && gameStatus == 0) // 每隔10秒,新增一批小球 { 
    Ball ball; ball.initiate(path); balls.push_back(ball); } if (balls.size() == 0) { 
    if (nowSencond > 100) { 
    gameStatus = 1; } return; } if (balls[0].indexInPath >= path.allPoints.size() - 1) // 第一个求到了终点,游戏失败 { 
    gameStatus = -1; return; } 

添加音效

在startup()中循环播放背景音乐:

讯享网mciSendString(_T("open game_music.mp3 alias bkmusic"), NULL, 0, NULL);//打开背景音乐 mciSendString(_T("play bkmusic repeat"), NULL, 0, NULL); // 循环播放 

在updateWithInput()中,消除小球时播放一次消除音效:

if (count >= 3) PlayMusicOnce((TCHAR*)_T("coin.mp3")); // 播放一次金币音效 

添加复杂轨道

讯享网path.keyPoints.push_back(Point(50, 300)); path.keyPoints.push_back(Point(50, 600)); path.keyPoints.push_back(Point(100, 650)); path.keyPoints.push_back(Point(700, 650)); path.keyPoints.push_back(Point(700, 550)); path.keyPoints.push_back(Point(250, 550)); path.keyPoints.push_back(Point(200, 500)); path.keyPoints.push_back(Point(200, 200)); path.keyPoints.push_back(Point(250, 150)); path.keyPoints.push_back(Point(800, 150)); path.keyPoints.push_back(Point(850, 200)); path.keyPoints.push_back(Point(850, 650)); path.keyPoints.push_back(Point(950, 650)); path.keyPoints.push_back(Point(950, 100)); path.keyPoints.push_back(Point(900, 50)); path.keyPoints.push_back(Point(150, 50)); 

在这里插入图片描述

最后对轨道做一些美化:

在这里插入图片描述

完整代码

#include <graphics.h> #include <conio.h> #include <time.h> #include <vector> #include <algorithm> #pragma comment(lib, "Winmm.lib") using namespace std; #define WIDTH 1000 #define HEIGHT 700 #define Radius 25 #define ColorNum 5 COLORREF colors[ColorNum] = { 
    RED, BLUE, GREEN, YELLOW, MAGENTA }; float Distance(float x1, float y1, float x2, float y2) { 
    float xd = x1 - x2; float yd = y1 - y2; float length = sqrt(xd * xd + yd * yd); return length; } void sleep(DWORD ms) // 精确延时函数 { 
    static DWORD oldtime = GetTickCount(); while (GetTickCount() - oldtime < ms) { 
    Sleep(1); } oldtime = GetTickCount(); } void PlayMusicOnce(TCHAR fileName[80]) { 
    TCHAR cmdString1[50]; swprintf_s(cmdString1, _T("open %s alias tmpmusic"), fileName); // 生成命令字符串 mciSendString(_T("close tmpmusic"), NULL, 0, NULL); // 先把前面一次的音乐关闭 mciSendString(cmdString1, NULL, 0, NULL); // 打开音乐 mciSendString(_T("play tmpmusic"), NULL, 0, NULL); // 仅播放一次 } class Point { 
    public: float x, y; Point() { 
    } Point(float ix, float iy) { 
    x = ix; y = iy; } }; class Path { 
    public: vector<Point> keyPoints; float sampleInterval; // 采样间隔 vector<Point> allPoints; // 所有采样得到的点 void getAllPoints() { 
    int i; for (i = 0; i < keyPoints.size() - 1; i++) { 
    float xd = keyPoints[i + 1].x - keyPoints[i].x; float yd = keyPoints[i + 1].y - keyPoints[i].y; float length = sqrt(xd * xd + yd * yd); int num = length / sampleInterval; for (int j = 0; j < num; j++) { 
    float x_sample = keyPoints[i].x + j * xd / num; float y_sample = keyPoints[i].y + j * yd / num; allPoints.push_back(Point(x_sample, y_sample)); } } allPoints.push_back(Point(keyPoints[i].x, keyPoints[i].y)); // 最后一个关键点 } void draw() { 
    setlinecolor(RGB(0, 0, 0)); setfillcolor(RGB(0, 0, 0)); for (int i = 0; i < keyPoints.size() - 1; i++) // 将关键点依次连接形成多条线段 { 
    line(keyPoints[i].x, keyPoints[i].y, keyPoints[i + 1].x, keyPoints[i + 1].y); } for (int i = 0; i < allPoints.size(); i++) // 在所有采样点处画一个小圆点 { 
    fillcircle(allPoints[i].x, allPoints[i].y, 2); } } ~Path() { 
    keyPoints.clear(); allPoints.clear(); } }; class Ball { 
    public: Point center; float radius; int colorId; int indexInPath; // 小球位置在Path的allPoints中的对应序号 int direction; void draw() { 
    setlinecolor(colors[colorId]); setfillcolor(colors[colorId]); fillcircle(center.x, center.y, radius); } void movetoIndexPath(Path path) { 
    center = path.allPoints[indexInPath]; } void initiate(Path path) { 
    radius = Radius; indexInPath = 0; direction = 0; movetoIndexPath(path); colorId = rand() % ColorNum; } void changeIndexbyDirection(Path path) { 
    if (direction == 1 && indexInPath + 1 < path.allPoints.size()) { 
    indexInPath++; } else if (direction == -1 && indexInPath - 1 >= 0) { 
    indexInPath--; } } }; class Cannon { 
    public: IMAGE im; IMAGE im_rotate; float x, y; Ball ball; // 炮台自带的小球 float angle; void draw() { 
    rotateimage(&im_rotate, &im, angle, RGB(160, 211, 255), false, false); putimage(x - im.getwidth() / 2, y - im.getheight() / 2, &im_rotate); ball.draw(); } void setBallPosition() { 
    ball.center.x = x + 100 * cos(angle); ball.center.y = y + 100 * sin(angle); } void updateWithMouseMOVE(int mx, int my) { 
    float xs = mx - x; float ys = my - y; float length = sqrt(xs * xs + ys * ys); if (length > 4) { 
    angle = atan2(-ys, xs); ball.center.x = x + 100 * xs / length; ball.center.y = y + 100 * ys / length; } } void updateWithRButtonDown() { 
    ball.colorId += 1; if (ball.colorId == ColorNum) { 
    ball.colorId = 0; } } }; int eraseSameColorBalls(int i, Ball fireball, Path& path, vector<Ball>& balls) { 
    vector<int> sameColorIndexes; // 记录与插入小球颜色一样的序号 int forward = i; int backward = i; sameColorIndexes.push_back(i); while (forward > 0 && balls[forward].colorId == fireball.colorId) // 向终点方向寻找 { 
    sameColorIndexes.push_back(forward); if (balls[forward - 1].indexInPath - balls[forward].indexInPath > 2 * Radius / path.sampleInterval) { 
    break; } forward--; } if (forward == 0 && balls[0].colorId == fireball.colorId) // 临界情况 { 
    sameColorIndexes.push_back(forward); } while (backward < balls.size() - 1 && balls[backward].colorId == fireball.colorId) // 向起点方向寻找 { 
    sameColorIndexes.push_back(backward); if (balls[backward].indexInPath - balls[backward + 1].indexInPath > 2 * Radius / path.sampleInterval) { 
    break; } backward++; } if (backward == balls.size() - 1 && balls[balls.size() - 1].colorId == fireball.colorId) { 
    sameColorIndexes.push_back(backward); } sort(sameColorIndexes.begin(), sameColorIndexes.end()); vector<int>::iterator ite = unique(sameColorIndexes.begin(), sameColorIndexes.end()); // 去重 sameColorIndexes.erase(ite, sameColorIndexes.end()); int NumSameColors = sameColorIndexes.size(); if (NumSameColors >= 3) { 
    int minIndex = sameColorIndexes[0]; int maxIndex = sameColorIndexes[NumSameColors - 1]; balls.erase(balls.begin() + minIndex, balls.begin() + maxIndex + 1); // 删除同颜色小球 return NumSameColors; } return 0; } Path path; vector<Ball> balls; IMAGE im_role, im_house, im_bk; // 一些图片 Cannon cannon; // 定义炮台对象 int gameStatus = 0; void startup() { 
    mciSendString(_T("open game_music.mp3 alias bkmusic"), NULL, 0, NULL);//打开背景音乐 mciSendString(_T("play bkmusic repeat"), NULL, 0, NULL); // 循环播放 srand(time(0)); initgraph(WIDTH, HEIGHT); cleardevice(); loadimage(&im_bk, _T("bk.jpg")); // 导入背景图片 loadimage(&im_role, _T("role.jpg")); // 导入角色图片 loadimage(&im_house, _T("house.jpg")); // 导入家图片 path.keyPoints.push_back(Point(50, 300)); path.keyPoints.push_back(Point(50, 600)); path.keyPoints.push_back(Point(100, 650)); path.keyPoints.push_back(Point(700, 650)); path.keyPoints.push_back(Point(700, 550)); path.keyPoints.push_back(Point(250, 550)); path.keyPoints.push_back(Point(200, 500)); path.keyPoints.push_back(Point(200, 200)); path.keyPoints.push_back(Point(250, 150)); path.keyPoints.push_back(Point(800, 150)); path.keyPoints.push_back(Point(850, 200)); path.keyPoints.push_back(Point(850, 650)); path.keyPoints.push_back(Point(950, 650)); path.keyPoints.push_back(Point(950, 100)); path.keyPoints.push_back(Point(900, 50)); path.keyPoints.push_back(Point(150, 50)); path.sampleInterval = Radius / 5; path.getAllPoints(); cannon.im = im_role; // 炮台角色图片 cannon.angle = 0; // 初始角度 cannon.x = 500; // 中心坐标 cannon.y = 350; cannon.ball.radius = Radius; // 炮台带的小球的半径 cannon.ball.colorId = rand() % ColorNum; // 炮台小球颜色 cannon.setBallPosition(); // 设置炮台小球的坐标 for (int i = 15; i >= 10; i--) { 
    Ball ball; ball.initiate(path); ball.indexInPath = i * (2 * ball.radius / path.sampleInterval); ball.movetoIndexPath(path); balls.push_back(ball); } for (int i = 5; i >= 0; i--) { 
    Ball ball; ball.initiate(path); ball.indexInPath = i * (2 * ball.radius / path.sampleInterval); ball.movetoIndexPath(path); balls.push_back(ball); } BeginBatchDraw(); } void show() { 
    putimage(0, 0, &im_bk); // 显示背景图片 putimage(30, 10, &im_house); // 显示房子图片 //path.draw(); cannon.draw(); for (int i = 0; i < balls.size(); i++) { 
    balls[i].draw(); } setbkmode(TRANSPARENT); settextcolor(RGB(255, 0, 0)); settextstyle(60, 0, _T("宋体")); if (gameStatus == 1) { 
    outtextxy(WIDTH * 0.35, HEIGHT * 0.35, _T("游戏胜利 :)")); } else if (gameStatus == -1) { 
    outtextxy(WIDTH * 0.35, HEIGHT * 0.35, _T("游戏失败 :(")); } FlushBatchDraw(); } void updateWithoutInput() { 
    static clock_t start = clock(); // 第一次运行时刻 clock_t now = clock(); // 当前时刻 int nowSencond = (int(now - start) / CLOCKS_PER_SEC); // 程序目前运行了多少秒 if (nowSencond % 10 == 0 && nowSencond <= 100 && gameStatus == 0) // 每隔10秒,新增一批小球 { 
    Ball ball; ball.initiate(path); balls.push_back(ball); } if (balls.size() == 0) { 
    if (nowSencond > 100) { 
    gameStatus = 1; } return; } if (balls[0].indexInPath >= path.allPoints.size() - 1) // 第一个求到了终点,游戏失败 { 
    gameStatus = -1; return; } int i; for (i = 0; i < balls.size(); i++) { 
    balls[i].direction = 0; } i = balls.size() - 1; // 最后一个小球 balls[i].direction = 1; // 最后一个小球向前运动 while (i > 0) { 
    if (balls[i - 1].indexInPath - balls[i].indexInPath <= 2 * Radius / path.sampleInterval) // 前后两个小球相切 { 
    balls[i - 1].direction = 1; balls[i - 1].indexInPath = balls[i].indexInPath + 2 * Radius / path.sampleInterval; i--; } else { 
    break; } } for (int i = 0; i < balls.size(); i++) // 小球根据direction更新位置 { 
    balls[i].movetoIndexPath(path); balls[i].changeIndexbyDirection(path); } Sleep(50); } void updateWithInput() { 
    if (gameStatus != 0) { 
    return; } int i, j; ExMessage e; while (peekmessage(&e)) { 
    if (e.message == WM_MOUSEMOVE) { 
    cannon.updateWithMouseMOVE(e.x, e.y); } else if (e.message == WM_RBUTTONDOWN) { 
    cannon.updateWithRButtonDown(); } else if (e.message == WM_LBUTTONDOWN) { 
    cannon.updateWithMouseMOVE(e.x, e.y); // 先更新炮台旋转角度、小球坐标 float vx = (cannon.ball.center.x - cannon.x) / 5; // 小球移动速度 float vy = (cannon.ball.center.y - cannon.y) / 5; int isCollider = 0; while (isCollider == 0 && cannon.ball.center.y > 0 && cannon.ball.center.y < HEIGHT && cannon.ball.center.x > 0 && cannon.ball.center.x < WIDTH) // 没有碰撞和越界 { 
    cannon.ball.center.x += vx; cannon.ball.center.y += vy; show(); // 显示移动轨迹 for (i = 0; i < balls.size(); i++) { 
    float distance = Distance(balls[i].center.x, balls[i].center.y, cannon.ball.center.x, cannon.ball.center.y); if (distance < Radius) // 找到与炮台小球碰撞的小球 { 
    isCollider = 1; cannon.updateWithMouseMOVE(e.x, e.y); // 恢复炮台小球位置 Ball fireball = balls[i]; fireball.colorId = cannon.ball.colorId; balls.insert(balls.begin() + i, fireball); // 复制一个小球插入 int count = eraseSameColorBalls(i, fireball, path, balls); if (count >= 3) { 
    PlayMusicOnce((TCHAR*)_T("coin.mp3")); // 播放一次金币音效 } if (count == 0) { 
    for (j = i; j >= 0; j--) { 
    if (balls[j].indexInPath - balls[j + 1].indexInPath <= 0) { 
    balls[j].indexInPath = balls[j + 1].indexInPath + 2 * Radius / path.sampleInterval; } else { 
    break; } } } return; } } } } } } void gameover() { 
    balls.clear(); } int main() { 
    startup(); while (1) { 
    show(); updateWithoutInput(); updateWithInput(); } gameover(); return 0; } 
小讯
上一篇 2025-01-28 13:35
下一篇 2025-01-17 13:42

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/28316.html