Click here to Skip to main content
Click here to Skip to main content
Go to top

Another Star Trek Game (The Retro One)

, 27 Aug 2008
Rate this:
Please Sign up or sign in to vote.
The Star Trek game reworked, using a 2D retro look.

Introduction

After reading the article of Michael Birken, one has to admit that it is again proven that there is only one ingredient that makes a good game: the gameplay. Not the looks, not the sound. As a huge fan of the Introversion Software games, which all have a special 'old' look, I thought why not add this simple retro look to this game.

Like James Curran stated, Michael Birken's article covers the game from top to bottom, there isn't much light I can shed over this. So, I will focus more on the differences of building a 2D game (which isn't a console) that looks like a console game.

Background

The first question I needed to solve was, which technique I would use for the game. DirectX or OpenGL? The second was even more important: Since this is my first project on graphics, would I be able to master the chosen technique and write all the needed code for this on time?

After looking for a while on the web, I stumbled upon the following site: IrrLicht. This is a free Graphics Engine that supports both DirectX and OpenGL, which also solved my first question. This engine provides both 2D and 3D modes, which is perfect. I could start in 2D, and move on to 3D without the need to change the engine.

One of the largest differences when not using a console is the way we process inputs. A console program is an input-driven program. After analysing the input, we perform the required logic and then redraw the screen. A Windows (non console) program is an event-driven program. This means that I would need to analyse the events and create my own input to drive the game logic. And, because it's a window, I would need to draw the screen myself and keep on updating the screen while waiting for input.

Before Using the Code

If you want to use and compile this code, you also need the IrrLicht SDK, this can be downloaded here. It contains everything you need to start, including an already built DLL and lib.

After placing the SDK on your disk, you need to add the location of the include files and the library files to Visual Studio. Open the Options menu under the Tools menu. Select the option Project and Solution and the sub option VC++ directories.

Using the Code

You are know ready to use and compile this code. I have started from a console project template; this gives the advantage that the console will be used as the output and trace window, which makes it easier to debug the engine. In the main, I create a Game object. This object contains the functionality to setup, run, and end the game.

int _tmain(int argc, _TCHAR* argv[])
{
  Game* pTheGame = Game::getInstance();
  if(pTheGame)
  {
    pTheGame->setupGame();
    pTheGame->createData();
    pTheGame->runGame();
    pTheGame->endGame();
  }  
  return 0;
}

The setupGame() method creates the game window and the game acts. The game acts will take care of the game logic. I have put the game logic in an Act object. This Act object is handed over to a Director object. And, this Director object will allow you to switch between acts. There are three acts in this game: IntroAct, PlayAct, and CreditsAct. The Intro will draw a Star Trek logo and will show the goal of the game. The Play will contain the actual game, while the Credits will be called when the game is over, to show tribute to our victorious captain or to weep over the destruction of the greatest starship ever. The last object we need is an InputManager. This object will convert the events returned by the IrrLicht engine to inputs we like to be notified about. In our case, these will be KeyPress events.

In order to send the KeyPress events to the acts, I could have used a simple callback function, but I like the delegate concept of C#. In C++, this can be done by a Functor. The boost library provides several classes to do this, but I didn't want to use boost (at least not for this article). So, I created my own hardcoded Functor for this job.

struct FKeyPressed
{
  virtual ~FKeyPressed() {};
  virtual bool operator()(EKEY_CODE) = 0;
};

template class KeyPressed : public FKeyPressed
{
public:
  typedef bool (ACTOR::*FunctionType)(EKEY_CODE);

public:
  KeyPressed(ACTOR* pActor, FunctionType pFunctor)
  {
    m_pActor        = pActor;
    m_pFunctor      = pFunctor;
  }

  virtual ~KeyPressed() {};

  virtual bool operator()(EKEY_CODE keyCode)
  {
    return (m_pActor->*m_pFunctor)(keyCode);
  }

protected:
  ACTOR*        m_pActor;  
  FunctionType  m_pFunctor;
};

The Act object that would like to receive KeyPress events just needs to provide a function of the following signature:

bool OnKeyPressed(EKEY_CODE keyCode);

In the function runGame, the screens are rendered. For us, this means the draw function of the active Act will be called. Either we draw everything here, which I will do for both the IntroAct and the CreditsAct, because it isn't much, or we load some IDrawable objects into the Act.

I have used two types of IDrawable, those that remain rather static, and those that are more dynamic. An example of a static drawable is the ShipDisplay. This class shows the status of the ship and the game on the right side of the screen. This information always needs to be drawn. An example of a dynamic drawable is the Torpedo, which will be drawn for some period of time and will be removed from the screen afterwards. This type, that I have called an Animator, implements the IAnimator interface, which expands the IDrawable interface. An example of this is the Phaser class. The Animator provides all the code to draw it and to control the lifecycle. So, the Phaser only needs to add what is different.

void Phaser::updateInfo(Info& rInfo)
{
  if(rInfo.Alpha >= 0)
  {
    rInfo.Alpha += rInfo.Fade;
  }

  if(rInfo.Alpha >= 255)
  {
    rInfo.Alpha = 255;
    rInfo.Fade = - rInfo.Fade;
  }
}

When the lifecycle of the Phaser ends, the endAnimator is called. In the case of the Phaser, the targeted vessel is hit.

void Phaser::endAnimator()
{
  if (m_pVessel)
  {
    m_pVessel->hitPhaser(m_iEnergy);
  }
}

One of the most difficult parts was to implement the console interface. The Console class handles the looks and the input. Whenever the Enter or Escape key is pressed, the CommandManager is triggered. This manager stores the state where the input of the game is in.

enum Mode
{
    WaitForCommand,
    NavigateWaitForCourse,
    NavigateWaitForDistance,
    LaunchWaitForEnergy,
    LaunchWaitForHit,
    LaunchWaitForCourse,
    LaunchWaitForCoordinates,
    TransferWaitForEnergy,
    ComputerWaitForCommand,
    WaitForAnimation
};

The CommandManager validates the input and takes the appropriate action, e.g., let the Enterprise object transfer energy to the shields. The third class is the Controller, this object actually creates the Torpedo or Phaser animators and adds them to the PlayAct. The function runEnemyAI checks if there is a need to run and whether there are enemy vessels in the sector or not. There are three vessels: Enterprise, KlingonShip, and StarBase, which all implement the IVessel interface.

Points of Interest

First, I would like to apologize for using a great library like IrrLicht to just draw a simple console look. But, it is fairly easy to change the characters <E> to a 2D image of the Enterprise. Or, you could go even further and make it full 3D. It should also be easy to change the Controller and CommandManager classes to make it real-time instead of turn-based.

References

History

  • 27 August 2008 - First release of this article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

BadKarma
Software Developer (Senior)
Belgium Belgium
I'm from Belgium, happily (should my wife ever read this) married and have two boys.
 
After a first attempt on the Atari during college in
an old foreign dialect of basic, via Pascal I am now
developing mostly in VC++ / VB / C#.

Comments and Discussions

 
Generalcould not find files PinmemberKarthickg4-Sep-08 2:18 
GeneralRe: could not find files PinmemberBadKarma4-Sep-08 2:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140921.1 | Last Updated 27 Aug 2008
Article Copyright 2008 by BadKarma
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid