骑马与砍杀中文站论坛

 找回密码
 注册(Register!)

QQ登录

只需一步,快速开始

搜索
购买CDKEY 小黑盒加速器
查看: 705|回复: 1

[功能与代码] 【骑砍2】实现真正的Add添加Custom自定义Missile投射物

[复制链接]

28

主题

219

回帖

179

积分

见习骑士

Rank: 3

UID
2758789
第纳尔
2118
精华
0
互助
19
荣誉
1
贡献
0
魅力
171
注册时间
2016-7-18
鲜花(18) 鸡蛋(0)
发表于 2024-2-21 15:12:18 | 显示全部楼层 |阅读模式
本帖最后由 路过的罗格 于 2024-3-6 15:58 编辑

(点击展开 / 收起)


好的,开始我们的addMissile的进阶部分。
原版的AddCustomMissile方法,只传递了投射物的伤害用作后续的伤害计算,咱们现在干的事就是把武器伤害也给他带上。并且呢,额外的把咱们生成的投射物的index都记录下来,这样可以更大程度上的控制我们生成的这些投射物。
首先在我们自己创建的继承了MissionLogic的类里,完成一个链表和一个字典,并且设定为静态的,方便以后调用。

  1.         //WoW_MissileIndex存放新添加的投射物index
  2.         //WoW_WeaponMissile存放投射物index和射击武器伤害值的键值对,可以根据投射物index查找到当时射击时武器的伤害值
  3.         public static List<int> WoW_MissileIndex = new List<int>();
  4.         public static Dictionary<int, int> WoW_WeaponMissile = new Dictionary<int, int>();
复制代码
然后处理一下这些链表和字典的维护。比如开启战场和离开战场时,清掉这些自己创建的东西,避免报错。还有就是在投射物撞击到战场上物体消失时,也把这个投射物从我们的链表/字典里移除。
  1.         public override void OnAfterMissionCreated()
  2.         {
  3.             base.OnAfterMissionCreated();
  4.             WoW_Agents.Clear();
  5.             WoW_MissileIndex.Clear();
  6.             WoW_WeaponMissile.Clear();
  7.             WoW_SmartMisslie.Clear();
  8.         }
  9.         protected override void OnEndMission()
  10.         {
  11.             base.OnEndMission();
  12.             WoW_Agents.Clear();
  13.             WoW_MissileIndex.Clear();
  14.             WoW_WeaponMissile.Clear();
  15.             WoW_SmartMisslie.Clear();
  16.         }
  17.         public override void OnMissileHit(Agent attacker, Agent victim, bool isCanceled, AttackCollisionData collisionData)
  18.         {
  19.             base.OnMissileHit(attacker, victim, isCanceled, collisionData);
  20.             if (WoW_MissileIndex.Contains(collisionData.AffectorWeaponSlotOrMissileIndex))
  21.             {
  22.                 WoW_MissileIndex.Remove(collisionData.AffectorWeaponSlotOrMissileIndex);
  23.                 WoW_WeaponMissile.Remove(collisionData.AffectorWeaponSlotOrMissileIndex);
  24.                 WoW_SmartMisslie.Remove(collisionData.AffectorWeaponSlotOrMissileIndex);

  25.             }
  26.         }
