This commit is contained in:
Matt Thorson
2020-05-07 20:38:02 -07:00
parent 80e1935b15
commit 786b692a3f
11 changed files with 0 additions and 0 deletions

49
src/Core/Component.bf Normal file
View File

@ -0,0 +1,49 @@
using System;
namespace Strawberry
{
public abstract class Component
{
public Entity Entity { get; private set; }
public bool Active;
public bool Visible;
public this(bool active, bool visible)
{
Active = active;
Visible = visible;
}
public void Added(Entity entity)
{
Entity = entity;
}
public void Removed()
{
Entity = null;
}
public virtual void Started()
{
}
public virtual void Update()
{
}
public virtual void Draw()
{
}
[Inline]
public void RemoveSelf()
{
Entity?.Remove(this);
}
}
}

412
src/Core/Entity.bf Normal file
View File

@ -0,0 +1,412 @@
using System;
using System.Collections;
namespace Strawberry
{
public abstract class Entity
{
public Scene Scene { get; private set; }
public int Depth;
public bool Active = true;
public bool Visible = true;
public bool Collidable = true;
public bool DeleteOnRemove = true;
private List<Component> components = new List<Component>() ~ delete _;
private HashSet<Component> toAdd = new HashSet<Component>() ~ delete _;
private HashSet<Component> toRemove = new HashSet<Component>() ~ delete _;
public this(Point position)
{
Positionf = position;
}
public ~this()
{
for (var c in components)
delete c;
for (var c in toAdd)
delete c;
}
public void Added(Scene scene)
{
Scene = scene;
}
public void Removed()
{
Scene = null;
}
public virtual void Started()
{
for (var c in components)
c.Started();
}
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()
{
Scene?.Remove(this);
}
// ===== Components =====
public T Add<T>(T component) where T : Component
{
if (component.Entity == null)
toAdd.Add(component);
return component;
}
public T Remove<T>(T component) where T : Component
{
if (component.Entity == this)
toRemove.Add(component);
return component;
}
public void UpdateLists()
{
if (toRemove.Count > 0)
{
for (var c in toRemove)
{
components.Remove(c);
c.Removed();
delete c;
}
toRemove.Clear();
}
if (toAdd.Count > 0)
{
for (var c in toAdd)
{
components.Add(c);
c.Added(this);
}
toAdd.Clear();
}
}
// ===== Position =====
public Vector Positionf;
public float Xf
{
[Inline]
get
{
return Positionf.X;
}
[Inline]
set
{
Positionf.X = value;
}
}
public float Yf
{
[Inline]
get
{
return Positionf.Y;
}
[Inline]
set
{
Positionf.Y = value;
}
}
public Point Position
{
[Inline]
get
{
return Positionf.Round();
}
[Inline]
set
{
Positionf = value;
}
}
public int X
{
[Inline]
get
{
return (int)Math.Round(Positionf.X);
}
[Inline]
set
{
Positionf.X = value;
}
}
public int Y
{
[Inline]
get
{
return (int)Math.Round(Positionf.Y);
}
[Inline]
set
{
Positionf.Y = value;
}
}
// ===== 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 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(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 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)
{
Draw.Rect(SceneHitbox, color);
}
static public int operator<=>(Entity a, Entity b)
{
return a.Depth <=> b.Depth;
}
}
}

237
src/Core/Game.bf Normal file
View File

