实现第一个动态链接库 游戏插件 成功在主程序中运行 dll 中定义的类

news2024/11/28 2:39:26

devc++ 5.11编译环境

dll编译环境设置参考

Dev c++ C语言实现第一个 dll 动态链接库 创建与调用-CSDN博客 

 插件 DLL代码和主程序代码如下

注意 dll 代码中的class 类名需要 和主程序 相同

其中使用了函数指针和强制类型转换

函数指针教程参考

以动态库链接库 .dll 探索结构体参数-CSDN博客

注意,退出游戏后需等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;

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1577060.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于51单片机的温度、烟雾、火焰检测设计

基于51单片机的火灾检测设计 &#xff08;仿真&#xff0b;程序&#xff0b;原理图设计报告&#xff09; 功能介绍 具体功能&#xff1a; 1.使用MQ-2烟雾采集&#xff0c;使用ADC0832将传感器输出的模拟信号转化为数字信号&#xff0c;再传给单片机。 2.使用DS18B20采集温度。…

fastlio2 保存每帧的点云和每帧的里程计为单独的文件做后端回环优化和手动回环优化

为了 提供数据做后端回环优化和手动回环优化,需要保存每帧的点云和每帧的里程计为单独的文件,并且需要保存的名字为ros时间戳。 效果很好,比我自己写的手动回环模块好用 // This is an advanced implementation of the algorithm described in the // following paper: /…

Ceph学习 -4.Ceph组件介绍

文章目录 1.Ceph组件介绍1.1 组件介绍1.2 流程解读1.2.1 综合效果图1.2.2 数据存储逻辑 1.3 小结 1.Ceph组件介绍 学习目标&#xff1a;这一节&#xff0c;我们从组件介绍、流程解读、小结三个方面来学习。 1.1 组件介绍 无论是想向云平台提供 Ceph 对象存储和 Ceph 块设备服务…

Mamba入局遥感图像分割 | Samba: 首个基于SSM的遥感高分图像语义分割框架

文章目录 1、导读 2、背景 3、动机 4、方法 5、实验 6、总结 标题&#xff1a;《Samba: Semantic Segmentation of Remotely Sensed Images with State Space Model》论文&#xff1a;https://arxiv.org/abs/2404.01705源码&#xff1a;https://github.com/zhuqinfeng1999…

原来科技感的三维地图可以这么简单实现

前言 2024.02.20 下午摸鱼时接到一个客户的数字孪生项目的需求&#xff0c;客户什么也没说&#xff0c;就要求“炫酷”和“科技感”地图&#xff0c;还要把他们的模型都放上去&#xff0c;起初我以为又是一个可视化大屏的项目&#xff0c;准备用高德地图应付过去&#xff0c;然…

spring面试八股

常用的注册bean的方式 ComponentScan扫描到的service和Controller等的注解 Configration配置类或者是xml文件的定义。 spring中有几种依赖注入的方式 1.构造器注入。 2.setter方法注入。 3.使用field属性的方式注入。 applicationContext是什么 spring bean spring aop Aop…

03-JAVA设计模式-建造者模式

建造者模式 什么是建造者模式 建造者模式&#xff08;Builder Pattern&#xff09;是一种对象构建的设计模式&#xff0c;它允许你通过一步一步地构建一个复杂对象&#xff0c;来隐藏复杂对象的创建细节。 这种模式将一个复杂对象的构建过程与其表示过程分离&#xff0c;使得…

目标点注意力Transformer:一种用于端到端自动驾驶的新型轨迹预测网络

目标点注意力Transformer&#xff1a;一种用于端到端自动驾驶的新型轨迹预测网络 附赠自动驾驶学习资料和量产经验&#xff1a;链接 摘要 本文介绍了目标点注意力Transformer&#xff1a;一种用于端到端自动驾驶的新型轨迹预测网络。在自动驾驶领域中&#xff0c;已经有很多…

深度比较Vue 3.0中的computed和watch属性用法与最佳实践

摘要&#xff1a;在Vue 3.0中&#xff0c;computed和watch属性是用于处理数据逻辑的重要工具。本文将详细对比这两个属性的工作原理、适用场景以及使用时的注意事项&#xff0c;旨在帮助开发者更有效地选择和使用它们。 一、computed属性 computed属性是Vue 3.0中用于计算数据…