复制代码
(题外话,在OnAgentShootMissile时,也会传递一个forcedMissileIndex,如果想控制agent正常射击生成的投射物,可以在OnAgentShootMissile时把forcedMissileIndex记录下来。)
然后是给链表/字典里添加数据。我这边直接封装好了一个函数,可以用来代替原来的AddCustomMissile
说一下函数的功能以及使用方法:
函数需要传进来的参数和一代的addmissile基本一致,分别是:谁,用什么武器,射击出什么投射物,在什么位置,以什么角度,什么速度射击。其中“用什么武器(ShotWeapon)”可以随便填MissionWeapon ,近战远程投掷都无所谓,但是“射击出什么投射物(AmmoWeapon)”必须是一个投射物,否则报错。
不过呢额外的整了点活,射击角度这里可以填入一个目标pos,如果传进来的武器能射击到这个pos,会自动计算一个射击角度,从StartPos射击到EndPos。如果不能射击到,一会的返回值会输出一个0,方便后续代码处理。
函数会传递出一个int值,是这个函数生成的投射物的index,如果需要做后续的控制,可以拿着这个index继续写代码。
然后是函数详细功能。首先是根据传递进来的ShotWeapon获取AddCustomMissile需要的missilespeed属性,如果传递进来一个近战武器,则固定使用30的速度,差不多是投矛的弹速。接着初始化一个index值,然后遍历一遍已有的Missiles,确保index不会重复。确认后,把index添加到WoW_MissileIndex里。同时把传递进来的武器的伤害值,以键值对的形式添加在WoW_WeaponMissile里,以后可以通过对应的index来获取到当时对应的射击武器伤害值。处理完这些后,就是生成投射物了,这里利用一下posrot的特性,判定传递进来的StartDirOrEndPos是一个角度还是地点,如果是角度,直接去生成投射物;如果是地点,走一下弹道计算的代码后,再生成投射物。最后返回一下投射物的index,如果返回了0,则说明自动瞄准无法计算出有效的弹道。
  1.         public static int FireProjectileFromAgentWithWeaponAtPosition(Agent shotAgent, MissionWeapon ShotWeapon, MissionWeapon AmmoWeapon, Vec3 StartPos, Vec3 StartDirOrEndPos, float baseSpeed = -1)
  2.         {

  3.             //需要设定一个缺省值,避免传入的物品是近战武器,从而无法获取弹药速度

  4.             if (ShotWeapon.CurrentUsageItem.IsRangedWeapon && baseSpeed == -1)
  5.             {
  6.                 baseSpeed = (float)ShotWeapon.GetModifiedMissileSpeedForCurrentUsage();
  7.             }
  8.             else if (!ShotWeapon.CurrentUsageItem.IsRangedWeapon && baseSpeed == -1)
  9.             {
  10.                 baseSpeed = 30;
  11.             }


  12.             int index = 100;

  13.             foreach (var missile in shotAgent.Mission.Missiles)
  14.             {
  15.                 //if (missile.Index == index || WoW_MissionSetting.WoW_MissileIndex.Contains(index) || WoW_MissionSetting.WoW_WeaponMissile.ContainsKey(index))
  16.                 //{
  17.                 //    //index++;//这边写的有点问题,应该至少再判定一下index+1后,是否在链表/字典里。因为不一定先射出的投射物先消失,所以这里直接这样写会index冲突。
  18.                 //}
  19.                 index = Math.Max(missile.Index, index);//干脆直接这样,index只增不减,反正只是一个数,不影响计算开销
  20.             }
  21.             index++;




  22.             WoW_MissionSetting.WoW_MissileIndex.Add(index);
  23.             WoW_MissionSetting.WoW_WeaponMissile.Add(index, ShotWeapon.GetModifiedMissileDamageForCurrentUsage());
  24.             //需要处理StartDirOrEndPos是dir还是pos。dir的长度会是1
  25.             if (Math.Round(StartDirOrEndPos.Length) == 1)
  26.             {
  27.                 Mission.Current.AddCustomMissile(shotAgent, AmmoWeapon, StartPos, StartDirOrEndPos, shotAgent.LookRotation, baseSpeed, baseSpeed, true, null, index);
  28.             }
  29.             else
  30.             {

  31.                 Vec3 shotDir = CalculateProjectileFiringSolution(StartPos, StartDirOrEndPos, baseSpeed, 9.81f);
  32.                 if (shotDir==Vec3.Invalid)
  33.                 {
  34.                     WoW_MissionSetting.WoW_MissileIndex.Remove(index);
  35.                     WoW_MissionSetting.WoW_WeaponMissile.Remove(index);
  36.                     return 0;
  37.                 }
  38.                 Mission.Current.AddCustomMissile(shotAgent, AmmoWeapon, StartPos, shotDir, shotAgent.LookRotation, baseSpeed, baseSpeed, true, null, index);
  39.             }

  40.             return index;
  41.         }
