吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 8313|回复: 75
上一主题 下一主题
收起左侧

[游戏安全] 通杀爆改 Unity FPS 游戏系列(第四章) 子弹范围子弹追踪

  [复制链接]
跳转到指定楼层
楼主
lyl610abc 发表于 2024-12-22 21:58 回帖奖励

索引

通杀爆改 Unity FPS 游戏系列-序章

通杀爆改 Unity FPS 游戏系列-第一章

通杀爆改 Unity FPS 游戏系列-第二章

通杀爆改 Unity FPS 游戏系列-第三章

本章内容

  1. 效果展示
  2. 子弹命中的判定机制
  3. Unity 提供的物理碰撞检测
  4. 分析子弹范围
  5. 实现子弹范围

效果展示

子弹范围

子弹命中的判定机制

目前子弹的判定机制有 3 种

  • Hitscan
  • Projectile
  • Hybrid(Hitscan+Projectile)

Hitscan

直译为命中扫描

可以简单地理解为:命中的结果是扫描出来的,而非实际命中碰撞得到的

这里的扫描:在发射的位置打出一条射线,通过射线来判断是否命中

如下图所示,从枪口发射出了一条射线,这条射线途径的物体会被判定为命中

image-20230925104540744


根据 Hitscan 的特性,可以推断出:在开枪(发出射线)的那一刻,命中结果就已经确定了

但这也会存在一个问题:损失了真实性,无法通过重力,风力等其他外部因素来影响命中的轨迹

同时假设一个敌人在很远的距离,理论上子弹需要飞行一段时间才会命中,这段时间内敌人是有机会躲避的

但是如果只是采用 Hitscan 进行判定,在开枪的那一刻结果就已经注定了


Projectile

Projectile 弹射

子弹是真实存在的物理对象,可以设定子弹的速度、重力 等一系列影响子弹运行轨迹的参数

只有当子弹真正 "运动" 到了对应的位置,才会判定为命中

拿个最简单的对比图:

image-20230925110814554

左图为 Hitscan

右图为 Projectile


Hybrid

前面提到的 Hitscan 和 Projectile 各有其优缺点

轨迹 性能 真实性 子弹
Hitscan 直线 计算简单 射线替代
Projectile 曲线 相对复杂 真实生成

而 Hybrid 就是共同使用 Hitscan 和 Projectile

在 Projectile 的基础上加上 Hitscan

以最高点为例子:

image-20240426182154337

红线表示子弹在 Projectile "真实运动" 到最高点时发出的 Hitscan 射线

如果红线命中了物体,则判定子弹击中了物体

可以简单地做如下理解:

Hitscan 是在子弹发射点(一般为枪口) 发出一条较长的射线,命中结果在开枪的这一刻就已经决定了

Projectile 则是模拟子弹真实运动,子弹的运动轨迹受重力和风速等参数影响,命中结果得在运动过程中实际碰到物体才行

Hybrid 则是在 Projectile 的基础上额外加上了 Hitscan 的命中判定,不过此时的射线对比 Hitscan  就短得多

轨迹 性能 真实性 子弹 应用场景
Hitscan 直线 计算简单 射线替代 狙击枪发射的子弹(飞行速度极快)
Projectile 曲线 相对复杂 真实生成 普通的子弹,比如霰弹枪,下坠很大
Hybrid 曲线 结合 真实生成 + 射线替代 相对自由

Unity 提供的物理碰撞检测

碰撞体

首先明确一点,物理碰撞检测的对象是碰撞体

碰撞体可以简单的理解为 不可见 的用于物理碰撞的游戏对象的形状,通常采用粗略近似而非完全贴合游戏对象(节省性能)

image-20241121103552174

如上图所示,绿色边框为一个 Sphere Collider (球形碰撞体),可以看到并没有和敌人完全贴合,而是粗略近似的覆盖


常用的碰撞体有:为盒型碰撞体球形碰撞体胶囊碰撞体,限于篇幅不展开介绍,简单的区别就是形状不同

而较复杂的碰撞体:MeshCollider(网格碰撞体) 虽然更精准和真实但受限于性能开销,一般较少使用


射线检测

在前面提到的 Hitscan 中,以一个点作为命中判定的起点,然后由该点沿着某个方向发出一条射线,得到这条射线途径的碰撞体

点射线

