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