Created
February 21, 2020 13:29
-
-
Save gregoryfmartin/9a165342763d59a9876b0b572e1b23b8 to your computer and use it in GitHub Desktop.
SFML.NET - Game Core Basic Structure
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
using SFML.System; | |
using SFML.Window; | |
using SFML.Graphics; | |
namespace SFMLNetGameCore { | |
static class Program { | |
public static void Main (String [] args) { | |
new GameCore ().Run (); | |
} | |
} | |
class GameCore { | |
/// <summary> | |
/// Is the Game Core engine running or not? | |
/// </summary> | |
private Boolean Running; | |
/// <summary> | |
/// The global clock that runs the game. | |
/// </summary> | |
private Clock GameClock; | |
/// <summary> | |
/// The main window where all the action will occur. | |
/// </summary> | |
private GameWindow MainWindow; | |
/// <summary> | |
/// A holder for the FPS Delta so that its value can be used in input delegate functions. | |
/// </summary> | |
private float FPSD; | |
private PlayerObject PO; | |
public GameCore () { | |
Running = true; | |
GameClock = null; | |
MainWindow = null; | |
PO = null; | |
} | |
public void Run () { | |
Init (); | |
while (Running) { | |
FPSD = GameClock.Restart ().AsSeconds (); | |
Update (FPSD); | |
Draw (); | |
} | |
Deinit (); | |
} | |
private void Init () { | |
MainWindow = new GameWindow (); | |
MainWindow.PrincipalWindow.SetFramerateLimit (60); | |
MainWindow.PrincipalWindow.SetVerticalSyncEnabled (true); | |
// Input is triaged through multidelegates, and automatically dispatched | |
// through the MainWindow. | |
MainWindow.KDownHandler += this.CheckGlobalInput; | |
MainWindow.KDownHandler += this.CheckPlayerInputPressed; | |
MainWindow.KUpHandler += this.CheckPlayerInputReleased; | |
PO = new PlayerObject (15.0f) { | |
FillColor = Color.Red | |
}; | |
MainWindow.ToRender.Add (PO); | |
GameClock = new Clock (); | |
} | |
private void Update (float Delta) { | |
MainWindow.Update (Delta); | |
PO.Update (Delta); | |
} | |
private void Draw () { | |
MainWindow.Draw (); | |
} | |
private void Deinit () { | |
MainWindow.PrincipalWindow.Close (); | |
} | |
private void CheckGlobalInput (Object sender, KeyEventArgs e) { | |
if (e.Code == Keyboard.Key.Escape) { | |
Running = false; | |
} | |
} | |
private void CheckPlayerInputPressed (Object sender, KeyEventArgs e) { | |
PO.CheckInputPressed (e); | |
} | |
private void CheckPlayerInputReleased (Object sender, KeyEventArgs e) { | |
PO.CheckInputReleased (e); | |
} | |
} | |
class GameWindow { | |
public RenderWindow PrincipalWindow { get; } | |
public List<Drawable> ToRender { get; } | |
public delegate void KeyDownHandler (Object sender, KeyEventArgs e); | |
public delegate void KeyUpHandler (Object sender, KeyEventArgs e); | |
public KeyUpHandler KUpHandler { get; set; } | |
public KeyDownHandler KDownHandler { get; set; } | |
public GameWindow () { | |
PrincipalWindow = new RenderWindow (new VideoMode (800, 600), "SFML.NET Game Core"); | |
ToRender = new List<Drawable> (); | |
PrincipalWindow.KeyPressed += this.Window_KeyPressed; | |
PrincipalWindow.KeyReleased += this.Window_KeyReleased; | |
// Need to embed these in the multidelegates first to avoid a runtime error. | |
KUpHandler += this.Dummy_KeyReleased; | |
KDownHandler += this.Dummy_KeyPressed; | |
} | |
public void Update (float Delta) { | |
PrincipalWindow.DispatchEvents (); | |
} | |
public void Draw () { | |
PrincipalWindow.Clear (Color.Black); | |
if (ToRender.Count > 0) { | |
foreach (Drawable d in ToRender) { | |
PrincipalWindow.Draw (d); | |
} | |
} | |
PrincipalWindow.Display (); | |
} | |
private void Window_KeyPressed (Object sender, KeyEventArgs e) { | |
KDownHandler (sender, e); | |
} | |
private void Window_KeyReleased (Object sender, KeyEventArgs e) { | |
KUpHandler (sender, e); | |
} | |
/// <summary> | |
/// This is a dummy function that prevents a runtime error where a called delegate has no | |
/// encapsulated function; this is embedded first by default in this class. | |
/// </summary> | |
/// <param name="sender"></param> | |
/// <param name="e"></param> | |
private void Dummy_KeyPressed (Object sender, KeyEventArgs e) { } | |
/// <summary> | |
/// This is a dummy function that prevents a runtime error where a called delegate has no | |
/// encapsulated function; this is embedded first by default in this class. | |
/// </summary> | |
/// <param name="sender"></param> | |
/// <param name="e"></param> | |
private void Dummy_KeyReleased (Object sender, KeyEventArgs e) { } | |
} | |
/// <summary> | |
/// The entity the player controls. The only major callout here is that it supports eight-directional | |
/// movement by translating key states to flag toggles in a dictionary for valid cardinal directions. | |
/// A combination of these will yield eight-directional movement patterns. | |
/// </summary> | |
class PlayerObject : CircleShape { | |
public Dictionary<String, Boolean> MovementStates { get; } | |
public Vector2f Velocity { get; set; } | |
public PlayerObject () : base () { | |
MovementStates = new Dictionary<String, Boolean> { | |
{ "Left", false }, | |
{ "Right", false }, | |
{ "Up", false }, | |
{ "Down", false } | |
}; | |
Velocity = new Vector2f (3.0f, 3.0f); | |
} | |
public PlayerObject (Single radius) : base (radius) { | |
MovementStates = new Dictionary<String, Boolean> { | |
{ "Left", false }, | |
{ "Right", false }, | |
{ "Up", false }, | |
{ "Down", false } | |
}; | |
Velocity = new Vector2f (3.0f, 3.0f); | |
} | |
public void CheckInputPressed (KeyEventArgs e) { | |
switch (e.Code) { | |
case Keyboard.Key.Right: | |
MovementStates ["Right"] = true; | |
break; | |
case Keyboard.Key.Left: | |
MovementStates ["Left"] = true; | |
break; | |
case Keyboard.Key.Up: | |
MovementStates ["Up"] = true; | |
break; | |
case Keyboard.Key.Down: | |
MovementStates ["Down"] = true; | |
break; | |
} | |
} | |
public void CheckInputReleased (KeyEventArgs e) { | |
switch (e.Code) { | |
case Keyboard.Key.Right: | |
MovementStates ["Right"] = false; | |
break; | |
case Keyboard.Key.Left: | |
MovementStates ["Left"] = false; | |
break; | |
case Keyboard.Key.Up: | |
MovementStates ["Up"] = false; | |
break; | |
case Keyboard.Key.Down: | |
MovementStates ["Down"] = false; | |
break; | |
} | |
} | |
/// <summary> | |
/// The scalar on the Delta here is reliant upon the fact that the desired FPS is set to 60 and VSync is on. Otherwise, | |
/// the Delta becomes highly irregular and this scalar could easily result in neighbouring values ranging from 1 to 11. | |
/// With VSync and nearly locked FPS, the value of Delta becomes considerably more predictable. There's definitely a better | |
/// way of doing this, but I'm unsure what it would be at the moment. | |
/// </summary> | |
/// <param name="Delta">The time between frames, taken from the main game loop through a multidelegate.</param> | |
public void Update (float Delta) { | |
if (MovementStates ["Right"]) { | |
Position += new Vector2f (Velocity.X * (Delta * 100), 0.0f); | |
} | |
if (MovementStates ["Left"]) { | |
Position += new Vector2f (-(Velocity.X * (Delta * 100)), 0.0f); | |
} | |
if (MovementStates ["Up"]) { | |
Position += new Vector2f (0.0f, -(Velocity.Y * (Delta * 100))); | |
} | |
if (MovementStates ["Down"]) { | |
Position += new Vector2f (0.0f, Velocity.Y * (Delta * 100)); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment