Cleaned up entity tracking. Scene.Camera. Matrix struct.

This commit is contained in:
Matt Thorson 2020-05-22 23:39:46 -07:00
parent 292213dae5
commit fe3672f30c
5 changed files with 582 additions and 72 deletions

View File

@ -25,7 +25,10 @@ namespace Strawberry
private Scene scene;
private Scene switchToScene;
private bool updating;
private Dictionary<Type, List<Type>> entityAssignableLists;
private Dictionary<Type, List<Type>> componentAssignableLists;
//SDL Vars
public SDL.Renderer* Renderer { get; private set; }
public Color ClearColor = .Black;
@ -78,7 +81,7 @@ namespace Strawberry
SDLMixer.OpenAudio(44100, SDLMixer.MIX_DEFAULT_FORMAT, 2, 4096);
SDLTTF.Init();
TypeTree.[Friend]Build();
BuildTypeLists();
Input.[Friend]Init(gamepadLimit);
}
@ -99,7 +102,7 @@ namespace Strawberry
delete VirtualInputs;
}
TypeTree.[Friend]Dispose();
DisposeTypeLists();
Input.[Friend]Dispose();
Game = null;
}
@ -210,5 +213,57 @@ namespace Strawberry
switchToScene = value;
}
}
// Type assignable caching
private void BuildTypeLists()
{
/*
For each Type that extends Entity, we build a list of all the other Entity Types that it is assignable to.
We cache these lists, and use them later to bucket Entities as they are added to the Scene.
This allows us to retrieve Entities by type very easily.
*/
entityAssignableLists = new Dictionary<Type, List<Type>>();
for (let type in Type.Enumerator())
{
if (type != typeof(Entity) && type.IsSubtypeOf(typeof(Entity)))
{
let list = new List<Type>();
for (let check in Type.Enumerator())
if (check != typeof(Entity) && check.IsSubtypeOf(typeof(Entity)) && type.IsSubtypeOf(check))
list.Add(check);
entityAssignableLists.Add(type, list);
}
}
/*
And then we also do this for components
*/
componentAssignableLists = new Dictionary<Type, List<Type>>();
for (let type in Type.Enumerator())
{
if (type != typeof(Component) && type.IsSubtypeOf(typeof(Component)))
{
let list = new List<Type>();
for (let check in Type.Enumerator())
if (check != typeof(Component) && check.IsSubtypeOf(typeof(Component)) && type.IsSubtypeOf(check))
list.Add(check);
componentAssignableLists.Add(type, list);
}
}
}
private void DisposeTypeLists()
{
for (let list in entityAssignableLists.Values)
delete list;
delete entityAssignableLists;
for (let list in componentAssignableLists.Values)
delete list;
delete componentAssignableLists;
}
}
}

View File

