remove time dilation priority system in favor of averaging

pull/5/head
thatcosmonaut 2019-11-22 13:14:36 -08:00
parent 71846a9a09
commit f05b7f725d
8 changed files with 93 additions and 176 deletions

View File

@ -1,15 +0,0 @@
using System;
namespace Encompass
{
[AttributeUsage(AttributeTargets.Class)]
public class TimeDilationPriority : Attribute
{
public int timeDilationPriority;
public TimeDilationPriority(int timeDilationPriority)
{
this.timeDilationPriority = timeDilationPriority;
}
}
}

View File

@ -27,7 +27,6 @@ namespace Encompass
/// <summary> /// <summary>
/// Used when activating time dilation. Lower priority overrides higher priority. /// Used when activating time dilation. Lower priority overrides higher priority.
/// </summary> /// </summary>
internal int? timeDilationPriority = null;
private EntityManager entityManager; private EntityManager entityManager;
private MessageManager messageManager; private MessageManager messageManager;
@ -139,9 +138,9 @@ namespace Encompass
/// <summary> /// <summary>
/// Returns true if an Entity with the specified ID exists. /// Returns true if an Entity with the specified ID exists.
/// </summary> /// </summary>
internal bool EntityExists(Guid entityID) protected bool EntityExists(Entity entity)
{ {
return entityManager.EntityExists(entityID); return entityManager.EntityExists(entity.ID);
} }
/// <summary> /// <summary>
@ -699,15 +698,10 @@ namespace Encompass
componentManager.MarkForRemoval(componentID); componentManager.MarkForRemoval(componentID);
} }
private void CheckTimeDilationPriorityExists()
{
if (!timeDilationPriority.HasValue) { throw new TimeDilationPriorityUndefinedException("Engines that activate time dilation must use the TimeDilationPriority attribute."); }
}
/// <summary> /// <summary>
/// Activates the Encompass time dilation system. /// Activates the Encompass time dilation system.
/// If activating time dilation, make sure the TimeDilationPriority attribute is set or an exception will be thrown.
/// Engines that have the IgnoresTimeDilation property will ignore all time dilation. /// Engines that have the IgnoresTimeDilation property will ignore all time dilation.
/// If multiple time dilations are active they will be averaged.
/// </summary> /// </summary>
/// <param name="factor">The time dilation factor, which is multiplied by real delta time.</param> /// <param name="factor">The time dilation factor, which is multiplied by real delta time.</param>
/// <param name="easeInTime">The time that will elapse before time is fully dilated, in real time.</param> /// <param name="easeInTime">The time that will elapse before time is fully dilated, in real time.</param>
@ -715,14 +709,13 @@ namespace Encompass
/// <param name="easeOutTime">The time that will elapse before time is fully undilated.</param> /// <param name="easeOutTime">The time that will elapse before time is fully undilated.</param>
public void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime) public void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime)
{ {
CheckTimeDilationPriorityExists(); timeManager.ActivateTimeDilation(factor, easeInTime, activeTime, easeOutTime);
timeManager.ActivateTimeDilation(factor, easeInTime, activeTime, easeOutTime, timeDilationPriority.Value);
} }
/// <summary> /// <summary>
/// Activates the Encompass time dilation system. /// Activates the Encompass time dilation system.
/// If activating time dilation, make sure the TimeDilationPriority attribute is set or an exception will be thrown.
/// Engines that have the IgnoresTimeDilation property will ignore all time dilation. /// Engines that have the IgnoresTimeDilation property will ignore all time dilation.
/// If multiple time dilations are active they will be averaged.
/// </summary> /// </summary>
/// <param name="factor">The time dilation factor, which is multiplied by real delta time.</param> /// <param name="factor">The time dilation factor, which is multiplied by real delta time.</param>
/// <param name="easeInTime">The time that will elapse before time is fully dilated, in real time.</param> /// <param name="easeInTime">The time that will elapse before time is fully dilated, in real time.</param>
@ -731,14 +724,13 @@ namespace Encompass
/// <param name="easeOutTime">The time that will elapse before time is fully undilated.</param> /// <param name="easeOutTime">The time that will elapse before time is fully undilated.</param>
public void ActivateTimeDilation(double factor, double easeInTime, System.Func<double, double, double, double, double> easeInFunction, double activeTime, double easeOutTime) public void ActivateTimeDilation(double factor, double easeInTime, System.Func<double, double, double, double, double> easeInFunction, double activeTime, double easeOutTime)
{ {
CheckTimeDilationPriorityExists(); timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime);
timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, timeDilationPriority.Value);
} }
/// <summary> /// <summary>
/// Activates the Encompass time dilation system. /// Activates the Encompass time dilation system.
/// If activating time dilation, make sure the TimeDilationPriority attribute is set or an exception will be thrown.
/// Engines that have the IgnoresTimeDilation property will ignore all time dilation. /// Engines that have the IgnoresTimeDilation property will ignore all time dilation.
/// If multiple time dilations are active they will be averaged.
/// </summary> /// </summary>
/// <param name="factor">The time dilation factor, which is multiplied by real delta time.</param> /// <param name="factor">The time dilation factor, which is multiplied by real delta time.</param>
/// <param name="easeInTime">The time that will elapse before time is fully dilated, in real time.</param> /// <param name="easeInTime">The time that will elapse before time is fully dilated, in real time.</param>
@ -747,14 +739,13 @@ namespace Encompass
/// <param name="easeOutFunction">An easing function for the easing out of time dilation.</param> /// <param name="easeOutFunction">An easing function for the easing out of time dilation.</param>
public void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime, System.Func<double, double, double, double, double> easeOutFunction) public void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime, System.Func<double, double, double, double, double> easeOutFunction)
{ {
CheckTimeDilationPriorityExists(); timeManager.ActivateTimeDilation(factor, easeInTime, activeTime, easeOutTime, easeOutFunction);
timeManager.ActivateTimeDilation(factor, easeInTime, activeTime, easeOutTime, easeOutFunction, timeDilationPriority.Value);
} }
/// <summary> /// <summary>
/// Activates the Encompass time dilation system. /// Activates the Encompass time dilation system.
/// If activating time dilation, make sure the TimeDilationPriority attribute is set or an exception will be thrown.
/// Engines that have the IgnoresTimeDilation property will ignore all time dilation. /// Engines that have the IgnoresTimeDilation property will ignore all time dilation.
/// If multiple time dilations are active they will be averaged.
/// </summary> /// </summary>
/// <param name="factor">The time dilation factor, which is multiplied by real delta time.</param> /// <param name="factor">The time dilation factor, which is multiplied by real delta time.</param>
/// <param name="easeInTime">The time that will elapse before time is fully dilated, in real time.</param> /// <param name="easeInTime">The time that will elapse before time is fully dilated, in real time.</param>
@ -764,8 +755,7 @@ namespace Encompass
/// <param name="easeOutFunction">An easing function for the easing out of time dilation.</param> /// <param name="easeOutFunction">An easing function for the easing out of time dilation.</param>
public void ActivateTimeDilation(double factor, double easeInTime, System.Func<double, double, double, double, double> easeInFunction, double activeTime, double easeOutTime, System.Func<double, double, double, double, double> easeOutFunction) public void ActivateTimeDilation(double factor, double easeInTime, System.Func<double, double, double, double, double> easeInFunction, double activeTime, double easeOutTime, System.Func<double, double, double, double, double> easeOutFunction)
{ {
CheckTimeDilationPriorityExists(); timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction);
timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction, timeDilationPriority.Value);
} }
} }
} }

