maint: added livewatch asset

This commit is contained in:
Chris
2025-08-31 18:14:07 -04:00
parent 7f5d95787b
commit ae2371a6fa
385 changed files with 150792 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fe50439153774769a917b9ef7c3444ed
timeCreated: 1726052848

View File

@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class EconomyConfig : ScriptableObject
{
public Dictionary<MobType, int> MobKillRewards
{
get
{
if (_mobRewardsDict != null)
return _mobRewardsDict;
_mobRewardsDict = new Dictionary<MobType, int>();
foreach (var mobKillReward in _mobKillRewards)
{
_mobRewardsDict.Add(mobKillReward.Type, mobKillReward.GoldReward);
}
return _mobRewardsDict;
}
}
public Dictionary<TowerType, int> TowerBuildCosts
{
get
{
if (_towerCostsDict != null)
return _towerCostsDict;
_towerCostsDict = new Dictionary<TowerType, int>();
foreach (var towerBuildCost in _towerCosts)
{
_towerCostsDict.Add(towerBuildCost.Type, towerBuildCost.GoldCost);
}
return _towerCostsDict;
}
}
public Dictionary<TowerType, int> TowerSellPrices
{
get
{
if (_towerSellPricesDict != null)
return _towerSellPricesDict;
_towerSellPricesDict = new Dictionary<TowerType, int>();
foreach (var towerBuildCost in _towerCosts)
{
_towerSellPricesDict.Add(towerBuildCost.Type, towerBuildCost.GoldSellPrice);
}
return _towerSellPricesDict;
}
}
[SerializeField] private MobRewardSetup[] _mobKillRewards;
[SerializeField] private TowerCostSetup[] _towerCosts;
private Dictionary<MobType, int> _mobRewardsDict;
private Dictionary<TowerType, int> _towerCostsDict;
private Dictionary<TowerType, int> _towerSellPricesDict;
[Serializable]
private class MobRewardSetup
{
public MobType Type;
public int GoldReward;
}
[Serializable]
private class TowerCostSetup
{
public TowerType Type;
public int GoldCost;
public int GoldSellPrice;
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 555126785c4f49a58db7735195a9ca5c
timeCreated: 1726052861
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/Configs/EconomyConfig.cs
uploadId: 770587

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class LevelConfig : ScriptableObject
{
public int MaxHealth = 20;
public int StartGold = 100;
public List<MobWave> Waves = new();
}
[Serializable]
public class MobWave
{
public List<MobSpawn> Spawns = new();
}
[Serializable]
public class MobSpawn
{
public float Delay;
public int MobCount;
public MobMain MobPrefab;
public float SpawnDelayBetween = 0.1f;
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 5acaf173387b48bba941b9b4d523a82e
timeCreated: 1725791173
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/Configs/LevelConfig.cs
uploadId: 770587

View File

@@ -0,0 +1,54 @@
using System;
using UnityEngine;
using UnityEngine.Serialization;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class EnergyManager : MonoBehaviour
{
public event Action EnergyChanged;
public int CurrentEnergy
{
get => _energy;
set
{
if (_energy == value)
return;
_energy = value;
EnergyChanged?.Invoke();
}
}
private int _energy;
private LevelScene _levelScene;
private void Awake()
{
_levelScene = FindObjectOfType<LevelScene>();
}
private void Start()
{
Watch.GetOrAdd("Energy", () => CurrentEnergy)
.SetSortOrder(TD_WatchSortOrder.Energy);
}
private void OnEnable()
{
_levelScene.MobManager.MobDiedByHp += OnMobDiedByHp;
}
public void ResetEnergy()
{
CurrentEnergy = _levelScene.LevelConfig.StartGold;
}
private void OnMobDiedByHp(MobMain mob)
{
var reward = _levelScene.EconomyConfig.MobKillRewards[mob.Type];
CurrentEnergy += reward;
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 356869e3279748738aee19e4491c4c56
timeCreated: 1725817574
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/EnergyManager.cs
uploadId: 770587

View File

@@ -0,0 +1,50 @@
using System;
using UnityEngine;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class GameSpeedManager : MonoBehaviour
{
public event Action SpeedChanged;
public SpeedMode CurrentSpeed
{
get => _currentSpeed;
protected set
{
if (_currentSpeed == value)
return;
_currentSpeed = value;
SpeedChanged?.Invoke();
}
}
private SpeedMode _currentSpeed;
public void SetDefaultSpeed()
{
SetGameSpeed(SpeedMode.x1);
}
public void SetGameSpeed(SpeedMode mode)
{
Time.timeScale = mode switch
{
SpeedMode.x1 => 1,
SpeedMode.x2 => 2,
SpeedMode.x4 => 4,
_ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null)
};
CurrentSpeed = mode;
}
}
public enum SpeedMode
{
x1,
x2,
x4
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 808c7e6ae676460ebcff47f63cd0863a
timeCreated: 1726311529
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/GameSpeedManager.cs
uploadId: 770587

View File

@@ -0,0 +1,59 @@
using System;
using UnityEngine;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class HealthManager : MonoBehaviour
{
public event Action HealthChanged;
public int CurrentHealth
{
get => _health;
set
{
if (_health == value)
return;
_health = value;
HealthChanged?.Invoke();
}
}
public int MaxHealth => _levelScene.LevelConfig.MaxHealth;
private LevelScene _levelScene;
private int _health;
private void Awake()
{
_levelScene = FindObjectOfType<LevelScene>();
}
private void Start()
{
Watch.GetOrAdd("Health", () => CurrentHealth)
.SetSortOrder(TD_WatchSortOrder.Health);
}
private void OnEnable()
{
_levelScene.MobManager.MobReachedFinish += OnMobReachedFinish;
}
private void OnDisable()
{
_levelScene.MobManager.MobReachedFinish -= OnMobReachedFinish;
}
public void ResetHealth()
{
CurrentHealth = MaxHealth;
}
private void OnMobReachedFinish(MobMain mob)
{
CurrentHealth = Mathf.Clamp(CurrentHealth - 1, 0, MaxHealth);
HealthChanged?.Invoke();
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 00c9be1057164002990d550ddbe0bb6d
timeCreated: 1725811243
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/HealthManager.cs
uploadId: 770587

View File

@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class LevelInitialTowerSpawner : MonoBehaviour
{
[SerializeField] private List<TowerSpawnSetup> _towers;
private LevelScene _levelScene;
private void Awake()
{
_levelScene = FindObjectOfType<LevelScene>();
}
public void SpawnAllTowers()
{
foreach (var towerSpawn in _towers)
{
_levelScene.TowerBuildManager.BuildTower(towerSpawn.Slot, towerSpawn.Type, false);
}
}
[Serializable]
public class TowerSpawnSetup
{
public TowerBuildSlot Slot;
public TowerType Type;
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: e02acdc164ea4da29f2d7515d985ef07
timeCreated: 1726310399
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/LevelInitialTowerSpawner.cs
uploadId: 770587

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class LevelScene : MonoBehaviour
{
public PoolManager PoolManager;
public GameSpeedManager SpeedManager;
public MobManager MobManager;
public HealthManager HealthManager;
public WaveManager WaveManager;
public EnergyManager EnergyManager;
public TowerManager TowerManager;
public TowerBuildManager TowerBuildManager;
public LevelInitialTowerSpawner InitialTowerSpawner;
public LevelStateManager LevelStateManager;
public LevelConfig LevelConfig;
public EconomyConfig EconomyConfig;
public MobTriggerZone FinishTriggerZone;
public List<Transform> Waypoints;
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: ae3bf07677c6d72439b069ddc0da2380
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/LevelScene.cs
uploadId: 770587

View File

@@ -0,0 +1,112 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class LevelStateManager : MonoBehaviour
{
public event Action StateChanged;
public LevelStateType CurrentState
{
get => _currentState;
set
{
if (_currentState == value)
return;
_currentState = value;
StateChanged?.Invoke();
}
}
private LevelStateType _currentState;
private LevelScene _levelScene;
private Dictionary<LevelStateType, string> _stateNamesCache = new();
private void Awake()
{
_levelScene = FindObjectOfType<LevelScene>();
}
private void Start()
{
StartLevel();
// Cache is needed to avoid GC from .ToString() every frame
foreach (var stateEnum in Enum.GetValues(typeof(LevelStateType)))
_stateNamesCache.Add((LevelStateType)stateEnum, stateEnum.ToString());
Watch.GetOrAdd("State", () => _stateNamesCache[CurrentState])
.SetSortOrder(TD_WatchSortOrder.State);
}
public void StartLevel()
{
if (CurrentState == LevelStateType.Playing)
return;
CurrentState = LevelStateType.Waiting;
_levelScene.SpeedManager.SetDefaultSpeed();
_levelScene.HealthManager.ResetHealth();
_levelScene.EnergyManager.ResetEnergy();
_levelScene.WaveManager.ResetWaves();
_levelScene.TowerBuildManager.DestroyAllTowers();
_levelScene.InitialTowerSpawner.SpawnAllTowers();
_levelScene.HealthManager.HealthChanged += OnHealthChanged;
_levelScene.WaveManager.WaveStarted += OnWaveStarted;
_levelScene.WaveManager.WaveFinished += OnWaveFinished;
}
public void FinishLevel(bool isWin)
{
if (CurrentState is LevelStateType.Win or LevelStateType.Lose)
return;
CurrentState = isWin ? LevelStateType.Win : LevelStateType.Lose;
_levelScene.TowerManager.DisableTowers();
_levelScene.MobManager.DespawnMobsAll();
_levelScene.WaveManager.FinishWave();
_levelScene.HealthManager.HealthChanged -= OnHealthChanged;
_levelScene.WaveManager.WaveStarted -= OnWaveStarted;
_levelScene.WaveManager.WaveFinished -= OnWaveFinished;
}
private void OnHealthChanged()
{
if (_levelScene.HealthManager.CurrentHealth > 0)
return;
FinishLevel(false);
}
private void OnWaveStarted()
{
CurrentState = LevelStateType.Playing;
_levelScene.TowerManager.EnabledTowers();
}
private void OnWaveFinished()
{
_levelScene.TowerManager.DisableTowers();
_levelScene.MobManager.DespawnMobsAll();
if (_levelScene.WaveManager.CurrentWave >= _levelScene.WaveManager.MaxWave)
{
FinishLevel(true);
return;
}
_levelScene.WaveManager.SetNextWave();
CurrentState = LevelStateType.Waiting;
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 6509b3e884d74eb698c4c2f82bc401cd
timeCreated: 1725799543
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/LevelStateManager.cs
uploadId: 770587

View File

@@ -0,0 +1,10 @@
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public enum LevelStateType
{
Waiting,
Playing,
Win,
Lose
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 97acdd860a8145bbab35cef358fd42d3
timeCreated: 1725818409
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/LevelStateType.cs
uploadId: 770587

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5ce3fb6d5e454192ba8f552525ef26b9
timeCreated: 1725791017

View File

@@ -0,0 +1,46 @@
using System;
using UnityEngine;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class MobHealth : MonoBehaviour
{
public Action HealthChanged;
public int CurrentHealth
{
get => _currentHealth;
set
{
if (_currentHealth == value)
return;
_currentHealth = value;
HealthChanged?.Invoke();
if (_currentHealth <= 0)
_deathCallback?.Invoke();
}
}
public int MaxHealth => maxHealth;
[SerializeField] private int maxHealth;
private int _currentHealth;
private Action _deathCallback;
public void ResetHealth()
{
CurrentHealth = MaxHealth;
}
public void TakeDamage(int damage)
{
CurrentHealth = Mathf.Clamp(CurrentHealth - damage, 0, MaxHealth);
}
public void SetOnDeath(Action deathCallback)
{
_deathCallback = deathCallback;
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: fc0bc970ef50473ab4dd1d71ec6d3213
timeCreated: 1725985325
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/Mob/MobHealth.cs
uploadId: 770587

View File

@@ -0,0 +1,40 @@
using System;
using UnityEngine;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class MobHealthBar : MonoBehaviour
{
[SerializeField] private MobMain mob;
[SerializeField] private Transform bar;
private Camera _mainCamera;
private void Awake()
{
_mainCamera = Camera.main;
}
private void OnEnable()
{
mob.Health.HealthChanged += RefreshBar;
RefreshBar();
}
private void OnDisable()
{
mob.Health.HealthChanged -= RefreshBar;
}
private void LateUpdate()
{
transform.rotation = _mainCamera.transform.rotation;
}
private void RefreshBar()
{
var progress = (float)mob.Health.CurrentHealth / mob.Health.MaxHealth;
bar.localScale = new Vector3(progress, bar.localScale.y, bar.localScale.z);
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: c8c4923e295d4d9986ac6214a28db302
timeCreated: 1725985707
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/Mob/MobHealthBar.cs
uploadId: 770587

View File

@@ -0,0 +1,71 @@
using System.Collections;
using UnityEngine;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class MobMain : MonoBehaviour
{
public string Id { get; private set; }
public MobType Type => _type;
public bool IsAlive { get; private set; }
public int CurrentHealth => Health.CurrentHealth;
public float SpawnTime { get; private set; }
public MobWaypointMover WaypointMover => _waypointMover;
public MobHealth Health => _health;
[SerializeField] private MobType _type;
[SerializeField] private MobWaypointMover _waypointMover;
[SerializeField] private MobHealth _health;
[SerializeField] private GameObject _deathFxPrefab;
private LevelScene _levelScene;
public void Spawn(string id, LevelScene level)
{
Id = id;
IsAlive = true;
SpawnTime = Time.time;
_levelScene = level;
_waypointMover.StartMoving(level.Waypoints);
_health.ResetHealth();
var mobWatch = Watch.GetOrAdd<Any>("Mobs").GetOrAdd<Any>(Id);
Watch.Push(mobWatch.GetOrAdd<string>("Type"), Type.ToString());
mobWatch.GetOrAdd("Health", () => CurrentHealth);
}
public void Despawn(bool showDeathFx = false, float delay = 0f)
{
if (!IsAlive)
return;
IsAlive = false;
_waypointMover.StopMoving();
StopAllCoroutines();
if (delay <= 0f)
Destroy(showDeathFx);
else
StartCoroutine(DelayedDestroying(showDeathFx, delay));
}
private IEnumerator DelayedDestroying(bool showDeathFx, float delay)
{
yield return new WaitForSeconds(delay);
Destroy(showDeathFx);
}
private void Destroy(bool showDeathFx)
{
_levelScene.PoolManager.Push(gameObject);
if (showDeathFx)
{
var deathFx = _levelScene.PoolManager.Get(_deathFxPrefab);
deathFx.transform.position = transform.position + Vector3.up;
}
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 2554fbc067f54ad08dbf5f7be16a1f23
timeCreated: 1725791025
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/Mob/MobMain.cs
uploadId: 770587

View File

@@ -0,0 +1,9 @@
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public enum MobType
{
Small,
Medium,
Big
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 132d1aa57e044b64a4962b10d8fa4d65
timeCreated: 1726052963
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/Mob/MobType.cs
uploadId: 770587

View File

@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class MobWaypointMover : MonoBehaviour
{
public float Speed;
public float RotationSpeed = 5f;
public float TravelledDist { get; private set; }
private const float ArriveThreshold = 0.01f;
private int _currentPointIndex;
private bool _isMoving;
private List<Transform> _waypoints;
private Transform nextPoint => _waypoints[_currentPointIndex];
private Vector2 currentPosition => new Vector2(transform.position.x, transform.position.z);
private Vector2 pointPosition => new Vector2(nextPoint.position.x, nextPoint.position.z);
public void StartMoving(List<Transform> waypoints)
{
_isMoving = true;
_currentPointIndex = 0;
_waypoints = waypoints;
TravelledDist = 0;
}
public void StopMoving()
{
_isMoving = false;
}
private void Update()
{
if (!_isMoving)
return;
UpdateTargetPoint();
UpdatePosition();
UpdateRotation();
}
private void UpdateTargetPoint()
{
var sqrDist = (currentPosition - pointPosition).sqrMagnitude;
if (sqrDist <= ArriveThreshold * ArriveThreshold)
_currentPointIndex = Mathf.Clamp(_currentPointIndex + 1, 0, _waypoints.Count - 1);
}
private void UpdatePosition()
{
var vector = pointPosition - currentPosition;
var distLeft = (pointPosition - currentPosition).magnitude;
if (distLeft < ArriveThreshold)
return;
var direction = vector / distLeft;
var moveDelta = Mathf.Clamp(Speed * Time.deltaTime, 0, distLeft);
var vectorMoveDelta = moveDelta * new Vector3(direction.x, 0, direction.y);
transform.position += vectorMoveDelta;
TravelledDist += moveDelta;
}
private void UpdateRotation()
{
var vector = pointPosition - currentPosition;
var distLeft = (pointPosition - currentPosition).magnitude;
if (distLeft < ArriveThreshold)
return;
var directionCurrent = new Vector2(transform.forward.x, transform.forward.z);
var directionTarget = vector;
var directionNew = Vector2.Lerp(directionCurrent, directionTarget, Time.unscaledDeltaTime * RotationSpeed);
transform.forward = new Vector3(directionNew.x, 0, directionNew.y);
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: af506fca87704f09b836f009d20f8f89
timeCreated: 1725791044
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/Mob/MobWaypointMover.cs
uploadId: 770587

View File

@@ -0,0 +1,91 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class MobManager : MonoBehaviour
{
public event Action<MobMain> MobDiedByHp;
public event Action<MobMain> MobReachedFinish;
public List<MobMain> Mobs { get; } = new();
private const float mobFinishDestroyDelay = 0.25f;
private LevelScene _levelScene;
private int _spawnedMobCount;
private void Awake()
{
_levelScene = FindObjectOfType<LevelScene>();
}
private void Start()
{
}
private void OnEnable()
{
_levelScene.WaveManager.WaveStarted += OnWaveStarted;
_levelScene.FinishTriggerZone.OnMobEntered += OnMobTriggeredFinished;
}
private void OnDisable()
{
_levelScene.WaveManager.WaveStarted -= OnWaveStarted;
_levelScene.FinishTriggerZone.OnMobEntered -= OnMobTriggeredFinished;
}
public void DespawnMobsAll()
{
foreach (var mob in Mobs.ToList())
DespawnMob(mob);
Mobs.Clear();
}
private void OnWaveStarted()
{
_spawnedMobCount = 0;
}
private void OnMobTriggeredFinished(MobMain mob)
{
MobReachedFinish?.Invoke(mob);
DespawnMob(mob, false, mobFinishDestroyDelay);
Watch.Push("MobLog", $"{mob.Id} reached base")
.SetSortOrder(TD_WatchSortOrder.MobLog);
}
public MobMain SpawnMob(MobMain prefab)
{
var mobObj = _levelScene.PoolManager.Get(prefab.gameObject);
var mob = mobObj.GetComponent<MobMain>();
mob.transform.SetParent(transform);
mob.transform.position = _levelScene.Waypoints[0].position;
var mobId = $"Mob_{++_spawnedMobCount}";
mob.Spawn(mobId, _levelScene);
mob.Health.SetOnDeath(() =>
{
MobDiedByHp?.Invoke(mob);
DespawnMob(mob, true);
});
Mobs.Add(mob);
Watch.Push("MobLog", $"{mob.Id} spawned")
.SetSortOrder(TD_WatchSortOrder.MobLog);
return mob;
}
public void DespawnMob(MobMain mob, bool deathFx = false, float delay = 0f)
{
Mobs.Remove(mob);
mob.Despawn(deathFx, delay);
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 240b5da4a1bb461dbc0f45725edce9c2
timeCreated: 1725791080
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/MobManager.cs
uploadId: 770587

View File

@@ -0,0 +1,23 @@
using System;
using UnityEngine;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class MobTriggerZone : MonoBehaviour
{
public event Action<MobMain> OnMobEntered;
[SerializeField] private ParticleSystem _attackFx;
private void OnTriggerEnter(Collider other)
{
var mob = other.GetComponentInParent<MobMain>();
if (mob == null)
return;
OnMobEntered?.Invoke(mob);
_attackFx.Play();
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 3156c2e028794a3a81a780de15656cb3
timeCreated: 1725805559
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/MobTriggerZone.cs
uploadId: 770587

View File

@@ -0,0 +1,42 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Pool;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class PoolManager : MonoBehaviour
{
private Dictionary<GameObject, Stack<GameObject>> _prefabToPools = new();
private Dictionary<GameObject, Stack<GameObject>> _objectToPools = new();
public GameObject Get(GameObject prefab)
{
var pool = GetPool(prefab);
var pooledObject = pool.Count > 0 ? pool.Pop() : Instantiate(prefab);
_objectToPools[pooledObject] = pool;
pooledObject.SetActive(true);
return pooledObject;
}
public void Push(GameObject pooledObject)
{
var pool = _objectToPools[pooledObject];
pool.Push(pooledObject);
pooledObject.SetActive(false);
pooledObject.transform.SetParent(transform);
}
private Stack<GameObject> GetPool(GameObject prefab)
{
if (_prefabToPools.TryGetValue(prefab, out var pool))
return pool;
pool = new Stack<GameObject>();
_prefabToPools.Add(prefab, pool);
return pool;
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 302c469964b746ffbd1488b942dae2c0
timeCreated: 1725798402
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/PoolManager.cs
uploadId: 770587

View File

@@ -0,0 +1,45 @@
using System;
using UnityEngine;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class ReturnToPoolTimer : MonoBehaviour
{
[SerializeField] private bool useUnscaledTime = false;
[SerializeField] private float delaySeconds = 3;
private PoolManager poolManager;
private float currentTimer;
private bool isTimerTicking;
private void Awake()
{
poolManager = FindObjectOfType<PoolManager>();
}
private void OnEnable()
{
isTimerTicking = true;
currentTimer = delaySeconds;
}
private void OnDisable()
{
isTimerTicking = false;
}
private void Update()
{
if (!isTimerTicking)
return;
currentTimer -= useUnscaledTime ? Time.unscaledDeltaTime : Time.deltaTime;
if (currentTimer <= 0)
{
isTimerTicking = false;
poolManager.Push(gameObject);
}
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 2eddfea6dfcf423eb70475a0a9f9bf64
timeCreated: 1729244736
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/ReturnToPoolTimer.cs
uploadId: 770587

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 589da07f7f0c48fd991821a2deabecdb
timeCreated: 1726052413

View File

@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
[Serializable]
public abstract class BasePopupUI : MonoBehaviour
{
public bool IsOpened { get; private set; }
[SerializeField] protected bool _closeByClickOutside = true;
[SerializeField] protected bool _lookAtCamera = true;
[SerializeField] protected GameObject _panel;
protected LevelScene _levelScene;
private Camera _mainCamera;
private bool _skipNextClick;
private HashSet<GameObject> _clickableChilds;
protected virtual void Awake()
{
_levelScene = FindObjectOfType<LevelScene>();
_clickableChilds = GetComponentsInChildren<Selectable>().Select(s => s.gameObject).ToHashSet();
_mainCamera = Camera.main;
}
private void Start()
{
if (_lookAtCamera)
{
transform.rotation = _mainCamera.transform.rotation;
}
OnCreated();
}
protected virtual void Update()
{
if (IsOpened
&& _closeByClickOutside
&& Input.GetMouseButtonDown(0)
&& !_clickableChilds.Contains(EventSystem.current.currentSelectedGameObject))
{
if (_skipNextClick)
_skipNextClick = false;
else
Close();
}
}
protected virtual void OnCreated()
{
}
protected virtual void OnOpened()
{
}
protected virtual void OnClosed()
{
}
public void Open()
{
if (IsOpened)
return;
IsOpened = true;
_skipNextClick = true;
_panel.SetActive(true);
OnOpened();
}
public void Close()
{
if (!IsOpened)
return;
OnClosed();
IsOpened = false;
_panel.SetActive(false);
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 635ac69aa74b473d9f0928567300d9ef
timeCreated: 1726055771
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/Slot/BasePopupUI.cs
uploadId: 770587

View File

@@ -0,0 +1,16 @@
using System;
using UnityEngine;
using UnityEngine.EventSystems;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class ClickableObject : MonoBehaviour
{
public event Action Clicked;
private void OnMouseDown()
{
Clicked?.Invoke();
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 1819d6599d204b50994dfa6aa395d2e8
timeCreated: 1726054870
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/Slot/ClickableObject.cs
uploadId: 770587

View File

@@ -0,0 +1,58 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class TowerBuildSlot : MonoBehaviour
{
public event Action OccupationChanged;
public bool IsOccupied
{
get => _isOccupied;
protected set
{
if (_isOccupied == value)
return;
_isOccupied = value;
OccupationChanged?.Invoke();
}
}
public int Id { get; private set; }
public TowerBase Tower { get; protected set; }
public Vector3 BuildLocation => transform.position;
[SerializeField] private GameObject _emptyStateObj;
private WatchReference<Any> slotWatch;
public void Init(int id)
{
Id = id;
slotWatch = Watch.GetOrAdd<Any>("TowerSlots").GetOrAdd<Any>(Id.ToString());
slotWatch.GetOrAdd("IsOccupied", () => IsOccupied);
}
public void Occupy(TowerBase tower)
{
Tower = tower;
IsOccupied = true;
_emptyStateObj.SetActive(false);
Watch.Push(slotWatch.GetOrAdd<string>("TowerId"), tower.Id);
}
public void Empty()
{
IsOccupied = false;
Tower = null;
_emptyStateObj.SetActive(true);
slotWatch.GetOrAdd<string>("TowerId").PushEmptyValue();
}
private bool _isOccupied;
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: fd36162806e5e204ba7176b2cb055a6d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/Slot/TowerBuildSlot.cs
uploadId: 770587

View File

@@ -0,0 +1,56 @@
using System;
using UnityEngine;
using UnityEngine.UI;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class TowerBuildSlotUIHandler : MonoBehaviour
{
[SerializeField] private TowerBuildSlot _targetSlot;
[SerializeField] private ClickableObject _slotClickable;
[SerializeField] private TowerSlotBuildPopupUI _buildUI;
[SerializeField] private TowerSlotDestroyPopupUI _destroyUI;
private void Awake()
{
_buildUI.TargetSlot = _targetSlot;
_destroyUI.TargetSlot = _targetSlot;
}
private void OnEnable()
{
_slotClickable.Clicked += OnSlotClicked;
_targetSlot.OccupationChanged += OnSlotOccupied;
}
private void OnDisable()
{
_slotClickable.Clicked -= OnSlotClicked;
_targetSlot.OccupationChanged -= OnSlotOccupied;
}
private void OnSlotClicked()
{
if (_targetSlot.IsOccupied)
{
_destroyUI.Open();
}
else
{
_buildUI.Open();
}
}
private void OnSlotOccupied()
{
if (_targetSlot.IsOccupied)
{
_buildUI.Close();
}
else
{
_destroyUI.Close();
}
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: b85b55a53a2a41e48622aa7404a32e00
timeCreated: 1726052468
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/Slot/TowerBuildSlotUIHandler.cs
uploadId: 770587

View File

@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class TowerSlotBuildPopupUI : BasePopupUI
{
[SerializeField] private List<BuildButton> _buildButtons = new();
public TowerBuildSlot TargetSlot { get; set; }
protected override void OnOpened()
{
base.OnOpened();
_levelScene.EnergyManager.EnergyChanged += OnEnergyChanged;
foreach (var buildButton in _buildButtons)
{
RefreshButton(buildButton);
buildButton.Button.onClick.AddListener(() => OnButtonClicked(buildButton));
}
}
protected override void OnClosed()
{
base.OnClosed();
_levelScene.EnergyManager.EnergyChanged -= OnEnergyChanged;
foreach (var buildButton in _buildButtons)
{
buildButton.Button.onClick.RemoveAllListeners();
}
}
private void RefreshButton(BuildButton buildButton)
{
var price = _levelScene.EconomyConfig.TowerBuildCosts[buildButton.Type];
var playerEnergy = _levelScene.EnergyManager.CurrentEnergy;
buildButton.Button.interactable = playerEnergy >= price;
buildButton.Text.text = $"{buildButton.Type}{Environment.NewLine}COST: -{price}".ToUpper();
}
private void OnEnergyChanged()
{
foreach (var buildButton in _buildButtons)
RefreshButton(buildButton);
}
private void OnButtonClicked(BuildButton buildButton)
{
_levelScene.TowerBuildManager.BuildTower(TargetSlot, buildButton.Type);
}
[Serializable]
public class BuildButton
{
[FormerlySerializedAs("Tower")] public TowerType Type;
public Button Button;
public Text Text;
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 015356ff48f342a0803b25a5ee3d9318
timeCreated: 1726059535
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/Slot/TowerSlotBuildPopupUI.cs
uploadId: 770587

View File

@@ -0,0 +1,36 @@
using System;
using UnityEngine;
using UnityEngine.UI;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class TowerSlotDestroyPopupUI : BasePopupUI
{
public TowerBuildSlot TargetSlot { get; set; }
[SerializeField] private Button sellButton;
[SerializeField] private Text sellPriceText;
protected override void OnOpened()
{
base.OnOpened();
var sellPrice = _levelScene.EconomyConfig.TowerSellPrices[TargetSlot.Tower.Type];
sellPriceText.text = $"Destroy{Environment.NewLine}+{sellPrice}".ToUpper();
sellButton.onClick.AddListener(OnSellClicked);
}
protected override void OnClosed()
{
base.OnClosed();
sellButton.onClick.RemoveListener(OnSellClicked);
}
private void OnSellClicked()
{
_levelScene.TowerBuildManager.DestroyTower(TargetSlot);
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 34b0bb117c064e3695b94aff839b0726
timeCreated: 1726059585
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/Slot/TowerSlotDestroyPopupUI.cs
uploadId: 770587

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fc1a6b10ff22a7b4186d82b63e52669e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
using System.Collections;
using UnityEngine;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class LaserEffect : MonoBehaviour
{
[SerializeField] private float _duration = 0.05f;
[SerializeField] private GameObject _laserBeam;
public void Play(Vector3 target)
{
target += Vector3.up * 0.5f;
var dist = (target - transform.position).magnitude;
transform.LookAt(target);
_laserBeam.transform.localScale = new Vector3(
_laserBeam.transform.localScale.x,
_laserBeam.transform.localScale.y,
dist/2f);
StopAllCoroutines();
StartCoroutine(ShowAndHide());
}
private IEnumerator ShowAndHide()
{
_laserBeam.SetActive(true);
yield return new WaitForSeconds(_duration);
_laserBeam.SetActive(false);
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: fc9b506708f84dc285684ec1f66a4793
timeCreated: 1726323107
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/Tower/LaserEffect.cs
uploadId: 770587

View File

@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class LaserTower : TowerBase
{
public override TowerType Type => TowerType.Laser;
[SerializeField] private float _cooldown = 0.5f;
[SerializeField] private int _damage = 5;
[SerializeField] private float _radius = 4f;
[SerializeField] private Transform _turretPivot;
[SerializeField] private ParticleSystem _laserEffect;
private LevelScene _levelScene;
private bool _isActive;
private float _lastFireTime;
private float _lastTargetUpdateTime;
private Vector2 ourPos => new Vector2(transform.position.x, transform.position.z);
private MobMain _previousTarget;
private const float targetFindCooldown = 0.05f;
private const float rotationSpeed = 5;
public override void Enable(LevelScene levelScene)
{
_isActive = true;
_lastFireTime = Time.time;
_levelScene = levelScene;
}
public override void Disable()
{
_isActive = false;
}
private void OnDrawGizmos()
{
Gizmos.DrawWireSphere(transform.position, _radius);
}
private void Update()
{
if (!_isActive)
return;
UpdateRotation();
if (Time.time < _lastFireTime + _cooldown)
return;
_lastFireTime = Time.time;
Fire();
}
private void UpdateRotation()
{
if (Time.time > _lastTargetUpdateTime + targetFindCooldown)
_previousTarget = FindNearestTarget();
if (_previousTarget == null || !_previousTarget.IsAlive)
return;
var targetDirection = _previousTarget.transform.position - _turretPivot.position;
var directionNew = Vector3.Slerp(_turretPivot.forward, targetDirection, Time.unscaledDeltaTime * rotationSpeed);
_turretPivot.forward = directionNew;
}
private MobMain FindNearestTarget()
{
MobMain targetMob = null;
foreach (var mob in _levelScene.MobManager.Mobs)
{
var mobPos = new Vector2(mob.transform.position.x, mob.transform.position.z);
var sqrDist = (mobPos - ourPos).sqrMagnitude;
if (sqrDist > _radius * _radius)
continue;
if (targetMob == null || mob.WaypointMover.TravelledDist > targetMob.WaypointMover.TravelledDist)
{
targetMob = mob;
}
}
return targetMob;
}
private void Fire()
{
var targetMob = FindNearestTarget();
if (targetMob != null)
{
if (targetMob.Health.CurrentHealth <= _damage)
Watch.Push("MobLog", $"{targetMob.Id} killed by {Id}")
.SetSortOrder(TD_WatchSortOrder.MobLog)
;
_turretPivot.forward = targetMob.transform.position - _turretPivot.position;
_laserEffect.transform.LookAt(targetMob.transform.position);
_laserEffect.Play();
targetMob.Health.TakeDamage(_damage);
}
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 163f0a04a378463a841d7ad2758ad6ad
timeCreated: 1726318386
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/Tower/LaserTower.cs
uploadId: 770587

View File

@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class ShockwaveTower : TowerBase
{
public override TowerType Type => TowerType.Shockwave;
[SerializeField] private float _cooldown = 1f;
[SerializeField] private int _damage = 20;
[SerializeField] private float _radius = 3f;
[SerializeField] private ParticleSystem _waveEffect;
private LevelScene _levelScene;
private bool _isActive;
private float _lastFireTime;
private Vector2 ourPos => new Vector2(transform.position.x, transform.position.z);
private List<MobMain> _pendingDamageMobs = new();
public override void Enable(LevelScene levelScene)
{
_isActive = true;
_lastFireTime = Time.time;
_levelScene = levelScene;
}
public override void Disable()
{
_isActive = false;
}
private void OnDrawGizmos()
{
Gizmos.DrawWireSphere(transform.position, _radius);
}
private void Update()
{
if (!_isActive)
return;
if (Time.time < _lastFireTime + _cooldown)
return;
_lastFireTime = Time.time;
Fire();
}
private void Fire()
{
_pendingDamageMobs.Clear();
foreach (var mob in _levelScene.MobManager.Mobs)
{
var mobPos = new Vector2(mob.transform.position.x, mob.transform.position.z);
var sqrDist = (mobPos - ourPos).sqrMagnitude;
if (sqrDist > _radius * _radius)
continue;
_pendingDamageMobs.Add(mob);
}
foreach (var mob in _pendingDamageMobs)
{
if (mob.Health.CurrentHealth <= _damage)
{
Watch.Push("MobLog", $"{mob.Id} killed by {Id}")
.SetSortOrder(TD_WatchSortOrder.MobLog)
;
}
mob.Health.TakeDamage(_damage);
}
_waveEffect.Play();
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 59b5965df77a462a9425484fb3d9f8f9
timeCreated: 1725990226
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/Tower/ShockwaveTower.cs
uploadId: 770587

View File

@@ -0,0 +1,12 @@
using UnityEngine;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public abstract class TowerBase : MonoBehaviour
{
public string Id { get; set; }
public abstract TowerType Type { get; }
public abstract void Enable(LevelScene levelScene);
public abstract void Disable();
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 3e0632d72869458bbc6ea2eb55f19201
timeCreated: 1725990613
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/Tower/TowerBase.cs
uploadId: 770587

View File

@@ -0,0 +1,8 @@
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public enum TowerType
{
Shockwave,
Laser
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: d9c13752bd084cdb978f2a51ce953371
timeCreated: 1726052989
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/Tower/TowerType.cs
uploadId: 770587

View File

@@ -0,0 +1,63 @@
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Serialization;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class WaveEffect : MonoBehaviour
{
[SerializeField] private float _growDuration = 0.1f;
[SerializeField] private float _stayDuration = 0.1f;
[SerializeField] private Transform _wave;
private float _radius;
private Coroutine _effectRoutine;
private void OnEnable()
{
SetSizeRaw(0);
}
public void SetRadius(float radius)
{
_radius = radius;
}
public void Play()
{
SetSizeRaw(0);
if (_effectRoutine != null)
StopCoroutine(_effectRoutine);
_effectRoutine = StartCoroutine(EffectPlaying());
}
private IEnumerator EffectPlaying()
{
SetSizeRaw(0);
var timer = _growDuration;
while (timer >= 0)
{
timer -= Time.deltaTime;
var progress = 1 - timer / _growDuration;
SetSizeRaw(_radius * progress);
yield return null;
}
yield return new WaitForSeconds(_stayDuration);
SetSizeRaw(0);
}
private void SetSizeRaw(float value)
{
_wave.localScale = value * Vector3.one;
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: fad3a9a7a9ac406ab0618ee81f7d420f
timeCreated: 1725993754
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/Tower/WaveEffect.cs
uploadId: 770587

View File

@@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Ingvar.LiveWatch.TowerDefenceDemo;
using UnityEngine;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class TowerBuildManager : MonoBehaviour
{
public List<TowerBuildSlot> Slots => _slots;
[SerializeField] private Transform _towersParent;
[SerializeField] private List<TowerBuildSlot> _slots;
[SerializeField] private List<TowerPrefab> _towerPrefabs;
private LevelScene _levelScene;
private void Awake()
{
_levelScene = FindObjectOfType<LevelScene>();
var slotId = 0;
foreach (var slot in _slots)
slot.Init(slotId++);
Watch.GetOrAdd<string>("BuildLog")
.SetSortOrder(TD_WatchSortOrder.TowerBuildLog);
}
public void DestroyAllTowers()
{
foreach (var slot in Slots)
{
if (!slot.IsOccupied)
continue;
DestroyTower(slot, false);
}
}
public void BuildTower(TowerBuildSlot slot, TowerType type, bool useGold = true)
{
var prefab = _towerPrefabs.First(p => p.Type == type).Prefab;
var towerObj = _levelScene.PoolManager.Get(prefab.gameObject);
var tower = towerObj.GetComponent<TowerBase>();
towerObj.transform.SetParent(_towersParent);
towerObj.transform.position = slot.BuildLocation;
_levelScene.TowerManager.AddTower(tower);
slot.Occupy(tower);
Watch.Push("BuildLog", $"Built {tower.Id} at {slot.Id} slot");
if (useGold)
{
var price = _levelScene.EconomyConfig.TowerBuildCosts[type];
_levelScene.EnergyManager.CurrentEnergy -= price;
}
}
public void DestroyTower(TowerBuildSlot slot, bool useGold = true)
{
var tower = slot.Tower;
Watch.Push("BuildLog", $"Destroyed {slot.Tower.Id} at {slot.Id}");
_levelScene.TowerManager.RemoveTower(tower);
_levelScene.PoolManager.Push(tower.gameObject);
slot.Empty();
if (useGold)
{
var price = _levelScene.EconomyConfig.TowerSellPrices[tower.Type];
_levelScene.EnergyManager.CurrentEnergy += price;
}
}
[Serializable]
public class TowerPrefab
{
public TowerType Type;
public TowerBase Prefab;
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: efcc9dd285e44b55b526b2a8b87ebdd9
timeCreated: 1726052278
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/TowerBuildManager.cs
uploadId: 770587

View File

@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class TowerManager : MonoBehaviour
{
private Dictionary<string, TowerBase> _towers = new();
private LevelScene _levelScene;
private int _spawnedTowersCount;
private void Awake()
{
_levelScene = FindObjectOfType<LevelScene>();
}
public void EnabledTowers()
{
foreach (var tower in _towers.Values)
{
tower.Enable(_levelScene);
}
}
public void DisableTowers()
{
foreach (var tower in _towers.Values)
{
tower.Disable();
}
}
public void AddTower(TowerBase tower)
{
tower.Id = $"{tower.Type}_{++_spawnedTowersCount}";
_towers.Add(tower.Id, tower);
if (_levelScene.LevelStateManager.CurrentState == LevelStateType.Playing)
tower.Enable(_levelScene);
}
public void RemoveTower(TowerBase tower)
{
_towers.Remove(tower.Id);
if (_levelScene.LevelStateManager.CurrentState == LevelStateType.Playing)
tower.Disable();
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 3dcaf7048f434c0c98737db4920b3740
timeCreated: 1725991927
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/TowerManager.cs
uploadId: 770587

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f0d32e44f88e4dbe97033609e73885af
timeCreated: 1725809354

View File

@@ -0,0 +1,74 @@
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
namespace Ingvar.LiveWatch.TowerDefenceDemo.UI
{
public class EnergyPanelUI : MonoBehaviour
{
[SerializeField] private float _animationExtraScale = 0.2f;
[SerializeField] private float _animationScaleDuration = 0.2f;
[SerializeField] private Text _energyText;
private LevelScene _levelScene;
private void Awake()
{
_levelScene = FindObjectOfType<LevelScene>();
}
private void OnEnable()
{
_levelScene.EnergyManager.EnergyChanged += OnEnergyChanged;
RefreshEnergy(false);
}
private void OnDisable()
{
_levelScene.EnergyManager.EnergyChanged -= OnEnergyChanged;
}
private void OnEnergyChanged()
{
RefreshEnergy();
}
private void RefreshEnergy(bool animate = true)
{
_energyText.text = $"{_levelScene.EnergyManager.CurrentEnergy}";
if (animate)
{
StopAllCoroutines();
StartCoroutine(AnimateChange());
}
}
private IEnumerator AnimateChange()
{
_energyText.transform.localScale = Vector3.one;
var zoomOutTime = 0f;
while (zoomOutTime <= _animationScaleDuration)
{
yield return new WaitForEndOfFrame();
zoomOutTime += Time.deltaTime;
var progress = Mathf.Clamp01(Mathf.InverseLerp(0, _animationScaleDuration, zoomOutTime));
_energyText.transform.localScale = (1 + progress * _animationExtraScale) * Vector3.one;
}
var zoomInTime = 0f;
while (zoomInTime <= _animationScaleDuration)
{
yield return new WaitForEndOfFrame();
zoomInTime += Time.deltaTime;
var progress = Mathf.Clamp01(Mathf.InverseLerp(0, _animationScaleDuration, zoomInTime));
_energyText.transform.localScale = (1 + _animationExtraScale - progress * _animationExtraScale) * Vector3.one;
}
_energyText.transform.localScale = Vector3.one;
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 9797888ece1d48379a82c2d289de9991
timeCreated: 1725995747
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/UI/EnergyPanelUI.cs
uploadId: 770587

View File

@@ -0,0 +1,73 @@
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
namespace Ingvar.LiveWatch.TowerDefenceDemo.UI
{
public class HealthPanelUI : MonoBehaviour
{
[SerializeField] private float animationExtraScale = 0.2f;
[SerializeField] private float animationScaleDuration = 0.2f;
[SerializeField] private Text healthText;
private LevelScene _levelScene;
private void Awake()
{
_levelScene = FindObjectOfType<LevelScene>();
}
private void OnEnable()
{
_levelScene.HealthManager.HealthChanged += OnHealthChanged;
RefreshHealth(false);
}
private void OnDisable()
{
_levelScene.HealthManager.HealthChanged -= OnHealthChanged;
}
private void OnHealthChanged()
{
RefreshHealth();
}
private void RefreshHealth(bool animate = true)
{
healthText.text = $"{_levelScene.HealthManager.CurrentHealth}";
if (animate)
{
StopAllCoroutines();
StartCoroutine(AnimateChange());
}
}
private IEnumerator AnimateChange()
{
healthText.transform.localScale = Vector3.one;
var zoomOutTime = 0f;
while (zoomOutTime <= animationScaleDuration)
{
yield return new WaitForEndOfFrame();
zoomOutTime += Time.deltaTime;
var progress = Mathf.Clamp01(Mathf.InverseLerp(0, animationScaleDuration, zoomOutTime));
healthText.transform.localScale = (1 + progress * animationExtraScale) * Vector3.one;
}
var zoomInTime = 0f;
while (zoomInTime <= animationScaleDuration)
{
yield return new WaitForEndOfFrame();
zoomInTime += Time.deltaTime;
var progress = Mathf.Clamp01(Mathf.InverseLerp(0, animationScaleDuration, zoomInTime));
healthText.transform.localScale = (1 + animationExtraScale - progress * animationExtraScale) * Vector3.one;
}
healthText.transform.localScale = Vector3.one;
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 0185bc50567c4c2fb9c6a083dba272c0
timeCreated: 1725809360
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/UI/HealthPanelUI.cs
uploadId: 770587

View File

@@ -0,0 +1,58 @@
using System;
using UnityEngine;
using UnityEngine.UI;
namespace Ingvar.LiveWatch.TowerDefenceDemo.UI
{
public class LevelFinishPanelUI : MonoBehaviour
{
[SerializeField] private CanvasGroup panelBase;
[SerializeField] private Text statusText;
[SerializeField] private Button restartButton;
private LevelScene _levelScene;
private void Awake()
{
_levelScene = FindObjectOfType<LevelScene>();
}
private void OnEnable()
{
restartButton.onClick.AddListener(OnRestartButtonClicked);
_levelScene.LevelStateManager.StateChanged += LevelStateManagerOnStateChanged;
SetShown(false);
}
private void OnDisable()
{
restartButton.onClick.RemoveListener(OnRestartButtonClicked);
_levelScene.LevelStateManager.StateChanged -= LevelStateManagerOnStateChanged;
}
private void LevelStateManagerOnStateChanged()
{
if (_levelScene.LevelStateManager.CurrentState is not LevelStateType.Win and not LevelStateType.Lose)
return;
SetShown(true);
statusText.text = _levelScene.LevelStateManager.CurrentState == LevelStateType.Win
? "YOU WON"
: "YOU LOST";
}
private void OnRestartButtonClicked()
{
SetShown(false);
_levelScene.LevelStateManager.StartLevel();
}
private void SetShown(bool isOn)
{
panelBase.alpha = isOn ? 1 : 0;
panelBase.blocksRaycasts = isOn;
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: fa255261a4664bb8b36eb5d6d0bfc246
timeCreated: 1725821124
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/UI/LevelFinishPanelUI.cs
uploadId: 770587

View File

@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace Ingvar.LiveWatch.TowerDefenceDemo.UI
{
public class SpeedPanelUI : MonoBehaviour
{
[SerializeField] private List<SpeedToggle> _speedToggles;
private LevelScene _levelScene;
private void Awake()
{
_levelScene = FindObjectOfType<LevelScene>();
}
private void OnEnable()
{
_levelScene.SpeedManager.SpeedChanged += RefreshToggles;
foreach (var speedToggle in _speedToggles)
speedToggle.Toggle.onValueChanged.AddListener((isOn) => OnToggleChanged(speedToggle, isOn));
RefreshToggles();
}
private void OnDisable()
{
_levelScene.SpeedManager.SpeedChanged -= RefreshToggles;
foreach (var speedToggle in _speedToggles)
speedToggle.Toggle.onValueChanged.RemoveAllListeners();
}
private void RefreshToggles()
{
foreach (var speedToggle in _speedToggles)
speedToggle.Toggle.SetIsOnWithoutNotify(speedToggle.Speed == _levelScene.SpeedManager.CurrentSpeed);
}
private void OnToggleChanged(SpeedToggle speedToggle, bool isOn)
{
if (!isOn)
return;
_levelScene.SpeedManager.SetGameSpeed(speedToggle.Speed);
}
[Serializable]
private class SpeedToggle
{
public Toggle Toggle;
public SpeedMode Speed;
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 566f30cbabcd4731b0879f05b6ac7e3e
timeCreated: 1726312021
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/UI/SpeedPanelUI.cs
uploadId: 770587

View File

@@ -0,0 +1,42 @@
using System;
using UnityEngine;
using UnityEngine.UI;
namespace Ingvar.LiveWatch.TowerDefenceDemo.UI
{
public class WaveButtonUI : MonoBehaviour
{
[SerializeField] private Button playWaveButton;
private LevelScene _levelScene;
private void Awake()
{
_levelScene = FindObjectOfType<LevelScene>();
}
private void OnEnable()
{
_levelScene.LevelStateManager.StateChanged += RefreshButton;
playWaveButton.onClick.AddListener(OnPlayButtonClicked);
RefreshButton();
}
private void OnDisable()
{
_levelScene.LevelStateManager.StateChanged -= RefreshButton;
playWaveButton.onClick.RemoveListener(OnPlayButtonClicked);
}
private void RefreshButton()
{
playWaveButton.interactable = _levelScene.LevelStateManager.CurrentState == LevelStateType.Waiting;
}
private void OnPlayButtonClicked()
{
_levelScene.WaveManager.StartWave();
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 25bd98f41f9b4d7eb3cb2459efb29153
timeCreated: 1725819869
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/UI/WaveButtonUI.cs
uploadId: 770587

View File

@@ -0,0 +1,38 @@
using System;
using UnityEngine;
using UnityEngine.UI;
namespace Ingvar.LiveWatch.TowerDefenceDemo.UI
{
public class WavePanelUI : MonoBehaviour
{
[SerializeField] private Text waveText;
private LevelScene _levelScene;
private void Awake()
{
_levelScene = FindObjectOfType<LevelScene>();
}
private void OnEnable()
{
_levelScene.WaveManager.WaveChanged += RefreshWave;
}
private void OnDisable()
{
_levelScene.WaveManager.WaveChanged -= RefreshWave;
}
private void Start()
{
RefreshWave();
}
private void RefreshWave()
{
waveText.text = $"WAVE: {_levelScene.WaveManager.CurrentWave+1}/{_levelScene.WaveManager.MaxWave+1}";
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 917360a2cc324e4bb5304ceb3a8d028e
timeCreated: 1725811761
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/UI/WavePanelUI.cs
uploadId: 770587

View File

@@ -0,0 +1,88 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Ingvar.LiveWatch.TowerDefenceDemo
{
public class WaveManager : MonoBehaviour
{
public event Action WaveStarted;
public event Action WaveFinished;
public event Action WaveChanged;
public bool IsActiveWave { get; private set; }
public int CurrentWave
{
get => _wave;
set
{
if (_wave == value)
return;
_wave = value;
WaveChanged?.Invoke();
}
}
public int MaxWave => _levelScene.LevelConfig.Waves.Count - 1;
public int CurrentSpawn { get; private set; }
private LevelScene _levelScene;
private int _wave;
private Coroutine _waveRoutine;
private List<MobWave> waves => _levelScene.LevelConfig.Waves;
private void Awake()
{
_levelScene = FindObjectOfType<LevelScene>();
}
public void ResetWaves()
{
CurrentWave = 0;
}
public void SetNextWave()
{
CurrentWave = Mathf.Clamp(CurrentWave + 1, 0, MaxWave);
}
public void StartWave()
{
IsActiveWave = true;
_waveRoutine = StartCoroutine(WaveProcessing(waves[CurrentWave]));
WaveStarted?.Invoke();
}
public void FinishWave()
{
if (_waveRoutine != null)
StopCoroutine(_waveRoutine);
IsActiveWave = false;
WaveFinished?.Invoke();
}
private IEnumerator WaveProcessing(MobWave wave)
{
CurrentSpawn = 0;
foreach (var spawn in wave.Spawns)
{
yield return new WaitForSeconds(spawn.Delay);
for (var i = 0; i < spawn.MobCount; i++)
{
_levelScene.MobManager.SpawnMob(spawn.MobPrefab);
yield return new WaitForSeconds(spawn.SpawnDelayBetween);
}
CurrentSpawn++;
}
yield return new WaitWhile(() => _levelScene.MobManager.Mobs.Count > 0);
FinishWave();
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: a7295529eb2a40b28cb726a23b6b6d35
timeCreated: 1725810836
AssetOrigin:
serializedVersion: 1
productId: 324001
packageName: LiveWatch Lite | Debug with full history of changes
packageVersion: 1.0.1
assetPath: Assets/LiveWatchLite/TowerDefenceDemo/Scripts/WaveManager.cs
uploadId: 770587