概述
目录
创建地图文件
游戏运行部分
所用头文件,结构体,函数
显示各菜单
自定义地图
游戏主体
所用到的图片和二进制文件编辑
写在前面:粗略学习了一下c++的文件操作就在这里实践了一下,在写这个的时候确实也发现了不少问题当然最后也都发现改正了。写完这个小项目后,对Easyx有了更进一步的了解,每次学一点点,慢慢积累总会有熟悉的一天。
这个游戏我写了两部分,一个是创建地图文件的程序,因为莫名的原因导致循环不能顺利的进行,于是我只能一遍一遍的运行程序同时更改代码中的地图序号来写入地图信息。另一部分就是游戏的主体,目前支持读取14关(因为界面只画了14关),自定义一关。一共15关,游戏地图都是从网上找的某个推箱子游戏的地图。由于没有边界检查所以自定义地图的时候会麻烦一点,有时还会出现地图边界不显示的问题。
由于初学,还有很多地方写的很乱,欢迎指正( •̀ ω •́ )✧
创建地图文件
#include <graphics.h>
#include <conio.h>
#include <fstream>
#include <iostream>
using namespace std;
struct object {
int x, y;
int flag; //记录方块种类,0空,1人,2箱子,4边界, 8目标区域
object(int a = 0, int b = 0, int c = 0) :x(a), y(b), flag(c) {}
};
int mapNum, mapWidth, mapHeight;
int blockSize = 30;
object** block = nullptr;
IMAGE blank,wall, box, chara, target;
void LOADIMAGE() { //载入图片
loadimage(&blank, _T("blank.png"), blockSize, blockSize, true);
loadimage(&wall, _T("wall.png"), blockSize, blockSize, true);
loadimage(&box, _T("box.png"), blockSize, blockSize, true);
loadimage(&chara, _T("chara.png"), blockSize, blockSize, true);
loadimage(&target, _T("target.png"), blockSize, blockSize, true);
}
void deleteBlock() {
if (block == nullptr) return;
for (int i = 0; i < mapWidth; i++) if (block[i] != nullptr) delete[] block[i];
if (block != nullptr) delete[] block;
}
void buildBlock() {
deleteBlock();
block = new object * [mapWidth]; //创建新地图 索引第一项 x
for (int i = 0; i < mapWidth; i++) block[i] = new object[mapHeight]; //创建地图索引第二项 y
//修改坐标
for (int i = 0; i < mapWidth; i++) {
for (int j = 0; j < mapHeight; j++) {
block[i][j].x = i * blockSize;
block[i][j].y = j * blockSize;
}
}
}
int main() {
fstream outfile(_T("map.dat"),
ios_base::out|ios_base::app|ios_base::binary);
LOADIMAGE();
/*if (!outfile.is_open()) {
cout << "can't open file" << endl;
}*/
//for (int i = 1; i <= 14; i++) { //创建14个地图
mapNum = 14;
cout << "输入地图最大宽度(格):";
cin >> mapWidth;
cout << "输入地图最大高度(格):";
cin >> mapHeight;
buildBlock();
//Sleep(3000);
initgraph(mapWidth * blockSize, mapHeight * blockSize);
setbkcolor(WHITE);
cleardevice();
ExMessage msg;
int tempflag = 0;
while (1) {
flushmessage();
getmessage(&msg, EM_MOUSE);
if (msg.lbutton) { //鼠标左键安置方块
int blockX = msg.x / blockSize, blockY = msg.y / blockSize;
block[blockX][blockY].flag = tempflag;
switch (block[blockX][blockY].flag) {
case 0:putimage(blockX * blockSize, blockY * blockSize, &blank); break;
case 1:putimage(blockX * blockSize, blockY * blockSize, &chara); break;
case 2:putimage(blockX * blockSize, blockY * blockSize, &box); break;
case 4:putimage(blockX * blockSize, blockY * blockSize, &wall); break;
case 8:putimage(blockX * blockSize, blockY * blockSize, &target); break;
}
}
else if (msg.rbutton) { //鼠标右键换材质
if (tempflag > 0 && tempflag <= 8) tempflag <<= 1;
else if (tempflag > 8) tempflag = 0;
else tempflag += 1;
}
else if (msg.mbutton) { //中键完成创建地图
break;
}
else continue;
}
outfile.write((char*)&mapNum, sizeof mapNum); //地图序号
outfile.write((char*)&mapWidth, sizeof mapWidth); //将地图大小,方块输入到地图文件中
outfile.write((char*)&mapHeight, sizeof mapHeight);
for (int i = 0; i < mapWidth; i++) {
for (int j = 0; j < mapHeight; j++) {
outfile.write((char*)&block[i][j], sizeof object);
}
}
closegraph();
cout << "Done!" << endl;
//}
return 0;
}
游戏运行部分
所用头文件,结构体,函数
headfile.h
#pragma once
#include <graphics.h>
#include <conio.h>
#include <iostream>
#include <time.h>
#include <fstream>
struct object {
int x, y;
int flag; //记录方块种类,0空,1人,2箱子,4边界, 8目标区域
object(int a = 0, int b = 0, int c = 0) :x(a), y(b), flag(c) {}
};
void LOADIMAGE();
void deleteBlock();
void readMap(int);
void showAll(object**&);
void moveAll();
void changeTarget();
bool judgeWin();
void START(int);
void buildBlock();
int CheckMap();
void BUILDMAP();
void ShowMainMenu(); //进入主菜单
void ShowSubMenu(); //展示关卡
void ShowDesignMenu(); //确定对自定义关卡的操作
void OutDesignText();
void OutSubText();
void OutText();
显示各菜单
Menu.cpp
#include "headfile.h"
extern int mapNum;
object submenu[4][4]; //flag == mapnum
void OutText() {
outtextxy(150 + 10, 120 + 5, _T("选择关卡"));
outtextxy(150 + 10, 220 + 5, _T("退出游戏"));
}
void OutSubText() {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (submenu[i][j].flag < 15) {
TCHAR s[10];
_stprintf_s(s, _T("关卡%d"), submenu[i][j].flag); // 高版本 VC 推荐使用 _stprintf_s 函数
outtextxy(submenu[i][j].x + 10, submenu[i][j].y + 5, s);
}
else if (submenu[i][j].flag == 15) outtextxy(submenu[i][j].x + 10, submenu[i][j].y + 5, _T("自定义"));
else outtextxy(submenu[i][j].x + 10, submenu[i][j].y + 5, _T("返回"));
}
}
}
void OutDesignText()
{
outtextxy(150 + 10, 120 + 5, _T("进入自定义关卡"));
outtextxy(150 + 10, 220 + 5, _T("重新定义关卡"));
}
void ShowMainMenu() //进入主菜单
{
ExMessage m;
initgraph(400, 400);
setbkcolor(BLACK);
cleardevice();
settextcolor(WHITE);
OutText();
while (1) {
getmessage(&m, EM_MOUSE); //接收鼠标消息
if (m.x >= 150 && m.x <= 250 && m.y >= 120 && m.y <= 150) { //选择关卡进入子菜单
if (m.message == WM_LBUTTONDOWN) {
ShowSubMenu();
closegraph();
break;
}
}
else if (m.x >= 150 && m.x <= 250 && m.y >= 220 && m.y <= 250) { //退出
if (m.message == WM_LBUTTONDOWN) exit(0);
}
}
closegraph();
}
void ShowSubMenu() //展示关卡
{
initgraph(400, 400);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
submenu[i][j].x = i * 100;
submenu[i][j].y = j * 100;
submenu[i][j].flag = i * 4 + j + 1;
}
}
cleardevice();
settextcolor(WHITE);
OutSubText();
ExMessage msg;
while (1) {
getmessage(&msg, EM_MOUSE);
if (msg.lbutton) {
mapNum = submenu[msg.x / 100][msg.y / 100].flag;
break;
}
}
closegraph();
}
void ShowDesignMenu() //确定对自定义关卡的操作
{
initgraph(400, 400);
OutDesignText();
ExMessage m;
while (1) {
getmessage(&m, EM_MOUSE); //接收鼠标消息
if (m.lbutton) {
if (m.x >= 150 && m.x <= 250 && m.y >= 120 && m.y <= 150) {
START(0);
break;
}
else if (m.x >= 150 && m.x <= 250 && m.y >= 220 && m.y <= 250) {
BUILDMAP();
break;
}
}
}
closegraph();
}
自定义地图
MyDesignMap.cpp
#include "headfile.h"
using namespace std;
extern IMAGE wall, box, chara, target, blank;
int d_mapWidth, d_mapHeight; //地图的大小,在读取文件时相应改变
extern int blockSize; //每个方块的大小
extern int targetnum; //记录目标位置的数量,以便动态的存储所有目标位置
object** d_block; //存储地图的二维数组
int chara_num = 0, box_num = 0, target_num = 0;//用于检查地图是否正确
void buildBlock() {
deleteBlock();
d_block = new object * [d_mapWidth]; //创建新地图 索引第一项 x
for (int i = 0; i < d_mapWidth; i++) d_block[i] = new object[d_mapHeight]; //创建地图索引第二项 y
//修改坐标
for (int i = 0; i < d_mapWidth; i++) {
for (int j = 0; j < d_mapHeight; j++) {
d_block[i][j].x = i * blockSize;
d_block[i][j].y = j * blockSize;
}
}
//设置最外围边界
for (int i = 0; i < d_mapWidth; i++) d_block[i][0].flag = d_block[i][d_mapHeight - 1].flag = 4;
for (int i = 0; i < d_mapHeight; i++) d_block[0][i].flag = d_block[d_mapWidth - 1][i].flag = 4;
}
int CheckMap() {
//核对数量
if (chara_num != 1) //人物数量不唯一
{
outtextxy(0, 0, _T("人物数量不唯一,请重新设置地图"));
Sleep(1000);
return false;
}
if (box_num < target_num) //箱子少于目标位置
{
outtextxy(0, 0, _T("箱子数量少于目标数量,请重新设置地图"));
Sleep(1000);
return false;
}
for (int i = 0; i < d_mapWidth; i++) {
if (d_block[i][0].flag != 4 || d_block[i][d_mapHeight - 1].flag != 4) {
outtextxy(0, 0, _T("边界被更改,请将边界恢复为默认"));
Sleep(1000);
return false;
}
}
for (int i = 0; i < d_mapHeight; i++) {
if (d_block[0][i].flag != 4 || d_block[d_mapWidth - 1][i].flag != 4) {
outtextxy(0, 0, _T("边界被更改,请将边界恢复为默认"));
Sleep(1000);
return false;
}
}
return true;
}
void BUILDMAP() { //创建自定义地图,写入文件
cleardevice();
int mapnum = 0;
fstream outfile(_T("selfdesign.dat"),
ios_base::out | ios_base::binary);
//TODO=输入宽,高
wchar_t tempwidth[10], tempheight[10];
InputBox(tempwidth, 10, L"请输入宽度");
d_mapWidth = _wtoi(tempwidth) + 2;
InputBox(tempheight, 10, L"请输入高度");
d_mapHeight = _wtoi(tempheight) + 2;
closegraph();
buildBlock();
initgraph(d_mapWidth * blockSize, d_mapHeight * blockSize);
setbkcolor(BLACK);
cleardevice();
LOADIMAGE();
showAll(d_block);
ExMessage msg;
int tempflag = 0;
while (1) {
flushmessage();
getmessage(&msg, EM_MOUSE);
if (msg.lbutton) { //鼠标左键安置方块
int blockX = msg.x / blockSize, blockY = msg.y / blockSize;
switch (d_block[blockX][blockY].flag) {
case 1:chara_num--; break;
case 2:box_num--; break;
case 8:target_num--; break;
}
d_block[blockX][blockY].flag = tempflag;
switch (d_block[blockX][blockY].flag) {
case 0:putimage(blockX * blockSize, blockY * blockSize, &blank); break;
case 1:putimage(blockX * blockSize, blockY * blockSize, &chara); chara_num++; break;
case 2:putimage(blockX * blockSize, blockY * blockSize, &box); box_num++; break;
case 4:putimage(blockX * blockSize, blockY * blockSize, &wall); break;
case 8:putimage(blockX * blockSize, blockY * blockSize, &target); target_num++; break;
}
}
else if (msg.rbutton) { //鼠标右键换材质
if (tempflag > 0 && tempflag <= 8) tempflag <<= 1;
else if (tempflag > 8) tempflag = 0;
else tempflag += 1;
}
else if (msg.mbutton) { //中键完成创建地图
if (CheckMap()) break;
else {
cleardevice();
showAll(d_block);
}
}
else continue;
}
outfile.write((char*)&mapnum, sizeof mapnum);
outfile.write((char*)&d_mapWidth, sizeof d_mapWidth); //将地图大小,方块输入到地图文件中
outfile.write((char*)&d_mapHeight, sizeof d_mapHeight);
for (int i = 0; i < d_mapWidth; i++) {
for (int j = 0; j < d_mapHeight; j++) {
outfile.write((char*)&d_block[i][j], sizeof object);
}
}
outfile.close();
outtextxy(0, 0, _T("Done!"));
Sleep(500);
closegraph();
initgraph(400, 400);
}
游戏主体
framework.cpp
#include "headfile.h"
using namespace std;
IMAGE wall, box, chara, target, blank;
int mapNum, mapWidth = 20, mapHeight = 11; //地图的大小,在读取文件时相应改变
int blockSize = 30; //每个方块的大小
int back_flag = 0; //在游戏中异步退出,返回上一界面
int targetnum = 0; //记录目标位置的数量
object** block = nullptr; //存储地图的二维数组
object* my_chara = nullptr; //记录人物位置的指针
object* my_target[50]; //记录所有目标位置的指针,每次循环遍历一遍判断是否过关
void LOADIMAGE() { //载入图片
loadimage(&wall, _T("wall.png"), blockSize, blockSize, true);
loadimage(&box, _T("box.png"), blockSize, blockSize, true);
loadimage(&chara, _T("chara.png"), blockSize, blockSize, true);
loadimage(&target, _T("target.png"), blockSize, blockSize, true);
loadimage(&blank, _T("blank.png"), blockSize, blockSize, true);
}
void deleteBlock() {
if (block == nullptr) return;
for (int i = 0; i < mapWidth; i++) if (block[i] != nullptr) delete[] block[i];
if (block != nullptr) delete[] block;
}
//void deleteTarget() {
// if (my_target != nullptr) delete[] my_target;
//}
void readMap(int mapnum) { //读入地图 并找目标区域,人物位置
deleteBlock();
fstream inFile;
if (mapnum == 0) inFile.open("selfdesign.dat", ios_base::in | ios_base::binary);
else inFile.open("map.dat", ios_base::in | ios_base::binary);
int tempnum;
while (1) { //定位目标地图在文件中的位置
inFile.read((char*)&tempnum, sizeof tempnum); //读入地图序号
inFile.read((char*)&mapWidth, sizeof mapWidth); //读入地图宽
inFile.read((char*)&mapHeight, sizeof mapHeight); //读入地图高
if (tempnum == mapnum) break;
else {
inFile.seekg(mapWidth * mapHeight * (sizeof object), ios_base::cur);
if (inFile.eof()) {
exit(1);
}
}
}
block = new object * [mapWidth]; //创建新地图 索引第一项 x
for (int i = 0; i < mapWidth; i++) block[i] = new object[mapHeight]; //创建地图索引第二项 y
for (int i = 0; i < mapWidth; i++) {
for (int j = 0; j < mapHeight; j++) {
inFile.read(reinterpret_cast<char*>(&block[i][j]), sizeof object);
}
}
inFile.close();
//寻找chara的位置
targetnum = 0;
for (int i = 0; i < mapWidth; i++) {
for (int j = 0; j < mapHeight; j++) {
if (block[i][j].flag == 1) my_chara = &block[i][j];
if (block[i][j].flag == 8) {
my_target[targetnum++] = &block[i][j];
}
}
}
}
void showAll(object**& block) {
for (int i = 0; i < mapWidth; i++) {
for (int j = 0; j < mapHeight; j++) {
if (block[i][j].flag == 1) putimage(block[i][j].x, block[i][j].y, &chara);
else if (block[i][j].flag == 2) putimage(block[i][j].x, block[i][j].y, &box);
else if (block[i][j].flag == 4) putimage(block[i][j].x, block[i][j].y, &wall);
else if (block[i][j].flag == 8) putimage(block[i][j].x, block[i][j].y, &target);
}
}
}
void moveAll() { //接收同步输入,移动人物箱子 ,改变坐标
char msg; //记录移动
msg = _getch();
if (msg == 72) {
if (block[my_chara->x / blockSize][my_chara->y / blockSize - 1].flag != 4 && block[my_chara->x / blockSize][my_chara->y / blockSize - 1].flag != 2) {
block[my_chara->x / blockSize][my_chara->y / blockSize - 1].flag = 1;
block[my_chara->x / blockSize][my_chara->y / blockSize].flag = 0;
my_chara = &block[my_chara->x / blockSize][my_chara->y / blockSize - 1];
}
else if (block[my_chara->x / blockSize][my_chara->y / blockSize - 1].flag == 2 && (block[my_chara->x / blockSize][my_chara->y / blockSize - 2].flag == 0 || block[my_chara->x / blockSize][my_chara->y / blockSize - 2].flag == 8)) {
block[my_chara->x / blockSize][my_chara->y / blockSize - 2].flag = 2;
block[my_chara->x / blockSize][my_chara->y / blockSize - 1].flag = 1;
block[my_chara->x / blockSize][my_chara->y / blockSize].flag = 0;
my_chara = &block[my_chara->x / blockSize][my_chara->y / blockSize - 1];
}
}
else if (msg == 75) {
if (block[my_chara->x / blockSize - 1][my_chara->y / blockSize].flag != 4 && block[my_chara->x / blockSize - 1][my_chara->y / blockSize].flag != 2) {
block[my_chara->x / blockSize - 1][my_chara->y / blockSize].flag = 1;
block[my_chara->x / blockSize][my_chara->y / blockSize].flag = 0;
my_chara = &block[my_chara->x / blockSize - 1][my_chara->y / blockSize];
}
else if (block[my_chara->x / blockSize - 1][my_chara->y / blockSize].flag == 2 && (block[my_chara->x / blockSize - 2][my_chara->y / blockSize].flag == 0 || block[my_chara->x / blockSize - 2][my_chara->y / blockSize].flag == 8)) {
block[my_chara->x / blockSize - 2][my_chara->y / blockSize].flag = 2;
block[my_chara->x / blockSize - 1][my_chara->y / blockSize].flag = 1;
block[my_chara->x / blockSize][my_chara->y / blockSize].flag = 0;
my_chara = &block[my_chara->x / blockSize - 1][my_chara->y / blockSize];
}
}
else if (msg == 80) {
if (block[my_chara->x / blockSize][my_chara->y / blockSize + 1].flag != 4 && block[my_chara->x / blockSize][my_chara->y / blockSize + 1].flag != 2) {
block[my_chara->x / blockSize][my_chara->y / blockSize + 1].flag = 1;
block[my_chara->x / blockSize][my_chara->y / blockSize].flag = 0;
my_chara = &block[my_chara->x / blockSize][my_chara->y / blockSize + 1];
}
else if (block[my_chara->x / blockSize][my_chara->y / blockSize + 1].flag == 2 && (block[my_chara->x / blockSize][my_chara->y / blockSize + 2].flag == 0 || block[my_chara->x / blockSize][my_chara->y / blockSize + 2].flag == 8)) {
block[my_chara->x / blockSize][my_chara->y / blockSize + 2].flag = 2;
block[my_chara->x / blockSize][my_chara->y / blockSize + 1].flag = 1;
block[my_chara->x / blockSize][my_chara->y / blockSize].flag = 0;
my_chara = &block[my_chara->x / blockSize][my_chara->y / blockSize + 1];
}
}
else if (msg == 77) {
if (block[my_chara->x / blockSize + 1][my_chara->y / blockSize].flag != 4 && block[my_chara->x / blockSize + 1][my_chara->y / blockSize].flag != 2) {
block[my_chara->x / blockSize + 1][my_chara->y / blockSize].flag = 1;
block[my_chara->x / blockSize][my_chara->y / blockSize].flag = 0;
my_chara = &block[my_chara->x / blockSize + 1][my_chara->y / blockSize];
}
else if (block[my_chara->x / blockSize + 1][my_chara->y / blockSize].flag == 2 && (block[my_chara->x / blockSize + 2][my_chara->y / blockSize].flag == 0 || block[my_chara->x / blockSize + 2][my_chara->y / blockSize].flag == 8)) {
block[my_chara->x / blockSize + 2][my_chara->y / blockSize].flag = 2;
block[my_chara->x / blockSize + 1][my_chara->y / blockSize].flag = 1;
block[my_chara->x / blockSize][my_chara->y / blockSize].flag = 0;
my_chara = &block[my_chara->x / blockSize + 1][my_chara->y / blockSize];
}
}
else if (msg == 27) back_flag = 1;
}
void changeTarget() {
for (int i = 0; i < targetnum; i++) {
if (my_target[i]->flag != 1 && my_target[i]->flag != 2) my_target[i]->flag = 8;
}
}
bool judgeWin() {
for (int i = 0; i < targetnum; i++) {
if (my_target[i]->flag != 2) return false;
}
return true;
}
void START(int mapnum) {
readMap(mapnum);
initgraph(mapWidth * blockSize, mapHeight * blockSize);
setbkcolor(BLACK);
cleardevice();
showAll(block);
while (1) {
moveAll();
if (back_flag == 1) {
back_flag = 0;
break;
}
cleardevice();
changeTarget();
showAll(block);
if (judgeWin()) {
outtextxy(0, 0, _T("完美通关!"));
Sleep(1000);
closegraph();
return;
}
}
}
int main() {
LOADIMAGE();
while (1) {
ShowMainMenu();
while (1) {
if (mapNum <= 14) START(mapNum);
else if (mapNum == 15) ShowDesignMenu();
else break;
ShowSubMenu();
}
}
deleteBlock();
//deleteTarget();
return 0;
}
所用到的图片和二进制文件![](https://file2.kaopuke.com:8081/files_image/2023061121/94566ed8bb3847179b5617b066dae2ee.png)
使用说明
方块:
人物-蓝色;
箱子-橙色;
目标位置-红叉;
墙体(边界)-红砖
基本操作:
除游戏界面外,都支持且仅支持鼠标操作,游戏界面使用上下左右方向键控制人物移动
游戏中可以使用 Esc 键 返回
自定义地图使用:
鼠标点击进入自定义地图,选择进入自定义关卡还是重新定义关卡
若选择 进入自定义关卡:
程序通过读取文件,来获得上一次定义的关卡,一旦定义将持续记录到下一次定义
若选择 重新定义关卡:
1.清楚原来的关卡定义
2.弹出对话框 强制要求 分别输入两个数据(地图的宽和高)
3.输入完成后进入创建地图界面
4.鼠标左键放置方块,右键切换方块,单击滚轮完成创建并返回上一界面
自定义地图时报错:
人物数量不唯一:必须修改人物数量使之为 1
箱子数量小于目标位置数量:增加箱子数量使其 大于等于 目标位置即可
边界被更改,请将边界恢复为默认:将地图 最外圈 全部 用红砖(边界)围起来,这里可能会出bug,如果开始边界显示不完整就会报错
最后
以上就是机智手套为你收集整理的推箱子-Easyx版的全部内容,希望文章能够帮你解决推箱子-Easyx版所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复