wpf TreeView 实现动态加载页面

实现以下的效果&#xff0c;在TreeView上点击节点&#xff0c;动态加载右边的页面&#xff0c;如下图所示&#xff1a; 1. 主页面如下&#xff1a; 2. 实现主页面刷新方法 _currentStateViewModel.RefreshState(); _currentStateViewModel就是点击TreeView上的节点&#xff0…

Linux安装最新版Docker完整教程

参考官网地址&#xff1a;Install Docker Engine on CentOS | Docker Docs 一、安装前准备工作 1.1 查看服务器系统版本以及内核版本 cat /etc/redhat-release1.2 查看服务器内核版本 uname -r这里我们使用的是CentOS 7.6 系统&#xff0c;内核版本为3.10 1.3 安装依赖包 …

c++20协程详解(四)

前言 到这就是协程的最后一节了。希望能帮到大家 代码 到这里我们整合下之前二、三节的代码 #include <coroutine> #include <functional> #include <chrono> #include <iostream> #include <thread> #include <mutex> #include <me…

政安晨【AIGC实践】(一):在Kaggle上部署使用Stable Diffusion

目录 简述 开始 配置 执行 安装完毕&#xff0c;一键运行 结果展示 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 人工智能数字虚拟世界实践 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提…

spring加载类初始化顺序

今天看spring官网的时候&#xff0c;提到了Ordered执行顺序。我当时记得PostConstruct注解会在bean加载后执行&#xff0c;现在又来了一个执行顺序&#xff0c;直接给我整蒙了。 于是我写了一个简单的dom来看看&#xff0c;它是什么&#xff1a; Service("t2ServerImpl&q…

Vue - 你知道Vue中key的工作原理吗

难度级别:中级及以上 提问概率:80% 在Vue项目开发中,并不推荐使用索引做为key,以为key必须是唯一的,可以使用服务端下发的唯一ID值,也不推荐使用随机值做为key,因为如果每次渲染都监听到不一样的key,那么节点将无法复用,这与Vue节省…

中药提取物备案数据库<5000+中药提取物>

NMPA中药提取物备案数据库的建立是确保中药提取物质量安全、规范生产行为、加强监管、保障公众用药安全、促进产业发展和国际化的重要措施。 通过查询中药提取物备案信息我们能了解到中药提取物的实用备案号、药品通用名称、药品生产企业、批准文号、备案日期、备案状态、中药…

分表?分库?分库分表?实践详谈 ShardingSphere-JDBC

如果有不是很了解ShardingSphere的可以先看一下这个文章&#xff1a; 《ShardingSphere JDBC?Sharding JDBC&#xff1f;》基本小白脱坑问题 阿丹&#xff1a; 在很多开发场景下面&#xff0c;很多的技术难题都是出自于&#xff0c;大数据量级或者并发的场景下面的。这里就出…

【LeetCode刷题记录】11. 盛最多水的容器

11 盛最多水的容器 给定一个长度为 n 的整数数组 height。有 n 条垂线&#xff0c;第 i 条线的两个端点是 ( i , 0 ) (i, 0) (i,0)和 ( i , h e i g h t [ i ] ) (i, height[i]) (i,height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的…

MySQL学习路线一条龙

引言 在当前的IT行业&#xff0c;无论是校园招聘还是社会招聘&#xff0c;MySQL的重要性不言而喻。 面试过程中&#xff0c;MySQL相关的问题经常出现&#xff0c;这不仅因为它是最流行的关系型数据库之一&#xff0c;而且在日常的软件开发中&#xff0c;MySQL的应用广泛&#…

数字人直播系统是什么?AI数字人直播间搭建方法来了!

无人直播的时代&#xff0c;短视频和直播平台正在风口&#xff0c;各条赛道内也早已人满为患&#xff0c;很多线下商家都想参与其中&#xff0c;因为时间、地方、设备等限制久久不能去实行起来。所以&#xff0c;数字人直播新模式成为了线下商家的救星&#xff0c;线下商家方法…