Merge branch 'feature/waveEditor' into feature/waveGroup

This commit is contained in:
Ader Alisma 01 2023-09-24 17:30:16 -04:00
commit fd57c8247d
26 changed files with 409 additions and 5 deletions

View File

@ -0,0 +1,22 @@
using UnityEditor;
namespace GatherAndDefend.LevelEditor
{
[CustomEditor(typeof(WaveConfig))]
public class WaveConfigCustomInspector : Editor
{
public override void OnInspectorGUI()
{
EditorGUILayout.HelpBox(@"How to use :
- ConstantSpawn: Drag the chosen Enemy and insert the number to make in Count
- GameDuration : Duration of the game, in minutes
- Usage : Drop this config into a level
- Creation interval is determined by the game duration and the number of enemies to create
Important consideration :
- You must assign at least 1 enemy of Count of at least 1 to play.", MessageType.None);
base.OnInspectorGUI();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8c38e7df17e51a6448682caeaba0f29e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -246,3 +246,4 @@ MonoBehaviour:
_renderLayer: Default
_position: {x: 0, y: 0}
_scale: {x: 1, y: 1}
_waveConfig: {fileID: 11400000, guid: 21b0f85f7c746974db1e72f2df646f5d, type: 2}

View File

@ -198,3 +198,4 @@ MonoBehaviour:
_renderLayer: Default
_position: {x: 0, y: 0}
_scale: {x: 1, y: 1}
_waveConfig: {fileID: 11400000, guid: 21b0f85f7c746974db1e72f2df646f5d, type: 2}

View File

@ -196,3 +196,4 @@ MonoBehaviour:
_renderLayer: Default
_position: {x: 0, y: 0}
_scale: {x: 1, y: 1}
_waveConfig: {fileID: 11400000, guid: 21b0f85f7c746974db1e72f2df646f5d, type: 2}

View File

@ -228,3 +228,4 @@ MonoBehaviour:
_renderLayer: Default
_position: {x: 0, y: 0}
_scale: {x: 1, y: 1}
_waveConfig: {fileID: 11400000, guid: 21b0f85f7c746974db1e72f2df646f5d, type: 2}

View File

@ -204,3 +204,4 @@ MonoBehaviour:
_renderLayer: Default
_position: {x: 0, y: 0}
_scale: {x: 1, y: 1}
_waveConfig: {fileID: 11400000, guid: 21b0f85f7c746974db1e72f2df646f5d, type: 2}

View File

@ -202,3 +202,4 @@ MonoBehaviour:
_renderLayer: Default
_position: {x: 0, y: 0}
_scale: {x: 1, y: 1}
_waveConfig: {fileID: 11400000, guid: 21b0f85f7c746974db1e72f2df646f5d, type: 2}

View File

@ -309,6 +309,7 @@ MonoBehaviour:
_position: {x: 10, y: 1, z: 0}
- _tile: {fileID: 11400000, guid: ef5a154519b23a34aaded32e86bf7f2f, type: 2}
_position: {x: 10, y: 2, z: 0}
_isSpawner: 0
_isInvisible: 0
_isCollidable: 0
_isTrigger: 0
@ -318,6 +319,7 @@ MonoBehaviour:
_scale: {x: 1, y: 1}
- _key: Entities
_tiles: []
_isSpawner: 0
_isInvisible: 0
_isCollidable: 0
_isTrigger: 0
@ -343,6 +345,7 @@ MonoBehaviour:
_position: {x: 10, y: 0, z: 0}
- _tile: {fileID: 11400000, guid: 4002377ed7e87b34699f126f2b10c703, type: 2}
_position: {x: 10, y: 2, z: 0}
_isSpawner: 0
_isInvisible: 0
_isCollidable: 0
_isTrigger: 0
@ -350,3 +353,4 @@ MonoBehaviour:
_renderLayer: Default
_position: {x: 0, y: 0}
_scale: {x: 1, y: 1}
_waveConfig: {fileID: 11400000, guid: 21b0f85f7c746974db1e72f2df646f5d, type: 2}

View File

@ -188,3 +188,4 @@ MonoBehaviour:
_renderLayer: Default
_position: {x: 0, y: 0}
_scale: {x: 1, y: 1}
_waveConfig: {fileID: 11400000, guid: 21b0f85f7c746974db1e72f2df646f5d, type: 2}

View File

@ -35,7 +35,7 @@ public abstract class Entity : LevelObject
Animation.SpeedMultiplier = SpeedMultiplier;
}
//Start the animation of death and the fading of the entity
public void Death()
public virtual void Death()
{
_animation.PlayDieAnim();
Invoke("Dying", 0.1f);

View File

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

View File

@ -0,0 +1,49 @@
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "Gather And Defend/Levels/WaveConfig")]
public class WaveConfig : ScriptableObject
{
[SerializeField]
private List<EnemyType> _constantSpawn = new List<EnemyType>();
[SerializeField]
private float _gameDuration = 0;
private float _enemySpawndOnStart = 0;
private int _enemySum = 0;
public List<EnemyType> ConstantSpawn
{
get
{
return _constantSpawn;
}
}
public float GetInterval()
{
float interval = _gameDuration * 60.0f / (_enemySum > 0 ? _enemySum.ToFloat() : SumCount()) ;
return interval;
}
public float GetUpdatedInterval()
{
_enemySpawndOnStart++;
float interval = Mathf.Max(_gameDuration * 60.0f / (_enemySum - _enemySpawndOnStart).ToFloat(), 0);
return interval;
}
public EnemyType GetRandomSpawn()
{
if (_constantSpawn.Count == 1)
{
return _constantSpawn[0];
}
return _constantSpawn[Random.Range(0, _constantSpawn.Count - 1)];
}
private float SumCount()
{
foreach (EnemyType enemy in _constantSpawn)
{
_enemySum += enemy.Count;
}
return _enemySum.ToFloat();
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eb0795e326609f0499365f5b65c2b5cd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,142 @@
using System.Collections.Generic;
using UnityEngine;
using System;
public class WaveObserver : Singleton<WaveObserver>
{
private List<SpawnerTile> _subjects = new List<SpawnerTile>();
private List<float> _aliveEnemyCount = new List<float>();
private List<int> _copyConstantSpawn;
private WaveConfig _levelConfig;
private const int MAXTOUGHNESS = 10;
private int _spawnerTiming = 0;
private List<int> _intervalTiming = new List<int>();
public WaveConfig LevelConfig
{
set
{
_levelConfig = value;
_copyConstantSpawn = new List<int>();
foreach (EnemyType enemy in _levelConfig.ConstantSpawn)
{
_copyConstantSpawn.Add(enemy.Count);
}
}
}
/**
* Called by spawner at the start of the game
* Assigns enemy to spawn and registers them
*/
public void Attach(SpawnerTile spawnerSubject)
{
spawnerSubject.Prefab = _levelConfig.GetRandomSpawn().GetEnemyObject();
_subjects.Add(spawnerSubject);
_aliveEnemyCount.Add(0);
_intervalTiming.Add(++_spawnerTiming);
}
/**
* Called by spawner when making enemies
* Assigns a new interval
*/
public void NotifySpawned(SpawnerTile spawnerSubject)
{
GameObject paramPrefab = spawnerSubject.Prefab;
spawnerSubject.ChangeSpawnSpeed(_levelConfig.GetInterval() * _spawnerTiming);
if (paramPrefab.Equals(_levelConfig.ConstantSpawn[0].GetEnemyObject()))
{
int currentCount = 0;
for (int i = 0; i < _copyConstantSpawn.Count; i++)
{
if (_levelConfig.ConstantSpawn[i].GetEnemyObject() == paramPrefab)
{
currentCount = --_copyConstantSpawn[i];
break;
}
}
if (currentCount <= 0)
{
foreach (SpawnerTile spawner in _subjects)
{
if (spawner.Prefab.Equals(paramPrefab))
{
spawner.StopSpawn();
}
}
}
}
}
/**
* Called by enemy when they spawn
* Keeps track of their row
*/
public int NotifyEnemy(float yPosition, float toughness)
{
int index = FindEnemyIndex(yPosition);
_aliveEnemyCount[index] += toughness;
if (_aliveEnemyCount[index] >= MAXTOUGHNESS)
{
_subjects[index].StopSpawn();
}
return index;
}
/**
* Called when an enemy dies
* Reactivates spawning on that row if disabled before
*/
public void NotifyDies(int position, float toughness)
{
_aliveEnemyCount[position] -= toughness;
if (_aliveEnemyCount[position] < MAXTOUGHNESS)
{
_subjects[position].StartSpawn();
}
}
/**
* Called when an enemy is spawned automatically at the start of the game
* Adjusts the intervall between spawns
*/
public void NotifyOnStart(SpawnerTile spawnerSubject)
{
float interval = _levelConfig.GetUpdatedInterval();
foreach (var subject in _subjects)
{
subject.ChangeSpawnSpeed(interval);
}
NotifySpawned(spawnerSubject);
}
// To find which spawner an ennemy came from.
private int FindEnemyIndex(float yPosition)
{
for (int i = 0; i < _subjects.Count; i++)
{
if (_subjects[i].Position.y == yPosition)
{
return i;
}
}
return -1;
}
/**
* Spawners wait 1 second to have time to register them all
* Then gets assigned a random spawn interval
*/
public void NotifyEndCooldown(SpawnerTile spawnerTile)
{
System.Random rand = new System.Random();
int index;
do
{
index = rand.Next(_subjects.Count);
} while (_intervalTiming.Count <= index);
spawnerTile.ChangeSpawnSpeed(_levelConfig.GetInterval() * _intervalTiming[index]);
_intervalTiming.Remove(index);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6eab34a14c5d4d746a70792d4d7914a2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -11,6 +11,8 @@ namespace GatherAndDefend.LevelEditor
public Rect Bounds => _bounds;
[SerializeField]
private List<TilemapData> _data = new List<TilemapData>();
[SerializeField]
private WaveConfig _waveConfig;
public void SaveFromTilemap(Tilemap tilemap)
{
var data = new TilemapData();
@ -24,6 +26,7 @@ namespace GatherAndDefend.LevelEditor
data.LoadToTilemap(tilemap);
}
public WaveConfig WaveConfig { get { return _waveConfig; } }
public IEnumerator<TilemapData> GetEnumerator()
{

View File

@ -48,7 +48,6 @@ namespace GatherAndDefend.LevelEditor
collision.isTrigger = _isTrigger;
}
foreach (TileData data in _tiles)
{
reference.SetTile(data.Position, data.Tile);

View File

@ -23,6 +23,7 @@ public class LevelManager : Singleton<LevelManager>
private readonly List<ILevelObject> _toAdd;
private readonly List<ILevelObject> _toRemove;
private readonly List<ILevelObject> _levelObjects;
private WaveObserver _waveObserver;
private Tilemap _dynamicTilemap;
public Tilemap DynamicTilemap
@ -150,6 +151,8 @@ public class LevelManager : Singleton<LevelManager>
}
_currentLevel = level;
_waveObserver = WaveObserver.Instance;
_waveObserver.LevelConfig = _currentLevel.WaveConfig;
Grid grid = Object.FindObjectOfType<Grid>();
//create new grid if there is none
if (!grid)

View File

@ -0,0 +1,28 @@
using UnityEngine;
using System;
[Serializable]
public class EnemyType
{
[SerializeField]
private Opponent _enemy;
[SerializeField]
private int _count;
public int GetEnemyToughness()
{
float toughness = Mathf.Round((_enemy.Hp / 10) / 2);
return (int)toughness;
}
public GameObject GetEnemyObject()
{
return _enemy.gameObject;
}
public int Count
{
get { return _count; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a747157705819b94499ad98134da8f88
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -12,6 +12,9 @@ public class Opponent : Entity
private Vector2 _movementVector = Vector2.zero;
private Rigidbody2D _rigidbody;
private WaveObserver _observer;
private int _observerIndex;
private float _toughness;
public override void Start()
{
@ -19,6 +22,9 @@ public class Opponent : Entity
_rigidbody = GetComponent<Rigidbody2D>();
Animation = gameObject.AddComponent<AnimationEntity>();
_observer = WaveObserver.Instance;
_toughness = Mathf.Round((Hp / 10) / 2);
_observerIndex = _observer.NotifyEnemy(transform.position.y, _toughness);
}
public override void Update()
@ -55,4 +61,10 @@ public class Opponent : Entity
AttackSpeedWait += Time.deltaTime;
}
public override void Death()
{
_observer.NotifyDies(_observerIndex, _toughness);
base.Death();
}
}

View File

@ -10,27 +10,43 @@ public class SpawnerTile : LevelTile
private bool _spawnOnStart;
private float _lifetime;
[SerializeField]
private float _spawnSpeed = 0;
private float _spawnSpeed = 1.0f;
[SerializeField, Range(0, 1.001f)]
private float _spawnCounter = 0;
private WaveObserver _observer;
private bool _stopped = false;
private bool _cooldownEnded = false;
public override void LevelStart()
{
_observer = WaveObserver.Instance;
_observer.Attach(this);
if (_spawnOnStart && _lifetime <= 0)
{
_prefab.Create(Position, parent: LevelManager.Instance.LevelTransform);
_observer.NotifyOnStart(this);
}
}
public override void LevelUpdate()
{
_lifetime += Time.deltaTime;
_spawnCounter += Time.deltaTime * _spawnSpeed;
if (_spawnCounter < 1) return;
if (!_stopped)
{
_spawnCounter += Time.deltaTime;
}
if (_spawnCounter < _spawnSpeed) return;
_spawnCounter = 0;
if (!_cooldownEnded)
{
_observer.NotifyEndCooldown(this);
_cooldownEnded = true;
return;
}
_prefab.Create(Position, parent: LevelManager.Instance.LevelTransform);
_observer.NotifySpawned(this);
}
public override bool Equals(ILevelObject other)
{
@ -50,6 +66,18 @@ public class SpawnerTile : LevelTile
dict[nameof(_spawnOnStart)] = _spawnOnStart;
return dict;
}
internal void StopSpawn()
{
_stopped = true;
}
internal void StartSpawn()
{
_stopped = false;
}
public override void LoadDictionary(Dictionary<string, object> dict)
{
base.LoadDictionary(dict);
@ -61,4 +89,25 @@ public class SpawnerTile : LevelTile
_lifetime = dict[nameof(_lifetime)].ToFloat();
_spawnOnStart = dict[nameof(_spawnOnStart)].ToBool();
}
public GameObject Prefab
{
set
{
_prefab = value;
}
get
{
return _prefab;
}
}
/**
* Called by observer
* Assigns a new spawn interval
*/
public void ChangeSpawnSpeed(float value)
{
_spawnSpeed = value;
}
}

8
Assets/WaveConfig.meta Normal file
View File

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

View File

@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: eb0795e326609f0499365f5b65c2b5cd, type: 3}
m_Name: Config01
m_EditorClassIdentifier:
_constantSpawn:
- _enemy: {fileID: 313037212318601125, guid: 5bbf0d85fa5bb3f4599da79f0a84e3a9, type: 3}
_count: 5
_gameDuration: 1

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 21b0f85f7c746974db1e72f2df646f5d
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant: