Collision response issue - basic circle within rectangle - javascript

I have an animation using JS and Fabric.js whereby circles more around the screen. I'm trying to contain them to a specific area but having some issues.
After some reading yesterday I thought that testing to see if the circle was inside the rectangle (their container) would be quite simple but I've yet to get it working properly.
The circles are created at the bottom of the screen which is also where their container is. With the code that I have they 'float' to the top and stay there in one spot.
The console logs that I have indicate that they are outside the rectangle immediately so I'm assuming that something is wrong with my collision function.
My aim is for them to stay within the containing moving about and when they hit the edges they should change direction so that they will stay inside again.
Thanks for any help.
EDIT : EDITED TO ENABLE A TOUCH OF CLARITY AND USING THE COLSIOSN DETECTION FROM BELOW ANSWER AS NOW THINK THE PROBLEM IS WITH THE RESPONSE INSTEAD OF THE DETECTION.
Collision function:
function testCollision(circle, rectangle) {
return circle.left + circle.radius < rectangle.left + rectangle.width/2 //right side
&& circle.left - circle.radius < rectangle.left - rectangle.width/2 //left side
&& circle.top + circle.radius < rectangle.top + rectangle.height/2 //top
&& circle.top - circle.radius < rectangle.top - rectangle.height/2;
}
left = x & top = y
There are maxX and maxY values which is the width and height of the container.
this is the test:
if(testCollision(circle, rect) == false){
var r = Math.atan2(y - maxY / 2, x - maxX / 2);
vx = -Math.cos(r);
vy = -Math.sin(r);
}
any help is hugely appreciated, thanks!

The way i see it, a circle defined by (x,y,r) coordinates of the center and radius is inside a n axis-aligned rectangle defined by (x,y,w,h) coordinates of the center, the width and the height if the 4 points top,right,bottom,left of the circle are inside the rectangle:
function testCollision(circle, rectangle) {
return circle.x + circle.r < rectangle.x + rectangle.w/2
&& circle.x - circle.r > rectangle.x - rectangle.w/2
&& circle.y + circle.r < rectangle.y + rectangle.h/2
&& circle.y - circle.r > rectangle.y - rectangle.h/2
}
I considered the positive direction of y to be towards the bottom, as is usual in coordinate systems on the web.

Related

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.

How to draw images on circles

I've drawn two circles on the canvas (one with a velocity and one with some controls) and I've been trying to draw an image on each of them, but I have no clue how to do this. I've uploaded the images. Anyone know how? Also, does someone know why my velocity x keys for the red circle aren't working?
Code: http://jsbin.com/vawitiziro/4/edit
Here is the updated jsbin: http://jsbin.com/tibuxezaca/5/edit
1) use context.clip for drawing images in a custom path:
context.clip();
var w = imageObject.width || 0;
var h = imageObject.height || 0;
context.drawImage(imageObject, circle.x - (w / 2), circle.y - (h / 2));
2) update the x velocity in your update function
circle.x += circle.vx;

JavaScript object positioning collision logic

