//------------------------------------------------------------ // Game Framework // Copyright © 2013-2021 Jiang Yin. All rights reserved. // Homepage: https://gameframework.cn/ // Feedback: mailto:ellan@gameframework.cn //------------------------------------------------------------ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using Definition.DataStruct; using Definition.Enum; using Entity; using Entity.Weapon; using UnityEngine; using UnityGameFramework.Runtime; using Random = UnityEngine.Random; namespace CustomUtility { /// /// AI 工具类。 /// public static class AIUtility { private static Dictionary s_CampPairToRelation = new(); private static Dictionary, CampType[]> s_CampAndRelationToCamps = new(); static AIUtility() { s_CampPairToRelation.Add(new CampPair(CampType.Player, CampType.Player), RelationType.Friendly); s_CampPairToRelation.Add(new CampPair(CampType.Player, CampType.Enemy), RelationType.Hostile); s_CampPairToRelation.Add(new CampPair(CampType.Player, CampType.Neutral), RelationType.Neutral); s_CampPairToRelation.Add(new CampPair(CampType.Player, CampType.Player2), RelationType.Hostile); s_CampPairToRelation.Add(new CampPair(CampType.Player, CampType.Enemy2), RelationType.Hostile); s_CampPairToRelation.Add(new CampPair(CampType.Player, CampType.Neutral2), RelationType.Neutral); s_CampPairToRelation.Add(new CampPair(CampType.Enemy, CampType.Enemy), RelationType.Friendly); s_CampPairToRelation.Add(new CampPair(CampType.Enemy, CampType.Neutral), RelationType.Neutral); s_CampPairToRelation.Add(new CampPair(CampType.Enemy, CampType.Player2), RelationType.Hostile); s_CampPairToRelation.Add(new CampPair(CampType.Enemy, CampType.Enemy2), RelationType.Hostile); s_CampPairToRelation.Add(new CampPair(CampType.Enemy, CampType.Neutral2), RelationType.Neutral); s_CampPairToRelation.Add(new CampPair(CampType.Neutral, CampType.Neutral), RelationType.Neutral); s_CampPairToRelation.Add(new CampPair(CampType.Neutral, CampType.Player2), RelationType.Neutral); s_CampPairToRelation.Add(new CampPair(CampType.Neutral, CampType.Enemy2), RelationType.Neutral); s_CampPairToRelation.Add(new CampPair(CampType.Neutral, CampType.Neutral2), RelationType.Hostile); s_CampPairToRelation.Add(new CampPair(CampType.Player2, CampType.Player2), RelationType.Friendly); s_CampPairToRelation.Add(new CampPair(CampType.Player2, CampType.Enemy2), RelationType.Hostile); s_CampPairToRelation.Add(new CampPair(CampType.Player2, CampType.Neutral2), RelationType.Neutral); s_CampPairToRelation.Add(new CampPair(CampType.Enemy2, CampType.Enemy2), RelationType.Friendly); s_CampPairToRelation.Add(new CampPair(CampType.Enemy2, CampType.Neutral2), RelationType.Neutral); s_CampPairToRelation.Add(new CampPair(CampType.Neutral2, CampType.Neutral2), RelationType.Neutral); } /// /// 获取两个阵营之间的关系。 /// /// 阵营一。 /// 阵营二。 /// 阵营间关系。 public static RelationType GetRelation(CampType first, CampType second) { if (first > second) { (first, second) = (second, first); } RelationType relationType; if (s_CampPairToRelation.TryGetValue(new CampPair(first, second), out relationType)) { return relationType; } Log.Warning("Unknown relation between '{0}' and '{1}'.", first.ToString(), second.ToString()); return RelationType.Unknown; } /// /// 获取和指定具有特定关系的所有阵营。 /// /// 指定阵营。 /// 关系。 /// 满足条件的阵营数组。 public static CampType[] GetCamps(CampType camp, RelationType relation) { KeyValuePair key = new KeyValuePair(camp, relation); if (s_CampAndRelationToCamps.TryGetValue(key, out var result)) { return result; } // TODO: GC Alloc List camps = new List(); Array campTypes = Enum.GetValues(typeof(CampType)); for (int i = 0; i < campTypes.Length; i++) { CampType campType = (CampType)campTypes.GetValue(i); if (GetRelation(camp, campType) == relation) { camps.Add(campType); } } // TODO: GC Alloc result = camps.ToArray(); s_CampAndRelationToCamps[key] = result; return result; } /// /// 获取实体间的距离。 /// /// 实体间的距离。 public static float GetDistance(EntityBase fromEntity, EntityBase toEntity) { Transform fromTransform = fromEntity.CachedTransform; Transform toTransform = toEntity.CachedTransform; return (toTransform.position - fromTransform.position).magnitude; } /// /// 获取实体间的平方距离。 /// /// 实体间的平方距离。 public static float GetSqrMagnitude(EntityBase fromEntity, EntityBase toEntity) { Transform fromTransform = fromEntity.CachedTransform; Transform toTransform = toEntity.CachedTransform; return (toTransform.position - fromTransform.position).sqrMagnitude; } /// /// 获取实体间在 XZ 平面的距离(忽略 Y 轴)。 /// /// 实体间在 XZ 平面的距离。 public static float GetDistanceXZ(EntityBase fromEntity, EntityBase toEntity) { return Mathf.Sqrt(GetSqrMagnitudeXZ(fromEntity, toEntity)); } /// /// 获取实体间在 XZ 平面的平方距离(忽略 Y 轴)。 /// /// 实体间在 XZ 平面的平方距离。 public static float GetSqrMagnitudeXZ(EntityBase fromEntity, EntityBase toEntity) { Transform fromTransform = fromEntity.CachedTransform; Transform toTransform = toEntity.CachedTransform; Vector3 delta = toTransform.position - fromTransform.position; delta.y = 0f; return delta.sqrMagnitude; } public static void PerformCollision(TargetableObject entity, EntityBase other) { PerformCollision(entity, other, false); } public static void PerformCollision(TargetableObject entity, EntityBase other, bool ignoreRuntimeState) { if (entity == null || other == null) { return; } // TargetableObject target = other as TargetableObject; // if (target != null) // { // ImpactData entityImpactData = entity.GetImpactData(); // ImpactData targetImpactData = target.GetImpactData(); // if (GetRelation(entityImpactData.Camp, targetImpactData.Camp) == RelationType.Friendly) // { // return; // } // // int entityDamageHP = CalcDamageHP(targetImpactData.Attack, entityImpactData.Defense); // int targetDamageHP = CalcDamageHP(entityImpactData.Attack, targetImpactData.Defense); // // entity.ApplyDamage(target, entityDamageHP); // target.ApplyDamage(entity, targetDamageHP); // return; // } // Bullet bullet = other as Bullet; // if (bullet != null) // { // ImpactData entityImpactData = entity.GetImpactData(); // ImpactData bulletImpactData = bullet.GetImpactData(); // if (GetRelation(entityImpactData.Camp, bulletImpactData.Camp) == RelationType.Friendly) // { // return; // } // // int entityDamageHP = CalcDamageHP(bulletImpactData.AttackBase, bulletImpactData.AttackStat, // entityImpactData.DefenseStat, entityImpactData.DodgeStat); // // entity.ApplyDamage(bullet, entityDamageHP); // GameEntry.Entity.HideEntity(bullet); // return; // } EnemyProjectile enemyProjectile = other as EnemyProjectile; if (enemyProjectile != null) { if (!ignoreRuntimeState && !enemyProjectile.IsActive) return; ImpactData entityImpactData = entity.GetImpactData(); ImpactData projectileImpactData = enemyProjectile.GetImpactData(); if (GetRelation(entityImpactData.Camp, projectileImpactData.Camp) == RelationType.Friendly) { return; } int entityDamageHP = CalcDamageHP(projectileImpactData.AttackBase, projectileImpactData.AttackStat, entityImpactData.DefenseStat, entityImpactData.DodgeStat); entity.ApplyDamage(enemyProjectile, entityDamageHP); enemyProjectile.Expire(); return; } WeaponBase weapon = other as WeaponBase; if (weapon != null) { if (!ignoreRuntimeState && !weapon.IsAttacking) return; ImpactData entityImpactData = entity.GetImpactData(); ImpactData weaponImpactData = weapon.GetImpactData(); if (GetRelation(entityImpactData.Camp, weaponImpactData.Camp) == RelationType.Friendly) { return; } int entityDamageHP = CalcDamageHP(weaponImpactData.AttackBase, weaponImpactData.AttackStat, entityImpactData.DefenseStat, entityImpactData.DodgeStat); entity.ApplyDamage(weapon, entityDamageHP); return; } } public static int CalcDamageHP(int attack, StatProperty attackStat, StatProperty defenseStat, StatProperty dodgeStat) { // 1. 处理闪避(闪避率取值 (0, 0.9),不允许拉满闪避) if (dodgeStat != null) { if (Random.value < Mathf.Clamp(dodgeStat.Value, 0, 0.9f)) return 0; } // 2. 处理攻击加成 最终伤害 = (基础伤害 + 伤害提升固定值) * 伤害提升率 float damage = attack; if (attackStat != null) { damage = (attack + attackStat.Value) * attackStat.Percent; } // 3. 处理防御加成 最终伤害 = (基础伤害 - 防御提升固定值) / 防御提升率 if (defenseStat != null) { damage = (damage - defenseStat.Value) / defenseStat.Percent; } // 将伤害限制为正整数,避免堆防御导致无法造成伤害 if (damage < 1) return 1; else return Mathf.CeilToInt(damage); } [StructLayout(LayoutKind.Auto)] private struct CampPair { private readonly CampType m_First; private readonly CampType m_Second; public CampPair(CampType first, CampType second) { m_First = first; m_Second = second; } public CampType First => m_First; public CampType Second => m_Second; } } }