Checking circular area around enemy for player - javascript

I'm working on a HTML5 game and have what I think is a math problem. The player and enemy objects have a pos.x and pos.y value indicating where they are on the screen. I have implemented proximity check code for some enemies and am not totally happy with it. Currently the enemy is checking if the player is within a certain distance from it, 200 or -200 on the x and y axis. What this means is that the entity is scanning a 400x400 square around itself.
I would like to make this a circle with a radius of 200 instead. My code as it stands.
if ((player.pos.x - enemy.pos.x > 200 && player.pos.x - enemy.pos.x < 200)
&& (player.pos.y - enemy.pos.y > 200 && player.pos.y - enemy.pos.y < 200)) {
//Do something...
}
Here's my game if you want to check it out. Proximity enemies are on the second and currently last level :)
http://project.dnsalias.com/

It's very basic math.
Check that (x1 - x0)2 + (y1 - y0)2 < r2
function inRange(p0, p1, r) {
r = r || 200;
var dx = p0.x - p1.x;
var dy = p0.y - p1.y;
return (dx * dx + dy * dy) < (r * r);
}
Call it like so:
if (inRange(player.pos, enemy.pos)) {
...
}
You can supply a third optional argument to change the detection radius.

Perhaps you could use the Euclidean distance function instead? It would be something like (player.pos.x - enemy.pos.x) x (player.pos.x - enemy.pos.x) + (player.pos.y - enemy.pos.y) x (player.pos.y - enemy.pos.y) = 200 x 200. Sorry I don't know the syntax for square root and square functions.

Related

Line to line intersection works only on one side

I'm creating a raycasting engine on javascript using p5js and there is an issue with the line to line (raycast to wall) intersection.
I found a lot of line to line collision algorithms, including p5 collide library, but the problem appears on every one of them.
this.intersects = function (raycastStart, raycastEnd) {
var x1 = this.startPoint.x; //Start point is the first point of a line.
var y1 = this.startPoint.y;
var x2 = this.endPoint.x; //End point is the second point of a line.
var y2 = this.endPoint.y;
var x3 = raycastStart.x;
var y3 = raycastStart.y;
var x4 = raycastEnd.x;
var y4 = raycastEnd.y;
var a_dx = x2 - x1;
var a_dy = y2 - y1;
var b_dx = x4 - x3;
var b_dy = y4 - y3;
var s = (-a_dy * (x1 - x3) + a_dx * (y1 - y3)) / (-b_dx * a_dy + a_dx * b_dy);
var t = (+b_dx * (y1 - y3) - b_dy * (x1 - x3)) / (-b_dx * a_dy + a_dx * b_dy);
//Vector2 is simply class with two fields: x and y.
return (s >= 0 && s <= 1 && t >= 0 && t <= 1) ? new Vector2(x1 + t * a_dx, y1 + t * a_dy) : null;
}
The line to line collision works on one side properly, but on the other, it works incorrect, according to my y position.
this is my map.
on one side it works perfectly
but on the other, it checks collision for line segments, that are lower than my Y position
(I would comment, but don't have enough reputation to do so...)
It appears that your line collision algorithm is working. But what appears to be missing is a check to determine which raycaster-to-line intersection is closer. That is, in your working example the raycast never casts across two line segments, so there is no question about which line segment constrains your raycast. But in your non-working example, the raycaster hits 2 of your 4 segments, so you now need to determine which of the 2 intersection points is closer to the raycast start, in order to determine which line segment is closer.

Calculating a circle's velocity

I've been working on this problem for a bit, and it doesn't seem too hard, but I'm getting tired and it seems more and more complicated the more I try (but it's probably really easy).
My goal is to have a ball bounce off another ball. Seems easy enough.
Ball 2 is controlled by the user's mouse (so far it's sort of like single player pong, but it's a circle instead of a rectangle) so its velocity doesn't matter.
Ball 1 has a few attributes, including dx (the x distance it moves every frame) and dy (dx, but for the y coordinate)
The problem with what I have so far is that you don't know what values will be positive and what will be negative (so the speed can severely increase or decrease instantly), you might be able to fix this using many else if's, but I'm too confused to think right now.
Here is the important part of this function. Also, I've tried to set it up so that dx + dy is always the same, even when the numbers change, so that it looks more natural.
if (collision(ball, paddle)) {
diffX = paddle.x-ball.x;
diffY = paddle.y-ball.y;
totalVel = ball.dx+ball.dy;
dir = {
x : diffX/(diffX+diffY)*-totalVel,
y : diffY/(diffX+diffY)*-totalVel
};
ball.dx = dir.x;
ball.dy = dir.y;
}
Here is a JSFiddle with the full code
https://jsfiddle.net/a2prr0uw/1/
So firstly let's start by defining what a "bounce" is - the speed is the same, but the direction (on both axis) will be inverted. If we treat dx and dy like a vector, then we can first get the incoming speed of the ball like this:
var ballSpeed = Math.sqrt((ball.dx * ball.dx) + (ball.dy * ball.dy));
The above value will always be positive, regardless of what dx and dy are doing.
Next, we'll need the incoming direction of the ball - that bit is the same as what you've currently got:
diffX = paddle.x-ball.x;
diffY = paddle.y-ball.y;
However if we treat this as a vector too, it essentially has a totally unknown length. So, let's normalise it so it's a direction vector with a length of 1:
var distanceBetweenPaddleAndBall = Math.sqrt((diffX * diffX) + (diffY * diffY));
diffX /= distanceBetweenPaddleAndBall;
diffY /= distanceBetweenPaddleAndBall;
diffX and diffY is now a normalised direction vector - the direction the ball is currently going in - and ballSpeed is the speed we'd like it to go.
So now we'll apply our bounce - flip the direction and retain the speed. That becomes this:
dir = {
x : -diffX * ballSpeed,
y : -diffY * ballSpeed
};
Put it all together and we end up with this:
if (collision(ball, paddle)) {
diffX = paddle.x-ball.x;
diffY = paddle.y-ball.y;
// How fast is the ball coming in?
var ballSpeed = Math.sqrt((ball.dx * ball.dx) + (ball.dy * ball.dy));
// How far is the ball from the paddle?
var distanceBetweenPaddleAndBall = Math.sqrt((diffX * diffX) + (diffY * diffY));
// Normalise diffX and diffY so we have a direction vector:
diffX /= distanceBetweenPaddleAndBall;
diffY /= distanceBetweenPaddleAndBall;
// Apply the bounce and the original ball speed:
dir = {
x : -diffX * ballSpeed,
y : -diffY * ballSpeed
};
ball.dx = dir.x;
ball.dy = dir.y;
}
And here it is as a fork of your fiddle too.
not an answer but some considerations on your bouncing logic:
you have to calculate the balls direction (dy/dx)
the collision has also a direction (angle beween both centers = b.x-p.x / b.y-p.y)
the angle after bouncing has to be calculated based on these two angles: using ther 2nd for mirroring
to calculate the new dx & dy after collision you will need the original velocity Math.abs(Math.sqrt(Math.pow(dx)+Math.pow(dy))) of the ball
based on this velocity and the new direction you can calc the new dx & dy

