Showing posts with label code. Show all posts
Showing posts with label code. Show all posts

Global Game Jam Postmortem

Monday, February 16, 2009

It has now been 2 weeks since the jam. I am finally making the time to write about it. As a side note, I posted to twitter while at the jam (I had very little sleep during the later parts of the jam so forgive any nonsense). Check our twitter feed starting from Jan 30th for those.

The Global Game Jam in brief was like a Lan Party but at the end you made a game instead of just played them. It was noncompetitive, the goal being to pump out as many cool games as possible. I went to the San Francisco-Silicon Valley jam and met a bunch of awesome people. I had an amazing time and definitely plan to do it again next year!

My teams room (no, that isn't a real person playing the guitar in the back, he is made out of cardboard (yes, it is a real person sleeping in the back, I don't know who that is))

LOCATION:

My jam was located at Cogswell College just south of San Francisco. They did an incredible job of running the event and supporting all the participants. There was always plenty of food/drink to consume and we never had any power or networking problems. They made it easy to concentrate on making games.

TEAMS:

There were about 35 participants at this location (hundreds of others around the globe) and we split into 7 teams of 5 people each. I think there were mostly programmers followed by artists and then a few producers and audio people.

Teams were initially (for the most part) chosen at random. You were free to move around and change up teams of course, however, it seemed like most people ended up staying with their random team which surprised me.

My team included:
Rob Jagnow: Programmer
James Hudson: Programmer
Jerome Bolusan: Artist
Dakota Hurst: Technical Artist/Programmer/Audio
and Me, Brian Cronin: Programmer

THEME:

The theme set for all games was: "Whenever we are together, we will never run out of problems".

The contraints were:
  1. A complete play session must always last 5 minutes or less.
  2. Have ONE of the following adjectives to incorporate in your game: thin, evolved, or rotating.
SCHEDULE:

