Documentation

Citrus Engine API

The Wiki to get start quickly and understand how the engine works!
The complete API documentation of Citrus Engine. Download the zip.

Related API references

 

Gist snippets for Citrus

List of all snippets.

How to destroy objects

// Inside a class which extends CitrusObject e.g. CitrusSprite, APhysicsObjects...
kill = true;

// If you have an access to the variable of the object :
hero.kill = true;

// Or within a State class (the previous code works too) :
remove(hero);

How to change or destroy State

// In the Main class :

public function Main() {
			
	state = new TiledMapGameState();
	
	setTimeout(otherState, 4000);
}

private function otherState():void {
	state = new AnOtherState();
}

// When you create a new state the previous one is automatically destroyed and garbage collected.
// Obviously you can destroy a state without associate a new one thanks to the destroy method :
state.destroy();

// You can also restart or change a state from a State class :
private function _restartLevel(cEvt:b2Contact):void {
	
	CitrusEngine.getInstance().state = new FlipperState();
}

How to play sounds

// In the Main class, register the sounds :
sound.addSound("Hurt", {sound:"sounds/hurt.mp3"});
sound.addSound("Collect", {sound:"sounds/collect.mp3"});
sound.addSound("Song", {sound:"sounds/song.mp3",timesToPlay:-1});

// In your GameState play them when needed :
private function handleHeroTakeDamage():void {
	_ce.sound.playSound("Hurt");
}

private function handleJewelCollected(contact:b2Contact):void {
	
	if (Box2DPhysicsObject.CollisionGetOther(self, contact) != _hero)
		return;

	_ce.sound.playSound("Collect");
}

How to access game’s graphics

// Citrus object's view management is powerful: when a view is added to the object it is loaded and added to a template 
// called SpriteArt which is a container of your art. This template allows to load external files thanks to their path.
// It works the same way with Starling.

var _hero:Hero = new Hero("my hero", {view:"myHero.swf"});
// _hero.view refers to the path "myHero.swf" not to the swf loaded.
// If the view specified is a DisplayObject, you can directly make whatever you want on it (filter, blendMode, ect).

var _heroGraphic:SpriteArt = view.getArt(_hero) as SpriteArt;
// _heroGraphic is the container of the art. It will loads and display the art. Most often you will work with this.

trace((view.getArt(_hero) as SpriteArt).content); // it will trace null, because your swf isn't loaded at this time.

// If your art is an external file you have to be sure it is loaded:
_heroGraphic.loader.contentLoaderInfo.addEventListener(Event.COMPLETE, handleHeroLoaded);

private function handleHeroLoaded(e:Event):void {
   // the swf is loaded, you have direct access to it.
   _heroGraphic.content.alpha = .5;
}

/*Now technically, you could have set the hero graphic's alpha in the initialize() function, 
because the SpriteArt object is a wrapper for the actual MovieClip graphic that represents your loaded SWF. 
Since the SpriteArt object is created right away, this will also work in the initialize() function:*/
_heroGraphic = view.getArt(_hero) as SpriteArt;
_heroGraphic.alpha = .5;

// Using SWF you can also call some functions that you've defined. Note that it can't work with Starling!
private function handleHeroLoaded(e:Event):void {
   MovieClip(_heroGraphic.content).setShirtColor(0xff0000);
}

/*The function setShirtColor() is a function that you would create on the first frame of your Hero graphic's FLA. 
MovieClips will allow you to call dynamic functions such as setShirtColor().*/

How to handle mouse events

var coin:Coin = new Coin("coin", {view:"art.png", touchable:true});

//Within your state class. Same process for Starling or Away3D
var coinArt:DisplayObject = view.getArt(coin) as DisplayObject;

coinArt.addEventListener(MouseEvent.CLICK, handleCoinClick);

private function handleCoinClick(e:MouseEvent):void
{
   var clickedCoin:Coin = view.getObjectFromArt(e.currentTarget) as Coin;
   if (clickedCoin)
   {
      //do your logic here
      clickedCoin.kill = true;
   }
}

How to set up a camera

// In a GameState, the parameters are the object to follow, the offset, the bounds and the easing.
view.camera.setUp(hero, new Point(stage.stageWidth / 2, stage.stageHeight / 2), new Rectangle(0, 0, 1550, 450), new Point(.25, .05));

// You can add more interactivity :
stage.addEventListener(MouseEvent.MOUSE_WHEEL, _mouseWheel);
CitrusEngine.getInstance().input.keyboard.addKeyAction("rotate", Keyboard.X);

