Sunday, May 27, 2012

Story Mode

Save all humans


I'm now mid-through my second loop, which has a focus on mid term gameplay.

I first worked on implementing the 'save humans' mechanic. The basic idea is to collect humans a la Choplifter, then drop them to a safe place. Here's the 1982 original Apple II game for those of you too young to remember:



Now you can see that in Choplifter the helicopter has to land on the ground to collect the humans, a control mechanic that I don't have in my game (and I don't want to have). Remember my 'ship' only flies at two altitudes and is never able to actually land on the ground.

In Jesse Schell's excellent book 'The art of game design', I remember an interesting point about the advantages of designing a mechanic that can be reused in various contexts. It gives the player confidence and provides a sense of satisfaction because using a control he already knows he can explore new possibilities without new things to learn. It also relates to the amplification factor of the action as the player only have to learn to use a particular action once, but then the multiple possible reactions (or effects) depend on the context and amplify the control possibilities.

To comply with this idea of reusing existing mechanics, I decided to use the same 'slow-down to a stop' mechanic I developed for the fuel tank. This mechanic works at the lower altitude. As the little humans obviously should be on the ground, this altitude difference problem needs to be addressed.

The solution I developed is simply to have platforms on which the ship can stop, and having the little humans running toward the platform while the ship is waiting.

So I first made a generic human model with as few polygons as possible, yet enough for skinning animation to be implemented later:


There came the problem of scale. Should the humans be at a realistic scale given my building scale? Or should they be scaled up in order to better identify with them? I kinda did a compromise here, I made them small enough to integrate with the environment, yet large enough to read their features. I do think it works but I can change my mind later when designing final assets.

I then made the platforms. The one for collecting humans (blue), and the ones for unloading them (green).


Note the blue one having a solid collision mesh attached for the aliens to collide with it. The spheres are the triggers.

I think the player will easily relate to the fact that when a human disappears below the platform, he is being lifted to the cargo. I've added some stats to the HUD to help.

Of course the graphic design will have to be made in order to convey the function of the platform. But I could say that for all the gameplay assets of the game anyways.

Portals

As seen in the above screenshot, I've added portals to the gameplay elements. The idea is quite simple, you're given an objective at the beginning of a level, when this objective is fulfilled, the portal opens and you can move to the next level. It doesn't mean you have to do so however, you can continue collecting extra items or points until you decide to leave. That's basically the way I designed the success and failure condition within the scope of a level in order to create a first version of the Story Mode.

Again, I've made it all data driven, so here's a screen of a level with the new items added:


I've made it so you can associate as many humans to a platform by simply placing those humans in its vicinity. They will automatically run toward the closest platform. The percentage of accomplishment is automatically computed depending on the number of humans in a level.

Finally, the sequence of levels for the story mode and the objective to reach is simply defined in a lua table.

Enough talk, let's see how it plays:


A lot needs to be tuned but you get the idea. Now I need to work on designing some actual levels and objectives and see how that works. Next I'll have to work again on enemies behavior (which are a little rough right now) and add more variety to both enemies and items.

Oh and did I talk about exploration yet? I think this will play a large part in my game, but for that I'll need some actual graphic assets so all stuff doesn't look the same...

Hope you enjoyed this post, and as always feel free to comment.



Sunday, April 22, 2012

PSP Jeep

Today, a post that has nothing to do with my game project.

Untapped potential


This small Jeep project is an exercise that I give to my second grade students @ HEAJ (Video Games Group).

The project makes use of various techniques in order to achieve the best possible rendering of a CJ-7 Jeep on the PSP using FreshEngine. A second exercise will come later where this jeep is placed inside a small game level designed and created for this purpose.

In a way, I think exercises like these also demonstrate how there is always untapped potential in a platform. That unfortunately is a reality of gaming hardware, they become obsolete before being pushed to their limit.

Actually, I think there's no limit but the imagination. Look at some of the latest C64 demos (from Booze Design for instance) and how new creative techniques can make this 30 years old machine do things that we though would never be possible.

So enough talk, here's a small video of the result of my small PSP Jeep project:



Making the PSP Jeep


One of the constraint was to make it fully functional for later use in an actual map. So movable parts had to be setup correctly and animation-ready.



Also I asked for the engine to be modeled so that if we crash the jeep into an obstacle, the hood could be opened and the engine revealed. The bottom of the jeep also had to be rendered realistically. All had to be done within a 4500 triangle budget with as few UV splits as possible.


