Why doesn't this function detect overlapping circles? - javascript

http://jsfiddle.net/goldrunt/SeAGU/52/
Line 49 checks for "false" on isOnCircle function before creating the new object. Function is on line 32. When creating more object, the function is passing when it should not pass.
if (isOnCanvas(location) && !isOnCircle(location)) {
console.log(location, isOnCanvas(location), isOnCircle(location));
create(location);
In fact I can't get the collision detection to register true no matter what values are passed to it
(Math.pow((a.x - i.x), 2) + Math.pow((a.y - i.y), 2) <= Math.pow((a.radius + i.radius), 2))

here I've fixed and given more descriptive variable names so you can see what's going on.
EDIT: I've noticed you don't always feed a circle but sometimes a point as A, which does not have a .radius property resulting in NaN, which also screws up your comparison.
function circleTest(a,b) {
var DistanceX = a.x - b.x;
var DistanceY = a.y - b.y;
var DistanceCenter = Math.sqrt(DistanceX * DistanceX + DistanceY * DistanceY);
var CollisionDistance = b.radius;
if (a.radius) CollisionDistance += a.radius
return DistanceCenter <= CollisionDistance;
}
I also noticed a problem in your function called "isOnCircle" where you are using i (a number) as if it were a circle object, with the above function this can be fixed like:
function isOnCircle(a) {
for (var i = 0; i < circles.length; i++) {
if (circleTest(a, circles[i])) return true;
}
return false;
}

Two problems:
i is the numerical index you are using to iterate through the circles array but you are using it as if it was a circle object; you need to use circles[i] to get the circle at each iteration.
a is a point and does not have a radius (in the code below I've left a.radius in just in-case you pass in a circle rather than a point and have ORed it with 0 so you get a valid number).
Defining some additional variables (for clarity) then you can replace the isOnCircle function with this:
function isOnCircle(a) {
var i=0,l=circles.length,x,y,d,c;
for (; i < l; ++i) {
c = circles[i];
x = a.x-c.x;
y = a.y-c.y;
d = (a.radius||0)+c.radius;
if (x*x+y*y <= d*d) {
return true;
}
}
return false;
}

Related

JavaScript: function return closest value

I am trying to return the value of the closest object to my position in a function.
I tried to put the entities in a array and than I tried return closest object to my position with a for loop, but it did not work. How can I do this?
function getMyEntity() {
return Game.currentGame.world.localPlayer.entity.getFromTick();
}
function getOtherEntity() {
var MyPlayerEntity = getMyEntity();
var entities = Game.currentGame.world.entities;
for (var uid in entities) {
// how i get closest entity to my player entity here?
var gameObject = entities[uid].fromTick;
console.log(entities[uid].fromTick.position, MyPlayerEntity.position)
if (gameObject.entityClass == "Prop" && gameObject.uid !== MyPlayerEntity.uid) {
return gameObject;
}
}
}
function aimAssist() {
var MyPlayerEntity = getMyEntity();
var OtherEntity = getOtherEntity();
if (OtherEntity == undefined) return
var aimPosition = {
x: OtherEntity.position.x - MyPlayerEntity.position.x,
y: OtherEntity.position.y - MyPlayerEntity.position.y
}
return aimPosition;
}
I'll give you a bad advice, for now it'll work, as your game grows it will be bad because of O(n^2) complexity. Read a bit about quadtrees and see if you can do that. Meanwhile you can compare the euclidean distance, do not need to take the square root:
Object.keys(entities)
.map(function(d,i){
var dx = entities[d].fromTick.position.x - MyPlayerEntity.position.x,
dy = entities[d].fromTick.position.y - MyPlayerEntity.position.y,
result = {D:(dx * dx) + (dy + dy), obj:entities[d] , valueOf: function(){return this.D};
return result;
}).sort(function(a,b){
return a-b;
})[0].obj; //this returns the closest one
So your original function becomes this:
function getOtherEntity() {
var MyPlayerEntity = getMyEntity();
var entities = Game.currentGame.world.entities;
return Object.keys(entities)
.map(function(d,i){
var dx = entities[d].fromTick.position.x - MyPlayerEntity.position.x,
dy = entities[d].fromTick.position.y - MyPlayerEntity.position.y,
result = {D:(dx * dx) + (dy + dy), obj:entities[d] , valueOf: function(){return this.D};
return result;
}).sort(function(a,b){
return a-b;
})[0].obj; //this returns the closest one
}
I'd create an array of objects which contain the Object-ID and distance, and sort it by distance.
The first array-item is the closest to the player.
The array may look like
[{uid: 1234, distance: 12}, {uid: 1235, distance: 16}, ...]
You can sort arrays with arrayName.sort(sortingFunction)
assuming you can get x and y coordinates from gameObject.position and MyPlayerEntity.position you could use a bit of Pitagoras: c^2 = a^2 +b^2, with c being the distance
let a = gameObject.position.x - MyPlayerEntity.position.x;
let b = gameObject.position.y - MyPlayerEntity.position.y;
let distanceSquared = a*a + b*b;
Since you don't seem to need the exact distance and sqrt() is expensive, you can use the value in distanceSquared and other variables declared outside of the loop to keep track
let closestDistance;
let closest;
to make the proper comparisons
if(distanceSquared < closestDistance){
closestDistance = distanceSquared;
closest = gameObject;
}
After you loop through the array, the closest entity reference should be:
return closest;

How find square root n by computing the next Xi term on javascript

Write the function sqrt(A) for computing square root of positive real numbers using next numerical method xi+1 = (1/2) * (xi +(A/xi)). Where the A - input rial number;
On zero iteration next statements have been taken: x0 = A;
The error should be at least 10^-6
You could take the last value xi-1 and compare it with the new value xi instead of using a loop counter.
function sqrt(a, x = 1) { // take 1 for x(0) as start value for recursion
var y = (x + a / x) / 2; // prepare next value x(i+1)
if (x === y) { // exit condition
return x;
}
return sqrt(a, y); // tail call optimization
} // https://stackoverflow.com/q/310974/1447675
console.log(sqrt(2));
console.log(sqrt(10));
console.log(sqrt(9));
console.log(sqrt(25));
Looks like your requirement is not just finding the square root of a number. If by any chance that is your requirement, use Math.sqrt.
If your requirement is to implement a function to find the square root for educational purpose, what you need is to write a recursive function as below. Modify the code as required to support error at 10^-6
function sqrt(A, i = 0) {
if (i === 0)
return A;
let prev = sqrt(A, i - 1);
return 0.5 * (prev + (A / prev));
}
console.log(sqrt(2,1000));
console.log(sqrt(3,1000));
console.log(sqrt(9,1000));
console.log(sqrt(25,1000));

Where are the arguments critter & vector defined in Eloquent JavaScript Chapter 7

Working slowly through the Eloquent JavaScript book by Marijn Haverbeke and am trying to get my head around the first step of the World.turn() function:
World.prototype.turn = function() {
var acted = [];
this.grid.forEach(function(critter, vector) {
if (critter.act && acted.indexOf(critter) == -1) {
acted.push(critter);
this.letAct(critter, vector);
}
}, this);
};
Where does the prototype forEach function get the arguments critter and vector from.
When I log their output to the console, I get an object with the originChar and a direction if valid, but can't get my head around where it is getting the arguments.
The link to the section is as follows: http://eloquentjavascript.net/07_elife.html#h_6OGIzAd5Tr
Thanks in advance :)
The World owns a Grid, which has forEach defined on it just above the section you're looking at.
Grid.prototype.forEach = function(f, context) {
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
var value = this.space[x + y * this.width];
if (value != null)
f.call(context, value, new Vector(x, y));
}
}
};
The grid's cells contain critters, which you see captured in var value = this.space[x + y * this.width] and passed to the function you provide forEach, along with new Vector(x, y) containing the location in the grid.

When you use JavaScript functions as classes, do they still get parsed in a top-down fashion?

I'm trying to figure out why my Google Chrome console is giving me the error "undefined is not a function." I have a hunch, but maybe I'm on the wrong track. My function boxCollision(...) is defined at the bottom of my class. Nearer to the top I have a statement
if (this.boxCollision(this.food.getBBox(), this.body[0].getBBox()))
this.food.translate(this.linkSize, 0);
the first line of which is causing the error I mentioned. I think that's maybe because I haven't yet defined boxCollision, so it's essentially nonexistent. Is that right? The getBBox() functions are recognized because they're from an external JavaScript file.
function snakegame(C, C_w, C_h, spd)
{
/* NOTE TO SELF: C is a Raphel object. Can't find a method to return the height
and width of a Raphael object in the documentation:
http://raphaeljs.com/reference.html#Raphael.
Using C_h and C_w for now, but should probably change it later.
*/
this.linkSize = 50; /* size of a snake unit, in pixels; must divide C_h and C_w */
this.link = C.rect(C_h/2, C_w/2, this.linkSize, this.linkSize);
this.link.attr("fill", "#E9E581");
this.body = [this.link];
this.food = C.rect(randInt(0,C_w/this.linkSize-1) * this.linkSize, randInt(0,C_h/this.linkSize-1) * this.linkSize, this.linkSize, this.linkSize);
if (this.boxCollision(this.food.getBBox(), this.body[0].getBBox()))
this.food.translate(this.linkSize, 0);
this.food.attr("fill","#B43535");
this.maxSnakeSize = C_h * C_w / (this.linkSize * this.linkSize);
/* On instantiation, the snake direction is down and has 1 link */
this.dy = 0;
this.dx = 0;
this.score = 0;
/* Event listener for changing the direction of the
snake with arroy keys on the keyboard
*/
this.redirect = function(dirnum)
{
switch (dirnum)
{
/*
dirnum corresponds to
1 ---> right
2 ---> down
3 ---> left
4 ---> up
*/
case 1:
this.dx = this.linkSize;
this.dy = 0;
break;
case 2:
this.dx = 0;
this.dy = this.linkSize;
break;
case 3:
this.dx = -this.linkSize;
this.dy = 0;
break;
case 4:
this.dx = 0;
this.dy = -this.linkSize;
break;
default: /* never happens */
break;
}
}
this.move = function()
{
if (this.body.length == this.maxSnakeSize)
{
this.destruct();
return;
}
var addLink = false;
var BBhead = this.body[0].getBBox();
if (this.hitWall(BBhead) || this.hitSnake(BBhead))
{
document.getElementById("snakescorediv").innerHTML = "<p>GAME OVER!</p><p>Score: "+ this.score +"</p>";
this.destruct();
return;
}
var BBfood = this.food.getBBox();
if (this.boxCollision(BBhead, BBfood))
{
this.moveFood();
this.score += 10;
document.getElementById("snakescorediv").innerHTML = this.score.toString();
addLink = true;
}
if (addLink)
this.body.push(this.body[this.body.length - 1].clone());
for (var i = this.body.length - 1; i > 0; --i)
{
var prevBB = this.body[i-1].getBBox();
var thisBB = this.body[i].getBBox();
this.body[i].translate(prevBB.x-thisBB.x, prevBB.y-thisBB.y)
}
this.body[0].translate(this.dx, this.dy);
}
this.mover = setInterval(this.move.bind(this), spd);
this.hitWall = function(bb)
{
return bb.x < 0 || bb.x2 > C_w || bb.y < 0 || bb.y2 > C_h;
}
this.hitSnake = function(bb)
{
var retval = false;
for (var i = 1, j = this.body.length; i < j; ++i)
{
var thisbb = this.body[i].getBBox();
if (this.boxCollision(bb, thisbb))
{
retval = true;
break;
}
}
return retval;
}
this.moveFood = function()
{
var bbf = this.food.getBBox(); // bounding box for food
do {
/* tx, ty: random translation units */
tx = randInt(0, C_w / this.linkSize - 1) * this.linkSize - bbf.x;
ty = randInt(0, C_h / this.linkSize - 1) * this.linkSize - bbf.y;
// translate copy of food
this.food.translate(tx, ty);
bbf = this.food.getBBox(); // update bbf
} while (this.hitSnake(bbf));
}
this.boxCollision = function(A, B)
{
return A.x == B.x && A.y == B.y;
}
this.destruct = function()
{
clearInterval(this.mover);
for (var i = 0, j = this.body.length; i < j; ++i)
{
this.body[i].removeData();
this.body[i].remove();
}
this.food.removeData();
this.food.remove();
this.score = 0;
}
}
Put the methods on the prototype to avoid this issue.
This won't work:
function Ctor() {
this.init()
this.init = function() {
console.log('init')
}
}
var inst = new Ctor // Error: undefined is not a function
But this will:
function Ctor() {
this.init()
}
Ctor.prototype.init = function() {
console.log('init')
}
var inst = new Ctor // init
Javascript parses code in two steps: compilation and evaluation.
The first step is compilation. In this step all definitions are compiled but no statement or expressions are evaluated. What this means is that definitions such as:
function a () {}
and:
var x
gets compiled into memory.
In the evaluation phase the javascript interpreter finally starts executing. This allows it to process operators which makes it possible to execute statements and expressions. It is in this step that variables get their values:
var x = 10;
^ ^
| |______ this part now gets assigned to `x` in the evaluation phase
|
this part was processed in the compilation phase
What this means is that for function expressions:
var x = function () {}
while both the variable and function body are compiled in the compilation phase, the anonymous function is not assigned to the variable until the evaluation phase. That's because the = operator is only executed in the evaluation phase (during the compilation phase all variables are allocated memory and assigned the value undefined).
Both the compilation phase and evaluation phase happen strictly top-down.
What some call "hoisting" is simply the fact that the compilation phase happen before the evaluation phase.
One work-around is to simply use a function definition instead of a function expression. Javascript support inner functions so a function defined in another function doesn't exist in the global scope:
function boxCollision (A, B) {
return A.x == B.x && A.y == B.y;
}
this.boxCollision = boxCollision;
Then you can use it at the top of your constructor:
if (boxCollision(this.food.getBBox(), this.body[0].getBBox()))
this.food.translate(this.linkSize, 0);
Note that you can't use this.boxCollision because it's still undefined when you call it.
Another obvious work-around is to of course assign this.boxCollision = function (){} at the top before using it.
Or you could even assign it to the constructor's prototype. Or you can have an init function that gets called at the top (note: function, not method - again the use of a definition instead of a function expression make use of "hoisting").
There are many ways to get around this. But it's useful to know why it's happening to understand what works and what doesn't.
See my answer to this related question for more examples of this behavior: JavaScript function declaration and evaluation order

Comparing values within an array to each other

I am new to JavaScript and HTML and am working on a small game.
I have four 'enemies' whose position on the canvas is determined by the values in the arrays 'enemyX' and 'enemyY'.
Very simply, I want to detect if the enemies have 'collided', i.e have moved within 30px of each other (the enemy image is 30px by 30px).
What I want to do is subtract the value of the i th value in the array with the other values in the same array and see if this value is less than 30. The less than 30 part is an if statement, so how do I go about subtracting all the values from each other without many lines of code?
Here's what I have tried based on the answers below:
var count = 0;
var innercount = 0;
while (count <= 3) {
while (innercount<=3) {
collisionDetect(count, innercount, enemyX[count], enemyY[count], enemyX[innercount], enemyY[innercount])
innercount++
}
count++
}
var i = 0;
while (i < enemyX.length) {
if (collisionX[i] == 1) {
directionX = directionX*-1;
}
if (collisionY[i] == 1) {
direction = directionY*-1;
}
}
}
}
function collisionDetect(count, innercount, x, y, xX, yY ) {
if ((Math.abs(x-xX)) <=30) {
collisionX[count] = 1
collisionX[innercount] = 1
}
if ((Math.abs(y - yY)) <=30) {
collisionY[count] = 1
collisionY[innercount] = 1
}
return collisionX, collisionY;
}
This code gives me a blank canvas.
Detection of an intersection between two objects (assuming rectangular shape) and the position defines the center of the object.
function getRect(x, y, w, h)
{
var left = x - Math.floor(w / 2),
top = y - Math.floor(h / 2);
return {
left: left,
top: top,
right: left + w,
bottom: top + h
};
}
function intersects(A, B)
{
return A.right >= B.left &&
A.left <= B.right &&
A.bottom >= B.top &&
A.top <= B.bottom;
}
alert(intersects(getRect(12, 56, 30, 30), getRect(30, 40, 30, 30))); // true
The getRect() function can be modified to accommodate for a different anchor position, e.g. top left.
You could use a function:
function colliding(x1, y1, x2, y2){
return Math.abs(x1-x2) <= 30 && Math.abs(y1-y2) <= 30;
}
then use the function to test the different combination of enemies: (1,2), (1,3), (1,4), (2,3), (2,4), and (3,4).
So, for example, you would use: colliding(enemyX[2], enemyY[2], enemyX[3], enemyY[3]) to check if enemy 2 and 3 are colliding. Do that with all the combinations above.
EDIT: to make it more readable, you could define an additional function:
function enemiesColliding(e1, e2){
return colliding(enemyX[e1], enemyY[e1], enemyX[e2], enemyY[e2])
}
and then use it:
enemiesColliding(1,2) || enemiesColliding(1,3) || enemiesColliding(1,4) ||
enemiesColliding(2,3) || enemiesColliding(2,4) || enemiesColliding(3,4)
I am going to restate my understanding of you question, just so there is no confusion.
You have two arrays in paralleled , one for x cords, and one for y cords. Each ship has a element in both arrays.
So, for example, ship 12 could be found at xPos[12] and yPos[12], where xPos and yPos are the arrays from above.
It also stands to reason that this a communicative. If ship[a] collides with ship[b] then ship[b] has collided with ship[a]. And I think that hold for 3 + ships.
I would start by writing a distance function.
dist(x1,y1,x2,y2)
{
return Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
Then I would write code run through the arrays.
Every element must be compared to every other element, one time.
var counter = 0;
var indexOfShipThatHaveCollided = [];
while(counter < Xpos.length)
{
var innerCounter = counter;
while(innerCounter < Xpos.length)
{
t = dist(Xpos[counter],Ypos[counter],Xpos[innerCounter],Ypos[innerCounter])
if(t < 30)
{
indexOfShipThatHaveCollided.push(counter)
indexOfShipThatHaveCollided.push(innerCounter)
}
}
}
The code above compares every ship to every other ship ONCE.
It will compare ship[1] to ship[8], but it will not compare ship[8] to ship[1].
I did not test ANY of this code, but I hope it moves you in the right direction.
If you have anymore question just give me a comment.
According my understanding of your question below is my solution:
I think you need to find distance between two points. by using distance you can apply your logic.
To find distance following is the formula:
Given the two points (x1, y1) and (x2, y2),
the distance between these points is given by the formula:

Categories

Resources