复制代码
  1.         public static Vec3 CalculateProjectileFiringSolution(Vec3 start, Vec3 end, float speed, float gravity)//pos射击pos,弹道计算,输出射击角度。凑合
  2.         {
  3.             // 计算水平距离
  4.             Vec2 horizontalDistance = new Vec2(end.x - start.x, end.y - start.y);
  5.             float horizontalRange = horizontalDistance.Length;

  6.             // 计算垂直距离,即高度差
  7.             float verticalDistance = end.z - start.z;

  8.             // 计算发射角度的可能解
  9.             float speedSquared = speed * speed;
  10.             float sqrtTerm = speedSquared * speedSquared - gravity * (gravity * horizontalRange * horizontalRange + 2 * verticalDistance * speedSquared);

  11.             // 如果这个术语小于0,则没有实际的解决方案,因为速度不够以克服重力
  12.             if (sqrtTerm < 0.0f)
  13.             {
  14.                 // throw new InvalidOperationException("No valid firing solution for given parameters.");
  15.                 return Vec3.Invalid;
  16.             }

  17.             float sqrtValue = (float)Math.Sqrt(sqrtTerm);

  18.             // 取两个可能的解中较小的一个(较高的一个将是较大的发射角)
  19.             float angle = (float)Math.Atan2(speedSquared - sqrtValue, gravity * horizontalRange);

  20.             // 将发射角转换为方向向量
  21.             Vec3 firingSolution = new Vec3(horizontalDistance.x, horizontalDistance.y, 0);
  22.             firingSolution.Normalize();
  23.             firingSolution *= (float)Math.Cos(angle) * speed;   // 水平速度分量
  24.             firingSolution.z = (float)Math.Sin(angle) * speed;  // 垂直速度分量
  25.             firingSolution.Normalize();
  26.             return firingSolution;
  27.         }
