Import 2D Level
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Platformer.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Fuzzy provides methods for using values +- an amount of random deviation, or fuzz.
|
||||
/// </summary>
|
||||
static class Fuzzy
|
||||
{
|
||||
public static bool ValueLessThan(float value, float test, float fuzz = 0.1f)
|
||||
{
|
||||
var delta = value - test;
|
||||
return delta < 0 ? true : Random.value > delta / (fuzz * test);
|
||||
}
|
||||
|
||||
public static bool ValueGreaterThan(float value, float test, float fuzz = 0.1f)
|
||||
{
|
||||
var delta = value - test;
|
||||
return delta < 0 ? Random.value > -1 * delta / (fuzz * test) : true;
|
||||
}
|
||||
|
||||
public static bool ValueNear(float value, float test, float fuzz = 0.1f)
|
||||
{
|
||||
return Mathf.Abs(1f - (value / test)) < fuzz;
|
||||
}
|
||||
|
||||
public static float Value(float value, float fuzz = 0.1f)
|
||||
{
|
||||
return value + value * Random.Range(-fuzz, +fuzz);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Platformer.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// HeapQueue provides a queue collection that is always ordered.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class HeapQueue<T> where T : IComparable<T>
|
||||
{
|
||||
List<T> items;
|
||||
|
||||
public int Count { get { return items.Count; } }
|
||||
|
||||
public bool IsEmpty { get { return items.Count == 0; } }
|
||||
|
||||
public T First { get { return items[0]; } }
|
||||
|
||||
public void Clear() => items.Clear();
|
||||
|
||||
public bool Contains(T item) => items.Contains(item);
|
||||
|
||||
public void Remove(T item) => items.Remove(item);
|
||||
|
||||
public T Peek() => items[0];
|
||||
|
||||
public HeapQueue()
|
||||
{
|
||||
items = new List<T>();
|
||||
}
|
||||
|
||||
public void Push(T item)
|
||||
{
|
||||
//add item to end of tree to extend the list
|
||||
items.Add(item);
|
||||
//find correct position for new item.
|
||||
SiftDown(0, items.Count - 1);
|
||||
}
|
||||
|
||||
public T Pop()
|
||||
{
|
||||
|
||||
//if there are more than 1 items, returned item will be first in tree.
|
||||
//then, add last item to front of tree, shrink the list
|
||||
//and find correct index in tree for first item.
|
||||
T item;
|
||||
var last = items[items.Count - 1];
|
||||
items.RemoveAt(items.Count - 1);
|
||||
if (items.Count > 0)
|
||||
{
|
||||
item = items[0];
|
||||
items[0] = last;
|
||||
SiftUp();
|
||||
}
|
||||
else
|
||||
{
|
||||
item = last;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
int Compare(T A, T B) => A.CompareTo(B);
|
||||
|
||||
void SiftDown(int startpos, int pos)
|
||||
{
|
||||
//preserve the newly added item.
|
||||
var newitem = items[pos];
|
||||
while (pos > startpos)
|
||||
{
|
||||
//find parent index in binary tree
|
||||
var parentpos = (pos - 1) >> 1;
|
||||
var parent = items[parentpos];
|
||||
//if new item precedes or equal to parent, pos is new item position.
|
||||
if (Compare(parent, newitem) <= 0)
|
||||
break;
|
||||
//else move parent into pos, then repeat for grand parent.
|
||||
items[pos] = parent;
|
||||
pos = parentpos;
|
||||
}
|
||||
items[pos] = newitem;
|
||||
}
|
||||
|
||||
void SiftUp()
|
||||
{
|
||||
var endpos = items.Count;
|
||||
var startpos = 0;
|
||||
//preserve the inserted item
|
||||
var newitem = items[0];
|
||||
var childpos = 1;
|
||||
var pos = 0;
|
||||
//find child position to insert into binary tree
|
||||
while (childpos < endpos)
|
||||
{
|
||||
//get right branch
|
||||
var rightpos = childpos + 1;
|
||||
//if right branch should precede left branch, move right branch up the tree
|
||||
if (rightpos < endpos && Compare(items[rightpos], items[childpos]) <= 0)
|
||||
childpos = rightpos;
|
||||
//move child up the tree
|
||||
items[pos] = items[childpos];
|
||||
pos = childpos;
|
||||
//move down the tree and repeat.
|
||||
childpos = 2 * pos + 1;
|
||||
}
|
||||
//the child position for the new item.
|
||||
items[pos] = newitem;
|
||||
SiftDown(startpos, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Platformer.Core
|
||||
{
|
||||
public static partial class Simulation
|
||||
{
|
||||
/// <summary>
|
||||
/// An event is something that happens at a point in time in a simulation.
|
||||
/// The Precondition method is used to check if the event should be executed,
|
||||
/// as conditions may have changed in the simulation since the event was
|
||||
/// originally scheduled.
|
||||
/// </summary>
|
||||
/// <typeparam name="Event"></typeparam>
|
||||
public abstract class Event : System.IComparable<Event>
|
||||
{
|
||||
internal float tick;
|
||||
|
||||
public int CompareTo(Event other)
|
||||
{
|
||||
return tick.CompareTo(other.tick);
|
||||
}
|
||||
|
||||
public abstract void Execute();
|
||||
|
||||
public virtual bool Precondition() => true;
|
||||
|
||||
internal virtual void ExecuteEvent()
|
||||
{
|
||||
if (Precondition())
|
||||
Execute();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is generally used to set references to null when required.
|
||||
/// It is automatically called by the Simulation when an event has completed.
|
||||
/// </summary>
|
||||
internal virtual void Cleanup()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event<T> adds the ability to hook into the OnExecute callback
|
||||
/// whenever the event is executed. Use this class to allow functionality
|
||||
/// to be plugged into your application with minimal or zero configuration.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class Event<T> : Event where T : Event<T>
|
||||
{
|
||||
public static System.Action<T> OnExecute;
|
||||
|
||||
internal override void ExecuteEvent()
|
||||
{
|
||||
if (Precondition())
|
||||
{
|
||||
Execute();
|
||||
OnExecute?.Invoke((T)this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace Platformer.Core
|
||||
{
|
||||
public static partial class Simulation
|
||||
{
|
||||
/// <summary>
|
||||
/// This class provides a container for creating singletons for any other class,
|
||||
/// within the scope of the Simulation. It is typically used to hold the simulation
|
||||
/// models and configuration classes.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
static class InstanceRegister<T> where T : class, new()
|
||||
{
|
||||
public static T instance = new T();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace Platformer.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// The Simulation class implements the discrete event simulator pattern.
|
||||
/// Events are pooled, with a default capacity of 4 instances.
|
||||
/// </summary>
|
||||
public static partial class Simulation
|
||||
{
|
||||
|
||||
static HeapQueue<Event> eventQueue = new HeapQueue<Event>();
|
||||
static Dictionary<System.Type, Stack<Event>> eventPools = new Dictionary<System.Type, Stack<Event>>();
|
||||
|
||||
/// <summary>
|
||||
/// Create a new event of type T and return it, but do not schedule it.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
static public T New<T>() where T : Event, new()
|
||||
{
|
||||
Stack<Event> pool;
|
||||
if (!eventPools.TryGetValue(typeof(T), out pool))
|
||||
{
|
||||
pool = new Stack<Event>(4);
|
||||
pool.Push(new T());
|
||||
eventPools[typeof(T)] = pool;
|
||||
}
|
||||
if (pool.Count > 0)
|
||||
return (T)pool.Pop();
|
||||
else
|
||||
return new T();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all pending events and reset the tick to 0.
|
||||
/// </summary>
|
||||
public static void Clear()
|
||||
{
|
||||
eventQueue.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Schedule an event for a future tick, and return it.
|
||||
/// </summary>
|
||||
/// <returns>The event.</returns>
|
||||
/// <param name="tick">Tick.</param>
|
||||
/// <typeparam name="T">The event type parameter.</typeparam>
|
||||
static public T Schedule<T>(float tick = 0) where T : Event, new()
|
||||
{
|
||||
var ev = New<T>();
|
||||
ev.tick = Time.time + tick;
|
||||
eventQueue.Push(ev);
|
||||
return ev;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reschedule an existing event for a future tick, and return it.
|
||||
/// </summary>
|
||||
/// <returns>The event.</returns>
|
||||
/// <param name="tick">Tick.</param>
|
||||
/// <typeparam name="T">The event type parameter.</typeparam>
|
||||
static public T Reschedule<T>(T ev, float tick) where T : Event, new()
|
||||
{
|
||||
ev.tick = Time.time + tick;
|
||||
eventQueue.Push(ev);
|
||||
return ev;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the simulation model instance for a class.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
static public T GetModel<T>() where T : class, new()
|
||||
{
|
||||
return InstanceRegister<T>.instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a simulation model instance for a class.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
static public void SetModel<T>(T instance) where T : class, new()
|
||||
{
|
||||
InstanceRegister<T>.instance = instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroy the simulation model instance for a class.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
static public void DestroyModel<T>() where T : class, new()
|
||||
{
|
||||
InstanceRegister<T>.instance = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tick the simulation. Returns the count of remaining events.
|
||||
/// If remaining events is zero, the simulation is finished unless events are
|
||||
/// injected from an external system via a Schedule() call.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
static public int Tick()
|
||||
{
|
||||
var time = Time.time;
|
||||
var executedEventCount = 0;
|
||||
while (eventQueue.Count > 0 && eventQueue.Peek().tick <= time)
|
||||
{
|
||||
var ev = eventQueue.Pop();
|
||||
var tick = ev.tick;
|
||||
ev.ExecuteEvent();
|
||||
if (ev.tick > tick)
|
||||
{
|
||||
//event was rescheduled, so do not return it to the pool.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Debug.Log($"<color=green>{ev.tick} {ev.GetType().Name}</color>");
|
||||
ev.Cleanup();
|
||||
try
|
||||
{
|
||||
eventPools[ev.GetType()].Push(ev);
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
//This really should never happen inside a production build.
|
||||
Debug.LogError($"No Pool for: {ev.GetType()}");
|
||||
}
|
||||
}
|
||||
executedEventCount++;
|
||||
}
|
||||
return eventQueue.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user