mirror of
https://github.com/MaddyThorson/StrawberryBF.git
synced 2025-01-18 05:08:27 +08:00
Big restructuring - sample game is currently broken
This commit is contained in:
parent
05c79b296e
commit
3d5130b45b
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
||||
SampleGame/build/*
|
||||
SampleGame/BeefSpace_User.toml
|
||||
build/*
|
||||
SampleGame/recovery/*
|
||||
|
@ -7,8 +7,8 @@ namespace Strawberry.Sample
|
||||
public this()
|
||||
{
|
||||
Add(new Player(.(50, 50)));
|
||||
Add(new Solid(.(0, 168), .(0, 0, 320, 12)));
|
||||
Add(new JumpThru(.(200, 132), 48));
|
||||
Add(new OldSolid(.(0, 168), .(0, 0, 320, 12)));
|
||||
Add(new OldJumpThru(.(200, 132), 48));
|
||||
Add(new MovingJumpThru(.(136, 100), 32, .(124, 140), 2f));
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
namespace Strawberry.Sample
|
||||
{
|
||||
public class MovingJumpThru : JumpThru
|
||||
public class MovingJumpThru : OldJumpThru
|
||||
{
|
||||
private Point moveFrom;
|
||||
private Point moveTo;
|
||||
|
@ -2,56 +2,49 @@ using System;
|
||||
|
||||
namespace Strawberry.Sample
|
||||
{
|
||||
[Reflect]
|
||||
public class Actor : Entity
|
||||
public class Physics : Component, IHasHitbox, IUpdate
|
||||
{
|
||||
public Hitbox Hitbox { get; private set; }
|
||||
public Vector Speed;
|
||||
|
||||
private Vector remainder;
|
||||
|
||||
// The amount that geometry has pushed or carried this Actor since the last frame
|
||||
public Point MovedByGeometry { get; private set; }
|
||||
|
||||
public this(Point position)
|
||||
: base(position)
|
||||
public this(Hitbox hitbox)
|
||||
{
|
||||
|
||||
Hitbox = hitbox;
|
||||
}
|
||||
|
||||
public Level Level => SceneAs<Level>();
|
||||
public Level Level => Entity.SceneAs<Level>();
|
||||
|
||||
public bool Check(Level level)
|
||||
{
|
||||
return level.SolidGrid != null && Check(level.SolidGrid);
|
||||
return level.SolidGrid != null && Hitbox.Check(level.SolidGrid);
|
||||
}
|
||||
|
||||
public bool Check(Level level, Point offset)
|
||||
{
|
||||
return level.SolidGrid != null && Check(level.SolidGrid, offset);
|
||||
return level.SolidGrid != null && Hitbox.Check(level.SolidGrid, offset);
|
||||
}
|
||||
|
||||
public bool GroundCheck(int distance = 1)
|
||||
{
|
||||
return Check<Solid>(.(0, distance)) || Check(Level, .(0, distance)) || CheckOutside<JumpThru>(.(0, distance));
|
||||
return Hitbox.Check<Solid>(.(0, distance)) || Check(Level, .(0, distance)) || Hitbox.CheckOutside<JumpThru>(.(0, distance));
|
||||
}
|
||||
|
||||
public virtual bool IsRiding(Solid solid)
|
||||
{
|
||||
return Check(solid, .(0, 1));
|
||||
return Hitbox.Check(solid, .(0, 1));
|
||||
}
|
||||
|
||||
public virtual bool IsRiding(JumpThru jumpThru)
|
||||
{
|
||||
return CheckOutside(jumpThru, .(0, 1));
|
||||
return Hitbox.CheckOutside(jumpThru, .(0, 1));
|
||||
}
|
||||
|
||||
public virtual void Squish(Collision collision)
|
||||
public void Update()
|
||||
{
|
||||
RemoveSelf();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
base.Update();
|
||||
MovedByGeometry = Point.Zero;
|
||||
MoveX(Speed.X * Time.Delta);
|
||||
MoveY(Speed.Y * Time.Delta);
|
||||
}
|
||||
|
||||
public bool MoveX(float amount, delegate void(Collision) onCollide = null)
|
||||
@ -83,130 +76,85 @@ namespace Strawberry.Sample
|
||||
[Inline]
|
||||
public void MoveToX(float x)
|
||||
{
|
||||
MoveX(x - (X + remainder.X), null);
|
||||
MoveX(x - (Entity.X + remainder.X), null);
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public void MoveToY(float y)
|
||||
{
|
||||
MoveY(y - (Y + remainder.Y), null);
|
||||
MoveY(y - (Entity.Y + remainder.Y), null);
|
||||
}
|
||||
|
||||
public bool MoveExactX(int amount, delegate void(Collision) onCollide = null, Geometry pusher = null, Geometry carrier = null)
|
||||
public bool MoveExactX(int amount, delegate void(Collision) onCollide = null)
|
||||
{
|
||||
int move = amount;
|
||||
int sign = Math.Sign(amount);
|
||||
bool byGeometry = carrier != null || pusher != null;
|
||||
|
||||
while (move != 0)
|
||||
{
|
||||
let hit = First<Solid>(.(sign, 0));
|
||||
if (hit != null)
|
||||
if (Check(Level, .(sign, 0)) || Hitbox.Check<Solid>(.(sign, 0)))
|
||||
{
|
||||
let c = Collision(
|
||||
Cardinals.FromPoint(Point.Right * sign),
|
||||
Math.Abs(amount),
|
||||
Math.Abs(amount - move),
|
||||
hit,
|
||||
pusher
|
||||
Math.Abs(amount - move)
|
||||
);
|
||||
|
||||
onCollide?.Invoke(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Check(Level, .(sign, 0)))
|
||||
{
|
||||
let c = Collision(
|
||||
Cardinals.FromPoint(Point.Right * sign),
|
||||
Math.Abs(amount),
|
||||
Math.Abs(amount - move),
|
||||
null,
|
||||
pusher
|
||||
);
|
||||
|
||||
onCollide?.Invoke(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
X += sign;
|
||||
if (byGeometry)
|
||||
MovedByGeometry.X += sign;
|
||||
Entity.X += sign;
|
||||
move -= sign;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool MoveExactY(int amount, delegate void(Collision) onCollide = null, Geometry pusher = null, Geometry carrier = null)
|
||||
public bool MoveExactY(int amount, delegate void(Collision) onCollide = null)
|
||||
{
|
||||
int move = amount;
|
||||
int sign = Math.Sign(amount);
|
||||
bool byGeometry = carrier != null || pusher != null;
|
||||
|
||||
while (move != 0)
|
||||
{
|
||||
Geometry hit = First<Solid>(.(0, sign));
|
||||
if (hit == null && sign == 1)
|
||||
hit = FirstOutside<JumpThru>(.(0, sign));
|
||||
|
||||
if (hit != null)
|
||||
if (Check(Level, .(0, sign)) || Hitbox.Check<Solid>(.(0, sign)) || Hitbox.CheckOutside<JumpThru>(.(0, sign)))
|
||||
{
|
||||
let c = Collision(
|
||||
Cardinals.FromPoint(Point.Down * sign),
|
||||
Math.Abs(amount),
|
||||
Math.Abs(amount - move),
|
||||
hit,
|
||||
pusher
|
||||
Math.Abs(amount - move)
|
||||
);
|
||||
|
||||
onCollide?.Invoke(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Check(Level, .(0, sign)))
|
||||
{
|
||||
let c = Collision(
|
||||
Cardinals.FromPoint(Point.Down * sign),
|
||||
Math.Abs(amount),
|
||||
Math.Abs(amount - move),
|
||||
null,
|
||||
pusher
|
||||
);
|
||||
|
||||
onCollide?.Invoke(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
Y += sign;
|
||||
if (byGeometry)
|
||||
MovedByGeometry.Y += sign;
|
||||
Entity.Y += sign;
|
||||
move -= sign;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public void ZeroRemainderX()
|
||||
{
|
||||
remainder.X = 0;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public void ZeroRemainderY()
|
||||
{
|
||||
remainder.Y = 0;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public void ZeroRemainders()
|
||||
{
|
||||
remainder = Vector.Zero;
|
||||
}
|
||||
|
||||
private void MoveByGeometry(Point amount)
|
||||
{
|
||||
MovedByGeometry += amount;
|
||||
}
|
||||
|
||||
public bool CornerCorrection(Cardinals direction, int maxAmount, int lookAhead = 1, int onlySign = 0)
|
||||
{
|
||||
Point dir = direction;
|
||||
@ -216,9 +164,9 @@ namespace Strawberry.Sample
|
||||
|
||||
delegate bool(Point) checker;
|
||||
if (dir == Point.Down)
|
||||
checker = scope:: (p) => !Check(Level, p) && !Check<Solid>(p) && !CheckOutside<JumpThru>(p);
|
||||
checker = scope:: (p) => !Check(Level, p) && !Hitbox.Check<Solid>(p) && !Hitbox.CheckOutside<JumpThru>(p);
|
||||
else
|
||||
checker = scope:: (p) => !Check(Level, p) && !Check<Solid>(p);
|
||||
checker = scope:: (p) => !Check(Level, p) && !Hitbox.Check<Solid>(p);
|
||||
|
||||
for (int i = 1; i <= maxAmount; i++)
|
||||
{
|
||||
@ -230,7 +178,7 @@ namespace Strawberry.Sample
|
||||
let offset = dir * lookAhead + perp * i * j;
|
||||
if (checker(offset))
|
||||
{
|
||||
Position += offset;
|
||||
Entity.Position += offset;
|
||||
return true;
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ using System;
|
||||
|
||||
namespace Strawberry.Sample
|
||||
{
|
||||
public class Player : Actor
|
||||
public class Player : Component, IUpdate
|
||||
{
|
||||
public Vector Speed;
|
||||
|
||||
@ -18,10 +18,8 @@ namespace Strawberry.Sample
|
||||
Add(tVarJump = new Timer());
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
public void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
const float coyoteTime = 0.1f; // Time after leaving a ledge when you can still jump
|
||||
const float varJumpTime = 0.2f; // Time after jumping that you can hold the jump button to continue gaining upward speed
|
||||
const float jumpSpeed = -160;
|
||||
|
@ -6,16 +6,12 @@ namespace Strawberry.Sample
|
||||
public Cardinals Direction;
|
||||
public int Magnitude;
|
||||
public int Completed;
|
||||
public Geometry Stopper;
|
||||
public Geometry Pusher;
|
||||
|
||||
public this(Cardinals direction, int magnitude, int completed, Geometry stopper, Geometry pusher)
|
||||
public this(Cardinals direction, int magnitude, int completed)
|
||||
{
|
||||
Direction = direction;
|
||||
Magnitude = magnitude;
|
||||
Completed = completed;
|
||||
Stopper = stopper;
|
||||
Pusher = pusher;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,83 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Strawberry.Sample
|
||||
{
|
||||
public abstract class Geometry : Entity
|
||||
{
|
||||
private Vector remainder;
|
||||
|
||||
public this(Point position)
|
||||
: base(position)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void MoveX(float amount)
|
||||
{
|
||||
remainder.X += amount;
|
||||
let move = (int)Math.Round(remainder.X);
|
||||
if (move != 0)
|
||||
{
|
||||
remainder.X -= move;
|
||||
MoveExactX(move);
|
||||
}
|
||||
}
|
||||
|
||||
public void MoveY(float amount)
|
||||
{
|
||||
remainder.Y += amount;
|
||||
let move = (int)Math.Round(remainder.Y);
|
||||
if (move != 0)
|
||||
{
|
||||
remainder.Y -= move;
|
||||
MoveExactY(move);
|
||||
}
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public void Move(Vector amount)
|
||||
{
|
||||
MoveX(amount.X);
|
||||
MoveY(amount.Y);
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public void MoveToX(float x)
|
||||
{
|
||||
MoveX(x - (X + remainder.X));
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public void MoveToY(float y)
|
||||
{
|
||||
MoveY(y - (Y + remainder.Y));
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public void MoveTo(Vector target)
|
||||
{
|
||||
MoveToX(target.X);
|
||||
MoveToY(target.Y);
|
||||
}
|
||||
|
||||
public abstract void MoveExactX(int amount);
|
||||
public abstract void MoveExactY(int amount);
|
||||
public abstract List<Actor> GetRiders(List<Actor> into);
|
||||
|
||||
public void ZeroRemainderX()
|
||||
{
|
||||
remainder.X = 0;
|
||||
}
|
||||
|
||||
public void ZeroRemainderY()
|
||||
{
|
||||
remainder.Y = 0;
|
||||
}
|
||||
|
||||
public void ZeroRemainders()
|
||||
{
|
||||
remainder = Vector.Zero;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,80 +1,14 @@
|
||||
using System.Collections;
|
||||
using System;
|
||||
|
||||
namespace Strawberry.Sample
|
||||
{
|
||||
public class JumpThru : Geometry
|
||||
public class JumpThru : Component, IHasHitbox
|
||||
{
|
||||
public this(Point position, int width)
|
||||
: base(position)
|
||||
{
|
||||
Hitbox = Rect(0, 0, width, 2);
|
||||
}
|
||||
public Hitbox Hitbox { get; private set; }
|
||||
|
||||
public this(JSON json)
|
||||
: this(.(json), json["width"])
|
||||
public this(Hitbox hitbox)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void MoveExactX(int amount)
|
||||
{
|
||||
if (Collidable)
|
||||
{
|
||||
let riders = GetRiders(scope List<Actor>());
|
||||
|
||||
X += amount;
|
||||
for (var a in riders)
|
||||
a.MoveExactX(amount, null, null, this);
|
||||
}
|
||||
else
|
||||
X += amount;
|
||||
}
|
||||
|
||||
public override void MoveExactY(int amount)
|
||||
{
|
||||
if (Collidable)
|
||||
{
|
||||
let riders = GetRiders(scope List<Actor>());
|
||||
|
||||
if (amount < 0)
|
||||
{
|
||||
for (var a in Scene.All<Actor>(scope List<Actor>()))
|
||||
{
|
||||
if (riders.Contains(a) || CheckOutside(a, Point.UnitY * amount))
|
||||
{
|
||||
let move = (Top + amount) - a.Bottom;
|
||||
a.MoveExactY(move, null, null, this);
|
||||
}
|
||||
}
|
||||
Y += amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
Collidable = false;
|
||||
|
||||
for (var a in riders)
|
||||
a.MoveExactY(amount, null, null, this);
|
||||
|
||||
Collidable = true;
|
||||
Y += amount;
|
||||
}
|
||||
}
|
||||
else
|
||||
Y += amount;
|
||||
}
|
||||
|
||||
public override List<Actor> GetRiders(List<Actor> into)
|
||||
{
|
||||
for (var a in Scene.All<Actor>(scope List<Actor>()))
|
||||
if (a.IsRiding(this))
|
||||
into.Add(a);
|
||||
return into;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
DrawHitbox(.LightGray);
|
||||
Hitbox = hitbox;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,93 +2,13 @@ using System.Collections;
|
||||
|
||||
namespace Strawberry.Sample
|
||||
{
|
||||
public class Solid : Geometry
|
||||
public class Solid : Component, IHasHitbox
|
||||
{
|
||||
public this(Point position, Rect hitbox)
|
||||
: base(position)
|
||||
public Hitbox Hitbox { get; private set; }
|
||||
|
||||
public this(Hitbox hitbox)
|
||||
{
|
||||
Hitbox = hitbox;
|
||||
}
|
||||
|
||||
public override List<Actor> GetRiders(List<Actor> into)
|
||||
{
|
||||
for (var a in Scene.All<Actor>(scope List<Actor>()))
|
||||
if (a.IsRiding(this))
|
||||
into.Add(a);
|
||||
return into;
|
||||
}
|
||||
|
||||
public override void MoveExactX(int amount)
|
||||
{
|
||||
if (Collidable)
|
||||
{
|
||||
let riders = GetRiders(scope List<Actor>());
|
||||
|
||||
X += amount;
|
||||
Collidable = false;
|
||||
|
||||
for (Actor a in Scene.All<Actor>(scope List<Actor>()))
|
||||
{
|
||||
if (Check(a))
|
||||
{
|
||||
//Push
|
||||
int move;
|
||||
if (amount > 0)
|
||||
move = Right - a.Left;
|
||||
else
|
||||
move = Left - a.Right;
|
||||
a.MoveExactX(move, scope => a.Squish, this);
|
||||
}
|
||||
else if (riders.Contains(a))
|
||||
{
|
||||
//Carry
|
||||
a.MoveExactX(amount, null, null, this);
|
||||
}
|
||||
}
|
||||
|
||||
Collidable = true;
|
||||
}
|
||||
else
|
||||
X += amount;
|
||||
}
|
||||
|
||||
public override void MoveExactY(int amount)
|
||||
{
|
||||
if (Collidable)
|
||||
{
|
||||
let riders = GetRiders(scope List<Actor>());
|
||||
|
||||
Y += amount;
|
||||
Collidable = false;
|
||||
|
||||
for (Actor a in Scene.All<Actor>(scope List<Actor>()))
|
||||
{
|
||||
if (Check(a))
|
||||
{
|
||||
//Push
|
||||
int move;
|
||||
if (amount > 0)
|
||||
move = Bottom - a.Top;
|
||||
else
|
||||
move = Top - a.Bottom;
|
||||
a.MoveExactY(move, scope => a.Squish, this);
|
||||
}
|
||||
else if (riders.Contains(a))
|
||||
{
|
||||
//Carry
|
||||
a.MoveExactY(amount, null, null, this);
|
||||
}
|
||||
}
|
||||
|
||||
Collidable = true;
|
||||
}
|
||||
else
|
||||
Y += amount;
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
DrawHitbox(.White);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
288
src/Components/Collision/Hitbox.bf
Normal file
288
src/Components/Collision/Hitbox.bf
Normal file
@ -0,0 +1,288 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Strawberry
|
||||
{
|
||||
public class Hitbox : Component, IDebugDraw
|
||||
{
|
||||
public bool Collidable = true;
|
||||
public Rect Rect;
|
||||
|
||||
public void DebugDraw()
|
||||
{
|
||||
Game.Batcher.Rect(SceneHitbox, .Red);
|
||||
}
|
||||
|
||||
public int Width
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return Rect.Width;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
set
|
||||
{
|
||||
Rect.Width = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int Height
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return Rect.Height;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
set
|
||||
{
|
||||
Rect.Height = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Rect SceneHitbox
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return Rect + Entity.Position;
|
||||
}
|
||||
}
|
||||
|
||||
public int Left
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return Entity.X + Rect.Left;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
set
|
||||
{
|
||||
Entity.X = value - Rect.Left;
|
||||
}
|
||||
}
|
||||
|
||||
public int Right
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return Entity.X + Rect.Right;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
set
|
||||
{
|
||||
Entity.X = value - Rect.Right;
|
||||
}
|
||||
}
|
||||
|
||||
public int Top
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return Entity.Y + Rect.Top;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
set
|
||||
{
|
||||
Entity.Y = value - Rect.Top;
|
||||
}
|
||||
}
|
||||
|
||||
public int Bottom
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return Entity.Y + Rect.Bottom;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
set
|
||||
{
|
||||
Entity.Y = value - Rect.Bottom;
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Collisions =====
|
||||
|
||||
public bool Check(Point point)
|
||||
{
|
||||
return SceneHitbox.Contains(point);
|
||||
}
|
||||
|
||||
public bool Check(Rect rect)
|
||||
{
|
||||
return SceneHitbox.Intersects(rect);
|
||||
}
|
||||
|
||||
public bool Check(Grid grid)
|
||||
{
|
||||
return grid != null && grid.Check(SceneHitbox);
|
||||
}
|
||||
|
||||
public bool Check(Grid grid, Point offset)
|
||||
{
|
||||
return grid != null && grid.Check(SceneHitbox + offset);
|
||||
}
|
||||
|
||||
public bool Check(Hitbox other)
|
||||
{
|
||||
return other.Collidable && SceneHitbox.Intersects(other.SceneHitbox);
|
||||
}
|
||||
|
||||
public bool Check(Hitbox other, Point offset)
|
||||
{
|
||||
return other.Collidable && (SceneHitbox + offset).Intersects(other.SceneHitbox);
|
||||
}
|
||||
|
||||
public bool CheckOutside(Hitbox other, Point offset)
|
||||
{
|
||||
return other.Collidable && !SceneHitbox.Intersects(other.SceneHitbox) && (SceneHitbox + offset).Intersects(other.SceneHitbox);
|
||||
}
|
||||
|
||||
public bool Check<T>(T other) where T : Component, IHasHitbox
|
||||
{
|
||||
return Check(other.Hitbox);
|
||||
}
|
||||
|
||||
public bool Check<T>(T other, Point offset) where T : Component, IHasHitbox
|
||||
{
|
||||
return Check(other.Hitbox, offset);
|
||||
}
|
||||
|
||||
public bool CheckOutside<T>(T other, Point offset) where T : Component, IHasHitbox
|
||||
{
|
||||
return CheckOutside(other.Hitbox, offset);
|
||||
}
|
||||
|
||||
public bool Check<T>() where T : Component, IHasHitbox
|
||||
{
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (Check(e.Hitbox))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Check<T>(Point offset) where T : Component, IHasHitbox
|
||||
{
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (Check(e.Hitbox, offset))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool CheckOutside<T>(Point offset) where T : Component, IHasHitbox
|
||||
{
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (CheckOutside(e.Hitbox, offset))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public T First<T>() where T : Component, IHasHitbox
|
||||
{
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (Check(e.Hitbox))
|
||||
return e;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public T First<T>(Point offset) where T : Component, IHasHitbox
|
||||
{
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (Check(e.Hitbox, offset))
|
||||
return e;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public T FirstOutside<T>(Point offset) where T : Component, IHasHitbox
|
||||
{
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (CheckOutside(e.Hitbox, offset))
|
||||
return e;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public T LeftmostOutside<T>(Point offset) where T : Component, IHasHitbox
|
||||
{
|
||||
T ret = null;
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (CheckOutside(e.Hitbox, offset) && (ret == null || e.Hitbox.Left < ret.Hitbox.Left))
|
||||
ret = e;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public T RightmostOutside<T>(Point offset) where T : Component, IHasHitbox
|
||||
{
|
||||
T ret = null;
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (CheckOutside(e.Hitbox, offset) && (ret == null || e.Hitbox.Right > ret.Hitbox.Right))
|
||||
ret = e;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public T TopmostOutside<T>(Point offset) where T : Component, IHasHitbox
|
||||
{
|
||||
T ret = null;
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (CheckOutside(e.Hitbox, offset) && (ret == null || e.Hitbox.Top < ret.Hitbox.Top))
|
||||
ret = e;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public T BottommostOutside<T>(Point offset) where T : Component, IHasHitbox
|
||||
{
|
||||
T ret = null;
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (CheckOutside(e.Hitbox, offset) && (ret == null || e.Hitbox.Bottom > ret.Hitbox.Bottom))
|
||||
ret = e;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public List<T> All<T>(List<T> into) where T : Component, IHasHitbox
|
||||
{
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (Check(e.Hitbox))
|
||||
into.Add(e);
|
||||
|
||||
return into;
|
||||
}
|
||||
|
||||
public List<T> All<T>(Point offset, List<T> into) where T : Component, IHasHitbox
|
||||
{
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (Check(e.Hitbox, offset))
|
||||
into.Add(e);
|
||||
|
||||
return into;
|
||||
}
|
||||
|
||||
public List<T> AllOutside<T>(Point offset, List<T> into) where T : Component, IHasHitbox
|
||||
{
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (CheckOutside(e.Hitbox, offset))
|
||||
into.Add(e);
|
||||
|
||||
return into;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,16 @@
|
||||
using System.Collections;
|
||||
namespace Strawberry
|
||||
{
|
||||
public class OnCollide<T> : Component where T : Entity
|
||||
public class OnCollide<T> : Component, IHasHitbox, IUpdate where T : Component, IHasHitbox
|
||||
{
|
||||
public Hitbox Hitbox { get; private set; }
|
||||
|
||||
// Takes as parameter the T collided with. Return false to stop checking for collisions until next frame.
|
||||
public delegate bool(T) Action;
|
||||
|
||||
public this(delegate bool(T) action)
|
||||
: base(true, false)
|
||||
public this(Hitbox hitbox, delegate bool(T) action)
|
||||
{
|
||||
Hitbox = hitbox;
|
||||
Action = action;
|
||||
}
|
||||
|
||||
@ -17,13 +19,13 @@ namespace Strawberry
|
||||
delete Action;
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
public void Update()
|
||||
{
|
||||
if (Action != null)
|
||||
{
|
||||
let list = Entity.Scene.All<T>(scope List<T>());
|
||||
for (let t in list)
|
||||
if (Entity.Check(t) && !Action(t))
|
||||
if (Hitbox.Check(t) && !Action(t))
|
||||
break;
|
||||
}
|
||||
}
|
19
src/Components/Drawing/DrawHitbox.bf
Normal file
19
src/Components/Drawing/DrawHitbox.bf
Normal file
@ -0,0 +1,19 @@
|
||||
namespace Strawberry
|
||||
{
|
||||
public class DrawHitbox : Component, IHasHitbox, IDraw
|
||||
{
|
||||
public Hitbox Hitbox { get; private set; }
|
||||
public Color Color;
|
||||
|
||||
public this(Hitbox hitbox, Color color)
|
||||
{
|
||||
Hitbox = hitbox;
|
||||
Color = color;
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
Game.Batcher.Rect(Hitbox.SceneHitbox, Color);
|
||||
}
|
||||
}
|
||||
}
|
8
src/Components/Interfaces/IDebugDraw.bf
Normal file
8
src/Components/Interfaces/IDebugDraw.bf
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Strawberry
|
||||
{
|
||||
[ComponentInterface]
|
||||
public interface IDebugDraw
|
||||
{
|
||||
public void DebugDraw();
|
||||
}
|
||||
}
|
8
src/Components/Interfaces/IDraw.bf
Normal file
8
src/Components/Interfaces/IDraw.bf
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Strawberry
|
||||
{
|
||||
[ComponentInterface]
|
||||
public interface IDraw
|
||||
{
|
||||
public void Draw();
|
||||
}
|
||||
}
|
8
src/Components/Interfaces/IHasHitbox.bf
Normal file
8
src/Components/Interfaces/IHasHitbox.bf
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Strawberry
|
||||
{
|
||||
[ComponentInterface]
|
||||
public interface IHasHitbox
|
||||
{
|
||||
public Hitbox Hitbox { get; }
|
||||
}
|
||||
}
|
8
src/Components/Interfaces/ILateUpdate.bf
Normal file
8
src/Components/Interfaces/ILateUpdate.bf
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Strawberry
|
||||
{
|
||||
[ComponentInterface]
|
||||
public interface ILateUpdate
|
||||
{
|
||||
public void LateUpdate();
|
||||
}
|
||||
}
|
8
src/Components/Interfaces/IUpdate.bf
Normal file
8
src/Components/Interfaces/IUpdate.bf
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Strawberry
|
||||
{
|
||||
[ComponentInterface]
|
||||
public interface IUpdate
|
||||
{
|
||||
public void Update();
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ using System.Diagnostics;
|
||||
|
||||
namespace Strawberry
|
||||
{
|
||||
public class StateMachine<TIndex> : Component where TIndex : struct, IHashable
|
||||
public class StateMachine<TIndex> : Component, IUpdate where TIndex : struct, IHashable
|
||||
{
|
||||
private Dictionary<TIndex, State> states = new Dictionary<TIndex, State>() ~ delete _;
|
||||
private TIndex state;
|
||||
@ -14,7 +14,6 @@ namespace Strawberry
|
||||
public TIndex NextState { get; private set; }
|
||||
|
||||
public this(TIndex startState)
|
||||
: base(true, false)
|
||||
{
|
||||
NextState = PreviousState = state = startState;
|
||||
}
|
||||
@ -30,7 +29,7 @@ namespace Strawberry
|
||||
CallEnter();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
public void Update()
|
||||
{
|
||||
CallUpdate();
|
||||
}
|
||||
|
@ -3,35 +3,27 @@ using System.Diagnostics;
|
||||
|
||||
namespace Strawberry
|
||||
{
|
||||
public class Timer : Component
|
||||
public class Timer : Component, IUpdate
|
||||
{
|
||||
private float value;
|
||||
|
||||
public Action OnComplete ~ delete _;
|
||||
public bool RemoveOnComplete;
|
||||
|
||||
public this()
|
||||
: base(false, false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public this(Action onComplete, bool destroyOnComplete = false)
|
||||
: base(false, false)
|
||||
{
|
||||
OnComplete = onComplete;
|
||||
RemoveOnComplete = destroyOnComplete;
|
||||
}
|
||||
|
||||
public this(float value, Action onComplete, bool destroyOnComplete = false)
|
||||
: base(false, false)
|
||||
{
|
||||
Value = value;
|
||||
OnComplete = onComplete;
|
||||
RemoveOnComplete = destroyOnComplete;
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
public void Update()
|
||||
{
|
||||
if (value > 0)
|
||||
{
|
||||
@ -39,8 +31,6 @@ namespace Strawberry
|
||||
if (value <= 0)
|
||||
{
|
||||
value = 0;
|
||||
Active = false;
|
||||
|
||||
OnComplete?.Invoke();
|
||||
if (RemoveOnComplete)
|
||||
RemoveSelf();
|
||||
@ -60,7 +50,6 @@ namespace Strawberry
|
||||
set
|
||||
{
|
||||
this.value = Math.Max(0, value);
|
||||
Active = (this.value > 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,7 +57,6 @@ namespace Strawberry
|
||||
public void Clear()
|
||||
{
|
||||
value = 0;
|
||||
Active = false;
|
||||
}
|
||||
|
||||
static public implicit operator bool(Timer timer)
|
||||
|
@ -1,33 +1,42 @@
|
||||
using System;
|
||||
namespace Strawberry
|
||||
{
|
||||
public class Tween : Component
|
||||
public class Tween : Component, IUpdate
|
||||
{
|
||||
public Ease.Easer Easer ~ delete _;
|
||||
public delegate void(float) OnUpdate ~ delete _;
|
||||
public delegate void() OnComplete ~ delete _;
|
||||
public bool RemoveOnComplete;
|
||||
|
||||
public bool Playing { get; private set; }
|
||||
public float T { get; private set; }
|
||||
|
||||
public this(Ease.Easer easer = null, delegate void(float) onUpdate = null, delegate void() onComplete = null, bool removeOnComplete = true, bool start = true)
|
||||
: base(start, false)
|
||||
{
|
||||
Playing = start;
|
||||
Easer = easer;
|
||||
OnUpdate = onUpdate;
|
||||
OnComplete = onComplete;
|
||||
RemoveOnComplete = removeOnComplete;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public float Eased => Easer != null ? Easer(T) : T;
|
||||
|
||||
[Inline]
|
||||
public void Play()
|
||||
{
|
||||
T = 0;
|
||||
Active = true;
|
||||
Playing = true;
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
[Inline]
|
||||
public void Stop()
|
||||
{
|
||||
Playing = false;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
T = Math.Min(T + Time.Delta, 1);
|
||||
OnUpdate?.Invoke(Eased);
|
||||
@ -35,7 +44,7 @@ namespace Strawberry
|
||||
if (T >= 1)
|
||||
{
|
||||
OnComplete?.Invoke();
|
||||
Active = false;
|
||||
Playing = false;
|
||||
if (RemoveOnComplete)
|
||||
RemoveSelf();
|
||||
}
|
||||
|
@ -6,15 +6,6 @@ namespace Strawberry
|
||||
{
|
||||
public Entity Entity { get; private set; }
|
||||
|
||||
public bool Active;
|
||||
public bool Visible;
|
||||
|
||||
public this(bool active, bool visible)
|
||||
{
|
||||
Active = active;
|
||||
Visible = visible;
|
||||
}
|
||||
|
||||
private void Added(Entity entity)
|
||||
{
|
||||
Entity = entity;
|
||||
@ -27,13 +18,20 @@ namespace Strawberry
|
||||
|
||||
public virtual void Started() { }
|
||||
public virtual void Ended() { }
|
||||
public virtual void Update() { }
|
||||
public virtual void Draw() { }
|
||||
|
||||
[Inline]
|
||||
public void RemoveSelf()
|
||||
{
|
||||
Entity?.Remove(this);
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public Scene Scene => Entity?.Scene;
|
||||
|
||||
[Inline]
|
||||
public T SceneAs<T>() where T : Scene
|
||||
{
|
||||
return Entity.SceneAs<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
9
src/Core/ComponentInterfaceAttribute.bf
Normal file
9
src/Core/ComponentInterfaceAttribute.bf
Normal file
@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Strawberry
|
||||
{
|
||||
public struct ComponentInterfaceAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -3,13 +3,9 @@ using System.Collections;
|
||||
|
||||
namespace Strawberry
|
||||
{
|
||||
public abstract class Entity
|
||||
public sealed class Entity
|
||||
{
|
||||
public Scene Scene { get; private set; }
|
||||
public int Priority;
|
||||
public bool Active = true;
|
||||
public bool Visible = true;
|
||||
public bool Collidable = true;
|
||||
public bool DeleteOnRemove = true;
|
||||
|
||||
private List<Component> components = new List<Component>() ~ delete _;
|
||||
@ -40,32 +36,18 @@ namespace Strawberry
|
||||
Scene = null;
|
||||
}
|
||||
|
||||
public virtual void Started()
|
||||
public void Started()
|
||||
{
|
||||
for (var c in components)
|
||||
c.Started();
|
||||
}
|
||||
|
||||
public virtual void Ended()
|
||||
public void Ended()
|
||||
{
|
||||
for (var c in components)
|
||||
c.Ended();
|
||||
}
|
||||
|
||||
public virtual void Update()
|
||||
{
|
||||
for (var c in components)
|
||||
if (c.Active)
|
||||
c.Update();
|
||||
}
|
||||
|
||||
public virtual void Draw()
|
||||
{
|
||||
for (var c in components)
|
||||
if (c.Visible)
|
||||
c.Draw();
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public void RemoveSelf()
|
||||
{
|
||||
@ -195,302 +177,13 @@ namespace Strawberry
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Hitbox =====
|
||||
|
||||
public Rect Hitbox;
|
||||
|
||||
public int Width
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return Hitbox.Width;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
set
|
||||
{
|
||||
Hitbox.Width = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int Height
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return Hitbox.Height;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
set
|
||||
{
|
||||
Hitbox.Height = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Rect SceneHitbox
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return Hitbox + Position;
|
||||
}
|
||||
}
|
||||
|
||||
public Rect SceneHitboxOutline
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
Rect hb = Hitbox + Position;
|
||||
hb.X -= 1;
|
||||
hb.Y -= 1;
|
||||
hb.Width += 2;
|
||||
hb.Height += 2;
|
||||
return hb;
|
||||
}
|
||||
}
|
||||
|
||||
public int Left
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return Position.X + Hitbox.Left;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
set
|
||||
{
|
||||
X = value - Hitbox.Left;
|
||||
}
|
||||
}
|
||||
|
||||
public int Right
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return Position.X + Hitbox.Right;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
set
|
||||
{
|
||||
Y = value - Hitbox.Right;
|
||||
}
|
||||
}
|
||||
|
||||
public int Top
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return Position.Y + Hitbox.Top;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
set
|
||||
{
|
||||
Y = value - Hitbox.Top;
|
||||
}
|
||||
}
|
||||
|
||||
public int Bottom
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return Position.Y + Hitbox.Bottom;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
set
|
||||
{
|
||||
Y = value - Hitbox.Bottom;
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Collisions =====
|
||||
|
||||
public bool Check(Point point)
|
||||
{
|
||||
return SceneHitbox.Contains(point);
|
||||
}
|
||||
|
||||
public bool Check(Rect rect)
|
||||
{
|
||||
return SceneHitbox.Intersects(rect);
|
||||
}
|
||||
|
||||
public bool Check(Grid grid)
|
||||
{
|
||||
return grid != null && grid.Check(SceneHitbox);
|
||||
}
|
||||
|
||||
public bool Check(Grid grid, Point offset)
|
||||
{
|
||||
return grid != null && grid.Check(SceneHitbox + offset);
|
||||
}
|
||||
|
||||
public bool Check(Entity other)
|
||||
{
|
||||
return other.Collidable && SceneHitbox.Intersects(other.SceneHitbox);
|
||||
}
|
||||
|
||||
public bool Check(Entity other, Point offset)
|
||||
{
|
||||
return other.Collidable && (SceneHitbox + offset).Intersects(other.SceneHitbox);
|
||||
}
|
||||
|
||||
public bool CheckOutside(Entity other, Point offset)
|
||||
{
|
||||
return other.Collidable && !SceneHitbox.Intersects(other.SceneHitbox) && (SceneHitbox + offset).Intersects(other.SceneHitbox);
|
||||
}
|
||||
|
||||
public bool Check<T>() where T : Entity
|
||||
{
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (Check(e))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Check<T>(Point offset) where T : Entity
|
||||
{
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (Check(e, offset))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool CheckOutside<T>(Point offset) where T : Entity
|
||||
{
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (CheckOutside(e, offset))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public T First<T>() where T : Entity
|
||||
{
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (Check(e))
|
||||
return e;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public T First<T>(Point offset) where T : Entity
|
||||
{
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (Check(e, offset))
|
||||
return e;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public T FirstOutside<T>(Point offset) where T : Entity
|
||||
{
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (CheckOutside(e, offset))
|
||||
return e;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public T LeftmostOutside<T>(Point offset) where T : Entity
|
||||
{
|
||||
T ret = null;
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (CheckOutside(e, offset) && (ret == null || e.Left < ret.Left))
|
||||
ret = e;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public T RightmostOutside<T>(Point offset) where T : Entity
|
||||
{
|
||||
T ret = null;
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (CheckOutside(e, offset) && (ret == null || e.Right > ret.Right))
|
||||
ret = e;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public T TopmostOutside<T>(Point offset) where T : Entity
|
||||
{
|
||||
T ret = null;
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (CheckOutside(e, offset) && (ret == null || e.Top < ret.Top))
|
||||
ret = e;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public T BottommostOutside<T>(Point offset) where T : Entity
|
||||
{
|
||||
T ret = null;
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (CheckOutside(e, offset) && (ret == null || e.Bottom > ret.Bottom))
|
||||
ret = e;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public List<T> All<T>(List<T> into) where T : Entity
|
||||
{
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (Check(e))
|
||||
into.Add(e);
|
||||
|
||||
return into;
|
||||
}
|
||||
|
||||
public List<T> All<T>(Point offset, List<T> into) where T : Entity
|
||||
{
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (Check(e, offset))
|
||||
into.Add(e);
|
||||
|
||||
return into;
|
||||
}
|
||||
|
||||
public List<T> AllOutside<T>(Point offset, List<T> into) where T : Entity
|
||||
{
|
||||
for (var e in Scene.All<T>(scope List<T>()))
|
||||
if (CheckOutside(e, offset))
|
||||
into.Add(e);
|
||||
|
||||
return into;
|
||||
}
|
||||
|
||||
// ===== Misc =====
|
||||
|
||||
public void DrawHitbox(Color color)
|
||||
{
|
||||
Game.Batcher.Rect(SceneHitbox, color);
|
||||
}
|
||||
|
||||
public void DrawHitboxOutline(Color color)
|
||||
{
|
||||
Game.Batcher.Rect(SceneHitboxOutline, color);
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public T SceneAs<T>() where T : Scene
|
||||
{
|
||||
Runtime.Assert(Scene is T, "Scene type mismatch!");
|
||||
return Scene as T;
|
||||
}
|
||||
|
||||
static public int Compare(Entity a, Entity b)
|
||||
{
|
||||
return a.Priority <=> b.Priority;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,6 @@ namespace Strawberry
|
||||
private Scene scene;
|
||||
private Scene switchToScene;
|
||||
private bool updating;
|
||||
private Dictionary<Type, List<Type>> entityAssignableLists;
|
||||
private Dictionary<Type, List<Type>> componentAssignableLists;
|
||||
|
||||
public PlatformLayer PlatformLayer { get; private set; }
|
||||
public Batcher Batcher { get; private set; }
|
||||
@ -60,9 +58,9 @@ namespace Strawberry
|
||||
VirtualInputs = new List<VirtualInput>();
|
||||
Input.[Friend]Init();
|
||||
|
||||
BuildTypeLists();
|
||||
Tracker.[Friend]BuildAssignmentLists();
|
||||
Assets.LoadAll();
|
||||
Strawberry.Console.Init();
|
||||
Strawberry.StrwConsole.Init();
|
||||
}
|
||||
|
||||
public ~this()
|
||||
@ -83,9 +81,8 @@ namespace Strawberry
|
||||
}
|
||||
|
||||
Assets.DisposeAll();
|
||||
DisposeTypeLists();
|
||||
Input.[Friend]Dispose();
|
||||
Strawberry.Console.Dispose();
|
||||
Strawberry.StrwConsole.Dispose();
|
||||
|
||||
delete Batcher;
|
||||
|
||||
@ -157,7 +154,7 @@ namespace Strawberry
|
||||
Time.Elapsed += Time.Delta;
|
||||
}
|
||||
|
||||
Strawberry.Console.[Friend]Update();
|
||||
Strawberry.StrwConsole.[Friend]Update();
|
||||
}
|
||||
|
||||
private void Render()
|
||||
@ -171,8 +168,8 @@ namespace Strawberry
|
||||
{
|
||||
Scene?.Draw();
|
||||
|
||||
if (Strawberry.Console.Enabled)
|
||||
Strawberry.Console.[Friend]Draw();
|
||||
if (Strawberry.StrwConsole.Enabled)
|
||||
Strawberry.StrwConsole.[Friend]Draw();
|
||||
|
||||
Batcher.Draw();
|
||||
}
|
||||
@ -191,57 +188,5 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ namespace Strawberry
|
||||
private List<Entity> entities;
|
||||
private HashSet<Entity> toRemove;
|
||||
private HashSet<Entity> toAdd;
|
||||
private Dictionary<Type, List<Entity>> entityTracker;
|
||||
private Dictionary<Type, List<Component>> componentTracker;
|
||||
|
||||
public this()
|
||||
@ -19,12 +18,10 @@ namespace Strawberry
|
||||
toAdd = new HashSet<Entity>();
|
||||
toRemove = new HashSet<Entity>();
|
||||
|
||||
entityTracker = new Dictionary<Type, List<Entity>>();
|
||||
for (let type in Game.[Friend]entityAssignableLists.Keys)
|
||||
entityTracker.Add(type, new List<Entity>());
|
||||
|
||||
componentTracker = new Dictionary<Type, List<Component>>();
|
||||
for (let type in Game.[Friend]componentAssignableLists.Keys)
|
||||
for (let type in Tracker.AssignmentLists.Keys)
|
||||
componentTracker.Add(type, new List<Component>());
|
||||
for (let type in Tracker.Interfaces)
|
||||
componentTracker.Add(type, new List<Component>());
|
||||
}
|
||||
|
||||
@ -42,10 +39,6 @@ namespace Strawberry
|
||||
|
||||
delete toRemove;
|
||||
|
||||
for (let list in entityTracker.Values)
|
||||
delete list;
|
||||
delete entityTracker;
|
||||
|
||||
for (let list in componentTracker.Values)
|
||||
delete list;
|
||||
delete componentTracker;
|
||||
@ -59,26 +52,23 @@ namespace Strawberry
|
||||
public virtual void Update()
|
||||
{
|
||||
UpdateLists();
|
||||
for (let e in entities)
|
||||
if (e.Active)
|
||||
e.Update();
|
||||
|
||||
|
||||
}
|
||||
|
||||
public virtual void Draw()
|
||||
{
|
||||
for (let e in entities)
|
||||
if (e.Visible)
|
||||
e.Draw();
|
||||
|
||||
}
|
||||
|
||||
public T Add<T>(T e) where T : Entity
|
||||
public Entity Add(Entity e)
|
||||
{
|
||||
if (e.Scene == null)
|
||||
toAdd.Add(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
public T Remove<T>(T e) where T : Entity
|
||||
public Entity Remove(Entity e)
|
||||
{
|
||||
if (e.Scene == this)
|
||||
toRemove.Add(e);
|
||||
@ -92,7 +82,6 @@ namespace Strawberry
|
||||
for (let e in toRemove)
|
||||
{
|
||||
entities.Remove(e);
|
||||
UntrackEntity(e);
|
||||
e.[Friend]Removed();
|
||||
if (e.DeleteOnRemove)
|
||||
delete e;
|
||||
@ -106,11 +95,8 @@ namespace Strawberry
|
||||
for (let e in toAdd)
|
||||
{
|
||||
entities.Add(e);
|
||||
TrackEntity(e);
|
||||
e.[Friend]Added(this);
|
||||
}
|
||||
|
||||
entities.Sort(scope => Entity.Compare);
|
||||
}
|
||||
|
||||
for (let e in entities)
|
||||
@ -126,33 +112,15 @@ namespace Strawberry
|
||||
|
||||
// Tracking
|
||||
|
||||
private void TrackEntity(Entity e)
|
||||
{
|
||||
for (let t in Game.[Friend]entityAssignableLists[e.GetType()])
|
||||
entityTracker[t].Add(e);
|
||||
|
||||
for (let c in e.[Friend]components)
|
||||
TrackComponent(c);
|
||||
}
|
||||
|
||||
private void UntrackEntity(Entity e)
|
||||
{
|
||||
for (let t in Game.[Friend]entityAssignableLists[e.GetType()])
|
||||
entityTracker[t].Remove(e);
|
||||
|
||||
for (let c in e.[Friend]components)
|
||||
UntrackComponent(c);
|
||||
}
|
||||
|
||||
private void TrackComponent(Component c)
|
||||
{
|
||||
for (let t in Game.[Friend]componentAssignableLists[c.GetType()])
|
||||
for (let t in Tracker.AssignmentLists[c.GetType()])
|
||||
componentTracker[t].Add(c);
|
||||
}
|
||||
|
||||
private void UntrackComponent(Component c)
|
||||
{
|
||||
for (let t in Game.[Friend]componentAssignableLists[c.GetType()])
|
||||
for (let t in Tracker.AssignmentLists[c.GetType()])
|
||||
componentTracker[t].Remove(c);
|
||||
}
|
||||
|
||||
@ -171,75 +139,6 @@ namespace Strawberry
|
||||
return (TimeElapsed - offset) % (interval * 2) >= interval;
|
||||
}
|
||||
|
||||
// Finding Entities
|
||||
|
||||
public int Count<T>() where T : Entity
|
||||
{
|
||||
return entityTracker[typeof(T)].Count;
|
||||
}
|
||||
|
||||
public bool Check<T>(Point point) where T : Entity
|
||||
{
|
||||
for (let e in entityTracker[typeof(T)])
|
||||
if (e.Check(point))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Check<T>(Rect rect) where T : Entity
|
||||
{
|
||||
for (let e in entityTracker[typeof(T)])
|
||||
if (e.Check(rect))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public T First<T>() where T : Entity
|
||||
{
|
||||
for (let e in entityTracker[typeof(T)])
|
||||
return e as T;
|
||||
return null;
|
||||
}
|
||||
|
||||
public T First<T>(Point point) where T : Entity
|
||||
{
|
||||
for (let e in entityTracker[typeof(T)])
|
||||
if (e.Check(point))
|
||||
return e as T;
|
||||
return null;
|
||||
}
|
||||
|
||||
public T First<T>(Rect rect) where T : Entity
|
||||
{
|
||||
for (let e in entityTracker[typeof(T)])
|
||||
if (e.Check(rect))
|
||||
return e as T;
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<T> All<T>(List<T> into) where T : Entity
|
||||
{
|
||||
for (let e in entityTracker[typeof(T)])
|
||||
into.Add(e as T);
|
||||
return into;
|
||||
}
|
||||
|
||||
public List<T> All<T>(Point point, List<T> into) where T : Entity
|
||||
{
|
||||
for (let e in entityTracker[typeof(T)])
|
||||
if (e.Check(point))
|
||||
into.Add(e as T);
|
||||
return into;
|
||||
}
|
||||
|
||||
public List<T> All<T>(Rect rect, List<T> into) where T : Entity
|
||||
{
|
||||
for (let e in entityTracker[typeof(T)])
|
||||
if (e.Check(rect))
|
||||
into.Add(e as T);
|
||||
return into;
|
||||
}
|
||||
|
||||
/*
|
||||
Finding Components
|
||||
*/
|
||||
@ -249,25 +148,64 @@ namespace Strawberry
|
||||
return componentTracker[typeof(T)].Count;
|
||||
}
|
||||
|
||||
public List<T> All<T>(List<T> into) where T : Component
|
||||
public bool Check<T>(Point point) where T : Component, IHasHitbox
|
||||
{
|
||||
for (let c in componentTracker[typeof(T)])
|
||||
for (T c in componentTracker[typeof(T)])
|
||||
if (c.Hitbox.Check(point))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Check<T>(Rect rect) where T : Component, IHasHitbox
|
||||
{
|
||||
for (T c in componentTracker[typeof(T)])
|
||||
if (c.Hitbox.Check(rect))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public T First<T>() where T : Component, IHasHitbox
|
||||
{
|
||||
for (T c in componentTracker[typeof(T)])
|
||||
return c;
|
||||
return null;
|
||||
}
|
||||
|
||||
public T First<T>(Point point) where T : Component, IHasHitbox
|
||||
{
|
||||
for (T c in componentTracker[typeof(T)])
|
||||
if (c.Hitbox.Check(point))
|
||||
return c as T;
|
||||
return null;
|
||||
}
|
||||
|
||||
public T First<T>(Rect rect) where T : Component, IHasHitbox
|
||||
{
|
||||
for (T c in componentTracker[typeof(T)])
|
||||
if (c.Hitbox.Check(rect))
|
||||
return c as T;
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<T> All<T>(List<T> into) where T : Component, IHasHitbox
|
||||
{
|
||||
for (T c in componentTracker[typeof(T)])
|
||||
into.Add(c as T);
|
||||
return into;
|
||||
}
|
||||
|
||||
public List<T> All<T>(Point point, List<T> into) where T : Component
|
||||
public List<T> All<T>(Point point, List<T> into) where T : Component, IHasHitbox
|
||||
{
|
||||
for (let c in componentTracker[typeof(T)])
|
||||
if (c.Entity.Check(point))
|
||||
for (T c in componentTracker[typeof(T)])
|
||||
if (c.Hitbox.Check(point))
|
||||
into.Add(c as T);
|
||||
return into;
|
||||
}
|
||||
|
||||
public List<T> All<T>(Rect rect, List<T> into) where T : Component
|
||||
public List<T> All<T>(Rect rect, List<T> into) where T : Component, IHasHitbox
|
||||
{
|
||||
for (let c in componentTracker[typeof(T)])
|
||||
if (c.Entity.Check(rect))
|
||||
for (T c in componentTracker[typeof(T)])
|
||||
if (c.Hitbox.Check(rect))
|
||||
into.Add(c as T);
|
||||
return into;
|
||||
}
|
||||
|
@ -143,9 +143,14 @@ namespace Strawberry
|
||||
return p.X >= 0 && p.Y >= 0 && p.X < CellsX && p.Y < CellsY;
|
||||
}
|
||||
|
||||
public bool Check(Entity entity)
|
||||
public bool Check(Hitbox hitbox)
|
||||
{
|
||||
return Check(entity.SceneHitbox);
|
||||
return Check(hitbox.SceneHitbox);
|
||||
}
|
||||
|
||||
public bool Check(IHasHitbox other)
|
||||
{
|
||||
return Check(other.Hitbox.SceneHitbox);
|
||||
}
|
||||
|
||||
public bool Check(Rect rect)
|
||||
|
@ -19,7 +19,7 @@ namespace Strawberry
|
||||
}
|
||||
|
||||
[Reflect]
|
||||
static public class Console
|
||||
static public class StrwConsole
|
||||
{
|
||||
static public bool Open;
|
||||
|
38
src/Static/Tracker.bf
Normal file
38
src/Static/Tracker.bf
Normal file
@ -0,0 +1,38 @@
|
||||
using System.Collections;
|
||||
using System;
|
||||
namespace Strawberry
|
||||
{
|
||||
static public class Tracker
|
||||
{
|
||||
static public Dictionary<Type, List<Type>> AssignmentLists = new .() ~ DeleteDictionaryAndValues!(_);
|
||||
static public List<Type> Interfaces = new .() ~ delete _;
|
||||
|
||||
static private void BuildAssignmentLists()
|
||||
{
|
||||
// Find all interfaces with ComponentInterfaceAttribute
|
||||
for (let type in Type.Enumerator())
|
||||
if (type.IsInterface && type.HasCustomAttribute<ComponentInterfaceAttribute>())
|
||||
Interfaces.Add(type);
|
||||
|
||||
/*
|
||||
For each Type that extends Component, we build a list of all the tracked Interfaces it implements.
|
||||
We use these lists later to bucket Components as they are added to the Scene.
|
||||
This allows us to retrieve Components by their type or by any of their implemented interface types.
|
||||
*/
|
||||
|
||||
for (let type in Type.Enumerator())
|
||||
{
|
||||
if (type != typeof(Component) && type.IsSubtypeOf(typeof(Component)))
|
||||
{
|
||||
let list = new List<Type>();
|
||||
list.Add(type);
|
||||
for (let check in Interfaces)
|
||||
if (type.IsSubtypeOf(check))
|
||||
list.Add(check);
|
||||
|
||||
AssignmentLists.Add(type, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user