复制代码
现在已经处理好了伤害数据的记录,接下来就是调整伤害计算的代码,让他可以把我们记录的武器伤害值正常的运用到计算过程中。伤害计算流程可以参考这个https://docs.qq.com/doc/DVGdkVVZTUkV3ZHhy?u=9533b05542c1477e84497ae00babe585
创建两个类,分别继承SandboxStrikeMagnitudeModelDefaultStrikeMagnitudeModel,一个用来处理沙盒游戏模式的,一个用来处理默认模式的(比如自定义快速战斗),代码完全一致。这边完成的功能,可以参考https://bbs.mountblade.com.cn/thread-2102995-1-1.html里面对AddCustomMissile伤害计算错误的分析,现在就是有了正确的数值后,把错误的数值改为正确的数值。
  1.     public class WOW_SandboxStrikeMagnitudeModel : SandboxStrikeMagnitudeModel
  2.     {
  3.         public override float CalculateStrikeMagnitudeForMissile(in AttackInformation attackInformation, in AttackCollisionData collisionData, in MissionWeapon weapon, float missileSpeed)
  4.         {
  5.             WoW_Scripts woW_Scripts = new WoW_Scripts();
  6.             if (WoW_MissionSetting.WoW_WeaponMissile.ContainsKey(collisionData.AffectorWeaponSlotOrMissileIndex) && (weapon.Item.PrimaryWeapon.WeaponClass == WeaponClass.Arrow || weapon.Item.PrimaryWeapon.WeaponClass == WeaponClass.Bolt))// WoW_MissionSetting.WoW_WeaponMissile字典里有acd中的missile。index,则说明这个箭矢是我代码生成的,所以return出去的伤害数值=mtd+字典里武器的伤害
  7.             {
  8.                 int weaponDamage;
  9.                 WoW_MissionSetting.WoW_WeaponMissile.TryGetValue(collisionData.AffectorWeaponSlotOrMissileIndex, out weaponDamage);
  10.                 float baseDam = base.CalculateStrikeMagnitudeForMissile(attackInformation, collisionData, weapon, missileSpeed);
  11.                 float mtd = collisionData.MissileTotalDamage;
  12.                 if (baseDam == 0) baseDam = 1;
  13.                 if (mtd == 0) mtd = 1;
  14.                 if (weaponDamage == 0) weaponDamage = (int)mtd;
  15.                 return baseDam / mtd * (weaponDamage + collisionData.MissileTotalDamage);
  16.             }
  17.             else if (weapon.CurrentUsageItem.IsConsumable && weapon.CurrentUsageItem.IsRangedWeapon)//对于自己生成的投掷类武器因为mtd值正确,所以不用weaponDamage+mtd。为了兼容非远程武器生成的投矛,所以用这个公式
  18.             {
  19.                 int weaponDamage;
  20.                 WoW_MissionSetting.WoW_WeaponMissile.TryGetValue(collisionData.AffectorWeaponSlotOrMissileIndex, out weaponDamage);
  21.                 float baseDam = base.CalculateStrikeMagnitudeForMissile(attackInformation, collisionData, weapon, missileSpeed);
  22.                 float mtd = collisionData.MissileTotalDamage;
  23.                 if (baseDam == 0) baseDam = 1;
  24.                 if (mtd == 0) mtd = 1;
  25.                 if (weaponDamage == 0) weaponDamage = (int)mtd;
  26.                 return baseDam / mtd * (weaponDamage);

  27.             }

  28.             return base.CalculateStrikeMagnitudeForMissile(attackInformation, collisionData, weapon, missileSpeed);//如果字典里没有,说明是游戏源代码生成 的,所以直接输出原来acd里的mtd

  29.         }
  30.     }
  31.     public class WOW_DefaultStrikeMagnitudeModel : DefaultStrikeMagnitudeModel
  32.     {
  33.         public override float CalculateStrikeMagnitudeForMissile(in AttackInformation attackInformation, in AttackCollisionData collisionData, in MissionWeapon weapon, float missileSpeed)
  34.         {
  35.             WoW_Scripts woW_Scripts = new WoW_Scripts();
  36.             if (WoW_MissionSetting.WoW_WeaponMissile.ContainsKey(collisionData.AffectorWeaponSlotOrMissileIndex) && (weapon.Item.PrimaryWeapon.WeaponClass == WeaponClass.Arrow || weapon.Item.PrimaryWeapon.WeaponClass == WeaponClass.Bolt))// WoW_MissionSetting.WoW_WeaponMissile字典里有acd中的missile。index,则说明这个箭矢是我代码生成的,所以return出去的伤害数值=mtd+字典里武器的伤害
  37.             {
  38.                 int weaponDamage;
  39.                 WoW_MissionSetting.WoW_WeaponMissile.TryGetValue(collisionData.AffectorWeaponSlotOrMissileIndex, out weaponDamage);
  40.                 float baseDam = base.CalculateStrikeMagnitudeForMissile(attackInformation, collisionData, weapon, missileSpeed);
  41.                 float mtd = collisionData.MissileTotalDamage;
  42.                 if (baseDam == 0) baseDam = 1;
  43.                 if (mtd == 0) mtd = 1;
  44.                 return baseDam / mtd * (weaponDamage + collisionData.MissileTotalDamage);
  45.             }
  46.             else if (weapon.CurrentUsageItem.IsConsumable && weapon.CurrentUsageItem.IsRangedWeapon)//对于自己生成的投掷类武器因为mtd值正确,所以不用weaponDamage+mtd。为了兼容非远程武器生成的投矛,所以用这个公式
  47.             {
  48.                 int weaponDamage;
  49.                 WoW_MissionSetting.WoW_WeaponMissile.TryGetValue(collisionData.AffectorWeaponSlotOrMissileIndex, out weaponDamage);
  50.                 float baseDam = base.CalculateStrikeMagnitudeForMissile(attackInformation, collisionData, weapon, missileSpeed);
  51.                 float mtd = collisionData.MissileTotalDamage;
  52.                 if (baseDam == 0) baseDam = 1;
  53.                 if (mtd == 0) mtd = 1;
  54.                 return baseDam / mtd * (weaponDamage);

  55.             }

  56.             return base.CalculateStrikeMagnitudeForMissile(attackInformation, collisionData, weapon, missileSpeed);//如果字典里没有,说明是游戏源代码生成 的,所以直接输出原来acd里的mtd

  57.         }
  58.     }