The whole event went from 5pm, Friday, Jan 30th to 3pm, Sunday, Feb 1st (I was told there was some important sporting event on this day involving a pig's skin that is tossed around a field with the goal of avoiding large agressive men who wish to tickle you).

Friday night was mostly about meeting everyone and forming teams. It was also when we learned of our theme and constraints. This was so people couldn't start working on anything before the event (it was a 48 hour jam after all) but also to force us to be creative.

We had about half an hour to talk to our team members and come up with a game idea(s). Our team worked really well together during this process and came to an agreement on a game concept very quickly. We decided that the theme called for a 2 player game. The theme also implied working together while also competing against each other. We decided that the players should be tied together with a sort of electric rubber band while trying to collect more coins than the other player. We chose the rotating adjective by having the players make turns around the level, thus causing the camera to rotate with them (turned out to be cooler than it sounds now that I type it). Our final game idea was modified very little throughout the development process as you can see:

Here's one of the game's many wild TURNS!!!

After this short design meeting, the teams presented their ideas to the entire group and some feedback was given. After this, we all rushed to find the best room to begin work.

The rest of the night was about setup and preparation for the next very full day of work. We decided to use XNA for the project. Only 1 of us had more than a day or two experience of with it. We decided to use it anyway as part of the point of the jam was to use and learn new technology and methods. My experience with XNA was generally good.

Our team (except for Dakota who I suspect to be a vampire, warewolf, or something) went to bed around 2am that night so we could get some rest to finish the game.

Saturday was when we got most of the game done (It is mostly a blur at this point). We worked on physics, input, rendering (per pixel lighting and shadows = awesome), level design, modeling, menu flow, and probably a bunch of other stuff. There was a ton of energy and always a lot of work to do. It was a blast!

Sunday, some of us got some sleep and some continued to polish up parts of the game. It was all coming together and we were really excited. The most surprising part of Sunday morning was finally playing the game with somebody else and realizing it was a lot of fun!

After putting in some final polish and fixing one last bug, we all ran down to the auditorium to see all the team's games and show ours off.

We were very late to this final event. All my fault. It worked out though...

All the games were very impressive considering they were made in less than 48 hours by teams of people who had never worked together before. Each game had at least 1 unique and interesting aspect to it.

Considering I plan to do this again next year, here is a right/wrong list...

5 things that went right:
  1. Very little team conflict, everyone was on the same page.
  2. Game design and scope, we didn't attempt to compete with Halo 4.
  3. Iteration, we used XNA and SVN which allowed us to quickly iterate game ideas.
  4. Communication, everyone was in the same room and was able to work together when they needed as well as, for the most part, leave somebody alone when they were working on a vital aspect of the game.
  5. Fun, we weren't struggling to make the game so we were able to have fun and enjoy the experience.
5 things that went wrong:
  1. Level format + pipeline: some more thought up front in our level pipeline would have saved a lot of time later on.
  2. XNA: it was great overall and I do not regret using it, but I did end up spending a large amount of time doing a few things that should not have taken so long (I speak only for myself on XNA).
  3. Sound code: the actual audio was great but we had some issues later in the project related to stopping and starting a music track. It wasn't the end of the world, but it was annoying.
  4. More time, the event was only 48 hours but because of the previously mentioned sports event, we had more like 36 hours, that meant we had less time to sleep.
  5. More art help, we had only 1 dedicated artist and he definitely had too much to do, one more dedicated artist would have been nice.

In closing, it was a great Jam. I fully encourage you to look out for and take part in any future events like this. If nothing else, it was great to take a break from the big project and work on a smaller game for a very small amount of time.

SuperProfiler Speed Improvement and Bug Fix

Just a quick update. Here is the change list:

  • Changed from using std::string to using const char * to improve overall speed
  • Changed SuperRoot::PopProfile() and SuperStack::Pop() to accept a name parameter, this helps SuperProfiler to find any mismatches in pushes and pops
  • Added SuperException which is thrown if an unmatched pop is detected (really useful actually)
  • Added SUPER_PROFILE_PUSH and SUPER_PROFILE_POP macros which are useful if you want to access SuperProfile from a scripting langauge that doesn't support RAII (cough, Lua, cough), Still use SUPER_PROFILE whenever you can however
  • Fixed a bug when there was no SUPER_PROFILE in a root parent function call (such as a main function). I would suggest not profiling a main type function, anything that only returns at exit
If you find any issues, please post them to the bug tracker here: http://code.google.com/p/superprofiler/issues/list

SuperProfiler Update and Screenshots

Sunday, February 8, 2009

I just updated SuperProfiler with support for comma separated value and XML output as noted here. There were a few other small changes as well.


This is an example of what you can do with the CSV output if you load it into a spreadsheet program:




And here is an example of what you can do with the XML output if you load it into an awesome program like the free XML Marker:




You can imagine how useful this can be I am sure.

Some things I plan to add in the near(ish) future are:

1. Real time data (view the performance of your code while running it!)
2. Per frame recording
3. SPIKE detection (just a way to mark a recorded frame specially if it took more processing time than the average)

SuperProfiler to the rescue!

Thursday, February 5, 2009

SuperProfiler is a library I wrote to help us track down performance issues in Zero Gear. It is open source so anyone can use it (and hopefully help make it better!).

You simply place a piece of code at the start of any function you want to profile and SuperProfiler takes care of the rest.

An example...


void SomeFunction2(int var1)
{
    SUPER_PROFILE("void SomeFunction2(int var1)");
    var1++;
}

void SomeFunction1(void)
{
    SUPER_PROFILE("void SomeFunction1(void)");
    int lala = 2;
    SomeFunction2(1);
}

void main(void)
{
    SomeFunction1();
    SuperProfiler::TextOutput textOutput("SuperProfilerResults.txt");
    SuperProfiler::Root::OutputResults(textOutput));
}


and in SuperProfilerResults.txt is...


Function List | Total Run Time=0 | Total Profiled Function Calls=2
-----------------------------------------
void SomeFunction1(void) | Total Time=0.00 | 50.00% of time | Total Calls=1 | 50.00% of calls
void SomeFunction2(int var1) | Total Time=0.00 | 50.00% of time | Total Calls=1 | 50.00% of calls

Call Tree
-----------------------------------------
ROOT
    void SomeFunction1(void) | Avg Time=0.0000 | Times called=1
        void SomeFunction2(int var1) | Avg Time=0.0000 | Times called=1


Simple and easy. Check out the Google code site for all the juicy details.

Let me know if you find it useful!

I <3 fake players

Sunday, January 11, 2009

Maybe there is a better term. A fake player is just like a real player as far as the server and other players can tell. It has no AI. It is just for testing. It exists only on the server. A lot of games have this feature (not claiming to have invented it or anything!).

I want to express my love for fake players. I just recently (yesterday) got this feature working in Zero Gear.

Here are some of the advantages to adding fake players to your multiplayer game:

  • Makes testing bandwidth and other networking issues much easier. Even some gameplay code might only have a bug when there are X number of players on the server.
  • Obviously a MMO would benefit from having fake players to test server load and whatever else those crazy MMO devs need to test.
  • Profiling. Your game might work great with 6 people playing. What happens when there is a full server with 16? Physics might slow down, clients may not be able to render all those models, maybe too much data is being sent over the net. It is better to find these sort of issues before getting 16 real people together to test.
  • Possible to translate the fake players into bots later. No promise of this for ZG! Since the fake player acts just like a real player, all it needs to do is send input to the server and the fake player is moving around.

The implementation was pretty easy after some code restructuring. I simply moved the client networking code (networking only, no graphics, input, sound, etc) into the server. Each fake player executes the same client networking code a real client executes and just like magic (Illusion, Michael), it works! This is because the server only really interacts with the real players through the network.

Overall I wish I would have gotten this feature in ZG a lot sooner! If you are developing a networked game, do it now!!!

Dedicated Server and Code

Monday, September 8, 2008

We are looking to rent a dedicated server pretty soon to host Zero Gear game servers as well as other services. We are hoping to get some suggestions from people for good (and cheap!) servers.

Here is what we are basically looking for:
1. We need full access. We have multiple software servers to run on the machine and need to be able to customize things to our liking.
2. Windows OS is required for the time being.
3. Hopefully around $100 a month but we are able to go a bit over if it is worth it.
4. It needs to have enough bandwidth and speed to host a few game servers. I don't have exact numbers for this yet but think a few TF2 or CS:S servers.
5. We want this first server to be in the L.A. or Bay Area in California.

If you have a suggestion, please leave a comment or better yet, reply to this forum post.

Now, on to the code!
Exposing the privates of a class to other classes explicitly

I have been told that title sounds dirty. I don't understand why.

This post will only be of interest to programmers who use C++ (and maybe programmers in general). No artsy fartsy stuff going on here.

Say I am designing a NetworkManager class. This class has a SendPacket() function that I don't want to expose to the public. However, I do want some classes, like my GameObject class, to have access to SendPacket().

The obvious solution is this:


class NetworkManager
{
    public:
    ...
private:
    friend class GameObject;
    void SendPacket(int packet);
    void ManageConnections(void);
    etc...
};


This works but has a few problems. First of all, we don't want GameObject to have access to ManageConnections(). Also, we later discover that the ChatManager class needs access to SendPacket as well. The lesson: friendship is hard.

The solution I use is to create an accessor class which is friend to NetworkManager and only exposes the SendPacket function. Then any other class can use this accessor to send a packet.

The best part about this is that I can be very explicit about which parts of NetworkManager I expose.

Code is the best explanation:


class NetworkManager
{
private:
    friend class SendPacketAccessor;
    void SendPacket(int packet)
    {
        //Success!
    }
};


class SendPacketAccessor
{
public:
    void SendPacket(NetworkManager & netManager, int packet)
    {
        //We have access to SendPacket()!
        netManager.SendPacket(packet);
    }
};


class GameObject
{
public:
    void Update(NetworkManager & netManager)
    {
        //We have access to SendPacket() though the accessor
        accessor.SendPacket(netManager, 1);
    }

private:
    //Our key to SendPacket()
    SendPacketAccessor accessor;
};


class ChatManager
{
public:
    void Update(NetworkManager & netManager)
    {
        accessor.SendPacket(netManager, 2)
    }

private:
    SendPacketAccessor accessor;
};


void main(void)
{
    NetworkManager netManager;
    GameObject obj1;
    ChatManager chat;

    obj1.Update(netManager);
    chat.Update(netManager);
}


If other parts of NetworkManager needed to be exposed, those parts should have special accessors just like SendPacket() does.

Happy 1000th revision Zero Gear!

Monday, September 1, 2008

I am happy to report that Zero Gear has just turned 1000!

Well, we just made our 1000th Subversion revision. Subversion is the version control tool we use to keep in sync. It is really a great tool and I recommend it to anyone who wants project management, even if you are a single developer.

We will have some more exciting posts this week with actual images and maybe even a video! I know everyone is getting bored of reading by now. Boo reading!

bitburo interview and technical STUFF

Friday, August 15, 2008

bitburo just posted a little interview with Dave about Zero Gear.

Here is the full link to the article


