collision detection on game - javascript

I am trying to make a simple Javascript and Jquery game in which a user controls a blue ball and has to avoid red balls. I attempted to make a collision detection system that would get rid of the red balls when one was touched. This works however it occasionally detects a collision when there isn't one and I cannot figure out why. I made a jsfiddle of the game. By the way, I commented out the function which makes the red balls move so that it would be easier to test the collisions. Thanks
https://jsfiddle.net/clayjames/bxcffwty/4/
Here is the javascript for the game so far:
var keyDown = function() {
$(document).keydown(function(e) {
switch(e.which) {
case 37 :
$("#blue").css("marginLeft","-=40");
blue.xcordinate = blue.xcordinate - 40;
check();
break;
case 38 :
$("#blue").css("marginTop","-=40");
blue.ycordinate = blue.ycordinate - 40;
check();
break;
case 39 :
$("#blue").css("marginLeft","+=40");
blue.xcordinate = blue.xcordinate + 40;
check();
break;
case 40 :
$("#blue").css("marginTop","+=40");
blue.ycordinate = blue.ycordinate + 40;
check();
break;
};
});
};
var redX = [0];
var redY = [0];
var loop = 0;
var createRed = function() {
var randTop = Math.floor(1+Math.random()*600);
var randLeft = Math.floor(1+Math.random()*1308);
$("body").append($("<div></div>").addClass("red").css("marginTop",randTop).css("marginLeft",randLeft));
redX[loop] = randLeft;
redY[loop] = randTop;
loop++;
}
var redMovement = function(){
var redDirection = Math.floor(1 + Math.random() * 4);
switch(redDirection) {
case 1 :
$(".red").animate({"marginLeft": "-=20"});
break;
case 2 :
$(".red").animate({"marginLeft": "+=20"});
break;
case 3 :
$(".red").animate({"marginTop": "-=20"});
break;
case 4 :
$(".red").animate({"marginTop": "+=20"});
};
};
var blue = {
xcordinate: 630,
ycordinate: 320
}
var xMatch = false;
var yMatch = false;
var check = function() {
for(x = 0; x < redX.length; x++){
if(((redX[x] - 25) <= blue.xcordinate + 25 && blue.xcordinate + 25 <= (redX[x] + 25)) || ((redX[x] - 25) <= blue.xcordinate - 25 && blue.xcordinate - 25 <= (redX[x] + 25))) {
xMatch = true;
};
};
for(y = 0; y < redY.length; y++){
if(((redY[y] - 25) <= blue.ycordinate + 25 && blue.ycordinate + 25 <= (redY[y] + 25)) || ((redY[y] - 25) <= blue.ycordinate - 25 && blue.ycordinate - 25 <= (redY[y] + 25))) {
yMatch = true;
};
};
if (xMatch === true && yMatch === true) {
$(".red").remove();
};
};
$(document).ready(function() {
$("#blue").hide()
$(".red").remove()
$("#border").hide()
setInterval(function(){
$("h5").fadeOut(500).fadeIn(500)
},1000);
$("body").keydown(function(e){
if(e.which == 13) {
var clayRocks = setInterval(createRed,10000);
$("h1, h5").remove();
$("#blue, #border").show();
};
});
keyDown();
//setInterval(redMovement,500);
});

It is probably easier to just use absolute value, like this:
for (x = 0; x < redX.length; x++) {
if (Math.abs(redX[x] - blue.xcordinate) <= 25) {
xMatch = true;
};
};
Demo

testing the collision between 2 balls is easy (compared to others shapes), you have to calculates the distance of the 2 balls (using pythagore théorem) and see if this distance is less than the sum of the radius of the 2 balls.

I don't know if that's the only issue, but your check function is flawed. What it currently does is check if there is a collision on the x-coordinate with any red ball and a collision on the y-coordinate with any (other!) red ball. It does not enforce that both collisions occur on the same red ball.
By the way, this is a rather strange and inaccurate of way of detecting collisions between spherical balls: the natural one is to check if the euclidean distance between the centers is less than the sum of the radiuses, i.e
function collides(blueX, blueY, blueR, redX, redY, redR) {
return Math.sqrt(Math.pow(blueX - redX, 2) + Math.pow(blueY - redY, 2)) < blueR + redR;
}

Related

My code gets into an infinite loop when setting up in p5

