Special Effects

8 02 2009

This is one of the last core parts of the game, at least when it comes to the Arenafighting (we got a lot of lobby, networking and boardgame to take care of). This is also one of the best parts as we will be able to handle explosions, lightnings, sparkles and other effects. I have decided to create an EffectEngine similar to the ShotEngine but to tweak it and keep it a little more open as effects will probably act and look very different depending on the effect we want to create.

First, as usual, I start with an Interface:

package interfaces
{
	public interface IEffect
	{
		function update():void
		function updateDying():void
		function remove():void
	}
}

As you can see, the effects will not have many common functions, but the ones they have, the EffectEngine will use. Let’s build an abstract class of the Effects, using the Interface we just made:

package effects
{
	import interfaces.IEffect;

	// abstract
	public class AEffect implements IEffect
	{
		public var life:int
		public var flag_Dying:Boolean
		public var flag_Dead:Boolean

		public function AEffect(life:int)
		{
			this.life = life
			flag_Dying = false
			flag_Dead = false
		}

		public function update():void
		{
			// here the effect will be updated each frametick
		}

		public function updateDying():void
		{
			// if flag_Dying==true , update with this function
			// instead of the normal update()
			// this is to be able to smoothly remove effects
			// when they are done
		}

		public function remove():void
		{
			// when updateDying is done (flag_Dead==true), remove effect
		}

	}
}

The only thing we add in this abstract class is 3 variables. time is the time the effect will be visible in millisecs. Now we are ready to create our EffectEngine.

package engine
{
	import effects.AEffect;
	import managers.Time;

	public class EffectEngine
	{

		static private var instance:EffectEngine
		private var aEffects:Array

		// ************ SINGLETON CLASS *************
		public function EffectEngine()
		{
			init()
		}

		public static function getInstance():EffectEngine
		{
			if (EffectEngine.instance == null)
			{
				EffectEngine.instance = new EffectEngine();
			}
			return EffectEngine.instance;
		}

		private function init()
		{
			aEffects = new Array()
		}

		public function addEffect(effect:AEffect)
		{
			//Add a new effect into the effectlist
			aEffects.push(effect);

			// different from the other engines, the effects
			// has their own addChild procedure as they looke very
			// different to each other.
		}

		public function updateEffects():void
		{
			var tempEffect:AEffect
			var flagDelete:Boolean

			for (var i:int = aEffects.length-1 ; i>-1 ; i--)
			{
				flagDelete = false	// reseting deleteflag.
				tempEffect = aEffects[i]

				if (tempEffect.flag_Dying == false)
				{
					// if effect is alive and kicking!
					tempEffect.update()
					if (tempEffect.life < 0)
					{
						tempEffect.flag_Dying = true
					}
					tempEffect.life -= Time.getInstance().timeElapsed
				}
				else
				{
					// if effect is about to die out
					tempEffect.updateDying()
					if (tempEffect.flag_Dead == true)
					{
						tempEffect.remove()
						flagDelete = true
					}
				}

				if (flagDelete == true)
				{
					trace("Removing effect from list")
					aEffects.splice(i,1) // removing shot from the shot list.
				}
			}
		}
	}
}&#91;/sourcecode&#93;

As usual, it is a Singleton-class preventing several instances of EffectsEngine being created. Also, The normal 'add' function is there just like the shotEngine. Biggest difference is in the updateEffects() function, hopefully it is explaining itself. The procedure of an effect is this:
First it gets created and then immediatly added to the EffectEngine.
The effect updates itself using update() until 'life' has reached 0.
Then the effect update the updateDying() instead until , flag_Dead == true
Last the effect is removed from the list and is running the function remove().