复制代码


完成了这些代码后,需要在继承了MBSubModuleBase的类里,告诉引擎去调用这些代码
  1.         protected override void InitializeGameStarter(Game game, IGameStarter gameStarterObject)
  2.         {            
  3.             //默认使用的代码   
  4.             gameStarterObject.AddModel(new WOW_DefaultStrikeMagnitudeModel());
  5.             gameStarterObject.AddModel(new WOW_CustomBattleAgentStatCalculateModel());
  6.             if (game.GameType is Campaign)
  7.             {
  8.                 //战役里使用的代码
  9.                 gameStarterObject.AddModel(new WOW_SandboxStrikeMagnitudeModel());
  10.                 gameStarterObject.AddModel(new WOW_SandboxAgentStatCalculateModel());
  11.             }
  12.         }
复制代码
至此,功能就已经完全完成了,我们可以通过FireProjectileFromAgentWithWeaponAtPosition函数,生成一个伤害正常的自定义投射物,并且还可以获得到他的index,可以完成更多的操作,比如给他挂点粒子效果。

(点击展开 / 收起)


评分

参与人数 1第纳尔 +20 互助 +2 魅力 +8 收起 理由
幼稚园殺手 + 20 + 2 + 8 您的帖子很有价值!

查看全部评分

鲜花鸡蛋

幼稚园殺手  在2024-2-21 22:48  送朵鲜花  并说:我非常同意你的观点,送朵鲜花鼓励一下

28

主题

4156

回帖

3130

积分

子爵[版主]

世纪风云制作组[程序]

圣殿骑士团[KT]
战团ID:Epig

中级术士

Rank: 7Rank: 7Rank: 7

UID
1706215
第纳尔
34958
精华
3
互助
157
荣誉
79
贡献
2005
魅力
207
注册时间
2013-12-8

骑砍中文站APP会员勋章原版正版勋章战团正版勋章火与剑正版勋章拿破仑正版勋章维京征服正版勋章汉匈决战正版勋章骑士美德之英勇勋章[杰出会员活跃勋章]骑士美德之仁慈勋章[杰出会员互助勋章]骑士美德之谦恭勋章[杰出会员财富勋章]骑士美德之公正勋章[杰出会员高级财富勋章]骑士美德之正义勋章[杰出会员荣誉勋章]骑士精神之文韬勋章杰出版主勋章骑士美德之奉献勋章骑士美德之高贵勋章骑砍中文站微博会员勋章骑砍中文站微信会员勋章骑友真人秀勋章汉匈决战荣誉用户勋章元老骑士勋章霸主正版勋章

鲜花(2039) 鸡蛋(904)
发表于 2024-2-23 17:30:13 | 显示全部楼层
写的很细节,值得学习
童鞋们,欢迎来到骑马与砍杀学院,我是你们的科任老师,猪猪老师,由我来为童鞋们介绍以下课程:
1、人间五十年life50 2.0测试版
2、永恒世界4.5.5公测版
3、永恒世界网页端 UCP2.0
4、大逃杀1.0公测版
5、永恒世界4.5特别版
6、常见PY报错解决方案
您需要登录后才可以回帖 登录 | 注册(Register!)

本版积分规则

Archiver|手机版|小黑屋|骑马与砍杀中文站

GMT+8, 2024-5-1 17:43 , Processed in 0.117577 second(s), 21 queries , Gzip On, MemCached On.

Powered by Discuz! X3.4 Licensed

© 2001-2023 Discuz! Team.

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