wxWidgets for a Blob Game Editor

A blob game is something like Loco Roco or Gish where there are characters and objects in the game that can compress and squeeze and roll around. Using the physics system I have been working on this semester I wanted to replicate that and create a game with the following in mind:

  • Must contain a character that can roll around
  • Must contain a level editor for placing composite objects
  • Must display features of a mass aggregate system

For me, creating the game would not be hard, but I wanted to be able to pay special attention to the editor I was going to make for the game. I wanted to create my level editor so that it was styled similar to that of Grey: The Lost Technology, and by that I mean a separate control panel so that I do not have to create a rendering loop for the active, drawable game space within a window. I knew that you could use Windows Forms with C++ so I tried setting that up. The problem with this idea is that the C++ implementation of Windows Forms is managed. This immediately created problems because my current game implementation is written in unmanaged code. After some fiddling around with Windows Forms, I eventually gave up. The primary reason I wanted to use was that I could use the visual designer, so it was primarily the speed of quickly mocking up a UI that I wanted.

I started looking at other C++ UI libraries and I found a few, such as Qt and wxWidgets. Both are really powerful UI libraries that offer all the functionality that Windows Forms offers, but cross-platform. I had done my research and found that Qt is easier to use, but wxWidgets was meant to be the most natural looking for the given system it runs on. It’s meant to look as close to system dialogs as possible. I had also looked at wxWidgets before and had already read their documentation because I saw that a company that I like was using it for their tools development. The decision was made… already having some prior knowledge of the library, I chose wxWidgets.

wxWidgets turned out to be a really easy platform to implement into my existing codebase. I usually have a lot of trouble linking a new library into my existing code, but not with this. The documentation is very clear, and I didn’t even have to compile my own version of the source code! The setup instructions for getting wx into your Visual Studio can be found here. After that, you can grab any of the “Hello World” scripts or samples from the wx documentation page.

 Code

Here is my minor implementation of a small control panel for my editor:

//CompositeCPanel.h
#pragma once

#include <wx/wx.h>
#include "CompositeEditor.h"

class CompositeCPanel : public wxFrame
{
public:
	CompositeCPanel(const wxString& title, CompositeEditor* editor);
	
	void OnUpdate(wxCommandEvent & event);
	void OnSave(wxCommandEvent & event);
	void OnComboBoxSelected(wxCommandEvent & event);

	wxTextCtrl * entryX;
	wxTextCtrl * entryY;
	wxTextCtrl * entryZ;
	wxTextCtrl * radius;

private:
	wxTextCtrl* mName;
	wxComboBox* mCombo;

	CompositeEditor* mEditor;
};

//CompositeCPanel.cpp
#include "CompositeCPanel.h"

CompositeCPanel::CompositeCPanel(const wxString& title, CompositeEditor* editor)
	: wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(250, 350))
	, mEditor(editor)
{
	wxPanel *panel = new wxPanel(this, wxID_ANY);

	wxStaticText* xText = new wxStaticText(panel, wxID_ANY, "X:", wxPoint(5, 20), wxDefaultSize, 0L, wxStaticTextNameStr);
	entryX = new wxTextCtrl(panel, wxID_ANY, "", wxPoint(20, 20), wxDefaultSize, 0L, wxDefaultValidator, wxTextCtrlNameStr);
	wxStaticText* yText = new wxStaticText(panel, wxID_ANY, "Y:", wxPoint(5, 45), wxDefaultSize, 0L, wxStaticTextNameStr);
	entryY = new wxTextCtrl(panel, wxID_ANY, "", wxPoint(20, 45), wxDefaultSize, 0L, wxDefaultValidator, wxTextCtrlNameStr);
	wxStaticText* zText = new wxStaticText(panel, wxID_ANY, "Z:", wxPoint(5, 70), wxDefaultSize, 0L, wxStaticTextNameStr);
	entryZ = new wxTextCtrl(panel, wxID_ANY, "", wxPoint(20, 70), wxDefaultSize, 0L, wxDefaultValidator, wxTextCtrlNameStr);

	wxStaticText* radiusText = new wxStaticText(panel, wxID_ANY, "Radius:", wxPoint(5, 95), wxDefaultSize, 0L, wxStaticTextNameStr);
	radius = new wxTextCtrl(panel, wxID_ANY, "", wxPoint(50, 95), wxDefaultSize, 0L, wxDefaultValidator, wxTextCtrlNameStr);

	wxButton *button = new wxButton(panel, wxID_EXIT, wxT("Update"), wxPoint(20, 120));
	Connect(wxID_EXIT, wxEVT_COMMAND_BUTTON_CLICKED, 
		wxCommandEventHandler(CompositeCPanel::OnUpdate));

	wxStaticText* nameText = new wxStaticText(panel, wxID_ANY, "Name:", wxPoint(5, 195), wxDefaultSize, 0L, wxStaticTextNameStr);
	mName = new wxTextCtrl(panel, wxID_ANY, "", wxPoint(50, 195), wxDefaultSize, 0L, wxDefaultValidator, wxTextCtrlNameStr);

	wxButton *buttonSave = new wxButton(panel, wxID_SAVE, wxT("Save"), wxPoint(20, 220));
	Connect(wxID_SAVE, wxEVT_COMMAND_BUTTON_CLICKED, 
		wxCommandEventHandler(CompositeCPanel::OnSave));

	wxString items[] = {"rod", "spring"};

	wxStaticText* typeT = new wxStaticText(panel, wxID_ANY, "Link Type: ", wxPoint(5, 170), wxDefaultSize, 0L, wxStaticTextNameStr);
	mCombo = new wxComboBox(panel, wxID_OK, wxEmptyString, wxPoint(70, 170), wxDefaultSize, WXSIZEOF(items), items);
	Connect(wxID_OK, wxEVT_COMMAND_COMBOBOX_SELECTED, 
		wxCommandEventHandler(CompositeCPanel::OnComboBoxSelected));
	mCombo->SetSelection(0);
	mEditor->NotifySelectedItem(mCombo->GetValue().ToStdString());

	button->SetFocus();

	mEditor->mFrame = this;
}

void CompositeCPanel::OnComboBoxSelected(wxCommandEvent & event)
{
	mEditor->NotifySelectedItem(mCombo->GetValue().ToStdString());
}

void CompositeCPanel::OnUpdate(wxCommandEvent & WXUNUSED(event))
{
	// update the game.
	mEditor->ReceiveStrings(
		entryX->GetValue().ToStdString(),
		entryY->GetValue().ToStdString(), 
		entryZ->GetValue().ToStdString(),
		radius->GetValue().ToStdString());
}

void CompositeCPanel::OnSave( wxCommandEvent & event )
{
	mEditor->SaveObject(mName->GetValue().ToStdString());
}

One of the problems I faced with using wxWidgets was that I did not fully understand how their event system worked and thus I was not able to take full advantage of their feature set. To achieve the goal that I wanted, I passed a reference to the class that this Dialog was linked to. While it’s probably not the best solution, it is a solution, and until I find out a better way this will be the way it stays for now.

Videos

How to make a platform engine in QuickBox2D – Part 2

Read Part One of the article, it covers some of the basics, and explains a few things.

With the release of QuickBox2D 1.0 I figure I should update my game engine articles. I won’t actually update the first one, but instead I will provide updated versions here, with links to download the files (source).

One of the best things about the new QuickBox2D is the addition of QuickContacts.

var contacts:QuickContacts = sim.addContactListener();
contacts.addEventListener(QuickContacts.ADD, onAdd);
contacts.addEventListener(QuickContacts.PERSIST, onPersist);
contacts.addEventListener(QuickContacts.REMOVE, onRemove);

function onAdd(evt:Event):void{
	if(contacts.currentPoint.shape1 == main.shape || contacts.currentPoint.shape2 == main.shape){
		if(contacts.currentPoint.normal.y >= -1 && contacts.currentPoint.normal.y < 0){
			grounded = true;
		}
	}
}
function onPersist(evt:Event):void{
	if(contacts.currentPoint.shape1 == main.shape || contacts.currentPoint.shape2 == main.shape){
		if(contacts.currentPoint.normal.y >= -1 && contacts.currentPoint.normal.y < 0){
			grounded = true;
		}
	}
}
function onRemove(evt:Event):void{
	grounded = false;
}

QuickContacts is a class that uses the b2ContactListener class that comes stock with Box2D. If you remember, we actually modified that class so we could detect if we were touching things (this way our character could jump). With this addition, we won’t actually need to modify that, AND we won’t have to make that global class. We can just do all the code on the timeline.

On the first part of the series, someone commented saying that if you add an object that does NOT have a zero density, that game would get pretty messed up. Well, he was right. That was because in the b2ContactListener, when we checked to see if we were grounded, the class was actually checking every single object against each other. Meaning that any object with zero density was being checked, and since it was sitting on the boxes right, it would set our grounded variable to true.

One more addition to the platform engine is the ability to make platforms that you can jump through the bottom, this isn’t really new feature, but it’s pretty nice. To be honest, the code that actually makes it so you can jump through the bottom of platforms was originally taken from a snippet on Actionsnippet.com, the creator of QB2D. So, thanks Zevan.

I also made numerous fixes to the code from the last version. One of these fixes include changing direct modification of gravity, to setting the main character object to sleep. I would change the gravity to 0 when the main character was standing on a platform, specifically on a slope, so that the character wouldn’t slide down. However, it turns out to be a better plan to set the object to sleep. Why? Well, if you push one of those non-zero density spheres off a platform, it won’t actually fall with the old system, whereas the new system fixes that.

Test the QB2D Engine

Download the source

What should I add to the new version? Suggestions?

Tom

How to make a platform engine in QuickBox2D

Check out part 2 RIGHT HERE

What’s up? Ready to learn a little something about QuickBox2D? Wait, you don’t know what QuickBox2D is? Well, QuickBox2D is a simpler way of using Box2D — A physics library originally written in C++ and ported to many other platforms including AS3 — and is much cleaner and easier to use than the straight Box2D library. Anyways, QuickBox2D was made by Zevan Rosser, who is a teacher at the School of Visual Arts in NYC. You can see a lot of great examples of how to use QuickBox2D at his blog, ActionSnippet.

Let’s get started here. First thing you need to do is get the latest version of Box2D and QuickBox2D at these links:
Box2DFlashAS3
QuickBox2D
And Save them in a new folder called ‘PhysicsPlatformer’.

Now open Flash CS3/4 and create a new Flash AS3 Document File. Go back to the File menu button and add another file, this time we are going to add an Actionscript Document (.as file). Save the .fla as game.fla, and save the .as file as global.as.

The global.as file is going to contain one variable (kind of a waste, I know, but it can be useful when you expand your engine a little bit more, like for implementing wall-jumping, it could be useful). Since global.as is the smallest, we’ll start with that. This is the code that should be in your global.as class file:

package {

	public class global {

		public static var grounded:Boolean = false;

		public function global():void {

		}
	}
}

The important thing to notice here is ‘static’ on line 5. When you use the word static in the definition of a variable, it means that you can access the variable by stating the class name then the variable name without instantiating the class. This is really great because when we need to access our boolean, grounded, all we type is “global.grounded”.

The next part is probably the hardest part to do, actually making the engine. When I was in works of creating the engine, I had some real trouble getting the main character (a ball in our case) to stay still on a slope, and i came up with an ingenious way of fixing this problem, and you’ll see in the tutorial here how I did it.

Lets start by setting up our QuickBox2D simulation:

import com.actionsnippet.qbox.*;

var sim:QuickBox2D = new QuickBox2D(this);
sim.setDefault({lineAlpha:0, fillColor:0x333333});
sim.createStageWalls();

sim.start();

If you paste this in the timeline of game.fla and test it, you should see a dark grey box surrounding your stage, If not, then look through your code checking for typos or something because we have a long way to go.

Next we can add our character, a simple ball will do:

import com.actionsnippet.qbox.*;

var sim:QuickBox2D = new QuickBox2D(this);
sim.setDefault({lineAlpha:0, fillColor:0x333333});
sim.createStageWalls();

var main:QuickObject = sim.addCircle({x:3, y:3, restitution:0, lineAlpha:1, fillColor:0x888888, allowSleep:false, fixedRotation:true});

sim.start();

There is obviously only one addition to this, it’s on line 7. First we make a new variable that’s typcasted as a QuickObject, then we set it equal to the results of the addCircle function. You’ll notice in the function that there is one argument, and it’s an object with a lot of stuff in it. I’ll point out the most important parts of those parameters. allowSleeping: false. If our object could ‘sleep’ then we would not be able to move it unless we wake it up, I suppose it’s fine to leave this as true (by default) and then whenever you want to move the character wake it up, but it’s not making the hugest impact on the game right now, so don’t worry about it. And, fixedRotation: true. Let’s say we are controlling our circle and we get to a slope, and we are trucking up the slope, but then we have to stop for a second because a friend is calling or something, when we let go of our keys, the ball will just start rolling down the slope, and we can’t have that.

So, great, we have a ball. How do we move it? We move the ball by adjusting it’s linear velocity. But first we have to set up a few functions to handle our keyboard input and such:

import com.actionsnippet.qbox.*;
import Box2D.Common.Math.*

var sim:QuickBox2D = new QuickBox2D(this);
sim.setDefault({lineAlpha:0, fillColor:0x333333});
sim.createStageWalls();

var main:QuickObject = sim.addCircle({x:3, y:3, restitution:0, lineAlpha:1, fillColor:0x888888, allowSleep:false, fixedRotation:true});

sim.start();

stage.addEventListener(Event.ENTER_FRAME, loop);
stage.addEventListener(KeyboardEvent.KEY_UP, checkKeysUp);
stage.addEventListener(KeyboardEvent.KEY_DOWN, checkKeysDown);

var keyArray:Array = new Array();
var speed:int = 5;
for (var i=0; i<222; i++) {
	keyArray.push(false);
}

function loop(e:Event):void {
	if(keyisdown(39)){
		main.body.SetLinearVelocity(new b2Vec2(speed, main.body.GetLinearVelocity().y));
	}
	if(keyisdown(37)){
		main.body.SetLinearVelocity(new b2Vec2(-speed, main.body.GetLinearVelocity().y));
	}
}

function checkKeysDown(event:KeyboardEvent):void {
	keyArray[event.keyCode]=true;
}
function checkKeysUp(event:KeyboardEvent):void {
	keyArray[event.keyCode]=false;
}
function keyisdown(X:Number):Boolean {
	return keyArray[X];
}

Continue reading “How to make a platform engine in QuickBox2D”