Now I will create two effects that will need some kind of explanation. One Explosion (that is actually two effects in one, I'll explain later) and one BombDust that will dust up the arenaground where the explosion has been.
Let's start with the Explosion :) Here is my visual idea of creating it:

<img class="alignnone size-full wp-image-215" title="explosion" src="https://papergem.files.wordpress.com/2009/02/explosion.jpg" alt="explosion" width="510" height="250" />

I will use two planes. One that is aligned to the ground giving a lightEffect on the ground, and one that is a "billboard". A Billboard is a plane that is always facing the camera, no matter where the camera is placed. The Billboard will have an animated movieclip where I use an explosion I rendered in 3DSMax. The groundaligned plane will just have a BitmapMaterial with an image I created in Photoshop. I will scale this image back and forth to simulate an intensity increase and decrease of the explosion.

One important thing I want to stress right now before we get to coding is that png and alpha-channels doesn't match good with performance and optimization. In some projects it is very convinient to use alpha-channels and transparency but when creating a PV3D-game you will immediatly choke the system with more than a few alphatransparencies on the screen.

My solution to this is to use the blendmode ADD. ADD 'adds' it's RGB values to the normal RGB-values giving a "lighter" effect whereever the RGB is more than 0,0,0. If you didn't catch that just remember this: Whatever on the image you don't want to impact the screen, use 0,0,0. Hmm I maybe should show you the images instead:

<img class="alignnone size-full wp-image-216" title="explosionground1" src="https://papergem.files.wordpress.com/2009/02/explosionground1.jpg" alt="explosionground1" width="90" height="89" /><img class="alignnone size-full wp-image-217" title="010" src="https://papergem.files.wordpress.com/2009/02/010.jpg" alt="010" width="128" height="128" />

Wherever it is black, it will be invisible. The brighter the rgb is, the more it will lighten up the actual screen, AND most important, this is a faster way to create lighteffects with than using alphachannels, transparency and png's. Let's look at our implementation of the Explosion.

package effects
{
    import flash.display.BlendMode;
    import managers.Layers;
    import org.papervision3d.materials.BitmapMaterial;
    import org.papervision3d.materials.MovieMaterial;
    import org.papervision3d.objects.primitives.Plane;
    import org.papervision3d.view.layer.ViewportLayer;
    import se.xcom.math.Degrees;

    public class Explosion extends AEffect
    {
        private static var layerIndex:int = 1
        private var airExp:Plane
        private var groundExp:Plane
        private var groundCount:int
        private var layerAir:ViewportLayer
        private var layerGround:ViewportLayer

        public function Explosion(posX:Number,posY:Number, posZ:Number)
        {
            super(1100);
            init(posX,posY,posZ)
        }

        private function init(posX:Number,posY:Number,posZ:Number)
        {
        // create round sphere explosion in air
            var expMat:MovieMaterial = new MovieMaterial(new mcExplosion1(),false,true)
            airExp = new Plane(expMat,1000,1000)
            airExp.x = posX
            airExp.y = posY
            airExp.z = posZ
            airExp.lookAt(View3D.scope.camera)

            layerAir = new ViewportLayer(View3D.scope.viewport,airExp)
            layerAir.blendMode = BlendMode.ADD
            layerAir.layerIndex = layerIndex
            Layers.EFFECTAIR.addLayer(layerAir)

        // create the explosionlight on ground
            var groundMat:BitmapMaterial = new BitmapMaterial(new bmpGroundExplosion1(0,0))
            groundExp = new Plane (groundMat,600,600)
            groundExp.x = posX
            groundExp.y = 0
            groundExp.z = posZ
            groundExp.rotationX = 90

            layerGround = new ViewportLayer(View3D.scope.viewport,groundExp)
            layerGround.blendMode = BlendMode.ADD
            layerGround.layerIndex = layerIndex++
            Layers.EFFECTGROUND_EXP.addLayer(layerGround)

            View3D.scope.scene.addChild(airExp)
            View3D.scope.scene.addChild(groundExp)

        }

        override public function update():void
        {
            airExp.lookAt(View3D.scope.camera)
            airExp.pitch(180)

            groundExp.scaleX = 1+Degrees.dSin(groundCount)*1
            groundExp.scaleY = 1+Degrees.dSin(groundCount)*1
            groundCount += 10
        } 

        override public function updateDying():void
        {
            this.flag_Dead = true
        }

        override public function remove():void
        {
            View3D.scope.scene.removeChild(airExp)
            View3D.scope.scene.removeChild(groundExp)
            Layers.EFFECTAIR.removeLayer(layerAir)
            Layers.EFFECTGROUND_EXP.removeLayer(layerGround)

        }

    }
}

Some explanation here is needed. In my Layers.as class I have created a lot of different layers and put them in a, in my opinion, good indexed order. Now if I just put my objects directly in one of those layers I would get a very strange effect where two explosions would be drawn upon each other like transparent squares. I want the light of the effects to blend with each other if several explosions are on the screen and that is why I put every object in a single unique layer, put the correct blendmode on them and finally put those layers on the parent layer in Layers.as
Yes it could take some time to get your head around, but hey, it works. This is this typical thing that is hard to explain unless you’ve been there yourself.
Well! The explosion now works! This one doesn’t have any long fading when it’s time to “die” but is removed immediatly.

As we’re on it, let’s create a simpler effect, the BombDust.as

package effects
{
	import flash.display.BlendMode;

	import managers.Layers;

	import org.papervision3d.materials.BitmapMaterial;
	import org.papervision3d.objects.primitives.Plane;
	import org.papervision3d.view.layer.ViewportLayer;

	public class BombDust extends AEffect
	{
		private static var layerIndex:int = 1
		private var layer:ViewportLayer
		private var airExp:Plane
		private var dust:Plane
		private var groundCount:int

		public function BombDust(posX:Number,posZ:Number)
		{
			super(8000);
			init(posX,posZ)
		}

		private function init(posX:Number,posZ:Number)
		{
		// create dustPlane
			var dustMat:BitmapMaterial = new BitmapMaterial(new bmpBombDust(0,0))
			dust = new Plane (dustMat,600,600)
			dust.x = posX
			dust.y = 0
			dust.z = posZ
			dust.rotationX = 90
			layer = new ViewportLayer(View3D.scope.viewport,dust)
			layer.blendMode = BlendMode.MULTIPLY
			layer.layerIndex = layerIndex++
			Layers.EFFECTGROUND_DUST.addLayer(layer)
			View3D.scope.scene.addChild(dust)
		}

		override public function updateDying():void
		{
			this.flag_Dead = true
		}

		override public function remove():void
		{
			View3D.scope.scene.removeChild(dust)
			Layers.EFFECTGROUND_DUST.removeLayer(layer)
		}
	}
}

Instead of the ADD-blenmode, this one uses the MULTIPLY, darkening the area instead of brighten it up.
bombdust

Now its just a few lines of code that must be implanted in the GrenadeClass just to spawn these effects whenever the grenade explodes.

override public function killByAge():void
        {
            var exp:Explosion = new Explosion(this.x,this.y,this.z)
            var dust:BombDust = new BombDust(this.x,this.z)

            EffectEngine.getInstance().addEffect(exp)
            EffectEngine.getInstance().addEffect(dust)
        }

Try out the link at the new page above (yes in the menu, called Archon2160). If you’re lucky, there might be a build there where you can see how the effects are triggered. Have fun and hope you learn something. I always do!

Advertisements




Important things to know when creating graphics for PV3D

30 01 2009

Behind this blog a lot of activity is going on, assetswise. The best thing with developing a game by yourself is that you can actually put something aside for a while and do something completely different. Tired of coding? Well grab that keyboard and create some music then? No, what about modelling? Not today, huh? Well, there’s always, texturing, level design, 2D-art, sound effects, visual effects, PR, in game dialogue, websites… etc etc.
So I’m creating a lot of content for the game and want to share some fundamental tips when it comes to modelling and texturing for PV3D.
First we need to realize that PV3D is way behind when it comes to 3D performance. We all probably know the reasons why and the fact that it is a great new experience for the flash environment doesn’t make it fresh and new as a 3D experience. Even back when 3dfx released it’s first Vodoo chip more than 13 years ago the computer could render polygons much faster and more accurate than PV3D is able to do. Still this doesn’t have to be something genuinly bad. Personally I see this as a wonderful creative challenge to actually be able to trick and cheat my way through PV3D to push the limits so the endresult still can intruige, shock and amaze users.

There are several PV3D users out there that compare the whole situation with the old demo scene from the C64, Atari and Amiga eras and I agree.

Now, then. Question is what CAN PV3D handle and how do we create optimized assets for the engine?

Let’s start with modelling:

Every triangle counts! Its obvious that it does but still people are really bad modelling optimized 3Dmodels. Even if you think you cannot remove any more polygons without distorting the whole model, you probably can. Let’s take my car as an example:

defender This model was created for another engine and even though it runs smooth as it is now on our testscenes , the model is way too detailed both for the game and for PV3D. At the moment the jeep is built up with 1108 triangles and a lot of them are very very useless. We always need to consider how close the camera will come to the unit as it’s closest and calculate whether some details are worth it.  The two crash pipes on top themselves use way too many polys and could easily be built up with 2 or 3 planes facing upwards. As the game will be a top viewed game I can remove all polygons underneath. There will be no physics in this game so the jeep won’t roll around showcasing whats underneath.

Rounded areas is always a problem so even if we cannot have square wheels we could easily take a few of the polys there. Remember that the camera will be rather zoomed out most of the time.

You can always keep the detailed model as well as might want to try a LOD technique as the camera moves in. LOD (Level of detail) is working in the way that when the camera gets closer to an object, the object switches to a more detailed model whenever the details will be more visible. In this manner you can save a lot of resources when the camera is zoomed out. I think I will try this technique in the game. Also remember to check for loose vertexes and faces when you are creating your model. In several 3D-apps there are a few automatic optimizefunctions that both removes unused vertices and also tries to optimize the model (based on angles). You could come very far with these features.

I cannot stress enough how important it is to keep the polycount down. There is nothing worse than realize in the middle of your development you will only be able to spawn 3 models and 2 bullets at the same time in a shoot’em up or the gameplay will be ruined by a framerate at 6 fps. Even though it runs smooth when you test your models, it might not do that when there are 3 in there + AI code + 2Dfx + HUD + networking etc etc. A lot of games has gone down the drain just because it lagged too much and the joy of creating the game just disappears.

Now there is another problem with PV3D. As PV3D is not able to set perspective correctly on each face, it “fakes” perspective by “skewing” the triangles. It works like a charm when a lot of triangles are involved and when its facing the camera but if you eg create a plane  with no division (only 2 triangles) and then rotate it on the y-axis you will clearly see a great distortion of the texture when ever it is facing any of the sides. This is not happening in a normal 3D-engine but is a terrible must in PV3D due to what we are given through Flash. This means that certain flat surfaces that easily could be built up with 2 triangles not instead requires a lot of polygons not to distort terribly.

This will be a huge problem for the arena ground in my game so I will think about a solution.

What about texturing then??? Well, how you handle textures in PV3D are more forgiving than triangles but if you try using a texture (BitmapMaterial) compared to a ColourMaterial you will of course see a huge difference in performance. But still.. we want textures, right?

texture_sizetest21

Above you can see the texture for the defender cannon. The difference between the 3 is the size of the texture (from left to right 128×128 , 512×512 , 256×256). Comparing the left and the right cannon you can see that the texture is of course smoothed out with 128×128 and there are more details in the 512×512 one. Same here is the question, how CLOSE will the camera get to the object? At the same time you must ask yourself, is it worth it?

I know that in this game during gameplay, the camera will be very much zoomed out just like the pic below:

hudSo in this case, any big textures are just a waste om memory and processor resources that I could use for a lot of other things. Still, usually in PV3D projects, I see a LOT of too small textures giving you that squary pixel textures. That aint cool! Using a 256×256  or 512×512 texture is more a question of memory than really that much processor so is you are seeing too many and too big pixels, try a bigger texture.

The classic way of deciding which size the texture should have is to make it on any size of 32,64,128,256 or 512. Now, (correct me if I’m wrong), believe that is a heritance from how the arcitechture of the gpu’s were built and one were always recommended not to use any bigger than 512×512. I do not believe that’s the case for PV3D as there is no GPU Hardware acceleration involved here so we’re using the normal memory. Still, going over 512×512 shouldnt be necessary unless you are working with big environments (eg the Arena ground in this game).

This texture-tips has only been about the “diffuse-maps” (the colour that are visible on the model) there are a lot to be covered when it comes to textures and materials that I probably will bring up later on in this blog. I’m not sure if any of the shaders will be used in the game but I would still like to mention them both for experience purposes and to uphold this blog as a tutorial blog :)

