Thursday, 06 January 2011 12:17
As mentioned in a previous post, I figured out how to use the same weapon AI code to register both enemy and player weapon hits. I've since implemented the code necessary to have the enemies fire at the player, and additionally, the turrets now track the player as they move past. Weapons aren't exactly coming out of barrels though, they're being emitted from the center of the object, so there's still a bit of work to.
I've also added in a property for all weapons called "target" which is simply an object handle. It's now theoretically possible for the enemies to track other objects besides the player and shoot at them. I'm thinking of including missions such as "protect the convoy", so the player will have allied units that can help or be shot at by enemies.
With this in mind, and the fact that the game is written to play using an accelerometer on a phone, I'm also investigating ways to make it easier to hit enemies since it's actually pretty hard having to tilt and twist the phone to line them up. One idea I had was to shoot weapons in the style of games such as Silkworm, basically firing two shots each time fire is pressed, one in the direction the helicopter is heading, and one towards the ground to make it easier to hit ground targets.
I've also removed the fire button. Simply tapping the screen fires the weapons, it doesn't matter where you tap. I figured since I can't support multi-touch, there's no real point forcing the player to hit a button to shoot.
The last thing I did in this area was to move the control panel to the top of the screen since the action takes place at the bottom and the last thing I want to do is hide what's going on down there...
Finally, it's time for a bit of drudgery - the menu system. I really needed to get something better as I'm up to the stage where I need to work out how levels will be designed. I'm planning on doing some sort of drag'n'drop system, so I can try levels as I develop them without having to go back and forth editing a level file, then importing the file into Shiva and restarting the engine.
The menu system is pretty complete, though not all options are functional.
I'm also pleased to say that so far, performance is still pretty good. Even with tonnes of weapons, explosions and enemies flying around the screen, the game has yet to drop below 25 frames per second on my Desire, so long as I disable the flash explosion effect. This bodes well for performance on more powerful phones such as the dual core beauties being revealed at CES as I write this...
Heli Merc Demo 5: Shooting enemies and menus
Return to the Heli Merc section!
Tuesday, 04 January 2011 18:14
Shiva3D provides two methods by which an object to detect collisions, colliders and sensors.
Setting an object as a collider basically makes the mesh sensitive to collisions, giving what can be considered pixel perfect collision detection. This setting also allows Shiva's dynamics controller to detect collisions and react accordingly, which is the basis for providing a gravity simulation.
Sensors on the other hand are very course, providing collision detection within a rectangular or spherical shape, which is positioned so that it overlaps the object. Because of the shape limitation, unless the object is similar, the collision area isn't going to be perfect. Multiple sensors can be used to cover as much of the volume of the object as possible.
The all relates to the method I'm currently using detect collisions between weapons and enemies. During each frame, a ray is calculated for each active bullet that projects the movement of the bullet during the next frame. This ray is then fired into the scene using scene.getFirstHitCollider(), which returns the first object that the ray hits, if any.
Obviously, since I was successfully using this method to detect bullet to enemy collisions, it'd work for bullet to player collisions?
Setting the helicopter set as a collider and assigning it a dynamics controller caused it to go berserk, gyrating all over the screen as though it was hitting dozens of invisible objects. Calling dynamics.getLastCollisionContactCount() returned 16, which was impossible as the helicopter wasn't actually touching anything.
Or was it?
After thinking about it for a bit, I realised what was going on. Basically, setting an object to be a collider in Shiva must create what is effectively an invisible object that overlaps the position of the real one. Since dynamics are sensitive to colliders, I can only assume that the dynamics engine was detecting collisions with the invisible sensor object.
An object that has a dynamics controller, can't simultaneously be a collider. And no, you can't cheat by overlaying an invisible helicopter on top of the real one because the dynamics controller will still detect it.
Because I'm using ray to object collision detection for weapons (rather than object to object collisions), the simplest solution was to create a clone of the helicopter's object and make it a collider, but disable dynamics. Then, at the start of each frame, the helicopter clone is moved so that it matches the rotation and position as the real one, except, offset by 50 units on the Y axis so it's off the top of the screen, making it both invisible to the camera and nowhere near the real helicopter which would cause problems with the dynamics controller.
During each frame where rays are fired into the scene for all the active bullets, a second ray is fired for each bullet, offset by 50 on the Y axis, to detect bullet collision with the player's helicopter.
This neatly bypasses the limitation with colliders and dynamics while still maintaining the physics simulation.
To illustrate the point, the above image shows a blue wire-frame helicopter that is the invisible version of the player, used for collision detection. Normally, this clone is off the top of the screen, I've just moved it down so it's in the shot.
Oh, and the vehicles are pink because that's how Shiva identifies objects that are colliders when the game is viewed in anything less than runtime mode.
Sunday, 02 January 2011 08:58
One of the things that annoys me about some games is that they favour prettiness over performance.
I'd rather a responsive game, than a beautiful slug.
With this in mind, I've just spent even more time digging further into the performance issues. I realised that I'd need more information to figure this out, so I created a HUD that contains check boxes to control the graphics, and I made it accessible from a Menu button at the bottom of the screen. As before, I suspected that the main performance eaters were one or more of the background terrain model, the cloudy sky image, the flashing light effect for explosions or the shiny specular lighting effects.
With some proper testing under way, it quickly became apparent that the biggest performance killer was the background hills. Further investigation revealed that it was entirely due to the material used to render them, which had been set to receive both fog and dynamic lighting. Simply turning off these settings gave the game an instant 4fps boost. To sidestep this problem, I textured the hills to look like they were being obscured by fog, making them successively lighter the further they were from the camera. The shadows are simply be drawn onto these textures, avoiding the need for lighting.
The Sky texture was also a big issue, which I found somewhat surprising because the it wasn't large, only 128x64 pixels, and it's just a stretch, textured polygon that was parented to the camera to keep it in place. Just turning this off while the game was running gave me back 10fps, so doing the sky as a billboard was a really, really bad idea.
Shiva supports the ability to use a texture as a scene background, and when I'd first begun to investigate Android development, this feature hadn't worked. On the off chance that it had been fixed, I decided to get rid of the sky model and try the background texture again. Sure enough, it's functional now, and even better, it consumes half the frame rate of the textured polygon version, weighing in at around 4 frames per second.
The last two settings, the explosion point light source and specular lighting, seem to have little effect on the frame rate, consuming approximately 1 frame per second each.
Finally, with all four features turned on, the frame rate hovers around 29 frames per second, dropping to 26 fps with half a dozen enemies on screen, which is a big improvement, however, I'd still like to see it in a range of 28fps to 32fps once I finish the tweaks.
Out of interest, with all four features disabled, leaving just the enemies and foggy ground models active, the game runs at slightly over 40 frames per second.
Thursday, 30 December 2010 14:11
I finally had some time over Christmas to get stuck into Heli Merc, starting with making some of the enemies move.
Rather than begin with the easy ones, such as the ground vehicles which can only move forwards and backwards, I decided to tackle a jet fighter. After creating the model, I set about modifying the enemy control script to have an "air plane" function.
To add some variety, I decided to make it so that the planes, once they have flown past their target (to start with, the player), will then execute a turn and come back for another pass. This looks like the plane rolls it's wings so that the camera is looking directly at the top of the plane, followed by a turn, then finally the plane will roll so that it's wings are level again. In order to avoid the well known gimbal lock problem, which in this case was triggered by rotating the plane on two axes, the rolling of the wings is accomplished by rotating the plane within it's local space, while the turn is performed by rotating the plane within the global space. There's various other ways of avoiding this issue such as calculating the rotations using quaternions, but it was just plain easier to do it the way I've done it for this case.
The next thing to do was some more work on the graphics. I simply wasn't happy with the way things have progressed so far when it comes to the background, and the biggest issue is the limited resources on the phone. It's simply not feasible to have fancy graphics, so I've been trying out loads of different ideas to come up with something that I feel looks OK.
I really like the idea of the "sunset look" which I've been trying to achieve. Initially, I had the sun in the background and made the sky a flat colour, but it really just looks too plain.
A couple of days ago I had a brainwave though, and I decided to make the background bright and the foreground dark, so the vehicles and aircraft in the foreground are almost a silhouette against a bright sky and hazy hills on the horizon. This also needed a background texture, which is simply a shot of some clouds given an orange hue and drawn onto a large square polygon that's attached to the camera so it stays in place when the camera moves.
This started to look a lot better, but I felt that there needed to be some sort of highlight on the foreground vehicles to give them some depth, so I got rid of the sun in the sky and moved the light source so that it's almost coming directly from the left. I also cranked up the yellow and turned the specular settings right up on the enemies and the player giving everything a yellow glow.
The final touch was getting rid of the cactuses and rocks I had previously and just putting in some small spiky bits of grass that are randomly placed.
It all looks a lot better, but it's still a pretty sparse background.
Another new feature is that I've added a point light source that is switched on for a couple of frames at the location where a missile explodes, giving a nice little flash that lights up any nearby vehicles. The plan with this is to use it when a vehicle explodes, but for now it's happening all the time.
And so yet again we encounter the biggest issue that I'm having with this project: the frame rate. Prior to my graphics improvements I was getting somewhere around 28 frames per second on the phone which would drop down to about 18 fps when the helicopter was close enough to the ground to trigger the dust whirlwind particle emitter.
With the latest changes, and having removed the dust particle emitter, I'm getting about 24fps, dropping to about 18fps when all the enemies are on screen at once and the player is flying near the bottom of the screen, so that they are all visible.
I've tried compiling versions of the game with various features turned on and off. The background cloud graphics seems to be eating 3 frames per second of overall performance. The fog effects consumes another 1 frame per second, the explosion point light takes 2 frames per second, and the bright yellow highlights seem to be sucking up about 3 frames. It's not much individually, but add them up and It's getting slow.
It should mention that it was worse than this before, crawling along at 10 frames per second. I had to tidy everything up as I was using too many textures and materials. I combined the individual textures into one 512x512 pixel atlas that is used by everything. There's now only three materials in total, one that is affected by fog, one that is not and a final one that is shared by all the vehicles to give that shiny effect.
The vehicles currently have no texture and are still just grey coloured.
The next step is to work on the performance further, and perhaps put some sort of options menu to turn off features like the explosion flash for slower devices. I'm sure there's still more than can be done to speed everything up though, so I'll keep investigating.
Things will start to get easier moving into next year when the dual core Tegra 2 phones start to hit, and even then, the Galaxy S is still pretty quick. I'm doing my testing on my HTC Desire which has pretty average 3D performance, so if I can get it running OK for that, it should be fine on the Galaxy S or higher.
Try out the latest version discussed in this blog post!
Return to the Heli Merc section!
Sunday, 28 November 2010 13:44
This time round I got stuck into the enemies, starting with designing a few low-poly models, including a jeep, a tank, an artillery piece with a few variations of each.
These are the brand new enemies, so fresh, that they haven't even had a coat of paint (texturing). I wanted to keep the polygon count as low as possible, so I've set a limit of 200 triangles per vehicle. So far, only the tank has come in under budget, and that's purely because it doesn't have wheels, so they'll need some more work before texturing starts.
With some sample models done, I had to get them into Shiva, so I wrote a Python script for Blender that would extract each of the individual models into separate files which could then be converted to DAE using Blender 2.5. The reason for doing this is that I like to work on all the models in one file to keep them all a similar size, style and share the same material between them which makes them easier to import into Shiva. I could have left them as one large group and imported them into Shiva, but then I'd manually have to break them up into individual models, and since I'll be re-working these models several times over the course of this project, I'd like to automate the process as much as possible.
Once the models were in, it was time to create a new AIModel to control enemies. This one is going to be a lot simpler than the sprite or weapon AIModels since I'm not going to use a pool of enemies. Instead I'll create the enemies at the start of the level. Of course, I may need to change a shared pool of enemies later depending on the resource requirements and how well it runs on the phone.
This proved to be pretty easy to set up, once I'd figured out how it works. I first had to make each of the enemy models a collider, which has to be done by opening up the models in the scene viewer and using the right mouse menu to set the object as a collider. Unfortunately, currently the only way to make a model a collider is to do it manually in Shiva, scripting can't do this.
To improve performance, only the enemies were set as colliders. During the weapon update event where weapons are moved each frame, every visible weapon checks to see if it is colliding with anything by using the scene's getFirstHitCollider method and passing it a vector that is equal to the difference between the position of the weapon this frame, and where it will be during the next frame. The result from this provides sufficient information to work out the collision position (if one occurs) with pixel perfect accuracy. When a collision happen, the weapon is replaced with a small explosion as it normally is, and the vehicle's hit points are reduced. When the hit points reach zero, the vehicle is replaced with an explosion.
Using the getFirstCollider with a movement vector is a good way of avoiding fast moving weapons appearing to "pass through" enemies between frames.
I added in an explosion sound so that the missiles and vehicles made a noise when they exploded, and in doing so, found out about a minor Shiva quirk.
Each object is assigned a "Sound Bank" which is a collection of sounds that the object can play. It turns out that although a single object can play multiple sounds at once, it can only ever play a single instance of each sound that is in the bank. For instance, if a missile explodes, and I decide I want to play the "boom" noise twice, I can't simply call the play method of the sound class twice or the second call gets ignored because the sound is already playing.To be able to play the same sound effect twice at the same time, two (or more) instances of the sound effect need to be assigned to the bank, and both need to be called separately.
Try out the latest version discussed in this blog post!
Return to the Heli Merc section!