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');
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.
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.
action
the action to bind; similar to ig.input.bind()
anchor
left, top, right or bottom positions for this buttonwidth
, height
width and height for the button and tile width and heightimage
the ig.Image
to use when drawing the button (optional)tile
the tile number to draw from this imageThe ig.TouchButtonCollection
takes an array of touch buttons and makes it a bit easier to handle.
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.
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; } }
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; }; }
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];
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.