魔塔描述:
- 本项目使用C语言完成,通过WASD控制人物上下作用移动和怪物和道具进行碰撞后执行相应的操作,根据NPC的提示完成任务。
实现的功能:
- 人物的上下左右移动
- 碰撞检测CollisionCB
- 楼层的切换
- 游戏的读档存档
- NPC对话
- 道具的捡拾和使用
- 怪物列表
- 商店购买道具
- 战斗系统
- 购买系统
使用到的技术:
- 使用结构体指针实现继承效果,让所有的结构体继承于基础结构体Base,把父结构体指针作为子结构体的成员属性,从而拿到父结构体的成员函数和属性;
- 使用函数指针实现成员函数,实现碰撞检测;
- 设计英雄和其他元素进行碰撞是由其他元素主动检测英雄,英雄处于被动检测,减少英雄的开销。
- 使用函数指针和指针数组配合,将各个结构体的初始化函数的指针存入到数组中,根据type选择初始化函数创建元素,从而实现动态创建;
- 设计背包系统,显示英雄的状态和信息,英雄的状态和属性与英雄是绑定的在进行关卡切换时逻辑删除本层英雄,在插入到切换层中,实现英雄属性的同步;
- 模拟攻击,在对战之前通过数学计算是否打得过,以及模拟攻击的行为,用于判断是否正式攻击;
- 通过文件加载地图和历史记录,在下次打开游戏可以进行读档,也可以执行存档保留本次游戏数据下次使用。
数据结构:
这里唯一要介绍的就是数据结构的不同,C和C++中的结构体是不一样的
C、C++结构体区别:
-
C的结构体内不允许有函数存在,C++内允许有内部成员函数,且允许时虚函数
-
C的结构体内没有构造函数、析构函数和this指针
-
C的结构体对成员变量的访问权限只能是public,而C++允许有public,protected,private三中
-
C语言的结构体是不可以基础的,C++的结构体是可以从其他结构体或者类继承过来的
-
C语言中使用结构体如果没有typedef必须加struct
数据结构:
//碰撞回调函数
typedef int(*FCOLLISIONCB)(void*, void*);
//保存回调函数
typedef int (*FSaveCB) (struct tagBase* pBase, FILE* pfile);
//初始结构
typedef struct tagBase
{
int x; //坐标
int y; //坐标
int type; //当前元素的类型 即 枚举值
const char* name; //当前元素打印的符号
FCOLLISIONCB callback; //发生碰撞时调用的回调函数
FSaveCB SaveCB; //保存数据时调用的回调函数
}TBase,*PTBase;
动态创建:
static FCREATEINITCB* pCreateInitCB; //函数指针 二级指针
//初始-初始化管理器 开辟内存储存函数指针
void InitMgro()
{
pCreateInitCB = NULL;
//申请一个数组长度的内存空间
pCreateInitCB = malloc(sizeof(FCREATEINITCB) * N);
//把当前指针指向地址的元素全部清零
memset(pCreateInitCB, 0, sizeof(FCREATEINITCB) * N);
pCreateInitCB[E_WALL] = CreateInitWall;
pCreateInitCB[E_HERO] = CreateHeroCB;
pCreateInitCB[E_REDSLIME] = CreateInitRedSlime;
pCreateInitCB[E_GREENSLIME] = CreateInitGreenSlime;
pCreateInitCB[E_BAT] = CreateInitBat;
pCreateInitCB[E_REDKEY] = CreateRedKey;
pCreateInitCB[E_YELLOWKEY] = CreateYellowKey;
pCreateInitCB[E_BLUEKEY] = CreateBlueKey;
pCreateInitCB[E_YELLOWDOOR] = CreateYellowDoor;
pCreateInitCB[E_BLUEDOOR] = CreateBlueDoor;
pCreateInitCB[E_REDDOOR] = CreateRedDoor;
pCreateInitCB[E_TRANSUP] = CreateTransferUP;
pCreateInitCB[E_TRANSDOWN] = CreateTransferDown;
pCreateInitCB[E_GREENBLOOD] = CreateGreenMedicine;
pCreateInitCB[E_REDBLOOD] = CreateRedMedicine;
pCreateInitCB[E_REDSTONE] = CreateRedStone;
pCreateInitCB[E_GREENSTONE] = CreateGreenStone;
pCreateInitCB[E_YITIANSWORD] = CreateYITIANSword;
pCreateInitCB[E_TULONGSWORD] = CreateTULONGSword;
pCreateInitCB[E_BOSSSWORD] = CreateBOSSSword;
pCreateInitCB[E_LONGSHIELD] = CreateLongShiled;
pCreateInitCB[E_BACKSHIELD] = CreateBackShiled;
pCreateInitCB[E_PENGSHIELD] = CreatePengShiled;
pCreateInitCB[E_MONMAP] = CreateMonMap;
pCreateInitCB[E_SKULL] = CreateInitSkull;
pCreateInitCB[E_SOLDER] = CreateInitSolder;
pCreateInitCB[E_MASTER] = CreateInitMaster;
pCreateInitCB[E_IRONDOOR] = CreateIronDoor;
pCreateInitCB[E_BUSINESSMAN] = CreateBusiness;
pCreateInitCB[E_CLEVER] = CreateClever;
pCreateInitCB[E_THIEF] = CreateThief;
pCreateInitCB[E_STORE] = CreateStore;
pCreateInitCB[E_PASS] = CreatePass;
//........
}
//根据枚举创建元素
PTBase CreateByEnum(int type, int x, int y, struct tagBroad* pBroad)
{
if (pCreateInitCB[type])
{
return pCreateInitCB[type](x, y, pBroad);
}
return NULL;
}
被动检测:
- 除了英雄以外其他元素含有碰撞回调函数,所有的元素检测英雄
- 英雄只负责移动的展示属性
//循环操作 移动
void UpdateBroad(PTBroad pBroad)
{
//指向游戏场景的循环
char c = _getch();
int dirX;
int dirY;
dirX = dirY = 0;
switch (c)
{
case 'w':dirX = -1;
break;
case 'a':dirY = -1;
break;
case 's':dirX = 1;
break;
case 'd':dirY = 1;
break;
case 'h':gpause = !gpause;
break;
case 'm':gmap = !gmap;
break;
case '