每天一个c语言小项目,提升你的编程能力!
这是个益智游戏,规则很简单,按上、下、左、右键,使游戏区域的所有方块向该方向滑动,每次滑动时,相碰的两个相同数字会合并。同时,空白的地方也会在随机出现一个数字方块(2 或者 4)。游戏目标,是想办法合成 2048 这个数字方块。
简单了解游戏后我们就来试试吧!(直接上源码,大家可以看注释)
代码展示:
/*项目名称:2048开发工具:visual studio 2019,easyx 20190415(beta)作者:洛枫*/#include #include #include #include #include #include #pragma comment( lib, msimg32.lib)// 方块的状态enum state{ exist, // 存在 destory // 销毁};// 二维向量,用于表示位置或者大小struct vector2{ float x; float y;};// 符号函数int sgn(float d){ if (d 0) return 1; return 0;}class block{private: state currentstate; // 当前的状态 state targetstate; // 移动后的状态 vector2 size; vector2 currentpos; // 当前位置 vector2 targetpos; // 目标位置 image *img; image *newimg; float deltapos; // 每秒移动多少位置 float deltasize; // 每秒变大多少 float animationspeed; // 动画速度public: block(const vector2 &pos, image *img) { currentpos = targetpos = pos; currentstate = targetstate = exist; size = { 50,50 }; this->img = this->newimg = img; deltapos = 100; deltasize = 40; animationspeed = 20.0f; } void update(float deltatime){ // 改变方块大小(图片刚生成时的由小到大的动画) if (size.x getwidth()) { size.x = size.y = size.x + deltasize * deltatime * animationspeed / 2; if (size.x > img->getwidth()) { size.x = size.y = (float)img->getwidth(); } } // 更新方块位置 if (currentpos.x != targetpos.x || currentpos.y != targetpos.y) { int directionx = sgn(targetpos.x - currentpos.x); int directiony = sgn(targetpos.y - currentpos.y); currentpos.x += deltapos * directionx * deltatime * animationspeed; // 相距小于 5 视为在同一位置 if (fabs(currentpos.x - targetpos.x) < 5) { currentpos.x = targetpos.x; } currentpos.y += deltapos * directiony * deltatime * animationspeed; if (fabs(currentpos.y - targetpos.y) getwidth(), img->getheight(), black); } // 把方块从当前位置移动到目标位置,移动后改变状态 void moveto(const vector2 &pos, image *newimg, state state = exist){ targetpos = pos; targetstate = state; this->newimg = newimg; } state getstate(){ return currentstate; }};int map[4][4]; // 4 * 4 地图block *blockmap[4][4]; // 方块索引int score; // 得分int maxscore; // 最高得分int currentmaxblock; // 当前最大方块int maxblock; // 历史最大方块int gameloop; // 游戏循环float keytime = 0; // 按键间隔std::map image; // 存储所有数字图像bool gameover = false; // 游戏是否结束float overtime; // 游戏结束后不会立刻退出循环,而是等待 0.5s 更新动画// 判断是否有可移动的方式,有返回 1 ,没有返回 0// 检测思路:如果碰到为 0 的格子,或者两个相邻的格子数字相等,则返回 1int judge(){ // 横向检测 for (int i = 0; i < 4; i++) { for (int j = 0; j < 3; j++) { if (map[i][j] == 0 || map[i][j] == map[i][j + 1] || map[i][j + 1] == 0) return 1; } } // 纵向检测 for (int i = 0; i < 4; i++) { for (int j = 0; j < 3; j++) { if (map[j][i] == 0 || map[j][i] == map[j + 1][i] || map[j + 1][i] == 0) return 1; } } return 0;}// 上移void up(){ int moveflag = 0; // 记录是否进行过移动 int mergeflag = 0; // 记录是否合并过 for (int i = 0; i < 4; i++) { for (int j = 0; j < 3; j++) { int k, z; // 找到一个不为 0 的方块向上移动,并判断是否可以和下边的方块合并 for (k = j; k < 4; k++) if (map[k][i] != 0) break; // 寻找右边不为 0 的方块 for (z = k + 1; z < 4; z++) if (map[z][i] != 0) break; // 当前行有非 0 方块 if (k < 4) { if (z moveto({ 25.0f + 100 * i,225.0f + 100 * j }, &image[map[j][i]]); blockmap[z][i]->moveto({ 25.0f + 100 * i,225.0f + 100 * (j + 1) }, &image[map[z][i]], destory); // 更新分数 score += map[j][i]; if (score > maxscore) maxscore = score; // 更新方块 if (value > currentmaxblock) currentmaxblock = value; if (currentmaxblock > maxblock) maxblock = currentmaxblock; mergeflag = 1; } else { // 不可以合并 int value = map[k][i]; map[k][i] = 0; map[j][i] = value; // 判断是否可以移动 if (k != j) { moveflag = 1; // 开启动画 block *temp = blockmap[k][i]; blockmap[k][i] = null; blockmap[j][i] = temp; blockmap[j][i]->moveto({ 25.0f + 100 * i,225.0f + 100 * j }, &image[map[j][i]]); } } } else // 判断下一行 { break; } } } // 如果发生了移动或合并,随机生成一个 2 或 4 if (moveflag || mergeflag) { int index; // 随机位置的索引 // 直到随机到一个空位置退出循环 do { index = rand() % 4; } while (map[3][index] != 0); // 80% 生成 2 , 20% 生成 4 int num = rand() % 10; if (num < 8) { map[3][index] = 2; blockmap[3][index] = new block({ 25.0f + 100 * index, 225.0f + 100 * 3 }, &image[2]); } else { map[3][index] = 4; blockmap[3][index] = new block({ 25.0f + 100 * index, 225.0f + 100 * 3 }, &image[4]); } }}// 下移void down(){ int moveflag = 0; // 记录是否进行过移动 int mergeflag = 0; // 记录是否合并过 for (int i = 0; i 0; j--) { int k, z; // 找到一个不为 0 的方块向下移动,并判断是否可以和上边的方块合并 for (k = j; k >= 0; k--) if (map[k][i] != 0) break; // 寻找右边不为 0 的方块 for (z = k - 1; z >= 0; z--) if (map[z][i] != 0) break; // 当前行有非 0 方块 if (k >= 0) { if (z >= 0 && map[k][i] == map[z][i]) { // 可以合并 int value = map[k][i] + map[z][i]; map[k][i] = map[z][i] = 0; map[j][i] = value; // 开启动画 block *temp = blockmap[k][i]; blockmap[k][i] = null; blockmap[j][i] = temp; blockmap[j][i]->moveto({ 25.0f + 100 * i,225.0f + 100 * j }, &image[map[j][i]]); blockmap[z][i]->moveto({ 25.0f + 100 * i,225.0f + 100 * (j - 1) }, &image[map[z][i]], destory); // 更新分数 score += map[j][i]; if (score > maxscore) maxscore = score; // 更新方块 if (value > currentmaxblock) currentmaxblock = value; if (currentmaxblock > maxblock) maxblock = currentmaxblock; mergeflag = 1; } else { // 不可以合并 int value = map[k][i]; map[k][i] = 0; map[j][i] = value; // 判断是否可以移动 if (k != j) { moveflag = 1; // 开启动画 block *temp = blockmap[k][i]; blockmap[k][i] = null; blockmap[j][i] = temp; blockmap[j][i]->moveto({ 25.0f + 100 * i,225.0f + 100 * j }, &image[map[j][i]]); } } } else // 判断下一行 { break; } } } // 如果发生了移动或合并,随机生成一个 2 或 4 if (moveflag || mergeflag) { int index; // 随机位置的索引 // 直到随机到一个为 0 的位置退出循环 do { index = rand() % 4; } while (map[0][index] != 0); // 80% 生成 2 , 20% 生成 4 int num = rand() % 10; if (num < 8) { map[0][index] = 2; blockmap[0][index] = new block({ 25.0f + 100 * index,225.0f + 100 * 0 }, &image[2]); } else { map[0][index] = 4; blockmap[0][index] = new block({ 25.0f + 100 * index,225.0f + 100 * 0 }, &image[4]); } }}// 左移void left(){ int moveflag = 0; // 记录是否进行过移动 int mergeflag = 0; // 记录是否合并过 for (int i = 0; i < 4; i++) { for (int j = 0; j < 3; j++) { int k, z; // 找到一个不为 0 的方块向左移动,并判断是否可以和右边的方块合并 for (k = j; k < 4; k++) if (map[i][k] != 0) break; // 寻找右边不为 0 的方块 for (z = k + 1; z < 4; z++) if (map[i][z] != 0) break; // 当前行有非 0 方块 if (k < 4) { if (z moveto({ 25.0f + 100 * j,225.0f + 100 * i }, &image[value]); blockmap[i][z]->moveto({ 25.0f + 100 * (j + 1),225.0f + 100 * i }, &image[map[z][i]], destory); // 更新分数 score += map[i][j]; if (score > maxscore) maxscore = score; // 更新方块 if (value > currentmaxblock) currentmaxblock = value; if (currentmaxblock > maxblock) maxblock = currentmaxblock; mergeflag = 1; } else { // 不可以合并 int value = map[i][k]; map[i][k] = 0; map[i][j] = value; // 判断是否可以移动 if (k != j) { moveflag = 1; // 开启动画 block *temp = blockmap[i][k]; blockmap[i][k] = null; blockmap[i][j] = temp; blockmap[i][j]->moveto({ 25.0f + 100 * j,225.0f + 100 * i }, &image[value]); } } } else // 判断下一行 { break; } } } // 如果发生了移动或合并,随机生成一个 2 或 4 if (moveflag || mergeflag) { int index; // 随机位置的索引 // 直到随机到一个为 0 的位置退出循环 do { index = rand() % 4; } while (map[index][3] != 0); // 80% 生成 2 , 20% 生成 4 int num = rand() % 10; if (num < 8) { map[index][3] = 2; blockmap[index][3] = new block({ 25.0f + 100 * 3,225.0f + 100 * index }, &image[2]); } else { map[index][3] = 4; blockmap[index][3] = new block({ 25.0f + 100 * 3,225.0f + 100 * index }, &image[4]); } }}// 右移void right(){ int moveflag = 0; // 记录是否进行过移动 int mergeflag = 0; // 记录是否合并过 for (int i = 0; i 0; j--) { int k, z; // 找到一个不为 0 的方块向右移动,并判断是否可以和左边的方块合并 for (k = j; k >= 0; k--) if (map[i][k] != 0) break; // 寻找右边不为 0 的方块 for (z = k - 1; z >= 0; z--) if (map[i][z] != 0) break; // 当前行有非 0 方块 if (k >= 0) { if (z >= 0 && map[i][k] == map[i][z]) { // 可以合并 int value = map[i][k] + map[i][z]; map[i][k] = map[i][z] = 0; map[i][j] = value; // 开启动画 block *temp = blockmap[i][k]; blockmap[i][k] = null; blockmap[i][j] = temp; blockmap[i][j]->moveto({ 25.0f + 100 * j,225.0f + 100 * i }, &image[value]); blockmap[i][z]->moveto({ 25.0f + 100 * (j - 1),225.0f + 100 * i }, &image[map[z][i]], destory); // 更新分数 score += map[i][j]; if (score > maxscore) maxscore = score; // 更新方块 if (value > currentmaxblock) currentmaxblock = value; if (currentmaxblock > maxblock) maxblock = currentmaxblock; mergeflag = 1; } else { // 不可以合并 int value = map[i][k]; map[i][k] = 0; map[i][j] = value; // 判断是否可以移动 if (k != j) { moveflag = 1; // 开启动画 block *temp = blockmap[i][k]; blockmap[i][k] = null; blockmap[i][j] = temp; blockmap[i][j]->moveto({ 25.0f + 100 * j,225.0f + 100 * i }, &image[value]); } } } else // 判断下一行 { break; } } } // 如果发生了移动或合并,随机生成一个 2 或 4 if (moveflag || mergeflag) { int index; // 随机位置的索引 do { index = rand() % 4; } while (map[index][0] != 0); // 80% 生成 2 , 20% 生成 4 int num = rand() % 10; if (num < 8) { map[index][0] = 2; blockmap[index][0] = new block({ 25.0f + 100 * 0,225.0f + 100 * index }, &image[2]); } else { map[index][0] = 4; blockmap[index][0] = new block({ 25.0f + 100 * 0,225.0f + 100 * index }, &image[4]); } }}void update(float deltatime){ // 更新方块 for (int i = 0; i < 4; i++) { for (int j = 0; j update(deltatime); if (blockmap[i][j]->getstate() == destory) { delete blockmap[i][j]; blockmap[i][j] = null; } } } } if (gameover) { overtime -= deltatime; if (overtime <= 0) gameloop = 0; } keytime += deltatime; // 0.2s 可以按键一次 if (keytime < 0.2f || gameover) return; if ((getasynckeystate(vk_up) & 0x8000) || (getasynckeystate('w') & 0x8000)) // 上 { up(); if (!judge()) { gameover = true; } keytime = 0; } else if ((getasynckeystate(vk_down) & 0x8000) || (getasynckeystate('s') & 0x8000)) // 下 { down(); if (!judge()) { gameover = true; } keytime = 0; } else if ((getasynckeystate(vk_left) & 0x8000) || (getasynckeystate('a') & 0x8000)) // 左 { left(); if (!judge()) { gameover = true; } keytime = 0; } else if ((getasynckeystate(vk_right) & 0x8000) || (getasynckeystate('d') & 0x8000)) // 右 { right(); if (!judge()) { gameover = true; } keytime = 0; }}// 设置文字样式和颜色void settext(int height, int weight, uint color){ settextstyle(height, 0, _t(arial), 0, 0, weight, false, false, false, ansi_charset, out_default_precis, clip_default_precis, antialiased_quality, default_pitch); settextcolor(color);}// 在指定矩形区域内居中输出字符串void printtext(lpctstr s, int left, int top, int right, int width){ rect r = { left, top, right, width }; drawtext(s, &r, dt_center | dt_vcenter | dt_singleline);}// 绘制界面void draw(){ // 历史最大方块 transparentblt(getimagehdc(null), 12, 30, 90, 90, getimagehdc(&image[maxblock]), 0, 0, 90, 90, 0x9eaebb); setfillcolor(0x9eaebb); // 绘制当前分数 solidroundrect(112, 30, 264, 119, 10, 10); settext(28, 800, 0xdbe6ee); printtext(_t(score), 112, 40, 264, 69); std::wstringstream ss; ss << score; settext(44, 800, white); printtext(ss.str().c_str(), 112, 70, 264, 114); ss.str(_t()); // 绘制最高分数 solidroundrect(275, 30, 427, 119, 10, 10); settext(28, 800, 0xdbe6ee); printtext(_t(best), 275, 40, 427, 69); ss << maxscore; settext(44, 800, white); printtext(ss.str().c_str(), 275, 70, 427, 114); ss.str(_t()); // 绘制提示信息 settextcolor(black); ss << join the numbers and get to the << currentmaxblock * 2 << tile!; settext(24, 800, 0x707b83); printtext(ss.str().c_str(), 0, 120, 439, 211); // 绘制方块底板 solidroundrect(12, 212, 427, 627, 10, 10); // 绘制方块 for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { putimage(25 + 100 * j, 225 + 100 * i, &image[0]); } } for (int i = 0; i < 4; i++) { for (int j = 0; j draw(); } }}// 初始化游戏void init(){ srand((unsigned int)time(null)); // 初始化随机数种子 memset(map, 0, 4 * 4 * sizeof(int)); // 把地图初始化为 0 memset(blockmap, 0, 4 * 4 * sizeof(block*)); score = 0; gameloop = 1; gameover = false; overtime = 0.5f; currentmaxblock = 2; map[0][0] = 2; map[0][1] = 2; blockmap[0][0] = new block({ 25,225 }, &image[2]); blockmap[0][1] = new block({ 125,225 }, &image[2]); setbkcolor(white); setbkmode(transparent);}// 游戏结束界面 返回 1 表示继续游戏 返回 0 表示结束游戏int overinterface(){ // 保存最高纪录 std::wstringstream ss; ss << maxscore; writeprivateprofilestring(_t(2048), _t(maxscore), ss.str().c_str(), _t(.\data.ini)); ss.str(_t()); ss <= 120 && x = 310 && y = 120 && x = 460 && y <= 539) return 0; } } sleep(100); } return 1;}// 释放内存void freemem(){ for (int i = 0; i < 4; i++) for (int j = 0; j getwidth() - 1, img->getheight() - 1, 10, 10); rect r = { 0,0,img->getwidth() - 1,img->getheight() - 1 }; drawtext(num, &r, dt_center | dt_vcenter | dt_singleline);}// 绘制图片缓存void load(){ image temp(90, 90); createimage(&temp, _t(), 0xb5becc, 72, white); image[0] = temp; createimage(&temp, _t(2), 0xdbe6ee, 72, 0x707b83); image[2] = temp; createimage(&temp, _t(4), 0xc7e1ed, 72, 0x707b83); image[4] = temp; createimage(&temp, _t(8), 0x78b2f4, 72, white); image[8] = temp; createimage(&temp, _t(16), 0x538ded, 72, white); image[16] = temp; createimage(&temp, _t(32), 0x607df6, 72, white); image[32] = temp; createimage(&temp, _t(64), 0x3958e9, 72, white); image[64] = temp; createimage(&temp, _t(128), 0x6bd9f5, 56, white); image[128] = temp; createimage(&temp, _t(256), 0x4bd0f2, 56, white); image[256] = temp; createimage(&temp, _t(512), 0x2ac0e4, 56, white); image[512] = temp; createimage(&temp, _t(1024), 0x13b8e3, 40, white); image[1024] = temp; createimage(&temp, _t(2048), 0x00c5eb, 40, white); image[2048] = temp; createimage(&temp, _t(4096), 0x3958e9, 40, white); image[4096] = temp; createimage(&temp, _t(8192), 0x3958e9, 40, white); image[8192] = temp; setworkingimage(null);}// 主函数int main(){ float deltatime = 0; // 每帧耗时 initgraph(440, 650); load(); beginbatchdraw(); maxscore = 0; // 读取最高分 maxscore = getprivateprofileint(_t(2048), _t(maxscore), 0, _t(.\data.ini)); // 读取最大方块 maxblock = getprivateprofileint(_t(2048), _t(maxblock), 2, _t(.\data.ini)); while (1) { init(); while (gameloop) { clock_t start = clock(); cleardevice(); update(deltatime); draw(); flushbatchdraw(); sleep(1); clock_t end = clock(); deltatime = (end - start) / 1000.0f; } freemem(); if (overinterface() == 0) break; flushmousemsgbuffer(); } closegraph();}
瞬态抑制二极管的分类_瞬态抑制二极管的选用技巧
三菱MRJ飞机已取得了型号合格证并将在美国进行试飞
小米6、小米5X区别对比评测:配置必须是小米6,颜值不一定是小米6
I2C总线数字式温湿度传感器SHT11及其在单片机系统的应用
加工条件不明确,选刀难免有偏差
用C语言完美实现2048数字方块游戏
C++写壳详细教程(下)
数码摄像头对焦方式/范围
盘点那些正在飞速发展的智能家居产品
是德科技的光纤接口如何应对5G测试
AKM终于恢复供货,音频DAC下一波换新指日可待?
小牛电动上市李一男身影难寻 尽管身价或超20亿
小米5C错过双11将在双12亮相,采用自主松果八核CPU支持NFC
软开关型脉冲MIG焊接电源系统原理设计
行业大咖如何看待智能车生态?
光纤熔接机的保养与清洁技巧
工信部截至7月底已核发5G设备进网批文7张进网标志37万个
小米手表Color全新体验,或将代替智能手环
对基于SoC系统设计的探查
前“锁”未有---信驰达智能门锁创新解决方案