Football game
By Oscar, 22 November, 2012
A while ago, the Flash player improved immensely performance-wise by allowing it to render with the GPU. This advantage made more complex 3D libraries for Flash possible. One of the modern libraries utilising the Stage3D API is Away3D. Having only tried out PaperVision3D way earlier, I was excited to try out the new possibilities of Away3D. It’s open source, and has a physics simulation engine that’s easily hooked into a project. How about making a game to test the library out?
The game idea was classic and simple, make two players share the same keyboard and play football against each other! First to reach five goals win. In case you’re wondering, yes, the game is supposed to take place inside of a prison.
First things first. To start with, I wanted to simply move a character around in a constrained area. Then toss in a ball to interact with. This was accomplished by setting up a few default shapes from Away3D and AwayPhysics. A regular plane as the ground and a cylinder for the player. On the cylinder, its rotation was disabled simply to prevent it from falling over, instead rotation is set manually from the arrow keys input. To move to cylinder around, a force is applied in the direction the cylinder is currently facing:
// Physics body settings _body = new AWPRigidBody(compoundShape, _gruntMesh, 3); _body.angularFactor = new Vector3D(0, 0, 0); // Disable this body's rotation by physics _body.friction = 1; _body.restitution = 0.5; _body.position = _initPos; _body.rotation = _initRot; _body.activationState = AWPCollisionObject.DISABLE_DEACTIVATION; _body.linearDamping = 0; // Applying force to the body if (_touchingGround) { var forceFront:Vector3D = _body.front; forceFront.scaleBy(_pushForce); _body.applyCentralForce(forceFront); }
Having the core of the game mechanics completed I moved onto creating a bit more appealing visuals. The environment was modelled in 3Ds Max and saved in the .3ds format. Away3D has a parser for this format so you can import it right away in your code. Though we could use lighting from within Away3D, I preferred to bake it into the textures instead. This way I’d be able to get the best looking visuals and global illumination.
I later simulated a sun in Away3D by by matching a direct light to the suns position in 3Ds Max. That way the characters on screen would get dynamic shadows.
The characters are actually just one character model with an orange or blue coloured texture. I modelled his base mesh in 3Ds max, took him into Zbrush for a few tweaks and colouring, and went back to my base mesh in max to rig, skin and animate it. In game, the characters’ animations are stored as plain text in .md5anim files, like run, kick, jump etc. That way it’s easy to switch animations on the fly while in game.
Skinning is almost always the hardest process for me, and it turned out being the most problematic aspect in terms of optimisation. I started out making an ordinary rig without any thoughts of optimisation which turned out giving the game a really low FPS. So I had a word of advice from one of the Away3D makers to try and keep my rig below 20 joints as well as to limit my bindings to two joints per vertex. The final result is closer to that goal but not quite there. Had I followed that limit thoroughly the game would probably run smoothly on slower computers. Performance could also probably be improved further by using vertex animation instead of skeletal animation.
Let’s have a look on the underlying structure of the physics world and the game logic. Players and goalkeepers have their own hit boxes to allow colliding with one another. Using collision filtering groups, I could determine what collided with what. For example, the goalkeeper will try and kick the opposing player if they collide, but not the player on his own team. Those purple areas in the image are sensors. Just like their name they function. They don’t block any collisions and are not affected by physics simulation forces, instead they will just recognise them. Score increases whenever the ball’s hit box touches the sensor, simple as that!
As for the goalkeeper, he also uses a sensor. It might look odd having it so tall and above his head and all, but it’s a great way to determine whenever he needs to jump. That’s why the sensor doesn’t cover his feet. If the ball hits the sensor, jump!
The AI for the goalkeeper is extremely simple. Here’s the functions for jumping sensor and the movement:
private function initSensor():void { _sensor = new Sensor(new Vector3D(200, 700, 500), [_ball.body], false); _view.scene.addChild(_sensor); _sensor.body.position = _initPos; _sensor.body.rotation = _initRot; _sensor.addEventListener(Sensor.COLLISION, function():void { jump(); }); override protected function preRender():void { var xOffset:Number = (_ball.body.position.x) - (_body.position.x); var zOffset:Number = (_ball.body.position.z) - (_body.position.z); // Move X if (xOffset > 20) { // Move up _body.applyCentralForce(new Vector3D(_pushForce, 0, 0)); } else if (xOffset < -20) { // Move down _body.applyCentralForce(new Vector3D(-_pushForce, 0, 0)); } // Move Z if (zOffset > 20) { // Move right _body.applyCentralForce(new Vector3D(0, 0, _pushForce)); } else if (zOffset < -20) { // Move left _body.applyCentralForce(new Vector3D(0, 0, -_pushForce)); } }
The pre-render is updated every frame. In Away3D the ”up” axis is the Y-axis, so those first two lines check how far away the ball is from the goalkeeper in the X and Z-axes. If it’s far enough in either axis, just move towards it! To keep the goalkeeper from moving too far away from the goal he is attached by a constraint as seen in image above (yellow outlines). He will keep trying but the constraint doesn’t let him. I guess that frustrates him and he needs to kick someone, like the opposing player.
You can play latest version (most probably final) game here. Beware of bugs:
http://www.x-com.se/lab-files/fotbollsspelet/deploy/
Because of the skinning optimisation issues I mentioned it’s quite heavy on your computer, so if you get a low frame rate, try it on a computer with a better graphics card. You could always just check the video at the top, in case you can’t play it on your device or if you’e on your phone or tablet.