View File

@ -1,12 +0,0 @@
using System;
namespace Encompass.Exceptions
{
public class TimeDilationPriorityConflictException : Exception
{
public TimeDilationPriorityConflictException(
string format,
params object[] args
) : base(string.Format(format, args)) { }
}
}

View File

@ -1,12 +0,0 @@
using System;
namespace Encompass.Exceptions
{
public class TimeDilationPriorityUndefinedException : Exception
{
public TimeDilationPriorityUndefinedException(
string format,
params object[] args
) : base(string.Format(format, args)) { }
}
}

View File

@ -29,6 +29,16 @@ namespace Encompass
return entityManager.GetEntity(entityID); return entityManager.GetEntity(entityID);
} }
protected IEnumerable<Entity> ReadEntities<TComponent>() where TComponent : struct, IComponent
{
return ReadComponentsIncludingEntity<TComponent>().Select(tuple => tuple.Item2);
}
protected Entity ReadEntity<TComponent>() where TComponent : struct, IComponent
{
return ReadComponentIncludingEntity<TComponent>().Item2;
}
protected IEnumerable<TComponent> ReadComponents<TComponent>() where TComponent : struct, IComponent protected IEnumerable<TComponent> ReadComponents<TComponent>() where TComponent : struct, IComponent
{ {
return componentManager.GetComponentsByType<TComponent>().Select(tuple => tuple.Item2); return componentManager.GetComponentsByType<TComponent>().Select(tuple => tuple.Item2);

View File

@ -1,73 +1,76 @@
using System.Collections.Generic;
using System.Linq;
namespace Encompass namespace Encompass
{ {
internal class TimeManager internal class TimeManager
{ {
private TimeDilationData timeDilationData = new TimeDilationData { factor = 1 }; private List<TimeDilationData> timeDilationDatas = new List<TimeDilationData>();
private bool newTimeDilationData = false;
private TimeDilationData nextFrameTimeDilationData = new TimeDilationData { factor = 1 };
private double Linear(double t, double b, double c, double d) private double Linear(double t, double b, double c, double d)
{ {
return c * t / d + b; return c * t / d + b;
} }
private int minPriority = int.MaxValue;
public double TimeDilationFactor public double TimeDilationFactor
{ {
get get
{ {
return timeDilationData.Factor; return timeDilationDatas.Count == 0 ? 1 : timeDilationDatas.Select(data => data.Factor).Average();
} }
} }
public bool TimeDilationActive public bool TimeDilationActive
{ {
get => TimeDilationFactor != 1; get => timeDilationDatas.Count != 0;
} }
public void Update(double dt) public void Update(double dt)
{ {
if (newTimeDilationData) for (var i = timeDilationDatas.Count - 1; i >= 0; i--)
{ {
timeDilationData = nextFrameTimeDilationData; var data = timeDilationDatas[i];
}
timeDilationData.elapsedTime += dt; data.elapsedTime += dt;
newTimeDilationData = false;
minPriority = int.MaxValue;
}
public void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime, int priority) if (data.elapsedTime > data.easeInTime + data.activeTime + data.easeOutTime)
{
ActivateTimeDilation(factor, easeInTime, Linear, activeTime, easeOutTime, Linear, priority);
}
public void ActivateTimeDilation(double factor, double easeInTime, System.Func<double, double, double, double, double> easeInFunction, double activeTime, double easeOutTime, int priority)
{
ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, Linear, priority);
}
public void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime, System.Func<double, double, double, double, double> easeOutFunction, int priority)
{
ActivateTimeDilation(factor, easeInTime, Linear, activeTime, easeOutTime, easeOutFunction, priority);
}
public void ActivateTimeDilation(double factor, double easeInTime, System.Func<double, double, double, double, double> easeInFunction, double activeTime, double easeOutTime, System.Func<double, double, double, double, double> easeOutFunction, int priority)
{
if (priority <= minPriority)
{
newTimeDilationData = true;
nextFrameTimeDilationData = new TimeDilationData
{ {
elapsedTime = 0, timeDilationDatas.RemoveAt(i);
easeInTime = easeInTime, }
easeInFunction = easeInFunction, else
activeTime = activeTime, {
easeOutTime = easeOutTime, timeDilationDatas[i] = data;
easeOutFunction = easeOutFunction, }
factor = factor
};
} }
} }
public void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime)
{
ActivateTimeDilation(factor, easeInTime, Linear, activeTime, easeOutTime, Linear);
}
public void ActivateTimeDilation(double factor, double easeInTime, System.Func<double, double, double, double, double> easeInFunction, double activeTime, double easeOutTime)
{
ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, Linear);
}
public void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime, System.Func<double, double, double, double, double> easeOutFunction)
{
ActivateTimeDilation(factor, easeInTime, Linear, activeTime, easeOutTime, easeOutFunction);
}
public void ActivateTimeDilation(double factor, double easeInTime, System.Func<double, double, double, double, double> easeInFunction, double activeTime, double easeOutTime, System.Func<double, double, double, double, double> easeOutFunction)
{
timeDilationDatas.Add(new TimeDilationData
{
elapsedTime = 0,
easeInTime = easeInTime,
easeInFunction = easeInFunction,
activeTime = activeTime,
easeOutTime = easeOutTime,
easeOutFunction = easeOutFunction,
factor = factor
});
}
} }
} }