On PSP, achieving nice environment reflections for the car paint and mirrors is challenging. One of the reason being that there is no real reflection mapping available in the hardware gpu. Instead it is capable of computing UV coordinates based on the result of lighting computations (shade mapping). So playing with bended normals and attaching shading lights to the camera can do the trick.

With this exercise, I explain to the students how to use multiple pass texturing with a custom blending equation to combine the environment texture with the color texture using the alpha channel of the same texture as a reflection mask:


Textures


The constraints for the two textures were as follow:

64x64 texels texture for environment, 512x512 texels texture for Jeep with alpha channel used as reflectivity mask for envmap. Both textures quantized to 8bits/texel.

I could have ask to split the jeep into color matching parts and assign each part to a 4 bits texture (16 colors) for the most aggressive optimization, but I thought it would be too much for them to handle. So I went for a single atlas texture with all the parts together and 8 bit quantization (256 colors). That's more in line with current texture compression techniques (like DXT1) where there is no global color constraint and you usually go for a single atlas. I might split and optimize my texture at some point to get the most out of the bandwidth and cache hits.

Anyways, here's what was asked:

Env map (here zoomed 2x):


Texture color channel:

Texture alpha channel:

Of course all has to be rendered @ 60 frames per second. Again, YouTube doesn't do justice to the smoothness of the animation. Also, it has been grabbed in interlaced mode because my grabbing hardware doesn't support 480p, so the actual quality is much higher than the one in the video.

I added some full-screen AA and bloom to make it shine. There's a fixed cost for these effects and the jeep in itself doesn't cost as much as what can be seen in the video (rather around 5 ms in close-up). Thus AA and bloom cost won't be added twice when I'll place the jeep in the level. I've got plenty of headroom for a nice environment @ 60 FPS.

Right now the animation is procedural. At some point, I'd like to set it up with dynamics so I can drive inside the map. That'll be for later if I can spend some time on it.

Software used: Maya, 3dCoat, Lua, FreshEngine PSP.

Hope you enjoyed it. As always, feel free to comment.

Thursday, April 12, 2012

First WIP video !

Sentinels

As can be seen in the Tiled screenshot in my previous post, some new aliens were placed in the maps, ready to be implemented. I made these to guard some areas where I'll be able to place more objectives later on. So I called those Sentinels.

So the idea is to have them move around, as if they were guarding the area. When the player enters the area, they turn on some laser, pivot toward the player and start tracking him down. Then their reaction is based on that laser sighting that triggers a blast when the player crosses the laser.

I needed to have the laser to follow walls so the player could hide behind a wall and not being touch by the laser. I use a ray that is casts against the environment and allows me to compute a scale factor for the laser mesh so that it scales to the exact distance to the wall. Here's my Sentinel setup:


The lower collision sphere is used for the Sentinel navigation (collision with the environment) while the upper sphere is a trigger that is used when the Sentinel is being shot at. There's also a cylinder trigger inside the laser mesh.

Video, yay !

I thought it would be nice for you to see the thing actually running (at last), so here it goes ! This first video has been grabbed on PC (using FRAPS). You can see the latest enhancements added to the project, mainly the shadows now integrated on PC as well. This helps a lot in feeling the height of objects. This also includes a quick Cg shader that I wrote to display basic textures and lighting, just for the sake of testing.



The game (and the video) is actually running at 60 Hz but YouTube is unfortunately unable to handle that for now. Please watch the video in 720p.

Hope you enjoyed this post, feel free to comment.





Sunday, April 8, 2012

The second coming

So long dear PSP.


After testing a new control scheme, I finally decided to stop support the PSP for my game and fully embrace twin stick control. It makes shooting at things a lot more precise and controllable and removes any of the previous frustration. It's a lot more fun to shoot stuff around.

My current controller layout is as follow:

Left stick -> move ship in screen space
Left shoulder -> switch altitude plane

Right stick -> shooting direction
Right shoulder -> intended to be used for weapon change, not yet implemented

So basically, movement control on the left side and weaponry on the right side. Sounds simple enough.

Note that I do not plan to use secondary shoulder buttons as these do not exist on the VITA, and I want my controls to be as universal as possible. So I'll use the common denominator of all current (and hopefully future) platforms.

Ditching the PSP incidentally makes life much simpler in term of development because the PSP was in my pipeline the only console to not support programmable shaders, and it was also a lot less powerful than the newer platforms cpu-wise.

