After my game school project (Kinessia), I’m still doing some experiments with the Citrus Engine.

On its latest update, there was some new objects :
- a powerful particle system.
- new objects : missile, reward and reward box.

In this tutorial, I wil explain how to create new specific object which extends a Citrus Engine object.
At the end, we will have : a teleporter, a treadmill and a cannon that fires missile !
An ugly example.

Now let’s start coding ! I start with my new classes and the state at the end.

A teleporter is simply a Sensor which can move PhysicsObject, it has a destination, and a waiting time :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package {
 
	import com.citrusengine.objects.PhysicsObject;
	import com.citrusengine.objects.platformer.Sensor;
 
	import flash.utils.clearTimeout;
	import flash.utils.setTimeout;
 
	/**
	 * @author Aymeric
	 */
	public class Teleporter extends Sensor {
 
		public var endX:Number;
		public var endY:Number;
		public var object:PhysicsObject;
 
		public var time:Number = 0;
		public var needToTeleport:Boolean = false;
 
		protected var _teleporting:Boolean = false;
 
		private var _teleportTimeoutID:uint;
 
		public function Teleporter(name:String, params:Object = null) {
			super(name, params);
		}
 
		override public function destroy():void {
 
			clearTimeout(_teleportTimeoutID);
 
			super.destroy();
		}
 
		override public function update(timeDelta:Number):void {
 
			super.update(timeDelta);
 
			if (needToTeleport) {
 
				_teleporting = true;
 
				_teleportTimeoutID = setTimeout(_teleport,time);
 
				needToTeleport = false;
			}
 
			_updateAnimation();
		}
 
		protected function _teleport():void {
 
			_teleporting = false;
 
			object.x = endX;
			object.y = endY;
 
			clearTimeout(_teleportTimeoutID);
		}
 
		protected function _updateAnimation():void {
 
			if (_teleporting) {
				_animation = "teleport";
			} else {
				_animation = "normal";
			}
		}
	}
}

A treadmill is a Platform with contacts. Instead of using a simple Platform, I use a MovingPlatform. So we can have a treadmill moving platform, that’s crazy :-D A treadmill increase velocity of its passengers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package {
 
	import Box2DAS.Dynamics.b2Body;
 
	import com.citrusengine.objects.platformer.MovingPlatform;
 
	/**
	 * @author Aymeric
	 */
	public class Treadmill extends MovingPlatform {
 
		public var speedTread:Number = 3;
		public var startingDirection:String = "right";
 
		public var enableTreadmill:Boolean = true;
 
		public function Treadmill(name:String, params:Object = null) {
			super(name, params);
 
			if (startingDirection == "left") {
				_inverted = true;
			}
		}
 
		override public function destroy():void {
 
			super.destroy();
		}
 
		override public function update(timeDelta:Number):void {
 
			super.update(timeDelta);
 
			if (enableTreadmill) {
 
				for each (var passengers:b2Body in _passengers) {
 
					if (startingDirection == "right") {
						passengers.GetUserData().x += speedTread;
					} else {
						passengers.GetUserData().x -= speedTread;
					}
 
				}
			}
 
			_updateAnimation();
		}
 
		protected function _updateAnimation():void {
 
			if (enableTreadmill) {
				_animation = "move";
			} else {
				_animation = "normal";
			}
		}
	}
}

