I feel it's the right time for me to try making a game all by myself (except for the engine part). This blog is about sharing my processes and experiences as I walk along the garage developper route. Hopefully, this will end up with an actual game :
Invasion Aftermath, Mankind's Revenge!
I had a couple things to do in order to complete my second loop. Mainly introduce dynamic environment elements and finalize life and points management.
Here's a small video showing dynamic environment with basic blocking objects and points accounting at the end of a level. Nothing fancy, just functional stuff.
Third loop: Visual design
Now this is something I've been waiting for. At last I can now start working on the visual designs for the game. This is something that will take me quite some time, so don't expect too frequent updates in the following months. I've also got a large school project in the making (more on that later), and time is flying by way too fast to my liking :P
My goal at the end of this 3rd loop is to provide a vertical slice of the game, that it a representative sample of a near-final quality version of the game.
During this loop, I plan to go through these visual design stages:
Visual identity and color signature
Hero and hero items
Aliens
Environments
Fonts
HUD
Title
Galaxy note
This piece of technology is just amazing. I bought it precisely because of the included stylus with pressure support on applications such as Sketch Book Mobile from Autodesk. And I must say it rocks. I'm using it to sketch concepts for my project when I'm on the go and I've got some time to spare.
Here are a few concepts I've been doing on the Note for my game (note these my not be representative of the final designs):
Hope you had some great holidays. Besides working on Invasion Aftermath and AMA studios' project, I actually spent most of this summer working on my personal website. I've had the domain name reserved for years but never took the chance to work on it. Now it's up and running.
It's still work in progress, but it's coming along nicely. Hope you'll enjoy it.
Now a new academic year is about to start. New students, new classes, and the introduction of a 4th grade in videogames development. This is going to be fun...and a lot of work too !
Turrets
A small update on my game project.
First, there was some bugs in the mother ship animation triggering that is now fixed.
I've also implemented a first version of turrets. Turrets are interesting because they work on two separate axis, and you have to figure out the angle for rotating the base and the angle for rotating the canon separately.
Behavior
I wanted the turets to fire only when they are almost aligned with their target. The idea is they would want to 'save' amunition if they were not sure there's a chance to hit their target. That prevents continuous firing and gives more 'thinking' behind the turrets, it makes them less mechanical and more realistic.
I also added some jitter to the turrets when firing. This serves two purposes: One, it makes the firing a bit more cahotic and more realistic, and broadens a little the target range. Without that, chances to be hit are very low as the turret is always a little 'late' in targeting you (as it tries to allign with your position).
Finally, I wanted the canon vertical angle to be limited to the horizontal plane (no possibilities to shoot downward). I'm planning to have passages in some maps where you can sneak-in by flying below their shooting range.
I also did some anticipation tests. By looking at the current biplane direction, I could rotate the turret in a way that the projectile would reach the biplane if it didn't change its route. It proved to be way too precise and much difficult to play as you basically would be hit pretty much every time. So I removed it and kept it a little dumb.
Implementation
As always I've made a basic model that is not visually representative of the final design, but is functionnaly the same:
I made a long base so it can be decided during the construction of the level (remember levels are stored as xml files and built at run time from a library of objects in memory) at which height to place the turret, depending on terrain altitude.
When doing the implementation, the question was: Do I want my projectiles to have a linear trajectory or fall down with gravity?
I decided to implement gravity as it is interesting to see the impacts of the projectiles on the ground. To my opinion it makes the experience more solid. It also alows to realise there's a turret offscreen if projectiles are falling nearby.
However I find it quite difficult to see the altitude of projectile and determine in advance if you'll be hit or not. It's ok when theres only one or two turrets firing, but it's quite difficult if there are three or more.
I made some tests with many turrets and it's just a mess. It's fun to watch but hard to play. I guess I need to think more about ways to make all that more playable, this is still a bit rough.
It could be quite interesting in stereoscopic 3D though.
I did not have this really planned for this dev loop but I realized I could not go further into exploring new mechanics if the environment was not a little more representative of my vision for the final game. In other words, if your game play relies a lot on the environment, these cannot stay completely abstract forever.
So I decided I needed to build some new test environments, and figure out an efficient pipeline for creating natural-looking terrains.
I looked for terrain software (I didn't want to sculpt all by hand) and purchased a license of the excellent World Machine, a node-based software specialized in the generation of terrain. I love it because it does only one thing and it does it really well. No fancy renderer included or whatnot, just building nice terrain data, wonderful.
So, eager to test this new software, I put up a simple node graph to create an island and get instantly some great results:
Only that my environment is all made of tiles, so I needed to figure out a working pipeline within this constraint.
Unique terrain or tiles?
In Tiled (the level editor I'm using), you can import multiple tilesets, each with its own tile size. So potentially you can have one layer with a single gigantic tile (like a unique terrain) and on top of that have a tileset with actual tiles (like the buildings or whatever).
So the question came to my mind, should I pursue in the use of small tiles both for the terrain and for the elements on top of it, or should I create a unique terrain for each map, with tiles on top?
Creating a unique terrain is very tempting. First because visually it is unique, and thus can have recogniseable features and shapes and be truely beautiful, with no repetition whatsoever. Second, because it all can be generated procedurally, while still having the ability to control the local features thanks to World Machine's fabulous vector layout capabilities.
But the sheer size of a unique terrain (even if limited to an island) poses serious challenges, both in term of memory and performances. To reduce memory usage, the traditional method is to have a set of repeating textures that are blended over the terrain depending on various criterion such as height or slope. You can also use low resolution masks (such as erosion) to further enrich your texture blending composition.
The main issue with this approach is that it requires a significant amount of computation and texture fetching per pixel. For instance if your shader requires a normal map, an albedo texture, a specular intensity map, a specular gloss (roughness) map, even by combining some of those, you'll need 3 samplers. And that's only for one material. Say you want at least some grass, rock, sand and snow, you're good for 12 samplers. And that's not counting any mask nor any shadow samplers. You could easily reach the 16 samplers limit of model 3 pixel shaders (which some of my targets are bound to) and yet have a rather poor material library.
Add to that the cost of blending all these, and you can say goodbye to your 60 fps on any machine but the PC.
With tiles on the other hand, you have a unique material (or texture) for each tile with no need for complex shaders to compute or blend multiple textures.
Generating the tiles
Tiles are great in many ways. They offer a terrific compression ratio. With a relatively small library of tiles you are pretty much limitless with the size of your map, and with the right use they can become barely visible for the average Joe. However, working with tiles can be such a pain, especially when you have the additional constraint of allowing your tiles to rotate by 90 degrees (which I do). Add to that the increased complexity of a 3rd dimension and you really are in trouble.
Fortunately, World Machine has the ability to create file-based input nodes (as opposed to fractal or random noise only) which allows the user to somewhat direct the tool.
So my plan was to create a set of hand painted 2d tiles (64x64 pixels) in Photoshop that would be the input node for World Machine to start with:
Here you can see I determined a few altitudes and transition. Black is sea level, dark grey is ground level, middle grey is my highest flying level and light grey up to white is blocking. Some tiles are repeated and rotated for test purpose.
I then input this control texture in World Machine for further processing. You can see the full graph of my first terrain implementation that generates a height field and a color texture :
World Machine has the ability to subdivide its world into regions (or tiles as they call it, but these do not necessary mean they repeat seamlessly). The great deal about these regions is that it allows for perfect matching between your input tiles and your final output. Also WM allows you to render and output specific regions, and when batching multiple regions it uses all your CPU cores in parallel, yay.
In the above screenshot you can see the effect of the WM graph on my tileset.
You can also explore details in perspective with a real-time preview renderer:
I will need to improve my generation graph a lot for generating the final assets, but this will do for now.
Let's now have a look at the output data. For each meaningful region, I exported a color and height-map (16 bits) file.
Here's the highest mountains tile height-map in Photoshop:
The main problem here is that even if the base tileset was perfectly tiling, some of the processes that were added in WM broke that tiling. Such as erosion which has some random factor and can potentially leak on another tile. For that reason, I created a Photoshop script to 're-tile' the generated region.
Now I need to generate normals. These are critical for the lighting and need to be precisely computed. For that, I don't want to rely on a 2d process, so instead I import the height-map into Mudbox. The tileable feature is back in Mubbox 2013, so that's even better. This will also give me the opportunity to sculpt some specific feature or add some extra detail when I will be producing the final assets for the game.
I bake the normals in tangent space based on the ground plane (equivalent to world base in Z up):
I process it in Photoshop to prepare it for DXT5 compression by placing the X component into the green channel and the Y component into the alpha channel:
Geometry
Now it's time to talk about the geometry for the terrain. Some engine have the ability to store terrains into an elevation data and generate the geometry on the fly. This is great for memory compression as it only stores a scalar. On the other hand, it requires even sampling, so memory is wasted where it's oversampling low frequency data (i.e. flat or smooth areas).
FreshEngine only deals with meshes, which means storing a position vector for each vertex. Meshes however can be adaptatively simplified (offline decimation) to more closely follow the frequency pattern of the data. So all in all, I prefer this approach as it allows capturing very precise, high frequency details only where it is necessary.
WM can export .obj meshes directly. However it cannot decimate. Also my decimation needs to maintain edge borders in order to keep my edges perfectly tileable. I decided to do it in Maya which has all the necessary features.
First I create a mesh with enough resolution for my needs, and less resolution on the borders (as those will be untouched by the decimation):
Then I import my elevation data through Artisan:
Finally, I made a small script that in one clic decimates the internal faces while leaving intact the border edge:
Placing textures
In a traditional height map terrain, texture coordinates (UV) are implicit. That is they are based on the XZ position of the terrain sample. With regular mesh texture mapping, you need to explicitly store UVs for each vertex. Not only that but when doing tangent-space normal mapping you need a normal and at least a tangent for computing your tangent basis (depending on the need, you can compute the binormal on the fly or store it).
As my tiles have all a fixed size, I can create UVs on the fly in my shader, based on the local XZ vertex position, much like height map terrains. Furthermore, I don't need to store a normal and a tangent to create a tangent basis as it is invarient across the tile. I just have to read the direction vector from the mesh transformation matrix and rotate my normals accordingly. In short, all that is actually needed from the mesh vertices is the XYZ position.
Now let's instantiate this tile a few times and rotating them. A few cracks are still there and need to be fixed:
This can be fixed easily in Maya by snapping the points together:
Now I repeat the process for all the tiles. At this point I did not fix all the edges as it is quite time consuming and it's not worth it at this point since these are not the final assets.
The only thing left to do it to create a smaller version of the tiles to create a tileset that can be used in Tiled to create the level:
And now, time for testing. It is really surprising at first because I was already used to the two building heights I had in my previous tests, but here there are various heights so it is more difficult to read. My conclusion is that I need to enforce some readable altitudes by scaling some peaks to fixed heights in Mudbox.
So please don't mind the cracks between tiles, again this is temporary and will be removed once I produce the final assets.
Thank you for your patience reading this extremely long post ! Feel free to comment or ask any question.
Wow, time is passing by so quickly, and with the busy end of the year at the school plus the work at AMA, I realize it's been a while since my last post. So let's get going.
Waco
Something that I've not talked about in my previous posts is the fact that the main 'character' of the game is not going to be a little spaceship like the one used in the prototype version of the game so far. As I was creating the back-story for the game, I was looking for some strong image that would strike the imagination of the viewer and make the game instantaneously recognizable. Not to mention an alien spaceship used to defend humanity from alien invasion is just so 'Independance day'...
Part of the story is the idea that the aliens strike the earth using large EMPs, changing instantaneously any hi-tech human weaponry into a pile of useless garbage. That's how they win the war so quickly.
I won't spoil all the details of the back-story here and now, but basically our hero found and old biplane that is immune to the EMP effects and will use it against the invaders.
I thought having a low-tech biplane fighting Huber high-tech aliens would provide a strong visual identity for the game, something I think is utterly important.
I choose a 1935 Waco bi-plane as a model, because it's a cool and beautiful piece of antique engineering and I think it is representative of the iconic image everyone has in mind of those magnificent planes.
Also, it is a civil plane and not a WWI plane. This is on purpose and part of the back-story of the game.
This image is from internet and copyright its owner/author.
Cargo
Usually, a plane like this can hold a maximum of two passengers (front seats) + the pilot. However part of my gameplay requires to transport humans to save them from the aliens. Only being able to transport two humans at a time was going to be extremely boring, so I had to devise some way of allowing the player improving the cargo capability of the plane.
So I made this little concept that shows how the cargo capacity could be increased from 2 to 8 by collecting upgrade bonuses through the game.
The same idea will be used throughout the game with extra weapons and structural modifications made available to customize and improve the capabilities of the plane.
To implement this, I made the choice to increase cargo by steps of 2 extra seats. So 2, 4, 6 and 8 maximum. I then simply made 4 models of the plane, one for each capacity.
Clockwise from bottom-left: 2 (normal), 4, 6 and 8 capacity. Bear in mind these models are still mock-ups and not the final modeling/rendering quality. Also, I've yet to think about the game color scheme and what you see here might not be representative.
All upgrade models are exported in the same hierarchy and only revealed at run time when the player gets the upgrade. Same for the little humans in the plane (except the pilot who obviously always stays in).
You can see in the Maya screenshot all moving parts being setup using point ant orient constraints, and ready to be animated in the game. All constraints are exported as they are and evaluated in realtime in the game, so I only have to animate (procedurally) the constrainer and any constrainee will behave accordingly.
Finally, I improved a little the camera by adding an extra zoom behavior when landing on a platform. I think it will be useful when the final graphic assets are in place to see all the little details in the models. I thought it was nice without distracting from the gameplay.
As you can see from the video, the wings span of the biplane is much larger than the one of the prototype ship I was using. I didn't want to have a tiny biplane to fly. Also my humans would have looked ridiculous. Instead I went for a little oversized plane but I didn't increase the size of the collision volume in order to keep the control as fluid as it was. This sometimes makes the wings get into the environment, but it's gameplay first in that case I guess.
As always, feel free to comment.
Next: some terrain work for improving localisation and exploration
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.
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.
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.
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.
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:
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.
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:
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.
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.