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

Actions

Information

7 responses

9 02 2009
Flashbookmarks

Oh this is great very nice article!

Join #papervision3d at irc freenode server is you want some talks with other pv3d developers or #jiglibflash for a new 3d physics engine we are working on.

9 02 2009
blackmeoo

Thanks U so much

9 02 2009
JUAN

This are really nice … articles .. why do you always use Interface?

9 02 2009
ajirenius

This is actually a perfect demonstration why and where you should use interfaces. At this stage I have only created one unit, one type of weapon, one steering and so on. But there will be 12 different units with different weapons, steering, transformation etc. If I didnt use interfaces I would need to write new code to handle each effect, each unit, each shot etc… eg using interfaces I can just put them all different effects in one array, loop them all through and call them as : IEffect.updateEffect() . If you don’t see this now, I’m ure you will see it soon enough as I have started to create more units and weapons to show later on.

9 02 2009
Robert Kabwe

Brilliant! Where are you getting all the time to create this AND document it so thoroughly? Keep it up and Thanks.

30 05 2009
Chris

Thanks very much! I’ve learned a lot from this Site.

19 03 2010
AlexG

Very nice post
I would like to ask how can I write ActionScript snippets with as3.0 sintaxys on my blog? I use WordPress. I tried but it gives italic font...

And the Special Effects application. I see the code but is there a real example we can see it in action? Thanks. Could you please also write me a mail?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




%d bloggers like this: