I'm working on a game using the now abandoned Famo.us Javascript framework.
I have the game in a running prototype and I'm building with Cordova and running it on an iPhone 6 using PhoneGap.
There seems to be some performance issues with it and I can't help but notice that a bit of lag happens right as a new enemy is generated onto the field.
There's a few random factors when generating a new enemy:
- position on the screen
- type of enemy
- direction of travel
I think the random factors are slowing the creation down because I'm not very good at high performing code, that said the following works:
//speed = velocity (randomly determined before hand - e.g. 300)
//timing = delay until next spawn (also random - e.g. 500)
function addEnemy(speed, timing){
// Famo.us (add node to scenegraph)
var newEnemy = gameEnemies.addChild();
newEnemy.name = "";
newEnemy.num = gameEnemies.iterator++;
// Possible sizes of enemy
var sizes = [30,40,50,60];
// choose a random size
var size = sizes[Math.floor(Math.random()*sizes.length)];
newEnemy.setSizeMode('absolute', 'absolute')
.setAbsoluteSize(size, size);
// possible sides of the screen
var sidesOps = [1,2,3,4];
// choose random side
var sideOp = sidesOps[Math.floor(Math.random()*sidesOps.length)];
// switch through possible sides and place in a random position along that axis and give enemy the name of the side it was placed on
switch (sideOp) {
case 1:
newEnemy.setPosition(gameSize[0],Math.round(Math.random() * gameSize[1]),2);
newEnemy.name = "right";
break;
case 2:
newEnemy.setPosition(-size,Math.round(Math.random() * gameSize[1]),2);
newEnemy.name = "left";
break;
case 3:
newEnemy.setPosition(Math.round(Math.random() * gameSize[0]),gameSize[1],2);
newEnemy.name = "bottom";
break;
case 4:
newEnemy.setPosition(Math.round(Math.random() * gameSize[0]),-size,2);
newEnemy.name = "top";
break;
}
// Famo.us adds DOMElement to the scenegraph, node and DOM tree
newEnemy.DOMElement = new DOMElement(newEnemy);
// 'types' of enemies
var colors = []
// score restraint (only add in new types past a score threshold)
if(game.score < 699){
colors = ['red','black','red','black','red','black','red','black'];
}else{
colors = ['red','black','blue','grey','green','yellow','orange','purple'];
}
// choose a random type but restrict it by chance (i.e. 1:2 or 1:100)
var color = colors[0];
var ran = Math.random();
var x = Math.floor(ran*100);
if (x > 47 && x < 94) {
color=colors[1];
}else if (x == 95){
color=colors[2];
}else if (x == 96) {
color=colors[3];
}else if (x == 97){
color=colors[4];
}else if (x == 98){
color=colors[5];
}else if (x == 99){
color=colors[6];
}else if (x == 100){
color=colors[7];
}
newEnemy.DOMElement.setProperty('background',color)
.setProperty('border-radius', "100%");
// Component used by Famo.us updater loop and engine
newEnemy.newEnemyComponent = {
id: null,
node: null,
// deletion function called when node should be terminated
done: function(node){
if(node in node._updater._updateQueue)FamousEngine._updateQueue.splice(node._updater._updateQueue.indexOf(node), 1);
if(node._updateQueue && node._updateQueue.length)
node._updateQueue = [];
if(node._nextUpdateQueue && node._nextUpdateQueue.length)
node._nextUpdateQueue = [];
game.world.remove(node.collision);
game.world.remove(node.sphere);
node.dismount();
},
// mounting function called when node is added to scenegraph
onMount: function (node){
this.id = node.addComponent(this);
this.node = node;
},
// updating function called when engine calls update callbacks
onUpdate: function(time){
// updating location of enemy
var spherePosition = this.node.sphere.getPosition();
// if enemy is beyond bounding walls of game, call it's done() function
if((spherePosition.x-65) > gameSize[0] || (spherePosition.x+65) < 0
|| (spherePosition.y-65) > gameSize[1] || (spherePosition.y+65) < 0){
if(this.node._id != null){
this.done(this.node);
}
// else update the position based on the location of the physics body and call the next update
}else{
this.node.setPosition(spherePosition.x,spherePosition.y);
this.node.requestUpdate(this.id);
}
}
};
newEnemy.addComponent(newEnemy.newEnemyComponent);
// add a physics body to the world and enemy node, position it accordingly
var newEnemyPosition = newEnemy.getPosition();
newEnemy.sphere = new Sphere({
mass: 1,
radius: size/2,
position:new Vec3(newEnemyPosition[0],newEnemyPosition[1])
});
newEnemy.sphere.node = newEnemy;
world.addBody(newEnemy.sphere);
// add collision between player and enemy to the physics engine
newEnemy.collision = world.addConstraint(
new Collision([game.boxNode.box,newEnemy.sphere],{restitution:0})
);
gameEnemies.constraintIterator++;
// randomly determine path of enemy (either straight forward or on a diagonal)
var diag = Math.random() < 0.5 ? true : false;
switch (newEnemy.name) {
case "left":
if(diag == true){
if(newEnemyPosition[1]> (gameSize[1]/2)){
newEnemy.sphere.setVelocity(speed,-speed);
}
else{
newEnemy.sphere.setVelocity(speed,speed);
}
}
else{
newEnemy.sphere.setVelocity(speed,0);
}
break;
case "right":
if(diag == true){
if(newEnemyPosition[1]> (gameSize[1]/2)){
newEnemy.sphere.setVelocity(-speed,-speed);
}
else{
newEnemy.sphere.setVelocity(-speed,speed);
}
}
else{
newEnemy.sphere.setVelocity(-speed,0);
}
break;
case "top":
if(diag == true){
if(newEnemyPosition[0]> (gameSize[0]/2)){
newEnemy.sphere.setVelocity(-speed,speed);
}
else{
newEnemy.sphere.setVelocity(speed,speed)
}
}
else{
newEnemy.sphere.setVelocity(0,speed);
}
break;
case "bottom":
if(diag == true){
if(newEnemyPosition[0]> (gameSize[0]/2)){
newEnemy.sphere.setVelocity(-speed,-speed);
}
else {
newEnemy.sphere.setVelocity(speed,-speed);
}
}
else{
newEnemy.sphere.setVelocity(0,-speed);
}
break;
}
// call the first update reqeust
newEnemy.requestUpdate(newEnemy.newEnemyComponent.id)
// set a timeout for the next enemy to be added (addEnemyUtil determines speed and timing factors and calls addEnemy)
FamousEngine.getClock().setTimeout(function(){
addEnemyUtil();
},timing);
}