diff --git a/SampleGame/src/Program.bf b/SampleGame/src/Program.bf index ee925e1..76d557b 100644 --- a/SampleGame/src/Program.bf +++ b/SampleGame/src/Program.bf @@ -6,7 +6,8 @@ namespace Strawberry.Sample { static public int Main(String[] args) { - let game = scope SampleGame(); + SDL2PlatformLayer sdl = scope SDL2PlatformLayer(); + let game = scope SampleGame(sdl); game.Run(); return 0; } diff --git a/SampleGame/src/SampleGame.bf b/SampleGame/src/SampleGame.bf index b5ede44..6e969e9 100644 --- a/SampleGame/src/SampleGame.bf +++ b/SampleGame/src/SampleGame.bf @@ -4,8 +4,8 @@ namespace Strawberry.Sample { public class SampleGame : Game { - public this() - : base("Strawberry Sample Game!", 320, 180, 3) + public this(PlatformLayer platformLayer) + : base(platformLayer, "Strawberry Sample Game!", 320, 180, 3) { Controls.Init(); Scene = new Level(); diff --git a/src/Core/Game.bf b/src/Core/Game.bf index 673a4bc..266dad6 100644 --- a/src/Core/Game.bf +++ b/src/Core/Game.bf @@ -20,6 +20,7 @@ namespace Strawberry public readonly int Width; public readonly int Height; public readonly int WindowScale; + public readonly int GamepadLimit; private Scene scene; private Scene switchToScene; @@ -27,17 +28,13 @@ namespace Strawberry private Dictionary> entityAssignableLists; private Dictionary> componentAssignableLists; - //SDL Vars - public SDL.Renderer* Renderer { get; private set; } + public PlatformLayer PlatformLayer { get; private set; } public Color ClearColor = .Black; - private SDL.Rect screenRect; - private SDL.Window* window; - private SDL.Surface* screen; private bool* keyboardState; 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() { Game = this; @@ -46,7 +43,7 @@ namespace Strawberry Width = width; Height = height; WindowScale = windowScale; - screenRect = SDL.Rect(0, 0, width * windowScale, height * windowScale); + GamepadLimit = gamepadLimit; String exePath = scope .(); Environment.GetExecutableFilePath(exePath); @@ -54,27 +51,10 @@ namespace Strawberry 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(); + platformLayer.Init(); VirtualInputs = new List(); - Input.[Friend]Init(gamepadLimit); + Input.[Friend]Init(); BuildTypeLists(); Assets.LoadAll(); @@ -107,51 +87,35 @@ namespace Strawberry public void Run() { - Stopwatch sw = scope .(); - sw.Start(); - int curPhysTickCount = 0; - - /* - Game loop adapted from Brian Fiete's SDLApp.bf in the Beef SDL2 Library - */ - + float msCounter = 0; while (true) { - SDL.Event event; - if (SDL.PollEvent(out event) != 0 && event.type == .Quit) - { + if (PlatformLayer.Closed()) return; - } - // Fixed 60 Hz update - double msPerTick = 1000 / 60.0; - int newPhysTickCount = (int)(sw.ElapsedMilliseconds / msPerTick); + msCounter += PlatformLayer.Tick(); - int addTicks = newPhysTickCount - curPhysTickCount; - if (curPhysTickCount == 0) + if (Time.FixedTimestep) { - // Initial render - Render(); + Time.RawDelta = Time.TargetDeltaTime; + while (msCounter >= Time.TargetMilliseconds) + { + PlatformLayer.UpdateInput(); + Update(); + Input.AfterUpdate(); + + msCounter -= Time.TargetMilliseconds; + } } else { - addTicks = Math.Min(addTicks, 20); // Limit catchup - if (addTicks > 0) - { - for (int i < addTicks) - { - Input.BeforeUpdate(); - updateCounter++; - Update(); - Input.AfterUpdate(); - } - Render(); - } - else - Thread.Sleep(1); + Time.RawDelta = msCounter / 1000; + PlatformLayer.UpdateInput(); + Update(); + Input.AfterUpdate(); } - curPhysTickCount = newPhysTickCount; + Render(); } } @@ -177,6 +141,9 @@ namespace Strawberry if (scene != null) scene.Update(); + Time.RawPreviousElapsed = Time.RawElapsed; + Time.RawElapsed += Time.RawDelta; + Time.PreviousElapsed = Time.Elapsed; Time.Elapsed += Time.Delta; } @@ -186,11 +153,9 @@ namespace Strawberry private void Render() { - SDL.SetRenderDrawColor(Renderer, ClearColor.R, ClearColor.G, ClearColor.B, ClearColor.A); - SDL.RenderClear(Renderer); - SDL.RenderSetScale(Renderer, WindowScale, WindowScale); + PlatformLayer.RenderBegin(); Draw(); - SDL.RenderPresent(Renderer); + PlatformLayer.RenderEnd(); } public virtual void Draw() diff --git a/src/Input/Axes.bf b/src/Input/Axes.bf new file mode 100644 index 0000000..ba6bce2 --- /dev/null +++ b/src/Input/Axes.bf @@ -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; + } +} diff --git a/src/Input/Buttons.bf b/src/Input/Buttons.bf new file mode 100644 index 0000000..3735d6f --- /dev/null +++ b/src/Input/Buttons.bf @@ -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; + } +} diff --git a/src/Input/Input.bf b/src/Input/Input.bf index 7ca147e..fee1703 100644 --- a/src/Input/Input.bf +++ b/src/Input/Input.bf @@ -1,4 +1,3 @@ -using SDL2; using System; using System.Diagnostics; @@ -6,81 +5,59 @@ namespace Strawberry { static public class Input { - static private bool* keyboard; + static private bool[] previousKeyboard; 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[(int)SDL.Scancode.NUMSCANCODES]; - lastKeypressTimes = new float[(int)SDL.Scancode.NUMSCANCODES]; - - gamepads = new SDL.SDL_GameController*[gamepadLimit]; - for (let i < gamepads.Count) - gamepads[i] = SDL.GameControllerOpen((int32)i); + previousKeyboard = new bool[Keys.Count]; + lastKeypressTimes = new float[Keys.Count]; } static private void Dispose() { delete previousKeyboard; delete lastKeypressTimes; - delete gamepads; - } - - static public void BeforeUpdate() - { - SDL.PumpEvents(); - SDL.GameControllerUpdate(); } static public void AfterUpdate() { for (let i < previousKeyboard.Count) { - if (!previousKeyboard[i] && keyboard[i]) + if (!previousKeyboard[i] && Game.PlatformLayer.PollKey((Keys)i)) 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 Alt => SDL.GetModState() & .ALT > 0; - static public bool Shift => SDL.GetModState() & .SHIFT > 0; - static public bool CapsLock => SDL.GetModState() & .Caps > 0; - static public bool NumLock => SDL.GetModState() & .Num > 0; + static public bool Ctrl => KeyCheck(Keys.LCtrl) || KeyCheck(Keys.RCtrl); + static public bool Alt => KeyCheck(Keys.LCtrl) || KeyCheck(Keys.RCtrl); + static public bool Shift => KeyCheck(Keys.LCtrl) || KeyCheck(Keys.RCtrl); + static public bool CapsLock => Game.PlatformLayer.CapsLock; + 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 keyboard[(int)key]; + return Game.PlatformLayer.PollKey(key); } - static public bool KeyPressed(SDL.Scancode key) + static public bool KeyPressed(Keys key) { - Debug.Assert(keyboard != null, "Polling keyboard before Input.Init"); - - return keyboard[(int)key] && !previousKeyboard[(int)key]; + return Game.PlatformLayer.PollKey(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"); - - let i = (int)key; - if (keyboard[i]) - return !previousKeyboard[i] || Time.OnInterval(repeatInterval, lastKeypressTimes[i] + repeatDelay); + if (Game.PlatformLayer.PollKey(key)) + return !previousKeyboard[(int)key] || Time.OnInterval(repeatInterval, lastKeypressTimes[(int)key] + repeatDelay); else 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 !keyboard[(int)key] && previousKeyboard[(int)key]; + return !Game.PlatformLayer.PollKey(key) && previousKeyboard[(int)key]; } static public void KeystrokesIntoString(String toString, float keyRepeatDelay, float keyRepeatInterval) @@ -93,11 +70,11 @@ namespace Strawberry 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 (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"); - 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; + return Game.PlatformLayer.PollGamepadButton(gamepadID, button); } - 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"); - 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; + return Game.PlatformLayer.PollGamepadAxis(gamepadID, axis); } } } diff --git a/src/Input/Keys.bf b/src/Input/Keys.bf new file mode 100644 index 0000000..f86b3ca --- /dev/null +++ b/src/Input/Keys.bf @@ -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; + } +} diff --git a/src/Input/VirtualAxis.bf b/src/Input/VirtualAxis.bf index 6a22d09..69221f8 100644 --- a/src/Input/VirtualAxis.bf +++ b/src/Input/VirtualAxis.bf @@ -1,6 +1,5 @@ using System.Collections; using System; -using SDL2; namespace Strawberry { @@ -89,19 +88,19 @@ namespace Strawberry // 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)); 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)); 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)); return this; @@ -137,24 +136,24 @@ namespace Strawberry private class KeyboardKeys : Node { public OverlapBehaviors OverlapBehavior; - public SDL.Scancode NegativeKeycode; - public SDL.Scancode PositiveKeycode; + public Keys NegativeKey; + public Keys PositiveKey; private float value; 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; - PositiveKeycode = positiveKey; + NegativeKey = negativeKey; + PositiveKey = positiveKey; OverlapBehavior = overlapBehavior; } public override void Update() { - if (Input.KeyCheck(PositiveKeycode)) + if (Input.KeyCheck(PositiveKey)) { - if (Input.KeyCheck(NegativeKeycode)) + if (Input.KeyCheck(NegativeKey)) { switch (OverlapBehavior) { @@ -181,7 +180,7 @@ namespace Strawberry value = 1; } } - else if (Input.KeyCheck(NegativeKeycode)) + else if (Input.KeyCheck(NegativeKey)) { turned = false; value = -1; @@ -206,13 +205,13 @@ namespace Strawberry { public int GamepadID; public OverlapBehaviors OverlapBehavior; - public SDL.SDL_GameControllerButton NegativeButton; - public SDL.SDL_GameControllerButton PositiveButton; + public Buttons NegativeButton; + public Buttons PositiveButton; private float value; 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; NegativeButton = negativeButton; @@ -275,10 +274,10 @@ namespace Strawberry private class GamepadAxis : Node { public int GamepadID; - public SDL.SDL_GameControllerAxis Axis; + public Axes Axis; public float Deadzone; - public this(int gamepadID, SDL.SDL_GameControllerAxis axis, float deadzone) + public this(int gamepadID, Axes axis, float deadzone) { GamepadID = gamepadID; Axis = axis; diff --git a/src/Input/VirtualButton.bf b/src/Input/VirtualButton.bf index 4c43302..b312458 100644 --- a/src/Input/VirtualButton.bf +++ b/src/Input/VirtualButton.bf @@ -1,7 +1,6 @@ using System.Collections; using System; using System.Diagnostics; -using SDL2; namespace Strawberry { @@ -85,19 +84,19 @@ namespace Strawberry // 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; } - public VirtualButton AddButton(int gamepadID, SDL.SDL_GameControllerButton button) + public VirtualButton AddButton(int gamepadID, Buttons button) { nodes.Add(new GamepadButton(gamepadID, button)); 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)); return this; @@ -131,18 +130,18 @@ namespace Strawberry 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 { get { - return Input.KeyCheck(Keycode); + return Input.KeyCheck(Key); } } } @@ -150,9 +149,9 @@ namespace Strawberry private class GamepadButton : Node { 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; Button = button; @@ -170,11 +169,11 @@ namespace Strawberry private class GamepadAxis : Node { public int GamepadID; - public SDL.SDL_GameControllerAxis Axis; + public Axes Axis; public float Threshold; 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; Axis = axis; diff --git a/src/PlatformLayer/PlatformLayer.bf b/src/PlatformLayer/PlatformLayer.bf new file mode 100644 index 0000000..7828199 --- /dev/null +++ b/src/PlatformLayer/PlatformLayer.bf @@ -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(); + } +} diff --git a/src/PlatformLayer/SDL2PlatformLayer.bf b/src/PlatformLayer/SDL2PlatformLayer.bf new file mode 100644 index 0000000..4a896dc --- /dev/null +++ b/src/PlatformLayer/SDL2PlatformLayer.bf @@ -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(); + } + } +} diff --git a/src/Static/Calc.bf b/src/Static/Calc.bf index d01f4db..a9a1f6f 100644 --- a/src/Static/Calc.bf +++ b/src/Static/Calc.bf @@ -26,6 +26,13 @@ namespace Strawberry 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 [Inline] static public float Map(float value, float oldMin, float oldMax) diff --git a/src/Static/Console.bf b/src/Static/Console.bf index 947a621..fa675de 100644 --- a/src/Static/Console.bf +++ b/src/Static/Console.bf @@ -32,6 +32,9 @@ namespace Strawberry static public void Init() { + bool* b = &enabled; + *b = false; + enabled = true; font = SDL2.SDLTTF.OpenFont("../../../../Strawberry/src/Content/strawberry-seeds.ttf", 8); entry = new String(); diff --git a/src/Static/Time.bf b/src/Static/Time.bf index 5b44d8f..4341dca 100644 --- a/src/Static/Time.bf +++ b/src/Static/Time.bf @@ -2,13 +2,18 @@ namespace Strawberry { static public class Time { + static public bool FixedTimestep = true; + static public float TargetDeltaTime = 1 / 60f; static public float Elapsed; static public float PreviousElapsed; + static public float RawElapsed; + static public float RawPreviousElapsed; static public float Rate = 1f; static public float Freeze; + static public float RawDelta; - static public float RawDelta => (1 / 60f); static public float Delta => RawDelta * Rate; + static public float TargetMilliseconds => 1000 * TargetDeltaTime; static public bool OnInterval(float interval, float startDelay = 0) {