Get coords of the intersection of the line in the plane

I have a canvas with this params:
width = 400, height = 400
and have a line passing through the point cursor[x1,y1] at an angle Q (in degree)
I need get all coords of the intersection of the line in the plane and write it to array. Now i use this equation: y - y1 = k * (x - x1)
to check all point I use this code:
var rad = Q * Math.PI/180;
for (ctrY = 0; ctrY < 400; ctrY += 1) {
for (ctrX = 0; ctrX < 400; ctrX += 1) {
if ( (ctrY - cursor.y) ===
~~(Math.tan(rad) * (ctrX - cursor.x)) ) {
z.push([ctrX, ctrY]);
}
}
}
For example when 0 < Q < 90 and cursor[x1,y1] = [200,200] z.length = 0 and it's not correct.
Where i'm wrong? Maybe there is a more convenient algorithm?
P.S. Sorry for my english
Seems you need line rastering algorithm. Consider Bresenham algorithm.
You can also look at DDA algorithm
I imagine an algorithm like this. (I only consider the case when 0 < Q < 90). First I will want to calculate the points where the line will intersect the Ox and Oy axes, considering the origin (0,0) point the upper left corner and if we imagine that the negative x and y values are respectively to the left and to the top of this point. Let x2 and y2 be the values where the line will intersect Ox and Oy. We want to calculate these values. We now have a system with 2 unknown variables (x2 and y2): Math.tan(rad) = (y1 -y2)/x1 and Math.tan(rad) = y1/(x1-x2). We can deduct these equations by drawing the line on the coordinate system and analyzing a bit. If we solve the system of equations we find something like: x2 = (x1*y1 -x1 * x1 * Math.tan(rad)/(2 * y1-x1)) and y2= y1- x1 * Math.tan(rad) (These need to be verified, I haven't double checked my calculus). A linear equation can be defined by the formula y = a*x + b and in our case a = x2 and b = y2. We can then calculate the points like this:
for (xIdx = 0; xIdx < 400; xIdx += 1) {
var ctrX = xIdx;
var ctrY = x2 * ctrX + y2 //todo: replace with the respective calculated variables x2 and y2(we could also define two functions in js) and proper rounding
z.push([ctrX, ctrY]);
}
I'm not sure if I'm 100% accurate but I hope you understand my idea.

Trying to make really simple collision in JS and Canvas

I have this game where I'm planning to make it into some kind of shooter (please also state if that's too hard considering the level I'm currently at) and I'm at the point of making things collide, like the player with point rectangles (should make orbs instead soon).
I've looked at different examples like
Collision Detection with javascript on the html canvas element without using jquery
and
http://jlongster.com/Making-Sprite-based-Games-with-Canvas
but it seems like I didn't understand the code well enough. Here's what I have:
if( (rX + (rX + 20)) >= x && rX <= (x + 20) && (rY + (rY + 20)) >= y && rY <= (y + 20)){
poeng++;
genererRandom();
}
Didn't work, so tried this one:
if (x >= rX || (rX+19) < (x+49) ||
y >= rY || (rY+19) < (y+49)) {
poeng++;
genererRandom();
}
x is the x position of the sprite I'm using (which is 60x60 large)
y is the y of x
rX is a randomly generated number and the x position of the point rectangle (again, think I should make it into orbs soon)
rY is the y of rX
Also, just ask if you want to see more code, although it is mostly not mine, I was given an unfinished game that I could make something out of.
Given a 2d axis aligned rectangle of width=w and height=h with its origin of x and y being in the top left corner, the check whether a point r with position rx and ry lies in the rectangle would be:
if (rx >= x && rx <= x+w && ry >= y && ry <= ry+h)
{
// we're inside the rectangle!
}
In simple terms this means:
check if the point's x lies between the rectangle's x and x+w
check if the point's y lies between the rectangle's y and y+h
In maths you could write it this way (doesn't work in JS though):
x <= rX <= x+w ∧ y <= rY <= y+h
The statements above imply that the coordinate system has the origin in the top left corner and the values increase towards bottom right.

html5 canvas elastic collision squares

I am re-asking this question since I did not make myself clear in what I wanted in my last question.
Does anyone know how to do elastic collision or handle collision in Canvas using rectangles? Or can point me in the right direction?
I created a canvas that has multiple square and would like each square to deflect when they touch.
Here is a quick fiddle that I put together showing to black buffer canvases http://jsfiddle.net/claireC/Y7MFq/10/
line 39 is where I started the collision detection and line 59 is where I tried to execute it. I will have more than 3 squares moving around and want them to deflect if/when they touch each other
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d");
context.fillStyle = "#FFA500";
context.fillRect(0, 0, canvas.width, canvas.height);
var renderToCanvas = function (width, height, renderFunction) {
var buffer = document.createElement('canvas');
buffer.width = width;
buffer.height = height;
renderFunction(buffer.getContext('2d'));
return buffer;
};
var drawing = renderToCanvas(100, 100, function (ctx) {
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, canvas.width, canvas.height);
});
var drawing2 = renderToCanvas(100, 100, function (ctx) {
ctx.fillStyle = "blue";
ctx.fillRect(0, 0, canvas.width, canvas.height);
});
var x = 0,
y = 0,
x2 = 200,
y2 = 10,
vx = .80,
vy = .80,
vx2 = .80,
vy2 = .80;
function collides(rectA, rectB) {
return !(rectA.x + rectA.width < rectB.x2 ||
rectB.x2 + rectB.width < rectA.x ||
rectA.y + rectA.height < rectB.y2 ||
rectB.y2 + rectB.height < rectA.y);
};
function executeFrame() {
x+=vx;
y+=vy;
x2+=vx2;
y2+=vy2;
if( x < 0 || x > 579) vx = -vx;
if( y < 0 || y > 265) vy = -vy;
if( x2 < 0 || x2 > 579) vx2 = - vx2;
if( y2 < 0 || y2 > 233) vy2 = - vy2;
if(collides(drawing, drawing2)){
//move in different direction
};
context.fillStyle = "#FFA500";
context.fillRect(0, 0, canvas.width, canvas.height);
context.drawImage(drawing, x, y);
context.drawImage(drawing2, x2, y2);
requestAnimationFrame(executeFrame);
}
//start animation
executeFrame();
Rectangular collision detection
To do a rectangular collision detection can be more complicated than it perhaps looks.
It's not just about figuring out if the two rectangles intersects or overlaps, but we also need to know at what angle they collide and what direction they move in order to deflect them properly, ideally transfer "velocity" to each other (mass/energy) and so forth.
This method that I present here will do the following steps:
First do a simple intersect detection to find out if they collide at all.
If an intersection: calculate the angle between the two rectangle
Divide a set primary rectangle into four zones of a circle where zone 1 is right, zone 2 is bottom and so forth.
Depending on zone, check in what direction the rectangle is moving, if towards the other rectangle deflect it based on which zone was detected.
➔ Online demo
➔ Version with higher speed here
Detect intersection and calculate angle
The code for detecting the intersection and angle is as follows, where r1 and r2 are here objects with properties x, y, w and h.
function collides(r1, r2) {
/// classic intersection test
var hit = !(r1.x + r1.w < r2.x ||
r2.x + r2.w < r1.x ||
r1.y + r1.h < r2.y ||
r2.y + r2.h < r1.y);
/// if intersects, get angle between the two rects to determine hit zone
if (hit) {
/// calc angle
var dx = r2.x - r1.x;
var dy = r2.y - r1.y;
/// for simplicity convert radians to degree
var angle = Math.atan2(dy, dx) * 180 / Math.PI;
if (angle < 0) angle += 360;
return angle;
} else
return null;
}
This function will return an angle or null which we then use to determine deflection in our loop (that is: the angle is used to determine the hit zone in our case). This is needed so that they bounce off in the correct direction.
Why hit zones?
With just a simple intersection test and deflection you can risk the boxes deflecting like the image on the right, which is not correct for a 2D scenario. You want the boxes to continue in the same direction of where there is no impact as in the left.
Determine collision zone and directions
Here is how we can determine which velocity vector to reverse (tip: if you want a more physical correct deflection you can let the rectangles "absorb" some of the other's velocity but I won't cover that here):
var angle = collides({x: x, y: y, w: 100, h: 100}, /// rect 1
{x: x2, y: y2, w: 100, h: 100}); /// rect 2
/// did we have an intersection?
if (angle !== null) {
/// if we're not already in a hit situation, create one
if (!hit) {
hit = true;
/// zone 1 - right
if ((angle >= 0 && angle < 45) || (angle > 315 && angle < 360)) {
/// if moving in + direction deflect rect 1 in x direction etc.
if (vx > 0) vx = -vx;
if (vx2 < 0) vx2 = -vx2;
} else if (angle >= 45 && angle < 135) { /// zone 2 - bottom
if (vy > 0) vy = -vy;
if (vy2 < 0) vy2 = -vy2;
} else if (angle >= 135 && angle < 225) { /// zone 3 - left
if (vx < 0) vx = -vx;
if (vx2 > 0) vx2 = -vx2;
} else { /// zone 4 - top
if (vy < 0) vy = -vy;
if (vy2 > 0) vy2 = -vy2;
}
}
} else
hit = false; /// reset hit when this hit is done (angle = null)
And that's pretty much it.
The hit flag is used so that when we get a hit we are marking the "situation" as a hit situation so we don't get internal deflections (which can happen at high speeds for example). As long as we get an angle after hit is set to true we are still in the same hit situation (in theory anyways). When we receive null we reset and are ready for a new hit situation.
Also worth to mention is that the primary rectangle here (whose side we check against) is the first one (the black in this case).
More than two rectangles
If you want to throw in more that two rectangle then I would suggest a different approach than used here when it comes to the rectangles themselves. I would recommend creating a rectangle object which is self-contained in regards to its position, size, color and also embeds methods to update velocity, direction and paint. The rectangle objects could be maintained by a host objects which performs the clearing and calls the objects' update method for example.
To detect collisions you could then iterate the array with these objects to find out which rectangle collided with the current being tested. It's important here that you "mark" (using a flag) a rectangle that has been tested as there will always be at least two in a collision and if you test A and then B you will end up reversing the effect of velocity change without using a flag to skip testing of the collision "partner" object per frame.
In conclusion
Note: there are special cases not covered here such as collision on exact corners, or where a rectangle is trapped between an edge and the other rectangle (you can use the hit flag mentioned above for the edge tests as well).
I have not optimized any of the code but tried to keep it as simple as I can to make it more understandable.
Hope this helps!
The answer is actually quite simple: swap the velocities of each block when they collide. That's it! Also for your collision test change RectA.x to just x, since they are normal variables given:
function collides(rectA, rectB) {
return !(x + rectA.width < x2 ||
x2 + rectB.width < x ||
y + rectA.height < y2 ||
y2 + rectB.height < y);
};
And swapping velocities:
if(collides(drawing, drawing2)){
var t = vx; var t2 = vy;
vx = vx2; vy = vy2;
vx2 = t; vy2 = t2;
};
And after those small changes we have working elastic collisions: http://jsfiddle.net/Y7MFq/11/

Categories

Resources