@ -8,6 +8,7 @@ namespace Strawberry
public float TimeStarted { get; private set; }
public Grid SolidGrid;
public Rect Bounds;
public Vector Camera;
private List<Entity> entities;
private HashSet<Entity> toRemove;
@ -22,11 +23,11 @@ namespace Strawberry
toRemove = new HashSet<Entity>();
entityTracker = new Dictionary<Type, List<Entity>>();
for (let type in TypeTree.[Friend]EntityAssignableLists.Keys)
for (let type in Game.[Friend]entityAssignableLists.Keys)
entityTracker.Add(type, new List<Entity>());
componentTracker = new Dictionary<Type, List<Component>>();
for (let type in TypeTree.[Friend]ComponentAssignableLists.Keys)
for (let type in Game.[Friend]componentAssignableLists.Keys)
componentTracker.Add(type, new List<Component>());
}
@ -63,6 +64,8 @@ namespace Strawberry
public virtual void Update()
{
Camera.X += 20 * Time.Delta;
UpdateLists();
for (var e in entities)
if (e.Active)
@ -132,7 +135,7 @@ namespace Strawberry
private void TrackEntity(Entity e)
{
for (let t in TypeTree.[Friend]EntityAssignableLists[e.GetType()])
for (let t in Game.[Friend]entityAssignableLists[e.GetType()])
entityTracker[t].Add(e);
for (let c in e.[Friend]components)
@ -141,7 +144,7 @@ namespace Strawberry
private void UntrackEntity(Entity e)
{
for (let t in TypeTree.[Friend]EntityAssignableLists[e.GetType()])
for (let t in Game.[Friend]entityAssignableLists[e.GetType()])
entityTracker[t].Remove(e);
for (let c in e.[Friend]components)
@ -150,13 +153,13 @@ namespace Strawberry
private void TrackComponent(Component c)
{
for (let t in TypeTree.[Friend]ComponentAssignableLists[c.GetType()])
for (let t in Game.[Friend]componentAssignableLists[c.GetType()])
componentTracker[t].Add(c);
}
private void UntrackComponent(Component c)
{
for (let t in TypeTree.[Friend]ComponentAssignableLists[c.GetType()])
for (let t in Game.[Friend]componentAssignableLists[c.GetType()])
componentTracker[t].Remove(c);
}

View File

@ -4,10 +4,12 @@ namespace Strawberry
{
static public class Draw
{
static public Point Camera => Game.Scene != null ? Game.Scene.Camera.Round() : Point.Zero;
static public void Rect(int x, int y, int w, int h, Color color)
{
SDL.SetRenderDrawColor(Game.Renderer, color.R, color.G, color.B, color.A);
SDL.RenderFillRect(Game.Renderer, &SDL.Rect((int32)x, (int32)y, (int32)w, (int32)h));
SDL.RenderFillRect(Game.Renderer, &SDL.Rect((int32)(x - Camera.X), (int32)(y - Camera.Y), (int32)w, (int32)h));
}
static public void Rect(Rect rect, Color color)
@ -18,7 +20,7 @@ namespace Strawberry
static public void HollowRect(int x, int y, int w, int h, Color color)
{
SDL.SetRenderDrawColor(Game.Renderer, color.R, color.G, color.B, color.A);
SDL.RenderDrawRect(Game.Renderer, &SDL.Rect((int32)x, (int32)y, (int32)w, (int32)h));
SDL.RenderDrawRect(Game.Renderer, &SDL.Rect((int32)(x - Camera.X), (int32)(y - Camera.Y), (int32)w, (int32)h));
}
static public void HollowRect(Rect rect, Color color)
@ -28,8 +30,11 @@ namespace Strawberry
static public void Line(Point from, Point to, Color color)
{
let fromn = (Point)(from - Camera);
let ton = (Point)(to - Camera);
SDL.SetRenderDrawColor(Game.Renderer, color.R, color.G, color.B, color.A);
SDL.RenderDrawLine(Game.Renderer, (int32)from.X, (int32)from.Y, (int32)to.X, (int32)to.Y);
SDL.RenderDrawLine(Game.Renderer, (int32)fromn.X, (int32)fromn.Y, (int32)ton.X, (int32)ton.Y);
}
}
}

View File

@ -1,61 +0,0 @@
using System.Collections;
using System;
namespace Strawberry
{
static public class TypeTree
{
static private Dictionary<Type, List<Type>> EntityAssignableLists;
static private Dictionary<Type, List<Type>> ComponentAssignableLists;
static private void Build()
{
/*
For each Type that extends Entity, we build a list of all the other Entity Types that it is assignable to.
We cache these lists, and use them later to bucket Entities as they are added to the Scene.
This allows us to retrieve Entities by type very easily.
*/
EntityAssignableLists = new Dictionary<Type, List<Type>>();
for (let type in Type.Enumerator())
{
if (type != typeof(Entity) && type.IsSubtypeOf(typeof(Entity)))
{
let list = new List<Type>();
for (let check in Type.Enumerator())
if (check != typeof(Entity) && check.IsSubtypeOf(typeof(Entity)) && type.IsSubtypeOf(check))
list.Add(check);
EntityAssignableLists.Add(type, list);
}
}
/*
And then we also do this for components
*/
ComponentAssignableLists = new Dictionary<Type, List<Type>>();
for (let type in Type.Enumerator())
{
if (type != typeof(Component) && type.IsSubtypeOf(typeof(Component)))
{
let list = new List<Type>();
for (let check in Type.Enumerator())
if (check != typeof(Component) && check.IsSubtypeOf(typeof(Component)) && type.IsSubtypeOf(check))
list.Add(check);
ComponentAssignableLists.Add(type, list);
}
}
}
static private void Dispose()
{
for (let list in EntityAssignableLists.Values)
delete list;
delete EntityAssignableLists;
for (let list in ComponentAssignableLists.Values)
delete list;
delete ComponentAssignableLists;
}
}
}

508
src/Struct/Matrix.bf Normal file
View File

@ -0,0 +1,508 @@
using System;
namespace Strawberry
{
/*
Based on Matrix3x2.cs by Microsoft, under MIT license
Source: https://github.com/microsoft/referencesource/blob/master/System.Numerics/System/Numerics/Matrix3x2.cs
Copyright (c) Microsoft. All rights reserved.
Licensed under the MIT license.
*/
public struct Matrix
{
static public readonly Matrix Identity = Matrix(1, 0, 0, 1, 0, 0);
// Row--vv--Column
public float M11;
public float M12;
public float M21;
public float M22;
public float M31;
public float M32;
public this(float m11, float m12, float m21, float m22, float m31, float m32)
{
M11 = m11;
M12 = m12;
M21 = m21;
M22 = m22;
M31 = m31;
M32 = m32;
}
public bool IsIdentity
{
get
{
return M11 == 1f && M22 == 1f && // Check diagonal element first for early out.
M12 == 0f &&
M21 == 0f &&
M31 == 0f && M32 == 0f;
}
}
public Vector Translation
{
get
{
return .(M31, M32);
}
set mut
{
M31 = value.X;
M32 = value.Y;
}
}
public Vector Scale
{
get
{
return .(M11, M22);
}
set mut
{
M11 = value.X;
M22 = value.Y;
}
}
public Vector Up
{
get
{
return .(M21, -M22);
}
set mut
{
M21 = value.X;
M22 = -value.Y;
}
}
public Vector Down
{
get
{
return .(M21, M22);
}
set mut
{
M21 = -value.X;
M22 = value.Y;
}
}
public Vector Right
{
get
{
return .(M11, -M12);
}
set mut
{
M11 = value.X;
M12 = -value.Y;
}
}
public Vector Left
{
get
{
return .(-M11, M12);
}
set mut
{
M11 = -value.X;
M12 = value.Y;
}
}
public Result<Matrix> Inverse
{
get => Invert(this);
set mut
{
this = Invert(value);
}
}
//Static Helpers
public static Matrix CreateTranslation(Vector position)
{
return Matrix(
1, 0,
0, 1,
position.X, position.Y
);
}
public static Matrix CreateScale(Vector scale)
{
return Matrix(
scale.X, 0,
0, scale.Y,
0, 0
);
}
public static Matrix CreateScale(Vector scale, Vector origin)
{
float tx = origin.X * (1 - scale.X);
float ty = origin.Y * (1 - scale.Y);
return Matrix(
scale.X, 0,
0, scale.Y,
tx, ty
);
}
public static Matrix CreateScale(float scale)
{
return Matrix(
scale, 0,
0, scale,
0, 0
);
}
public static Matrix CreateScale(float scale, Vector origin)
{
float tx = origin.X * (1 - scale);
float ty = origin.Y * (1 - scale);
return Matrix(
scale, 0,
0, scale,
tx, ty
);
}
public static Matrix CreateSkew(float radiansX, float radiansY)
{
float xTan = (float)Math.Tan(radiansX);
float yTan = (float)Math.Tan(radiansY);
return Matrix(
1, yTan,
xTan, 1,
0, 0
);
}
public static Matrix CreateSkew(float radiansX, float radiansY, Vector origin)
{
float xTan = (float)Math.Tan(radiansX);
float yTan = (float)Math.Tan(radiansY);
float tx = -origin.Y * xTan;
float ty = -origin.X * yTan;
return Matrix(
1, yTan,
xTan, 1,
tx, ty
);
}
public static Matrix CreateRotation(float radians)
{
let rad = (float)Math.IEEERemainder(radians, Math.PI_f * 2);
float c, s;
const float epsilon = 0.001f * (float)Math.PI_f / 180f; // 0.1% of a degree
if (rad > -epsilon && rad < epsilon)
{
// Exact case for zero rotation.
c = 1;
s = 0;
}
else if (rad > Math.PI_f / 2 - epsilon && rad < Math.PI_f / 2 + epsilon)
{
// Exact case for 90 degree rotation.
c = 0;
s = 1;
}
else if (rad < -Math.PI_f + epsilon || rad > Math.PI_f - epsilon)
{
// Exact case for 180 degree rotation.
c = -1;
s = 0;
}
else if (rad > -Math.PI_f / 2 - epsilon && rad < -Math.PI_f / 2 + epsilon)
{
// Exact case for 270 degree rotation.
c = 0;
s = -1;
}
else
{
// Arbitrary rotation.
c = (float)Math.Cos(rad);
s = (float)Math.Sin(rad);
}
return Matrix(
c, s,
-s, c,
0, 0
);
}
public static Matrix CreateRotation(float radians, Vector origin)
{
let rad = (float)Math.IEEERemainder(radians, Math.PI_f * 2);
float c, s;
const float epsilon = 0.001f * (float)Math.PI_f / 180f; // 0.1% of a degree
if (rad > -epsilon && rad < epsilon)
{
// Exact case for zero rotation.
c = 1;
s = 0;
}
else if (rad > Math.PI_f / 2 - epsilon && rad < Math.PI_f / 2 + epsilon)
{
// Exact case for 90 degree rotation.
c = 0;
s = 1;
}
else if (rad < -Math.PI_f + epsilon || rad > Math.PI_f - epsilon)
{
// Exact case for 180 degree rotation.
c = -1;
s = 0;
}
else if (rad > -Math.PI_f / 2 - epsilon && rad < -Math.PI_f / 2 + epsilon)
{
// Exact case for 270 degree rotation.
c = 0;
s = -1;
}
else
{
// Arbitrary rotation.
c = (float)Math.Cos(rad);
s = (float)Math.Sin(rad);
}
float x = origin.X * (1 - c) + origin.Y * s;
float y = origin.Y * (1 - c) - origin.X * s;
return Matrix(
c, s,
-s, c,
x, y
);
}
/// Calculates the determinant for this matrix.
/// The determinant is calculated by expanding the matrix with a third column whose values are (0,0,1).
public float GetDeterminant()
{
// There isn't actually any such thing as a determinant for a non-square matrix,
// but this 3x2 type is really just an optimization of a 3x3 where we happen to
// know the rightmost column is always (0, 0, 1). So we expand to 3x3 format:
//
// [ M11, M12, 0 ]
// [ M21, M22, 0 ]
// [ M31, M32, 1 ]
//
// Sum the diagonal products:
// (M11 * M22 * 1) + (M12 * 0 * M31) + (0 * M21 * M32)
//
// Subtract the opposite diagonal products:
// (M31 * M22 * 0) + (M32 * 0 * M11) + (1 * M21 * M12)
//
// Collapse out the constants and oh look, this is just a 2x2 determinant!
return (M11 * M22) - (M21 * M12);
}
public static Result<Matrix> Invert(Matrix matrix)
{
let det = (matrix.M11 * matrix.M22) - (matrix.M21 * matrix.M12);
if (Math.Abs(det) < float.Epsilon)
return .Err;
let invDet = 1.0f / det;
return Matrix(
matrix.M22 * invDet,
-matrix.M12 * invDet,
-matrix.M21 * invDet,
matrix.M11 * invDet,
(matrix.M21 * matrix.M32 - matrix.M31 * matrix.M22) * invDet,
(matrix.M31 * matrix.M12 - matrix.M11 * matrix.M32) * invDet
);
}
public static Matrix Lerp(Matrix a, Matrix b, float t)
{
return Matrix(
a.M11 + (b.M11 - a.M11) * t,
a.M12 + (b.M12 - a.M12) * t,
a.M21 + (b.M21 - a.M21) * t,
a.M22 + (b.M22 - a.M22) * t,
a.M31 + (b.M31 - a.M31) * t,
a.M32 + (b.M32 - a.M32) * t
);
}
public static Matrix Negate(Matrix mat)
{
return Matrix(
-mat.M11, -mat.M12,
-mat.M21, -mat.M22,
-mat.M31, -mat.M32
);
}
public static Matrix Add(Matrix a, Matrix b)
{
return Matrix(
a.M11 + b.M11, a.M12 + b.M12,
a.M21 + b.M21, a.M22 + b.M22,
a.M31 + b.M31, a.M32 + b.M32
);
}
public static Matrix Subtract(Matrix a, Matrix b)
{
return Matrix(
a.M11 - b.M11, a.M12 - b.M12,
a.M21 - b.M21, a.M22 - b.M22,
a.M31 - b.M31, a.M32 - b.M32
);
}
public static Matrix Multiply(Matrix a, Matrix b)
{
return Matrix(
a.M11 * b.M11 + a.M12 * b.M21,
a.M11 * b.M12 + a.M12 * b.M22,
a.M21 * b.M11 + a.M22 * b.M21,
a.M21 * b.M12 + a.M22 * b.M22,
a.M31 * b.M11 + a.M32 * b.M21 + b.M31,
a.M31 * b.M12 + a.M32 * b.M22 + b.M32
);
}
public static Matrix Multiply(Matrix a, float scale)
{
return Matrix(
a.M11 * scale, a.M12 * scale,
a.M21 * scale, a.M22 * scale,
a.M31 * scale, a.M32 * scale
);
}
// Operators
public static Matrix operator -(Matrix mat)
{
return Matrix(
-mat.M11, -mat.M12,
-mat.M21, -mat.M22,
-mat.M31, -mat.M32
);
}
public static Matrix operator +(Matrix a, Matrix b)
{
return Matrix(
a.M11 + b.M11, a.M12 + b.M12,
a.M21 + b.M21, a.M22 + b.M22,
a.M31 + b.M31, a.M32 + b.M32
);
}
public static Matrix operator -(Matrix a, Matrix b)
{
return Matrix(
a.M11 - b.M11, a.M12 - b.M12,
a.M21 - b.M21, a.M22 - b.M22,
a.M31 - b.M31, a.M32 - b.M32
);
}
public static Matrix operator *(Matrix a, Matrix b)
{
return Matrix(
a.M11 * b.M11 + a.M12 * b.M21,
a.M11 * b.M12 + a.M12 * b.M22,
a.M21 * b.M11 + a.M22 * b.M21,
a.M21 * b.M12 + a.M22 * b.M22,
a.M31 * b.M11 + a.M32 * b.M21 + b.M31,
a.M31 * b.M12 + a.M32 * b.M22 + b.M32
);
}
public static Matrix operator *(Matrix mat, float scale)
{
return Matrix(
mat.M11 * scale, mat.M12 * scale,
mat.M21 * scale, mat.M22 * scale,
mat.M31 * scale, mat.M32 * scale
);
}
public static bool operator ==(Matrix a, Matrix b)
{
return (a.M11 == b.M11 && a.M22 == b.M22 && // Check diagonal element first for early out.
a.M12 == b.M12 &&
a.M21 == b.M21 &&
a.M31 == b.M31 && a.M32 == b.M32);
}
public static bool operator !=(Matrix a, Matrix b)
{
return (a.M11 != b.M11 || a.M12 != b.M12 ||
a.M21 != b.M21 || a.M22 != b.M22 ||
a.M31 != b.M31 || a.M32 != b.M32);
}
public override void ToString(String strBuffer)
{
let str = scope String();
str.Append("[ ");
M11.ToString(str);
str.Append(", ");
M12.ToString(str);
str.Append(",\n");
M21.ToString(str);
str.Append(", ");
M22.ToString(str);
str.Append(",\n");
M31.ToString(str);
str.Append(", ");
M32.ToString(str);
str.Append(" ]");
}
}
}