PlatformLayer in, except rendering

This commit is contained in:
Matt Thorson 2020-08-03 19:09:45 -07:00
parent d736ded07e
commit 823656d8e5
14 changed files with 554 additions and 163 deletions

View File

@ -6,7 +6,8 @@ namespace Strawberry.Sample
{ {
static public int Main(String[] args) static public int Main(String[] args)
{ {
let game = scope SampleGame(); SDL2PlatformLayer sdl = scope SDL2PlatformLayer();
let game = scope SampleGame(sdl);
game.Run(); game.Run();
return 0; return 0;
} }

View File

@ -4,8 +4,8 @@ namespace Strawberry.Sample
{ {
public class SampleGame : Game public class SampleGame : Game
{ {
public this() public this(PlatformLayer platformLayer)
: base("Strawberry Sample Game!", 320, 180, 3) : base(platformLayer, "Strawberry Sample Game!", 320, 180, 3)
{ {
Controls.Init(); Controls.Init();
Scene = new Level(); Scene = new Level();

View File

@ -20,6 +20,7 @@ namespace Strawberry
public readonly int Width; public readonly int Width;
public readonly int Height; public readonly int Height;
public readonly int WindowScale; public readonly int WindowScale;
public readonly int GamepadLimit;
private Scene scene; private Scene scene;
private Scene switchToScene; private Scene switchToScene;
@ -27,17 +28,13 @@ namespace Strawberry
private Dictionary<Type, List<Type>> entityAssignableLists; private Dictionary<Type, List<Type>> entityAssignableLists;
private Dictionary<Type, List<Type>> componentAssignableLists; private Dictionary<Type, List<Type>> componentAssignableLists;
//SDL Vars public PlatformLayer PlatformLayer { get; private set; }
public SDL.Renderer* Renderer { get; private set; }
public Color ClearColor = .Black; public Color ClearColor = .Black;
private SDL.Rect screenRect;
private SDL.Window* window;
private SDL.Surface* screen;
private bool* keyboardState; private bool* keyboardState;
private int32 updateCounter; private int32 updateCounter;
public this(String windowTitle, int32 width, int32 height, int32 windowScale, int gamepadLimit = 1) public this(PlatformLayer platformLayer, String windowTitle, int32 width, int32 height, int32 windowScale, int gamepadLimit = 1)
: base() : base()
{ {
Game = this; Game = this;
@ -46,7 +43,7 @@ namespace Strawberry
Width = width; Width = width;
Height = height; Height = height;
WindowScale = windowScale; WindowScale = windowScale;
screenRect = SDL.Rect(0, 0, width * windowScale, height * windowScale); GamepadLimit = gamepadLimit;
String exePath = scope .(); String exePath = scope .();
Environment.GetExecutableFilePath(exePath); Environment.GetExecutableFilePath(exePath);
@ -54,27 +51,10 @@ namespace Strawberry
Path.GetDirectoryPath(exePath, exeDir); Path.GetDirectoryPath(exePath, exeDir);
Directory.SetCurrentDirectory(exeDir); Directory.SetCurrentDirectory(exeDir);
SDL.InitFlag init = .Video | .Events | .Audio; platformLayer.Init();
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();
VirtualInputs = new List<VirtualInput>(); VirtualInputs = new List<VirtualInput>();
Input.[Friend]Init(gamepadLimit); Input.[Friend]Init();
BuildTypeLists(); BuildTypeLists();
Assets.LoadAll(); Assets.LoadAll();
@ -107,51 +87,35 @@ namespace Strawberry
public void Run() public void Run()
{ {
Stopwatch sw = scope .(); float msCounter = 0;
sw.Start();
int curPhysTickCount = 0;
/*
Game loop adapted from Brian Fiete's SDLApp.bf in the Beef SDL2 Library
*/
while (true) while (true)
{ {
SDL.Event event; if (PlatformLayer.Closed())
if (SDL.PollEvent(out event) != 0 && event.type == .Quit)
{
return; return;
}
// Fixed 60 Hz update msCounter += PlatformLayer.Tick();
double msPerTick = 1000 / 60.0;
int newPhysTickCount = (int)(sw.ElapsedMilliseconds / msPerTick);
int addTicks = newPhysTickCount - curPhysTickCount; if (Time.FixedTimestep)
if (curPhysTickCount == 0)
{ {
// Initial render Time.RawDelta = Time.TargetDeltaTime;
Render(); while (msCounter >= Time.TargetMilliseconds)
{
PlatformLayer.UpdateInput();
Update();
Input.AfterUpdate();
msCounter -= Time.TargetMilliseconds;
}
} }
else else
{ {
addTicks = Math.Min(addTicks, 20); // Limit catchup Time.RawDelta = msCounter / 1000;
if (addTicks > 0) PlatformLayer.UpdateInput();
{
for (int i < addTicks)
{
Input.BeforeUpdate();
updateCounter++;
Update(); Update();
Input.AfterUpdate(); Input.AfterUpdate();
} }
Render();
}
else
Thread.Sleep(1);
}
curPhysTickCount = newPhysTickCount; Render();
} }
} }
@ -177,6 +141,9 @@ namespace Strawberry
if (scene != null) if (scene != null)
scene.Update(); scene.Update();
Time.RawPreviousElapsed = Time.RawElapsed;
Time.RawElapsed += Time.RawDelta;
Time.PreviousElapsed = Time.Elapsed; Time.PreviousElapsed = Time.Elapsed;
Time.Elapsed += Time.Delta; Time.Elapsed += Time.Delta;
} }
@ -186,11 +153,9 @@ namespace Strawberry
private void Render() private void Render()
{ {
SDL.SetRenderDrawColor(Renderer, ClearColor.R, ClearColor.G, ClearColor.B, ClearColor.A); PlatformLayer.RenderBegin();
SDL.RenderClear(Renderer);
SDL.RenderSetScale(Renderer, WindowScale, WindowScale);
Draw(); Draw();
SDL.RenderPresent(Renderer); PlatformLayer.RenderEnd();
} }
public virtual void Draw() public virtual void Draw()

15
src/Input/Axes.bf Normal file
View File

@ -0,0 +1,15 @@
namespace Strawberry
{
enum Axes : int32
{
case Invalid = -1;
case LeftX;
case LeftY;
case RightX;
case RightY;
case TriggerLeft;
case TriggerRight;
public int Count => (int)TriggerRight + 1;
}
}

24
src/Input/Buttons.bf Normal file
View File

@ -0,0 +1,24 @@
namespace Strawberry
{
enum Buttons : int32
{
case Invalid = -1;
case A;
case B;
case X;
case Y;
case Back;
case Guide;
case Start;
case LeftStick;
case RightStick;
case LeftShoulder;
case RightShoulder;
case DpadUp;
case DpadDown;
case DpadLeft;
case DpadRight;
public int Count => (int)DpadRight + 1;
}
}

View File

@ -1,4 +1,3 @@
using SDL2;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
@ -6,81 +5,59 @@ namespace Strawberry
{ {
static public class Input static public class Input
{ {
static private bool* keyboard;
static private bool[] previousKeyboard; static private bool[] previousKeyboard;
static private float[] lastKeypressTimes; static private float[] lastKeypressTimes;
static private SDL.SDL_GameController*[] gamepads;
static private void Init(int gamepadLimit) static private void Init()
{ {
keyboard = SDL.GetKeyboardState(null); previousKeyboard = new bool[Keys.Count];
previousKeyboard = new bool[(int)SDL.Scancode.NUMSCANCODES]; lastKeypressTimes = new float[Keys.Count];
lastKeypressTimes = new float[(int)SDL.Scancode.NUMSCANCODES];
gamepads = new SDL.SDL_GameController*[gamepadLimit];
for (let i < gamepads.Count)
gamepads[i] = SDL.GameControllerOpen((int32)i);
} }
static private void Dispose() static private void Dispose()
{ {
delete previousKeyboard; delete previousKeyboard;
delete lastKeypressTimes; delete lastKeypressTimes;
delete gamepads;
}
static public void BeforeUpdate()
{
SDL.PumpEvents();
SDL.GameControllerUpdate();
} }
static public void AfterUpdate() static public void AfterUpdate()
{ {
for (let i < previousKeyboard.Count) for (let i < previousKeyboard.Count)
{ {
if (!previousKeyboard[i] && keyboard[i]) if (!previousKeyboard[i] && Game.PlatformLayer.PollKey((Keys)i))
lastKeypressTimes[i] = Time.Elapsed; lastKeypressTimes[i] = Time.Elapsed;
previousKeyboard[i] = keyboard[i]; previousKeyboard[i] = Game.PlatformLayer.PollKey((Keys)i);
} }
} }
static public bool Ctrl => SDL.GetModState() & .CTRL > 0; static public bool Ctrl => KeyCheck(Keys.LCtrl) || KeyCheck(Keys.RCtrl);
static public bool Alt => SDL.GetModState() & .ALT > 0; static public bool Alt => KeyCheck(Keys.LCtrl) || KeyCheck(Keys.RCtrl);
static public bool Shift => SDL.GetModState() & .SHIFT > 0; static public bool Shift => KeyCheck(Keys.LCtrl) || KeyCheck(Keys.RCtrl);
static public bool CapsLock => SDL.GetModState() & .Caps > 0; static public bool CapsLock => Game.PlatformLayer.CapsLock;
static public bool NumLock => SDL.GetModState() & .Num > 0; static public bool NumLock => Game.PlatformLayer.NumLock;
static public bool KeyCheck(SDL.Scancode key) static public bool KeyCheck(Keys key)
{ {
Debug.Assert(keyboard != null, "Polling keyboard before Input.Init"); return Game.PlatformLayer.PollKey(key);
return keyboard[(int)key];
} }
static public bool KeyPressed(SDL.Scancode key) static public bool KeyPressed(Keys key)
{ {
Debug.Assert(keyboard != null, "Polling keyboard before Input.Init"); return Game.PlatformLayer.PollKey(key) && !previousKeyboard[(int)key];
return keyboard[(int)key] && !previousKeyboard[(int)key];
} }
static public bool KeyPressed(SDL.Scancode key, float repeatDelay, float repeatInterval) static public bool KeyPressed(Keys key, float repeatDelay, float repeatInterval)
{ {
Debug.Assert(keyboard != null, "Polling keyboard before Input.Init"); if (Game.PlatformLayer.PollKey(key))
return !previousKeyboard[(int)key] || Time.OnInterval(repeatInterval, lastKeypressTimes[(int)key] + repeatDelay);
let i = (int)key;
if (keyboard[i])
return !previousKeyboard[i] || Time.OnInterval(repeatInterval, lastKeypressTimes[i] + repeatDelay);
else else
return false; return false;
} }
static public bool KeyReleased(SDL.Scancode key) static public bool KeyReleased(Keys key)
{ {
Debug.Assert(keyboard != null, "Polling keyboard before Input.Init"); return !Game.PlatformLayer.PollKey(key) && previousKeyboard[(int)key];
return !keyboard[(int)key] && previousKeyboard[(int)key];
} }
static public void KeystrokesIntoString(String toString, float keyRepeatDelay, float keyRepeatInterval) static public void KeystrokesIntoString(String toString, float keyRepeatDelay, float keyRepeatInterval)
@ -93,11 +70,11 @@ namespace Strawberry
KeystrokesIntoStringUtil(toString, scope => Input.KeyPressed); KeystrokesIntoStringUtil(toString, scope => Input.KeyPressed);
} }
static private void KeystrokesIntoStringUtil(String toString, delegate bool(SDL.Scancode) pressed) static private void KeystrokesIntoStringUtil(String toString, delegate bool(Keys) pressed)
{ {
for (let i < (int)SDL2.SDL.Scancode.NUMSCANCODES) for (let i < (int)Keys.Count)
{ {
let key = (SDL2.SDL.Scancode)i; let key = (Keys)i;
if (pressed(key)) if (pressed(key))
{ {
if (key >= .A && key <= .Z) if (key >= .A && key <= .Z)
@ -192,28 +169,14 @@ namespace Strawberry
} }
} }
static public bool GamepadButtonCheck(int gamepadID, SDL.SDL_GameControllerButton button) static public bool GamepadButtonCheck(int gamepadID, Buttons button)
{ {
Debug.Assert(gamepads != null, "Polling gamepad before Input.Init"); return Game.PlatformLayer.PollGamepadButton(gamepadID, button);
Debug.Assert(gamepadID < gamepads.Count, "Gamepad index out of range (increase Game.gamepadLimit!");
Debug.Assert(gamepadID >= 0, "Negative gamepad index!");
return SDL.GameControllerGetButton(gamepads[gamepadID], button) == 1;
} }
static public float GamepadAxisCheck(int gamepadID, SDL.SDL_GameControllerAxis axis) static public float GamepadAxisCheck(int gamepadID, Axes axis)
{ {
Debug.Assert(gamepads != null, "Polling gamepad before Input.Init"); return Game.PlatformLayer.PollGamepadAxis(gamepadID, axis);
Debug.Assert(gamepadID < gamepads.Count, "Gamepad index out of range (increase Game.gamepadLimit!");
Debug.Assert(gamepadID >= 0, "Negative gamepad index!");
let val = SDL.GameControllerGetAxis(gamepads[gamepadID], axis);
if (val == 0)
return 0;
else if (val > 0)
return val / 32767f;
else
return val / 32768f;
} }
} }
} }

268
src/Input/Keys.bf Normal file
View File

@ -0,0 +1,268 @@
namespace Strawberry
{
public enum Keys : uint32
{
case Unknown = 0;
case A = 4;
case B = 5;
case C = 6;
case D = 7;
case E = 8;
case F = 9;
case G = 10;
case H = 11;
case I = 12;
case J = 13;
case K = 14;
case L = 15;
case M = 16;
case N = 17;
case O = 18;
case P = 19;
case Q = 20;
case R = 21;
case S = 22;
case T = 23;
case U = 24;
case V = 25;
case W = 26;
case X = 27;
case Y = 28;
case Z = 29;
case Key1 = 30;
case Key2 = 31;
case Key3 = 32;
case Key4 = 33;
case Key5 = 34;
case Key6 = 35;
case Key7 = 36;
case Key8 = 37;
case Key9 = 38;
case Key0 = 39;
case Return = 40;
case Escape = 41;
case BackSpace = 42;
case Tab = 43;
case Space = 44;
case Minus = 45;
case Equals = 46;
case LeftBracket = 47;
case RightBracket = 48;
case BackSlash = 49;
case NonUSHash = 50;
case Semicolon = 51;
case Apostrophe = 52;
case Grave = 53;
case Comma = 54;
case Period = 55;
case Slash = 56;
case CapsLock = 57;
case F1 = 58;
case F2 = 59;
case F3 = 60;
case F4 = 61;
case F5 = 62;
case F6 = 63;
case F7 = 64;
case F8 = 65;
case F9 = 66;
case F10 = 67;
case F11 = 68;
case F12 = 69;
case PrintScreen = 70;
case ScrollLock = 71;
case Pause = 72;
case Insert = 73;
case Home = 74;
case PageUp = 75;
case Delete = 76;
case End = 77;
case PageDown = 78;
case Right = 79;
case Left = 80;
case Down = 81;
case Up = 82;
case NumLockClear = 83;
case KpDivide = 84;
case KpMultiply = 85;
case KpMinus = 86;
case KpPlus = 87;
case KpEnter = 88;
case Kp1 = 89;
case Kp2 = 90;
case Kp3 = 91;
case Kp4 = 92;
case Kp5 = 93;
case Kp6 = 94;
case Kp7 = 95;
case Kp8 = 96;
case Kp9 = 97;
case Kp0 = 98;
case KpPeriod = 99;
case NonUSBackslash = 100;
case Application = 101;
case Power = 102;
case KpEquals = 103;
case F13 = 104;
case F14 = 105;
case F15 = 106;
case F16 = 107;
case F17 = 108;
case F18 = 109;
case F19 = 110;
case F20 = 111;
case F21 = 112;
case F22 = 113;
case F23 = 114;
case F24 = 115;
case Execute = 116;
case Help = 117;
case Menu = 118;
case Select = 119;
case Stop = 120;
case Again = 121;
case Undo = 122;
case Cut = 123;
case Copy = 124;
case Paste = 125;
case Find = 126;
case Mute = 127;
case VolumeUp = 128;
case VolumeDown = 129;
case LockingCapsLock = 130;
case LockingNumLock = 131;
case LockingScrollLock = 132;
case KpComma = 133;
case KpEqualsAS400 = 134;
case International1 = 135;
case International2 = 136;
case International3 = 137;
case International4 = 138;
case International5 = 139;
case International6 = 140;
case International7 = 141;
case International8 = 142;
case International9 = 143;
case Lang1 = 144;
case Lang2 = 145;
case Lang3 = 146;
case Lang4 = 147;
case Lang5 = 148;
case Lang6 = 149;
case Lang7 = 150;
case Lang8 = 151;
case Lang9 = 152;
case AltErase = 153;
case SysReq = 154;
case Cancel = 155;
case Clear = 156;
case Prior = 157;
case Return2 = 158;
case Separator = 159;
case Out = 160;
case Oper = 161;
case ClearAgain = 162;
case CrSel = 163;
case ExSel = 164;
case Kp00 = 176;
case Kp000 = 177;
case ThousandsSeparator = 178;
case DecimalSeparator = 179;
case CurrencyUnit = 180;
case CurrencySubUnit = 181;
case KpLeftParen = 182;
case KpRightParen = 183;
case KpLeftBrace = 184;
case KpRightBrace = 185;
case KpTab = 186;
case KpBackspace = 187;
case KPA = 188;
case KPB = 189;
case KPC = 190;
case KPD = 191;
case KPE = 192;
case KPF = 193;
case KpXor = 194;
case KpPower = 195;
case KpPercent = 196;
case KpLess = 197;
case KpGreater = 198;
case KpAmpersand = 199;
case KpdBlAmpersand = 200;
case KpVerticalBar = 201;
case KpDblVerticalBar = 202;
case KpColon = 203;
case KpHash = 204;
case KpSpace = 205;
case KpAt = 206;
case KpExclam = 207;
case KpMemstore = 208;
case KpMemRecall = 209;
case KpMemClear = 210;
case KpMemAdd = 211;
case KpMemSubtract = 212;
case KpMemMultiply = 213;
case KpMemDivide = 214;
case KpPlusMinus = 215;
case KpClear = 216;
case KpClearEntry = 217;
case KpBinary = 218;
case KpOctal = 219;
case KpDecimal = 220;
case KpHexadecimal = 221;
case LCtrl = 224;
case LShift = 225;
case LAlt = 226;
case LGui = 227;
case RCtrl = 228;
case RShift = 229;
case RAlt = 230;
case RGui = 231;
case Mode = 257;
// these come from the usb consumer page (0x0c)
case AudioNext = 258;
case AudioPrev = 259;
case AudioStop = 260;
case AudioPlay = 261;
case AudioMute = 262;
case MediaSelect = 263;
case Www = 264;
case Mail = 265;
case Calculator = 266;
case Computer = 267;
case AcSearch = 268;
case AcHome = 269;
case AcBack = 270;
case AcForward = 271;
case AcStop = 272;
case AcRefresh = 273;
case AcBookmarks = 274;
// these come from other sources; and are mostly mac related
case BrightnessDown = 275;
case BrightnessUp = 276;
case DisplaySwitch = 277;
case KbdIllumToggle = 278;
case KbdIllumDown = 279;
case KbdIllumUp = 280;
case Eject = 281;
case Sleep = 282;
case App1 = 283;
case App2 = 284;
static public int Count => 512;
}
}

View File

@ -1,6 +1,5 @@
using System.Collections; using System.Collections;
using System; using System;
using SDL2;
namespace Strawberry namespace Strawberry
{ {
@ -89,19 +88,19 @@ namespace Strawberry
// Setup Calls // Setup Calls
public VirtualAxis AddKeys(SDL.Scancode negativeKey, SDL.Scancode positiveKey, OverlapBehaviors overlapBehavior = .TakeNewer) public VirtualAxis AddKeys(Keys negativeKey, Keys positiveKey, OverlapBehaviors overlapBehavior = .TakeNewer)
{ {
nodes.Add(new KeyboardKeys(negativeKey, positiveKey, overlapBehavior)); nodes.Add(new KeyboardKeys(negativeKey, positiveKey, overlapBehavior));
return this; return this;
} }
public VirtualAxis AddButtons(int gamepadID, SDL.SDL_GameControllerButton negativeButton, SDL.SDL_GameControllerButton positiveButton, OverlapBehaviors overlapBehavior = .TakeNewer) public VirtualAxis AddButtons(int gamepadID, Buttons negativeButton, Buttons positiveButton, OverlapBehaviors overlapBehavior = .TakeNewer)
{ {
nodes.Add(new GamepadButtons(gamepadID, negativeButton, positiveButton, overlapBehavior)); nodes.Add(new GamepadButtons(gamepadID, negativeButton, positiveButton, overlapBehavior));
return this; return this;
} }
public VirtualAxis AddAxis(int gamepadID, SDL.SDL_GameControllerAxis axis, float deadzone) public VirtualAxis AddAxis(int gamepadID, Axes axis, float deadzone)
{ {
nodes.Add(new GamepadAxis(gamepadID, axis, deadzone)); nodes.Add(new GamepadAxis(gamepadID, axis, deadzone));
return this; return this;
@ -137,24 +136,24 @@ namespace Strawberry
private class KeyboardKeys : Node private class KeyboardKeys : Node
{ {
public OverlapBehaviors OverlapBehavior; public OverlapBehaviors OverlapBehavior;
public SDL.Scancode NegativeKeycode; public Keys NegativeKey;
public SDL.Scancode PositiveKeycode; public Keys PositiveKey;
private float value; private float value;
private bool turned; private bool turned;
public this(SDL.Scancode negativeKey, SDL.Scancode positiveKey, OverlapBehaviors overlapBehavior = .TakeNewer) public this(Keys negativeKey, Keys positiveKey, OverlapBehaviors overlapBehavior = .TakeNewer)
{ {
NegativeKeycode = negativeKey; NegativeKey = negativeKey;
PositiveKeycode = positiveKey; PositiveKey = positiveKey;
OverlapBehavior = overlapBehavior; OverlapBehavior = overlapBehavior;
} }
public override void Update() public override void Update()
{ {
if (Input.KeyCheck(PositiveKeycode)) if (Input.KeyCheck(PositiveKey))
{ {
if (Input.KeyCheck(NegativeKeycode)) if (Input.KeyCheck(NegativeKey))
{ {
switch (OverlapBehavior) switch (OverlapBehavior)
{ {
@ -181,7 +180,7 @@ namespace Strawberry
value = 1; value = 1;
} }
} }
else if (Input.KeyCheck(NegativeKeycode)) else if (Input.KeyCheck(NegativeKey))
{ {
turned = false; turned = false;
value = -1; value = -1;
@ -206,13 +205,13 @@ namespace Strawberry
{ {
public int GamepadID; public int GamepadID;
public OverlapBehaviors OverlapBehavior; public OverlapBehaviors OverlapBehavior;
public SDL.SDL_GameControllerButton NegativeButton; public Buttons NegativeButton;
public SDL.SDL_GameControllerButton PositiveButton; public Buttons PositiveButton;
private float value; private float value;
private bool turned; private bool turned;
public this(int gamepadID, SDL.SDL_GameControllerButton negativeButton, SDL.SDL_GameControllerButton positiveButton, OverlapBehaviors overlapBehavior = .TakeNewer) public this(int gamepadID, Buttons negativeButton, Buttons positiveButton, OverlapBehaviors overlapBehavior = .TakeNewer)
{ {
GamepadID = gamepadID; GamepadID = gamepadID;
NegativeButton = negativeButton; NegativeButton = negativeButton;
@ -275,10 +274,10 @@ namespace Strawberry
private class GamepadAxis : Node private class GamepadAxis : Node
{ {
public int GamepadID; public int GamepadID;
public SDL.SDL_GameControllerAxis Axis; public Axes Axis;
public float Deadzone; public float Deadzone;
public this(int gamepadID, SDL.SDL_GameControllerAxis axis, float deadzone) public this(int gamepadID, Axes axis, float deadzone)
{ {
GamepadID = gamepadID; GamepadID = gamepadID;
Axis = axis; Axis = axis;

View File

@ -1,7 +1,6 @@
using System.Collections; using System.Collections;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using SDL2;
namespace Strawberry namespace Strawberry
{ {
@ -85,19 +84,19 @@ namespace Strawberry
// Setup Calls // Setup Calls
public VirtualButton AddKey(SDL.Scancode keycode) public VirtualButton AddKey(Keys key)
{ {
nodes.Add(new KeyboardKey(keycode)); nodes.Add(new KeyboardKey(key));
return this; return this;
} }
public VirtualButton AddButton(int gamepadID, SDL.SDL_GameControllerButton button) public VirtualButton AddButton(int gamepadID, Buttons button)
{ {
nodes.Add(new GamepadButton(gamepadID, button)); nodes.Add(new GamepadButton(gamepadID, button));
return this; return this;
} }
public VirtualButton AddAxis(int gamepadID, SDL.SDL_GameControllerAxis axis, float threshold, ThresholdConditions condition = .GreaterThan) public VirtualButton AddAxis(int gamepadID, Axes axis, float threshold, ThresholdConditions condition = .GreaterThan)
{ {
nodes.Add(new GamepadAxis(gamepadID, axis, threshold, condition)); nodes.Add(new GamepadAxis(gamepadID, axis, threshold, condition));
return this; return this;
@ -131,18 +130,18 @@ namespace Strawberry
private class KeyboardKey : Node private class KeyboardKey : Node
{ {
public SDL.Scancode Keycode; public Keys Key;
public this(SDL.Scancode keycode) public this(Keys key)
{ {
Keycode = keycode; Key = key;
} }
override public bool Check override public bool Check
{ {
get get
{ {
return Input.KeyCheck(Keycode); return Input.KeyCheck(Key);
} }
} }
} }
@ -150,9 +149,9 @@ namespace Strawberry
private class GamepadButton : Node private class GamepadButton : Node
{ {
public int GamepadID; public int GamepadID;
public SDL.SDL_GameControllerButton Button; public Buttons Button;
public this(int gamepadID, SDL.SDL_GameControllerButton button) public this(int gamepadID, Buttons button)
{ {
GamepadID = gamepadID; GamepadID = gamepadID;
Button = button; Button = button;
@ -170,11 +169,11 @@ namespace Strawberry
private class GamepadAxis : Node private class GamepadAxis : Node
{ {
public int GamepadID; public int GamepadID;
public SDL.SDL_GameControllerAxis Axis; public Axes Axis;
public float Threshold; public float Threshold;
public ThresholdConditions Condition; public ThresholdConditions Condition;
public this(int gamepadID, SDL.SDL_GameControllerAxis axis, float threshold, ThresholdConditions condition = .GreaterThan) public this(int gamepadID, Axes axis, float threshold, ThresholdConditions condition = .GreaterThan)
{ {
GamepadID = gamepadID; GamepadID = gamepadID;
Axis = axis; Axis = axis;

View File

@ -0,0 +1,23 @@
using System;
namespace Strawberry
{
public abstract class PlatformLayer
{
public abstract void Init();
public abstract void RenderBegin();
public abstract void RenderEnd();
public abstract void UpdateInput();
public abstract bool PollKey(Keys key);
public abstract bool CapsLock { get; }
public abstract bool NumLock { get; }
public abstract bool PollGamepadButton(int gamepadID, Buttons button);
public abstract float PollGamepadAxis(int gamepadID, Axes axis);
// Returns milliseconds since last tick
public abstract uint32 Tick();
// If the game window has been closed
public abstract bool Closed();
}
}

View File

@ -0,0 +1,119 @@
using SDL2;
using System;
using System.Diagnostics;
namespace Strawberry
{
public class SDL2PlatformLayer : PlatformLayer
{
private SDL.Rect screenRect;
private SDL.Window* window;
private SDL.Surface* screen;
private SDL.Renderer* renderer;
private SDL.SDL_GameController*[] gamepads;
private bool* keyboard;
public override void Init()
{
SDL.Version version;
SDL.GetVersion(out version);
Calc.Log("Init SDL Version {0}.{1}.{2}", version.major, version.minor, version.patch);
{
SDL.InitFlag init = .Video | .Events | .Audio | .Timer;
if (Game.GamepadLimit > 0)
init |= .GameController;
if (SDL.Init(init) != 0)
Runtime.FatalError("Failed to initialize SDL");
}
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(Game.Title, .Centered, .Centered, screenRect.w, screenRect.h, .Shown);
screenRect = SDL.Rect(0, 0, (int32)(Game.Width * Game.WindowScale), (int32)(Game.Height * Game.WindowScale));
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();
keyboard = SDL.GetKeyboardState(null);
gamepads = new SDL.SDL_GameController*[Game.GamepadLimit];
for (let i < gamepads.Count)
gamepads[i] = SDL.GameControllerOpen((int32)i);
}
public ~this()
{
delete gamepads;
}
public override void RenderBegin()
{
SDL.SetRenderDrawColor(renderer, Game.ClearColor.R, Game.ClearColor.G, Game.ClearColor.B, Game.ClearColor.A);
SDL.RenderClear(renderer);
SDL.RenderSetScale(renderer, Game.WindowScale, Game.WindowScale);
}
public override void RenderEnd()
{
SDL.RenderPresent(renderer);
}
public override bool Closed()
{
SDL.Event event;
return (SDL.PollEvent(out event) != 0 && event.type == .Quit);
}
public override void UpdateInput()
{
SDL.PumpEvents();
SDL.GameControllerUpdate();
}
public override bool PollKey(Keys key)
{
Debug.Assert(keyboard != null, "Polling keyboard before initialized");
return keyboard[(uint32)key];
}
public override bool CapsLock => SDL.GetModState() & .Caps > 0;
public override bool NumLock => SDL.GetModState() & .Num > 0;
public override bool PollGamepadButton(int gamepadID, Buttons button)
{
Debug.Assert(gamepads != null, "Polling gamepad before initialized");
Debug.Assert(gamepadID >= 0 && gamepadID < gamepads.Count, "Gamepad index out of range (increase Game.gamepadLimit?)");
return SDL.GameControllerGetButton(gamepads[gamepadID], (SDL.SDL_GameControllerButton)button) == 1;
}
public override float PollGamepadAxis(int gamepadID, Axes axis)
{
Debug.Assert(gamepads != null, "Polling gamepad before initialized");
Debug.Assert(gamepadID >= 0 && gamepadID < gamepads.Count, "Gamepad index out of range (increase Game.gamepadLimit?)");
let val = SDL.GameControllerGetAxis(gamepads[gamepadID], (SDL.SDL_GameControllerAxis)axis);
if (val == 0)
return 0;
else if (val > 0)
return val / 32767f;
else
return val / 32768f;
}
public override uint32 Tick()
{
return SDL.GetTicks();
}
}
}

View File

@ -26,6 +26,13 @@ namespace Strawberry
return value > target ? Math.Max(value - maxDelta, target) : Math.Min(value + maxDelta, target); return value > target ? Math.Max(value - maxDelta, target) : Math.Min(value + maxDelta, target);
} }
//Move toward a target value without crossing it
[Inline]
static public void Approach(float* value, float target, float maxDelta)
{
*value = *value > target ? Math.Max(*value - maxDelta, target) : Math.Min(*value + maxDelta, target);
}
//Convert from a value between arbitrary min and max to between 0 and 1 //Convert from a value between arbitrary min and max to between 0 and 1
[Inline] [Inline]
static public float Map(float value, float oldMin, float oldMax) static public float Map(float value, float oldMin, float oldMax)

View File

@ -32,6 +32,9 @@ namespace Strawberry
static public void Init() static public void Init()
{ {
bool* b = &enabled;
*b = false;
enabled = true; enabled = true;
font = SDL2.SDLTTF.OpenFont("../../../../Strawberry/src/Content/strawberry-seeds.ttf", 8); font = SDL2.SDLTTF.OpenFont("../../../../Strawberry/src/Content/strawberry-seeds.ttf", 8);
entry = new String(); entry = new String();

View File

@ -2,13 +2,18 @@ namespace Strawberry
{ {
static public class Time static public class Time
{ {
static public bool FixedTimestep = true;
static public float TargetDeltaTime = 1 / 60f;
static public float Elapsed; static public float Elapsed;
static public float PreviousElapsed; static public float PreviousElapsed;
static public float RawElapsed;
static public float RawPreviousElapsed;
static public float Rate = 1f; static public float Rate = 1f;
static public float Freeze; static public float Freeze;
static public float RawDelta;
static public float RawDelta => (1 / 60f);
static public float Delta => RawDelta * Rate; static public float Delta => RawDelta * Rate;
static public float TargetMilliseconds => 1000 * TargetDeltaTime;
static public bool OnInterval(float interval, float startDelay = 0) static public bool OnInterval(float interval, float startDelay = 0)
{ {