Felix Boucher 8adc563d47 make transition fool proof
problème :
Il y avait plusieurs manières de faire planter le loading screen en appuyant sur des boutons

changements:
- turn off buttons when loading screen is active
- turn on buttons when loading screen is not active
- add event aggregator class to project and migrate every event to it
- fix bugs and regressions
2023-10-09 22:21:06 -04:00

255 lines
10 KiB
C#

using System.Collections.Generic;
using UnityEngine.Tilemaps;
using UnityEngine;
using System;
using System.Collections;
using System.Linq;
using Newtonsoft.Json;
using System.Threading.Tasks;
namespace GatherAndDefend.LevelEditor
{
[Serializable]
public class TilemapData : IEnumerable<TileData>
{
public const int INVISIBLE_LAYER = 6;
[SerializeField]
private string _key;
[SerializeField]
private List<TileData> _tiles;
[SerializeField]
private bool _isInvisible;
[SerializeField]
private bool _isCollidable;
[SerializeField]
private bool _isTrigger;
[SerializeField]
private int _renderOrder;
[SerializeField]
private string _renderLayer;
[SerializeField]
private Vector2 _position;
[SerializeField]
private Vector2 _scale;
public string Key => _key;
/// <summary>
///
/// </summary>
/// <param name="reference">the current tilemap</param>
/// <returns></returns>
public void LoadToTilemapEditor(Tilemap reference)
{
var tilesPerSecond = Application.isPlaying ? GlobalConfig.Instance.Current.baseTileSpawnSpeed : float.Epsilon;
reference.transform.localPosition = _position;
reference.transform.localScale = _scale;
var rend = reference.GetComponent<TilemapRenderer>();
rend.sortingOrder = _renderOrder;
rend.sortingLayerName = _renderLayer;
if (_isInvisible) rend.gameObject.layer = INVISIBLE_LAYER;
if (_isCollidable)
{
var collision = rend.gameObject.AddComponent<TilemapCollider2D>();
collision.isTrigger = _isTrigger;
}
//all tiles are loaded after their animation is over. use a task to asyncroneously load them and keep control over the flow
var tasks = new List<Task>();
foreach (TileData data in _tiles)
{
reference.SetTile(data.Position, data.Tile);
}
}
/// <summary>
///
/// </summary>
/// <param name="reference">the current tilemap</param>
/// <param name="placementAnimation">the tiles falling on the tilemap</param>
/// <param name="allTilesSpawned">a reference value that is used in caller method to detect when all tiles have been spawned</param>
/// <param name="tileSpawnAcceleration">a delegate which speeds up the spawning of the tiles over time</param>
/// <returns></returns>
public async Task LoadToTilemap(Tilemap reference, Func<bool> shouldKillTask, PlacementAnimationHandler placementAnimation = null, Action allTilesSpawned = default, Func<float> tileSpawnAcceleration = default)
{
// if the function we receive is null, we just make it constant 1
if (tileSpawnAcceleration == default) tileSpawnAcceleration = () => 1;
if (placementAnimation == null) placementAnimation = (tileData, placer, killer) =>
{
reference.SetTile(tileData.Position, tileData.Tile);
return null;
};
var tilesPerSecond = Application.isPlaying ? GlobalConfig.Instance.Current.baseTileSpawnSpeed : float.Epsilon;
reference.transform.localPosition = _position;
reference.transform.localScale = _scale;
var rend = reference.GetComponent<TilemapRenderer>();
rend.sortingOrder = _renderOrder;
rend.sortingLayerName = _renderLayer;
if (_isInvisible) rend.gameObject.layer = INVISIBLE_LAYER;
if (_isCollidable)
{
var collision = rend.gameObject.AddComponent<TilemapCollider2D>();
collision.isTrigger = _isTrigger;
}
//all tiles are loaded after their animation is over. use a task to asyncroneously load them and keep control over the flow
var tasks = new List<Task>();
foreach (TileData data in _tiles)
{
tasks.Add(placementAnimation(data, () => reference.SetTile(data.Position, data.Tile), shouldKillTask));
if (shouldKillTask())
{
return;
}
await Task.Delay((int)(1000f / (tilesPerSecond * tileSpawnAcceleration())));
}
allTilesSpawned?.Invoke();
if (shouldKillTask())
{
return;
}
await Task.WhenAll(tasks);
}
/// <summary>
/// saves a tilemap into the level object
/// </summary>
/// <param name="reference"></param>
/// <returns>the bounds of the tilemap</returns>
public void SaveFromTilemap(Tilemap reference)
{
_key = reference.name;
if (_isCollidable = reference.GetComponent<TilemapCollider2D>())
{
_isTrigger = reference.GetComponent<TilemapCollider2D>().isTrigger;
}
_isInvisible = reference.gameObject.layer == INVISIBLE_LAYER;
_renderLayer = reference.GetComponent<TilemapRenderer>().sortingLayerName;
_renderOrder = reference.GetComponent<TilemapRenderer>().sortingOrder;
_position = reference.transform.localPosition;
_scale = reference.transform.localScale;
_tiles = new List<TileData>();
BoundsInt bounds = reference.cellBounds;
for (int i = bounds.xMin; i <= bounds.xMax; i++)
{
for (int j = bounds.yMin; j <= bounds.yMax; j++)
{
Vector3Int position = new Vector3Int(i, j);
TileBase tile = reference.GetTile(position);
if (!tile) continue;
var tileData = new TileData(position, tile);
_tiles.Add(tileData);
}
}
}
/// <summary>
/// returns a dictionary representation of the tilemap
/// </summary>
/// <returns></returns>
public Dictionary<string, object> ToDictionary()
{
return new Dictionary<string, object>()
{
{nameof(_key), _key },
{nameof(_isInvisible), _isInvisible },
{nameof(_isCollidable), _isCollidable },
{nameof(_isTrigger), _isTrigger },
{nameof(_renderOrder), _renderOrder },
{nameof(_renderLayer), _renderLayer },
{nameof(_position), new float[]{_position.x, _position.y, 0 } },
{nameof(_scale), new float[]{ _scale.x, _scale.y, 0 } },
{nameof(_tiles), _tiles.FindAll(x => !(x.Tile is LevelTile))
.Select(x => new Dictionary<string, object>() {
{nameof(x.Position), new float[] { x.Position.x, x.Position.y, x.Position.z } },
{nameof(x.Tile), x.Tile.name } }).ToArray() }
};
}
/// <summary>
/// builds a tilemap from a dictionary representation (from the save file)
/// </summary>
/// <param name="dict"></param>
/// <returns></returns>
public static Tilemap FromDictionary(Dictionary<string, object> dict)
{
//get all tilemap data
var key = dict[nameof(_key)].ToString();
var invisible = dict[nameof(_isInvisible)].ToBool();
var collidable = dict[nameof(_isCollidable)].ToBool();
var trigger = dict[nameof(_isTrigger)].ToBool();
var renderOrder = dict[nameof(_renderOrder)].ToInt();
var renderLayer = dict[nameof(_renderLayer)].ToString();
var position = dict[nameof(_position)].ToVector3();
var scale = dict[nameof(_scale)].ToVector3();
var tiles = dict[nameof(_tiles)];
//get grid
var grid = GameObject.FindObjectOfType<Grid>();
if (!grid)
{
var levelMgrScript = GameObject.FindObjectOfType<LevelManagerScript>();
grid = new GameObject("Grid").AddComponent<Grid>();
grid.transform.SetParent(levelMgrScript.transform.parent);
}
//get tilemap by name
var tilemap = grid.GetComponentInChildren<Tilemap>(key);
TilemapRenderer renderer;
if (!tilemap)
{
tilemap = new GameObject(key).AddComponent<Tilemap>();
tilemap.tileAnchor = Vector3.zero;
tilemap.gameObject.AddComponent<TilemapRenderer>();
tilemap.transform.SetParent(grid.transform);
}
tilemap.transform.localPosition = position;
tilemap.transform.localScale = scale;
renderer = tilemap.GetComponent<TilemapRenderer>();
//populate tilemap according to specs
renderer.sortingOrder = renderOrder;
renderer.sortingLayerName = renderLayer;
if (invisible) tilemap.gameObject.layer = INVISIBLE_LAYER;
if (collidable)
{
tilemap.gameObject.AddComponent<TilemapCollider2D>().isTrigger = trigger;
}
//populate tilemaps with non-LevelTile tiles
foreach (var tileObj in tiles as IEnumerable)
{
var tileDict = JsonConvert.DeserializeObject<Dictionary<string, object>>(tileObj.ToString());
var tileName = tileDict["Tile"].ToString();
var tile = Database.Instance.ScriptableObjects[tileName];
var tilePos = tileDict["Position"].ToVector3();
tilemap.SetTile(Vector3Int.RoundToInt(tilePos), tile as TileBase);
}
return tilemap;
}
public IEnumerator<TileData> GetEnumerator()
{
return _tiles.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _tiles.GetEnumerator();
}
public TilemapData()
{
_tiles = new List<TileData>();
}
}
}