diff --git a/SampleGame/BeefProj.toml b/SampleGame/BeefProj.toml index 76cb18f..5a27c4c 100644 --- a/SampleGame/BeefProj.toml +++ b/SampleGame/BeefProj.toml @@ -15,4 +15,3 @@ ReflectAlwaysInclude = "IncludeAll" Type = "Folder" Name = "Physics" AutoInclude = true -Source = ["Physics.bf", "Solid.bf", "JumpThru.bf"] diff --git a/SampleGame/src/Entities/MovingJumpThru.bf b/SampleGame/src/Entities/MovingJumpThru.bf new file mode 100644 index 0000000..ef9c715 --- /dev/null +++ b/SampleGame/src/Entities/MovingJumpThru.bf @@ -0,0 +1,61 @@ +namespace Strawberry.Sample +{ + public class MovingJumpThru : Component, IUpdate + { + static public Entity Create(Point pos, int width, Point moveTo, float moveTime) + { + let e = new Entity(pos); + + let hitbox = e.Add(new Hitbox(0, 0, width, 4)); + let jumpThru = e.Add(new JumpThru(hitbox)); + e.Add(new MovingJumpThru(jumpThru, moveTo, moveTime)); + e.Add(new DrawHitbox(hitbox, .LightGray)); + + return e; + } + + private JumpThru jumpThru; + private Point moveFrom; + private Point moveTo; + private float moveTime; + private float movingLerp = 0; + private bool movingPositive = true; + + public this(JumpThru jumpThru, Point moveTo, float moveTime) + { + this.jumpThru = jumpThru; + this.moveTo = moveTo; + this.moveTime = moveTime; + } + + protected override void Awake() + { + moveFrom = Entity.Position; + } + + public void Update() + { + if (movingPositive) + { + movingLerp += Time.Delta / moveTime; + if (movingLerp >= 1) + { + movingLerp = 1; + movingPositive = false; + } + } + else + { + movingLerp -= Time.Delta / moveTime; + if (movingLerp <= 0) + { + movingLerp = 0; + movingPositive = true; + } + } + + let target = Vector.Lerp(moveFrom, moveTo, Ease.CubeInOut(movingLerp)); + jumpThru.MoveTo(target); + } + } +} diff --git a/SampleGame/src/Player.bf b/SampleGame/src/Entities/Player.bf similarity index 90% rename from SampleGame/src/Player.bf rename to SampleGame/src/Entities/Player.bf index 8201370..513abc3 100644 --- a/SampleGame/src/Player.bf +++ b/SampleGame/src/Entities/Player.bf @@ -2,7 +2,7 @@ using System; namespace Strawberry.Sample { - public class Player : Component, IUpdate, IDraw + public class Player : Component, IUpdate { static public Entity Create(Point pos) { @@ -10,18 +10,19 @@ namespace Strawberry.Sample e.Add(new Player()); let hitbox = e.Add(new Hitbox(-4, -8, 16, 16)); - e.Add(new Physics(hitbox)); + e.Add(new Actor(hitbox)); + e.Add(new DrawHitbox(hitbox, .Red)); return e; } public Vector Speed; - private Physics physics; + private Actor physics; private Timer tJumpGrace; private Timer tVarJump; - public override void Added() + protected override void Added() { base.Added(); @@ -29,9 +30,9 @@ namespace Strawberry.Sample tVarJump = Entity.Add(new Timer()); } - public override void Awake() + protected override void Awake() { - physics = Entity.First(); + physics = Entity.First(); } public void Update() @@ -111,10 +112,5 @@ namespace Strawberry.Sample Speed.Y = 0; physics.ZeroRemainderY(); } - - public void Draw() - { - physics.Hitbox.DebugDraw(); - } } } diff --git a/SampleGame/src/Entities/StaticGeometry.bf b/SampleGame/src/Entities/StaticGeometry.bf new file mode 100644 index 0000000..f82b88b --- /dev/null +++ b/SampleGame/src/Entities/StaticGeometry.bf @@ -0,0 +1,29 @@ +using System; + +namespace Strawberry.Sample +{ + static public class StaticGeometry + { + static public Entity CreateSolid(Point pos, Rect bounds) + { + let e = new Entity(pos); + + let hitbox = e.Add(new Hitbox(bounds)); + e.Add(new Solid(hitbox)); + e.Add(new DrawHitbox(hitbox, .White)); + + return e; + } + + static public Entity CreateJumpThru(Point pos, int width) + { + let e = new Entity(pos); + + let hitbox = e.Add(new Hitbox(0, 0, width, 4)); + e.Add(new JumpThru(hitbox)); + e.Add(new DrawHitbox(hitbox, .LightGray)); + + return e; + } + } +} \ No newline at end of file diff --git a/SampleGame/src/Level.bf b/SampleGame/src/Level.bf index 429d50c..b51fab1 100644 --- a/SampleGame/src/Level.bf +++ b/SampleGame/src/Level.bf @@ -7,10 +7,9 @@ namespace Strawberry.Sample public this() { Add(Player.Create(.(50, 50))); - - Add(new OldSolid(.(0, 168), .(0, 0, 320, 12))); - Add(new OldJumpThru(.(200, 132), 48)); - Add(new MovingJumpThru(.(136, 100), 32, .(124, 140), 2f)); + Add(MovingJumpThru.Create(.(136, 100), 32, .(124, 140), 2f)); + Add(StaticGeometry.CreateSolid(.(0, 168), .(0, 0, 320, 12))); + Add(StaticGeometry.CreateJumpThru(.(200, 132), 48)); } } } diff --git a/SampleGame/src/MovingJumpThru.bf b/SampleGame/src/MovingJumpThru.bf deleted file mode 100644 index 0aa7517..0000000 --- a/SampleGame/src/MovingJumpThru.bf +++ /dev/null @@ -1,50 +0,0 @@ -namespace Strawberry.Sample -{ - public class MovingJumpThru : JumpThru - { - private Point moveFrom; - private Point moveTo; - private float moveTime; - - private float movingLerp; - private bool movingPositive; - - public this(Point pos, int width, Point moveTo, float moveTime) - : base(pos, width) - { - moveFrom = Position; - this.moveTo = moveTo; - this.moveTime = moveTime; - - movingLerp = 0; - movingPositive = true; - } - - public override void Update() - { - base.Update(); - - if (movingPositive) - { - movingLerp += Time.Delta / moveTime; - if (movingLerp >= 1) - { - movingLerp = 1; - movingPositive = false; - } - } - else - { - movingLerp -= Time.Delta / moveTime; - if (movingLerp <= 0) - { - movingLerp = 0; - movingPositive = true; - } - } - - let target = Vector.Lerp(moveFrom, moveTo, Ease.CubeInOut(movingLerp)); - MoveTo(target); - } - } -} diff --git a/SampleGame/src/Physics/Physics.bf b/SampleGame/src/Physics/Actor.bf similarity index 71% rename from SampleGame/src/Physics/Physics.bf rename to SampleGame/src/Physics/Actor.bf index 02d9811..f5eca0b 100644 --- a/SampleGame/src/Physics/Physics.bf +++ b/SampleGame/src/Physics/Actor.bf @@ -1,20 +1,32 @@ using System; +using System.Collections; namespace Strawberry.Sample { - public class Physics : Component, IHasHitbox, IUpdate + public class Actor : Component, IHasHitbox, IUpdate { public Hitbox Hitbox { get; private set; } public Vector Speed; private Vector remainder; + public Level Level => Entity.SceneAs(); + public Vector ExactPosition => Entity.Position + remainder; + public this(Hitbox hitbox) { Hitbox = hitbox; } - public Level Level => Entity.SceneAs(); + public void Update() + { + MoveX(Speed.X * Time.Delta); + MoveY(Speed.Y * Time.Delta); + } + + /* + Collision Helpers + */ public bool Check(Level level) { @@ -31,21 +43,19 @@ namespace Strawberry.Sample return Hitbox.Check(.(0, distance)) || Check(Level, .(0, distance)) || Hitbox.CheckOutside(.(0, distance)); } - public virtual bool IsRiding(Solid solid) + public bool IsRiding(Solid solid) { return Hitbox.Check(solid, .(0, 1)); } - public virtual bool IsRiding(JumpThru jumpThru) + public bool IsRiding(JumpThru jumpThru) { return Hitbox.CheckOutside(jumpThru, .(0, 1)); } - public void Update() - { - MoveX(Speed.X * Time.Delta); - MoveY(Speed.Y * Time.Delta); - } + /* + Movement + */ public bool MoveX(float amount, delegate void(Collision) onCollide = null) { @@ -74,15 +84,15 @@ namespace Strawberry.Sample } [Inline] - public void MoveToX(float x) + public void MoveToX(float x, delegate void(Collision) onCollide = null) { - MoveX(x - (Entity.X + remainder.X), null); + MoveX(x - (Entity.X + remainder.X), onCollide); } [Inline] - public void MoveToY(float y) + public void MoveToY(float y, delegate void(Collision) onCollide = null) { - MoveY(y - (Entity.Y + remainder.Y), null); + MoveY(y - (Entity.Y + remainder.Y), onCollide); } public bool MoveExactX(int amount, delegate void(Collision) onCollide = null) @@ -116,22 +126,45 @@ namespace Strawberry.Sample int move = amount; int sign = Math.Sign(amount); - while (move != 0) + if (move > 0) { - if (Check(Level, .(0, sign)) || Hitbox.Check(.(0, sign)) || Hitbox.CheckOutside(.(0, sign))) + while (move != 0) { - let c = Collision( - Cardinals.FromPoint(Point.Down * sign), - Math.Abs(amount), - Math.Abs(amount - move) - ); + if (Check(Level, .(0, sign)) || Hitbox.Check(.(0, sign)) || Hitbox.CheckOutside(.(0, sign))) + { + let c = Collision( + Cardinals.FromPoint(Point.Down * sign), + Math.Abs(amount), + Math.Abs(amount - move) + ); - onCollide?.Invoke(c); - return true; + onCollide?.Invoke(c); + return true; + } + + Entity.Y += sign; + move -= sign; } + } + else + { + while (move != 0) + { + if (Check(Level, .(0, sign)) || Hitbox.Check(.(0, sign))) + { + let c = Collision( + Cardinals.FromPoint(Point.Down * sign), + Math.Abs(amount), + Math.Abs(amount - move) + ); - Entity.Y += sign; - move -= sign; + onCollide?.Invoke(c); + return true; + } + + Entity.Y += sign; + move -= sign; + } } return false; diff --git a/SampleGame/src/physics/JumpThru.bf b/SampleGame/src/physics/JumpThru.bf index 68223a1..dff7f53 100644 --- a/SampleGame/src/physics/JumpThru.bf +++ b/SampleGame/src/physics/JumpThru.bf @@ -1,4 +1,5 @@ using System.Collections; +using System.Collections; namespace Strawberry.Sample { @@ -6,9 +7,54 @@ namespace Strawberry.Sample { public Hitbox Hitbox { get; private set; } + private Vector remainder; + + public Vector ExactPosition => Entity.Position + remainder; + public this(Hitbox hitbox) { Hitbox = hitbox; } + + public void FindRiders(List into) + { + for (let a in Scene.All(scope .())) + if (a.IsRiding(this)) + into.Add(a); + } + + public void Move(Vector amount) + { + remainder += amount; + Point move = remainder.Round(); + MoveExact(move); + } + + public void MoveTo(Vector pos) + { + Move(pos - ExactPosition); + } + + public void MoveExact(Point amount) + { + if (amount != .Zero) + { + if (Hitbox.Collidable) + { + List riders = FindRiders(..scope .()); + Hitbox.Collidable = false; + + for (let r in riders) + { + r.MoveExactX(amount.X); + r.MoveExactY(amount.Y); + } + + Hitbox.Collidable = true; + } + + Entity.Position += amount; + } + } } } diff --git a/SampleGame/src/physics/Solid.bf b/SampleGame/src/physics/Solid.bf index 6a7041d..f75f207 100644 --- a/SampleGame/src/physics/Solid.bf +++ b/SampleGame/src/physics/Solid.bf @@ -6,9 +6,15 @@ namespace Strawberry.Sample { public Hitbox Hitbox { get; private set; } + private Vector remainder; + + public Vector ExactPosition => Entity.Position + remainder; + public this(Hitbox hitbox) { Hitbox = hitbox; } + + } } diff --git a/src/Components/Collision/Hitbox.bf b/src/Components/Collision/Hitbox.bf index 20490c3..2ea94d8 100644 --- a/src/Components/Collision/Hitbox.bf +++ b/src/Components/Collision/Hitbox.bf @@ -335,19 +335,19 @@ namespace Strawberry [Inline] public bool Check(Hitbox other) { - return other.Collidable && SceneHitbox.Intersects(other.SceneHitbox); + return other.Collidable && other.Entity != Entity && SceneHitbox.Intersects(other.SceneHitbox); } [Inline] public bool Check(Hitbox other, Point offset) { - return other.Collidable && (SceneHitbox + offset).Intersects(other.SceneHitbox); + return other.Collidable && other.Entity != Entity && (SceneHitbox + offset).Intersects(other.SceneHitbox); } [Inline] public bool CheckOutside(Hitbox other, Point offset) { - return other.Collidable && !SceneHitbox.Intersects(other.SceneHitbox) && (SceneHitbox + offset).Intersects(other.SceneHitbox); + return other.Collidable && other.Entity != Entity && !SceneHitbox.Intersects(other.SceneHitbox) && (SceneHitbox + offset).Intersects(other.SceneHitbox); } [Inline] diff --git a/src/Components/Logic/StateMachine.bf b/src/Components/Logic/StateMachine.bf index 8252bff..3f31ea6 100644 --- a/src/Components/Logic/StateMachine.bf +++ b/src/Components/Logic/StateMachine.bf @@ -24,7 +24,7 @@ namespace Strawberry delete s; } - public override void Awake() + protected override void Awake() { CallEnter(); } diff --git a/src/Components/Logic/Updater.bf b/src/Components/Logic/Updater.bf new file mode 100644 index 0000000..c09ddd4 --- /dev/null +++ b/src/Components/Logic/Updater.bf @@ -0,0 +1,19 @@ +using System; + +namespace Strawberry +{ + public class Updater : Component, IUpdate + { + private delegate void() update ~ delete _; + + public this(delegate void() update) + { + this.update = update; + } + + public void Update() + { + update?.Invoke(); + } + } +} \ No newline at end of file diff --git a/src/Core/Component.bf b/src/Core/Component.bf index 2dccd7f..2b038ae 100644 --- a/src/Core/Component.bf +++ b/src/Core/Component.bf @@ -7,9 +7,9 @@ namespace Strawberry public Entity Entity { get; private set; } public bool IsAwake { get; private set; } - public virtual void Added() { } - public virtual void Awake() { } - public virtual void End() { } + protected virtual void Added() { } + protected virtual void Awake() { } + protected virtual void End() { } [Inline] public void Destroy() diff --git a/src/Core/Entity.bf b/src/Core/Entity.bf index 218522a..32b814e 100644 --- a/src/Core/Entity.bf +++ b/src/Core/Entity.bf @@ -35,7 +35,7 @@ namespace Strawberry { for (var c in components) { - c.End(); + c.[Friend]End(); c.[Friend]IsAwake = false; Scene.[Friend]UntrackComponent(c); } @@ -49,7 +49,7 @@ namespace Strawberry { if (!c.[Friend]IsAwake) { - c.Awake(); + c.[Friend]Awake(); c.[Friend]IsAwake = true; } } @@ -67,6 +67,11 @@ namespace Strawberry Components */ + public bool Has() where T : Component + { + return First() != null; + } + public T First() where T : Component { for (let c in components) @@ -75,7 +80,7 @@ namespace Strawberry return null; } - public ICollection All(ICollection into) where T : Component + public List All(List into) where T : Component { for (let c in components) if (c is T) @@ -119,14 +124,14 @@ namespace Strawberry components.Add(c); Scene.[Friend]TrackComponent(c); c.[Friend]Entity = this; - c.Added(); + c.[Friend]Added(); } if (IsAwake) { for (var c in toAdd) { - c.Awake(); + c.[Friend]Awake(); c.[Friend]IsAwake = true; } } diff --git a/src/Core/Scene.bf b/src/Core/Scene.bf index 256e22a..43ee09a 100644 --- a/src/Core/Scene.bf +++ b/src/Core/Scene.bf @@ -38,9 +38,16 @@ namespace Strawberry public virtual void Update() { - ForEach(scope (u) => u.EarlyUpdate()); - ForEach(scope (u) => u.Update()); - ForEach(scope (u) => u.LateUpdate()); + { + delegate void(IEarlyUpdate) early = scope (u) => u.EarlyUpdate(); + delegate void(IUpdate) update = scope (u) => u.Update(); + delegate void(ILateUpdate) late = scope (u) => u.LateUpdate(); + + ForEach(early); + ForEach(update); + ForEach(late); + } + UpdateLists(); } diff --git a/src/Static/Engine.bf b/src/Static/Engine.bf index 7951fd3..d421130 100644 --- a/src/Static/Engine.bf +++ b/src/Static/Engine.bf @@ -11,7 +11,6 @@ namespace Strawberry while (currentModule != null) { let newModule = currentModule.[Friend]Run(); - delete currentModule; currentModule = newModule; } diff --git a/src/Struct/Point.bf b/src/Struct/Point.bf index 0b4e513..e6fb08b 100644 --- a/src/Struct/Point.bf +++ b/src/Struct/Point.bf @@ -4,14 +4,14 @@ namespace Strawberry { public struct Point : IHashable { - static public readonly Point Right = .(1, 0); - static public readonly Point Left = .(-1, 0); - static public readonly Point Up = .(0, -1); - static public readonly Point Down = .(0, 1); - static public readonly Point UnitX = .(1, 0); - static public readonly Point UnitY = .(0, 1); - static public readonly Point Zero = .(0, 0); - static public readonly Point One = .(1, 1); + public const Point Right = .(1, 0); + public const Point Left = .(-1, 0); + public const Point Up = .(0, -1); + public const Point Down = .(0, 1); + public const Point UnitX = .(1, 0); + public const Point UnitY = .(0, 1); + public const Point Zero = .(0, 0); + public const Point One = .(1, 1); public int X; public int Y; @@ -73,7 +73,7 @@ namespace Strawberry return .((int32)a.X, (int32)a.Y); } - [Inline] + [Inline, Commutable] static public bool operator==(Point a, Point b) { return a.X == b.X && a.Y == b.Y;