#Pointy:
As I said below, the size variable adjusts the size by a percentage. So this number could have, for example, a value of 1, or 1.5, or 2, etc. The default circle diameter is 100 pixels, and the actual diameter of the circle displayed is size*100. When checking for collision I use size*120 because I want to leave some extra room.
To my understanding, the CSS left and top properties positions the object in terms of its left and top bounds, respectively. Thus if I want to check collision on the right side of the object, I'd have to take the left boundary and add the diameter of the circle (which is given by size*100). When checking the collision on the left side, I take the left bound and subtract size*(1.2-1)*100, because I do not need to account for the diameter of the circle as I am starting from the left bound. The same thing applies to the y-axis.
I hope this was understandable, thanks for helping me out.
Original Post:
I have two circles on my screen, a pink one and a purple one. The positions of these circles (x,y) are randomly determined. Initially, only the purple circle is on the page. One second later, the pink circle will appear. I want to check if these two circles will overlap, and if so, reposition the pink circle.
My code:
if(document.getElementById("purple").style.left+(size*120) > x
&& document.getElementById("purple").style.left-(size*20) < x)
{
if(x + 200 <= rightBound) {
x = x+200;
}
else {
x = x-200;
}
}
else if(document.getElementById("purple").style.top+(size*120) > y
&& document.getElementById("purple").style.top-(size*20) < y)
{
if(y+200 <= bottomBound) {
y = y+200;
}
else {
y = y-200;
}
}
document.getElementById("pink").style.left = x+"px";
document.getElementById("pink").style.top = y+"px";
The size variable modifies the circle size by a percentage. The default circle size is 100 pixels, but in my code I specify 120 pixels to leave a bit of extra room.
The rightBound and bottomBound variables dictate the bounds in which the circle must be randomly positioned. My code checks if moving the circle 200 pixels to the right, or 200 pixels down would still position the circle within the bounds. If so, then this is done, otherwise the circle is moved 200 pixels to the left instead.
By the way, in case it is not clear x and y are the randomly generated values for the position of the pink circle.
With this code, however, I have still experienced instances where the pink and purple circle will overlap. I'm wondering what the problem is with my logic, as in my explanation and thinking I feel that everything should be working fine.
Why not just keep trying new pink circles until you get one that's right? Random numbers are cheap, after all:
http://jsfiddle.net/HVX7d/
while(distance(purple, pink) < 120) {
pink = {
x: int_rand(100, 500),
y: int_rand(100, 500)
};
}
Just use a loop to keep generating new positions for pink until you're sure there are no collisions, using Pythagorean to calculate how far apart the circles are.
Original Answer (incorrectly assumed 100 was a radius):
The issue is that you're specifying 120 in your boundary logic but only moving the circle by 200 when you detect it. That means there's a scenario where the circles originally don't overlap, but when you move them by only 200 you've created an overlap that didn't previously exist.
Change your instances of 200 to 240 and you shouldn't see any more issues.
For example: you have purple at 200, 200 and pink at 310, 200. That's enough to trigger your boundary logic because 310 - 200 is only 110, so you choose to move pink. Let's say you move it up because it's near the bottom boundary, so now it's at 110, 200 and you've created overlap.
I think this should do what you need:
function reposition_check(x, y, rightBound, bottomBound, size, elem) {
var c1_size = 120 * size;
var c2_size = 120 * size;
var c1_x = parseInt(elem.style.left.replace("px", "")) + (c1_size / 2); //center the purple x coordinate
var c1_y = parseInt(elem.style.top.replace("px", "")) + (c1_size / 2); //center the purple y coordinate
var c2_x = x + (c2_size / 2); //center the pink x coordinate
var c2_y = y + (c2_size / 2); //center the pink y coordinate
var d_x = c1_x - c2_x; //distance on the x plane
var d_y = c1_y - c2_y; //distance on the y plane
if (Math.sqrt((d_x * d_x) + (d_y * d_y)) < (c1_size / 2) + (c2_size / 2)) { //Pythagorean theorum to determine if overlapping
console.log("is overlapping");
if (Math.abs(d_x) < (c1_size / 2) + (c2_size / 2)) { //if true, the overlap is on the x plane
//if(x + (c2_size) + 200 <= rightBound && d_x > 0) {
if(d_x > 0) {
x = (x + 200 <= rightBound) ? x + 200 : (x - 200 - d_x);
}else{
x = (x + 200 <= rightBound) ? x - d_x + 200 : (x - 200);
}
return {x: x, y: y};
}
if (Math.abs(d_y) < (c1_size / 2) + (c2_size / 2)) { //if true, the overlap is on the y plane
if(d_y > 0) {
y = (y + 200 <= bottomBound) ? y + 200 : (y - 200 - d_y);
}else{
y = (y + 200 <= bottomBound) ? y - d_y + 200 : (y - 200);
}
return {x: x, y: y};
}
}
return {x: x, y: y};
}
//Here's how you would use it:
var purple = document.getElementById("purple");
var values = reposition_check(111, 1, 1000, 800, 1, purple); //x, y, rightBound, bottomBound, size, element
document.getElementById("pink").style.left = values.x + "px";
document.getElementById("pink").style.top = values.y + "px";

Checking circular area around enemy for player

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.

How to convert a point to polar coordinates in ECMAScript?

I need to turn a click location into a polar coordinate.
This is my current algorithm. Location is the location on the canvas of the click ({x:evt.clientX, y:evt.clientY}), center is the offset of the origin from 0,0. For example, if the circle is centered on 250, 250, center is {x:250, y:250}. Scale is the scale of the radius. For example, if the radius of a circle from the center would normally be 50 and the scale is .5, the radius becomes 25. (it's for zooming in/out)
this.getPolarLocation = function(location){
var unscaledFromCenter = {
x: location.x - center.x,
y: location.y - center.y
};
var angle = this.getAngleOnCircle(unscaledFromCenter);
var dist = Math.sqrt(unscaledFromCenter.x * unscaledFromCenter.x + unscaledFromCenter.y * unscaledFromCenter.y) * this.ds.scale;
return {
angle:angle,
dist:dist,
toString: function(){
return "Theta: ".concat(angle).concat("; dist: ").concat(dist);
}
};
}
this.getAngleOnCircle = function(location){
var x = location.x;
var y = location.y;
if(x == 0 && y > 0)
return Math.PI / 2;
if(x == 0 && y < 0)
return 3 * Math.PI / 2;
if(y == 0 && x > 0)
return 0;
if(y == 0 && x < 0)
return Math.PI;
var angle = Math.atan(y/x);
if(x > 0 && y > 0)
return angle;
if(x < 0)
return Math.PI + angle
return Math.PI * 2 + angle;
}
Screenshots of the issue. The left is what happens zoomed out (and is not supposed to happen). The right is zoomed in (scale >= 1), and is what is supposed to happen.
I'm under the impression that my center coordinates are being shifted slightly off. It seems to work fine for scale >= 1, but not for scale < 1
Source:
circos.html: http://pastie.org/private/cowsjz7mcihy8wtv4u4ag
circos.js: http://pastie.org/private/o9w3dwccmimalez9fropa
datasource.js: http://pastie.org/private/iko9bqq8eztbfh8xpvnoaw
Run in Firefox
So my question is: why doesn't this work?
For some reason, the program automagically works when I close firebug. It doesn't seem to work on Firefox 5, only the version I have (in the 3s somewhere). Either way, I'm scrapping the project for something more object oriented. There's no way the current algorithm could handle a genome. (which is exactly what I'm going to be mapping)
UPDATE:
I figured out the problem... I was measuring the distance from the top left of the page, not the top left of the canvas. Thus, when firebug was enabled, the screen was shifted, making the problems worse. The solution is the use canvas.offsetLeft and canvas.offsetTop to calculate the position on the canvas.

Categories

Resources