Skip to content

Instantly share code, notes, and snippets.

@gregoryfmartin
Created February 21, 2020 13:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save gregoryfmartin/9a165342763d59a9876b0b572e1b23b8 to your computer and use it in GitHub Desktop.
Save gregoryfmartin/9a165342763d59a9876b0b572e1b23b8 to your computer and use it in GitHub Desktop.
SFML.NET - Game Core Basic Structure
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