Sunday, March 25, 2012

First loop wrap up


Blind data

During my last work on blocking elements, I started playing with the idea of elements that could not only block the player but also damage the ship. Hence the electric barriers you could see in my previous post.

I need some way to apply information about the type of damages these objects can inflict. I could do with some trigger boxes, but these would be somewhat imprecise and not very efficient as they are meant to be used with dynamic objects. So I will use blind data instead.

Blind data is invisible data that can be added to a triangle of a collision mesh and queried when a collision with that triangle occurs. A 16bits integer value can be stored at each triangle to represent a collection of arbitrary properties.

In order to create and edit blind data, Maya provides an editor with the ability to visualize categories and properties in false colors:


Here, I've setup an 'info' category with value 0 as normal (green), 1 as electric (blue), 2 as fire (orange), 3 as laser (not being used yet). Black geometry has no blind data and will return 0 (normal).

Setting up the blind data in lua is pretty straightforward:

Globals.first().mBlindDataInfo = 
{
 { mName = "info",  mMask=0xffff, mShift=0, mColors = {Vector(0,1,0,1), Vector(0,0,1,1), Vector(1,0,0,1), Vector(0,1,1,1)}
 },
}

The mask and shift allow to differentiate categories and query them as separate values. Here I have a unique 'info' category, so no masking and no shift.

The color vectors are there in case you need to visualize the false colors in FreshEngine.

Head Up Display

Next was to implement shield management so I could start with full shield and have some damage when hit by an enemy mine or when colliding with the electric barrier.

We have the ability to add a custom attribute to an entity in FreshEngine:

ship:addAttr{ currentFuel = FUEL,
burningRate = FUEL_BURNING_RATE,
iddleRate = FUEL_IDDLE_BURNING_RATE,
shield = SHIELD,
minSpeed = MIN_SPEED}

Items in upper case are my initial game constants defined in a global file.

These attributes can then be changed when a particular event occurs. They can also be queried and used to display the status of the entity in the HUD for instance.


Here you can see the shield changing from green to red after a collision with the electric barrier. After a few seconds, the shield recovers to yellow and then green.

I quickly added a scoring system in order to feel a sense of achievement when doing some action (killing enemies, re-fuelling, grabbing ammo or power-ups).

Remember the graphics are all placeholders and in no way the final design :P

If you wonder what the green disk bottom right is, it's just there as I was wondering if I would need a radar to help the player figure out where he is and where the next objectives/enemies are. If it's not useful, I'll get rid of it.

Fuel

I did also implement the fuel management and the re-fueling action which simply consist of flying through the fuel tank and stop for a few seconds.


As the tank empties, the fuel bar on the left increases.

End of first loop

Well that pretty much wraps up my first loop objectives.

Now let's have a look at what these objectives were, and how close to them I came to:
  • Is my control pleasant and intuitive?

I honestly think so. And so are the people who tried it so far. It's fun (because of the amplification factor (automatic tilting of the ship, automatic camera, single click to change altitude...)) and reactive (no delay or strong inertia), intuitive (view space), and the collisions react as expected (slides and glides) so no frustration at all.



  • Can the player easily understand the height of the ship?
Yes on PSP and no on PC/PS3 because I don't have the shadows implemented yet on those platforms. But I can be reassured that it will work on these platforms as well once the shadows are in place.


  • Is it intuitive and easy to shoot static objectives on ground and in mid air?
It's alright but not perfect. As you only shoot straight of, you need to be well aligned.


  • Same for moving objectives?
Nope. This doesn't work well. As enemies are passing by, you need to turn around in order to face them again and shoot. Aiming is not precise and leads to a lot of frustration (and wasted ammo).


  • I'm quite fluent with Lua, but can I implement and integrate custom functionnalities in C++ when I need higher performance ?

I still need to learn a lot on this side. But so far I was pretty much able to implement what I needed. Thanks to the help from Fabrice and Yann. I'm looking forward to improve much on that matter.

  • Do I have a fully functionnal and convenient pipeline for quickly building dozens of levels?
Almost. I can definitely create levels very quickly indeed and that's very good for trying ideas and tuning gameplay, but working with tiles brings a lot of constraint to level building as well. I have to carefully plan my tile elements to minimize their number and allow flexible level building. For instance I need to separate the ground tiles from the building/elements tiles and use the layer feature in Tiled.

What's next : fix-up

I think I need to tackle the shooting problem first. I will try a new control scheme with separate shooting control on the right stick and see how it does.

If it works well, that means I have to ditch the PSP version as it doesn't have two sticks.

Next I need to re-factor my tile elements and support multiple layers for increased productivity and flexibility in my level building pipeline.


Sunday, March 11, 2012

My int is bigger than your float

Past weeks have been somewhat hectic with a lot of things to do both for AMA studios and the school. Finally I managed to free up some time to get back to working on my game.

The things I wanted to add at this point was the ability to load and unload maps to quickly test various level configurations. But prior to that, I needed some maps.


The key level building thing at this point is to create new elements to control the space where the player can evolve, and thus provide some control over the flow of the game.

Since the player can evolve at two height levels (upper and lower), I started by building some elements that can block the player at each level and force him to change the level if he wants to go further:


On the above picture you can see new barrier elements. Low barriers that are for blocking at lower level, high electric barriers that block at the upper level, and plain high barriers that completely block the player.

Note that I did some quick texture tests in order to see how transparency affects perception.

Now time for editing some levels with these new elements:


Ah,  I've had one tremendously annoying issue with the wall tiles. I needed to be able to turn them by 90 degrees. Fortunately Tiled just implemented that feature in one of its latest release...but...

Tiled encodes the rotation and flipping of a tile in its 4 most significant bits of the 32 bits integer tile index. Sounded fair enough until I realized that lua did not have enough precision with its 32 bits float (24 bits mantissa) to correctly interpret that data when converting from int to float (lua doesn't handle int). Unless you're working with double, you can't get a correct 32 bits integer converted into floating point. And on consoles we don't use double yet.

EDIT: Also I forgot to mention that there's no bit-wise operation in lua 5.1 anyways, I think that's something coming up in the next release.

So I came up with a rather twisted solution:

When parsing the xml file, I save the tile index as a string (I do not convert it from the file into a number).
Then I send the string to a C++ function that converts is back to int (actually a long integer) and then figures out the tile rotation based on the aforementioned most significant bits. It looks like this:


IA::tile IA::unpackTile(const char *tileID)
{
IA::tile currentTile;


unsigned long rawID = strtoul(tileID,0,10);
currentTile.id = rawID & 0x0fffffff; //mask rotation and flip info stored in the 4 most significant bits
int rotID = rawID >> 30; //keep only 2 most significant bits where the rotation info is stored
if (rotID == 0) currentTile.rot = 0;
if (rotID == 2) currentTile.rot = -90;
if (rotID == 3) currentTile.rot = -180;
if (rotID == 1) currentTile.rot = -270;


return currentTile;
}


Alright, now I've got my tiles with the right orientation, time to test some levels.

To choose the level to test, I need some menu. So while I was at it, I implemented a menu system that is completely data driven and allows me to add menu hierarchy and content in a simple lua table.


The first menu structure implements some basic states of the game, like Main Menu; Play; Pause; Game Over; Retry and Restart.

Here you can see the menu that pops up when the game is paused in the middle of an explosion (by pressing Start button) :


On this screenshot you can also see some of the barriers that forces the player to go to the upper level and confront the mines if he wants to grab the blue bonuses.

Next: life, shield and fuel management, and HUD