devc++ 5.11编译环境
dll编译环境设置参考
Dev c++ C语言实现第一个 dll 动态链接库 创建与调用-CSDN博客
插件 DLL代码和主程序代码如下
注意 dll 代码中的class 类名需要 和主程序 相同
其中使用了函数指针和强制类型转换
函数指针教程参考
注意,退出游戏后需等30-50秒,让dll自行销毁,然后重开时才能正常运行。否则进入后不显示地图。
有dll时:
无DLL插件读取到时,即dll不在同程序目录下
dll.h
#ifndef _DLL_H_
#define _DLL_H_
#include <iostream>
using namespace std;
// 游戏背景操作板,可以通过坐标直接改
class Bkmap
{
public:
Bkmap(int height,int wide)
{
this->wide=wide;
this->height=height;
this->bkmap=new char*[this->height];
for(int i=0; i<height; i++)
{
this->bkmap[i]=new char[this->wide];
for(int j=0; j<wide; j++)
{
this->bkmap[i][j]='\0';
}
}
}
public:
char** bkmap;
int wide;
int height;
};
// 玩家类,移动,攻击,地图修改
class Player
{
public:
Player(int x,int y,int limitx,int limity)
{
this->playerx=x;
this->playery=y;
is_atk=0;
is_buff=0;
flag_x=0;
flag_y=0;
this->limitx=limitx;
this->limity=limity;
}
~Player();
public:
char player='1';
int playerx;
int playery;
int flag_x;
int flag_y;
int limitx;
int limity;
int is_atk;
int is_buff;
public:
};
// 攻击类,这里直线攻击做测试
class Aking
{
public:
Aking(int height,int wide,int atk_time,int number)
{
this->height=height;
this->wide=wide;
this->number=number;
this->atk_time=atk_time;
atk_count=0;
}
public:
int height; // 攻击范围长宽
int wide;
int number; // 攻击序号,因为有多个攻击技能,所以编号,可以查找攻击
int atk_time; // 攻击时长
int atk_count; // 当前攻击持续时间
public:
// 攻击中
void atking(Bkmap* bkmap,Player* player)
{
if (atk_count != atk_time) // 攻击
{
atk_count++;
cout<<"DLL"<<atk_count<<endl;
cout<<"DLL:playerx player y:"<<player->playerx<<","<<player->playery<<endl;
cout<<"DLL: player is_atk:"<<player->is_atk<<endl;
for(int j=0; j<10; j++)
{
bkmap->bkmap[player->playery][player->playerx+1+j]='P';
}
}
else
{
atk_count=0;
player->is_atk=0; // 玩家状态复位
}
}
};
static Aking* akv3=new Aking(20,50,20,3); // 静态类,在调用时,保存被调用的值,调用结束后数值不清空
extern "C"{
void mainDll(Bkmap* bkmap,Player* player);
}
#endif
插件 dll.cpp
/* Replace "dll.h" with the name of your header */
#include "dll.h"
#include <windows.h>
void mainDll(Bkmap* bkmap,Player* player)
{
// cout<<"DLL: atking class start"<<endl;
akv3->atking(bkmap,player);
}
主程序,注意和dll放在同一个目录下
#include <iostream>
#include <string.h>
#include <windows.h>
#define KEY_DOWN(vKey) ((GetAsyncKeyState(vKey) & 0x8000) ? 1:0) // 判断是否按下按键,按下之后,最高位变成 1,所以需要位运算去除其他位,只检测最高位
#define KEY_DOWN_FOREGROUND(hWnd, vk) (KEY_DOWN(vk) && GetForegroundWindow() == hWnd) // 前景窗口判断
#pragma warning(disable : 4996)
using namespace std;
// 加入的游戏背景数据,通常不修改,放到 Bkmap 进行修改
class Gamemap
{
public:
Gamemap(int height,int wide)
{
this->wide=wide;
this->height=height;
this->gamemap=new int*[this->height];
for(int i=0; i<height; i++)
{
this->gamemap[i]=new int[this->wide];
for(int j=0; j<this->wide; j++)
{
this->gamemap[i][j]=0;
}
}
}
~Gamemap();
public:
int** gamemap;
int wide;
int height;
};
// 游戏背景操作板,可以通过坐标直接改
class Bkmap
{
public:
Bkmap(int height,int wide)
{
this->wide=wide;
this->height=height;
this->bkmap=new char*[this->height];
for(int i=0; i<height; i++)
{
this->bkmap[i]=new char[this->wide];
for(int j=0; j<wide; j++)
{
this->bkmap[i][j]='\0';
}
}
}
public:
char** bkmap;
int wide;
int height;
public:
void adddata()
{
for(int i=0; i<this->height; i++) // 地图数据写入
{
for(int j=0; j<this->wide-1; j++)
{
this->bkmap[i][j]='*';
}
this->bkmap[i][this->wide-1]='\0';
cout<<this->bkmap[i]<<"ok"<<endl;
}
}
void fresh(Gamemap* gamemap)
{
for(int i=0; i<gamemap->height; i++) // 地图复印到选区
{
for(int j=0; j<gamemap->wide; j++)
{
if(gamemap->gamemap[i][j]==0)
this->bkmap[i][j]=' '; // 这里决定地图打印在屏幕的样子
}
}
}
};
// 字符串缓冲类
class Showmap
{
public:
Showmap(int height,int wide)
{
this->showmap=new char[wide*height+1000];
strcpy(showmap,"");
// showmap={}; // 不能这样写,否则报错,指针重新变空
}
public:
char* showmap;
public:
void adddata(Bkmap* bkmap)
{
cout<<"test"<<endl;
if(showmap==NULL)
{
cout<<"NULL";
cout<<"函数因空指针结束"<<endl;
return;
}
strcpy(showmap,"");
for(int i=0; i<bkmap->height; i++) // 选区加入到打印缓冲区
{
strcat(showmap,bkmap->bkmap[i]);
strcat(this->showmap,"\n");
}
}
void show()
{
cout<<this->showmap;
}
};
// 玩家类,移动,攻击,地图修改
class Player
{
public:
Player(int x,int y,int limitx,int limity)
{
this->playerx=x;
this->playery=y;
is_atk=0;
is_buff=0;
flag_x=0;
flag_y=0;
this->limitx=limitx;
this->limity=limity;
}
~Player();
public:
char player='1';
int playerx;
int playery;
int flag_x;
int flag_y;
int limitx;
int limity;
int is_atk;
int is_buff;
public:
//玩家移动检测
void checkmove(HWND hwnd)
{
flag_x=0;
flag_y=0;
if (KEY_DOWN_FOREGROUND(hwnd, 0x41)) // A
{
flag_x -= 1;
}
if (KEY_DOWN_FOREGROUND(hwnd, 0x57)) // W
{
flag_y -= 1;
}
if (KEY_DOWN_FOREGROUND(hwnd, 0x44)) // D
{
flag_x += 1;
}
if (KEY_DOWN_FOREGROUND(hwnd, 0x53)) // S
{
flag_y += 1;
}
}
// 打包速度检测
void checkspeed()
{
if (flag_x > 1) // 速度限制
flag_x = 1;
else if (flag_x < -1)
flag_x = -1;
if (flag_y > 1)
flag_y = 1;
else if (flag_y < -1)
flag_y = -1;
}
// 改变玩家位置
void move()
{
if (flag_x) // 位移改变
playerx += flag_x;
if (flag_y)
playery += flag_y;
}
// 边界检测
void checkboundary()
{
if (playerx >= limitx) // 角色位置限制
playerx = limitx - 1;
else if (playerx < 0)
playerx = 0;
if (playery >= limity)
playery = limity - 1;
else if (playery < 0)
playery = 0;
}
void putinmap(Bkmap* bkmap)
{
bkmap->bkmap[playery][playerx]=player;
}
// 检测攻击
void checkatk(HWND hwnd)
{
if (is_atk == 0 && KEY_DOWN_FOREGROUND(hwnd, 0x4A)) // j 键攻击
{
is_atk = 1;
}
}
// 检测buff
void checkbuff(HWND hwnd)
{
if (is_buff == 0 && KEY_DOWN_FOREGROUND(hwnd, 0x4B)) // k 键增加范围 buff
{
is_buff = 1;
}
}
// 绘制地图
void drawmap(HWND hwnd,Gamemap* gamemap)
{
if (KEY_DOWN_FOREGROUND(hwnd, 0x30)) // 0 键绘制地图
gamemap->gamemap[playery][playerx] = 0;
if (KEY_DOWN_FOREGROUND(hwnd, 0x36)) // 6 键绘制地图
gamemap->gamemap[playery][playerx] = 6;
if (KEY_DOWN_FOREGROUND(hwnd, 0x37)) // 7 键绘制地图
gamemap->gamemap[playery][playerx] = 7;
if (KEY_DOWN_FOREGROUND(hwnd, 0x38)) // 8 键绘制地图
gamemap->gamemap[playery][playerx] = 8;
if (KEY_DOWN_FOREGROUND(hwnd, 0x39)) // 9 键绘制地图
gamemap->gamemap[playery][playerx] = 9;
}
};
class Dlling
{
typedef void(*Menu)(Bkmap* bkmap,Player* player); // 定义函数指针,函数返回值是 void, 指针类型名是 Menu, 参数是Bkmap*
public:
Dlling(char* name,int number)
{
mainDll="mainDll";
HINSTANCE hDLL = LoadLibrary(name); // 加载dll库
if(hDLL==NULL)
{
cout<<"没有在本目录里找到 dllv3.dll"<<endl;
atk=NULL;
Sleep(300);
}
else
{
void(*atkv2)(Bkmap* bkmap,Player*)= (void (*)(Bkmap*,Player*))GetProcAddress(hDLL,mainDll); // 加载dll 的入口主函数 强制类型转换后,加载的dll主函数成为atkv2的替身
if(atkv2==NULL)
{
cout<<"dllv3 插件没有读取"<<endl;
atk=NULL;
Sleep(300);
}
else
{
cout<<"dllv3 读取成功"<<endl;
Sleep(300);
atk = atkv2;
}
}
count=0;
}
public:
char* mainDll;
Menu atk;
int count; // 成员是一个指针,目前没有函数替身,所以没有编译报错
public:
void aking(Bkmap* bkmap,Player* player)
{
cout<<"class_atkv3"<<endl;
if(player->is_atk&&atk!=NULL)
{
cout<<"class_atkv3 is going"<<endl;
atk(bkmap,player); // 函数指针在初始化过程在有了替身函数,所以可以加入变量
}
else if(player->is_atk&&atk==NULL)
{
cout<<"dll是空的,跳过执行函数"<<endl;
count++;
if(count>10)
{
player->is_atk=0;
count=0;
}
}
}
};
int main()
{
Gamemap* gamemap = new Gamemap(20,50);
Bkmap* bkmap=new Bkmap(20,50);
Showmap* showmap= new Showmap(20,80);
Dlling* dll = new Dlling("dllv3.dll",3);
bkmap->fresh(gamemap);
// bkmap->adddata();
showmap->adddata(bkmap);
showmap->show();
HWND hwnd = GetForegroundWindow(); // 获取前端窗口句柄,由于程序刚运行时是在前端,所以这就是本程序的窗口句柄
Player* player = new Player(0,0,50,20);
while(1)
{
if (KEY_DOWN_FOREGROUND(hwnd, VK_ESCAPE))
{
printf("游戏退出\n");
break;
}
if(KEY_DOWN_FOREGROUND(hwnd,0x31))
{
player->is_atk=1;
}
player->checkmove(hwnd);
player->checkspeed();
player->move();
player->checkboundary();
bkmap->fresh(gamemap);
dll->aking(bkmap,player);
player->putinmap(bkmap);
showmap->adddata(bkmap);
cout<<"玩家位置:"<<player->playerx<<","<<player->playery<<endl;
showmap->show();
Sleep(100);
system("cls");
}
return 0;
}