Moved example physics stuff into the sample game

This commit is contained in:
Matt Thorson
2020-09-12 23:52:13 -07:00
parent ae21809566
commit ce099d0cc8
17 changed files with 127 additions and 57 deletions

View File

@ -2,6 +2,8 @@ namespace Strawberry.Sample
{
public class Level : Scene
{
public Grid SolidGrid { get; private set; }
public this()
{
Add(new Player(.(50, 50)));

View File

@ -103,7 +103,7 @@ namespace Strawberry.Sample
base.Draw();
DrawHitbox(.Green);
Game.Batcher.Tex(Assets.Textures["test"], X - 4, Y - 8);
//Game.Batcher.Tex(Assets.Textures["test"], X - 4, Y - 8);
}
}
}

View File

@ -0,0 +1,242 @@
using System;
namespace Strawberry.Sample
{
[Reflect]
public class Actor : Entity
{
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 Level Level => SceneAs<Level>();
public bool Check(Level level)
{
return level.SolidGrid != null && Check(level.SolidGrid);
}
public bool Check(Level level, Point offset)
{
return level.SolidGrid != null && Check(level.SolidGrid, offset);
}
public bool GroundCheck(int distance = 1)
{
return Check<Solid>(.(0, distance)) || Check(Level, .(0, distance)) || CheckOutside<JumpThru>(.(0, distance));
}
public virtual bool IsRiding(Solid solid)
{
return Check(solid, .(0, 1));
}
public virtual bool IsRiding(JumpThru jumpThru)
{
return CheckOutside(jumpThru, .(0, 1));
}
public virtual void Squish(Collision collision)
{
RemoveSelf();
}
public override void Update()
{
base.Update();
MovedByGeometry = Point.Zero;
}
public bool MoveX(float amount, delegate void(Collision) onCollide = null)
{
remainder.X += amount;
let move = (int)Math.Round(remainder.X);
if (move != 0)
{
remainder.X -= move;
return MoveExactX(move, onCollide);
}
else
return false;
}
public bool MoveY(float amount, delegate void(Collision) onCollide = null)
{
remainder.Y += amount;
let move = (int)Math.Round(remainder.Y);
if (move != 0)
{
remainder.Y -= move;
return MoveExactY(move, onCollide);
}
else
return false;
}
[Inline]
public void MoveToX(float x)
{
MoveX(x - (X + remainder.X), null);
}
[Inline]
public void MoveToY(float y)
{
MoveY(y - (Y + remainder.Y), null);
}
public bool MoveExactX(int amount, delegate void(Collision) onCollide = null, Geometry pusher = null, Geometry carrier = 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)
{
let c = Collision(
Cardinals.FromPoint(Point.Right * sign),
Math.Abs(amount),
Math.Abs(amount - move),
hit,
pusher
);
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;
move -= sign;
}
return false;
}
public bool MoveExactY(int amount, delegate void(Collision) onCollide = null, Geometry pusher = null, Geometry carrier = 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)
{
let c = Collision(
Cardinals.FromPoint(Point.Down * sign),
Math.Abs(amount),
Math.Abs(amount - move),
hit,
pusher
);
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;
move -= sign;
}
return false;
}
public void ZeroRemainderX()
{
remainder.X = 0;
}
public void ZeroRemainderY()
{
remainder.Y = 0;
}
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;
Point perp = dir.Perpendicular();
perp.X = Math.Abs(perp.X);
perp.Y = Math.Abs(perp.Y);
delegate bool(Point) checker;
if (dir == Point.Down)
checker = scope:: (p) => !Check(Level, p) && !Check<Solid>(p) && !CheckOutside<JumpThru>(p);
else
checker = scope:: (p) => !Check(Level, p) && !Check<Solid>(p);
for (int i = 1; i <= maxAmount; i++)
{
for (int j = -1; j <= 1; j += 2)
{
if (onlySign != 0 && onlySign != j)
continue;
let offset = dir * lookAhead + perp * i * j;
if (checker(offset))
{
Position += offset;
return true;
}
}
}
return false;
}
}
}

View File

@ -0,0 +1,21 @@
namespace Strawberry.Sample
{
public struct Collision
{
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)
{
Direction = direction;
Magnitude = magnitude;
Completed = completed;
Stopper = stopper;
Pusher = pusher;
}
}
}

View File

@ -0,0 +1,83 @@
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;
}
}
}

View File

@ -0,0 +1,80 @@
using System.Collections;
using System;
namespace Strawberry.Sample
{
public class JumpThru : Geometry
{
public this(Point position, int width)
: base(position)
{
Hitbox = Rect(0, 0, width, 2);
}
public this(JSON json)
: this(.(json), json["width"])
{
}
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);
}
}
}

View File

@ -0,0 +1,94 @@
using System.Collections;
namespace Strawberry.Sample
{
public class Solid : Geometry
{
public this(Point position, Rect hitbox)
: base(position)
{
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);
}
}
}