diff --git a/Assets/LevelManager class diagram.png b/Assets/LevelManager class diagram.png new file mode 100644 index 0000000..e2c161f Binary files /dev/null and b/Assets/LevelManager class diagram.png differ diff --git a/Assets/LevelManager class diagram.png.meta b/Assets/LevelManager class diagram.png.meta new file mode 100644 index 0000000..5131351 --- /dev/null +++ b/Assets/LevelManager class diagram.png.meta @@ -0,0 +1,147 @@ +fileFormatVersion: 2 +guid: 692cbdef9f1490b4b85ea6fc032ca4a8 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/LevelEditor/SpawnerTile.cs b/Assets/Scripts/LevelEditor/SpawnerTile.cs index b840ed8..4004c26 100644 --- a/Assets/Scripts/LevelEditor/SpawnerTile.cs +++ b/Assets/Scripts/LevelEditor/SpawnerTile.cs @@ -1,24 +1,47 @@ using UnityEngine; -using UnityEngine.Tilemaps; [CreateAssetMenu(menuName = "Gather And Defend/Spawner Tile")] -public class SpawnerTile : TileBase +public class SpawnerTile : LevelTile { [SerializeField] private Sprite _sprite; [SerializeField] private GameObject _prefab; + [SerializeField] + private bool _spawnOnStart = true; + [SerializeField] + private float _spawnSpeed = 0; + private float _spawnCounter = 0; - public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData) + public override bool Equals(ILevelObject other) { - tileData.sprite = _sprite; - tileData.transform.SetTRS(Vector3.zero, Quaternion.identity, Vector3.one); - tileData.color = Color.white; + return other is SpawnerTile spawner + && spawner.Position == Position + && spawner.Tilemap == Tilemap + && spawner._prefab == _prefab + && spawner._spawnOnStart == _spawnOnStart + && spawner._spawnSpeed == _spawnSpeed; } - public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject go) + + public override void LevelDestroy() { - if (!Application.isPlaying) return base.StartUp(position, tilemap, go); - Instantiate(_prefab, new Vector3(0.5f, 0.5f) + position, Quaternion.identity); - return base.StartUp(position, tilemap, go); + //nothing + } + + public override void LevelStart() + { + if (!_spawnOnStart) return; + var instance = Instantiate(_prefab, Position, Quaternion.identity); + instance.transform.SetParent(LevelManager.Instance.transform); + } + + public override void LevelUpdate() + { + _spawnCounter += Time.deltaTime * _spawnSpeed; + if (_spawnCounter < 1) return; + + _spawnCounter = 0; + var instance = Instantiate(_prefab, Position, Quaternion.identity); + instance.transform.SetParent(LevelManager.Instance.transform); } } \ No newline at end of file diff --git a/Assets/Scripts/LevelManager/Extensions.cs b/Assets/Scripts/LevelManager/Extensions.cs new file mode 100644 index 0000000..72301a2 --- /dev/null +++ b/Assets/Scripts/LevelManager/Extensions.cs @@ -0,0 +1,9 @@ +using UnityEngine; + +public static class Extensions +{ + public static bool Approximately(this Vector3 vect, Vector3 other) + { + return Mathf.Approximately(Vector3.Distance(vect, other), 0); + } +} \ No newline at end of file diff --git a/Assets/Scripts/LevelManager/Extensions.cs.meta b/Assets/Scripts/LevelManager/Extensions.cs.meta new file mode 100644 index 0000000..7b92ff1 --- /dev/null +++ b/Assets/Scripts/LevelManager/Extensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dc70649308eb8914c847e075e85e0e28 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/LevelManager/ILevelObject.cs b/Assets/Scripts/LevelManager/ILevelObject.cs index 13874ff..e59a0a8 100644 --- a/Assets/Scripts/LevelManager/ILevelObject.cs +++ b/Assets/Scripts/LevelManager/ILevelObject.cs @@ -1,3 +1,10 @@ -public interface ILevelObject +using UnityEngine; + +public interface ILevelObject { + Vector3 Position { get; } + void LevelUpdate(); + void LevelStart(); + void LevelDestroy(); + bool Equals(ILevelObject other); } \ No newline at end of file diff --git a/Assets/Scripts/LevelManager/LevelManager.cs b/Assets/Scripts/LevelManager/LevelManager.cs index 6e0001c..eb20c50 100644 --- a/Assets/Scripts/LevelManager/LevelManager.cs +++ b/Assets/Scripts/LevelManager/LevelManager.cs @@ -1,58 +1,79 @@ using System; using System.Collections.Generic; -public class LevelManager : Singleton +public class LevelManager : SingletonBehaviour { - public event System.Action Added; - public event System.Action Removed; + public event Action Added; + public event Action Removed; + private List toAdd; + private List toRemove; private List levelObjects; public LevelManager() { + toAdd = new List(); + toRemove = new List(); levelObjects = new List(); } public void Add(ILevelObject levelObject) { - if (levelObjects.Contains(levelObject)) return; - levelObjects.Add(levelObject); - Added?.Invoke(levelObject); + if (levelObjects.Exists(obj => obj.Equals(levelObject))) return; + toAdd.Add(levelObject); } public void Remove(ILevelObject levelObject) { if (!levelObjects.Contains(levelObject)) return; - levelObjects.Remove(levelObject); - Removed?.Invoke(levelObject); + toRemove.Add(levelObject); } public void Clear() { - levelObjects.RemoveAll(obj => - { - Removed?.Invoke(obj); - return true; - }); + toAdd.Clear(); + toRemove.Clear(); + levelObjects.Clear(); } - public T Get(System.Func predicate = default) where T : ILevelObject + public T Get(Func predicate = null) where T : ILevelObject { - if (predicate == default) predicate = (t) => true; + if (predicate == null) predicate = (t) => true; return (T)levelObjects.Find(t => t is T t1 && predicate(t1)); } - public List GetAll(System.Func predicate = default) where T : ILevelObject + public List GetAll(Func predicate = null) where T : ILevelObject { - if (predicate == default) predicate = (t) => true; + if (predicate == null) predicate = (t) => true; List ret = new List(); foreach (var t in levelObjects) if (t is T t1 && predicate(t1)) ret.Add(t1); return ret; } - public int Count(Func predicate = default) where T : ILevelObject + public int Count(Func predicate = null) where T : ILevelObject { return GetAll(predicate).Count; } - public bool Has(System.Func predicate = default) + public bool Has(Func predicate = null) { - if (predicate == default) predicate = (t) => true; + if (predicate == null) predicate = (t) => true; return levelObjects.Exists(t => t is T && predicate((T)t)); } + + void Update() + { + levelObjects.ForEach(obj => obj.LevelUpdate()); + + toAdd.ForEach(tile => + { + levelObjects.Add(tile); + Added?.Invoke(tile); + tile.LevelStart(); + }); + toAdd.Clear(); + + toRemove.ForEach(tile => + { + levelObjects.Remove(tile); + Removed?.Invoke(tile); + tile.LevelDestroy(); + }); + toRemove.Clear(); + } } \ No newline at end of file diff --git a/Assets/Scripts/LevelManager/LevelTile.cs b/Assets/Scripts/LevelManager/LevelTile.cs new file mode 100644 index 0000000..1b97a91 --- /dev/null +++ b/Assets/Scripts/LevelManager/LevelTile.cs @@ -0,0 +1,54 @@ +using UnityEngine; +using UnityEngine.Tilemaps; + +/// +/// can be inherited by tiles in order to be added to the level manager +/// +public abstract class LevelTile : TileBase, ILevelObject +{ + [SerializeField] + private Sprite _sprite; + public Vector3 Position { get; protected set; } + public Tilemap Tilemap { get; private set; } + + public abstract void LevelStart(); + public abstract void LevelDestroy(); + public abstract void LevelUpdate(); + public abstract bool Equals(ILevelObject other); + + public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject go) + { + //only execute if application is in play mode + if (!Application.isPlaying) + { + return base.StartUp(position, tilemap, go); + } + + var comp = tilemap.GetComponent(); + + //need to create an instance of the tile, otherwise the position will change for all tiles instead of only this one. + var instance = Instantiate(this); + instance.Position = position; + instance.Tilemap = comp; + + //lambda expression to be used to check if the tile already exists + bool isSameTile(LevelTile tile) => tile.Equals(instance); + + //if tile is exactly the same as before, don't add. + if (LevelManager.Instance.Has(isSameTile)) return base.StartUp(position, tilemap, go); + + + + LevelManager.Instance.Add(instance); + return base.StartUp(position, tilemap, go); + } + public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData) + { + tileData.sprite = _sprite; + tileData.transform.SetTRS(Vector3.zero, Quaternion.identity, Vector3.one); + tileData.color = Color.white; + } + + + +} \ No newline at end of file diff --git a/Assets/Scripts/LevelManager/LevelTile.cs.meta b/Assets/Scripts/LevelManager/LevelTile.cs.meta new file mode 100644 index 0000000..c3f5a26 --- /dev/null +++ b/Assets/Scripts/LevelManager/LevelTile.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 758530b8a0ef486448757813ce1eef67 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/LevelManager/SingletonBehaviour.cs b/Assets/Scripts/LevelManager/SingletonBehaviour.cs new file mode 100644 index 0000000..64d3e6b --- /dev/null +++ b/Assets/Scripts/LevelManager/SingletonBehaviour.cs @@ -0,0 +1,16 @@ +using UnityEngine; + +public class SingletonBehaviour : MonoBehaviour where T : SingletonBehaviour +{ + public static T Instance + { + get; + private set; + } + + protected virtual void Awake() + { + if (!Instance) Instance = this as T; + else Destroy(gameObject); + } +} \ No newline at end of file diff --git a/Assets/Scripts/LevelManager/SingletonBehaviour.cs.meta b/Assets/Scripts/LevelManager/SingletonBehaviour.cs.meta new file mode 100644 index 0000000..d7d6d6c --- /dev/null +++ b/Assets/Scripts/LevelManager/SingletonBehaviour.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a27e0ff408916844d82fcbdf5b332589 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Tiles/LevelObject.cs b/Assets/Scripts/Tiles/LevelObject.cs new file mode 100644 index 0000000..fdc0c53 --- /dev/null +++ b/Assets/Scripts/Tiles/LevelObject.cs @@ -0,0 +1,22 @@ +using UnityEngine; +using UnityEngine.Tilemaps; +using UnityEngine.UIElements; + +/// +/// can be inherited by MonoBehaviours in order to be added to the level manager +/// +public abstract class LevelObject : MonoBehaviour, ILevelObject +{ + public Vector3 Position => transform.position; + void Awake() + { + if (LevelManager.Instance.Has(tile => tile.Position.Approximately(Position))) return; + + LevelManager.Instance.Add(this); + } + + public abstract void LevelStart(); + public abstract void LevelDestroy(); + public abstract void LevelUpdate(); + public abstract bool Equals(ILevelObject other); +} \ No newline at end of file diff --git a/Assets/Scripts/Tiles/LevelObject.cs.meta b/Assets/Scripts/Tiles/LevelObject.cs.meta new file mode 100644 index 0000000..8dd5cdc --- /dev/null +++ b/Assets/Scripts/Tiles/LevelObject.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b25405eece6b19547806a7eca9f8ce85 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Tiles/ResourceTile.cs b/Assets/Scripts/Tiles/ResourceTile.cs index 184c050..5138799 100644 --- a/Assets/Scripts/Tiles/ResourceTile.cs +++ b/Assets/Scripts/Tiles/ResourceTile.cs @@ -5,30 +5,47 @@ using UnityEngine.Tilemaps; [CreateAssetMenu(menuName = "Gather And Defend/Resource Tile")] -public class ResourceTile : TileBase, ILevelObject +public class ResourceTile : LevelTile { [SerializeField] [Tooltip("the prefab of the currency that will be spawned when mining this resource")] private GameObject _yieldPrefab; - [SerializeField] - private Sprite _sprite; - public Vector3 Position { get; private set; } - public Sprite Sprite { get => _sprite; set => _sprite = value; } - public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject go) - { - if (!Application.isPlaying) return base.StartUp(position, tilemap, go); - //need to create an instance of the tile, otherwise the position will change for all tiles instead of only this one. - var instance = Instantiate(this); - instance.Position = position; - LevelManager.Instance.Add(instance); - return base.StartUp(position, tilemap, go); - } - public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData) + [SerializeField] + private float _yieldSpeed = 1; //resource per second + private float _yieldCounter = 0; + + public bool Occupied { get; set; } + + public override void LevelDestroy() { - tileData.sprite = _sprite; - tileData.transform.SetTRS(Vector3.zero, Quaternion.identity, Vector3.one); - tileData.color = Color.white; + //nothing + } + + public override void LevelStart() + { + //nothing + } + + public override void LevelUpdate() + { + if (!Occupied) return; + + + _yieldCounter += Time.deltaTime * _yieldSpeed; + if (_yieldCounter < 1) return; + + _yieldCounter = 0; + var yielded = Instantiate(_yieldPrefab, Position, Quaternion.identity); + yielded.transform.SetParent(LevelManager.Instance.transform); + } + public override bool Equals(ILevelObject other) + { + return other is ResourceTile otherRes && + Position == otherRes.Position + && Tilemap == otherRes.Tilemap + && _yieldPrefab == otherRes._yieldPrefab + && _yieldCounter == otherRes._yieldCounter; } } \ No newline at end of file diff --git a/Assets/Tests/Playmode/TestLevelManager.cs b/Assets/Tests/Playmode/TestLevelManager.cs index 655d1f2..9aaae31 100644 --- a/Assets/Tests/Playmode/TestLevelManager.cs +++ b/Assets/Tests/Playmode/TestLevelManager.cs @@ -16,6 +16,7 @@ public class TestLevelManager [SetUp] public void SetUp() { + new GameObject("LevelManager").AddComponent(); _farm = ScriptableObject.CreateInstance(); _farm.name = nameof(_farm);