And the last object, the cannon, a bit more complex. It extends Platform, in most of the games you can’t move it, so it doesn’t need to be a box2d dynamic body, static is ok. A cannon has a fire rate, a direction, and it fires missiles.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package {
 
	import com.citrusengine.objects.PhysicsObject;
	import com.citrusengine.objects.platformer.Missile;
	import com.citrusengine.objects.platformer.Platform;
 
	import org.osflash.signals.Signal;
 
	import flash.events.TimerEvent;
	import flash.utils.Timer;
 
	/**
	 * @author Aymeric
	 */
	public class Cannon extends Platform {
 
		public var onGiveDamage:Signal;
 
		public var fireRate:Number;
		public var startingDirection:String = "right";
 
		public var missileWidth:uint = 20;
		public var missileHeight:uint = 20;
		public var missileSpeed:Number = 2;
		public var missileAngle:Number = 0;
		public var missileExplodeDuration:Number = 1000;
		public var missileFuseDuration:Number = 10000;
		public var missileView:String = "";
 
		protected var _firing:Boolean = false;
 
		private var _timer:Timer;
 
		public function Cannon(name:String, params:Object = null) {
			super(name, params);
 
			onGiveDamage = new Signal(PhysicsObject);
		}
 
		override public function destroy():void {
 
			onGiveDamage.removeAll();
 
			_timer.stop();
			_timer.removeEventListener(TimerEvent.TIMER, _fire);
 
			super.destroy();
		}
 
		override public function update(timeDelta:Number):void {
 
			super.update(timeDelta);
 
			_updateAnimation();
		}
 
		protected function _damage(missile:Missile, contact:PhysicsObject):void {
 
			if (contact != null) {
				onGiveDamage.dispatch(contact);
			}
		}
 
		public function startFire():void {
 
			_firing = true;
 
			_timer = new Timer(fireRate);
			_timer.addEventListener(TimerEvent.TIMER, _fire);
			_timer.start();
		}
 
		public function stopFire():void {
 
			_firing = false;
 
			_timer.stop();
			_timer.removeEventListener(TimerEvent.TIMER, _fire);
		}
 
		protected function _fire(tEvt:TimerEvent):void {
 
			var missile:Missile;
 
			if (startingDirection == "right") {
				missile = new Missile("Missile", {x:x + width, y:y, width:missileWidth, height:missileHeight, speed:missileSpeed, angle:missileAngle, explodeDuration:missileExplodeDuration, fuseDuration:missileFuseDuration, view:missileView});
			} else {
				missile = new Missile("Missile", {x:x - width, y:y, width:missileWidth, height:missileHeight, speed:-missileSpeed, angle:missileAngle, explodeDuration:missileExplodeDuration, fuseDuration:missileFuseDuration, view:missileView});
			}
 
			_ce.state.add(missile);
			missile.onExplode.addOnce(_damage);
		}
 
		protected function _updateAnimation():void {
 
			if (_firing) {
				_animation = "fire";
			} else {
				_animation = "normal";
			}
		}
	}
}

I use a Signal to dispatch event onGiveDamage. It happens if a missile hits PhysicsObject.

And finally my game state :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package {
 
	import Box2DAS.Dynamics.ContactEvent;
 
	import com.citrusengine.core.State;
	import com.citrusengine.objects.PhysicsObject;
	import com.citrusengine.objects.platformer.Hero;
	import com.citrusengine.objects.platformer.Platform;
	import com.citrusengine.physics.Box2D;
 
	/**
	 * @author Aymeric
	 */
	public class MyState extends State {
 
		public function MyState() {
			super();
		}
 
		override public function initialize():void {
 
			var box2d:Box2D = new Box2D("Box2d");
			//box2d.visible = true;
			add(box2d);
 
			var hero:Hero = new Hero("Hero", {x:50, width:30, height:60, view:"hero.swf"});
			add(hero);
 
			var treadmill:Treadmill = new Treadmill("Treadmill", {x:200, y:300, width:400, height:20, view:"treadmill.swf"});
			add(treadmill);
 
			treadmill.enabled = false; //remove moving platform
 
			var teleporter:Teleporter = new Teleporter("Teleporter", {x:300, y:250, width:20, height:20, view:"teleporter.swf"});
			add(teleporter);
 
			teleporter.endX = 50;
			teleporter.endY = 0;
			teleporter.time = 200;
 
			teleporter.onBeginContact.add(_teleport);
 
			Platform.Make("Platform", -5, 150, 5, 300);
 
			var cannon:Cannon = new Cannon("Cannon", {x:450, y:170, width:40, height:40, view:"cannon.swf"});
			add(cannon);
 
			cannon.fireRate = 2500;
			cannon.startingDirection = "left";
			cannon.missileFuseDuration = 4000;
			cannon.missileView = "missile.swf";
			cannon.startFire();
 
			cannon.onGiveDamage.add(_cannonHurt);
		}
 
		private function _teleport(cEvt:ContactEvent):void {
 
			if (cEvt.other.GetBody().GetUserData() is Hero) {
				cEvt.fixture.GetBody().GetUserData().object = cEvt.other.GetBody().GetUserData();
				cEvt.fixture.GetBody().GetUserData().needToTeleport = true;
			}
		}
 
		private function _cannonHurt(contact:PhysicsObject):void {
 
			if (contact is Hero) {
				Hero(contact).hurt();
			}
		}
	}
}

I handle the teleported object in the state because if I only want to teleport my Hero (so it doesn’t affect Bad Guy) it is easier. The same for cannon hurting : it affects only my Hero, missile will just explode on a Bady Guy without killing it !

As usual you can download my zip.

Flas of my new objects are not included because I didn’t save it… But you can make it really fast using my flash panels for the Citrus Engine ;-) If you haven’t tested it yet, gives it a try !!