常用(这里只列举了一个)对应的 Unity 函数为:

    public static int RaycastNonAlloc(Vector3 origin, Vector3 direction, RaycastHit[] results, [UnityEngine.Internal.DefaultValue("Mathf.Infinity")] float maxDistance, [UnityEngine.Internal.DefaultValue("DefaultRaycastLayers")] int layerMask, [UnityEngine.Internal.DefaultValue("QueryTriggerInteraction.UseGlobal")] QueryTriggerInteraction queryTriggerInteraction)
    {
        return defaultPhysicsScene.Raycast(origin, direction, results, maxDistance, layerMask, queryTriggerInteraction);
    }
参数 说明
origin 射线的起点
direction 射线的方向
results 命中结果
maxDistance 允许射线命中距射线起点的最大距离
layerMask 用于在投射射线时选择性地忽略碰撞器
queryTriggerInteraction 指定此查询是否应命中触发器

返回值:命中结果的数量


这里会注意到一个不太 "真实" 的地方,那就是忽略了子弹的体积,直接把子弹看作了一个点,好处显而易见,节省性能

当然在大多数情况下,由于被命中的碰撞体和子弹体积相差较大,因此将子弹看作一个点并不会导致体感上的不真实

但是当子弹体积相对较大时,比如发射一个光波(镭射激光),这个时候用一个点进行判定显然就不太合适了

除了使用前面提到的 Projectile 命中判定(给子弹也挂一个碰撞体,当子弹的碰撞体和其它碰撞体实际接触时才判定命中)外

还可以使用球形射线

球形射线

球形射线:对场景中的所有碰撞器投射一个球体并返回每个被击中的碰撞器的详细信息

image-20241122182357315

常用(这里只列举了一个)对应的 Unity 函数为:

    public static RaycastHit[] SphereCastAll(Vector3 origin, float radius, Vector3 direction, [UnityEngine.Internal.DefaultValue("Mathf.Infinity")] float maxDistance, [UnityEngine.Internal.DefaultValue("DefaultRaycastLayers")] int layerMask, [UnityEngine.Internal.DefaultValue("QueryTriggerInteraction.UseGlobal")] QueryTriggerInteraction queryTriggerInteraction)
    {
        float magnitude = direction.magnitude;
        if (magnitude > float.Epsilon)
        {
            Vector3 direction2 = direction / magnitude;
            return Query_SphereCastAll(defaultPhysicsScene, origin, radius, direction2, maxDistance, layerMask, queryTriggerInteraction);
        }

        return new RaycastHit[0];
    }
参数 说明
origin 射线的起点
radius 球体的半径
direction 射线的方向
results 命中结果
maxDistance 允许射线命中距射线起点的最大距离
layerMask 用于在投射射线时选择性地忽略碰撞器
queryTriggerInteraction 指定此查询是否应命中触发器

可以发现,相较前面的RaycastNonAlloc,多了一个参数也就上球体的半径

球体的运动路径合是一个圆柱体,圆柱体扫过的区域如果包含碰撞体则判定命中

触发器

对于 Projectile 判定机制,则是使用触发器进行碰撞检测

触发器通常用于检测对象之间的重叠或进入某个区域。它们通过 OnTriggerEnterOnTriggerStayOnTriggerExit 等回调函数触发事件

简单理解就是,触发器这一机制将碰撞检测移交给了引擎,当引擎发现有其它碰撞体触发了对象时,会通知给对应事件

特性 触发器(Triggers) 射线(Raycasts)
基本概念 用于检测物体进入、停留或离开触发区域。 用于检测射线与物体的碰撞。
实现方式 通过 Collider 组件设置为触发器。 通过代码发射射线进行检测。
回调函数 使用 OnTriggerEnterOnTriggerStayOnTriggerExit 通过 Physics.Raycast 等方法手动处理。
依赖组件 需要 Collider(设置为触发器)和 Rigidbody。 不需要特定组件,但通常与 Collider 一起使用。
检测频率 低频,基于物体的物理运动触发。 高频,可在每帧或按需调用。
性能 对于大量动态对象,可能会影响性能。 频繁调用可能导致性能问题,需优化使用。
适用场景 区域检测、进入/退出事件。 精确检测、射击、视线检测。
灵活性 依赖物理引擎的更新周期,较为自动化。 手动控制,灵活性高。
复杂性 实现简单,适合初学者。 需要编写更多代码,适合复杂检测。
使用限制 需要物体进入触发器区域才能检测。 可以检测任意方向和距离的碰撞。

对于子弹来说,通常不会使用触发器进行检测,因为触发器低频的特性不大适合作用于子弹

只有少数特定的子弹可以采用触发器进行检测,比如火箭筒的子弹火箭弹,飞行速度较慢,体积较大,频次不会过高

所以这里的触发器只是作为扩展科普内容,不具体展开

分析子弹范围