private function _mouseWheel(mEvt:MouseEvent):void {
	
	if (e.delta > 0)
		_camera.setZoom(_camera.getZoom() + 0.1);
	else if (e.delta < 0)
		_camera.setZoom(_camera.getZoom() - 0.1);
}

override public function update(timeDelta:Number):void {

	super.update(timeDelta);
	
	if (CitrusEngine.getInstance().input.justDid("rotate"))
		_camera.rotate(Math.PI / 2);
}

How to manage deeply z-sorting

// In your GameState class extending StarlingState (working for State and Away3DState too).

override public function initialize():void {
	super.initialize();
	
	// The group property specifies the depth sorting: objects placed in group 1 will be behind objects placed in group 2.
	// Here both objects have the same group, therefore the first added will be behind the second.
	var cs1:CitrusSprite = new CitrusSprite("cs1", {x:100, y:100, group:5, view:new Quad(100, 100, 0xFF0000)});
	add(cs1);
	
	var cs2:CitrusSprite = new CitrusSprite("cs2", {x:150, y:100, group:5, view:new Quad(100, 100, 0x00FF00)});
	add(cs2);
	
	// Each group is a container, each CitrusObject's view is added to the corresponding container.
	var container:Sprite = view.getArt(cs1).parent;

	// You can also change order into this container!
	container.swapChildren(view.getArt(cs1) as StarlingArt, view.getArt(cs2) as StarlingArt);

	// We make easily an affect to all children into the group5.
	container.filter = new BlurFilter();

	// group's container are automatically created when a new group is precised into an object.
	// In our case group from 0-5 are created, default object group is 0.
}

How to specify the view

// If you want to use Blitting view instead of the SpriteView override this method in your State class :
//Make sure and call this override to specify Blitting mode.

override protected function createView():CitrusView {
     return new BlittingView(this);
}

//Using Starling or Away3D you don't need to override the view since it uses StarlingState/Away3D which already specify StarlingView/Away3DView.
//Don't forget that your Main class should extend StarlingCitrusEngine or Away3DCitrusEngine.

How to add commands to the console

//In the Main class add this command. It will pause and unpause the game :
this.console.addCommand("pause", pauseGame);

private function pauseGame():void {
    this.playing = !this.playing;
}

//Using parameters :
this.console.addCommand("goto", goto);

private function goto(level:uint):void {
    trace("you should load level " + level);
}

How to make a preloader

// Arts specified by a path to a file like "levels/coin.png" are loaded under the hood by the Citrus Engine.
//You can follow the progression in your GameState :

//add a mask to the game to hide objects loading in the background
_maskDuringLoading = new Quad(stage.stageWidth, stage.stageHeight);
addChild(_maskDuringLoading);

// create a textfield to show the loading %
_percentTF = new TextField(400, 200, "", "ArialMT");
addChild(_percentTF);

// when the loading is completed...
view.loadManager.onLoadComplete.addOnce(_handleLoadComplete);

protected function _handleLoadComplete():void {
	
	removeChild(_percentTF, true);
	removeChild(_maskDuringLoading, true);
}

override public function update(timeDelta:Number):void {
	
	super.update(timeDelta);

	var percent:uint = view.loadManager.bytesLoaded / view.loadManager.bytesTotal * 100;
	if (percent < 99)
		_percentTF.text = percent.toString() + "%";
}

// Note: if your views are never set up as a path to a file, the handleLoadComplete will never be called.
// If you're using Starling, we advice you to use its AssetManager to parse sprite sheets.

How to use the Level Manager

//In your Main class :
public function Main() {
	
	levelManager = new LevelManager(ALevel);
	levelManager.applicationDomain = ApplicationDomain.currentDomain; // to be able to load your SWF level on iOS
	levelManager.onLevelChanged.add(_onLevelChanged);
	levelManager.levels = [[Level1, "levels/A1/LevelA1.swf"], [Level2, "levels/A2/LevelA2.swf"]];
	levelManager.gotoLevel(); //load the first level, you can change the index. You can also call it later.
}

private function _onLevelChanged(lvl:ALevel):void {

	state = lvl;

	lvl.lvlEnded.add(_nextLevel);
	lvl.restartLevel.add(_restartLevel);
}

private function _nextLevel():void {

	levelManager.nextLevel();
}

private function _restartLevel():void {

	state = levelManager.currentLevel as IState;
}

// Note that you must create an abstract class : Level1 and Level2 extends ALevel 
// which extends the State or StarlingState class. From our example :
public class ALevel extends State {

