Checkpoint 5 & Checkpoint 6
This commit is contained in:
@@ -7,6 +7,7 @@ public abstract class EnemyBase : TargetableObject
|
||||
protected Transform _target;
|
||||
|
||||
public abstract override ImpactData GetImpactData();
|
||||
public virtual float AttackRange => 1f;
|
||||
|
||||
public virtual void SetTarget(Transform target) => _target = target;
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
using Definition.DataStruct;
|
||||
using Definition.Enum;
|
||||
using Entity.EntityData;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace Entity
|
||||
{
|
||||
public class EnemyProjectile : EntityBase
|
||||
{
|
||||
private EnemyProjectileData _projectileData;
|
||||
private Vector3 _direction = Vector3.forward;
|
||||
private float _elapsedTime;
|
||||
private bool _isActive;
|
||||
private bool _isSimulationDriven;
|
||||
private ImpactData _impactData;
|
||||
private Collider[] _cachedColliders;
|
||||
|
||||
public bool IsActive => _isActive;
|
||||
public ImpactData GetImpactData() => _impactData;
|
||||
|
||||
protected override void OnShow(object userData)
|
||||
{
|
||||
base.OnShow(userData);
|
||||
|
||||
_projectileData = userData as EnemyProjectileData;
|
||||
if (_projectileData == null)
|
||||
{
|
||||
Log.Error("Enemy projectile data is invalid.");
|
||||
_isActive = false;
|
||||
GameEntry.Entity.HideEntity(this);
|
||||
return;
|
||||
}
|
||||
|
||||
_isActive = true;
|
||||
_elapsedTime = 0f;
|
||||
_impactData = new ImpactData(_projectileData.OwnerCamp, _projectileData.AttackDamage);
|
||||
|
||||
_direction = _projectileData.Direction;
|
||||
_direction.y = 0f;
|
||||
if (_direction.sqrMagnitude <= Mathf.Epsilon)
|
||||
{
|
||||
_direction = CachedTransform.forward;
|
||||
_direction.y = 0f;
|
||||
}
|
||||
|
||||
if (_direction.sqrMagnitude <= Mathf.Epsilon)
|
||||
{
|
||||
_direction = Vector3.forward;
|
||||
}
|
||||
else
|
||||
{
|
||||
_direction.Normalize();
|
||||
}
|
||||
|
||||
CachedTransform.rotation = Quaternion.LookRotation(_direction, Vector3.up);
|
||||
|
||||
if (_projectileData.OwnerCamp == CampType.Player)
|
||||
{
|
||||
gameObject.layer = LayerMask.NameToLayer("PlayerWeapon");
|
||||
}
|
||||
else if (_projectileData.OwnerCamp == CampType.Enemy)
|
||||
{
|
||||
gameObject.layer = LayerMask.NameToLayer("EnemyWeapon");
|
||||
}
|
||||
|
||||
_isSimulationDriven = IsDrivenBySimulationWorld();
|
||||
SetColliderEnabled(!_isSimulationDriven);
|
||||
}
|
||||
|
||||
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
{
|
||||
base.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
|
||||
if (!_isActive || _projectileData == null) return;
|
||||
|
||||
bool isSimulationDriven = IsDrivenBySimulationWorld();
|
||||
if (isSimulationDriven != _isSimulationDriven)
|
||||
{
|
||||
_isSimulationDriven = isSimulationDriven;
|
||||
SetColliderEnabled(!_isSimulationDriven);
|
||||
}
|
||||
|
||||
if (_isSimulationDriven) return;
|
||||
|
||||
if (_projectileData.Speed > 0f)
|
||||
{
|
||||
CachedTransform.position += _direction * (_projectileData.Speed * elapseSeconds);
|
||||
}
|
||||
|
||||
_elapsedTime += elapseSeconds;
|
||||
if (_projectileData.LifeTime > 0f && _elapsedTime >= _projectileData.LifeTime)
|
||||
{
|
||||
Expire();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnHide(bool isShutdown, object userData)
|
||||
{
|
||||
_isActive = false;
|
||||
_projectileData = null;
|
||||
_elapsedTime = 0f;
|
||||
_isSimulationDriven = false;
|
||||
_impactData = default;
|
||||
_direction = Vector3.forward;
|
||||
|
||||
base.OnHide(isShutdown, userData);
|
||||
}
|
||||
|
||||
public void Expire()
|
||||
{
|
||||
if (!_isActive) return;
|
||||
_isActive = false;
|
||||
GameEntry.Entity.HideEntity(this);
|
||||
}
|
||||
|
||||
private static bool IsDrivenBySimulationWorld()
|
||||
{
|
||||
var simulationWorld = GameEntry.SimulationWorld;
|
||||
return simulationWorld != null && simulationWorld.UseSimulationMovement && simulationWorld.UseJobSimulation;
|
||||
}
|
||||
|
||||
private void SetColliderEnabled(bool enabled)
|
||||
{
|
||||
_cachedColliders ??= GetComponentsInChildren<Collider>(true);
|
||||
if (_cachedColliders == null) return;
|
||||
|
||||
for (int i = 0; i < _cachedColliders.Length; i++)
|
||||
{
|
||||
Collider collider = _cachedColliders[i];
|
||||
if (collider == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
collider.enabled = enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 22624f81b9364c8681b32d993f5e618f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,7 +1,8 @@
|
||||
using Components;
|
||||
using CustomUtility;
|
||||
using Definition.DataStruct;
|
||||
using Definition.Enum;
|
||||
using Entity.EntityData;
|
||||
using Entity.Weapon;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
@@ -9,17 +10,33 @@ namespace Entity
|
||||
{
|
||||
public class MeleeEnemy : EnemyBase
|
||||
{
|
||||
private enum AttackStateType
|
||||
{
|
||||
Idle,
|
||||
Check_OutRange,
|
||||
Check_InRange,
|
||||
Attack
|
||||
}
|
||||
|
||||
private MovementComponent _movementComponent;
|
||||
|
||||
private float _attackRange = 1f;
|
||||
private float _attackRangeSquared;
|
||||
private float _attackCooldown = 1f;
|
||||
private int _attackDamage = 1;
|
||||
|
||||
private float _sqrAttackRange;
|
||||
private float _currAttackTimer;
|
||||
|
||||
private AttackStateType _attackState = AttackStateType.Idle;
|
||||
private EnemyData _meleeEnemyData;
|
||||
private WeaponBase _weapon;
|
||||
private TargetableObject _targetableTarget;
|
||||
|
||||
protected override TargetableObjectData _targetableObjectData => _meleeEnemyData;
|
||||
public override float AttackRange => _attackRange;
|
||||
|
||||
public override ImpactData GetImpactData()
|
||||
{
|
||||
return new ImpactData(_meleeEnemyData.Camp, 0);
|
||||
return new ImpactData(_meleeEnemyData.Camp, _attackDamage);
|
||||
}
|
||||
|
||||
#region FSM
|
||||
@@ -35,51 +52,42 @@ namespace Entity
|
||||
protected override void OnShow(object userData)
|
||||
{
|
||||
base.OnShow(userData);
|
||||
|
||||
|
||||
if (userData is EnemyData enemyData)
|
||||
{
|
||||
_meleeEnemyData = enemyData;
|
||||
_healthComponent.OnInit(enemyData.MaxHealthBase);
|
||||
_movementComponent.OnInit(_meleeEnemyData.SpeedBase, this.CachedTransform, null, true);
|
||||
_movementComponent.SetMove(true);
|
||||
_attackRangeSquared = _attackRange * _attackRange;
|
||||
|
||||
_attackRange = Mathf.Max(0.1f, _meleeEnemyData.AttackRange);
|
||||
_attackCooldown = Mathf.Max(0.01f, _meleeEnemyData.AttackCooldown);
|
||||
_attackDamage = Mathf.Max(1, _meleeEnemyData.AttackDamage);
|
||||
_sqrAttackRange = _attackRange * _attackRange;
|
||||
|
||||
_currAttackTimer = 0f;
|
||||
_attackState = AttackStateType.Idle;
|
||||
_targetableTarget = null;
|
||||
|
||||
this.CachedTransform.position = enemyData.Position;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"Invalid data type. Data type: {userData?.GetType()}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
{
|
||||
base.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
|
||||
UpdateAttackState(elapseSeconds);
|
||||
|
||||
if (IsSimulationMovementEnabled())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
base.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
|
||||
if (_target == null)
|
||||
{
|
||||
_movementComponent.SetMove(false);
|
||||
_movementComponent.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
return;
|
||||
}
|
||||
|
||||
float distanceSquared = (this.CachedTransform.position - _target.position).sqrMagnitude;
|
||||
if (distanceSquared < _attackRangeSquared)
|
||||
{
|
||||
// 攻击
|
||||
_movementComponent.SetMove(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_movementComponent.SetMove(true);
|
||||
_movementComponent.SetDirection(GetTargetDirection());
|
||||
}
|
||||
|
||||
_movementComponent.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
}
|
||||
|
||||
@@ -102,7 +110,7 @@ namespace Entity
|
||||
};
|
||||
GameEntry.Entity.ShowExp(data);
|
||||
}
|
||||
|
||||
|
||||
base.OnDead(attacker);
|
||||
}
|
||||
|
||||
@@ -110,12 +118,143 @@ namespace Entity
|
||||
{
|
||||
_movementComponent.OnReset();
|
||||
_healthComponent.OnReset();
|
||||
_targetableTarget = null;
|
||||
_currAttackTimer = 0f;
|
||||
_attackState = AttackStateType.Idle;
|
||||
|
||||
base.OnHide(isShutdown, userData);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override void SetTarget(Transform target)
|
||||
{
|
||||
base.SetTarget(target);
|
||||
_targetableTarget = target != null ? target.GetComponent<TargetableObject>() : null;
|
||||
}
|
||||
|
||||
private void UpdateAttackState(float elapseSeconds)
|
||||
{
|
||||
_currAttackTimer += elapseSeconds;
|
||||
|
||||
switch (_attackState)
|
||||
{
|
||||
case AttackStateType.Idle:
|
||||
SetMove(false);
|
||||
if (HasValidTarget())
|
||||
{
|
||||
TransitionTo(AttackStateType.Check_OutRange);
|
||||
}
|
||||
|
||||
break;
|
||||
case AttackStateType.Check_OutRange:
|
||||
if (!HasValidTarget())
|
||||
{
|
||||
TransitionTo(AttackStateType.Idle);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsTargetInRange())
|
||||
{
|
||||
TransitionTo(AttackStateType.Check_InRange);
|
||||
return;
|
||||
}
|
||||
|
||||
SetMove(true);
|
||||
_movementComponent.SetDirection(GetTargetDirection());
|
||||
break;
|
||||
case AttackStateType.Check_InRange:
|
||||
if (!HasValidTarget())
|
||||
{
|
||||
TransitionTo(AttackStateType.Idle);
|
||||
return;
|
||||
}
|
||||
|
||||
SetMove(false);
|
||||
if (!IsTargetInRange())
|
||||
{
|
||||
TransitionTo(AttackStateType.Check_OutRange);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currAttackTimer >= _attackCooldown)
|
||||
{
|
||||
TransitionTo(AttackStateType.Attack);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void TransitionTo(AttackStateType newState)
|
||||
{
|
||||
_attackState = newState;
|
||||
|
||||
if (_attackState == AttackStateType.Check_InRange)
|
||||
{
|
||||
SetMove(false);
|
||||
}
|
||||
|
||||
if (_attackState == AttackStateType.Check_InRange && _currAttackTimer >= _attackCooldown)
|
||||
{
|
||||
TransitionTo(AttackStateType.Attack);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_attackState == AttackStateType.Attack)
|
||||
{
|
||||
SetMove(false);
|
||||
ExecuteAttack();
|
||||
_currAttackTimer = 0f;
|
||||
TransitionTo(AttackStateType.Check_InRange);
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasValidTarget()
|
||||
{
|
||||
if (_target == null) return false;
|
||||
|
||||
if (_targetableTarget == null || _targetableTarget.CachedTransform != _target)
|
||||
{
|
||||
_targetableTarget = _target.GetComponent<TargetableObject>();
|
||||
}
|
||||
|
||||
return _targetableTarget != null && _targetableTarget.Available && !_targetableTarget.IsDead;
|
||||
}
|
||||
|
||||
private bool IsTargetInRange()
|
||||
{
|
||||
if (_target == null) return false;
|
||||
|
||||
Vector3 delta = _target.position - this.CachedTransform.position;
|
||||
delta.y = 0f;
|
||||
return delta.sqrMagnitude <= _sqrAttackRange;
|
||||
}
|
||||
|
||||
private void ExecuteAttack()
|
||||
{
|
||||
if (!HasValidTarget() || !IsTargetInRange()) return;
|
||||
|
||||
ImpactData targetImpactData = _targetableTarget.GetImpactData();
|
||||
ImpactData selfImpactData = GetImpactData();
|
||||
if (AIUtility.GetRelation(selfImpactData.Camp, targetImpactData.Camp) == RelationType.Friendly)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int damage = AIUtility.CalcDamageHP(_attackDamage, null, targetImpactData.DefenseStat,
|
||||
targetImpactData.DodgeStat);
|
||||
_targetableTarget.ApplyDamage(this, damage);
|
||||
}
|
||||
|
||||
private void SetMove(bool value)
|
||||
{
|
||||
if (_movementComponent != null)
|
||||
{
|
||||
_movementComponent.SetMove(value);
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3 GetTargetDirection()
|
||||
{
|
||||
if (_target == null)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Components;
|
||||
using CustomUtility;
|
||||
using Definition;
|
||||
using Definition.DataStruct;
|
||||
using Entity.EntityData;
|
||||
using UnityEngine;
|
||||
@@ -8,17 +10,39 @@ namespace Entity
|
||||
{
|
||||
public class RemoteEnemy : EnemyBase
|
||||
{
|
||||
private float _speed;
|
||||
private const string EnemyProjectileGroupName = "Bullet";
|
||||
private const float EnemyProjectileGroupAutoReleaseInterval = 60f;
|
||||
private const int EnemyProjectileGroupCapacity = 64;
|
||||
private const float EnemyProjectileGroupExpireTime = 60f;
|
||||
private const int EnemyProjectileGroupPriority = 0;
|
||||
|
||||
private const string ProjectileSpeedParamKey = "ProjectileSpeed";
|
||||
private const string ProjectileLifeTimeParamKey = "ProjectileLifeTime";
|
||||
private const string ProjectileSpawnForwardOffsetParamKey = "ProjectileSpawnForwardOffset";
|
||||
private const string ProjectileSpawnHeightOffsetParamKey = "ProjectileSpawnHeightOffset";
|
||||
private const string ProjectileAssetNameParamKey = "ProjectileAssetName";
|
||||
|
||||
private MovementComponent _movementComponent;
|
||||
private float _attackRange = 1f;
|
||||
private float _attackRangeSquared;
|
||||
private float _attackCooldown = 1f;
|
||||
private int _attackDamage = 1;
|
||||
private float _currAttackTimer;
|
||||
|
||||
[SerializeField] private float _projectileSpeed = 12f;
|
||||
[SerializeField] private float _projectileLifeTime = 3f;
|
||||
[SerializeField] private float _projectileSpawnForwardOffset = 0.7f;
|
||||
[SerializeField] private float _projectileSpawnHeightOffset = 0.6f;
|
||||
[SerializeField] private string _projectileAssetName = "BulletHandgun";
|
||||
|
||||
private EnemyData _remoteEnemyData;
|
||||
|
||||
protected override TargetableObjectData _targetableObjectData => _remoteEnemyData;
|
||||
public override float AttackRange => _attackRange;
|
||||
|
||||
public override ImpactData GetImpactData()
|
||||
{
|
||||
return new ImpactData(_remoteEnemyData.Camp, 0);
|
||||
return new ImpactData(_remoteEnemyData.Camp, _attackDamage);
|
||||
}
|
||||
|
||||
protected override void OnInit(object userData)
|
||||
@@ -39,7 +63,21 @@ namespace Entity
|
||||
_healthComponent.OnInit(enemyData.MaxHealthBase);
|
||||
_movementComponent.OnInit(_remoteEnemyData.SpeedBase, this.CachedTransform, null, true);
|
||||
_movementComponent.SetMove(true);
|
||||
|
||||
_attackRange = Mathf.Max(0.1f, _remoteEnemyData.AttackRange);
|
||||
_attackRangeSquared = _attackRange * _attackRange;
|
||||
_attackCooldown = Mathf.Max(0.01f, _remoteEnemyData.AttackCooldown);
|
||||
_attackDamage = Mathf.Max(1, _remoteEnemyData.AttackDamage);
|
||||
|
||||
_projectileSpeed = ReadPositiveParam(ProjectileSpeedParamKey, _projectileSpeed);
|
||||
_projectileLifeTime = ReadPositiveParam(ProjectileLifeTimeParamKey, _projectileLifeTime);
|
||||
_projectileSpawnForwardOffset = ReadPositiveParam(ProjectileSpawnForwardOffsetParamKey,
|
||||
_projectileSpawnForwardOffset);
|
||||
_projectileSpawnHeightOffset = ReadPositiveParam(ProjectileSpawnHeightOffsetParamKey,
|
||||
_projectileSpawnHeightOffset);
|
||||
_projectileAssetName = ReadStringParam(ProjectileAssetNameParamKey, _projectileAssetName);
|
||||
|
||||
_currAttackTimer = 0f;
|
||||
this.CachedTransform.position = enemyData.Position;
|
||||
}
|
||||
else
|
||||
@@ -50,25 +88,28 @@ namespace Entity
|
||||
|
||||
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
{
|
||||
if (IsSimulationMovementEnabled())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
base.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
|
||||
_currAttackTimer += elapseSeconds;
|
||||
|
||||
if (_target == null)
|
||||
{
|
||||
_movementComponent.SetMove(false);
|
||||
_movementComponent.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
if (!IsSimulationMovementEnabled())
|
||||
{
|
||||
_movementComponent.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
float distanceSquared = (this.CachedTransform.position - _target.position).sqrMagnitude;
|
||||
if (distanceSquared < _attackRangeSquared)
|
||||
Vector3 toTarget = _target.position - this.CachedTransform.position;
|
||||
toTarget.y = 0f;
|
||||
float distanceSquared = toTarget.sqrMagnitude;
|
||||
if (distanceSquared <= _attackRangeSquared)
|
||||
{
|
||||
// 攻击
|
||||
_movementComponent.SetMove(false);
|
||||
TryFireProjectile();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -76,17 +117,115 @@ namespace Entity
|
||||
_movementComponent.SetDirection(GetTargetDirection());
|
||||
}
|
||||
|
||||
_movementComponent.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
if (!IsSimulationMovementEnabled())
|
||||
{
|
||||
_movementComponent.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnHide(bool isShutdown, object userData)
|
||||
{
|
||||
_movementComponent.OnReset();
|
||||
_healthComponent.OnReset();
|
||||
_currAttackTimer = 0f;
|
||||
|
||||
base.OnHide(isShutdown, userData);
|
||||
}
|
||||
|
||||
private void TryFireProjectile()
|
||||
{
|
||||
if (_currAttackTimer < _attackCooldown || _target == null) return;
|
||||
if (!EnsureEnemyProjectileGroup()) return;
|
||||
|
||||
Vector3 spawnPosition = this.CachedTransform.position +
|
||||
this.CachedTransform.forward * _projectileSpawnForwardOffset +
|
||||
Vector3.up * _projectileSpawnHeightOffset;
|
||||
Vector3 direction = _target.position - spawnPosition;
|
||||
direction.y = 0f;
|
||||
if (direction.sqrMagnitude <= Mathf.Epsilon)
|
||||
{
|
||||
direction = this.CachedTransform.forward;
|
||||
direction.y = 0f;
|
||||
}
|
||||
|
||||
if (direction.sqrMagnitude <= Mathf.Epsilon)
|
||||
{
|
||||
direction = Vector3.forward;
|
||||
}
|
||||
else
|
||||
{
|
||||
direction.Normalize();
|
||||
}
|
||||
|
||||
int projectileEntityId = GameEntry.Entity.GenerateSerialId();
|
||||
var projectileData = new EnemyProjectileData(projectileEntityId, Id, _remoteEnemyData.Camp,
|
||||
_attackDamage, _projectileSpeed, _projectileLifeTime, direction)
|
||||
{
|
||||
Position = spawnPosition,
|
||||
Rotation = Quaternion.LookRotation(direction, Vector3.up)
|
||||
};
|
||||
|
||||
GameEntry.Entity.ShowEntity(
|
||||
entityId: projectileEntityId,
|
||||
entityLogicType: typeof(EnemyProjectile),
|
||||
entityAssetName: AssetUtility.GetEntityAsset(_projectileAssetName),
|
||||
entityGroupName: EnemyProjectileGroupName,
|
||||
priority: Constant.AssetPriority.BulletAsset,
|
||||
userData: projectileData);
|
||||
|
||||
_currAttackTimer = 0f;
|
||||
}
|
||||
|
||||
private static bool EnsureEnemyProjectileGroup()
|
||||
{
|
||||
var entityComponent = GameEntry.Entity;
|
||||
if (entityComponent == null) return false;
|
||||
|
||||
if (entityComponent.HasEntityGroup(EnemyProjectileGroupName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addResult = entityComponent.AddEntityGroup(
|
||||
EnemyProjectileGroupName,
|
||||
EnemyProjectileGroupAutoReleaseInterval,
|
||||
EnemyProjectileGroupCapacity,
|
||||
EnemyProjectileGroupExpireTime,
|
||||
EnemyProjectileGroupPriority);
|
||||
|
||||
if (!addResult)
|
||||
{
|
||||
Log.Warning("Can not create entity group '{0}'.", EnemyProjectileGroupName);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private float ReadPositiveParam(string paramName, float defaultValue)
|
||||
{
|
||||
if (_remoteEnemyData != null &&
|
||||
_remoteEnemyData.TryGetParam(paramName, out string rawValue) &&
|
||||
float.TryParse(rawValue, out float parsedValue))
|
||||
{
|
||||
return Mathf.Max(0.01f, parsedValue);
|
||||
}
|
||||
|
||||
return Mathf.Max(0.01f, defaultValue);
|
||||
}
|
||||
|
||||
private string ReadStringParam(string paramName, string defaultValue)
|
||||
{
|
||||
if (_remoteEnemyData != null &&
|
||||
_remoteEnemyData.TryGetParam(paramName, out string rawValue) &&
|
||||
!string.IsNullOrWhiteSpace(rawValue))
|
||||
{
|
||||
return rawValue;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private Vector3 GetTargetDirection()
|
||||
{
|
||||
if (_target == null)
|
||||
|
||||
Reference in New Issue
Block a user