游戏外挂开发原理
外挂现在大体上来讲分为三种,分别是模拟按键,WPE封包和内存挂。比如模拟键盘的,鼠标的,这种就叫做按键模拟,也叫脚本精灵;还有修改数据包的,这种就叫做WPE封包外挂,这种外挂相对而言比较稳定,但是对于技术要求难度也比较高,因为修改WPE封包,你需要知道和了解加密解密算法等等一系列的知识;还有最后一种就是修改本地内存的,这种相对而言比较常见,市场上面比较常见的也是这种游戏外挂,相对而言技术要求也不是太高,资料也比较齐全,比较大众;但好像没有修改服务器内存的哦,呵呵。其实修改服务器也是有办法的,只是技术太高一般人没有办法入手而已。这里就记一下本地修改内存的外挂制作原理。
其实,制作内存挂也不是很难,步骤就这么几步。第一步,找游戏数据内存地址,偏移地址;第二步,修改内存地址的值,达到外挂的目的;第三步,用编程语言写出一个程序,使得外挂方便于携带传输,同时也方便于下次玩游戏直接使用。其实第三步主要是方便使用,方便贩卖等等,很多时候对于一个高手而言,根本不需要写出来,记录下内存地址,偏移地址以后,下次上游戏直接工具里面修改就是了。但是如果经常玩某个游戏,可以写出来,用不着每次玩游戏都去修改。
外挂开发第一步——找游戏内存地址
说道找游戏内存地址,偏移地址,这可能是整个游戏外挂开发中最难的一步,因为一个大型的网络游戏,可能一个技能或者血条的内存基址可能会偏移很多次,最后才得到一个内存地址也叫物理地址。那么,这里有个问题来了,我们为什么要找技能或者血条的内存基址?不是直接修改物理地址就行了吗?这样是不行的,因为物理地址是会变动的;举个例子,每次关闭游戏重新启动以后,游戏进程是会变动的,同样的,游戏代码在内存中的存放位置,因为游戏结束以后,释放掉了内存地址,这个内存地址可能会被其他程序所占用,所以是会变动的;当你关闭游戏重新进入以后,游戏的技能或者血条在内存中的物理地址就变了。但是,游戏技能或者血条的内存基址是不会变动的,在内存中的物理地址不管怎么变动,内存基址都不会变动。那么内存基址和物理地址之间是一种什么样的关系呢?看下面这个公式:1
2
3
4物理地址 = 一级偏移地址 + 偏移地址
一级偏移地址 = 二级偏移地址 + 偏移地址
二级偏移地址 = 三级偏移地址 + 偏移地址
三级偏移地址 = 游戏技能基址 + 偏移地址
如上公式,一个游戏的物理地址是等于这个游戏的基址加上偏移地址的,所以,想要每次重新进入游戏都能够找到准确的物理地址进行修改内存地址的值,达到外挂的目的的话,就必须找到这个物理地址当前对应的偏移地址和基址,只有这样才能够确保每次登陆游戏都能够修改物理地址的值。下面,就通过《植物大战僵尸》这款经典的单机游戏来演示一下找这款游戏的内存基址的过程。
首先,介绍一款找游戏内存基址的工具Cheat Engine简称CE,Cheat Engine是一款内存修改编辑工具 ,它允许修改游戏或软件内存数据,以得到一些其他功能。它包括16进制编辑,反汇编程序,内存查找工具。与同类修改工具相比,它具有强大的反汇编功能,且自身附带了外挂制作工具,可以用它直接生成外挂。好了,这个工具的具体介绍有兴趣的去百度,这里也就是百度了一下,简单的说了一下这个工具的作用,下面,打开植物大战僵尸,打开CE,在CE中找到植物大战僵尸,附加到CE中去,如图所示:
游戏附加到OD以后,看自己想修改哪儿的内容,就去找对应的内存基址,怎么找呢?比如修改无限阳光吧;首先,看一下最开始的阳光是多少?比如这里,开局阳光是50,就在CE中数值
一项输入50,扫描类型
选择精确数值
在数值类型
选择4字节
然后点击首次搜索
,这里不懂为什么这样选择的,自己百度科普一下基础知识。如图所示:
如上图所示,出来了这么多的地址,究竟哪一个是我们想要的物理地址呢?接下来就需要在游戏中改动一下阳光的值,然后点击再次搜索
,结果就会出来了,如图所示:
如上第二幅图所示,我们想要的物理地址就出来了,把这个地址添加到地址列表中去,然后修改一下阳光的值,确认一下是不是想要的地址,不过这里要注意,在修改的时候,我们需要点击列表地址中我们拉下来的这一行的数值
这一栏,其他是修改地址和字节类型用的,不要乱动,如图所示:
如上图所示,这就是我们想要的物理地址,这里我们可以锁定这个内存地址的值,一直是100或者200或者更多。只需要点击前面激活
这一栏选中就行了,从而达到锁定阳光值的效果。但是当关闭游戏,重新进入,这个地址就没用了,又需要重新找。所以,这里就需要找到真正的阳光值的基址和偏移地址,那么,怎么找呢?我们找到这个物理地址以后,就右键
,选择找出是什么改写了这个地址
也就是快捷键F6
这个名字每个版本的汉化效果都不一样的,这里就会弹出一个窗口,是空白,然后我们再到游戏中改变阳光的值,看看这个空白窗口中出现的代码,如图所示:
如上图三所示,分析这两条代码,看见最终都指向了一个指针数值17C88118
,然后我们分析一下这两条代码什么意思,相信稍微懂一点汇编语言的人都能够看懂。首先分析一下mov [edi+00005560],esi
这条指令,我们在详细信息
中看见,寄存器esi
中的代码是000026DE
这个值换算成十进制刚好是9950,那么这个9950是怎么来的呢?其实就是刚刚我在做上一步的时候,说了要变动游戏中的阳光值,所以我先直接把阳光值修改成了10000,然后种了一个石头,刚好是50,所以阳光值发生了变化,这条指令mov [edi+00005560],esi
就是把寄存器esi
的9950的阳光值放入edi+00005560
这个地址中去,这个地址是谁呢?其实这个地址就是刚刚找出来的物理地址,刚好满足前面的公式:物理地址 = 阳光基址地址 + 偏移地址
不信的朋友可以自己加一下算一算,用寄存器edi
里面的指针数值17C88118
加上偏移地址00005560
看看是不是等于阳光物理地址17C8D678
。接下看看add [eax+00005560],ecx
这条指令,add
指令是相加的意思,前面的mov
是赋值或者移动的意思,这条代码的意思就是,把ecx
寄存器中的值加入[eax+00005560]
这个地址中的值当中去,再看看寄存器eax
的地址是多少?刚好又是17C88118
这个指针数值,那么,我们就可以确定一个事情,那就是17C88118
这个指针数值,肯定是上一级的地址的指针数值,那么上一级要么是基址,要么还是一个不偏移地址,这个需要进一步来确认。怎么确认呢?这里不是出来了17C88118
这个指针数值嘛,再次放到CE中搜索一波,看看这个值对应的地址是多少,看看出来的是一个动态地址还是静态地址,这里需要普及一个知识点,在CE中,黑色的地址表示动态地址,绿色的表示静态地址,那好,丢到CE中搜索,这里这个值是十六进制的,所以在搜索的时候,需要勾选搜索框前面的hex,也就是十六进制搜索的意思,这里填好以后点击新的扫描
,首次扫描
如图所示:
这里可以看见结果为56个地址,这么多,我们怎么来确认究竟是哪个呢?可以看见还有很多红色的地址。其实,这里的红色的地址,是一直在改变的地址,我们不用管它,直接点击再次扫描
,把这些红色的过滤掉,然后看看还剩下的地址有哪些,如图所示:
多次点击再次扫描
以后发现不管怎么点击都剩下来了52个结果,然后看了一下这些结果中并没有绿色的静态地址,并且其他地址开头几位都比较像,唯独如图所示两个地址比较特殊,这两个可能就是我们需要的地址了,后来我也去百度了一波,结果所说的是这种开头比较像的是数组
,也就是说,遇到这种,基本都不是我们需要的地址,直接放弃,把这两个特殊的拉进下边的地址列表,开始分析,双击地址
这一栏,在出来的更改地址
窗口中复制下这个地址,然后再勾选指针
选项,在出来的内容框中填写入刚刚复制的地址,在上面的内容框中写入刚刚找到的00005560
这个偏移地址,得到的结果刚好是阳光的物理地址,最后的阳光值也正好和当前的阳光值一样,并且,这两个地址都是一样的,而且都是动态地址,如图所示:
如上图所示,这两个地址加上偏移地址都可以得到阳光物理地址,那么哪个才是我们所需要的真正的一级偏移地址呢?这里就需要我们对这两个地址进行详细的分析了,同样的右键
-找出是什么访问了这个地址
,也就是快捷键的F5
,这里一定不要搞混淆了,前面是找出是什么改写了这个地址
,不一样的!我们进去以后也是和前面一样出来一个窗口,但是这两个窗口里面的内容就不一样了,第一个地址0264A400
在F5
找出是什么访问了这个地址以后的窗口中会出现内容,但是第二个地址0956BD3C
在出现的窗口里面是一片空白,所以,这两个地址中,0264A400
才是我们要找的真正的一级偏移地址,如图所示:
如上第二幅图所示,找到了真正的一级偏移地址0264A400
后,访问这个地址的窗口中看这些代码指令,通过对比,发现这些代码都指向同一个指针数值02649C98
且都有偏移地址00000768
,如图所示:
如上图所示,既然所有的代码都指向了同一个指针数值02649C98
那么就继续按照前面找一级偏移地址的办法,丢到CE中,继续新的十六进制搜索,搜索指针数值02649C98
以后得到如下图所示的结果:
如上图所示,一共搜索到923个地址,而且还有几个绿色的静态地址,这里我们就可以尝试一下了,看看这几个绿色的静态地址究竟是不是我们想要的阳光基址,同样的原理,我们把几个绿色的静态地址拉到地址列表中,在地址
这一栏双击,在出来的更改地址
窗口中复制下这个地址,然后再勾选指针
选项,在出来的内容框中填写入刚刚复制的地址,在上面的内容框中写入刚刚找到的00000768
这个偏移地址,然后再点击添加偏移
在出来的内容框中输入刚刚第一次找到的00005560
这个偏移地址,得到的结果刚好是阳光物理地址和阳光当前的值,并且这几个静态地址的结果都是一样的。这里需要说明一下,为什么这里要填写两个偏移地址,因为刚刚第一次找到的是一级偏移地址,这次是二级偏移地址了,所以,这里1
2
3阳光物理地址 = 一级偏移地址 + 偏移地址
一级偏移地址 = 阳光基址 + 偏移地址
阳光物理地址 =阳光基址 + 第一次的偏移地址 + 第二次的偏移地址
如图所示:
如上四幅图所示,四个静态地址最终结果都是一样的,都可以用作阳光基址,那么就随便取一个就行。这样,阳关的基址和偏移地址就找到了,阳光的基址和偏移地址如下:1
2
3阳光基址:006A9EC0
一级偏移:00000768
二级偏移:00005560
这里可能有人会问,一级偏移地址不是我们第一次找出来的地址吗?你这里的一级偏移不应该是00005560
吗?对,也不对,我这里所说的一级偏移不是一级偏移地址的偏移,而是基于阳光基址的一级偏移,可以理解为从阳关基址开始,第一次偏移是00000768
第二次偏移才是00005560
,这样做主要是为了方便后面写代码。好了,这就是找游戏基址的思路,同样的道理,还可以找植物冷却时间,不过找冷却时间思路不太一样,因为冷却时间我们不知道具体的值,可以通过找未知的初始化值
的方式来找,也可以通过字节
的方式来找,具体详见百度,我这里只是把找游戏内存基址的思路和过程中怎么分析的给写了出来,免得后人掉坑,比如我在找植物冷却时间的时候,就掉了一个坑,我是通过字节
的方式来找来找的,通过植物冷却的时候为0
,冷却好了为1
找到了物理地址以后,在找一级偏移地址的时候,忘记把搜索方式字节
换为4字节
型了,找了半天没找到!其他思路是一样的!
外挂开发第二步——修改游戏内存地址的值
其实,这一步就比较简单了,比如阳光的值,找到了以后,我们每次上游戏,直接在CE的地址列表框中手动添加地址,把像上面我们验证这几个基址的时候一样,把基址,偏移地址填写好,然后在数值
这一栏双击,修改数值成为我们想要的阳光值就可以了,冷却就把数值改为1
即可,这一步就不用过多的提及了!
外挂开发第三步——编写游戏外挂
其实编写外挂只有会写程序的人都知道该怎么弄了,只要找出来想要的功能的内存基址,偏移等,直接在程序中写上读取和写入内存地址就行了,下面就是我写的植物大战僵尸的外挂的源码,简单的分析一波:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193.版本 2
.程序集 窗口程序集_启动窗口
.程序集变量 进程ID, 整数型
.程序集变量 a, 整数型
.子程序 __启动窗口_创建完毕, , , 窗口创建完毕就利用时钟事件获取阳光值并输出
进程ID = 取进程ID (“PlantsVsZombies.exe”)
时钟1.时钟周期 = 999
.子程序 _时钟9_周期事件
标签提示信息.左边 = 标签提示信息.左边 - 2
.如果真 (标签提示信息.左边 = -300)
标签提示信息.左边 = 300
.如果真结束
.子程序 _时钟1_周期事件, , , 获取阳光值并输出
.局部变量 读阳光基址, 整数型
.局部变量 读偏移地址一, 整数型
.局部变量 阳光物理地址, 整数型
读阳光基址 = 读内存整数型 (进程ID, 十六到十 (“006A9EC0”), )
读偏移地址一 = 读内存整数型 (进程ID, 读阳光基址 + 十六到十 (“768”))
阳光物理地址 = 读内存整数型 (进程ID, 读偏移地址一 + 十六到十 (“5560”), )
标签当前阳光值.标题 = “当前阳光值:” + 到文本 (阳光物理地址)
.子程序 _编辑框输入阳光值_获得焦点, , , 设置输入框在被获取焦点以后情况编辑框内容
编辑框输入阳光值.内容 = “”
.子程序 _按钮增加阳光_被单击, , , 增加阳光值
.局部变量 取阳光基址, 整数型
.局部变量 取偏移地址一, 整数型
.局部变量 取阳光物理地址, 整数型
取阳光基址 = 读内存整数型 (进程ID, 十六到十 (“006A9EC0”), )
取偏移地址一 = 读内存整数型 (进程ID, 取阳光基址 + 十六到十 (“768”))
取阳光物理地址 = 读内存整数型 (进程ID, 取偏移地址一 + 十六到十 (“5560”), )
a = 到整数 (编辑框输入阳光值.内容)
写内存整数型 (进程ID, 取偏移地址一 + 十六到十 (“5560”), 取阳光物理地址 + a)
.子程序 _按钮减少阳光_被单击
.局部变量 取阳光基址, 整数型
.局部变量 取偏移地址一, 整数型
.局部变量 取阳光物理地址, 整数型
取阳光基址 = 读内存整数型 (进程ID, 十六到十 (“006A9EC0”), )
取偏移地址一 = 读内存整数型 (进程ID, 取阳光基址 + 十六到十 (“768”))
取阳光物理地址 = 读内存整数型 (进程ID, 取偏移地址一 + 十六到十 (“5560”), )
a = 到整数 (编辑框输入阳光值.内容)
写内存整数型 (进程ID, 取偏移地址一 + 十六到十 (“5560”), 取阳光物理地址 - a)
.子程序 _按钮锁定阳光值_被单击, , , 锁定阳光值
时钟2.时钟周期 = 100
.子程序 _时钟2_周期事件, , , 锁定阳光值时钟事件
.局部变量 锁定阳光基址, 整数型
.局部变量 锁定偏移地址一, 整数型
.局部变量 锁定阳光物理地址, 整数型
锁定阳光基址 = 读内存整数型 (进程ID, 十六到十 (“006A9EC0”), )
锁定偏移地址一 = 读内存整数型 (进程ID, 锁定阳光基址 + 十六到十 (“768”))
锁定阳光物理地址 = 读内存整数型 (进程ID, 锁定偏移地址一 + 十六到十 (“5560”), )
写内存整数型 (进程ID, 锁定偏移地址一 + 十六到十 (“5560”), 1000)
.子程序 _按钮取消锁定阳光_被单击, , , 取消锁定阳光值
时钟2.时钟周期 = 0
.子程序 _按钮锁定冷却1_被单击, , , 锁定冷却1
时钟3.时钟周期 = 100
.子程序 _时钟3_周期事件, , , 锁定冷却1时钟事件
.局部变量 锁定冷却一基址, 整数型
.局部变量 锁定冷却一偏移地址一, 整数型
.局部变量 锁定冷却一偏移地址二, 整数型
.局部变量 锁定冷却一物理地址, 整数型
锁定冷却一基址 = 读内存整数型 (进程ID, 十六到十 (“006A9F38”), )
锁定冷却一偏移地址一 = 读内存整数型 (进程ID, 锁定冷却一基址 + 十六到十 (“768”))
锁定冷却一偏移地址二 = 读内存整数型 (进程ID, 锁定冷却一偏移地址一 + 十六到十 (“144”), )
锁定冷却一物理地址 = 读内存整数型 (进程ID, 锁定冷却一偏移地址二 + 十六到十 (“70”), )
写内存整数型 (进程ID, 锁定冷却一偏移地址二 + 十六到十 (“70”), 1)
.子程序 _按钮取消锁定冷却1_被单击, , , 取消锁定冷却1
时钟3.时钟周期 = 0
.子程序 _按钮锁定冷却2_被单击, , , 锁定冷却2
时钟4.时钟周期 = 100
.子程序 _时钟4_周期事件, , , 锁定冷却2时钟事件
.局部变量 锁定冷却二基址, 整数型
.局部变量 锁定冷却二偏移地址一, 整数型
.局部变量 锁定冷却二偏移地址二, 整数型
.局部变量 锁定冷却二物理地址, 整数型
锁定冷却二基址 = 读内存整数型 (进程ID, 十六到十 (“006A9F78”), )
锁定冷却二偏移地址一 = 读内存整数型 (进程ID, 锁定冷却二基址 + 十六到十 (“768”))
锁定冷却二偏移地址二 = 读内存整数型 (进程ID, 锁定冷却二偏移地址一 + 十六到十 (“144”), )
锁定冷却二物理地址 = 读内存整数型 (进程ID, 锁定冷却二偏移地址二 + 十六到十 (“C0”), )
写内存整数型 (进程ID, 锁定冷却二偏移地址二 + 十六到十 (“C0”), 1)
.子程序 _按钮取消锁定冷却2_被单击, , , 取消锁定冷却2
时钟4.时钟周期 = 0
.子程序 _按钮锁定冷却3_被单击, , , 锁定冷却3
时钟5.时钟周期 = 100
.子程序 _时钟5_周期事件, , , 锁定冷却3时钟事件
.局部变量 锁定冷却三基址, 整数型
.局部变量 锁定冷却三偏移地址一, 整数型
.局部变量 锁定冷却三偏移地址二, 整数型
.局部变量 锁定冷却三物理地址, 整数型
锁定冷却三基址 = 读内存整数型 (进程ID, 十六到十 (“006AA00C”), )
锁定冷却三偏移地址一 = 读内存整数型 (进程ID, 锁定冷却三基址 + 十六到十 (“768”))
锁定冷却三偏移地址二 = 读内存整数型 (进程ID, 锁定冷却三偏移地址一 + 十六到十 (“144”), )
锁定冷却三物理地址 = 读内存整数型 (进程ID, 锁定冷却三偏移地址二 + 十六到十 (“110”), )
写内存整数型 (进程ID, 锁定冷却三偏移地址二 + 十六到十 (“110”), 1)
.子程序 _按钮取消锁定冷却3_被单击, , , 取消锁定冷却3
时钟5.时钟周期 = 0
.子程序 _按钮锁定冷却4_被单击, , , 锁定冷却4
时钟6.时钟周期 = 100
.子程序 _时钟6_周期事件, , , 锁定冷却4时钟事件
.局部变量 锁定冷却四基址, 整数型
.局部变量 锁定冷却四偏移地址一, 整数型
.局部变量 锁定冷却四偏移地址二, 整数型
.局部变量 锁定冷却四物理地址, 整数型
锁定冷却四基址 = 读内存整数型 (进程ID, 十六到十 (“006AA00C”), )
锁定冷却四偏移地址一 = 读内存整数型 (进程ID, 锁定冷却四基址 + 十六到十 (“768”))
锁定冷却四偏移地址二 = 读内存整数型 (进程ID, 锁定冷却四偏移地址一 + 十六到十 (“144”), )
锁定冷却四物理地址 = 读内存整数型 (进程ID, 锁定冷却四偏移地址二 + 十六到十 (“160”), )
写内存整数型 (进程ID, 锁定冷却四偏移地址二 + 十六到十 (“160”), 1)
.子程序 _按钮取消锁定冷却4_被单击, , , 取消锁定冷却4
时钟6.时钟周期 = 0
.子程序 _按钮锁定冷却5_被单击, , , 锁定冷却5
时钟7.时钟周期 = 100
.子程序 _时钟7_周期事件, , , 锁定冷却5始终事件
.局部变量 锁定冷却五基址, 整数型
.局部变量 锁定冷却五偏移地址一, 整数型
.局部变量 锁定冷却五偏移地址二, 整数型
.局部变量 锁定冷却五物理地址, 整数型
锁定冷却五基址 = 读内存整数型 (进程ID, 十六到十 (“006AA00C”), )
锁定冷却五偏移地址一 = 读内存整数型 (进程ID, 锁定冷却五基址 + 十六到十 (“768”))
锁定冷却五偏移地址二 = 读内存整数型 (进程ID, 锁定冷却五偏移地址一 + 十六到十 (“144”), )
锁定冷却五物理地址 = 读内存整数型 (进程ID, 锁定冷却五偏移地址二 + 十六到十 (“1B0”), )
写内存整数型 (进程ID, 锁定冷却五偏移地址二 + 十六到十 (“1B0”), 1)
.子程序 _按钮取消锁定冷却5_被单击, , , 取消锁定冷却5
时钟7.时钟周期 = 0
.子程序 _按钮锁定冷却6_被单击, , , 锁定冷却6
时钟8.时钟周期 = 100
.子程序 _时钟8_周期事件, , , 锁定冷却6时钟事件
.局部变量 锁定冷却六基址, 整数型
.局部变量 锁定冷却六偏移地址一, 整数型
.局部变量 锁定冷却六偏移地址二, 整数型
.局部变量 锁定冷却六物理地址, 整数型
锁定冷却六基址 = 读内存整数型 (进程ID, 十六到十 (“006AA00C”), )
锁定冷却六偏移地址一 = 读内存整数型 (进程ID, 锁定冷却六基址 + 十六到十 (“768”))
锁定冷却六偏移地址二 = 读内存整数型 (进程ID, 锁定冷却六偏移地址一 + 十六到十 (“144”), )
锁定冷却六物理地址 = 读内存整数型 (进程ID, 锁定冷却六偏移地址二 + 十六到十 (“200”), )
写内存整数型 (进程ID, 锁定冷却六偏移地址二 + 十六到十 (“200”), 1)
.子程序 _按钮取消锁定冷却6_被单击, , , 取消锁定冷却6
时钟8.时钟周期 = 0
由于是易语言写的,比较简单快捷,我大概说一下就好了,就是在外挂软件启动以后,我就读取植物大战僵尸的游戏进程ID,然后读取阳光的物理地址,加以输出显示,然后在启动窗口创建完毕的时候还加了一个时钟,达到实时监控阳光值的目的,然后设置了几个按钮,分别是增加阳光、锁定阳光、锁定冷却时间和减少阳光、取消锁定阳光、取消锁定冷却时间。下面分别来说一下几个按钮怎么实现功能的。
增加阳光
增加阳光就定义了一个全局变量,变量中获取输入框中的值,然后让阳光物理地址里面的值加上输入的值,就达到了增加阳光的目的!
减少阳光
减少阳光和增加阳关的写法一模一样,只不过把阳光物理地址里面的值设置为了减去输入的值而已,这样就达到了减少阳关的目的!
锁定阳光
锁定阳光我定义了一个时钟,设置时钟周期为999毫秒,然后设置了阳光固定值1000。当按钮锁定阳光被单击以后,就执行这个时钟的时钟周期,也就是每隔999毫秒执行一次时钟下面的代码,也就是执行在阳光物理地址中改阳光的值为1000,这样就达到了锁定阳光的目的!
取消锁定阳光
取消锁定阳光其实就是把锁定阳光这个时钟的时钟周期设置为0,当取消锁定阳光的按钮被单击以后,就执行这个时钟周期事件,时钟下面的代码也就不执行了,达到了取消阳光锁定的目的!
后面的锁定冷却时间和取消锁定冷却时间的原理和上面锁定阳光和取消锁定阳光的原理一样,就不再做详细的解释了,因为我这里针对每个栏位的植物做了单独的冷却锁定和取消锁定,所以代码比较长,其实在找植物冷却时间的时候,可以配合OD,直接找到冷却时间前面的循环干掉,这样所有的植物栏的冷却时间都没了!
总结
其实不管是单机游戏也好,还是网络游戏也罢,原理都一样,只不过,可能游戏类型不同,所需要的东西也不一样,比如射击类游戏,就需要D3D技术来做人物透视的定位等等,这个具体的方法,可以百度参考。但是万变不离其中,都是找内存基址,找偏移地址。