Now go and create some assets! Here’s some wip of mine:

djn2sallysizecomparison





Paint the paper

28 01 2009

My guess is that PV3D chose the word “paper” because the way to treat 3D objects is very much like folding paper in different ways. Even if a 3D-object in the end could look like a very solid rock it is built up with simple planes and triangles that is carefully bent and rotated (actually the building blocks gets smaller than that but let’s rest here for a while). The small sides (triangles) that are building up the world doesn’t even have two sides. If you are looking on the other side of a triangle you will actually see… nothing. Nothing at all! So to even create the simpliest sheet of paper you will need to create two planes and flip one and stick it to the other to make it look like in real life (have both a front and a back side).

Now, technically, Papervision have a lot to wish for when it comes to performance and no matter how amazing you might be codewise you won’t just be able to create detailed lifelike models because it will take you thousands and thousands of these triangles to fold and model the details of a reallife complex object. Here is where “texturing comes in”.

Instead of using all these resourcedemanding triangles to showcase something you can actually paint stuff right on the triangles. Say you want to create an orange. Instead of creating every single small bump on an orange you could just paint those bumps on an image and then ‘wrap’ this image around a more simple sphere still creating the illusion of an orange.

Now, I’ve started up creating a few of the models I want to use in my game. Below you will see my unit: “DEFENDER” – a smaller buggy-like car which later on will have a big ass cannon on the roof.