View File

@ -234,22 +234,8 @@ namespace Encompass
var writePriorities = new Dictionary<Type, HashSet<int>>(); var writePriorities = new Dictionary<Type, HashSet<int>>();
var writeMessageToEngines = new Dictionary<Type, List<Engine>>(); var writeMessageToEngines = new Dictionary<Type, List<Engine>>();
var timeDilationPriorities = new Dictionary<int, HashSet<Engine>>();
foreach (var engine in engines) foreach (var engine in engines)
{ {
var timeDilationPriorityAttribute = engine.GetType().GetCustomAttribute<TimeDilationPriority>();
if (timeDilationPriorityAttribute != null)
{
engine.timeDilationPriority = timeDilationPriorityAttribute.timeDilationPriority;
if (!timeDilationPriorities.ContainsKey(timeDilationPriorityAttribute.timeDilationPriority))
{
timeDilationPriorities.Add(timeDilationPriorityAttribute.timeDilationPriority, new HashSet<Engine>());
}
timeDilationPriorities[timeDilationPriorityAttribute.timeDilationPriority].Add(engine);
}
if (engine.GetType().GetCustomAttribute<IgnoresTimeDilation>() != null) if (engine.GetType().GetCustomAttribute<IgnoresTimeDilation>() != null)
{ {
engine.usesTimeDilation = false; engine.usesTimeDilation = false;
@ -344,18 +330,6 @@ namespace Encompass
throw new EngineWriteConflictException(errorString); throw new EngineWriteConflictException(errorString);
} }
foreach (var timeDilationEngines in timeDilationPriorities)
{
var priority = timeDilationEngines.Key;
var engines = timeDilationEngines.Value;
if (engines.Count > 1)
{
var errorString = "Multiple Engines have the same Time Dilation Priority value: ";
errorString += string.Join(", ", engines);
throw new TimeDilationPriorityConflictException(errorString);
}
}
var engineOrder = new List<Engine>(); var engineOrder = new List<Engine>();
foreach (var engine in engineGraph.TopologicalSort()) foreach (var engine in engineGraph.TopologicalSort())
{ {

View File

@ -960,7 +960,6 @@ namespace Tests
static double dilatedDeltaTime; static double dilatedDeltaTime;
[TimeDilationPriority(0)]
class ActivateTimeDilationEngine : Engine class ActivateTimeDilationEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -1001,54 +1000,39 @@ namespace Tests
dilatedDeltaTime.Should().BeApproximately(0.3, 0.01); dilatedDeltaTime.Should().BeApproximately(0.3, 0.01);
} }
class ActivateTimeDilationWithoutPriorityEngine : Engine class ActivateTimeDilationLowerFactorEngine : Engine
{ {
private bool activated = false;
public override void Update(double dt) public override void Update(double dt)
{ {
ActivateTimeDilation(0.2, 1, 1, 1); if (!activated)
}
}
[Test]
public void ActivateTimeDilationWithoutPriorityThrows()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ActivateTimeDilationWithoutPriorityEngine());
var world = worldBuilder.Build();
Assert.Throws<TimeDilationPriorityUndefinedException>(() => world.Update(0.01));
}
[TimeDilationPriority(0)]
class ActivateTimeDilationLowerPriorityEngine : Engine
{
public override void Update(double dt)
{
if (!TimeDilationActive)
{ {
ActivateTimeDilation(0.2, 1, 1, 1); ActivateTimeDilation(0.2, 1, 1, 1);
} activated = true;
else
{
dilatedDeltaTime = dt;
} }
} }
} }
[TimeDilationPriority(1)] class ActivateTimeDilationHigherFactorEngine : Engine
class ActivateTimeDilationHigherPriorityEngine : Engine {
private bool activated = false;
public override void Update(double dt)
{
if (!activated)
{
ActivateTimeDilation(0.5, 1, 1, 1);
activated = true;
}
}
}
class ReadDilatedDeltaTimeEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
{ {
if (!TimeDilationActive) dilatedDeltaTime = dt;
{
ActivateTimeDilation(0.5, 1, 1, 1);
}
else
{
dilatedDeltaTime = dt;
}
} }
} }
@ -1056,26 +1040,21 @@ namespace Tests
public void MultipleActivateTimeDilation() public void MultipleActivateTimeDilation()
{ {
var worldBuilder = new WorldBuilder(); var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ActivateTimeDilationLowerPriorityEngine()); worldBuilder.AddEngine(new ReadDilatedDeltaTimeEngine());
worldBuilder.AddEngine(new ActivateTimeDilationHigherPriorityEngine()); worldBuilder.AddEngine(new ActivateTimeDilationLowerFactorEngine());
worldBuilder.AddEngine(new ActivateTimeDilationHigherFactorEngine());
var world = worldBuilder.Build(); var world = worldBuilder.Build();
world.Update(0.01); // activate time dilation world.Update(0.01); // activate time dilation
world.Update(0.5); world.Update(0.5); // 0.3 and 0.375
dilatedDeltaTime.Should().BeApproximately(0.3, 0.01); dilatedDeltaTime.Should().BeApproximately(0.3375, 0.01);
}
[Test] world.Update(5);
public void MultipleActivateTimeDilationWithDuplicatePriority()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new ActivateTimeDilationEngine());
worldBuilder.AddEngine(new ActivateTimeDilationLowerPriorityEngine());
Assert.Throws<TimeDilationPriorityConflictException>(() => worldBuilder.Build()); dilatedDeltaTime.Should().BeApproximately(5, 0.01);
} }
static double undilatedDeltaTime; static double undilatedDeltaTime;