On the technical side of things, I decided today was the day to finally get my networking code threaded. Dave is touching up some of our maps and one of them was taking a bit too long to load which caused the client's connection to the server to timeout. This no longer will happen...

The latest version of Boost came out yesterday for you C++ programmers who use it. The Thread library has been improved greatly from pre-1.35 versions.

Also, Bullet 2.70 was released a few days ago. I haven't updated yet but I will be soon. They are doing some really cool stuff with soft bodies in newer versions of Bullet. Check out this video:



Will you see soft bodies in Zero Gear? Perhaps ;)

A Mini Map With Maximum Enjoyment

Friday, June 13, 2008

After getting Hikari working, we wanted to test it.

We wanted to create something we could use in game. Something that would be difficult or time consuming to create manually. We also wanted to see how well it held up. What better test than a Mini Map?

First, here is the Mini Map in action:


It was created by Dave's brother Ian and supports rotating, zooming, and any number of icons on the map. It was written in ActionScript 3 and is a Flash element that is being rendering in our game using Hikari.

It performs quite well. There is barely a FPS drop and it is pretty smooth.

You can see the full source here and the data file here.

Here is a quick summery of what I needed to do to get it working in game.

I call the ActionScript
loadMap(imageUrl:String, mapWidth:Number, mapHeight:Number):void
function from Lua, passing in the name of the mini map image, the width of the map in game units (1 unit is a meter), and the height (or depth in 3D).

I can then call the ActionScript
setPlayerPosition(x:Number, y:Number, rot:Number, zoom:Number):void
function, passing in the x position, y position (or z in 3D), the degrees rotated around the y axis, and a zoom amount (based on speed of the player).

There is a similar function for setting data related to other players called setObjectPosition().

From there Ian takes care of the rest and that means I have time to watch an episode of Dexter instead of writing a Mini Map in C++. Yay!

Hikari - In Game Flash GUI

Sunday, June 8, 2008












It seems lately the Holy Grail of game GUIs has been Flash. Scaleform is an example of a commercial GUI based on Flash. This does not work for indies because of the cost.

Hikari is a great option for Ogre users. It was just released yesterday and I already have it integrated into Zero Gear. It is still new and needs a little work but thanks to it being open source (LGPL), we should start seeing a lot of support from the community.

P.S. One thing to mention is Hikari only works on Windows.

You can't do it alone...

Tuesday, April 1, 2008

...unless you have some help.

Zero Gear is being created by two people. One artist and one programmer.

That is not entirely true however. We are making use of many, many tools to help us create this game. These tools have large communities of people creating them. I wanted to give some credit to these people and also just show how we are doing this so others might see how they can speed up their development with free tools.

Programming Language:
C++
C++ is a very popular programming language and we use it. Most of these tools (not all!) apply to C++.

3D Rendering:
Ogre3D
Every game needs to draw stuff on the screen. For 3D graphics Ogre is a great library. It has been in development for 6 years and been used in a lot of projects. Check it out, it will make your life easier.

GUI:
Navi
Navi is fairly new but a great solution if you want to get complex GUIs in game very fast. It uses the Gecko renderer (Firefox uses this renderer too) to render webpages to a texture. This allows you to create a GUI in something like Dreamweaver or any standard HTML, CSS, JavaScript tools. The only catch is that it is a bit slow for any real time displays. Note: This only works with Ogre.

Particles:
Particle Universe
The very FIRST thing that any game should have is PARTICLES. This is a great system to create awesome looking effects quickly. I am told that an editor is in the works. Note: This also only works with Ogre.

Texture Maps:
xNormal
xNormal is a great little tool that is kind of the swiss army knife of baking and displaying maps on 3D models. We use it to bake Ambient Occlusion into the textures of many of our models.

Physics:
Bullet
A game where nothing moves isn't very fun. Bullet will make things move. You can see it in action in our last video.

Networking:
ENet
ENet is a simple UDP networking library. It is a layer on top of UDP that adds reliability and ordered packets when needed and basic connection management. It is great if you want something easy to use but plan to implement most networking features yourself.

Network Prediction:
EPIC
EPIC is a class to extrapolate the players on the client past the most current physics update from the server. See this great article for some tips on how Valve does this.

Scripting:
Lua
Lua is a scripting language we have embedded in our game. We plan to have all gameplay coded in Lua. This will mean that it will be very easy to add new game modes, items, GUIs, animations, etc during development and perhaps even allow the community to create these too. It also helps speed up development as we don't have to recompile the game every time we want to change a single value or algorithm.

