参考

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

C++入门——实现“祖玛”游戏相关推荐

  1. 贪吃蛇的使命之零基础入门贪吃蛇游戏(附演示地址)

    作者: C you again,从事软件开发 努力在IT搬砖路上的技术小白 公众号: [C you again],分享计算机类毕业设计源码.IT技术文章.游戏源码.网页模板.程序人生等等.公众号回复 ...

  2. ​LeetCode刷题实战488:祖玛游戏

    算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试.所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 ! 今天和大家 ...

  3. java祖玛_Canvas小练习_祖玛游戏01

    这几天学习了一个祖玛游戏的小Demo,记录下这个小游戏的编写过程. 1:首先实现的一个简单界面效果. 2:在图形绘制完成后,让黑色小球沿着圆形弧线运动. 编码过程 创建画布,绘制圆形弧线. 首先在Ht ...

  4. Leetcode 488.祖玛游戏

    祖玛游戏 回忆一下祖玛游戏.现在桌上有一串球,颜色有红色(R),黄色(Y),蓝色(B),绿色(G),还有白色(W). 现在你手里也有几个球. 每一次,你可以从手里的球选一个,然后把这个球插入到一串球中 ...

  5. Java实现 LeetCode 488 祖玛游戏

    488. 祖玛游戏 回忆一下祖玛游戏.现在桌上有一串球,颜色有红色®,黄色(Y),蓝色(B),绿色(G),还有白色(W). 现在你手里也有几个球. 每一次,你可以从手里的球选一个,然后把这个球插入到一 ...

  6. 2021-11-09祖玛游戏

    488. 祖玛游戏 难度困难208 你正在参与祖玛游戏的一个变种. 在这个祖玛游戏变体中,桌面上有 一排 彩球,每个球的颜色可能是:红色 'R'.黄色 'Y'.蓝色 'B'.绿色 'G' 或白色 'W ...

  7. LeetCode每日一题488. 祖玛游戏

    488. 祖玛游戏 你正在参与祖玛游戏的一个变种. 在这个祖玛游戏变体中,桌面上有 一排 彩球,每个球的颜色可能是:红色 'R'.黄色 'Y'.蓝色 'B'.绿色 'G' 或白色 'W' .你的手中也 ...

  8. 祖玛游戏python

    你正在参与祖玛游戏的一个变种. 在这个祖玛游戏变体中,桌面上有 一排 彩球,每个球的颜色可能是:红色 'R'.黄色 'Y'.蓝色 'B'.绿色 'G' 或白色 'W' .你的手中也有一些彩球. 你的目 ...

  9. 力扣解题思路:488. 祖玛游戏

    488. 祖玛游戏 思路: 实际上就是简单的消消乐,如果时间允许,最简单的暴力递归法也是可以的,就是把所有字母插入所有的位置,取最短且可以消去的插入球数即可.但是这样无脑插入是很浪费时间的,所以我们在 ...

最新文章

  1. 实战篇一 python常用模块和库介绍
  2. SAP 销售与分销模块定价完全实施手册(SAP SD Pricing Handbook)
  3. 【无标题】RestHighLevelClient工具类
  4. asp-Webshell免杀
  5. linux下面拷贝gbk编码的网页
  6. Asp.Net中using的使用的方法(转)
  7. 【JAVA基础篇】抽象类和接口的区别
  8. 华为p20有没有计算机,用华为P20不知道这三个功能就亏大了!第一个很少人知道!...
  9. 网易2017春招笔试真题编程题集合
  10. hello word!------为什么开通博客以及自我介绍
  11. php实现观看记录,PHP实现浏览历史记录
  12. AC/DC、DC/DC转换器基础指南(二)
  13. 全网音乐下载,抖音热歌、无损音乐下载
  14. Redisson读写锁
  15. 服务器打补丁重启时候系统掉,服务器自动重启我的服务器windowssever高级版,但每次开 爱问知识人...
  16. MySql自动同步主库数据(Canal)
  17. Android系统编译aosp
  18. 让IE浏览器打开JSON文件直接显示
  19. java棒棒糖和皮卡丘八音盒_KFC用玩具提前要走了压岁钱
  20. 机器人军团防护罩_冒险岛贴吧 - 1000A导轨保护罩Y轴保护盖板的简单介绍

热门文章

  1. 春招季如何横扫 Javascript 面试核心考点(基础版)?
  2. Elasticsearch terms查询
  3. 使用vue开发钉钉H5微应用
  4. QPS常用的测试以及优化方法
  5. MyBatis以及Druid 防止sql注入攻击
  6. linux未被识别crond服务,crond服务不能启动
  7. 五个值得学习和收藏的英文网址
  8. 春季饮食红枣的妙用之处
  9. Linux系统设置全局代理(http代理,socks代理)
  10. 「JVM 内存管理」低延迟的 Shenandoah GC 与 ZGC