Impact

Getting Started

To have your game running in Ejecta, you have to put all source files and assets into the App/ directory. You should end up with a layout like this:

App/index.js
App/lib/game/
App/lib/impact/
App/media/
etc

Ejecta doesn't understand HTML, so instead of the index.html file, we have to load Impact and the game from the App/index.js file. It should look like this:


// Load the game
ejecta.include('lib/impact/impact.js');
ejecta.include('lib/game/main.js');

Screen Size

You can use the Screen Canvas' .style property to stretch a smaller canvas over the whole screen (see .style in the Overview).

To always fill the screen completely, you should choose the screen size in such a way that the aspect ratio fits the current device. E.g. if you want a vertical resolution of 160px, the horizontal resolution should be derived from the vertical resolution and the available screen width to keeping pixels square.

var height = 160;
var scale = window.innerHeight / height;
var width = window.innerWidth / scale;

canvas.style.width = window.innerWidth;
canvas.style.height = window.innerHeight;

// Force nearest neighbour scaling; good for pixel style games
ig.System.scaleMode = ig.System.SCALE.CRISP;

ig.main('#canvas', MyGame, 60, width, height, 1);

Also, when running under Ejecta, always call ig.main() with a scale of 1. Don't let Impact do the scaling, or load times will be really long. You can check if you're running under Ejecta with if( window.ejecta ) {…}

Use ig.System.scaleMode to specify wether to use nearest-neighbour or bilinear filtering when scaling images.

Touch Buttons

For on-screen buttons, you can use the TouchButton Plugin – just put it in lib/plugins/touch-button.js and require the plugins.touch-button module in your main.js. This plugin will work in Mobile Browsers as well as in Ejecta.

See the Super Generic Jump'n'Run on your download page for a nice example on how to use this plugin, or try it in action here.

A touch button is anchored to either the left or right and top or bottom edge of the screen. This makes it easy to always have them sit in one corner of the game screen, no matter the screen size.

new ig.TouchButton( action, anchor, width, height, [image], [tile] );

The ig.TouchButtonCollection takes an array of touch buttons and makes it a bit easier to handle.

new ig.TouchButtonCollection( [buttons] )

Example:

MyGame = ig.Game.extend({
	
	buttons: null,
	buttonImage: new ig.Image( 'media/buttons.png' ),
	
	init: function() {
		// For Desktop Browsers
		ig.input.bind( ig.KEY.LEFT_ARROW, 'left' );
		ig.input.bind( ig.KEY.RIGHT_ARROW, 'right' );
		ig.input.bind( ig.KEY.C, 'shoot' );
		ig.input.bind( ig.KEY.X, 'jump' );
		
		// For Mobile Browsers and Ejecta
		if( ig.ua.mobile ) {
			this.buttons = new ig.TouchButtonCollection([
				new ig.TouchButton( 'left', {left: 0, bottom: 0}, 128, 128, this.buttonImage, 0 ),
				new ig.TouchButton( 'right', {left: 128, bottom: 0}, 128, 128, this.buttonImage, 1 ),
				new ig.TouchButton( 'shoot', {right: 128, bottom: 0}, 128, 128, this.buttonImage, 2 ),
				new ig.TouchButton( 'jump', {right: 0, bottom: 96}, 128, 128, this.buttonImage, 3 )
			]);
			
			// Align the touch buttons to the screen edges; you have 
			// to call this function once after creating the 
			// TouchButtonCollection and then every time you change 
			// the game's screen size
			this.buttons.align();
		}
	},
	
	draw: function() {
		this.parent();
		
		// Draw all touch buttons - if we have any
		if( this.buttons ) {
			this.buttons.draw(); 
		}
	}
});

The TouchButtonCollection has to be aligned to the current screen size using the .align() method. If you change the game's screen size, you have to align the buttons again.

In order to actually draw the TouchButtons in a TouchButtonCollection, call the .draw() method. Usually this should be done as the last step in your game's .draw() method, to paint the buttons on top of everything else.

Performance

Having a lot of draw calls can still slow the game down significantly. You can enable background pre-rendering for all background maps to reduce the number of draw call needed.

// In your game class; Overwrite load level do enable 
// pre-rendering on all background maps when a level 
// was loaded

loadLevel: function( data ) {
	this.parent( data );
	
	for( var i = 0; i < this.backgroundMaps.length; i++ ) {
		this.backgroundMaps[i].preRender = true;
	}
}

Smooth Scrolling

Because scaling in Ejecta works a bit differently than in a Browser, Impact's SMOOTH drawMode may not work as expected for pixel style games. If you use the AUTHENTIC or SUBPIXEL draw mode, you can ignore this section though.

In a Browser, Impact scales all Images up and uses a bigger Canvas to draw everything. In Ejecta however, Impact's scaling should remain at 1, while Ejecta itself scales the Canvas to fit the screen. This means that the AUTHENTIC and SMOOTH draw modes will behave the same in Ejecta, as Impact's internal scaling still remains at 1.

If, for example, your game is 240×160px Ejecta will scale it up 4x on the retina iPhone4, to match the 960x640 resolution. Image positions will be rounded to the initial 240×160 resolution – the smallest scroll step will be 1 internal pixel = 4 hardware pixels.

To have smoother scrolling in 1 hardware pixel steps, you can overwrite the systems .drawMode in your game's init() with the following:

if( window.ejecta ) {
	// Figure out the scaling ratio of internal to hardware pixels
	var hwpx = (window.innerWidth / ig.system.width) * ig.ua.pixelRatio;
	console.log('Hardware Pixel Scale: ', hwpx);

	ig.system.getDrawPos = function( p ) {
		// Snap draw positions to the closest hardware pixel
		return Math.round(p * hwpx) / hwpx;
	};
}

Sound and Music

Ejecta loads MP3, M4A and Apple's CAFF format; Ogg Vorbis is not supported at this time.

For short sound effects, Apple recommends using uncompressed PCM in a CAFF container while Music should be compressed with AAC. However, MP3 should work just fine as well.

If you want convert your sound files to CAFF, use the following commands in your terminal:

# sound effects should be in uncompressed PCM format:
afconvert -f caff -d LEI16@44100 -c 1 soundfile.wav

# music can be compressed with AAC
afconvert -f caff -d aac -c 1 music.wav

Ejecta will play all sound files < 512kb with OpenAL. Above 512kb Apple's AVAudioPlayer is used. You can change this limit in Classes/Ejecta/EJAudio/EJBindingAudio.h.

To make sure Impact searches for .caf files first instead of loading .mp3 or .m4a, include it in the list of sound formats to probe:

// Set this before you call ig.main()
ig.Sound.use = [ig.Sound.FORMAT.CAF, ig.Sound.FORMAT.OGG, ig.Sound.FORMAT.MP3];

Upgrading from iOSImpact

If you previously used iOSImpact to run your Impact game on iOS, there are some important changes you have to make to run it with Ejecta. Namely with controls, screen size and the plugins.ios modules.

Ejecta implements the Canvas API more closely than iOSImpact did. iOSImpact relied on a bunch of plugins that make Impact work; these plugins are no longer needed for Ejecta. Delete the lib/plugins/ios/ folder and remove the plugins.ios.ios module from your main.js.

Furthermore, the ig.input.bindTouchArea() method is no longer supported. See the Touch Buttons section above.

It's also very important that, when running in Ejecta, you call ig.main() with a scaling factor of 1. Ejecta automatically scales the whole Canvas elements, so there's no need for Impact's slow internal scaling.