# Adding an 'enemy' to a game (with randomized properties)

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)
// Famo.us (add node to scenegraph)
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)
// 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.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);
}
}
};
// add a physics body to the world and enemy node, position it accordingly
var newEnemyPosition = newEnemy.getPosition();
newEnemy.sphere = new Sphere({
mass: 1,
position:new Vec3(newEnemyPosition[0],newEnemyPosition[1])
});
newEnemy.sphere.node = newEnemy;
// add collision between player and enemy to the physics engine
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(){
},timing);
}


I don't think that it is the Math.random() that is causing problems. This jsfiddle calls Math.random() 1 million times in about 60-80 ms (chrome on iPhone 5s). I believe the issue is that you are doing a lot of calculations for each new enemy. There are a couple of ways I can think to avoid this heavy calculation.
• I found another bug that was causing the stuttering, this calculation of random properties isn't actually so bad on my performance there was another looping call that was causing issues, anyway, do you think these type of if-else and switch algorithms are ok? Feb 17 '16 at 2:40
• Yeah, if-else if statements shouldn't be too bad. You are doing a lot of computations though (about 160 lines of it). Because of that the only way to reduce time is to put computation at loading phase. Maybe you could precompute 100 enemies and randomly choose from those, after a certain amount of time you would get repeats but that might be the best way. If you really want to pin down what is taking the longest you could add timing statement throughout your code to find what bits are taking the longest. My guess is that it will be fairly uniform though. Feb 17 '16 at 18:00