I am having real trouble finding where my loop is. I run the code and it hangs. I am trying to make a circuits game where there are circuits for the user to connect. But I am getting stuck at square one even setting up the map to make sure it is solvable. There is an infinite loop but I can't find it I looked and looked... here is the code.`
//maxLength of circut board is the board
let theHighestMaxLength = 10;
let board;
let gridX = 10;
let gridY = 10;
let perX = 10;
let perY = 10;
//s is here so we don't have to pass it everywhere the square we are looking at in functions
let s=null;
//length and usedsquares and begin are here to be used across functions
let length=0;
let usedSquares=0;
let begin=null;
class Asquare {
constructor(x, y) {
this.x = x;
this.y = y;
this.isCircut = false;
this.isWire = false;
this.OtherCircut = null;
this.Left = null;
this.Right = null;
this.Top = null;
this.Bottom = null;
this.otherCircut = null;
this.isBlank = false;
}
drawSelf() {
if (this.isCircut) {
rectMode(CENTER)
var xx = max(this.otherCircut.x, this.x);
var yy = max(this.otherCircut.y, this.y);
fill(color(xx * 25, yy * 25, 0));
square((this.x + 0.5) * perX, (this.y + 0.5) * perY, perX);
}
else if (this.isWire) {
fill(color(this.otherCircut.x * 20, this.otherCircut.y * 20, 0));
circle((this.x + 0.5) * perX, (this.y + 0.5) * perY, perX);
}
else if (this.isBlank) {
fill(color(0, 204, 0))
circle((this.x + 0.5) * perX, (this.y + 0.5) * perY, perX);
}
}
}
function handleWireMove(){
s.isWire = true;
//remember the starting circut
s.otherCircut = begin;
informAll(s);
//the length is used
length++;
}
function informAll() {
//tell all the other squares of s
if (s.x - 1 >= 0) {
board[s.x - 1][s.y].Right = s;
}
if (s.x + 1 < gridX) {
board[s.x + 1][s.y].Left = s;
}
if (s.y - 1 >= 0) {
board[s.x][s.y - 1].Bottom = s;
}
if (s.y + 1 < gridY) {
board[s.x][s.y + 1].Top = s;
}
//the used squares is now higher
usedSquares++;
}
function setup() {
createCanvas(gridX * perX, gridY * perY);
noLoop();
//fill the board with squares
board = new Array(gridX).fill(0).map(() => new Array(gridY).fill(0));
for (var x = 0; x < gridX; x++) {
for (var y = 0; y < gridY; y++) {
board[x][y] = new Asquare(x, y);
}
}
//the number of squares in the grid used
usedSquares = 0;
//till the board is full
while (usedSquares < gridX * gridY) {
//get a random x y
var rx = floor(random(gridX));
var ry = floor(random(gridY));
//create an s and begin var s for every nw square and begin for the first
s = board[rx][ry];
//if this square is already in use
if(s.isBlank||s.isCircut||s.isWire){continue;}
//begin at this square
begin = s;
//if there is some way to go
if (s.Left == null || s.Right == null || s.Top == null || s.Bottom == null) {
// get a random length to try and reach
var maxLength = floor(random(1, theHighestMaxLength))
//the starting length
length = 0;
begin.isCircut = true;
//inform all the surounding squares and increase the number of used
informAll();
//while the length isn't full
while (length <= maxLength) {
//add an option count for left right top botoom
var numOption = s.Left == null ? 1 : 0;
numOption = s.Right == null ? numOption + 1 : numOption;
numOption = s.Top == null ? numOption + 1 : numOption;
numOption = s.Bottom == null ? numOption + 1 : numOption;
//if there are no toptions to move we must stop here ot if the maxLength is reached
if (numOption == 0 || length == maxLength) {
//this is a circut the beigin circut is begin the begin other circut is this final one
s.isCircut = true;
s.isWire = false;
s.otherCicut = begin;
begin.otherCircut = s;
//make sure the loop ends
length=9999;
break;
}
//pick a random direction number
var randomChoiseNumber = floor(random(numOption));
//if left is an option
if (s.Left == null) {
//if r is already 0 that means we picked left
if (randomChoiseNumber == 0) {
//while left is an option and the maxlength isn't hit and left isn't off the map
while (s.Left == null && length < maxLength && s.x - 1 >= 0) {
//set s to the left
s = board[s.x - 1][s.y];
//handleWireMove the move
handleWireMove()
}
//continue we used the direction
continue;
} else {
//we passed an option reduce the number
randomChoiseNumber--;
}
}
//if right is an option
if (s.Right == null) {
//if this is the zero choice
if (randomChoiseNumber == 0) {
//if right is not off the map and an option while the length is not hit
while (s.Right == null && length < maxLength && s.x + 1 < gridX) {
//set s to right
s = board[s.x + 1][s.y];
//handleWireMove the move
handleWireMove();
}
continue;
} else {
//reduce the number as we passed an option
randomChoiseNumber--;
}
}
//if top is an option
if (s.Top == null) {
//if this is the zero option
if (randomChoiseNumber == 0) {
//while top is a choise and the length is not reached and top is not off the map
while (s.Top == null && length < maxLength && s.y - 1 >= 0) {
//move to the top
s = board[s.x][s.y - 1];
//handleWireMove the move
handleWireMove();
}
//continue the direction is used up
continue;
} else {
//we passed a number reduce the choise number
randomChoiseNumber--;
}
}
//if bottom is an option
if (s.Bottom == null) {
//if this is the zero option
if (randomChoiseNumber == 0) {
//while bottom is a choise and the length is not reached and it is not off the map
while (s.Bottom == null && length < maxLength && s.y + 1 < gridY) {
//go to the bottom
s = board[s.x][s.y + 1];
//handleWireMove the move
handleWireMove();
}
}
}
}
}
else {
//if there was no way to go the square is blank tell the others and increace usedSquares
s.isBlank = true;
informAll();
}
}
}
function drawAll() {
board.forEach(a => a.forEach(b => b.drawSelf()));
}
function draw() {
background(gridX * perX);
drawAll();
}
The problem is in the setup function but I can't find it. Please help
The problem is that you compute the number of available numOption only based on whether the randomly selected grid square has unconnected slots (Left, Top, Right, or Bottom) without considering whether the value of s.x and s.y make it possible to select one of those directions). As a result your inner loop repeats infinitely because it never calls handleWireMove() and thus never increases length.
The code below is modified with some extra logging and it becomes obvious where the infinite loop is (it eventually prints 'we failed to make a choice. that is not good' forever):
//the number of squares in the grid used
usedSquares = 0;
//till the board is full
while (usedSquares < gridX * gridY) {
console.log(usedSquares);
//get a random x y
var rx = floor(random(gridX));
var ry = floor(random(gridY));
//create an s and begin var s for every nw square and begin for the first
s = board[rx][ry];
//if this square is already in use
if (s.isBlank || s.isCircut || s.isWire) {
continue;
}
//begin at this square
begin = s;
//if there is some way to go
if (
s.Left == null ||
s.Right == null ||
s.Top == null ||
s.Bottom == null
) {
// get a random length to try and reach
var maxLength = floor(random(1, theHighestMaxLength));
//the starting length
length = 0;
begin.isCircut = true;
//inform all the surounding squares and increase the number of used
informAll();
//while the length isn't full
console.log('beggining inner loop');
while (length <= maxLength) {
//add an option count for left right top botoom
var numOption = s.Left == null ? 1 : 0;
numOption = s.Right == null ? numOption + 1 : numOption;
numOption = s.Top == null ? numOption + 1 : numOption;
numOption = s.Bottom == null ? numOption + 1 : numOption;
//if there are no toptions to move we must stop here ot if the maxLength is reached
if (numOption == 0 || length == maxLength) {
//this is a circut the beigin circut is begin the begin other circut is this final one
s.isCircut = true;
s.isWire = false;
s.otherCicut = begin;
begin.otherCircut = s;
//make sure the loop ends
console.log('no options, abort');
length = 9999;
break;
}
//pick a random direction number
var randomChoiseNumber = floor(random(numOption));
//if left is an option
if (s.Left == null) {
//if r is already 0 that means we picked left
if (randomChoiseNumber == 0) {
//while left is an option and the maxlength isn't hit and left isn't off the map
while (s.Left == null && length < maxLength && s.x - 1 >= 0) {
//set s to the left
s = board[s.x - 1][s.y];
//handleWireMove the move
handleWireMove();
}
//continue we used the direction
continue;
} else {
//we passed an option reduce the number
randomChoiseNumber--;
}
}
//if right is an option
if (s.Right == null) {
//if this is the zero choice
if (randomChoiseNumber == 0) {
//if right is not off the map and an option while the length is not hit
while (s.Right == null && length < maxLength && s.x + 1 < gridX) {
//set s to right
s = board[s.x + 1][s.y];
//handleWireMove the move
handleWireMove();
}
continue;
} else {
//reduce the number as we passed an option
randomChoiseNumber--;
}
}
//if top is an option
if (s.Top == null) {
//if this is the zero option
if (randomChoiseNumber == 0) {
//while top is a choise and the length is not reached and top is not off the map
while (s.Top == null && length < maxLength && s.y - 1 >= 0) {
//move to the top
s = board[s.x][s.y - 1];
//handleWireMove the move
handleWireMove();
}
//continue the direction is used up
continue;
} else {
//we passed a number reduce the choise number
randomChoiseNumber--;
}
}
//if bottom is an option
if (s.Bottom == null) {
//if this is the zero option
if (randomChoiseNumber == 0) {
//while bottom is a choise and the length is not reached and it is not off the map
while (s.Bottom == null && length < maxLength && s.y + 1 < gridY) {
//go to the bottom
s = board[s.x][s.y + 1];
//handleWireMove the move
handleWireMove();
}
}
}
console.log('we failed to make a choice. that is not good');
}
console.log('exited inner loop');
} else {
//if there was no way to go the square is blank tell the others and increace usedSquares
s.isBlank = true;
informAll();
}
}
Here is a working version that correctly checks not only if there is an opening to the Left/Top/Right/Bottom, but also if that direction is possible (not off the edge of the grid):
//maxLength of circut board is the board
let theHighestMaxLength = 10;
let board;
let gridX = 10;
let gridY = 10;
let perX = 10;
let perY = 10;
//s is here so we don't have to pass it everywhere the square we are looking at in functions
let s = null;
//length and usedsquares and begin are here to be used across functions
let length = 0;
let usedSquares = 0;
let begin = null;
class Asquare {
constructor(x, y) {
this.x = x;
this.y = y;
this.isCircut = false;
this.isWire = false;
this.OtherCircut = null;
this.Left = null;
this.Right = null;
this.Top = null;
this.Bottom = null;
this.otherCircut = null;
this.isBlank = false;
}
drawSelf() {
if (this.isCircut) {
rectMode(CENTER);
var xx = max(this.otherCircut.x, this.x);
var yy = max(this.otherCircut.y, this.y);
fill(color(xx * 25, yy * 25, 0));
square((this.x + 0.5) * perX, (this.y + 0.5) * perY, perX);
} else if (this.isWire) {
fill(color(this.otherCircut.x * 20, this.otherCircut.y * 20, 0));
circle((this.x + 0.5) * perX, (this.y + 0.5) * perY, perX);
} else if (this.isBlank) {
fill(color(0, 204, 0));
circle((this.x + 0.5) * perX, (this.y + 0.5) * perY, perX);
}
}
}
function handleWireMove() {
s.isWire = true;
//remember the starting circut
s.otherCircut = begin;
informAll(s);
//the length is used
length++;
}
function informAll() {
//tell all the other squares of s
if (s.x - 1 >= 0) {
board[s.x - 1][s.y].Right = s;
}
if (s.x + 1 < gridX) {
board[s.x + 1][s.y].Left = s;
}
if (s.y - 1 >= 0) {
board[s.x][s.y - 1].Bottom = s;
}
if (s.y + 1 < gridY) {
board[s.x][s.y + 1].Top = s;
}
//the used squares is now higher
usedSquares++;
}
function setup() {
createCanvas(gridX * perX, gridY * perY);
noLoop();
//fill the board with squares
board = new Array(gridX).fill(0).map(() => new Array(gridY).fill(0));
for (var x = 0; x < gridX; x++) {
for (var y = 0; y < gridY; y++) {
board[x][y] = new Asquare(x, y);
}
}
//the number of squares in the grid used
usedSquares = 0;
//till the board is full
while (usedSquares < gridX * gridY) {
console.log(usedSquares);
//get a random x y
var rx = floor(random(gridX));
var ry = floor(random(gridY));
//create an s and begin var s for every nw square and begin for the first
s = board[rx][ry];
//if this square is already in use
if (s.isBlank || s.isCircut || s.isWire) {
continue;
}
//begin at this square
begin = s;
//if there is some way to go
if (
s.Left == null ||
s.Right == null ||
s.Top == null ||
s.Bottom == null
) {
// get a random length to try and reach
var maxLength = floor(random(1, theHighestMaxLength));
//the starting length
length = 0;
begin.isCircut = true;
//inform all the surounding squares and increase the number of used
informAll();
//while the length isn't full
console.log('beggining inner loop');
while (length <= maxLength) {
//add an option count for left right top botoom
var numOption = s.Left == null && s.x - 1 >= 0 ? 1 : 0;
numOption = s.Right == null && s.x + 1 < gridX ? numOption + 1 : numOption;
numOption = s.Top == null && s.y - 1 >= 0 ? numOption + 1 : numOption;
numOption = s.Bottom == null && s.y + 1 < gridY ? numOption + 1 : numOption;
//if there are no toptions to move we must stop here ot if the maxLength is reached
if (numOption == 0 || length == maxLength) {
//this is a circut the beigin circut is begin the begin other circut is this final one
s.isCircut = true;
s.isWire = false;
s.otherCicut = begin;
begin.otherCircut = s;
//make sure the loop ends
console.log('no options, abort');
length = 9999;
break;
}
//pick a random direction number
var randomChoiseNumber = floor(random(numOption));
//if left is an option
if (s.Left == null && s.x - 1 >= 0) {
//if r is already 0 that means we picked left
if (randomChoiseNumber == 0) {
//while left is an option and the maxlength isn't hit and left isn't off the map
while (s.Left == null && length < maxLength && s.x - 1 >= 0) {
//set s to the left
s = board[s.x - 1][s.y];
//handleWireMove the move
handleWireMove();
}
//continue we used the direction
continue;
} else {
//we passed an option reduce the number
randomChoiseNumber--;
}
}
//if right is an option
if (s.Right == null && s.x + 1 < gridX) {
//if this is the zero choice
if (randomChoiseNumber == 0) {
//if right is not off the map and an option while the length is not hit
while (s.Right == null && length < maxLength && s.x + 1 < gridX) {
//set s to right
s = board[s.x + 1][s.y];
//handleWireMove the move
handleWireMove();
}
continue;
} else {
//reduce the number as we passed an option
randomChoiseNumber--;
}
}
//if top is an option
if (s.Top == null && s.y - 1 >= 0) {
//if this is the zero option
if (randomChoiseNumber == 0) {
//while top is a choise and the length is not reached and top is not off the map
while (s.Top == null && length < maxLength && s.y - 1 >= 0) {
//move to the top
s = board[s.x][s.y - 1];
//handleWireMove the move
handleWireMove();
}
//continue the direction is used up
continue;
} else {
//we passed a number reduce the choise number
randomChoiseNumber--;
}
}
//if bottom is an option
if (s.Bottom == null && s.y + 1 < gridY) {
//if this is the zero option
if (randomChoiseNumber == 0) {
//while bottom is a choise and the length is not reached and it is not off the map
while (s.Bottom == null && length < maxLength && s.y + 1 < gridY) {
//go to the bottom
s = board[s.x][s.y + 1];
//handleWireMove the move
handleWireMove();
}
}
}
console.log('we failed to make a choice. that is not good');
}
console.log('exited inner loop');
} else {
//if there was no way to go the square is blank tell the others and increace usedSquares
s.isBlank = true;
informAll();
}
}
}
function drawAll() {
board.forEach((a) => a.forEach((b) => b.drawSelf()));
}
function draw() {
background(gridX * perX);
drawAll();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
One closing though: this bug is a symptom of this code being horribly complicated and violating fundamental principles of good design such as not having shared global state that is updated as a side effect of function calls.

Javascript canvas, collision detection via array not working

Basically, I have a program that makes a square, and stores the left, right, top and bottoms in an array. When it makes a new square, it cycles through the array. If the AABB collision detection makes the new square overlap with another square, it should make sure that the square is not displayed, and tries again. Here is a snippet of the code I made, where I think the problem is:
var xTopsBotsYTopsBotsSquares = [];
//Makes a randint() function.
function randint(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function checkForOccupiedAlready(left, top, right, bottom) {
if (xTopsBotsYTopsBotsSquares.length == 0) {
return true;
}
for (i in xTopsBotsYTopsBotsSquares) {
if (i[0] <= right || i[1] <= bottom ||
i[2] >= left || i[3] >= top) {/*Do nothing*/}
else {
return false;
}
}
return true;
}
//Makes a new square
function makeNewsquare() {
var checkingIfRepeatCords = true;
//DO loop that checks if there is a repeat.
do {
//Makes the square x/y positions
var squareRandomXPos = randint(50, canvas.width - 50);
var squareRandomYPos = randint(50, canvas.height - 50);
//Tests if that area is already occupied
if (checkForOccupiedAlready(squareRandomXPos,
squareRandomYPos,
squareRandomXPos+50,
squareRandomYPos+50) == true) {
xTopsBotsYTopsBotsSquares.push([squareRandomXPos,
squareRandomYPos,
squareRandomXPos+50,
squareRandomYPos+50]);
checkingIfRepeatCords = false;
}
}
while (checkingIfRepeatCords == true);
}
Any help is much appreciated.
Your loop is incorrect I think, since you use i as a value, whereas it is a key:
for (i in xTopsBotsYTopsBotsSquares) {
if (i[0] <= right || i[1] <= bottom ||
i[2] >= left || i[3] >= top) {/*Do nothing*/}
else {
return false;
}
}
Could become:
for (var i = 0, l < xTopsBotsYTopsBotsSquares.length; i < l; i++) {
var data = xTopsBotsYTopsBotsSquares[i];
if (data[0] <= right || data[1] <= bottom ||
data[2] >= left || data[3] >= top) {/*Do nothing*/}
else {
return false;
}
}

Javascript Else If switch using HTML input. Checking a value and displaying a result accordingly. Cost Estimator

I am making a simple cost estimator. I want to make an else if switch that checks the imputed values and displays a certain string dependant on those values. The cost estimation is working but I cant get the Else If switch to function.
Here is my codepen: http://codepen.io/FredHair/pen/FgJAd
I have tried to have a go here:
function myContainer(){
var container;
var x = Number(document.getElementById("x").value);
var y = Number(document.getElementById("y").value);
var z = Number(document.getElementById("z").value);
if (z < 25 && x < 110 && y < 90 ){
container = "1";
}
else if (z < 25 && x < 110 && y < 180 ){
container = "2";
}
else if (z < 25 && x < 220 && y < 90 ){
container = "3";
}
else if (z < 50 && x < 110 && y < 90 ){
container = "4";
}
else if (z < 50 && x < 110 && y < 180 ){
container = "5";
}
else if (z < 50 && x < 220 && y < 90 ){
container = "6";
}
else if (z < 75 && x < 110 && y < 90 ){
container = "7";
}
else if (z < 75 && x < 110 && y < 180 ){
container = "8";
}
else if (z < 75 && x < 220 && y < 90 ){
container = "9";
}
else if (z < 100 && x < 110 && y < 90 ){
container = "10";
}
else if (z < 100 && x < 110 && y < 180 ){
container = "11";
}
else if (z < 100 && x < 220 && y < 90 ){
container = "12";
}
else{
container = "?"
}
document.getElementById("container").innerHTML = container;
}
This is obviously meant to display a value in the HTML h3 id= "container", but currently nothing happens.
Where am I going wrong? is there a much better way to set up this? Using JQuery perhaps? Any help will be greatly appreciated. Sorry for my basic coding.
Your myContainer function is never being called. If you were to add it to your other function after you display the results, you'd get the results you wanted. Such as:
...
//Display Result//
document.getElementById("result").innerHTML = " = £ " + result;
myContainer();
}
That said you could simplify both functions. For example, the switch statement doesn't really serve much of a purpose, since you can modify your markup to produce the results that you want by changing your values to 50 and 30 such as:
<select id="choice" >
<option value = "1">1</option>
<option value = "50">2</option>
<option value = "30">3</option>
</select>
Now in your calculator function, you can set the result with a ternary operator. You read it like an if statement, meaning (if the value is true) ? (do this) : (else do this).
function calculator(){
var x = Number(document.getElementById("x").value);
var y = Number(document.getElementById("y").value);
var z = Number(document.getElementById("z").value);
var choice = Number(document.getElementById("choice").value);
var result = (choice === 1) ? z * 3 : x * 3 + choice;
//Display Result//
document.getElementById("result").innerHTML = " = £ " + result;
myContainer(x,y,z); //pass the values of x, y, z as arguments
}
You could simplify your myContainer as well, by passing the values to it for x, y and z directly from the calculator function, then you don't need to go back to the DOM and retrieve those values again.
function myContainer(x, y, z){
var container;
//all your if-else here ..
document.getElementById("container").innerHTML = container;
}

Creating 2d platforms using JavaScript

I'm developing a HTML5 Canvas game using EaselJS and I've written a function that allows me to create "blocks" just by setting one or more images, size and position.
and by "blocks", what I mean is:
I'm doing this using two methods:
First method:
With this method the blocks are created in the available space inside the location I've set, using the images randomly.
Second method:
The blocks are created inside the location I've set using specific images for the top left corner, top side, top right corner, left side, center, right side, bottom left corner, bottom side and bottom right corner, and there can be more than a single image for each one of those parts (so the system uses a random one to avoid repeating the same image multiple times).
Ok, but what's the problem?
This function uses a zillion 77 lines (131 lines counting with the collision-detection-related part)! I know there's a better way of doing this, that will take about a half or less lines than it's taking now, but I don't know how to do it and when someone show me, I'll use the "right way" for the rest of my life. Can you help me?
What I want:
A possible way to use less lines is to use a single "method" that allows me to create blocks that are compound by blocks that are compound by the 9-or-more images (I just don't know how to do it, and I know it's difficult to understand. Try to imagine the third image being used 9 times). // This part of the question makes it on-topic!
Note that this question isn't subjective, since the goal here is to use less lines, and I'm not using the EaselJS tag because the question isn't EaselJS-specific, anyone with JavaScript knowledge can answer me.
Here's my incredibly big JavaScript function:
var Graphic = function (src, blockWidth, blockHeight) {
return {
createBlockAt: function (x, y, blockGroupWidth, blockGroupHeight, clsdir, alpha) {
for (var blockY = 0; blockY < blockGroupHeight / blockHeight; blockY++) {
for (var blockX = 0; blockX < blockGroupWidth / blockWidth; blockX++) {
var obj = new createjs.Bitmap(src[Math.floor(Math.random() * src.length)]);
obj.width = blockWidth;
obj.height = blockHeight;
if (typeof alpha !== 'undefined') {
obj.alpha = alpha; // While debugging this can be used to check if a block was made over another block.
}
obj.x = Math.round(x + (blockWidth * blockX));
obj.y = Math.round(y + (blockHeight * blockY));
stage.addChild(obj);
}
}
}
}
}
var complexBlock = function (topLeft, topCenter, topRight, middleLeft, middleCenter, middleRight, bottomLeft, bottomCenter, bottomRight, blockWidth, blockHeight) {
return {
createBlockAt: function (x, y, blockGroupWidth, blockGroupHeight, clsdir, alpha) {
for (var blockY = 0; blockY < blockGroupHeight / blockHeight; blockY++) {
for (var blockX = 0; blockX < blockGroupWidth / blockWidth; blockX++) {
if (blockY == 0 && blockX == 0) {
var obj = new createjs.Bitmap(topLeft[Math.floor(Math.random() * topLeft.length)]);
}
if (blockY == 0 && blockX != 0 && blockX != (blockGroupWidth / blockWidth - 1)) {
var obj = new createjs.Bitmap(topCenter[Math.floor(Math.random() * topCenter.length)]);
}
if (blockY == 0 && blockX == (blockGroupWidth / blockWidth - 1)) {
var obj = new createjs.Bitmap(topRight[Math.floor(Math.random() * topRight.length)]);
}
if (blockY != 0 && blockY != (blockGroupHeight / blockHeight - 1) && blockX == 0) {
var obj = new createjs.Bitmap(middleLeft[Math.floor(Math.random() * middleLeft.length)]);
}
if (blockY != 0 && blockY != (blockGroupHeight / blockHeight - 1) && blockX != 0 && blockX != (blockGroupWidth / blockWidth - 1)) {
var obj = new createjs.Bitmap(middleCenter[Math.floor(Math.random() * middleCenter.length)]);
}
if (blockY != 0 && blockY != (blockGroupHeight / blockHeight - 1) && blockX == (blockGroupWidth / blockWidth - 1)) {
var obj = new createjs.Bitmap(middleRight[Math.floor(Math.random() * middleRight.length)]);
}
if (blockY == (blockGroupHeight / blockHeight - 1) && blockX == 0) {
var obj = new createjs.Bitmap(bottomLeft[Math.floor(Math.random() * bottomLeft.length)]);
}
if (blockY == (blockGroupHeight / blockHeight - 1) && blockX != 0 && blockX != (blockGroupWidth / blockWidth - 1)) {
var obj = new createjs.Bitmap(bottomCenter[Math.floor(Math.random() * bottomCenter.length)]);
}
if (blockY == (blockGroupHeight / blockHeight - 1) && blockX == (blockGroupWidth / blockWidth - 1)) {
var obj = new createjs.Bitmap(bottomRight[Math.floor(Math.random() * bottomRight.length)]);
}
obj.width = blockWidth;
obj.height = blockHeight;
if (typeof alpha !== 'undefined') {
obj.alpha = alpha; // While debugging this can be used to check if a block was made over another block.
}
obj.x = Math.round(x + (blockWidth * blockX));
obj.y = Math.round(y + (blockHeight * blockY));
stage.addChild(obj);
}
}
}
}
}
var bigDirt = complexBlock(["http://i.imgur.com/DLwZMwJ.png"], ["http://i.imgur.com/UJn3Mtb.png"], ["http://i.imgur.com/AC2GFM2.png"], ["http://i.imgur.com/iH6wFj0.png"], ["http://i.imgur.com/wDSNzyc.png", "http://i.imgur.com/NUPhXaa.png"], ["http://i.imgur.com/b9vCjrO.png"], ["http://i.imgur.com/hNumqPG.png"], ["http://i.imgur.com/zXvJECc.png"], ["http://i.imgur.com/Whp7EuL.png"], 40, 40);
bigDirt.createBlockAt(0, 0, 40*3, 40*3);
Okay... Lots of code here, how do I test?
Here we go: JSFiddle
I don't see an easy way to reduce the number of lines given the nine possible branches, but you can substantially reduce the repetition in your code:
function randomImage(arr) {
return new createjs.Bitmap(arr[Math.floor(Math.random() * arr.length)]);
}
if (blockY == 0 && blockX == 0) {
var obj = randomImage(topLeft);
} // etc
Re: the nine possible branches, you should note that they are mutually exclusive, so should be using else if instead of just if, and that they are also naturally grouped in threes, suggesting that they should be nested.
EDIT in fact, there is a way to reduce the function size a lot. Note that for X and Y you have three options each (nine in total). It is possible to encode which image array you want based on a two-dimensional lookup table:
var blocksHigh = blockGroupHeight / blockHeight;
var blocksWide = blockGroupWidth / blockWidth;
var blockSelector = [
[topLeft, topCenter, topRight],
[middleLeft, middleCenter, middleRight],
[bottomLeft, bottomCenter, bottomRight]
];
for (var blockY = 0; blockY < blocksHigh; blockY++) {
var blockSY = (blockY == 0) ? 0 : blockY < (blocksHigh - 1) ? 1 : 2;
for (var blockX = 0; blockX < blocksWide; blockX++) {
var blockSX = (blockY == 0) ? 0 : blockY < (blocksWide - 1) ? 1 : 2;
var array = blockSelector[blockSY][blockSX];
var obj = randomImage(array);
...
}
}
Note the definitions of blocksHigh and blocksWide outside of the loop to reduce expensive repeated division operations.
See http://jsfiddle.net/alnitak/Kpj3E/
Ok, it's almost a year later now and I decided to come back here to improve the existing answers. Alnitak's suggestion on creating a "2-dimensional lookup table" was genius, but there's a even better way of doing what I was asking for.
Sprite Sheets
The problem core is the need for picking lots of separated images and merge them in order to create a bigger mosaic. To solve this, I've merged all images into a sprite sheet. Then, with EaselJS, I've separated each part of the platform (topLeft, topCenter, etc) in multiple animations, and alternative images of the same platform part that would be used randomly are inserted within it's default part animation, as an array (so topLeft can be five images that are used randomly).
This was achieved by making a class that creates an EaselJS container object, puts the sprite sheet inside this container, moves the sprite sheet to the correct position, caches the frame and updates the container cache using the "source-overlay" compositeOperation — which puts the current cache over the last one — then it does this again until the platform is finished.
My collision detection system is then applied to the container.
Here's the resulting JavaScript code:
createMosaic = function (oArgs) { // Required arguments: source: String, width: Int, height: Int, frameLabels: Object
oArgs.repeatX = oArgs.repeatX || 1;
oArgs.repeatY = oArgs.repeatY || 1;
this.self = new createjs.Container();
this.self.set({
x: oArgs.x || 0,
y: oArgs.y || 0,
width: ((oArgs.columnWidth || oArgs.width) * oArgs.repeatX) + oArgs.margin[1] + oArgs.margin[2],
height: ((oArgs.lineHeight || oArgs.height) * oArgs.repeatY) + oArgs.margin[0] + oArgs.margin[3],
weight: (oArgs.weight || 20) * (oArgs.repeatX * oArgs.repeatY)
}).set(oArgs.customProperties || {});
this.self.cache(
0, 0,
this.self.width, this.self.height
);
var _bmp = new createjs.Bitmap(oArgs.source);
_bmp.filters = oArgs.filters || [];
_bmp.cache(0, 0, _bmp.image.width, _bmp.image.height);
var spriteSheet = new createjs.SpriteSheet({
images: [_bmp.cacheCanvas],
frames: {width: oArgs.width, height: oArgs.height},
animations: oArgs.frameLabels
});
var sprite = new createjs.Sprite(spriteSheet);
this.self.addChild(sprite);
for (var hl = 0; hl < oArgs.repeatY; hl++) {
for (var vl = 0; vl < oArgs.repeatX; vl++) {
var _yid = (hl < 1) ? "top" : (hl < oArgs.repeatY - 1) ? "middle" : "bottom";
var _xid = (vl < 1) ? "Left" : (vl < oArgs.repeatX - 1) ? "Center" : "Right";
if(typeof oArgs.frameLabels[_yid + _xid] === "undefined"){
oArgs.frameLabels[_yid + _xid] = oArgs.frameLabels["topLeft"];
} // Case the expected frameLabel animation is missing, it will default to "topLeft"
sprite.gotoAndStop(_yid + _xid);
if (utils.getRandomArbitrary(0, 1) <= (oArgs.alternativeTileProbability || 0) && oArgs.frameLabels[_yid + _xid].length > 1) { // If there are multiple frames in the current frameLabels animation, this code choses a random one based on probability
var _randomPieceFrame = oArgs.frameLabels[_yid + _xid][utils.getRandomInt(1, oArgs.frameLabels[_yid + _xid].length - 1)];
sprite.gotoAndStop(_randomPieceFrame);
}
sprite.set({x: vl * (oArgs.columnWidth || oArgs.width), y: hl * (oArgs.lineHeight || oArgs.height)});
this.self.updateCache("source-overlay");
}
}
this.self.removeChild(sprite);
awake.container.addChild(this.self);
};
Usage:
createMosaic({
source: "path/to/spritesheet.png",
width: 20,
height: 20,
frameLabels: {
topLeft: 0, topCenter: 1, topRight: 3,
middleLeft: 4, middleCenter: [5, 6, 9, 10], middleRight: 7,
bottomLeft: 12, bottomCenter: 13, bottomRight: 15
},
x: 100,
y: 100,
repeatX: 30,
repeatY: 15,
alternativeTileProbability: 75 / 100
});
I would recommend using the "createMosaic" as a function returned by a constructor that passes the required arguments to it, so you'll not need to write the source image path, width, height and frameLabels every time you want to create a dirt platform, for example.
Also, this answer may have more LoC than the others that came before, but it's made this way in order to have more structure.

HTML5/Javascript game dev - If I have more than one boundary, only the most recent one works

So basically what I have is a function called createBoxBoundary, which, when the player position is within certain limits, sets boundary variables to true. Pretty straightforward. However, when I call this function more than once in the main game loop, only the one most recently called works. Below is a sampling of my code
//It should be noted the player deminsions are 40x80
function createBoxBoundary(x,y,width,height){
//right boundaries
if(playerXPos + 40 == x && playerYPos + 80 >= y && playerYPos < y + height){
boundaryRight = true;
} else{boundaryRight = false;}
//bottom boundaries
if(playerYPos == y + height && playerXPos + 40 >= x && playerXPos <= x + width){
boundaryTop = true;
} else{boundaryTop = false;}
//left boundaries
if(playerXPos == x + width && playerYPos + 80 >= y && playerYPos <= y + height){
boundaryLeft = true;
} else{boundaryLeft = false;}
//bottom boundaries
if(playerYPos + 80 == y && playerXPos + 40 >= x && playerXPos < x + width){
boundaryBottom = true;
} else{boundaryBottom = false;}
}
I've also set up a fiddle with the full game code. And if anyone has advice on a better way to do collisions/boundaries in javascript, I'm open to that as well. Any help is appreciated!
The problem is that you are not checking if boundaryRight is already set to true before checking what is value is for the second boundary.
Something like this should work:
boundaryRight = ( boundaryRight || ( playerXPos + 40 == x && playerYPos + 80 >= y && playerYPos < y + height );
I have also done some other tweaks in this jsFiddle that add some more flexibility of defining boundaries and other things. Also adds the ability to move in more than one direction.
http://jsfiddle.net/petersendidit/QdCMG/7/

Categories