textures

To create such a model, you will need some kind of a 3D-program. I’ve been working with 3D Studio MAX for the last 10 years so that is choice of app. First of all you create the actual “folding” of the object. Placing all triangles where they should be, bending rotating and modelling out all planes will give you a model something like the untextured one above. The texture in the picture is what I draw on a regular image in Photoshop. Wrapping that image on the planes causes the textured model which gives an impression of a much more detailed model than it really is. Good textures are very very important to create a good looking game. Spend a lot of time on that!

If you are interested in special tutorials in modelling, texturing, UVW-mapping etc.. just give me a call and I’ll see what I can do. At the moment I just want to get my “defender” to be a part of the game. To use these kind of custom models you will have to save them into some format that PV3D recognizes like *.3ds or collada.  In this case I will save my model into the *.3ds format and use the inbuilt parser Max3DS in PV3D.

package
{
	import controllers.*;
	import flash.events.*;
	import interfaces.*;
	import org.papervision3d.events.FileLoadEvent;
	import org.papervision3d.lights.PointLight3D;
	import org.papervision3d.objects.DisplayObject3D;
	import org.papervision3d.objects.parsers.Max3DS;
	import org.papervision3d.view.BasicView;
	import transformers.*;
	import units.*;

	public class View3D extends BasicView
	{
		private var defender:AUnit
		private var defenderMesh:Max3DS
		private var light:PointLight3D

		public function View3D()
		{
			super();

			// set the camera in a nice position
			camera.y = 200
			camera.z = -1000
			camera.zoom = 90

			// place a light somewhere (just to get used to it, this one doesn't do much at the moment)
			light = new PointLight3D()
			light.x = 1000
			light.y = 5000
			light.z = -6000
			scene.addChild(light)

			startLoadMesh()

		}

		private function startLoadMesh()
		{
			// lets load our mesh
			defenderMesh = new Max3DS()
			defenderMesh.load("meshes/units/DefenderBody.3DS",null,"./meshes/units/")
			defenderMesh.addEventListener(FileLoadEvent.LOAD_COMPLETE,meshLoaded)
		}

		private function meshLoaded(e:Event)
		{
			// mesh has been loaded and is now put in as a parameter in this new object called Defender
			defender = new Defender(defenderMesh,new LocalController(),new CarTransform(7,0.85,0.3));
			scene.addChild(defender)

			// startRendering() must be called for the 3D-engine to actually start drawing each frame
			this.startRendering()

			// each time the 3Dengine redraws the screen, onFrameTick is called.
			this.addEventListener(Event.ENTER_FRAME,onFrameTick)
		}

		private function onFrameTick(e:Event)
		{
			// lets transform the unit due to the control-data it recieves.
			defender.transformUnit()
		}
	}
}

The code above actually loads my mesh and adds it on the scene in 3D. Not only that, the mesh is passed into my new class: Defender.as which is extended from AUnit so now FINALLY the chain is connected and I got a unit that do have a mesh, a control component and a transform component. I got my first primitive unit driving around on the scene!! Well.. almost, I just need to create that Defender class..

package units
{
	import interfaces.*;
	import org.papervision3d.objects.DisplayObject3D;

	public class Defender extends AUnit
	{
		public function Ball(mesh:DisplayObject3D,controller:IUnitController,transformer:ITransformer)
		{

			super(mesh,controller,transformer,0);
		}

	}
}

We need no more than that to create our unit. Remember now that we can create as many different components as we want to. Just combine them, reuse them and experiment with them to create new units.