鬼知道这两天我经历了什么。
之前的贴删了,原因看下面吧……
Sain模组,相信玩离线版的没有不安装的吧。事情的起因请看情景演示:
你拿着一支八倍镜大狙,开了一局森林,趴在狙击山上,向两百米外的ai开了一枪。
很不幸,没打死。
此时ai仅花了一秒钟便转头望向狙击山,竟在两百米外与你对起枪来。
不仅如此,随着枪声响起,其他ai竟然也神秘地发现了你的位置,顿时你身边的石头火花四溅!
屏幕一黑,你睁开眼睛一看,头顶下颌腋下。
我顿时陷入了深深的怀疑。
为什么?这些ai凭什么能发现我?!
于是我去github下了Sain的源代码,一个下午过去后,再次陷入了深深的怀疑。
长话短说,Sain中ai索敌的方法有两种,声音和视力。
先说声音。
Sain对声音的处理逻辑,包括枪声、非枪声(说话、脚步、换弹、扔雷等等),和“子弹打中物体声”三种情况。
于是我找到了HearingInputClass.cs,这儿负责处理以上的所有声音输入。
本来想找找ai到底是怎么发现我的,好家伙,一眼看到好几个bug。
bug1:近距离震聋
if (_BotDeafedTime < Time.time)
{
return true;
}
这段代码的机制是ai在近距离可能被枪声震聋(听力暂时下降),
但这儿的大于号方向反了,导致这机制完全无效!
bug2:非枪声处理
// 处理对话声音时,传入的列表错误
if (AISoundCachedEvents_Conversations.Count > 0)
{
ProcessSounds(AISoundCachedEvents, AlreadyDeafened, DeafenCoef_Convo, SoundDataToReactTo);
// Bug: 这里传入了 AISoundCachedEvents (通用列表),而不是 AISoundCachedEvents_Conversations
}
这段代码的机制是让ai对非枪声也能有反应,然而可能是他写顺手了,参数复制粘贴错了!
我说我一直F1怎么都钓不到ai,感情是根本没生效!现版本的Sain机器人只对枪声有反应!
力竭了。Sain作者的代码风格……怎么说呢,比较粗犷。
改完这两个bug之后,我找到这儿有一个“子弹打中物体声”的逻辑:
float dispersion = distance / IMPACT_DISPERSION; Vector3 random = UnityEngine.Random.onUnitSphere;random.y = 0;random = random.normalized * dispersion; Vector3 estimatedPos = enemy.EnemyPosition + random;
这下看懂了。
意思就是说,如果你在两百米外马枪了,打中了ai旁边的地板,
该方法会立刻返回你的精确坐标+一个随机误差(属实有点脱裤子放屁)。
换言之,开枪的那一秒你的位置就暴露了。
然后,我又找到了HearingDispersionClass.cs,这儿负责处理枪声的模糊。结果一进来就又绷不住了。
float ratio = distanceFromLastKnown - MIN_DISTANCE_LAST_KNOWN_NO_RANDOMIZATION / MAX_DISTANCE_LASTKNOWN_REDUCE_RANDOM - MIN_DISTANCE_LAST_KNOWN_NO_RANDOMIZATION;
减法除法,没加括号!小学生级别的错误!
这段的设计意图是:当敌人位置离“最后已知位置”近时,判断敌人新的位置更加准确。当然,根本没生效。
小翻两份代码找到三个bug,不敢想全部代码里有多少虫
改完所有bug,我终于发现了ai对声音处理的逻辑:
private float getBaseDispersion(float enemyDistance, SAINSoundType soundType)
{
// ...
return enemyDistance / dispersionValue;
}
当你朝天放空枪,或者开枪打中ai附近,
ai会直接获取你的准确位置+一个线性增长的误差(简直是脱裤子放屁)。
这个误差小的嘛,100m大概误差10m吧,难绷。
于是我改成了以下逻辑:
0-50m:保持线性,听觉定位相对准确。 50-100m:曲线从线性过渡到二次方增长,误差开始膨胀。 >100m:直接返回一个极大的误差,再传递给 finalDispersion, 最终触发远距离的 Mathf.Max(finalDispersion, 90f) 逻辑,保证远距离极差的声音定位。
到这儿,声音的部分算是改完了。
但是还没完,声音决定了ai“往哪看”,视觉决定了ai“用什么看”。
我找到了VisionPatches.cs,ai的视觉倒是相当复杂:
综合了天气、光线、姿势、暴露程度、是否有掩体……等等因素,算出来一个发现因子。
然而这个发现因子只与你被“看见”的时间有关!
什么意思呢,就是说假如你在200m外只露出头皮,
只要ai恰好看向你这边,盯一会儿,就绝对能看到你(什么超绝静态视力)!
我直接把发现因子这部分改成了发现概率,即发现因子越小,你被发现的概率越低,发现因子是与距离相关的。
测试了两把,中近距离ai的枪法依然犀利,但它们终于无法发现200m外架狙的我了。
至此,我终于能够在塔科夫中享受成为一位臭狙仔的快乐了。
————昨天晚上的我是这么想的,然后我把代码打包发上论坛,然后又开了一把森林。
这次我没有瞄头,200m外的ai被我的大狙抽得不知所措,爽。
然而,我发现它周围扫视一圈后,突然精准地锁定了我的方位,然后,
又在200m外和我对起枪来。
还有高手!
什么千里眼葫芦娃?!
我没改干净?!
于是我火速上论坛把帖子删了,又是一通找,在一个旮沓里发现了SAINSteeringClass.cs里的两个方法:
private void LookToUnderFirePos() private void LookToLastHitPos()
当ai被压制或者被击中时,它会直接尝试看向开火敌人的最后已知位置。
如果找不到敌人,它会立刻精确地转向子弹来袭的方向(UnderFireFromPosition)。无论距离多远!
这诗人啊?!
没办法,加一个距离判断,同样的80m。
现在玩家在远距离成功命中ai,ai将无法立即精确判断子弹来源,其反应会从“锁头级”降级为“迷茫级”。
然后再回到VisionPatches.cs,引入一个概率判定开关:
50米以内:100% 概率通过,行为与原代码完全相同。 50-80米:概率从 100% 线性衰减。
越远,你被ai“看到”的概率就越低。这很合理,实测:如果不开镜,80m外的敌人比蚂蚁都要小了。
现在如果你在80m外击中敌人,他不会再尝试用机瞄与你对枪,而是会逃跑或直接寻找掩体。
这很合理,反正我是没见过两个人隔着80m(大概一个半工厂的长度)对枪的。无息对八倍,不跑干什么?
好的,那我终于改完了……吗?
不对。那狙击ai不就废了?拿着狙击枪的aipmc不也废了?
然后我又做了一系列漫长的修改………………
尝试加入倍镜判断………………重构音效和视觉逻辑………………
又花了一天,终于结束了。
综上所述,我对Sain模组改动了什么?
1.修复近距离震聋效果,枪声有可能导致ai短暂的听力削弱; 2.ai终于会对非枪声做出反应(尽情大叫吧,如果你开了动态地图,你就会发现全场目光向你看齐); 3.ai终于会通过记忆和枪声准确判断近距离的你的位置; 4.如果ai手中无倍镜,再也无法通过蜘蛛感应发现隐蔽在远方草丛中的你,它会四处张望、找掩体或逃跑; 5.如果ai手中有倍镜,它也无法立刻通过蜘蛛感应转向你开火的方向, 它会四处张望、找掩体或逃跑。但!它四处张望的时候,有可能通过手中的倍镜发现你!
如果ai有倍镜,那么他有资格和我对枪!
又测试了一把,我对现在的效果很满意。
以上。
感谢收看,欢迎测试。
附上我修改的代码和编译完毕后的dll,在Build文件夹里,直接替换原版Sain即可。有兴趣的也可以改动我的编码再编译。
折腾了两天,记录一下这个过程。










好帅的改
NB
好改,建议去给他们comment一下,尤其是错误部分
为什么会吃评论?
怎么用 小白不清楚!
求说明!!!
这么强?!