Script Binding:
luabind
luabind helps us connect Lua to our game. It uses C++ templates to generate binding code for your classes (Really good use of TMP!!). There are a few options for doing this. We decided luabind was best for us.

Script Debugger:
Decoda
This is a great tool to debug code written in Lua. It allows the standard debugging operations such as call stack, breakpoints, watch, etc. It is great if you have a lot of code written in Lua and you aren't a perfect human. This is not free however (but it is cheap and useful!).

XML Parsing:
TinyXML
XML is a really great way to store data in a human readable format. Just use it...

General Code Support:
Boost
Boost is actually a large collection of different libraries for C++.
We use:
shared_ptr
bind
function
signal
I will have to do another post about these as they are a bit more complicated...

Templatize your factories

Sunday, March 16, 2008

In Object Oriented Programming we create systems that manage objects. However, it is always a good idea to separate the implementation of the objects from the systems. This way, we can add or change objects and the system doesn't need to change.

This post is related to object Factories. A factory is responsible for creating objects. Here is a very simple example of an object factory:


IObject * ObjectFactory::CreateObject(const string & objectType)
{
    if (objectType == "Model")
        return new Model();
    else if (objectType == "Sound")
        return new Sound();
    else if (objectType == "Light")
        return new Light();

    return NULL;
}


Each of these 3 objects derive from the IObject interface. Based on the type of the object ("Model", "Sound", or "Light") we create that object (new Model(), etc).

This is great. But we can improve...

What if we want to get a list of object types that the factory is able to create? This was my question and here is the way I solved the problem.

First we create an interface for our object factory. This would allow us to create multiple object factories for different parts of an application (one for the map editor, one for the driving level, etc) or for different applications all together.


class IObjectFactory
{
    public:
        IObject *(*Creator)(void);

        virtual ~IObjectFactory() = 0 { }

        /**
        * Returns a list of objects that this factory can create
        **/
        list<string> GetCreationTypes(void);

        /**
        * Create an object of the type passed in
        **/
        IObject * CreateObject(const string & objectType);

    protected:
        void RegisterCreator(const std::string & objectType, IObjectFactory::Creator creator);

        void UnregisterCreator(const std::string & objectType);

    private:
        IObjectFactory::Creator * GetCreator(const std::string & objectType);

        map<string, IObjectFactory::Creator> objectCreators;
};


The concept is that we can Register a Creator into the IObjectFactory and then the factory will handle everything for us. We can create an object or get a list of object types that the factory can create at runtime without any additional modifications from us. The implementation for this interface is trivial...

This line is important:

typedef IObject *(*Creator)(void);


This defines a function pointer to a "Creator" function. This is a function that will create a specific object for us.

Here is a templated "Creator" for you to use:


template <typename T>
IObject * ObjectCreateFunction(void)
{
    return new T();
}


Then simply inherit from IObjectFactory in your concrete object factory and define a constructor similar to this:


ConcreateObjectFactory::ConcreateObjectFactory()
{
    RegisterCreator("Model", &ObjectCreateFunction<Model>);
    RegisterCreator("Sound", &ObjectCreateFunction<Sound>);
    RegisterCreator("Light", &ObjectCreateFunction<Light>);
}


P.S. I know that the IObjectFactory isn't a purely an interface but I am still going to call it an interface. Deal with it.

MVC LUA GUI FTW!!!

Wednesday, February 20, 2008

This is a more technical post but don't run away!

When designing a game (or any application) we tend to want to separate the graphical user interface (GUI) from the game logic. A way of doing this is called Model-View-Controller (or MVC). Here is a summery of what this means:

MODEL: This is the code and data that controls your game. An example could be setting the color of the player.
VIEW: This is your GUI. An example is a GUI widget to select a color and a button to apply that color to the player.
CONTROLLER: This is what ties the MODEL and the VIEW together. It is some logic that takes the color selected from the GUI and applies it to the Player in game.

The reason for doing this is that is separates the MODEL from the VIEW. This means we can easily change the VIEW without changing the MODEL and vice versa by only changing the CONTROLLER. And most the time we don't even need to change the CONTROLLER.