@ -0,0 +1,237 @@
using SDL2;
using System;
using System.Collections;
using System.Reflection;
using System.IO;
using System.Diagnostics;
using System.Threading;
namespace Strawberry
{
static
{
static public Game Game;
}
public class Game
{
public readonly List<VirtualInput> VirtualInputs;
public readonly String Title;
public readonly int Width;
public readonly int Height;
public readonly int WindowScale;
private Scene scene;
private Scene switchToScene;
private bool updating;
public SDL.Renderer* Renderer { get; private set; }
public Color ClearColor = .Black;
private SDL.Rect screenRect;
private SDL.Window* window;
private SDL.Surface* screen;
private bool* keyboardState;
private SDL.SDL_GameController*[] gamepads;
private int32 updateCounter;
public this(String windowTitle, int32 width, int32 height, int32 windowScale, int gamepadLimit = 1)
: base()
{
Game = this;
VirtualInputs = new List<VirtualInput>();
Title = windowTitle;
Width = width;
Height = height;
WindowScale = windowScale;
screenRect = SDL.Rect(0, 0, width * windowScale, height * windowScale);
String exePath = scope .();
Environment.GetExecutableFilePath(exePath);
String exeDir = scope .();
Path.GetDirectoryPath(exePath, exeDir);
Directory.SetCurrentDirectory(exeDir);
SDL.InitFlag init = .Video | .Events | .Audio;
if (gamepadLimit > 0)
init |= .GameController;
SDL.Init(init);
SDL.EventState(.JoyAxisMotion, .Disable);
SDL.EventState(.JoyBallMotion, .Disable);
SDL.EventState(.JoyHatMotion, .Disable);
SDL.EventState(.JoyButtonDown, .Disable);
SDL.EventState(.JoyButtonUp, .Disable);
SDL.EventState(.JoyDeviceAdded, .Disable);
SDL.EventState(.JoyDeviceRemoved, .Disable);
window = SDL.CreateWindow(Title, .Centered, .Centered, screenRect.w, screenRect.h, .Shown);
Renderer = SDL.CreateRenderer(window, -1, .Accelerated);
screen = SDL.GetWindowSurface(window);
SDLImage.Init(.PNG | .JPG);
SDLMixer.OpenAudio(44100, SDLMixer.MIX_DEFAULT_FORMAT, 2, 4096);
SDLTTF.Init();
this.gamepads = new SDL.SDL_GameController*[gamepadLimit];
for (let i < this.gamepads.Count)
this.gamepads[i] = SDL.GameControllerOpen((int32)i);
}
public ~this()
{
if (scene != null)
delete scene;
if (switchToScene != scene && switchToScene != null)
delete switchToScene;
{
let list = scope List<VirtualInput>();
for (var i in VirtualInputs)
list.Add(i);
for (var i in list)
delete i;
delete VirtualInputs;
}
delete gamepads;
Game = null;
}
public void Run()
{
Stopwatch sw = scope .();
sw.Start();
int curPhysTickCount = 0;
while (true)
{
SDL.Event event;
if (SDL.PollEvent(out event) != 0 && event.type == .Quit)
{
return;
}
// Fixed 60 Hz update
double msPerTick = 1000 / 60.0;
int newPhysTickCount = (int)(sw.ElapsedMilliseconds / msPerTick);
int addTicks = newPhysTickCount - curPhysTickCount;
if (curPhysTickCount == 0)
{
// Initial render
Render();
}
else
{
keyboardState = SDL.GetKeyboardState(null);
SDL.GameControllerUpdate();
addTicks = Math.Min(addTicks, 20); // Limit catchup
if (addTicks > 0)
{
for (int i < addTicks)
{
updateCounter++;
Update();
}
Render();
}
else
Thread.Sleep(1);
}
curPhysTickCount = newPhysTickCount;
}
}
public virtual void Update()
{
//Input
for (var i in VirtualInputs)
i.Update();
//Switch scenes
if (switchToScene != scene)
{
if (scene != null)
delete scene;
scene = switchToScene;
scene.Started();
}
if (Time.Freeze > 0)
Time.Freeze -= Time.RawDelta;
else
{
if (scene != null)
scene.Update();
Time.PreviousElapsed = Time.Elapsed;
Time.Elapsed += Time.Delta;
}
}
public void Render()
{
SDL.SetRenderDrawColor(Renderer, ClearColor.R, ClearColor.G, ClearColor.B, ClearColor.A);
SDL.RenderClear(Renderer);
SDL.RenderSetScale(Renderer, WindowScale, WindowScale);
Draw();
SDL.RenderPresent(Renderer);
}
public virtual void Draw()
{
if (Scene != null)
Scene.Draw();
}
public Scene Scene
{
get
{
return scene;
}
set
{
if (switchToScene != scene && switchToScene != null)
delete switchToScene;
switchToScene = value;
}
}
// Input
public bool KeyCheck(SDL.Scancode key)
{
if (keyboardState == null)
return false;
return keyboardState[(int)key];
}
public bool GamepadButtonCheck(int gamepadID, SDL.SDL_GameControllerButton button)
{
if (gamepads == null)
return false;
return SDL.GameControllerGetButton(gamepads[gamepadID], button) == 1;
}
public float GamepadAxisCheck(int gamepadID, SDL.SDL_GameControllerAxis axis)
{
if (gamepads == null)
return 0;
let val = SDL.GameControllerGetAxis(gamepads[gamepadID], axis);
if (val == 0)
return 0;
else if (val > 0)
return val / 32767f;
else
return val / 32768f;
}
}
}

