HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Basic console game architecture

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
consolegamebasicarchitecture

Problem

I'm working on some simple console game and I'm starting to get stranded in my modelling. There is some fragmentation and tight coupling going on!

In this version I would just want to be able to move my paddle over the screen (but this needs to be extended to multiple players, ill get back to that later).

This paddle is modeled as follows:

```
public class Paddle : IDynamicRenderable, ICommandObserver
{
public int Top { get; private set; }
public int Left { get; private set; }

public bool RenderStateChanged { get; private set; }

private int LastTop { get; set; }
private int LastLeft { get; set; }

private Dictionary configuration;

public Paddle()
{
Top = LastTop = 10;
Left = LastLeft = 35;

RenderStateChanged = false;

configuration = new Dictionary()
{
{ "UpCommand", ConsoleKey.UpArrow },
{ "DownCommand", ConsoleKey.DownArrow },
{ "LeftCommand", ConsoleKey.LeftArrow },
{ "RightCommand", ConsoleKey.RightArrow }
};

Draw();
}

public void Render()
{
Clear();
Draw();

RenderStateChanged = false;
}

private void Draw()
{
Console.SetCursorPosition(Left, Top);
Console.Write('█');
}

private void Clear()
{
Console.SetCursorPosition(LastLeft, LastTop);
Console.Write(' ');
}

public void Notify(ConsoleKey consoleCommand)
{
if (consoleCommand == configuration["DownCommand"])
{
LastLeft = Left;
LastTop = Top;
Top += 1;

RenderStateChanged = true;
}
else if (consoleCommand == configuration["UpCommand"])
{
LastLeft = Left;
LastTop = Top;
Top -= 1;

RenderStateChanged = true;
}
else if (consoleCommand == configuration["LeftCommand"])

Solution

I'm going architectural because coupling is an architectural issue and there is a fairly common general architecture for screen oriented games.

Game Engine

I think it will be difficult to create a screen oriented game without using a traditional game loop architecture. The basic loop might look like:

while(true) {
   timerState = timerList.tick(timerState);
   keyboardState   = keyboardState.nextState(keyboardState);  
   UpdateScreen(timerState, keyboardState);
   pause(frameRate);       
 }


In the abstract screen oriented games are simple: every so often, process the event queue and update the screen. A screen oriented game is a controlled animation.

Events

The basic event in any animation is the the transition to the next frame. To insure that there is a next frame, games have timers to generate events. Keypress events are less frequent.

All the bussiness logic is in the handlers for events. The handlers transform events into messages and send them to the screen.

Screen

A screen interprets the messages and creates and then executes a set of instructions for painting the screen. The screen is where logic that scrolls the display or bounces a ball off a paddle lives.

Sprites

Given a location and a "color", a sprite knows how to draw itself. That's it. A sprite does not know anything about screens or events. It doesn't know anything about why it is being drawn. For example, if paddle1 is a sprite:

paddle1.draw(paddle1.x, (paddle1.y + player1.paddle().delta_y), foregroundColor);


might be executed by a screen in response to some particular event.

Game State

Their can be a data structure sitting above all this which keeps track of game configuration such as the list of high scores or the mapping of logical keys like paddle_move_up to physical keys such as w.

Analysis

The big issue is that paddles know too much...or rather way way too much. They know about the hardware [key presses], they know about rendering, eventually they will have to know about other paddles.

The second issue is that there are no timer events, so besides the paddle, there aren't a lot of places for render() to live.

Code Snippets

while(true) {
   timerState = timerList.tick(timerState);
   keyboardState   = keyboardState.nextState(keyboardState);  
   UpdateScreen(timerState, keyboardState);
   pause(frameRate);       
 }
paddle1.draw(paddle1.x, (paddle1.y + player1.paddle().delta_y), foregroundColor);

Context

StackExchange Code Review Q#67803, answer score: 3

Revisions (0)

No revisions yet.