- 调整项目结构以适应打包需求
- 添加控制台便于在游戏运行过程中调试各项功能 - 添加 LevelRarity 表,增加了随关卡数动态调整的商品道具品质刷新概率
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6befdfdd5f3e430a864a90bf445000e8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,67 @@
|
||||
using System.Collections.Generic;
|
||||
using GameFramework.ObjectPool;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace CustomComponent
|
||||
{
|
||||
public class DamageTextComponent : GameFrameworkComponent
|
||||
{
|
||||
[SerializeField] private int _instancePoolCapacity = 32;
|
||||
|
||||
[SerializeField] private string _poolName = "DamageTextItem";
|
||||
|
||||
[SerializeField] private RectTransform _instanceRoot;
|
||||
|
||||
[SerializeField] private Canvas _canvas;
|
||||
|
||||
[SerializeField] private GameObject _damageTextItemPrefab;
|
||||
|
||||
private IObjectPool<DamageTextItemObject> _damageTextItemPool;
|
||||
|
||||
private readonly List<DamageTextItem> _activeDamageTextItems = new();
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_damageTextItemPool =
|
||||
GameEntry.ObjectPool.CreateSingleSpawnObjectPool<DamageTextItemObject>(_poolName,
|
||||
_instancePoolCapacity);
|
||||
}
|
||||
|
||||
public void ShowDamage(Vector3 worldPosition, int damage)
|
||||
{
|
||||
if (damage <= 0 || _damageTextItemPool == null || _canvas == null) return;
|
||||
|
||||
DamageTextItem item = CreateDamageTextItem();
|
||||
if (item == null) return;
|
||||
|
||||
item.Show(worldPosition, damage, _canvas, OnItemComplete);
|
||||
_activeDamageTextItems.Add(item);
|
||||
}
|
||||
|
||||
private DamageTextItem CreateDamageTextItem()
|
||||
{
|
||||
DamageTextItemObject itemObject = _damageTextItemPool.Spawn();
|
||||
if (itemObject != null)
|
||||
{
|
||||
return (DamageTextItem)itemObject.Target;
|
||||
}
|
||||
|
||||
GameObject itemGo = Instantiate(_damageTextItemPrefab, _instanceRoot, false);
|
||||
|
||||
DamageTextItem item = itemGo.GetComponent<DamageTextItem>();
|
||||
_damageTextItemPool.Register(DamageTextItemObject.Create(item), true);
|
||||
return item;
|
||||
}
|
||||
|
||||
private void OnItemComplete(DamageTextItem item)
|
||||
{
|
||||
if (item == null) return;
|
||||
item.ResetItem();
|
||||
_activeDamageTextItems.Remove(item);
|
||||
_damageTextItemPool.Unspawn(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 366eb88b821a4048891b7fb01a1b8d42
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using DG.Tweening;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace CustomComponent
|
||||
{
|
||||
public class DamageTextItem : MonoBehaviour
|
||||
{
|
||||
private const float RiseDistance = 80f;
|
||||
private const float RiseDuration = 0.55f;
|
||||
private const float FadeDelay = 0.2f;
|
||||
private const float FadeDuration = 0.35f;
|
||||
private static readonly Color NormalColor = new Color(1f, 0.32f, 0.23f, 1f);
|
||||
|
||||
[SerializeField] private RectTransform _cachedTransform;
|
||||
|
||||
[SerializeField] private CanvasGroup _canvasGroup;
|
||||
|
||||
[SerializeField] private TMP_Text _text;
|
||||
|
||||
private Sequence _sequence;
|
||||
private Action<DamageTextItem> _onComplete;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (_cachedTransform == null) _cachedTransform = GetComponent<RectTransform>();
|
||||
if (_canvasGroup == null) _canvasGroup = GetComponent<CanvasGroup>();
|
||||
if (_text == null) _text = GetComponent<TMP_Text>();
|
||||
}
|
||||
|
||||
public void Show(Vector3 worldPosition, int damage, Canvas canvas, Action<DamageTextItem> onComplete)
|
||||
{
|
||||
if (canvas == null || GameEntry.Scene.MainCamera == null) return;
|
||||
|
||||
_onComplete = onComplete;
|
||||
gameObject.SetActive(true);
|
||||
KillSequence();
|
||||
|
||||
_canvasGroup.alpha = 1f;
|
||||
_cachedTransform.localScale = Vector3.one;
|
||||
_text.color = NormalColor;
|
||||
_text.text = damage.ToString();
|
||||
|
||||
Vector3 screenPos = GameEntry.Scene.MainCamera.WorldToScreenPoint(worldPosition);
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform)canvas.transform, screenPos,
|
||||
canvas.worldCamera, out Vector2 localPos);
|
||||
localPos.x += UnityEngine.Random.Range(-24f, 24f);
|
||||
_cachedTransform.anchoredPosition = localPos;
|
||||
|
||||
_sequence = DOTween.Sequence();
|
||||
_sequence.Append(_cachedTransform.DOAnchorPosY(localPos.y + RiseDistance, RiseDuration)
|
||||
.SetEase(Ease.OutQuad));
|
||||
_sequence.Join(_cachedTransform.DOPunchScale(new Vector3(0.15f, 0.15f, 0f), 0.18f, 6, 0.75f));
|
||||
_sequence.Join(_canvasGroup.DOFade(0f, FadeDuration).SetDelay(FadeDelay));
|
||||
_sequence.OnComplete(() => _onComplete?.Invoke(this));
|
||||
}
|
||||
|
||||
public void ResetItem()
|
||||
{
|
||||
KillSequence();
|
||||
_onComplete = null;
|
||||
_canvasGroup.alpha = 1f;
|
||||
_text.text = string.Empty;
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
KillSequence();
|
||||
}
|
||||
|
||||
private void KillSequence()
|
||||
{
|
||||
if (_sequence == null) return;
|
||||
_sequence.Kill();
|
||||
_sequence = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 501e678f9cb14af6ac45e8d2050388d3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,23 @@
|
||||
using GameFramework;
|
||||
using GameFramework.ObjectPool;
|
||||
using UnityEngine;
|
||||
|
||||
namespace CustomComponent
|
||||
{
|
||||
public class DamageTextItemObject : ObjectBase
|
||||
{
|
||||
public static DamageTextItemObject Create(object target)
|
||||
{
|
||||
DamageTextItemObject itemObject = ReferencePool.Acquire<DamageTextItemObject>();
|
||||
itemObject.Initialize(target);
|
||||
return itemObject;
|
||||
}
|
||||
|
||||
protected override void Release(bool isShutdown)
|
||||
{
|
||||
DamageTextItem item = (DamageTextItem)Target;
|
||||
if (item == null) return;
|
||||
Object.Destroy(item.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4aba1230dece448eba1fa7579ca7a13d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a11ef2e359b24bfdb6c9120cc2ec81b8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,349 @@
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||
using System;
|
||||
using System.Linq;
|
||||
using DataTable;
|
||||
using Definition.DataStruct;
|
||||
using Entity;
|
||||
using Game.Utility;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
using UnityEngine.InputSystem;
|
||||
#endif
|
||||
|
||||
namespace CustomComponent
|
||||
{
|
||||
public class RuntimeDebugPanelComponent : GameFrameworkComponent
|
||||
{
|
||||
private const float MinSpawnRate = 0.1f;
|
||||
private const float CornerTapWindow = 0.6f;
|
||||
private const int RequiredCornerTapCount = 3;
|
||||
|
||||
private Rect _windowRect = new Rect(20f, 60f, 460f, 620f);
|
||||
private bool _isPanelVisible;
|
||||
private int _windowId;
|
||||
|
||||
private string _searchText = string.Empty;
|
||||
private int _selectedIndex;
|
||||
private int _addCount = 1;
|
||||
private Vector2 _buffScroll;
|
||||
|
||||
private float _spawnRateScaleInput = 1f;
|
||||
private float _extendDurationSeconds = 30f;
|
||||
|
||||
private DRProp[] _allProps = Array.Empty<DRProp>();
|
||||
private DRProp[] _filteredProps = Array.Empty<DRProp>();
|
||||
private string[] _displayNames = Array.Empty<string>();
|
||||
|
||||
private int _cornerTapCount;
|
||||
private float _lastCornerTapTime = -10f;
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
_windowId = GetInstanceID();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (IsTogglePressed())
|
||||
{
|
||||
_isPanelVisible = !_isPanelVisible;
|
||||
}
|
||||
|
||||
HandleCornerTapGesture();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
DrawToggleButton();
|
||||
if (!_isPanelVisible) return;
|
||||
|
||||
_windowRect = GUI.Window(_windowId, _windowRect, DrawWindow, "Runtime Debug Panel");
|
||||
}
|
||||
|
||||
private void DrawToggleButton()
|
||||
{
|
||||
const float width = 64f;
|
||||
const float height = 30f;
|
||||
Rect buttonRect = new Rect(Screen.width - width - 12f, 10f, width, height);
|
||||
if (GUI.Button(buttonRect, "DEBUG"))
|
||||
{
|
||||
_isPanelVisible = !_isPanelVisible;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawWindow(int windowId)
|
||||
{
|
||||
EnsurePropList();
|
||||
|
||||
GUILayout.BeginVertical();
|
||||
|
||||
DrawBuffSection();
|
||||
|
||||
GUILayout.Space(8f);
|
||||
GUILayout.Label(string.Empty, GUI.skin.horizontalSlider);
|
||||
GUILayout.Space(8f);
|
||||
|
||||
DrawBattleSection();
|
||||
|
||||
GUILayout.Space(8f);
|
||||
GUILayout.Label("Tips: press `F8` or tap top-left corner 3 times to toggle.", GUILayout.Height(20f));
|
||||
|
||||
GUILayout.EndVertical();
|
||||
GUI.DragWindow(new Rect(0, 0, 10000, 22));
|
||||
}
|
||||
|
||||
private void DrawBuffSection()
|
||||
{
|
||||
GUILayout.Label("Buff Debug");
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label("Search", GUILayout.Width(52f));
|
||||
_searchText = GUILayout.TextField(_searchText);
|
||||
if (GUILayout.Button("Refresh", GUILayout.Width(80f)))
|
||||
{
|
||||
EnsurePropList(true);
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
ApplyFilter(_searchText);
|
||||
if (_displayNames.Length == 0)
|
||||
{
|
||||
GUILayout.Label("No Buff data. Enter battle and try again.");
|
||||
return;
|
||||
}
|
||||
|
||||
_selectedIndex = Mathf.Clamp(_selectedIndex, 0, _displayNames.Length - 1);
|
||||
_buffScroll = GUILayout.BeginScrollView(_buffScroll, GUILayout.Height(120f));
|
||||
_selectedIndex = GUILayout.SelectionGrid(_selectedIndex, _displayNames, 1);
|
||||
GUILayout.EndScrollView();
|
||||
|
||||
DRProp selectedProp = GetSelectedProp();
|
||||
if (selectedProp == null) return;
|
||||
|
||||
GUILayout.Label($"Selected: {selectedProp.Title} ({selectedProp.Rarity})");
|
||||
GUILayout.Label(ItemDescUtility.CreatePropDescription(selectedProp.Modifiers), GUILayout.Height(40f));
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label("Count", GUILayout.Width(52f));
|
||||
string addCountText = GUILayout.TextField(_addCount.ToString(), GUILayout.Width(60f));
|
||||
if (!int.TryParse(addCountText, out _addCount)) _addCount = 1;
|
||||
_addCount = Mathf.Clamp(_addCount, 1, 99);
|
||||
|
||||
if (GUILayout.Button("Add Buff To Player", GUILayout.Height(24f)))
|
||||
{
|
||||
AddSelectedBuffToPlayer(selectedProp, _addCount);
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void DrawBattleSection()
|
||||
{
|
||||
GUILayout.Label("Battle Debug");
|
||||
EnemyManagerComponent enemyManager = GameEntry.EnemyManager;
|
||||
Player player = FindPlayer();
|
||||
|
||||
if (enemyManager == null)
|
||||
{
|
||||
GUILayout.Label("EnemyManager unavailable.");
|
||||
return;
|
||||
}
|
||||
|
||||
GUILayout.Label($"Spawn Rate: {enemyManager.SpawnRateScale:F2}");
|
||||
GUILayout.Label($"Battle Time: {enemyManager.ElapsedBattleTime:F1}s / {enemyManager.BattleDuration:F1}s");
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label("Rate", GUILayout.Width(52f));
|
||||
string rateText = GUILayout.TextField(_spawnRateScaleInput.ToString("F2"), GUILayout.Width(60f));
|
||||
if (float.TryParse(rateText, out float parsedRate))
|
||||
{
|
||||
_spawnRateScaleInput = Mathf.Clamp(parsedRate, MinSpawnRate, 50f);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Apply", GUILayout.Width(70f)))
|
||||
{
|
||||
enemyManager.SetSpawnRateScale(_spawnRateScaleInput);
|
||||
}
|
||||
if (GUILayout.Button("x0.5", GUILayout.Width(60f)))
|
||||
{
|
||||
_spawnRateScaleInput = Mathf.Max(MinSpawnRate, enemyManager.SpawnRateScale * 0.5f);
|
||||
enemyManager.SetSpawnRateScale(_spawnRateScaleInput);
|
||||
}
|
||||
if (GUILayout.Button("x2", GUILayout.Width(60f)))
|
||||
{
|
||||
_spawnRateScaleInput = enemyManager.SpawnRateScale * 2f;
|
||||
enemyManager.SetSpawnRateScale(_spawnRateScaleInput);
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label("Add Sec", GUILayout.Width(52f));
|
||||
string durationText = GUILayout.TextField(_extendDurationSeconds.ToString("F0"), GUILayout.Width(60f));
|
||||
if (float.TryParse(durationText, out float parsedDuration))
|
||||
{
|
||||
_extendDurationSeconds = Mathf.Clamp(parsedDuration, 1f, 3600f);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Extend Battle", GUILayout.Height(24f)))
|
||||
{
|
||||
enemyManager.AddBattleDuration(_extendDurationSeconds);
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.Label($"Player Weapon: {(player == null ? "Player not found" : (player.WeaponEnabled ? "Enabled" : "Disabled"))}");
|
||||
GUILayout.BeginHorizontal();
|
||||
GUI.enabled = player != null;
|
||||
if (GUILayout.Button("Disable Weapons", GUILayout.Height(24f)))
|
||||
{
|
||||
player.SetWeaponEnabled(false);
|
||||
}
|
||||
if (GUILayout.Button("Enable Weapons", GUILayout.Height(24f)))
|
||||
{
|
||||
player.SetWeaponEnabled(true);
|
||||
}
|
||||
GUI.enabled = true;
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void EnsurePropList(bool force = false)
|
||||
{
|
||||
if (!force && _allProps != null && _allProps.Length > 0) return;
|
||||
|
||||
if (GameEntry.DataTable == null)
|
||||
{
|
||||
_allProps = Array.Empty<DRProp>();
|
||||
_filteredProps = Array.Empty<DRProp>();
|
||||
_displayNames = Array.Empty<string>();
|
||||
return;
|
||||
}
|
||||
|
||||
var table = GameEntry.DataTable.GetDataTable<DRProp>();
|
||||
_allProps = table != null ? table.ToArray() : Array.Empty<DRProp>();
|
||||
_selectedIndex = Mathf.Clamp(_selectedIndex, 0, Mathf.Max(0, _allProps.Length - 1));
|
||||
ApplyFilter(_searchText);
|
||||
}
|
||||
|
||||
private void ApplyFilter(string keyword)
|
||||
{
|
||||
if (_allProps == null || _allProps.Length == 0)
|
||||
{
|
||||
_filteredProps = Array.Empty<DRProp>();
|
||||
_displayNames = Array.Empty<string>();
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(keyword))
|
||||
{
|
||||
_filteredProps = _allProps;
|
||||
}
|
||||
else
|
||||
{
|
||||
string search = keyword.Trim();
|
||||
_filteredProps = _allProps.Where(p =>
|
||||
p != null &&
|
||||
(p.Title?.IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||
p.Id.ToString().Contains(search))).ToArray();
|
||||
}
|
||||
|
||||
_displayNames = _filteredProps.Select(p => $"[{p.Id}] {p.Title} ({p.Rarity})").ToArray();
|
||||
if (_displayNames.Length == 0) _selectedIndex = 0;
|
||||
else _selectedIndex = Mathf.Clamp(_selectedIndex, 0, _displayNames.Length - 1);
|
||||
}
|
||||
|
||||
private DRProp GetSelectedProp()
|
||||
{
|
||||
if (_filteredProps == null || _filteredProps.Length == 0) return null;
|
||||
_selectedIndex = Mathf.Clamp(_selectedIndex, 0, _filteredProps.Length - 1);
|
||||
return _filteredProps[_selectedIndex];
|
||||
}
|
||||
|
||||
private static Player FindPlayer()
|
||||
{
|
||||
return UnityEngine.Object.FindObjectOfType<Player>();
|
||||
}
|
||||
|
||||
private static void AddSelectedBuffToPlayer(DRProp prop, int count)
|
||||
{
|
||||
Player player = FindPlayer();
|
||||
if (player == null || prop == null || prop.Modifiers == null) return;
|
||||
|
||||
int applyCount = Mathf.Clamp(count, 1, 99);
|
||||
for (int i = 0; i < applyCount; i++)
|
||||
{
|
||||
player.AddProp(new PropItem(prop.Modifiers, prop.Rarity, prop.Title, prop.IconAssetName));
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCornerTapGesture()
|
||||
{
|
||||
if (!TryGetTouchReleased(out Vector2 touchPosition)) return;
|
||||
if (touchPosition.x > 80f || touchPosition.y < Screen.height - 80f) return;
|
||||
|
||||
float now = Time.unscaledTime;
|
||||
if (now - _lastCornerTapTime > CornerTapWindow)
|
||||
{
|
||||
_cornerTapCount = 0;
|
||||
}
|
||||
|
||||
_lastCornerTapTime = now;
|
||||
_cornerTapCount++;
|
||||
|
||||
if (_cornerTapCount >= RequiredCornerTapCount)
|
||||
{
|
||||
_cornerTapCount = 0;
|
||||
_isPanelVisible = !_isPanelVisible;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsTogglePressed()
|
||||
{
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
Keyboard keyboard = Keyboard.current;
|
||||
if (keyboard == null) return false;
|
||||
return keyboard.backquoteKey.wasPressedThisFrame || keyboard.f8Key.wasPressedThisFrame;
|
||||
#else
|
||||
return Input.GetKeyDown(KeyCode.BackQuote) || Input.GetKeyDown(KeyCode.F8);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static bool TryGetTouchReleased(out Vector2 touchPosition)
|
||||
{
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
Touchscreen touchscreen = Touchscreen.current;
|
||||
if (touchscreen == null)
|
||||
{
|
||||
touchPosition = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var touch = touchscreen.primaryTouch;
|
||||
if (!touch.press.wasReleasedThisFrame)
|
||||
{
|
||||
touchPosition = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
touchPosition = touch.position.ReadValue();
|
||||
return true;
|
||||
#else
|
||||
if (Input.touchCount <= 0)
|
||||
{
|
||||
touchPosition = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
Touch touch = Input.GetTouch(0);
|
||||
if (touch.phase != TouchPhase.Ended)
|
||||
{
|
||||
touchPosition = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
touchPosition = touch.position;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d8ada5157a04921a6e543a040e57960
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -13,6 +13,8 @@ namespace CustomComponent
|
||||
{
|
||||
public class EnemyManagerComponent : GameFrameworkComponent
|
||||
{
|
||||
private const float MinSpawnRateScale = 0.1f;
|
||||
|
||||
private EntityComponent _entity;
|
||||
|
||||
private List<EntityBase> _enemies;
|
||||
@@ -20,23 +22,35 @@ namespace CustomComponent
|
||||
public List<EntityBase> Enemies => _enemies;
|
||||
|
||||
private float _spawnEnemyTimer;
|
||||
|
||||
private int _spawnEnemyMaxCount = 5000;
|
||||
|
||||
private int _currentEnemyCount;
|
||||
|
||||
private int _spawnDistanceFromPlayer = 20;
|
||||
|
||||
private int _currentSpawnEnemyId;
|
||||
|
||||
private int _currentLevel;
|
||||
|
||||
private float[] _baseSpawnEnemyIntervals;
|
||||
private float[] _spawnEnemyIntervals;
|
||||
private int[] _spawnEnemyIds;
|
||||
private int[] _spawnEnemyCounts;
|
||||
private float _duration;
|
||||
private float _baseDuration;
|
||||
|
||||
private float[] _nextSpawnTimes;
|
||||
private float _spawnRateScale = 1f;
|
||||
|
||||
private Transform _player;
|
||||
|
||||
private GameStateBattle _battle;
|
||||
|
||||
public float SpawnRateScale => _spawnRateScale;
|
||||
public float BattleDuration => _duration;
|
||||
public float ElapsedBattleTime => _spawnEnemyTimer;
|
||||
|
||||
#region FSM
|
||||
|
||||
private void Start()
|
||||
@@ -64,12 +78,14 @@ namespace CustomComponent
|
||||
_currentLevel = level;
|
||||
|
||||
DRLevel levelData = GameEntry.DataTable.GetDataTableRow<DRLevel>(_currentLevel);
|
||||
_spawnEnemyIntervals = levelData.Intervals;
|
||||
_baseSpawnEnemyIntervals = (float[])levelData.Intervals.Clone();
|
||||
_spawnEnemyIntervals = (float[])_baseSpawnEnemyIntervals.Clone();
|
||||
_spawnEnemyIds = levelData.EntityIds;
|
||||
_spawnEnemyCounts = levelData.EntityCounts;
|
||||
_duration = levelData.Duration;
|
||||
_baseDuration = levelData.Duration;
|
||||
_duration = _baseDuration;
|
||||
|
||||
_nextSpawnTimes = (float[])_spawnEnemyIntervals.Clone();
|
||||
SetSpawnRateScale(_spawnRateScale);
|
||||
|
||||
_currentSpawnEnemyId = 0;
|
||||
}
|
||||
@@ -77,10 +93,7 @@ namespace CustomComponent
|
||||
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
{
|
||||
_spawnEnemyTimer += elapseSeconds;
|
||||
// if (_spawnEnemyTimer < _spawnEnemyInterval) return;
|
||||
// SpawnEnemy();
|
||||
// _spawnEnemyTimer = 0;
|
||||
// _spawnEnemyInterval = Mathf.Max(0.1f, _spawnEnemyInterval - _spawnEnemyAccelerate);
|
||||
|
||||
if (_spawnEnemyTimer > _duration)
|
||||
{
|
||||
_battle.LevelOver();
|
||||
@@ -108,9 +121,11 @@ namespace CustomComponent
|
||||
_currentSpawnEnemyId = 0;
|
||||
_currentLevel = 0;
|
||||
|
||||
_baseSpawnEnemyIntervals = null;
|
||||
_spawnEnemyIntervals = null;
|
||||
_spawnEnemyIds = null;
|
||||
_spawnEnemyCounts = null;
|
||||
_baseDuration = 0;
|
||||
_duration = 0;
|
||||
|
||||
_nextSpawnTimes = null;
|
||||
@@ -120,6 +135,57 @@ namespace CustomComponent
|
||||
ClearEnemies();
|
||||
}
|
||||
|
||||
public void SetSpawnRateScale(float scale)
|
||||
{
|
||||
float newScale = Mathf.Max(MinSpawnRateScale, scale);
|
||||
if (_baseSpawnEnemyIntervals == null || _baseSpawnEnemyIntervals.Length == 0)
|
||||
{
|
||||
_spawnRateScale = newScale;
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasRuntimeState = _nextSpawnTimes != null && _spawnEnemyIntervals != null &&
|
||||
_nextSpawnTimes.Length == _baseSpawnEnemyIntervals.Length &&
|
||||
_spawnEnemyIntervals.Length == _baseSpawnEnemyIntervals.Length;
|
||||
float oldScale = _spawnRateScale;
|
||||
_spawnRateScale = newScale;
|
||||
|
||||
if (!hasRuntimeState)
|
||||
{
|
||||
for (int i = 0; i < _baseSpawnEnemyIntervals.Length; i++)
|
||||
{
|
||||
_spawnEnemyIntervals[i] = GetScaledInterval(_baseSpawnEnemyIntervals[i], _spawnRateScale);
|
||||
}
|
||||
|
||||
_nextSpawnTimes = (float[])_spawnEnemyIntervals.Clone();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _baseSpawnEnemyIntervals.Length; i++)
|
||||
{
|
||||
float oldInterval = GetScaledInterval(_baseSpawnEnemyIntervals[i], oldScale);
|
||||
float newInterval = GetScaledInterval(_baseSpawnEnemyIntervals[i], _spawnRateScale);
|
||||
|
||||
float remainTime = Mathf.Max(0f, _nextSpawnTimes[i] - _spawnEnemyTimer);
|
||||
float remainRatio = oldInterval > Mathf.Epsilon ? Mathf.Clamp01(remainTime / oldInterval) : 0f;
|
||||
|
||||
_spawnEnemyIntervals[i] = newInterval;
|
||||
_nextSpawnTimes[i] = _spawnEnemyTimer + newInterval * remainRatio;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddBattleDuration(float seconds)
|
||||
{
|
||||
if (seconds <= 0f) return;
|
||||
_duration += seconds;
|
||||
}
|
||||
|
||||
private static float GetScaledInterval(float baseInterval, float scale)
|
||||
{
|
||||
float safeScale = Mathf.Max(MinSpawnRateScale, scale);
|
||||
return baseInterval / safeScale;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void SpawnEnemy(int entityId)
|
||||
@@ -128,7 +194,7 @@ namespace CustomComponent
|
||||
|
||||
if (_currentEnemyCount >= _spawnEnemyMaxCount) return;
|
||||
int entityPoolId = _currentSpawnEnemyId % _spawnEnemyMaxCount;
|
||||
var enemyData = new EnemyData(entityPoolId, entityId)
|
||||
var enemyData = new EnemyData(entityPoolId, entityId, _currentLevel)
|
||||
{
|
||||
Position = GetRandomPosition()
|
||||
};
|
||||
@@ -187,4 +253,4 @@ namespace CustomComponent
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,14 +17,11 @@ namespace CustomComponent
|
||||
{
|
||||
public class HPBarComponent : GameFrameworkComponent
|
||||
{
|
||||
[FormerlySerializedAs("m_HPBarItemTemplate")] [SerializeField]
|
||||
private HPBarItem _hpBarItemTemplate = null;
|
||||
[SerializeField] private HPBarItem _hpBarItemTemplate = null;
|
||||
|
||||
[FormerlySerializedAs("m_HPBarInstanceRoot")] [SerializeField]
|
||||
private Transform _hpBarInstanceRoot = null;
|
||||
[SerializeField] private Transform _hpBarInstanceRoot = null;
|
||||
|
||||
[FormerlySerializedAs("m_InstancePoolCapacity")] [SerializeField]
|
||||
private int _instancePoolCapacity = 16;
|
||||
[SerializeField] private int _instancePoolCapacity = 16;
|
||||
|
||||
private IObjectPool<HPBarItemObject> _hpBarItemObjectPool = null;
|
||||
private List<HPBarItem> _activeHPBarItems = null;
|
||||
@@ -39,7 +36,8 @@ namespace CustomComponent
|
||||
}
|
||||
|
||||
_cachedCanvas = _hpBarInstanceRoot.GetComponent<Canvas>();
|
||||
_hpBarItemObjectPool = GameEntry.ObjectPool.CreateSingleSpawnObjectPool<HPBarItemObject>("HPBarItem", _instancePoolCapacity);
|
||||
_hpBarItemObjectPool =
|
||||
GameEntry.ObjectPool.CreateSingleSpawnObjectPool<HPBarItemObject>("HPBarItem", _instancePoolCapacity);
|
||||
_activeHPBarItems = new List<HPBarItem>();
|
||||
}
|
||||
|
||||
@@ -124,4 +122,4 @@ namespace CustomComponent
|
||||
return hpBarItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user