子弹范围,即子弹打到目标的附近,而不用是目标身上即可判定命中

要修改实现子弹范围,首先得要找到对应进行射线判定的函数,前面已经提到了,大多数子弹都是采用射线检测的

首先理一下逻辑

  • 武器 → 开火 → 消耗武器备弹量  →  生成子弹
  • 子弹飞行 → 飞行过程中打射线判定命中

所以可以以子弹备弹量为突破口,找到开火的逻辑,在开火逻辑附近找到生成子弹的逻辑

从生成子弹的逻辑那确定子弹的类名,在子弹这个类里查找 刷新 的函数

在 刷新 函数里找到射线函数

简单概括一下路径就是 备弹量消耗 → 武器类 → 子弹类 → 刷新函数 → 射线函数

查找子弹类

通过备弹量的消耗可以很快找到对应消耗的函数:Unity.FPS.Game.WeaponController.HandleShoot

在这个函数的开头,偏移 1FE (Unity.FPS.Game.WeaponController.HandleShoot+1FE) 可以发现一个调用

GameAssembly.dll+1FC33E - E8 ED170600           - call GameAssembly.dll+25DB30

到函数里面可以发现

image-20241222204023595

GameAssembly.dll+25DC3E - E8 0DEE9300           - call UnityEngine.Object.Instantiate

UnityEngine.Object.Instantiate 函数是 Unity 引擎中用于创建对象实例的重要方法。它的主要作用是根据给定的原型(Prefab)创建一个新的对象实例。这个函数可以用来在游戏运行时动态生成游戏对象,例如角色、道具、敌人等。

可以推测这里是生成一个子弹实例

回到函数外,在函数调用结束的返回值处下断点

GameAssembly.dll+1FC343 - 8B F0                 - mov esi,eax

image-20241222210018459


开一枪,断下来以后将 EAX 的地址丢入 Tools → Dissect data/structures Ctrl+D 中解析

image-20241222210217091


得到了此时发射的子弹类名:ProjectileStandard

可以在 .NET info 中找到该类的所有函数

image-20241222210446767


定位命中判定

根据前面的分析,判定子弹在飞行过程中(每帧刷新时)会进行命中判定,进入 Update 函数中查找命中判定相关的逻辑

可以很轻松地在函数中找到前面提到的球形射线检测函数

GameAssembly.dll+206F7D - E8 6E869D00           - call UnityEngine.Physics.SphereCastAll

显然本游戏 Demo 使用了射线进行命中判定,再结合子弹的 Fields 中包含 Speed (速度) 和 Gravity (重力) 等关键字,可以确定命中判定机制为 Hybrid

实现子弹范围

实现子弹范围,其核心就是影响物理碰撞检测的判定

有 3 个方向可以修改

  • 修改碰撞体大小,通常为修改要被命中的敌人的碰撞体大小,因为修改子弹大小可能会让子弹在飞行过程中先撞上其它障碍物
  • 修改子弹的生成逻辑,把子弹"修正"到能命中敌人,修正可以直接把子弹位置改到敌人身上,也可以把子弹方向”对准“到敌人身上
  • 修改射线检测输入参数,通常为起始点和方向,对于球形射线检测则多了个半径作为参数

本篇以第三个方向进行实现,前 2 个方向当做是扩展不具体展开

在前面定位到的球形射线检测函数那下断点,观察堆栈信息

image-20241222213150611

不难推断出前 3 个数值为子弹的位置,第四个数值固定为 0.01  也就是球形射线检测的半径

所以将第四个数值改大即可实现子弹范围

开始 Auto assemble 写脚本,关于脚本部分在第二章已经说得比较详细了,这里不再赘述

直接给出脚本代码:

[ENABLE]
//code from here to '[DISABLE]' will be used to enable the cheat
alloc(newmem,2048)
label(returnhere)
label(originalcode)
label(exit)

newmem: //this is allocated memory, you have read,write,execute access
//place your code here
mov [esp+c],(float)1
originalcode:
call UnityEngine.Physics.SphereCastAll

exit:
jmp returnhere

"GameAssembly.dll"+206F7D:
jmp newmem
returnhere:

[DISABLE]
//code from here till the end of the code will be used to disable the cheat
dealloc(newmem)
"GameAssembly.dll"+206F7D:
db E8 6E 86 9D 00
//call UnityEngine.Physics.SphereCastAll

作业

因为本 Demo 采用的是球形射线检测,所以修改判定时的球体半径可以很轻易实现子弹追踪的功能

但如果是普通的射线,就会比较麻烦,同时这种修改半径的方式也有局限性,中间遇到障碍物会先命中障碍物