Here is the way I am handling this:
MODEL: In C++ I have code that sets a player's color Player::SetColor(colorVal)
VIEW: Using Navi, we have created a simple GUI to pick a color. This has some code in JavaScript that gets called when you change the color which sends an event telling us the color has changed and what color it changed to.
CONTROLLER: This is where Lua comes into play. Lua is the CONTROLLER that receives this event from the VIEW. The Lua code calls the C++ code Player::SetColor(colorVal) with the color from the event. This modifies the MODEL which changes the color of the player on screen.

It might seem complicated but it actually simplified things greatly and allows us to change one aspect of the game without affecting other things.

I wanna pick the color

Friday, December 7, 2007

In taking the kart color concept a bit further, we spent some time last night working on a color picker GUI. You can see the results of our work in this video.


ZeroGear color picker from marshmonkey on Vimeo

Now here is why this is cool. We did this using Navi + Lua. The C++ framework only needed slight modifications. I feel this was done extremely quick and has a nice, clean result. Here is how we did it.

We got the color picker html + javascript code from here.

And here is the part that I added to the color picker javascript:

function HSVupdate(v)
{
  var tempColor = HSV=v?v:slideHSV;
  v = hsv2hex(tempColor);

  $('plugHEX').innerHTML=v;
  $S('plugCUR').background='#'+v;
  $S('plugID').background='#'+v;

  $ND('HandleEvent', {LUAFUNC: 'SetKartColor', Color: v}).send();

  return(v);
}


This function is called whenever the user changes the color in the GUI. The line that starts with "$ND" is the message from the Navi GUI to Lua. "$ND" is the javascript code included with Navi that sends the message. "HandleEvent" is the function that Navi calls in C++ and LUAFUNC is the Lua function that "HandleEvent" will call when this event happens. "SetKartColor" is the name of the Lua function and it is passed the new color as a parameter.

Here is that Lua function:

function SetKartColor(guiArgs)

  local colorValue = GetNaviMultiValue(guiArgs, "Color")
  local color = colorValue:str()
  local colorNum = hex.to_dec("0x" .. color)

  local red = bit.brshift(colorNum, 16) / 255
  local green = bit.brshift(colorNum, 8)
  green = bit.band(green, 255) / 255
  local blue = bit.band(colorNum, 255) / 255
  local alpha = 1
  kart:SetColor(red, green, blue, alpha)

end


It is basically just a conversion function. The javascript code gives the color as a hex value in this format "4499FF". So I do some conversion with this lua bit library to get the color as red, green, and blue float values to pass to the C++ code.

And here is the SetColor C++ code that changes the color of the kart:


void OGREPlayerKart::SetColor(float red, float green, float blue, float alpha)
{
  Ogre::Entity * testEnt = kart->GetEntity();
  for (unsigned int i = 0; i <>getNumSubEntities(); i++)
  {
    Ogre::SubEntity * testSubEnt = testEnt->getSubEntity(i);
    Ogre::MaterialPtr testMatPtr = testSubEnt->getMaterial();
    Ogre::Technique * testTech = testMatPtr->getBestTechnique();
    Ogre::Pass * testPass = testTech->getPass("ColorTweak");
    Ogre::TextureUnitState * testTUS = testPass->getTextureUnitState("ColorTweak");
    testTUS->setColourOperationEx(Ogre::LBX_BLEND_TEXTURE_ALPHA, Ogre::LBS_MANUAL, Ogre::LBS_CURRENT,     Ogre::ColourValue(red, green, blue, alpha), Ogre::ColourValue(1, 1, 1, 1), 0);
  }
}


This C++ function is where I want to improve. I want to support changing colors on different parts of the player and karts (shirt, skin, paint, windshield, whatever). Also, different karts will have different parts that you can change the color of.

As you can see, this was all done in only a few lines of code. I bet you can see how easy something like a color picker is when you are using HTML/JS/CSS and Lua for your GUI.

Webify your GUI!

Monday, November 26, 2007

Alright, enough of this "art" nonsense. Time for some code updates!

I was looking for a good solution to creating a GUI system and found Navi. We have been using it for our GUI elements inside the in-game editor we are developing and I wanted to show my praise for the beauty that is a web-based GUI.

Navi is a GUI library for Ogre that allows somebody to create the look and feel of a GUI element entirely in HTML + CSS + JS and whatever else Gecko (Firefox's renderer) supports. Check out the Screenshots to see what this looks like.

Using Navi + Lua has allowed Dave to design GUI elements in Dreamweaver and me to code all the logic associated with it in Lua. This means that I only have to touch about 10% of the GUI related code which makes me a very happy programmer.