	public var lvlEnded:Signal;
	public var restartLevel:Signal;
	protected var _level:MovieClip;

	public function ALevel(level:MovieClip = null) {
		super();
		
		_level = level;
		
		lvlEnded = new Signal();
		restartLevel = new Signal();
		
		// Useful for not forgetting to import object from the Level Editor
		var objectsUsed:Array = [Hero, Platform, Enemy, Sensor, CitrusSprite];
	}

	override public function initialize():void {
		super.initialize();
		
		var box2d:Box2D = new Box2D("Box2D");
		add(box2d);
				
		// create objects from our level made with Flash Pro
		ObjectMaker2D.FromMovieClip(_level);
	}
}

How to use the AGameData class

// The AGameData class is an abstract (it should be extend) and dynamic class (you won't have problem
// to access its chidren properties, be careful your IDE may not indicate children properties).

public class MyGameData extends AGameData {

	public function MyGameData() {
		super();
		
		_levels = [[Level1, "levels/A1/LevelA1.swf"], [Level2, "levels/A2/LevelA2.swf"]];
	}
	
	public function get levels():Array {
		return _levels;
	}
}

// In your Main class :
gameData = new MyGameData();

// Combine it with the Level Manager (if used) :
levelManager.levels = gameData.levels;

// In your GameState you can access its properties this way :
var ce:CitrusEngine = CitrusEngine.getInstance();
trace(ce.gameData.levels);
ce.gameData.lives = 4;

// When the data changes, it dispatches a Signal. Be sure to register it before any gameData's properties update.
_ce.gameData.dataChanged.add(onDataChanged);

private function onDataChanged(data:String, value:Object):void {
	if (data == "lives" && value == 0)
		trace("game over");
}

How to grab and drag physics objects

// In a state class :

//Here we use Box2D. /!\ Don't forget that your _hero must have its touchable property set to true!
var draggableHeroArt:DisplayObject = view.getArt(_hero) as DisplayObject;
draggableHeroArt.addEventListener(MouseEvent.MOUSE_DOWN, _handleGrab);

stage.addEventListener(MouseEvent.MOUSE_UP, _handleRelease);

private function _handleGrab(mEvt:MouseEvent):void {

	var clickedObject:CitrusObject = view.getObjectFromArt(mEvt.currentTarget) as CitrusObject;

	if (clickedObject)
		_hero.enableHolding(mEvt.currentTarget.parent);
}

private function _handleRelease(mEvt:MouseEvent):void {
	_hero.disableHolding();
}

// The DraggableHero class :

package{

	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.Joints.b2MouseJoint;
	import Box2D.Dynamics.Joints.b2MouseJointDef;

	import com.citrusengine.objects.platformer.box2d.Hero;

	import flash.display.DisplayObject;

	public class DraggableHero extends Hero{

		private var _jointDef:b2MouseJointDef;
		private var _joint:b2MouseJoint;
		private var _mouseScope:DisplayObject;

		public function DraggableHero(name:String, params:Object = null) {
			
			touchable = true;
			
			super(name, params);
		}

		override public function destroy():void {
			
			if (_joint)
				_box2D.world.DestroyJoint(_joint);
				
			super.destroy();
		}

		override public function update(timeDelta:Number):void {
			
			super.update(timeDelta);

			if (_joint) {
				_joint.SetTarget(new b2Vec2(_mouseScope.mouseX / _box2D.scale, _mouseScope.mouseY / _box2D.scale));
			}
		}

		public function enableHolding(mouseScope:DisplayObject):void {
			
			if (_joint)
				return;

			_mouseScope = mouseScope;
			_jointDef.target = new b2Vec2(_mouseScope.mouseX / _box2D.scale, _mouseScope.mouseY / _box2D.scale);
			_joint = _box2D.world.CreateJoint(_jointDef) as b2MouseJoint;
		}

		public function disableHolding():void {
			
			if (!_joint)
				return;

			_box2D.world.DestroyJoint(_joint);
			_joint = null;
		}

		override protected function defineFixture():void {
			
			super.defineFixture();
			
			_fixtureDef.density = 0.1;
		}

		override protected function defineJoint():void {
			
			super.defineJoint();

			_jointDef = new b2MouseJointDef();
			_jointDef.bodyA = _box2D.world.GetGroundBody();
			_jointDef.bodyB = _body;
			_jointDef.dampingRatio = .2;
			_jointDef.frequencyHz = 100;
			_jointDef.maxForce = 100;
		}
	}
}