Tuesday, August 26, 2014

C-3: Yonder Window Breaks

All the window logic, that I've come across, on the web with relation to Game Development is unnecessarily complicated. I don't quite understand the need for this added complexity. Why does everyone insist that a Window has to created manually using the functions that Win32 API provides. They all seem to work in C++ as language, but utilize none of the advantages the language brings. Baffling. Perhaps it's knowledge issue, perhaps the API to make window creation easy simply isn't widely known. I suspect that to be the case, but I am not certain. MS is partly to blame for this, their own code doesn't make use of the API they ship for window creation most of the time. Maybe there is a reason for that, I don't know again. BUT I am lazy, and I don't feel like writing the same bit of logic/boilerplate every-time a new windows application is needed.

So to that end, I am using ATL. More specifically CWindowImpl template that is described in ATL.

Basic implementation is stupidly simple. You can compare this to the effort taken to do same from scratch.
Window.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <Windows.h>
#include <atlbase.h>
#include <atlwin.h>

// Uses ATL/WTL to create the window.
class Window : public CWindowImpl<Window>
{
public:
 Window();
 ~Window();

 void ProcessMessages();

 BEGIN_MSG_MAP(Window)
  MESSAGE_HANDLER(WM_PAINT, OnPaint)
  MESSAGE_HANDLER(WM_ACTIVATE, OnActivate)
  MESSAGE_HANDLER(WM_CREATE, OnCreate)
  MESSAGE_HANDLER(WM_CLOSE, OnClose)
  MESSAGE_HANDLER(WM_SIZE, OnResize)
  MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
  MESSAGE_HANDLER(WM_KEYUP, OnKeyUp)
  MESSAGE_HANDLER(WM_CHAR, OnCharInput)
  MESSAGE_HANDLER(WM_INPUT, OnRawInput)
 END_MSG_MAP()

private:
 LRESULT OnPaint(UINT msg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
 LRESULT OnActivate(UINT msg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);

 LRESULT OnCreate(UINT msg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
 LRESULT OnClose(UINT msg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);

 LRESULT OnResize(UINT msg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);

 LRESULT OnKeyDown(UINT msg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
 LRESULT OnKeyUp(UINT msg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
 LRESULT OnCharInput(UINT msg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
 LRESULT OnRawInput(UINT msg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
};
And your class definition simply becomes.

Window.cpp

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
#include "Window.h"

Window::Window(WindowMessageHandler &handler) : m_isActive(false), m_ExtHandler(handler)
{
}

Window::~Window()
{
 if (m_hWnd)
 {
  DestroyWindow();
  m_hWnd = NULL;
 }
}

void Window::ProcessMessages()
{
 MSG msg;

 while (m_hWnd)
 {
  if (m_isActive)
  {
   if (!PeekMessage(&msg, m_hWnd, 0, 0, PM_REMOVE))
    break;
  }
  else
  {
   if (!GetMessage(&msg, m_hWnd, 0, 0))
    break;
  }

  TranslateMessage(&msg);
  DispatchMessage(&msg);
 }
}

LRESULT Window::OnPaint(UINT msg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
 PAINTSTRUCT ps;
 HDC hdc;
 hdc = BeginPaint(&ps);

 if (m_ExtHandler.Paint)
 {
  m_ExtHandler.Paint(reinterpret_cast<WPARAM>(hdc), reinterpret_cast<LPARAM>(m_hWnd));
 }

 EndPaint(&ps);

 bHandled = true;

 return 0;
}

LRESULT Window::OnActivate(UINT msg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
 if (wParam == WA_INACTIVE)
 {
  m_isActive = false;
 }
 else
 {
  m_isActive = true;
 }

 return 0;
}

LRESULT Window::OnCreate(UINT msg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
 return DefWindowProc(msg, wParam, lParam);
}

LRESULT Window::OnClose(UINT msg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
 m_isActive = false;
 
 if (m_hWnd)
 {
  DestroyWindow();
  m_hWnd = NULL;
 }

 return 0;
}

LRESULT Window::OnResize(UINT msg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
 return DefWindowProc(msg, wParam, lParam);
}

LRESULT Window::OnKeyDown(UINT msg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
 return DefWindowProc(msg, wParam, lParam);
}

LRESULT Window::OnKeyUp(UINT msg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
 return DefWindowProc(msg, wParam, lParam);
}

LRESULT Window::OnCharInput(UINT msg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
 return DefWindowProc(msg, wParam, lParam);
}

LRESULT Window::OnRawInput(UINT msg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
{
 return DefWindowProc(msg, wParam, lParam);
}
And how do you use it? Simple
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Window wnd;

wnd.Create(NULL,
  CWindow::rcDefault,
  _T("Default Window Title"),
  WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE,
  WS_EX_OVERLAPPEDWINDOW| WS_EX_COMPOSITED
 );

while (wnd.m_hWnd)
{
 wnd.ProcessMessages();
}

Now I've added some stuff in this logic to help with things that will be talked about in future, but for now, it's enough. Whole ATL header is very human readable, and easy to figure out which methods you need to call if you want some functionality of base win32. It essentially wrap Win32 functions in to methods you can call, forgoing some of the common bits, like always having to pass HWND to the method. All in all, I think it should be used more, assuming you are doing what MS terms "Desktop applications". The library isn't support for "Windows Store applications". Reason for which seem entirely arbitrary to me.

Anyways, that's it for now. Next I will get wiring to get DirectX renderer and/or RawInput Handler going.
I've set up multiple branches on Git, so I can work on these items independently.

Tuesday, August 19, 2014

Act 2: The refinement

I've thought more about the ideas/brain-dump from the last post. This post is the refinement of those ideas, getting some concrete game-play ideas on the page. Previous post was very abstract in type of game it was going to be.

The Refinement
I've already mentioned I want the game to be turn based game with simultaneous resolution. But what exactly is the type of game?, what are the objectives?, how is it played? Hopefully I can answer all these question in this post.

Game Type: In short it's an Asteroids clone. Pretty much every one has played an Asteroids clone at some point in their lives. It's a classic staple of gaming world. It gets rehashed in different way by different people, gets called different things, but at it's core it's simple addictive 2D game.

Game Play: Unlike traditional asteroids, this one isn't going to be fast paced shooter. Since goal here is to make it turn-based. Instead, player will interact with the game in phases, well, two phases.
  • Phase 1: Planning, this is where player (and AI) will create a command list for their ship or ships (and NPC/T). I'll get into commands and how to issue them later. The ship will then follow those commands until next round or until it dies, which ever happens first.
  • Phase 2: Resolution, this is where both the game and ship will execute their commands list, and their interactions resolved based on the physics rules of the universe. Each resolution last a preset amount of time. That time period is TBD based on play testing. But the player will know how long a resolution time is.
Player commands the ship(s): All the player commands for this ship fall into two group, which is nice and simple.
  • Move Ship: Command for movement will be quite unlike inspiration source games. Player will not get to point to a spot in empty space. Instead they will provide Thrust Value and Thrust Direction. The Game Universe will use the Newton's Laws of Motion, so unless the player provides opposing thrust their ship will continue to move in the initial direction for all subsequent rounds. Again I'll tweak the physics model based on the play testing. I might have to add some sort of friction to slow the ship down. But for the moment let's go with no friction in space.
  • Fire Weapon: Commands for weapons are again like movement, in that player is required to be predictive, and aim at position where the Target will be when the projectile or beam gets to it. I've divide the weapons into two categories, Laser-like, and Missile-like. Laser-like weapons only travel in straight lines, and require rotating the ship to face the target. While Missile-like weapons can be fired in any direction without having to rotate the ship.
Shamelessly stolen image from http://www.bluerosegames.com/silverlight-games-101/post/Generating-Asteroids-for-the-Game.aspx
Starting Position
So that's the basics principle of the game for now. Next I'll go through a paper walk-thru of the game-play. Sort of like in board-game/flow-chart style format, for one round.
Starting preconditions: Player is stationary in center of the screen, there are some asteroids around him, moving in random directions with random speeds (within some predefined range). As in the image to Right.
Command Phase (Phase 1): When player mouses over each rock of interest, the game will show them it's current velocity and projected path over the duration of current turn. Player can then select their ship and issue commands to Rotate the Ship, Thrust in particular direction (this can be independent of rotation, allowing for strafing). I still have to figure out how to display the time line properly, so that commands can be issues to be execute at particular time or in particular order. When player is satisfied with his sequence of command, he can punch the "execute" button, which will start the resolution phase.
Resolution Phase (Phase 2): This is where all the system really interact, Game's Physics system, AI system and Player's commands. The simulation will run for set time, and activate commands in whatever sequence and as specified at times specified. How the simulation occurs, is an implementation detail which we'll go over when coding it.

Rinse, Repeat. Till all asteroids are gone OR ship is dead. Lot of this will be finessed by play-testing.

Now, I think it's good time to get into Coding. Set up some basic framework/boilerplate stuff.


Wednesday, August 13, 2014

Session 1: An idea, a design, and opportunity for scope creep

Now that I've decided to make a game. It's time decide what sort of game. As with any sort of systems design, you start with brain dump, then refine, then finalize, and then get to work. One could get to work in parallel while refining the game design, but I am trying to be structured here.

The Brain-dump:
I would like to make a simultaneous turn-based tactics game. Example of which, and inspiration for my idea, include Flotilla, Leviathan Warships and Frozen Synapse. Why these particular ones? Simple, I've played those. And more importantly I've enjoyed those. 

Flotilla (F) is 3D play area, where as Leviathan Warships (LW) and Frozen Synapse (FS) are both 2D play areas. For this effort, I will go with 2D play area as well. It's simpler in terms of math and code complexity, which I suspect will be essential to get to completion.

My game will be closer in look and play style to LW and FS. In that, the player controls one or more ships, i.e issues orders to, ships then execute as many of those orders as possible within the time limit. Thing that will be different is way the orders are issued. The input and command mechanism in both LW and FS bugged me. So the goal there is to come up with a scheme that works and still maintains the depth of the game-play system.

As for art style and audio, I am not an artist nor an effects or music specialist. So, art will be all place holders hand-drawn and scanned in. Everyone loves programmer art. Audio will be again programmer audio, i.e effects made by my voice recorded by Sound Recorder in Windows. And lastly for music, I am tempted to just some public domain classical works, if I can find any, else it's no music. But these are for the moment secondary concerns. Primary concern is Code, and how to structure said code.

Next time we try to refine these ideas in to some static screens to illustrate the flow of the game. Perhaps list out some component properties, such as types of objects and their behaviours. Composition of components and their properties/stats. I suspect next post will be quite big and contain several images.

Tuesday, August 12, 2014

What to do with this space?

What to do indeed!

I know!!

Over the next few weeks, I will attempt to document my efforts. Effort which predominately relate to developing a game, using C++ and Direct X, which as you probably figured is purely Windows only. However, I would like the Rendering, Audio and Input systems to be replaceable or portable to other APIs, so lets see if I can accomplish that.

I will also provide sources/links to sources which I use in this effort. Perhaps they will help others as well. As for my own Source Code, I suspect that would be available via some Version Control Site, such as Bitbucket or Github. We'll cross that bridge when we get to it.

That's it for now. I have to get going there is a Ontario Cider Festival going on. I must try some of them.

Later Alligators!