171
src/Core/Scene.bf Normal file
View File

@ -0,0 +1,171 @@
using System.Collections;
using System;
namespace Strawberry
{
public class Scene
{
public float TimeStarted { get; private set; }
private List<Entity> entities;
private HashSet<Entity> toRemove;
private HashSet<Entity> toAdd;
public this()
{
entities = new List<Entity>();
toAdd = new HashSet<Entity>();
toRemove = new HashSet<Entity>();
}
public ~this()
{
for (var e in entities)
if (e.DeleteOnRemove)
delete e;
delete entities;
for (var e in toAdd)
if (e.DeleteOnRemove)
delete e;
delete toAdd;
delete toRemove;
}
public virtual void Started()
{
TimeStarted = Time.Elapsed;
}
public virtual void Update()
{
UpdateLists();
for (var e in entities)
if (e.Active)
e.Update();
}
public virtual void Draw()
{
for (var e in entities)
if (e.Visible)
e.Draw();
}
public T Add<T>(T e) where T : Entity
{
if (e.Scene == null)
toAdd.Add(e);
return e;
}
public T Remove<T>(T e) where T : Entity
{
if (e.Scene == this)
toRemove.Add(e);
return e;
}
private void UpdateLists()
{
if (toRemove.Count > 0)
{
for (var e in toRemove)
{
entities.Remove(e);
e.Removed();
if (e.DeleteOnRemove)
delete e;
}
toRemove.Clear();
}
if (toAdd.Count > 0)
{
for (var e in toAdd)
{
entities.Add(e);
e.Added(this);
}
}
for (var e in entities)
e.UpdateLists();
if (toAdd.Count > 0)
{
for (var e in toAdd)
e.Started();
toAdd.Clear();
}
}
// Time
public float TimeElapsed => Time.Elapsed - TimeStarted;
public float PreviousTimeElapsed => Time.PreviousElapsed - TimeStarted;
public bool TimeOnInterval(float interval, float offset = 0)
{
return (int)((TimeElapsed - offset) / interval) != (int)((PreviousTimeElapsed - offset) / interval);
}
public bool TimeBetweenInterval(float interval, float offset = 0)
{
return (TimeElapsed - offset) % (interval * 2) >= interval;
}
// Finding Entities
public T First<T>() where T : Entity
{
for (var e in entities)
if (e is T)
return e as T;
return null;
}
public T First<T>(Point point) where T : Entity
{
for (var e in entities)
if (e is T && e.Check(point))
return e as T;
return null;
}
public T First<T>(Rect rect) where T : Entity
{
for (var e in entities)
if (e is T && e.Check(rect))
return e as T;
return null;
}
public List<T> All<T>(List<T> into) where T : Entity
{
for (var e in entities)
if (e is T)
into.Add(e as T);
return into;
}
public List<T> All<T>(Point point, List<T> into) where T : Entity
{
for (var e in entities)
if (e is T && e.Check(point))
into.Add(e as T);
return into;
}
public List<T> All<T>(Rect rect, List<T> into) where T : Entity
{
for (var e in entities)
if (e is T && e.Check(rect))
into.Add(e as T);
return into;
}
}
}