From 1e4d3ef8d74eda6cf537a9dab435f948c784889f Mon Sep 17 00:00:00 2001 From: basil <2428390463@qq.com> Date: Tue, 15 Sep 2026 11:31:20 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B8=85=E7=90=86=20Player=20=E4=BB=A5?= =?UTF-8?q?=E5=8F=8A=E7=9B=B8=E5=85=B3=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GameMain/DataTables/tbglobalconfig.bytes | Bin 87 -> 101 bytes .../GameMain/DataTables/tbglobalconfig.json | 9 +++- .../GameMain/Scripts/Base/Gen/GlobalConfig.cs | 49 ++++++++++++++++++ .../Scripts/Base/Gen/TbGlobalConfig.cs | 28 ++++++++++ .../Scripts/Procedure/Game/GameStateBase.cs | 6 --- .../Runtime/Components/AbsorbComponent.cs | 41 ++++++--------- .../Runtime/Components/AttackComponent.cs | 24 --------- .../Components/AttackComponent.cs.meta | 11 ---- .../Runtime/Components/BackpackComponent.cs | 1 - .../Runtime/Components/HealthComponent.cs | 12 +++-- .../Runtime/Components/InputComponent.cs | 3 -- .../Runtime/Components/MovementComponent.cs | 38 +++++++------- .../Runtime/Components/StatComponent.cs | 17 +++--- .../Scripts/Runtime/EntityLogic/Player.cs | 45 ++++++---------- 数据表/Datas/GlobalConfig_全局配置.xlsx | Bin 11181 -> 11690 bytes 15 files changed, 150 insertions(+), 134 deletions(-) delete mode 100644 Assets/GameMain/Scripts/Runtime/Components/AttackComponent.cs delete mode 100644 Assets/GameMain/Scripts/Runtime/Components/AttackComponent.cs.meta diff --git a/Assets/GameMain/DataTables/tbglobalconfig.bytes b/Assets/GameMain/DataTables/tbglobalconfig.bytes index 87dcf6ab7e7017421e44c0060d3e423da693c042..2020384d8b24cf32ebb1d6313d40d44cf0145d00 100644 GIT binary patch delta 35 pcmWGf6=7ui@E-`+0&)^7Q;T-ZXpHOLF_F)cqs8YysX^l*CIBo35ETFb delta 20 acmYcd=VN61@E-`+0&)^7Q;Q~wcme=nh6x`4 diff --git a/Assets/GameMain/DataTables/tbglobalconfig.json b/Assets/GameMain/DataTables/tbglobalconfig.json index 108db4d..1b726f7 100644 --- a/Assets/GameMain/DataTables/tbglobalconfig.json +++ b/Assets/GameMain/DataTables/tbglobalconfig.json @@ -3,6 +3,9 @@ "weaponCapacity": 6, "propMaxCount": -1, "playerEntity": "Player", + "absorbSpeed": 15000, + "collectDistance": 350, + "defaultAbsorbRange": 3000, "weaponRecycleRate": 300, "shopGoodsCount": 4, "shopPriceRandomPercent": 50, @@ -24,6 +27,10 @@ "projectileLifeTime": 3000, "projectileHeightOffset": 600, "projectileSpeed": 12000, - "projectileEntity": "Projectile" + "projectileEntity": "Projectile", + "damageTextYOffset": 1100, + "entityAsyncTimeout": 30000, + "enemyBodyRadius": 450, + "enemySeparationIterations": 2 } ] \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Base/Gen/GlobalConfig.cs b/Assets/GameMain/Scripts/Base/Gen/GlobalConfig.cs index 6ffec1a..198781e 100644 --- a/Assets/GameMain/Scripts/Base/Gen/GlobalConfig.cs +++ b/Assets/GameMain/Scripts/Base/Gen/GlobalConfig.cs @@ -20,6 +20,9 @@ public sealed partial class GlobalConfig : Luban.BeanBase PropMaxCount = _buf.ReadInt(); PlayerEntity = _buf.ReadString(); PlayerEntity_Ref = null; + AbsorbSpeed = _buf.ReadInt(); + CollectDistance = _buf.ReadInt(); + DefaultAbsorbRange = _buf.ReadInt(); WeaponRecycleRate = _buf.ReadInt(); ShopGoodsCount = _buf.ReadInt(); ShopPriceRandomPercent = _buf.ReadInt(); @@ -43,6 +46,10 @@ public sealed partial class GlobalConfig : Luban.BeanBase ProjectileSpeed = _buf.ReadInt(); ProjectileEntity = _buf.ReadString(); ProjectileEntity_Ref = null; + DamageTextYOffset = _buf.ReadInt(); + EntityAsyncTimeout = _buf.ReadInt(); + EnemyBodyRadius = _buf.ReadInt(); + EnemySeparationIterations = _buf.ReadInt(); } public static GlobalConfig DeserializeGlobalConfig(ByteBuf _buf) @@ -64,6 +71,18 @@ public sealed partial class GlobalConfig : Luban.BeanBase public readonly string PlayerEntity; public EntityConfig PlayerEntity_Ref; /// + /// 自动收集速度 + /// + public readonly int AbsorbSpeed; + /// + /// 自动收集距离 + /// + public readonly int CollectDistance; + /// + /// 默认吸收距离 + /// + public readonly int DefaultAbsorbRange; + /// /// 武器回收价比例 /// public readonly int WeaponRecycleRate; @@ -152,6 +171,22 @@ public sealed partial class GlobalConfig : Luban.BeanBase /// public readonly string ProjectileEntity; public EntityConfig ProjectileEntity_Ref; + /// + /// 伤害数字高度偏移 + /// + public readonly int DamageTextYOffset; + /// + /// 实体生成超时时间 + /// + public readonly int EntityAsyncTimeout; + /// + /// 敌人碰撞半径 + /// + public readonly int EnemyBodyRadius; + /// + /// 敌人分离迭代次数 + /// + public readonly int EnemySeparationIterations; public const int __ID__ = -958250779; public override int GetTypeId() => __ID__; @@ -178,11 +213,18 @@ public sealed partial class GlobalConfig : Luban.BeanBase + + + ProjectileEntity_Ref = tables.TbEntityConfig.GetOrDefault(ProjectileEntity); + + + + } public override string ToString() @@ -191,6 +233,9 @@ public sealed partial class GlobalConfig : Luban.BeanBase + "weaponCapacity:" + WeaponCapacity + "," + "propMaxCount:" + PropMaxCount + "," + "playerEntity:" + PlayerEntity + "," + + "absorbSpeed:" + AbsorbSpeed + "," + + "collectDistance:" + CollectDistance + "," + + "defaultAbsorbRange:" + DefaultAbsorbRange + "," + "weaponRecycleRate:" + WeaponRecycleRate + "," + "shopGoodsCount:" + ShopGoodsCount + "," + "shopPriceRandomPercent:" + ShopPriceRandomPercent + "," @@ -213,6 +258,10 @@ public sealed partial class GlobalConfig : Luban.BeanBase + "projectileHeightOffset:" + ProjectileHeightOffset + "," + "projectileSpeed:" + ProjectileSpeed + "," + "projectileEntity:" + ProjectileEntity + "," + + "damageTextYOffset:" + DamageTextYOffset + "," + + "entityAsyncTimeout:" + EntityAsyncTimeout + "," + + "enemyBodyRadius:" + EnemyBodyRadius + "," + + "enemySeparationIterations:" + EnemySeparationIterations + "," + "}"; } } diff --git a/Assets/GameMain/Scripts/Base/Gen/TbGlobalConfig.cs b/Assets/GameMain/Scripts/Base/Gen/TbGlobalConfig.cs index 4c6b621..20f6ffd 100644 --- a/Assets/GameMain/Scripts/Base/Gen/TbGlobalConfig.cs +++ b/Assets/GameMain/Scripts/Base/Gen/TbGlobalConfig.cs @@ -43,6 +43,18 @@ public partial class TbGlobalConfig /// public string PlayerEntity => _data.PlayerEntity; /// + /// 自动收集速度 + /// + public int AbsorbSpeed => _data.AbsorbSpeed; + /// + /// 自动收集距离 + /// + public int CollectDistance => _data.CollectDistance; + /// + /// 默认吸收距离 + /// + public int DefaultAbsorbRange => _data.DefaultAbsorbRange; + /// /// 武器回收价比例 /// public int WeaponRecycleRate => _data.WeaponRecycleRate; @@ -130,6 +142,22 @@ public partial class TbGlobalConfig /// 投射物实体 /// public string ProjectileEntity => _data.ProjectileEntity; + /// + /// 伤害数字高度偏移 + /// + public int DamageTextYOffset => _data.DamageTextYOffset; + /// + /// 实体生成超时时间 + /// + public int EntityAsyncTimeout => _data.EntityAsyncTimeout; + /// + /// 敌人碰撞半径 + /// + public int EnemyBodyRadius => _data.EnemyBodyRadius; + /// + /// 敌人分离迭代次数 + /// + public int EnemySeparationIterations => _data.EnemySeparationIterations; public void ResolveRef(Tables tables) { diff --git a/Assets/GameMain/Scripts/Procedure/Game/GameStateBase.cs b/Assets/GameMain/Scripts/Procedure/Game/GameStateBase.cs index ae91c3b..21cf2d4 100644 --- a/Assets/GameMain/Scripts/Procedure/Game/GameStateBase.cs +++ b/Assets/GameMain/Scripts/Procedure/Game/GameStateBase.cs @@ -1,9 +1,3 @@ -using System.Collections; -using System.Collections.Generic; -using GameFramework.Fsm; -using GameFramework.Procedure; -using UnityEngine; - namespace SepCore.Procedure { public abstract class GameStateBase diff --git a/Assets/GameMain/Scripts/Runtime/Components/AbsorbComponent.cs b/Assets/GameMain/Scripts/Runtime/Components/AbsorbComponent.cs index ee88a85..8ca25ae 100644 --- a/Assets/GameMain/Scripts/Runtime/Components/AbsorbComponent.cs +++ b/Assets/GameMain/Scripts/Runtime/Components/AbsorbComponent.cs @@ -7,11 +7,7 @@ namespace SepCore.Components { public class AbsorbComponent : MonoBehaviour { - [SerializeField] private float _absorbRangeBase = 3f; - [SerializeField] private float _absorbSpeed = 15f; - [SerializeField] private float _collectDistance = 0.35f; [SerializeField] private int _maxDetectCount = 64; - [SerializeField] private LayerMask _detectLayerMask = ~0; private Player _player; private Transform _target; @@ -21,38 +17,34 @@ namespace SepCore.Components private StatProperty _absorbRangeStat; private Action _absorbRangeStatCallback; - private float AbsorbRange => Mathf.Max(0f, (_absorbRangeBase + _absorbRangeStat.Value) * _absorbRangeStat.Percent); + private float AbsorbRangeBase => GameEntry.Luban.Global.DefaultAbsorbRange / 1000f; + private float AbsorbSpeed => GameEntry.Luban.Global.AbsorbSpeed / 1000f; + private float CollectDistance => GameEntry.Luban.Global.CollectDistance / 1000f; - public void OnInit(Player player, StatComponent statComponent = null) + private float AbsorbRange => Mathf.Max(0f, (AbsorbRangeBase + _absorbRangeStat.Value) * _absorbRangeStat.Percent); + + public void OnInit(Player player, StatComponent statComponent) { _player = player; - _target = player != null ? player.CachedTransform : null; + _target = player.CachedTransform; _statComponent = statComponent; _buffer = new Collider[Mathf.Max(1, _maxDetectCount)]; - if (_statComponent != null) - { - _absorbRangeStat = _statComponent.GetStat(StatType.AbsorbRange); - _absorbRangeStatCallback = (modifier, isApply) => - _statComponent.UpdateStat(_absorbRangeStat, modifier, isApply); - _statComponent.Subscribe(StatType.AbsorbRange, _absorbRangeStatCallback); - } - else - { - _absorbRangeStat = new StatProperty(); - } + _absorbRangeStat = _statComponent.GetStat(StatType.AbsorbRange); + _absorbRangeStatCallback = (modifier, isApply) => + _statComponent.UpdateStat(_absorbRangeStat, modifier, isApply); + _statComponent.Subscribe(StatType.AbsorbRange, _absorbRangeStatCallback); } public void OnUpdate(float elapseSeconds, float realElapseSeconds) { - if (_player == null || !_player.Available || _target == null || _buffer == null) return; + if (!_player.Available) return; int hitCount = Physics.OverlapSphereNonAlloc( _target.position, AbsorbRange, _buffer, - _detectLayerMask, - QueryTriggerInteraction.Collide + Constant.Layer.DropLayerMask ); for (int i = 0; i < hitCount; i++) @@ -64,7 +56,7 @@ namespace SepCore.Components if (coin != null && coin.Available) { AbsorbDrop(coin.CachedTransform, elapseSeconds); - coin.TryCollect(_player, _collectDistance); + coin.TryCollect(_player, CollectDistance); continue; } @@ -72,7 +64,7 @@ namespace SepCore.Components if (exp != null && exp.Available) { AbsorbDrop(exp.CachedTransform, elapseSeconds); - exp.TryCollect(_player, _collectDistance); + exp.TryCollect(_player, CollectDistance); } } } @@ -93,8 +85,7 @@ namespace SepCore.Components private void AbsorbDrop(Transform drop, float deltaTime) { - if (drop == null || _target == null) return; - drop.position = Vector3.MoveTowards(drop.position, _target.position, _absorbSpeed * deltaTime); + drop.position = Vector3.MoveTowards(drop.position, _target.position, AbsorbSpeed * deltaTime); } } } diff --git a/Assets/GameMain/Scripts/Runtime/Components/AttackComponent.cs b/Assets/GameMain/Scripts/Runtime/Components/AttackComponent.cs deleted file mode 100644 index 05d51ff..0000000 --- a/Assets/GameMain/Scripts/Runtime/Components/AttackComponent.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using SepCore.Entity; -using UnityEngine; - -namespace SepCore.Components -{ - public class AttackComponent : MonoBehaviour - { - private bool _isAttacking = false; - private List _weapons; - - public void SetIsAttacking(bool isAttacking) => _isAttacking = isAttacking; - - public void OnInit() - { - } - - public void OnUpdate(float elapsedTime, float realElapseSeconds) - { - - } - } -} \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Runtime/Components/AttackComponent.cs.meta b/Assets/GameMain/Scripts/Runtime/Components/AttackComponent.cs.meta deleted file mode 100644 index 76a3a6d..0000000 --- a/Assets/GameMain/Scripts/Runtime/Components/AttackComponent.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: a2b35b5ae4185cb4cab2d84b0569db3d -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Runtime/Components/BackpackComponent.cs b/Assets/GameMain/Scripts/Runtime/Components/BackpackComponent.cs index 89a6a78..826e8a3 100644 --- a/Assets/GameMain/Scripts/Runtime/Components/BackpackComponent.cs +++ b/Assets/GameMain/Scripts/Runtime/Components/BackpackComponent.cs @@ -94,7 +94,6 @@ namespace SepCore.Components public void SetWeaponState(bool state) { - if (_weapons == null) return; foreach (var weapon in _weapons) { weapon.SetEnabled(state); diff --git a/Assets/GameMain/Scripts/Runtime/Components/HealthComponent.cs b/Assets/GameMain/Scripts/Runtime/Components/HealthComponent.cs index 6363b04..39adc68 100644 --- a/Assets/GameMain/Scripts/Runtime/Components/HealthComponent.cs +++ b/Assets/GameMain/Scripts/Runtime/Components/HealthComponent.cs @@ -43,8 +43,6 @@ namespace SepCore.Components { _maxHealthBase = maxHealth; _currentHealth = maxHealth; - _statComponent = statComponent; - _statComponent = statComponent; if (_statComponent != null) { @@ -98,7 +96,8 @@ namespace SepCore.Components { if (_isDead) return; - if (_dodgeStat.Value > UnityEngine.Random.value) + float maxDodge = GameEntry.Luban.Global.MaxDodgeRate / 1000f; + if (UnityEngine.Random.value < Mathf.Clamp(_dodgeStat.Value, 0f, maxDodge)) { damage = 0; } @@ -108,6 +107,8 @@ namespace SepCore.Components float defensePercent = _defenseStat.Percent; damage -= defenseValue; damage = Mathf.CeilToInt(damage / defensePercent); + int minDamage = GameEntry.Luban.Global.MinDamage; + if (damage < minDamage) damage = minDamage; } _currentHealth -= damage; @@ -118,8 +119,9 @@ namespace SepCore.Components private void CauseDamage(int damage) { - if (damage <= 0 || GameEntry.DamageText == null) return; - GameEntry.DamageText.ShowDamage(transform.position + Vector3.up * 1.1f, damage); + if (damage <= 0) return; + float yOffset = GameEntry.Luban.Global.DamageTextYOffset / 1000f; + GameEntry.DamageText.ShowDamage(transform.position + Vector3.up * yOffset, damage); } } } diff --git a/Assets/GameMain/Scripts/Runtime/Components/InputComponent.cs b/Assets/GameMain/Scripts/Runtime/Components/InputComponent.cs index e453d14..6d760b5 100644 --- a/Assets/GameMain/Scripts/Runtime/Components/InputComponent.cs +++ b/Assets/GameMain/Scripts/Runtime/Components/InputComponent.cs @@ -15,9 +15,6 @@ namespace SepCore.Components GameEntry.InputModule.RegisterListener(InputActionId.Move, OnMove); } - public void OnUpdate(float elapseSeconds, float realElapseSeconds) - { - } public void OnReset() { diff --git a/Assets/GameMain/Scripts/Runtime/Components/MovementComponent.cs b/Assets/GameMain/Scripts/Runtime/Components/MovementComponent.cs index dfafd00..862ec88 100644 --- a/Assets/GameMain/Scripts/Runtime/Components/MovementComponent.cs +++ b/Assets/GameMain/Scripts/Runtime/Components/MovementComponent.cs @@ -13,8 +13,8 @@ namespace SepCore.Components [SerializeField] private Vector3 _direction; [SerializeField] private Transform _cachedTransform; [SerializeField] private bool _avoidEnemyOverlap; - [SerializeField] private float _enemyBodyRadius = 0.45f; - [SerializeField] private int _separationIterations = 2; + [SerializeField] private float _enemyBodyRadius; + [SerializeField] private int _separationIterations; public float Speed => (_speedBase + _movementStat.Value) * _movementStat.Percent; public bool AvoidEnemyOverlap => _avoidEnemyOverlap; @@ -26,19 +26,24 @@ namespace SepCore.Components [SerializeField] private float _speedBase; private StatComponent _statComponent; + private Player _player; + private EnemyBase _enemy; private StatProperty _movementStat; private Action _movementStatCallback; public void OnInit(float speed, Transform target, StatComponent statComponent = null, - bool avoidEnemyOverlap = false, float enemyBodyRadius = 0.45f, int separationIterations = 2) + bool avoidEnemyOverlap = false, float? enemyBodyRadius = null, int? separationIterations = null) { _speedBase = speed; _cachedTransform = target; _direction = Vector3.forward; _avoidEnemyOverlap = avoidEnemyOverlap; - _enemyBodyRadius = Mathf.Max(0.01f, enemyBodyRadius); - _separationIterations = Mathf.Max(1, separationIterations); + _enemyBodyRadius = Mathf.Max(0.01f, enemyBodyRadius ?? (GameEntry.Luban.Global.EnemyBodyRadius / 1000f)); + _separationIterations = Mathf.Max(1, separationIterations ?? GameEntry.Luban.Global.EnemySeparationIterations); + + TryGetComponent(out _player); + TryGetComponent(out _enemy); _statComponent = statComponent; if (_statComponent != null) @@ -73,8 +78,10 @@ namespace SepCore.Components _direction = Vector3.zero; _isMoving = false; _avoidEnemyOverlap = false; - _enemyBodyRadius = 0.45f; - _separationIterations = 2; + _enemyBodyRadius = GameEntry.Luban.Global.EnemyBodyRadius / 1000f; + _separationIterations = GameEntry.Luban.Global.EnemySeparationIterations; + _player = null; + _enemy = null; if (_statComponent != null) { @@ -86,8 +93,7 @@ namespace SepCore.Components if (transformToUnregister != null) { - var simulationWorld = GameEntry.SimulationWorld; - simulationWorld?.UnregisterPlayerMovement(transformToUnregister); + GameEntry.SimulationWorld.UnregisterPlayerMovement(transformToUnregister); } } @@ -112,12 +118,6 @@ namespace SepCore.Components return; } - SimulationWorld simulationWorld = GameEntry.SimulationWorld; - if (simulationWorld == null) - { - return; - } - Vector3 direction = _direction; direction.y = 0f; if (direction.sqrMagnitude > Mathf.Epsilon) @@ -125,15 +125,15 @@ namespace SepCore.Components direction.Normalize(); } - if (TryGetComponent(out Player _)) + if (_player != null) { - simulationWorld.SyncPlayerMovementInput(_cachedTransform, _isMoving, direction, Speed); + GameEntry.SimulationWorld.SyncPlayerMovementInput(_cachedTransform, _isMoving, direction, Speed); return; } - if (TryGetComponent(out EnemyBase enemy)) + if (_enemy != null) { - simulationWorld.SyncEnemyMovementInput(enemy.Id, _isMoving, direction, Speed, + GameEntry.SimulationWorld.SyncEnemyMovementInput(_enemy.Id, _isMoving, direction, Speed, _avoidEnemyOverlap, _enemyBodyRadius, _separationIterations); } } diff --git a/Assets/GameMain/Scripts/Runtime/Components/StatComponent.cs b/Assets/GameMain/Scripts/Runtime/Components/StatComponent.cs index 26a4f37..82f522b 100644 --- a/Assets/GameMain/Scripts/Runtime/Components/StatComponent.cs +++ b/Assets/GameMain/Scripts/Runtime/Components/StatComponent.cs @@ -7,13 +7,11 @@ namespace SepCore.Components { public class StatComponent : MonoBehaviour { - private Dictionary> _statModifiers; - private Dictionary> _statModifierCallbacks; + private readonly Dictionary> _statModifiers = new(); + private readonly Dictionary> _statModifierCallbacks = new(); public void OnInit() { - _statModifiers = new Dictionary>(); - _statModifierCallbacks = new Dictionary>(); } public void OnReset() @@ -26,13 +24,10 @@ namespace SepCore.Components { if (!_statModifiers.TryGetValue(modifier.StatType, out var modifiers)) { - _statModifiers.Add(modifier.StatType, modifiers = new List()); - modifiers.Add(modifier); - } - else - { - modifiers.Add(modifier); + modifiers = new List(); + _statModifiers.Add(modifier.StatType, modifiers); } + modifiers.Add(modifier); if (_statModifierCallbacks.TryGetValue(modifier.StatType, out var callbacks)) callbacks?.Invoke(modifier, true); @@ -42,7 +37,7 @@ namespace SepCore.Components { if (_statModifiers.TryGetValue(modifier.StatType, out var modifiers)) { - _statModifiers[modifier.StatType].Remove(modifier); + modifiers.Remove(modifier); } if (_statModifierCallbacks.TryGetValue(modifier.StatType, out var callbacks)) diff --git a/Assets/GameMain/Scripts/Runtime/EntityLogic/Player.cs b/Assets/GameMain/Scripts/Runtime/EntityLogic/Player.cs index 8d0d705..1fcb494 100644 --- a/Assets/GameMain/Scripts/Runtime/EntityLogic/Player.cs +++ b/Assets/GameMain/Scripts/Runtime/EntityLogic/Player.cs @@ -64,7 +64,7 @@ namespace SepCore.Entity get => _exp; set { - if (_expRequires != null && _currentLevel + 1 < _expRequires.Length && value >= _expRequires[_currentLevel + 1]) + if (_currentLevel + 1 < _expRequires.Length && value >= _expRequires[_currentLevel + 1]) { value -= _expRequires[_currentLevel + 1]; _currentLevel++; @@ -72,9 +72,9 @@ namespace SepCore.Entity GameEntry.Event.Fire(this, PlayerLevelUpEventArgs.Create()); } - int requiredExp = _expRequires != null && _currentLevel + 1 < _expRequires.Length + int requiredExp = _currentLevel + 1 < _expRequires.Length ? _expRequires[_currentLevel + 1] - : 100; + : _expRequires[^1]; GameEntry.Event.Fire(this, PlayerExpChangeEventArgs.Create(value, requiredExp)); _exp = value; } @@ -84,24 +84,25 @@ namespace SepCore.Entity public int PendingLevelPoints { get; set; } - public IReadOnlyList Weapons => _backpackComponent != null ? _backpackComponent.Weapons : null; - public IReadOnlyList Props => _backpackComponent != null ? _backpackComponent.Props : null; - public int WeaponCapacity => _backpackComponent != null ? _backpackComponent.WeaponCapacity : 0; + public IReadOnlyList Weapons => _backpackComponent.Weapons; + public IReadOnlyList Props => _backpackComponent.Props; + public int WeaponCapacity => _backpackComponent.WeaponCapacity; public bool AddProp(PropItem prop) { - if (prop == null || _backpackComponent == null) return false; + if (prop == null) return false; return _backpackComponent.AttachProp(prop); } - public async UniTask AddWeaponAsync(WeaponData weaponData, float timeout = 30f) + public async UniTask AddWeaponAsync(WeaponData weaponData, float? timeout = null) { - if (weaponData == null || _backpackComponent == null) + if (weaponData == null) { return null; } - WeaponBase weapon = await GameEntry.Entity.ShowWeaponAsync(weaponData, timeout); + float actualTimeout = timeout ?? (GameEntry.Luban.Global.EntityAsyncTimeout / 1000f); + WeaponBase weapon = await GameEntry.Entity.ShowWeaponAsync(weaponData, actualTimeout); if (weapon == null) { return null; @@ -118,7 +119,7 @@ namespace SepCore.Entity public bool RemoveWeapon(WeaponBase weapon) { - if (weapon == null || _backpackComponent == null) + if (weapon == null) { return false; } @@ -150,7 +151,7 @@ namespace SepCore.Entity public void SetWeaponEnabled(bool enabled) { _weaponEnabled = enabled; - _backpackComponent?.SetWeaponState(_enable && _weaponEnabled); + _backpackComponent.SetWeaponState(_enable && _weaponEnabled); } public void InitRole(RoleConfig role, LevelExperienceConfig expConfig) @@ -168,22 +169,18 @@ namespace SepCore.Entity AddWeaponAsync(EntityDataFactory.Create(GameEntry.Entity.NextId(), WeaponType.Knife, Id, _playerData.Camp)).Forget(); // StatComponent - if (role.Modifiers != null) + foreach (var modifier in role.Modifiers) { - foreach (var modifier in role.Modifiers) - { - _statComponent.AddModifier(modifier); - } + _statComponent.AddModifier(modifier); } - _expRequires = expConfig != null ? expConfig.ToTargetLevelArray() : null; + _expRequires = expConfig.ToTargetLevelArray(); // Fire Events GameEntry.Event.Fire(this, PlayerHealthChangeEventArgs.Create(0, _healthComponent.CurrentHealth, _healthComponent.MaxHealth)); - int initialTargetExp = _expRequires != null && _expRequires.Length > 1 ? _expRequires[1] : 100; - GameEntry.Event.Fire(this, PlayerExpChangeEventArgs.Create(0, initialTargetExp)); + GameEntry.Event.Fire(this, PlayerExpChangeEventArgs.Create(0, _expRequires[1])); GameEntry.Event.Fire(this, PlayerCoinChangeEventArgs.Create(Coin, Coin)); } @@ -203,13 +200,7 @@ namespace SepCore.Entity _movementComponent = GetComponent(); _absorbComponent = GetComponent(); _statComponent = GetComponent(); - _healthComponent = GetComponent(); _backpackComponent = GetComponent(); - - if (_absorbComponent == null) - { - _absorbComponent = gameObject.AddComponent(); - } } protected override void OnShow(object userData) @@ -235,8 +226,6 @@ namespace SepCore.Entity { base.OnUpdate(elapseSeconds, realElapseSeconds); - _inputComponent.OnUpdate(elapseSeconds, realElapseSeconds); - _movementComponent.SetDirection(_inputComponent.Direction); _absorbComponent.OnUpdate(elapseSeconds, realElapseSeconds); } diff --git a/数据表/Datas/GlobalConfig_全局配置.xlsx b/数据表/Datas/GlobalConfig_全局配置.xlsx index 3c162060be192c09d59323065528f9883eb372f3..0e73089bd6577f20819677d2fd6211eb5ae25eb9 100644 GIT binary patch delta 7184 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