而且也不够"爽",无法背对着敌人随便开枪都能追踪过去

如何实现全图子弹范围?留作一个课后小作业o(*≧▽≦)ツ

总结

本篇的主要内容并不在于修改本身,而侧重于命中判定以及物理引擎的说明

子弹追踪实际上就是影响命中判定,让客户端"认为"命中了

命中判定的三要素:

  • 子弹碰撞体大小
  • 子弹生成位置和方向
  • 射线参数

修改的难点不在于改,而在于定位,定位命中判定相关的位置

通常可以通过 备弹量消耗 → 武器类 → 子弹类 → 刷新函数 → 射线函数 这一路径来定位

免费评分

参与人数 43威望 +2 吾爱币 +144 热心值 +39 收起 理由
dadadaa + 1 谢谢@Thanks!
SnowDDD + 1 + 1 谢谢@Thanks!
JackTerry + 1 + 1 谢谢@Thanks!
QWEWSY + 1 + 1 用心讨论,共获提升!
lin5789 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
agthe + 1 + 1 谢谢@Thanks!
一动不动不敢动 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
willJ + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
yi十二丶 + 1 谢谢@Thanks!
搞点什么 + 1 + 1 用心讨论,共获提升!
Tusimple + 1 + 1 热心回复!
Zsh251618 + 1 谢谢@Thanks!
Hameel + 1 谢谢@Thanks!
change1982 + 1 + 1 谢谢@Thanks!
烛九阴! + 1 + 1 我很赞同!
acbzdm + 1 + 1 用心讨论,共获提升!
xiuyang6620 + 1 + 1 谢谢@Thanks!
RikimaruMarlon + 1 + 1 Untiy的 必须收藏起来
taiyangjie + 1 + 1 热心回复!
xxxlsy + 1 + 1 我很赞同!
meiguo110 + 1 + 1 谢谢@Thanks!
ae86111 + 1 + 1 我很赞同!
xiiye + 1 热心回复!
Issacclark1 + 1 谢谢@Thanks!
G4732246 + 1 + 1 膜拜大佬一下
love1999 + 1 我很赞同!
stysty0930 + 1 + 1 热心回复!
ssyld + 1 + 1 热心回复!
Minesa + 1 + 1 用心讨论,共获提升!
Fhhjjh36678 + 1 + 1 我很赞同!
xiaoze1993 + 1 + 1 谢谢@Thanks!
WolfAvenue + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
TADYCB + 1 + 1 热心回复!
SherlockProel + 1 + 1 向大佬膜拜
debug_cat + 2 + 1 谢谢@Thanks!
忆魂丶天雷 + 2 + 1 用心讨论,共获提升!
CrazyNut + 3 + 1 用心讨论,共获提升!
BrutusScipio + 1 + 1 用心讨论,共获提升!
CYTuo0601 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
注册个id + 1 + 1 用心讨论,共获提升!
FitContent + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
tantanxin147 + 2 + 1 牛B,看不懂

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
CrazyNut 发表于 2024-12-23 00:14
谢谢大佬更新,其实重点就应该是先去学一下正常的Unity或者UE开发,知道正常的开发流程后再去逆向就轻松很多
推荐
debug_cat 发表于 2024-12-23 09:23
我还记得看第一篇文章的时候,好激动,终于有大佬讲这方面的东西,而且是系统的讲。然后配置开发环境,创建了文章的那个sample,自己编译到手机上玩,自己加了敌人,改了部分逻辑,可惜的是,这个项目默认就不支持手机设备,应该说没适配手机设备,找了很多相关方向接入和配置都没有很好解决人物移动的问题,视觉和开火右半屏逻辑完善。后来忙起来了就没时间继续探索适配移动端移动人物的完善。
3#
 楼主| lyl610abc 发表于 2024-12-22 22:04 |楼主
占个位置,用于扩充命中判定前两个要素的修改方法
4#
pepessss 发表于 2024-12-22 22:20
挂海论坛啊这是,厉害
5#
LXGZJ237 发表于 2024-12-22 22:23
牛逼的帖子
6#
注册个id 发表于 2024-12-22 23:08
终于更新了, 前排支持
7#
BrutusScipio 发表于 2024-12-22 23:17
C#反编译是这样的,或者说托管都有这问题
8#
zpbooks 发表于 2024-12-22 23:59
厉害了,学习一下
9#
pcf8 发表于 2024-12-23 00:59
厉害,学习了
10#
cxk6681 发表于 2024-12-23 01:01
感谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-6-16 18:03

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表