初始化仓库

This commit is contained in:
SepComet
2026-02-07 19:38:49 +08:00
commit 88641dae11
1540 changed files with 371772 additions and 0 deletions
@@ -0,0 +1,53 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using Definition.DataStruct;
using Entity.EntityData;
using StarForce;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace Entity
{
/// <summary>
/// 子弹类。
/// </summary>
public class Bullet : EntityBase
{
[SerializeField] private BulletData m_BulletData = null;
public ImpactData GetImpactData()
{
//TODO:子弹的 AttackStat 不该为 null
return new ImpactData(m_BulletData.OwnerCamp, m_BulletData.Attack, null);
}
protected override void OnInit(object userData)
{
base.OnInit(userData);
}
protected override void OnShow(object userData)
{
base.OnShow(userData);
m_BulletData = userData as BulletData;
if (m_BulletData == null)
{
Log.Error("Bullet data is invalid.");
return;
}
}
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(elapseSeconds, realElapseSeconds);
CachedTransform.Translate(Vector3.forward * m_BulletData.Speed * elapseSeconds, Space.World);
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 822be177ff3fd674fa34da0755f1a427
timeCreated: 1528026153
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,37 @@
using Entity.EntityData;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace Entity
{
public class CoinEntity : EntityBase
{
private CoinData _coinData;
protected override void OnShow(object userData)
{
base.OnShow(userData);
if (userData is CoinData coinData)
{
_coinData = coinData;
}
else
{
Log.Error("CoinEntity.OnShow() called with wrong type.");
}
this.CachedTransform.localScale = new Vector3(0.3f, 0.3f, 0.3f);
}
private void OnTriggerEnter(Collider other)
{
var player = other.GetComponent<Player>();
if (player != null && player.Available)
{
player.Coin += _coinData.Value;
GameEntry.Entity.HideEntity(this);
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0878ab0829c34f44a83d5e5a1eb586ea
timeCreated: 1770292173
@@ -0,0 +1,58 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using Entity;
using Entity.EntityData;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace Entity
{
/// <summary>
/// 特效类。
/// </summary>
public class Effect : EntityBase
{
[SerializeField]
private EffectData m_EffectData = null;
private float m_ElapseSeconds = 0f;
#if UNITY_2017_3_OR_NEWER
protected override void OnShow(object userData)
#else
protected internal override void OnShow(object userData)
#endif
{
base.OnShow(userData);
m_EffectData = userData as EffectData;
if (m_EffectData == null)
{
Log.Error("Effect data is invalid.");
return;
}
m_ElapseSeconds = 0f;
}
#if UNITY_2017_3_OR_NEWER
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
#else
protected internal override void OnUpdate(float elapseSeconds, float realElapseSeconds)
#endif
{
base.OnUpdate(elapseSeconds, realElapseSeconds);
m_ElapseSeconds += elapseSeconds;
if (m_ElapseSeconds >= m_EffectData.KeepTime)
{
GameEntry.Entity.HideEntity(this);
}
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c3e408851e9ea064bb8bc08fbb10b33f
timeCreated: 1528026154
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 81c9b48d9ff1bce45b91ffa040b1db2a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,12 @@
using Definition.DataStruct;
using Entity;
using UnityEngine;
public abstract class EnemyBase : TargetableObject
{
protected Transform _target;
public abstract override ImpactData GetImpactData();
public virtual void SetTarget(Transform target) => _target = target;
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d33d6ac2d1874d4418780e3354e6b4e3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,115 @@
using Components;
using Definition.DataStruct;
using Entity.EntityData;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace Entity
{
public class MeleeEnemy : EnemyBase
{
private MovementComponent _movementComponent;
private float _attackRange = 1f;
private float _attackRangeSquared;
private EnemyData _meleeEnemyData;
private WeaponBase _weapon;
protected override TargetableObjectData _targetableObjectData => _meleeEnemyData;
public override ImpactData GetImpactData()
{
return new ImpactData(_meleeEnemyData.Camp, 0);
}
#region FSM
protected override void OnInit(object userData)
{
base.OnInit(userData);
_movementComponent = GetComponent<MovementComponent>();
_healthComponent = GetComponent<HealthComponent>();
}
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);
_movementComponent.SetMove(true);
_attackRangeSquared = _attackRange * _attackRange;
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);
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);
}
protected override void OnDead(EntityBase attacker)
{
if (Random.value < _meleeEnemyData.DropPercent)
{
var data = new CoinData(_meleeEnemyData.DropCoin, GameEntry.Entity.GenerateSerialId(), 10001)
{
Position = this.CachedTransform.position
};
GameEntry.Entity.ShowCoin(data);
}
if (Random.value < _meleeEnemyData.DropPercent)
{
var data = new ExpData(_meleeEnemyData.DropExp, GameEntry.Entity.GenerateSerialId(), 10002)
{
Position = this.CachedTransform.position
};
GameEntry.Entity.ShowExp(data);
}
base.OnDead(attacker);
}
protected override void OnHide(bool isShutdown, object userData)
{
_movementComponent.OnReset();
_healthComponent.OnReset();
base.OnHide(isShutdown, userData);
}
#endregion
private Vector3 GetTargetDirection()
{
return new Vector3(
_target.position.x - this.CachedTransform.position.x,
0f,
_target.position.z - this.CachedTransform.position.z
).normalized;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 112f2a17ab82438484782c9a763fdee1
timeCreated: 1768892870
@@ -0,0 +1,87 @@
using Components;
using Definition.DataStruct;
using Entity.EntityData;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace Entity
{
public class RemoteEnemy : EnemyBase
{
private float _speed;
private MovementComponent _movementComponent;
private float _attackRange = 1f;
private float _attackRangeSquared;
private EnemyData _remoteEnemyData;
protected override TargetableObjectData _targetableObjectData => _remoteEnemyData;
public override ImpactData GetImpactData()
{
return new ImpactData(_remoteEnemyData.Camp, 0);
}
protected override void OnInit(object userData)
{
base.OnInit(userData);
_movementComponent = GetComponent<MovementComponent>();
_healthComponent = GetComponent<HealthComponent>();
}
protected override void OnShow(object userData)
{
base.OnShow(userData);
if (userData is EnemyData enemyData)
{
_remoteEnemyData = enemyData;
_healthComponent.OnInit(enemyData.MaxHealthBase);
_movementComponent.OnInit(_remoteEnemyData.SpeedBase, this.CachedTransform);
_movementComponent.SetMove(true);
_attackRangeSquared = _attackRange * _attackRange;
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);
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);
}
protected override void OnHide(bool isShutdown, object userData)
{
_movementComponent.OnReset();
_healthComponent.OnReset();
base.OnHide(isShutdown, userData);
}
private Vector3 GetTargetDirection()
{
return new Vector3(
_target.position.x - this.CachedTransform.position.x,
0f,
_target.position.z - this.CachedTransform.position.z
).normalized;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b90e05d1e7a5475bb928b2445e113767
timeCreated: 1770209676
@@ -0,0 +1,82 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using Entity.EntityData;
using GameFramework;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace Entity
{
public abstract class EntityBase : EntityLogic
{
[SerializeField] private EntityDataBase _entityData = null;
public int Id => Entity.Id;
public Animation CachedAnimation { get; private set; }
protected override void OnInit(object userData)
{
base.OnInit(userData);
CachedAnimation = GetComponent<Animation>();
}
protected override void OnRecycle()
{
base.OnRecycle();
}
protected override void OnShow(object userData)
{
base.OnShow(userData);
_entityData = userData as EntityDataBase;
if (_entityData == null)
{
Log.Error("Entity data is invalid.");
return;
}
Name = Utility.Text.Format("[Entity {0}]", Id);
CachedTransform.localPosition = _entityData.Position;
CachedTransform.localRotation = _entityData.Rotation;
CachedTransform.localScale = Vector3.one;
}
protected override void OnHide(bool isShutdown, object userData)
{
base.OnHide(isShutdown, userData);
}
protected override void OnAttached(EntityLogic childEntity, Transform parentTransform, object userData)
{
base.OnAttached(childEntity, parentTransform, userData);
}
protected override void OnDetached(EntityLogic childEntity, object userData)
{
base.OnDetached(childEntity, userData);
}
protected override void OnAttachTo(EntityLogic parentEntity, Transform parentTransform, object userData)
{
base.OnAttachTo(parentEntity, parentTransform, userData);
}
protected override void OnDetachFrom(EntityLogic parentEntity, object userData)
{
base.OnDetachFrom(parentEntity, userData);
}
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(elapseSeconds, realElapseSeconds);
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 878fbbe82b4bde944812bcb7c964cff8
timeCreated: 1528026154
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,36 @@
using Entity.EntityData;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace Entity
{
public class ExpEntity:EntityBase
{
private ExpData _expData;
protected override void OnShow(object userData)
{
base.OnShow(userData);
if (userData is ExpData expData)
{
_expData = expData;
}
else
{
Log.Error("expEntity.OnShow() called with wrong type.");
}
this.CachedTransform.localScale = new Vector3(0.3f, 0.3f, 0.3f);
}
private void OnTriggerEnter(Collider other)
{
var player = other.GetComponent<Player>();
if (player != null && player.Available)
{
player.Exp += _expData.Value;
GameEntry.Entity.HideEntity(this);
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ec4ae4b0a327442a88463eafd59c5b6e
timeCreated: 1770293001
@@ -0,0 +1,191 @@
using Components;
using CustomEvent;
using DataTable;
using Definition.DataStruct;
using Entity.EntityData;
using GameFramework.Event;
using UnityGameFramework.Runtime;
namespace Entity
{
public class Player : TargetableObject
{
protected override TargetableObjectData _targetableObjectData => _playerData;
private InputComponent _inputComponent;
private MovementComponent _movementComponent;
private StatComponent _statComponent;
private BackpackComponent _backpackComponent;
private PlayerData _playerData;
/// <summary>
/// 当前金币。
/// </summary>
private int _coin;
/// <summary>
/// 当前经验。
/// </summary>
private int _exp;
/// <summary>
/// 每级所需的经验。
/// tip: _expRequires[1] 表示 Lv 0 -> Lv 1 需要的经验
/// </summary>
private int[] _expRequires;
/// <summary>
/// 当前的角色等级。
/// </summary>
private int _currentLevel;
/// <summary>
/// 玩家是否启用。
/// </summary>
private bool _enable;
public int Coin
{
get => _coin;
set
{
GameEntry.Event.Fire(this, PlayerCoinChangeEventArgs.Create(value - _coin, value));
_coin = value;
}
}
public int Exp
{
get => _exp;
set
{
if (value >= _expRequires[_currentLevel + 1])
{
value -= _expRequires[_currentLevel + 1];
_currentLevel++;
}
GameEntry.Event.Fire(this, PlayerExpChangeEventArgs.Create(value, _expRequires[_currentLevel + 1]));
_exp = value;
}
}
public bool Enable
{
get => _enable;
set
{
if (value == _enable) return;
_movementComponent.SetMove(value);
_backpackComponent.SetWeaponState(value);
_inputComponent.SetListening(value);
}
}
public void InitRole(DRRole role)
{
// MovementComponent
_movementComponent.OnInit(role.Speed, this.CachedTransform, _statComponent);
_movementComponent.SetMove(true);
// HealthComponent
_healthComponent.OnInit(role.MaxHealth, _statComponent);
// BackpackComponent
Coin = role.Coin;
_backpackComponent.OnInit(this, role.WeaponCapacity);
GameEntry.Entity.ShowWeapon(new WeaponKnifeData(GameEntry.Entity.GenerateSerialId(), 201,
this.Id, _playerData.Camp));
// StatComponent
foreach (var modifier in role.InitialProperties)
{
_statComponent.AddModifier(modifier);
}
_expRequires = role.ExpRequires;
// Fire Events
GameEntry.Event.Fire(this,
PlayerHealthChangeEventArgs.Create(0, _healthComponent.CurrentHealth, _healthComponent.MaxHealth));
GameEntry.Event.Fire(this, PlayerExpChangeEventArgs.Create(0, 100));
GameEntry.Event.Fire(this,
PlayerCoinChangeEventArgs.Create(Coin, Coin));
}
#region FSM
public override ImpactData GetImpactData()
{
return new ImpactData(_playerData.Camp, 0, null, _healthComponent.DefenseStat, _healthComponent.DodgeStat);
}
protected override void OnInit(object userData)
{
base.OnInit(userData);
_inputComponent = GetComponent<InputComponent>();
_movementComponent = GetComponent<MovementComponent>();
_statComponent = GetComponent<StatComponent>();
_healthComponent = GetComponent<HealthComponent>();
_backpackComponent = GetComponent<BackpackComponent>();
}
protected override void OnShow(object userData)
{
base.OnShow(userData);
GameEntry.Event.Subscribe(ShowEntitySuccessEventArgs.EventId, ShowEntitySuccess);
_statComponent.OnInit();
if (userData is PlayerData playerData)
{
_playerData = playerData;
}
_inputComponent.OnInit();
_inputComponent.SetListening(true);
_currentLevel = 0;
}
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(elapseSeconds, realElapseSeconds);
_inputComponent.OnUpdate(elapseSeconds, realElapseSeconds);
_movementComponent.SetDirection(_inputComponent.Direction);
_movementComponent.OnUpdate(elapseSeconds, realElapseSeconds);
}
protected override void OnHide(bool isShutdown, object userData)
{
_inputComponent.OnReset();
_movementComponent.OnReset();
_healthComponent.OnReset();
_statComponent.OnReset();
_backpackComponent.OnReset();
base.OnHide(isShutdown, userData);
}
#endregion
#region Event Handlers
private void ShowEntitySuccess(object sender, GameEventArgs e)
{
if (!(e is ShowEntitySuccessEventArgs args)) return;
if (args.Entity.Logic is WeaponBase weapon)
{
_backpackComponent.AttachWeapon(weapon);
}
}
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3979cb10abff0944aad09de04ec7813a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,83 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using Components;
using Definition;
using Definition.DataStruct;
using Entity.EntityData;
using StarForce;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace Entity
{
/// <summary>
/// 可作为目标的实体类。
/// </summary>
public abstract class TargetableObject : EntityBase
{
protected abstract TargetableObjectData _targetableObjectData { get; }
public bool IsDead => _healthComponent.CurrentHealth <= 0;
protected HealthComponent _healthComponent;
public abstract ImpactData GetImpactData();
public void ApplyDamage(EntityBase attacker, int damageHP)
{
float fromHPRatio = _healthComponent.HealthRatio;
_healthComponent.TakeDamage(damageHP);
float toHPRatio = _healthComponent.HealthRatio;
if (fromHPRatio > toHPRatio)
{
GameEntry.HPBar.ShowHPBar(this, fromHPRatio, toHPRatio);
}
if (_healthComponent.CurrentHealth <= 0)
{
OnDead(attacker);
}
}
protected override void OnInit(object userData)
{
base.OnInit(userData);
_healthComponent = GetComponent<HealthComponent>();
}
protected override void OnShow(object userData)
{
base.OnShow(userData);
}
protected virtual void OnDead(EntityBase attacker)
{
GameEntry.Entity.HideEntity(this);
}
private void OnTriggerEnter(Collider other)
{
EntityBase entity = other.gameObject.GetComponent<EntityBase>();
if (entity == null)
{
return;
}
if (entity is TargetableObject && entity.Id < Id)
{
// 碰撞事件由 Id 大的一方处理
// 在这里规定所有的 Enemy 的 Id 均大于 0
// 而其他的 Entity (Player, Weapon, Bullet) 的 Id 均小于 0
return;
}
AIUtility.PerformCollision(this, entity);
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4a1fd351b05d994459122be1a39507b1
timeCreated: 1528026154
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6b5f40df44fe005498fc03555871b8ad
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,83 @@
using System;
using Definition.DataStruct;
using Entity.EntityData;
using GameFramework;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace Entity
{
public enum WeaponStateType
{
Disabled,
Check,
Attack,
Transition,
Idle,
Check_OutRange,
Check_InRange,
}
/// <summary>
/// 武器类。
/// </summary>
public abstract class WeaponBase : EntityBase
{
protected const string AttachPoint = "Weapon Point";
protected bool _isAttacking = false;
protected bool _isEnabled = false;
public WeaponData WeaponData;
public bool IsAttacking => _isAttacking;
protected WeaponStateBase _currentState;
#region FSM
protected override void OnInit(object userData)
{
base.OnInit(userData);
}
//protected abstract void OnShow(object userData);
// {
// base.OnShow(userData);
//
// _weaponData = userData as WeaponData;
// if (_weaponData == null)
// {
// Log.Error("Weapon data is invalid.");
// return;
// }
// }
protected override void OnAttachTo(EntityLogic parentEntity, Transform parentTransform, object userData)
{
base.OnAttachTo(parentEntity, parentTransform, userData);
Name = Utility.Text.Format("Weapon of {0}", parentEntity.Name);
CachedTransform.localPosition = Vector3.zero;
}
#endregion
public abstract ImpactData GetImpactData();
protected abstract void Attack();
protected abstract void Check();
protected abstract void TransitionTo(WeaponStateType newState);
public void SetEnabled(bool value)
{
this._isEnabled = value;
TransitionTo(WeaponStateType.Idle);
}
}
public abstract class WeaponStateBase
{
public abstract WeaponStateType State { get; }
public abstract void OnInit(WeaponBase weapon);
public abstract void OnEnter();
public abstract void OnUpdate(float elapseSeconds, float realElapseSeconds);
public abstract void OnLeave();
public override string ToString() => State.ToString();
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: fc18f1a1232bb9644be1191168741922
timeCreated: 1528026154
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,332 @@
using System;
using System.Collections.Generic;
using Components;
using CustomComponent;
using Definition.DataStruct;
using Definition.Enum;
using DG.Tweening;
using Entity.EntityData;
using StarForce;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace Entity
{
public class WeaponKnife : WeaponBase
{
private WeaponKnifeData _weaponData;
private float _currAttackTimer = 0f;
private EnemyManagerComponent _enemy;
private EntityBase _target;
private float _sqrRange;
private Collider _collider;
private Rigidbody _rigidbody;
private Quaternion _cachedRotation;
[SerializeField] private float _rotateSpeed = 2f;
private Dictionary<WeaponStateType, WeaponStateBase> _weaponStates;
private StatProperty _attackStat = new();
private Action<StatModifier, bool> _attackStatCallback;
[SerializeField] private float attackDuration = 0.15f; // 戳刺持续时间
[SerializeField] private float returnDuration = 0.2f; // 回归持续时间
public override ImpactData GetImpactData()
{
return new ImpactData(_weaponData.OwnerCamp, _weaponData.Attack, _attackStat);
}
private void InitStates()
{
_weaponStates = new Dictionary<WeaponStateType, WeaponStateBase>
{
{ WeaponStateType.Idle, new IdleState() },
{ WeaponStateType.Check_InRange, new CheckInRangeState() },
{ WeaponStateType.Check_OutRange, new CheckOutRangeState() },
{ WeaponStateType.Attack, new AttackState() }
};
foreach (var state in _weaponStates.Values)
{
state.OnInit(this);
}
}
#region FSM Methods
protected override void Attack()
{
Vector3 targetPos = CachedTransform.position + CachedTransform.forward * _weaponData.AttackRange;
_isAttacking = true;
_collider.enabled = true;
_rigidbody.detectCollisions = true;
Transform parentTransform = CachedTransform.parent;
CachedTransform.SetParent(null);
var sequence = DOTween.Sequence();
sequence.Append(CachedTransform.DOMove(targetPos, attackDuration).SetEase(Ease.OutQuad));
sequence.AppendCallback(() =>
{
_collider.enabled = false;
_rigidbody.detectCollisions = false;
CachedTransform.SetParent(parentTransform);
});
sequence.Append(CachedTransform.DOLocalMove(Vector3.zero, returnDuration / 2f).SetEase(Ease.InQuad));
sequence.AppendCallback(() => { _isAttacking = false; });
}
protected override void Check()
{
_target = null;
float minSqrMagnitude = float.MaxValue;
var enemies = _enemy.Enemies;
foreach (var enemy in enemies)
{
if (enemy == null || !enemy.Available) continue;
float sqrMagnitude = AIUtility.GetSqrMagnitude(this, enemy);
if (minSqrMagnitude < sqrMagnitude) continue;
_target = enemy;
minSqrMagnitude = sqrMagnitude;
}
}
private void RotateToTarget(float elapseSeconds)
{
if (_target == null || !_target.Available) return;
Vector3 directionToTarget = (_target.CachedTransform.position - transform.position).normalized;
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget, Vector3.up);
var quaternion = Quaternion.Slerp(CachedTransform.rotation, targetRotation, _rotateSpeed * elapseSeconds);
CachedTransform.rotation = quaternion;
}
private void RotateToOrigin(float elapseSeconds)
{
var quaternion = Quaternion.Slerp(CachedTransform.rotation, _cachedRotation, _rotateSpeed * elapseSeconds);
CachedTransform.rotation = quaternion;
}
protected override void TransitionTo(WeaponStateType state)
{
_currentState?.OnLeave();
_currentState = _weaponStates[state];
_currentState.OnEnter();
}
#endregion
#region FSM
protected override void OnShow(object userData)
{
base.OnShow(userData);
_weaponData = userData as WeaponKnifeData;
if (_weaponData == null)
{
Log.Error("WeaponKnife data is invalid.");
return;
}
_collider = GetComponent<Collider>();
_rigidbody = GetComponent<Rigidbody>();
_currAttackTimer = 0;
_enemy = GameEntry.EnemyManager;
_sqrRange = _weaponData.AttackRange * _weaponData.AttackRange;
_cachedRotation = transform.rotation;
_collider.enabled = false;
_rigidbody.detectCollisions = false;
if (_weaponData.OwnerCamp == CampType.Player)
this.gameObject.layer = LayerMask.NameToLayer("PlayerWeapon");
if (_weaponData.OwnerCamp == CampType.Enemy)
this.gameObject.layer = LayerMask.NameToLayer("EnemyWeapon");
InitStates();
TransitionTo(WeaponStateType.Idle);
}
protected override void OnAttachTo(EntityLogic parentEntity, Transform parentTransform, object userData)
{
base.OnAttachTo(parentEntity, parentTransform, userData);
if (parentEntity is Player player)
{
var statComponent = player.GetComponent<StatComponent>();
if (statComponent != null)
{
this._attackStat = statComponent.GetStat(StatType.Attack);
this._attackStatCallback = (modifier, b) => statComponent.UpdateStat(_attackStat, modifier, b);
statComponent.Subscribe(StatType.Attack, this._attackStatCallback);
}
}
}
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(elapseSeconds, realElapseSeconds);
if (!_isEnabled) return;
Log.Debug($"current state: {_currentState}");
_currentState?.OnUpdate(elapseSeconds, realElapseSeconds);
}
protected override void OnDetachFrom(EntityLogic parentEntity, object userData)
{
base.OnDetachFrom(parentEntity, userData);
if (parentEntity is Player player)
{
var statComponent = player.GetComponent<StatComponent>();
if (statComponent != null)
{
statComponent.Unsubscribe(StatType.Attack, this._attackStatCallback);
}
}
this._attackStat = null;
this._attackStatCallback = null;
}
#endregion
private class IdleState : WeaponStateBase
{
private WeaponKnife _weapon;
public override WeaponStateType State => WeaponStateType.Idle;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponKnife;
public override void OnEnter()
{
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToOrigin(elapseSeconds);
if (_weapon._target != null && _weapon._target.Available)
{
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
}
}
public override void OnLeave()
{
}
}
private class CheckOutRangeState : WeaponStateBase
{
private WeaponKnife _weapon;
public override WeaponStateType State => WeaponStateType.Check_OutRange;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponKnife;
public override void OnEnter()
{
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToTarget(elapseSeconds);
if (_weapon._target == null || !_weapon._target.Available)
{
_weapon.TransitionTo(WeaponStateType.Idle);
return;
}
if (AIUtility.GetSqrMagnitude(_weapon._target, _weapon) < _weapon._sqrRange)
{
_weapon.TransitionTo(WeaponStateType.Check_InRange);
return;
}
}
public override void OnLeave()
{
}
}
private class CheckInRangeState : WeaponStateBase
{
private WeaponKnife _weapon;
public override WeaponStateType State => WeaponStateType.Check_InRange;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponKnife;
public override void OnEnter()
{
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_weapon.Check();
_weapon.RotateToTarget(elapseSeconds);
_weapon._currAttackTimer += elapseSeconds;
if (_weapon._target == null || !_weapon._target.Available)
{
_weapon.TransitionTo(WeaponStateType.Idle);
return;
}
if (AIUtility.GetSqrMagnitude(_weapon._target, _weapon) >= _weapon._sqrRange)
{
_weapon.TransitionTo(WeaponStateType.Check_OutRange);
return;
}
if (_weapon._currAttackTimer >= _weapon._weaponData.Cooldown)
{
_weapon.TransitionTo(WeaponStateType.Attack);
return;
}
}
public override void OnLeave()
{
_weapon._currAttackTimer = 0;
}
}
private class AttackState : WeaponStateBase
{
private WeaponKnife _weapon;
public override WeaponStateType State => WeaponStateType.Attack;
public override void OnInit(WeaponBase weapon) => _weapon = weapon as WeaponKnife;
public override void OnEnter()
{
_weapon.Attack();
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
if (!_weapon._isAttacking)
{
_weapon.TransitionTo(WeaponStateType.Check_InRange);
}
}
public override void OnLeave()
{
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bbf2e0cccf970934e82f8006d5be7846
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: