tweaks to LevelManager
le fait qu'il faille updater des tiles du tilemap posait un réel problème création d'un système d'héritage pour que les game objects et les tiles puissent cohabiter dans une même game loop - ILevelObject contient des fonctions de start, de destroy, d'update, d'égalité et une position - LevelTile et LevelObject héritent de ILevelObject et peuvent être hérité par des tiles ou des MonoBehaviours. - le level manager est un monobehaviour singleton qui update tous les ILevelObjects à chaque frame.
This commit is contained in:
parent
4c2ad3dbd2
commit
d10677db6d
BIN
Assets/LevelManager class diagram.png
Normal file
BIN
Assets/LevelManager class diagram.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 110 KiB |
147
Assets/LevelManager class diagram.png.meta
Normal file
147
Assets/LevelManager class diagram.png.meta
Normal file
@ -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:
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
9
Assets/Scripts/LevelManager/Extensions.cs
Normal file
9
Assets/Scripts/LevelManager/Extensions.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/LevelManager/Extensions.cs.meta
Normal file
11
Assets/Scripts/LevelManager/Extensions.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc70649308eb8914c847e075e85e0e28
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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);
|
||||
}
|
||||
@ -1,58 +1,79 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class LevelManager : Singleton<LevelManager>
|
||||
public class LevelManager : SingletonBehaviour<LevelManager>
|
||||
{
|
||||
public event System.Action<ILevelObject> Added;
|
||||
public event System.Action<ILevelObject> Removed;
|
||||
public event Action<ILevelObject> Added;
|
||||
public event Action<ILevelObject> Removed;
|
||||
|
||||
private List<ILevelObject> toAdd;
|
||||
private List<ILevelObject> toRemove;
|
||||
private List<ILevelObject> levelObjects;
|
||||
public LevelManager()
|
||||
{
|
||||
toAdd = new List<ILevelObject>();
|
||||
toRemove = new List<ILevelObject>();
|
||||
levelObjects = new List<ILevelObject>();
|
||||
}
|
||||
|
||||
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<T>(System.Func<T, bool> predicate = default) where T : ILevelObject
|
||||
public T Get<T>(Func<T, bool> 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<T> GetAll<T>(System.Func<T, bool> predicate = default) where T : ILevelObject
|
||||
public List<T> GetAll<T>(Func<T, bool> predicate = null) where T : ILevelObject
|
||||
{
|
||||
if (predicate == default) predicate = (t) => true;
|
||||
if (predicate == null) predicate = (t) => true;
|
||||
List<T> ret = new List<T>();
|
||||
foreach (var t in levelObjects) if (t is T t1 && predicate(t1)) ret.Add(t1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public int Count<T>(Func<T, bool> predicate = default) where T : ILevelObject
|
||||
public int Count<T>(Func<T, bool> predicate = null) where T : ILevelObject
|
||||
{
|
||||
return GetAll(predicate).Count;
|
||||
}
|
||||
|
||||
public bool Has<T>(System.Func<T, bool> predicate = default)
|
||||
public bool Has<T>(Func<T, bool> 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();
|
||||
}
|
||||
}
|
||||
54
Assets/Scripts/LevelManager/LevelTile.cs
Normal file
54
Assets/Scripts/LevelManager/LevelTile.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Tilemaps;
|
||||
|
||||
/// <summary>
|
||||
/// can be inherited by tiles in order to be added to the level manager
|
||||
/// </summary>
|
||||
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<Tilemap>();
|
||||
|
||||
//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<LevelTile>(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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
11
Assets/Scripts/LevelManager/LevelTile.cs.meta
Normal file
11
Assets/Scripts/LevelManager/LevelTile.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 758530b8a0ef486448757813ce1eef67
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
16
Assets/Scripts/LevelManager/SingletonBehaviour.cs
Normal file
16
Assets/Scripts/LevelManager/SingletonBehaviour.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class SingletonBehaviour<T> : MonoBehaviour where T : SingletonBehaviour<T>
|
||||
{
|
||||
public static T Instance
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
if (!Instance) Instance = this as T;
|
||||
else Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/LevelManager/SingletonBehaviour.cs.meta
Normal file
11
Assets/Scripts/LevelManager/SingletonBehaviour.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a27e0ff408916844d82fcbdf5b332589
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
22
Assets/Scripts/Tiles/LevelObject.cs
Normal file
22
Assets/Scripts/Tiles/LevelObject.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Tilemaps;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
/// <summary>
|
||||
/// can be inherited by MonoBehaviours in order to be added to the level manager
|
||||
/// </summary>
|
||||
public abstract class LevelObject : MonoBehaviour, ILevelObject
|
||||
{
|
||||
public Vector3 Position => transform.position;
|
||||
void Awake()
|
||||
{
|
||||
if (LevelManager.Instance.Has<LevelTile>(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);
|
||||
}
|
||||
11
Assets/Scripts/Tiles/LevelObject.cs.meta
Normal file
11
Assets/Scripts/Tiles/LevelObject.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b25405eece6b19547806a7eca9f8ce85
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -16,6 +16,7 @@ public class TestLevelManager
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
new GameObject("LevelManager").AddComponent<LevelManager>();
|
||||
_farm = ScriptableObject.CreateInstance<ResourceTile>();
|
||||
_farm.name = nameof(_farm);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user