I will now have to write a basic Cg shader that will be used during my early development stage and replace the old fixed-pipeline shaders with this one. Remember, the eye candy will come later, so will the fancy shaders :)

I have to say it saddens me to stop making this game on the PSP, it's such a great little platform. I will keep working on the PSP for teaching at the school though. I will post soon some of the latest stuff I've done on it, mostly for fun.

Level building improvement


As I said in my previous post, the only thing missing for a flexible level building pipeline was to support layers in Tiled, that's done now. I also support various tile sizes (through support of different tilesets) so now I can add large pieces to my environment. This will be useful for creating huge natural environment (Canyon, mountains... you name it).


You can see I can now superimpose objects to the ground elements, which allows for many combination. For instance the new fire element can be placed either on small or large ground tiles.

Second loop: Mid-term gameplay

Now that the the Toy is working just fine, I need to start thinking about the player's mid-term objectives. This will be the main theme of my second loop:

  • Develop others and more rich enemy behaviors so that the player can use his skills at controlling the ship, and use the environment elements to his advantage
  • Create mid-term objectives, such as saving humans or collecting special items
  • Implement success and failure conditions to develop a real sense of accomplishment
  • Implement 'story' mode with level dependence and progress through a series of levels
  • Develop new dynamic environment elements such as doors
  • Manage level boundaries
  • Integrate difficulty levels

Oh, and next post, I'll try to have some video of the game up and running. Thanks for reading and happy Easter.


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


Wednesday, February 15, 2012

PSP Water

Today, a post that has nothing to do with my game project :)

I've been asked several times how we do our water effect on the PSP:
http://www.youtube.com/watch?v=QtIHRqt9dV0&feature=g-all-f&context=G2f3806aFAAAAAAAABAA

As the PSP is ageing now and slowly moving to the exclusives realms of homebrew and demo makers, I thought it wouldn't hurt to explain how we do.


It's a bit tricky so I hope I can make myself clear enough :) Here it goes:

The idea is first to have an animated map representing the waves moving. To do that I used some procedural texture generation tool to create each frame of the moving waves. Here is a frame of the animation.



You have to pack all the frames of the animation into a single 512x512 texture (the largest supported by the PSP) and then later do the animation by moving the UV coordinates over each frame.

I choose 102x102 pixels for each frame so i could pack 25 frames of animation into the 512x512 texture (five rows of five frames). I know it sounds weird.

I could have used 16 128x128 frames, but that would have been a short loop @ 60 frames per second.
Or I could have used 64 64x64 frames, but those would have been pretty low def.
Hence the peculiar resolution choice.

Then I used nVidia normal map filter pluggin for photoshop in order to generate a normal texture.


Then you need to quantize your texture using a very specific 256 colors palette. To create the palette, think of it as 16 row of 16 columns. In each column you can store the X component of a normal spanning an entire hemisphere, and on each row you can store the Y component.

Basically that gives you all the possible normals over an hemisphere stored into the palette:


Now when you quantize the texture with this palette, that means each of the texels now uses one of the 256 possible normals. It's a little rough, but when it's moving you barely see the quantization. Here's one frame from the quantized texture (cropped).


To render the reflection effect,you just have for each palette entry (256 entries) to compute the reflected vector from the eye vector around the normal vector that you know is stored in that palette entry (it's just a XY gradient so you can do that procedurally without actually using the palette).

That gives you a new vector that you can use as texture coordinates to sample an hemispheric environment texture. We use a small 32x32 texture of a sky with clouds and sun. The reflection and sampling with bilinear filtering is done in software using the VFPU (implemented by my partner @ Fresh3d Yann Robert), so it's very fast (a few microseconds).

Here's the environment texture:



Last you have to replace the palette entry used by the texture with this sampled color. So basically 256 vector reflection and color sampling per frame.

For every texel using this entry (normal) in the texture the reflected color will be displayed. And voila.

I think it's a neat method because of its fixed cost and no use of multiple pass fill-rate hungry methods.

There's a drawback in that the reflection is not in perspective, it's parallel, but it's hard to say if you don't know it. Furthermore this can be hidden by some billboard fake global, low freq, specular effect on top of it (that's what we do) to modulate the hi-freq wave effect.

Told ya it would be tricky :)

There's a envMap() lua function that applies to a color lookup table (palette) to directly compute all this and do the palette update. For those of you lucky enough to be able to use the engine :) Check this on the engine documentation here: http://freshengine.net/FreshEngineCommunity/documentation/scripting-reference-guide

Look for clut (color lookup table) in the effect section.