First a note that I didn’t get the code for the last couple of articles posted before so that’s been done now. Today’s article will work on setting up some debugging and counter logic that we’ll use going forward. We’re also going to fix a small bug in our handling of turns left in last time.
Last time we created a pair of counters to track how long we’ve been running. The tick
variable holds how many times our game has run through the update
handler while the turn
variable tells us how many times the player has performed an action. We’re going to add three more counters and move these values into a separate class. Create a new file in the program named GameCounters.cs
and define it as follows
{
public int Tick { get; private set; }
public int Turn { get; private set; }
public DateTime StartTime { get; private set; }
private DateTime LastFrameTime { get; set; }
private double FrameDelta { get; set; }
GameCounters()
{
Tick = Turn = 0;
StartTime = DateTime.Now;
LastFrameTime = StartTime;
FrameDelta = 0.0;
}
}[/code]
This creates a class that holds our current Tick and Turn counters. It also adds three new values that hold the date and time we start our game, the date and time of each frame, and the difference in time since the last frame for each frame.
Now we’ll add a few three more calculated properties to this class. Before the constructor add the following three properties.
public double AverageFPS => Tick / SecondsSinceStart;
public double CurrentFPS => 1.0 / FrameDelta;[/code]
If this looks unfamiliar, I’m using a new syntax introduced in C# 6. This uses the lamba arrow (=>) to define the body for the expression. The effect is exactly the same as if the methods had had a block body with a single return statement. In other words
[code language=”csharp”]
public double CurrentFPS => 1.0 / FrameDelta;
[/code]is the equivalent of:
[code language=”csharp”]public double CurrentFPS
{
get
{
return 1.0 / FrameDelta;
}
}[/code]in a smaller space.
For short
get
methods I find the new syntax easier to read.
We’ll also add methods to the class to update the counters from our Update
handler.
{
DateTime now = DateTime.Now;
Tick++;
FrameDelta = (now – LastFrameTime).TotalSeconds;
LastFrameTime = now;
}
public void NewTurn()
{
Turn++;
}[/code]
Now we need to update our game to use the new class. Replace the two existing counter variables with a single variable of the new GameCounter class.
We could initialize the counter class in our OnLoad
handler, but I want to keep that to setting the initial game state. Instead we create the instance of the class immediately before starting the loop at the end of the Main method before the _rootConsole.Run();
line.
_rootConsole.Run();[/code]
We also now need to use the new methods whenever a turn or tick passes instead of directly changing the variables. Replace the turn++
call at the end of the loop handling key strokes with _counters.NewTurn();
. Replace the increase of the tick counter at the end of the Update handler with a call to the new method so tick += 1;
becomes _counters.NewFrame();
.
In additon let’s add a new print statement to show the new counters. In the Render handler after the existing print statement add a new one.
While these print statements are useful, I don’t think we’ll want them on the screen all the time as we add more functionality. Let’s add one more property to the CameCounters class.
and update the constructor to set this property to false initially.
{
Tick = Turn = 0;
StartTime = DateTime.Now;
LastFrameTime = StartTime;
FrameDelta = 0.0;
Visible = false;
}[/code]
We’ll update our Render handler to only show these statements when the Visible
property is set to true in our Render handler.
{
_rootConsole.Clear();
_rootConsole.SetChar(playerX, playerY, ‘@’);
if (_counters.Visible)
{
_rootConsole.Print(1, 1, $"Current tick: {_counters.Tick} Current turn: {_counters.Turn}", RLColor.White);
_rootConsole.Print(1, 2, $"Time since start: {_counters.SecondsSinceStart:.000} Current FPS: {_counters.CurrentFPS:.0 fps} Average FPS: {_counters.AverageFPS:.0} fps", RLColor.White);
}
_rootConsole.Draw();
}[/code]
To toggle the flag, we’ll use the d key (for Debug). After the case statement for the Q and Escape keys, add one more.
_counters.Visible = !_counters.Visible;
break;[/code]
If you run the application as it is now, you’ll see that the statements are hidden at first, but will show after you press the D
key. Pressing the D
key again hides the information.
I also mentioned a bug in our handling of turns. Restart the game and press D
. You’ll notice it shows one turn has passed. In fact if you press any key, including unrecognized ones such as F
and the turn counter increases. That’s because we’re setting the userAction
flag to true any time that we get a keypress.
We only want turns to pass when the player actually performs an action. Interface type activity (such as showing the menu here with the D
key) shouldn’t count nor should keys we don’t recognize or act upon. Let’s fix this by only seting the userAction
flag to true for keys resulting in the player peforming an action. Change the swtich statement handling keypresses within the Update
handler to the following.
if (key != null)
{
switch (key.Key)
{
case RLKey.Up:
userAction = true;
playerY -= 1;
break;
case RLKey.Left:
userAction = true;
playerX -= 1;
break;
case RLKey.Down:
userAction = true;
playerY += 1;
break;
case RLKey.Right:
userAction = true;
playerX += 1;
break;
case RLKey.Q:
case RLKey.Escape:
_rootConsole.Close();
break;
case RLKey.D:
_counters.Visible = !_counters.Visible;
break;
}
}[/code]
Now we only set the flag when the user moves. Quitting or toggling the display of debugging information does not count as an action.
Today we’ll do one more change to move the code that should only execute after a turn and the code that executes every time through the Update
handler into separate functions. This will leave the Update
handler focused on code to process player input.
Create two new methods at the bottom of the Program class.
{
_counters.NewFrame();
}
private static void ProcessTurn()
{
// Ensure player stays on the screen
if (playerX < 0)
playerX = 0;
if (playerX > screenWidth – 1)
playerX = screenWidth – 1;
if (playerY < 0)
playerY = 0;
if (playerY > screenHeight – 1)
playerY = screenHeight – 1;
_counters.NewTurn();
}[/code]
And change the end of the Update
handler to call these methods. Change this part of the Update
handler:
if (userAction)
{
// Ensure player stays on the screen
if (playerX < 0)
playerX = 0;
if (playerX > screenWidth – 1)
playerX = screenWidth – 1;
if (playerY < 0)
playerY = 0;
if (playerY > screenHeight – 1)
playerY = screenHeight – 1;
_counters.NewTurn();
}
// Real time actions
_counters.NewFrame();[/code]
into
if (userAction)
{
ProcessTurn();
}
// Update frame timers and counters
ProcessFrame();[/code]
We’ve done a number of changes in this article. download a zip file with the code to this point. Next time we’ll actually start implementing a real game by adding someone else besides our player to the game.