本帖最后由 路过的罗格 于 2024-3-6 15:58 编辑
刨游戏源码的时候看到了一个更好的设置多重箭生成角度的方法
原来我这边直接用的agent.LookDirection来获取一个vec3类型的方向,不过这个方向实际使用起来多数时间没什么问题,但生产多重箭这种需要额外旋转角度的东西就很蛋疼。
现在看到可以用Mat3类型的数据解决这个问题。mat3类里有更好的横向和纵向旋转的方法,然后最后使用mat3.f来获取一个vec3值用作生产箭矢的角度
- Random random = new Random();
- for (int i = num; i > 0; i--)
- {
- float M = random.NextFloat();
- int N = random.Next(-4, 2);
- //每次循环的时候,重新取一遍本身射击的角度,去做角度转换
- Mat3 mat3 = agent.LookRotation;
- float radians = (M + N) * (3.1415f / 180.0f); // 角度转换为弧度
- mat3.RotateAboutUp(radians);
- M = random.NextFloat();
- N = random.Next(-4, 2);
- radians = (M + N) * (3.1415f / 180.0f); // 角度转换为弧度
- mat3.RotateAboutSide(radians);
- int index = WoW_Scripts.FireProjectileFromAgentWithWeaponAtPosition(agent, agent.Equipment[mainHandIndex], mainHandEquipmentElement, headPosition, mat3.f, baseSpeed);
- }
复制代码
|
好的,开始我们的addMissile的进阶部分。 原版的AddCustomMissile方法,只传递了投射物的伤害用作后续的伤害计算,咱们现在干的事就是把武器伤害也给他带上。并且呢,额外的把咱们生成的投射物的index都记录下来,这样可以更大程度上的控制我们生成的这些投射物。 首先在我们自己创建的继承了MissionLogic的类里,完成一个链表和一个字典,并且设定为静态的,方便以后调用。
- //WoW_MissileIndex存放新添加的投射物index
- //WoW_WeaponMissile存放投射物index和射击武器伤害值的键值对,可以根据投射物index查找到当时射击时武器的伤害值
- public static List<int> WoW_MissileIndex = new List<int>();
- public static Dictionary<int, int> WoW_WeaponMissile = new Dictionary<int, int>();
复制代码然后处理一下这些链表和字典的维护。比如开启战场和离开战场时,清掉这些自己创建的东西,避免报错。还有就是在投射物撞击到战场上物体消失时,也把这个投射物从我们的链表/字典里移除。 - public override void OnAfterMissionCreated()
- {
- base.OnAfterMissionCreated();
- WoW_Agents.Clear();
- WoW_MissileIndex.Clear();
- WoW_WeaponMissile.Clear();
- WoW_SmartMisslie.Clear();
- }
- protected override void OnEndMission()
- {
- base.OnEndMission();
- WoW_Agents.Clear();
- WoW_MissileIndex.Clear();
- WoW_WeaponMissile.Clear();
- WoW_SmartMisslie.Clear();
- }
- public override void OnMissileHit(Agent attacker, Agent victim, bool isCanceled, AttackCollisionData collisionData)
- {
- base.OnMissileHit(attacker, victim, isCanceled, collisionData);
- if (WoW_MissileIndex.Contains(collisionData.AffectorWeaponSlotOrMissileIndex))
- {
- WoW_MissileIndex.Remove(collisionData.AffectorWeaponSlotOrMissileIndex);
- WoW_WeaponMissile.Remove(collisionData.AffectorWeaponSlotOrMissileIndex);
- WoW_SmartMisslie.Remove(collisionData.AffectorWeaponSlotOrMissileIndex);
- }
- }
复制代码(题外话,在OnAgentShootMissile时,也会传递一个forcedMissileIndex,如果想控制agent正常射击生成的投射物,可以在OnAgentShootMissile时把forcedMissileIndex记录下来。) 然后是给链表/字典里添加数据。我这边直接封装好了一个函数,可以用来代替原来的AddCustomMissile。 说一下函数的功能以及使用方法: 函数需要传进来的参数和一代的addmissile基本一致,分别是:谁,用什么武器,射击出什么投射物,在什么位置,以什么角度,什么速度射击。其中“用什么武器(ShotWeapon)”可以随便填MissionWeapon ,近战远程投掷都无所谓,但是“射击出什么投射物(AmmoWeapon)”必须是一个投射物,否则报错。 不过呢额外的整了点活,射击角度这里可以填入一个目标pos,如果传进来的武器能射击到这个pos,会自动计算一个射击角度,从StartPos射击到EndPos。如果不能射击到,一会的返回值会输出一个0,方便后续代码处理。 函数会传递出一个int值,是这个函数生成的投射物的index,如果需要做后续的控制,可以拿着这个index继续写代码。 然后是函数详细功能。首先是根据传递进来的ShotWeapon获取AddCustomMissile需要的missile的speed属性,如果传递进来一个近战武器,则固定使用30的速度,差不多是投矛的弹速。接着初始化一个index值,然后遍历一遍已有的Missiles,确保index不会重复。确认后,把index添加到WoW_MissileIndex里。同时把传递进来的武器的伤害值,以键值对的形式添加在WoW_WeaponMissile里,以后可以通过对应的index来获取到当时对应的射击武器伤害值。处理完这些后,就是生成投射物了,这里利用一下pos和rot的特性,判定传递进来的StartDirOrEndPos是一个角度还是地点,如果是角度,直接去生成投射物;如果是地点,走一下弹道计算的代码后,再生成投射物。最后返回一下投射物的index,如果返回了0,则说明自动瞄准无法计算出有效的弹道。 - public static int FireProjectileFromAgentWithWeaponAtPosition(Agent shotAgent, MissionWeapon ShotWeapon, MissionWeapon AmmoWeapon, Vec3 StartPos, Vec3 StartDirOrEndPos, float baseSpeed = -1)
- {
- //需要设定一个缺省值,避免传入的物品是近战武器,从而无法获取弹药速度
- if (ShotWeapon.CurrentUsageItem.IsRangedWeapon && baseSpeed == -1)
- {
- baseSpeed = (float)ShotWeapon.GetModifiedMissileSpeedForCurrentUsage();
- }
- else if (!ShotWeapon.CurrentUsageItem.IsRangedWeapon && baseSpeed == -1)
- {
- baseSpeed = 30;
- }
- int index = 100;
- foreach (var missile in shotAgent.Mission.Missiles)
- {
- //if (missile.Index == index || WoW_MissionSetting.WoW_MissileIndex.Contains(index) || WoW_MissionSetting.WoW_WeaponMissile.ContainsKey(index))
- //{
- // //index++;//这边写的有点问题,应该至少再判定一下index+1后,是否在链表/字典里。因为不一定先射出的投射物先消失,所以这里直接这样写会index冲突。
- //}
- index = Math.Max(missile.Index, index);//干脆直接这样,index只增不减,反正只是一个数,不影响计算开销
- }
- index++;
- WoW_MissionSetting.WoW_MissileIndex.Add(index);
- WoW_MissionSetting.WoW_WeaponMissile.Add(index, ShotWeapon.GetModifiedMissileDamageForCurrentUsage());
- //需要处理StartDirOrEndPos是dir还是pos。dir的长度会是1
- if (Math.Round(StartDirOrEndPos.Length) == 1)
- {
- Mission.Current.AddCustomMissile(shotAgent, AmmoWeapon, StartPos, StartDirOrEndPos, shotAgent.LookRotation, baseSpeed, baseSpeed, true, null, index);
- }
- else
- {
- Vec3 shotDir = CalculateProjectileFiringSolution(StartPos, StartDirOrEndPos, baseSpeed, 9.81f);
- if (shotDir==Vec3.Invalid)
- {
- WoW_MissionSetting.WoW_MissileIndex.Remove(index);
- WoW_MissionSetting.WoW_WeaponMissile.Remove(index);
- return 0;
- }
- Mission.Current.AddCustomMissile(shotAgent, AmmoWeapon, StartPos, shotDir, shotAgent.LookRotation, baseSpeed, baseSpeed, true, null, index);
- }
- return index;
- }
复制代码- public static Vec3 CalculateProjectileFiringSolution(Vec3 start, Vec3 end, float speed, float gravity)//pos射击pos,弹道计算,输出射击角度。凑合
- {
- // 计算水平距离
- Vec2 horizontalDistance = new Vec2(end.x - start.x, end.y - start.y);
- float horizontalRange = horizontalDistance.Length;
- // 计算垂直距离,即高度差
- float verticalDistance = end.z - start.z;
- // 计算发射角度的可能解
- float speedSquared = speed * speed;
- float sqrtTerm = speedSquared * speedSquared - gravity * (gravity * horizontalRange * horizontalRange + 2 * verticalDistance * speedSquared);
- // 如果这个术语小于0,则没有实际的解决方案,因为速度不够以克服重力
- if (sqrtTerm < 0.0f)
- {
- // throw new InvalidOperationException("No valid firing solution for given parameters.");
- return Vec3.Invalid;
- }
- float sqrtValue = (float)Math.Sqrt(sqrtTerm);
- // 取两个可能的解中较小的一个(较高的一个将是较大的发射角)
- float angle = (float)Math.Atan2(speedSquared - sqrtValue, gravity * horizontalRange);
- // 将发射角转换为方向向量
- Vec3 firingSolution = new Vec3(horizontalDistance.x, horizontalDistance.y, 0);
- firingSolution.Normalize();
- firingSolution *= (float)Math.Cos(angle) * speed; // 水平速度分量
- firingSolution.z = (float)Math.Sin(angle) * speed; // 垂直速度分量
- firingSolution.Normalize();
- return firingSolution;
- }
复制代码现在已经处理好了伤害数据的记录,接下来就是调整伤害计算的代码,让他可以把我们记录的武器伤害值正常的运用到计算过程中。伤害计算流程可以参考这个https://docs.qq.com/doc/DVGdkVVZTUkV3ZHhy?u=9533b05542c1477e84497ae00babe585。 创建两个类,分别继承SandboxStrikeMagnitudeModel和DefaultStrikeMagnitudeModel,一个用来处理沙盒游戏模式的,一个用来处理默认模式的(比如自定义快速战斗),代码完全一致。这边完成的功能,可以参考https://bbs.mountblade.com.cn/thread-2102995-1-1.html里面对AddCustomMissile伤害计算错误的分析,现在就是有了正确的数值后,把错误的数值改为正确的数值。 - public class WOW_SandboxStrikeMagnitudeModel : SandboxStrikeMagnitudeModel
- {
- public override float CalculateStrikeMagnitudeForMissile(in AttackInformation attackInformation, in AttackCollisionData collisionData, in MissionWeapon weapon, float missileSpeed)
- {
- WoW_Scripts woW_Scripts = new WoW_Scripts();
- 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+字典里武器的伤害
- {
- int weaponDamage;
- WoW_MissionSetting.WoW_WeaponMissile.TryGetValue(collisionData.AffectorWeaponSlotOrMissileIndex, out weaponDamage);
- float baseDam = base.CalculateStrikeMagnitudeForMissile(attackInformation, collisionData, weapon, missileSpeed);
- float mtd = collisionData.MissileTotalDamage;
- if (baseDam == 0) baseDam = 1;
- if (mtd == 0) mtd = 1;
- if (weaponDamage == 0) weaponDamage = (int)mtd;
- return baseDam / mtd * (weaponDamage + collisionData.MissileTotalDamage);
- }
- else if (weapon.CurrentUsageItem.IsConsumable && weapon.CurrentUsageItem.IsRangedWeapon)//对于自己生成的投掷类武器因为mtd值正确,所以不用weaponDamage+mtd。为了兼容非远程武器生成的投矛,所以用这个公式
- {
- int weaponDamage;
- WoW_MissionSetting.WoW_WeaponMissile.TryGetValue(collisionData.AffectorWeaponSlotOrMissileIndex, out weaponDamage);
- float baseDam = base.CalculateStrikeMagnitudeForMissile(attackInformation, collisionData, weapon, missileSpeed);
- float mtd = collisionData.MissileTotalDamage;
- if (baseDam == 0) baseDam = 1;
- if (mtd == 0) mtd = 1;
- if (weaponDamage == 0) weaponDamage = (int)mtd;
- return baseDam / mtd * (weaponDamage);
- }
- return base.CalculateStrikeMagnitudeForMissile(attackInformation, collisionData, weapon, missileSpeed);//如果字典里没有,说明是游戏源代码生成 的,所以直接输出原来acd里的mtd
- }
- }
- public class WOW_DefaultStrikeMagnitudeModel : DefaultStrikeMagnitudeModel
- {
- public override float CalculateStrikeMagnitudeForMissile(in AttackInformation attackInformation, in AttackCollisionData collisionData, in MissionWeapon weapon, float missileSpeed)
- {
- WoW_Scripts woW_Scripts = new WoW_Scripts();
- 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+字典里武器的伤害
- {
- int weaponDamage;
- WoW_MissionSetting.WoW_WeaponMissile.TryGetValue(collisionData.AffectorWeaponSlotOrMissileIndex, out weaponDamage);
- float baseDam = base.CalculateStrikeMagnitudeForMissile(attackInformation, collisionData, weapon, missileSpeed);
- float mtd = collisionData.MissileTotalDamage;
- if (baseDam == 0) baseDam = 1;
- if (mtd == 0) mtd = 1;
- return baseDam / mtd * (weaponDamage + collisionData.MissileTotalDamage);
- }
- else if (weapon.CurrentUsageItem.IsConsumable && weapon.CurrentUsageItem.IsRangedWeapon)//对于自己生成的投掷类武器因为mtd值正确,所以不用weaponDamage+mtd。为了兼容非远程武器生成的投矛,所以用这个公式
- {
- int weaponDamage;
- WoW_MissionSetting.WoW_WeaponMissile.TryGetValue(collisionData.AffectorWeaponSlotOrMissileIndex, out weaponDamage);
- float baseDam = base.CalculateStrikeMagnitudeForMissile(attackInformation, collisionData, weapon, missileSpeed);
- float mtd = collisionData.MissileTotalDamage;
- if (baseDam == 0) baseDam = 1;
- if (mtd == 0) mtd = 1;
- return baseDam / mtd * (weaponDamage);
- }
- return base.CalculateStrikeMagnitudeForMissile(attackInformation, collisionData, weapon, missileSpeed);//如果字典里没有,说明是游戏源代码生成 的,所以直接输出原来acd里的mtd
- }
- }
复制代码
完成了这些代码后,需要在继承了MBSubModuleBase的类里,告诉引擎去调用这些代码 - protected override void InitializeGameStarter(Game game, IGameStarter gameStarterObject)
- {
- //默认使用的代码
- gameStarterObject.AddModel(new WOW_DefaultStrikeMagnitudeModel());
- gameStarterObject.AddModel(new WOW_CustomBattleAgentStatCalculateModel());
- if (game.GameType is Campaign)
- {
- //战役里使用的代码
- gameStarterObject.AddModel(new WOW_SandboxStrikeMagnitudeModel());
- gameStarterObject.AddModel(new WOW_SandboxAgentStatCalculateModel());
- }
- }
复制代码至此,功能就已经完全完成了,我们可以通过FireProjectileFromAgentWithWeaponAtPosition函数,生成一个伤害正常的自定义投射物,并且还可以获得到他的index,可以完成更多的操作,比如给他挂点粒子效果。
因为简单看了一遍api里的方法函数后,找不着1代里的百分比增减伤,所以就自己写吧,也就是上面没有删掉的 WOW_CustomBattleAgentStatCalculateModel、WOW_SandboxAgentStatCalculateModel。- public class WOW_CustomBattleAgentStatCalculateModel : CustomBattleAgentStatCalculateModel
- {
- public override float GetWeaponDamageMultiplier(Agent agent, WeaponComponentData weapon)
- {
- float native = base.GetWeaponDamageMultiplier(agent, weapon);
- native = WoW_Scripts.WOW_Script_AgentStatCalculateModel(agent, native);
- return native;
- //接下来的代码会乘等这个函数的输出值,所以这个函数的数值1==100%
- //float weaponDamageMultiplier = MissionGameModels.Current.AgentStatCalculateModel.GetWeaponDamageMultiplier(attackInformation.AttackerAgent, currentUsageItem2);
- //baseMagnitude *= weaponDamageMultiplier;
- }
- }
- public class WOW_SandboxAgentStatCalculateModel : SandboxAgentStatCalculateModel
- {
- public override float GetWeaponDamageMultiplier(Agent agent, WeaponComponentData weapon)
- {
- float native = base.GetWeaponDamageMultiplier(agent, weapon);
- native = WoW_Scripts.WOW_Script_AgentStatCalculateModel(agent, native);
- return native;
- //接下来的代码会乘等这个函数的输出值,所以这个函数的数值1==100%
- //float weaponDamageMultiplier = MissionGameModels.Current.AgentStatCalculateModel.GetWeaponDamageMultiplier(attackInformation.AttackerAgent, currentUsageItem2);
- //baseMagnitude *= weaponDamageMultiplier;
- }
- }
- public static float WOW_Script_AgentStatCalculateModel(Agent agent, float native)
- {//AgentStatCalculateModel输入agent后,根据agent的状态,附加额外的增伤数值
- if (WoW_MissionSetting.WoW_Agents.TryGetValue(agent.Index, out var WOW_agent))
- {
- if (WOW_agent.passiveSkill == "nuyikuangji")
- {
- float time = WOW_agent.passiveSkillDur;
- native = native + time * 0.5f;
- time += 5;
- WOW_agent.passiveSkillDur = time;
- InformationManager.DisplayMessage(new InformationMessage("nuyikuangji"));
- }
- }
- return native;
- }
复制代码
|
|