I have a small circle which is inside a bigger circle. The small Circle is flying around, and if the small circle comes to the border of the big circle it should collide. I have almost managed to do so, but it still does not work perfect. Sometimes the circle collides just before the border, and sometimes right after the border. This is my code:
if (!(Math.pow((xSmallCircle + radiusSmallCircle) - (xBigCircle), 2) + Math.pow((
ySmallCircle + radiusSmallCircle) - yBigCircle, 2) < Math.pow(radiusBigCircle + 10, 2))) {
xVelocity *= -1;
yVelocity *= -1;
} else if (!(Math.pow((xSmallCircle - radiusSmallCircle) - (xBigCircle), 2) + Math.pow((
ySmallCircle - radiusSmallCircle) - yBigCircle, 2) < Math.pow(radiusBigCircle + 10, 2))) {
xVelocity *= -1;
yVelocity *= -1;
} else if (!(Math.pow((xSmallCircle + radiusSmallCircle) - (xBigCircle), 2) + Math.pow((
ySmallCircle - radiusSmallCircle) - yBigCircle, 2) < Math.pow(radiusBigCircle + 10, 2))) {
xVelocity *= -1;
yVelocity *= -1;
} else if (!(Math.pow((xSmallCircle - radiusSmallCircle) - (xBigCircle), 2) + Math.pow((
ySmallCircle + radiusSmallCircle) - yBigCircle, 2) < Math.pow(radiusBigCircle + 10, 2))) {
xVelocity *= -1;
yVelocity *= -1;
}
Any ideas why this does not work?
It's because you're only checking for collision at four points, which are actually all outside the inner circle. Try a simpler, and mathematically correct collision detection mechanism.
var sep = Math.sqrt(
Math.pow(xSmallCircle-xBigCircle, 2)
+ Math.pow(ySmallCircle-yBigCircle, 2)
);
if( sep + radiusSmallCircle + borderWidthSmallCircle >= radiusBigCircle ){
//You have a collision Here
}
The logic here is that if you calculate the length of the line segment between the centers of each circle, and add the small circle's radius, you'll get the radius of the smallest circle which is (a) concentric to the the outer circle, and (b) completely encircling the smaller circle. If that circle is larger or the same size as your outer circle, you have a collision.
If you still have inexact collisions, it's probably a rounding error.
Related
I am making an online game and I use HTML5 canvas and Javascript to build my game.
I have some shapes and one ball that moves and when collided the shapes, the shapes should disapear.
the shapes and the ball are image and my big problem is how to detect collision between ball and shape because my shapes are rectangle, triangle, polygon and ... e.g.: This Shape
this is my code to detect collision but it just works for rectangles:
function collide(r1, r2) {
var dx = (r1.x + r1.width / 2) - (r2.x + r2.width / 2);
var dy = (r1.y + r1.height / 2) - (r2.y + r2.height / 2);
var width = (r1.width + r2.width) / 2;
var height = (r1.height + r2.height) / 2;
var crossWidth = width * dy;
var crossHeight = height * dx;
var collision = 'none';
if (Math.abs(dx) <= width && Math.abs(dy) <= height) {
if (crossWidth > crossHeight) {
collision = (crossWidth > (-crossHeight)) ? 'bottom' : 'left';
} else {
collision = (crossWidth > -(crossHeight)) ? 'right' : 'top';
}
}
return (collision);
}
Just get your collision code to work for triangles and then it will work for all shapes! Here is the formula, in Javascript code for doing just that, taken from(mostly) this question.
function sign(p1, p2, p3) {
return (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1]);
}
function inTriangle(point, trip1, trip2, trip3) {
var b1 = sign(point, trip1, trip2) < 0.0;
var b2 = sign(point, trip2, trip3) < 0.0;
var b3 = sign(point, trip3, trip1) < 0.0;
return ((b1 == b2) && (b2 == b3));
}
With this code you just have to run, inTriangle(p, v1, v2, v3), where p is the point your testing for, in collision detection that would be each of the corners of a shape, with a ball or circle just test some points on the circumference, v1; v2; v3 are the three points of a triangle your testing for.
Keep in mind that it is more efficient to test for the collision of a rectangle so if you have a shape that can be divided up like a rectangle, you should do so, instead of dividing it into triangles.
I nailed a collision detection system, but now i'm trying to make a system that makes it so that, when the function is called, the items in the parameters aren't able to touch each other. I'm fairly new to JavaScript and its the first language ive really tried to learn. The way my rectangles are being drawn is so that x and y are in the middle of the rect, rather than in the top left corner of it. The system i've built technically works, but only if its a perfect square, for some reason rectangles are buggy and that i cant figure out. Even when it is a perfect square though, it seems clunky and really bad compared to what i'm used to, which is code.org's item1.collide(item2); which works perfectly and exactly how I want, but i cant find the code behind that. I am using p5.js, by the way.
Here is how i'm drawing my rectangles:
rect(this.x-this.width/2,this.y-this.height/2,this.width,this.height);
And here is the blockCollision function I currently have:
function blockCollision(a,b){
if(a.x+a.width/2 > b.x-b.width/2 &&
a.x-a.width/2 < b.x+b.width/2 &&
a.y-a.height/2 < b.y+b.height/2 &&
a.y+a.height/2 > b.y-b.height/2) {
if(a.x<b.x-b.width/2) a.x=b.x-b.width/2-a.width/2;
if(a.x>b.x+b.width/2) a.x=b.x+b.width/2+a.width/2;
if(a.y<b.x-b.height/2) a.y=b.x-b.height/2-a.height/2;
if(a.y>b.x+b.height/2) a.y=b.x+b.height/2+a.height/2;
}
}
Also, here is the entire code download if It helps: https://drive.google.com/open?id=0B-F5CHOIQvvGVlR3Njd1M1NLS1E
I presume by "block collision", you mean that you would like to move one of the blocks in the "opposite" direction of the collision so that the collision does not happen.
What you need to do is essentially determine the direction of the collision (top/bottom/left/right), and move the offending block away:
function blockCollision(a, b) {
// Assuming (0, 0) is the top left corner
const aTop = a.y - a.height / 2;
const aBottom = a.y + a.height / 2;
const aLeft = a.x - a.width / 2;
const aRight = a.x + a.width / 2;
const bTop = b.y - b.height / 2;
const bBottom = b.y + b.height / 2;
const bLeft = b.x - b.width / 2;
const bRight = b.x + b.width / 2;
const collisions = [];
if (aTop <= bTop && aBottom >= bTop) {
// a is above B, potential collision
if (aLeft <= bRight && bLeft <= aRight) {
// Prevent collision, push a to the top
a.y = bTop - (a.height / 2) - 1;
}
}
if (aBottom >= bBottom && aTop <= bBottom) {
// a is below B, potential collision
if (aLeft <= bRight && bLeft <= aRight) {
// Prevent collision
a.y = bBottom + (a.height / 2) + 1;
}
}
if (aLeft <= bLeft && aRight >= bLeft) {
// a is left of B, potential collision
if (aTop <= bBottom && bTop <= aBottom) {
// Prevent collision, push a to the left
a.x = bLeft - (a.width / 2) - 1;
}
}
if (aRight >= bRight && aLeft <= bRight) {
// a is right of B, potential collision
if (aTop <= bBottom && bTop <= aBottom) {
// Prevent collision, push a to the right
a.x = bRight + (a.width / 2) + 1;
}
}
}
See codepen example.
Essentially, what I'm doing is placing a bunch of random width/height rects onto a grid (near the center of it), then pushing them all away from each other until none of them overlap. I have another version where I check for collisions before I place them on the grid, but that's not what I'm going for in this build.
I'm wondering if someone can explain a better way to go about this?
What I've tried so far is something similar to:
let r1/r2 = rect1/rect2
do {
var ox = Math.max(0, Math.min(r1.x + r1.w, r2.x + r2.w) - Math.max(r1.x, r2.x)),
oy = Math.max(0, Math.min(r1.y + r1.h, r2.y + r2.h) - Math.max(r1.y, r2.y)),
dx = r2.x - r1.x,
dy = r2.y - r1.y;
if (ox > 0 && oy > 0) {
if (ox >= oy) {
if (r1.x >= r2.x && Math.random() > .1) {
r1.x += ox;
spaced = true;
continue;
} else {
r1.x -= ox;
spaced = true;
continue;
}
} else {
if (r1.y >= r2.y && Math.random() > .1) {
r1.y += oy;
spaced = true;
continue;
} else {
r1.y -= oy;
spaced = true;
continue;
}
}
}
} while ( /* stuff */ )
the random is only there because I will run into times when a certain rect gets pushed back and forth and never gets free and causes an infinite loop. This way is horribly inefficient however.
I believe what your trying to accomplish is known as a packing problem http://en.wikipedia.org/wiki/Packing_problem. If you just search stack overflow for "2d bin packing" you should be able to find all you need to roll a much more efficient algorithm.
I currently have a function which detects the direction of the mouse enter. It returns an integer (0-3) for each section as illustrated below.
I'm trying to figure out how to alter this function to produce an output based on this illustration:
Basically, I just want to figure out if the user is entering the image from the left or the right. I've had a few attempts at trial and error, but I'm not very good at this type of math.
I've set up a JSFiddle with a console output as an example.
The function to determine direction is:
function determineDirection($el, pos){
var w = $el.width(),
h = $el.height(),
x = (pos.x - $el.offset().left - (w/2)) * (w > h ? (h/w) : 1),
y = (pos.y - $el.offset().top - (h/2)) * (h > w ? (w/h) : 1);
return Math.round((((Math.atan2(y,x) * (180/Math.PI)) + 180)) / 90 + 3) % 4;
}
Where pos is an object: {x: e.pageX, y: e.pageY}, and $el is the jQuery object containing the target element.
Replace return Math.round((((Math.atan2(y,x) * (180/Math.PI)) + 180)) / 90 + 3) % 4; with return (x > 0 ? 0 : 1);
Another option which I like better (simpler idea):
function determineDirection($el, pos){
var w = $el.width(),
middle = $el.offset().left + w/2;
return (pos.x > middle ? 0 : 1);
}
I'm working on a top down shooter, and basically the character starts in the middle of the screen, inside a rect (Safe Zone). The character isn't static, the scene is. He can walk around, inside the safe zone. As soon as the character walks out of this zone, the statics switch over ... the character is static, and the scene is moving around him.
The only problem with this is that I can't walk back into the safe zone, allowing my statics to switch over again.
So I'm forever stuck outside the zone. All I'm doing is checking to see whether my character position is 'within' a certain value (which is the rect), if he's out - then my KeyControls then affect the Map, not the character.
So this is my boundary (Safe Zone) checker:
//Walking Window Boundaries
var boundarySizeX = 400;
var boundarySizeY = 200;
ctxWalkBoundary.fillStyle = "grey";
ctxWalkBoundary.fillRect(gameWidth/2 - boundarySizeX/2, gameHeight/2 - boundarySizeY/2, boundarySizeX, boundarySizeY);
ctxWalkBoundary.clearRect((gameWidth/2 - boundarySizeX/2) + 2, (gameHeight/2 - boundarySizeY/2) + 2, (boundarySizeX) - 4, (boundarySizeY) -4 );
var paddingLeft = (gameWidth - boundarySizeX) / 2;
var paddingRight = gameWidth - ((gameWidth - boundarySizeX) / 2) - this.charWidth;
var paddingTop = (gameHeight - boundarySizeY) / 2;
var paddingBottom = gameHeight - ((gameHeight - boundarySizeY) / 2) - this.charHeight;
var paddingY = (gameHeight - boundarySizeY) / 2;
if(this.drawX > paddingLeft && this.drawX < paddingRight && this.drawY > paddingTop && this.drawY < paddingBottom){
inBoundary = true;
}
else{
inBoundary = false;
console.debug("Out Of Boundary!");
}
And this is my KeyChecker:
//UP
if(this.isUpKey){
//Character movement
if(inBoundary){
this.drawX = this.drawX + this.speed * Math.cos((this.characterRotation));
this.drawY = this.drawY + this.speed * Math.sin((this.characterRotation));
}
else{
mapPositionX = mapPositionX - this.speed * Math.cos((this.characterRotation));
mapPositionY = mapPositionY - this.speed * Math.sin((this.characterRotation));
}
My character always faces my mouse (rotates). So every time the user pressed W, or Up - the character will always walk towards the mouse position.
Any ideas how I can get back into the zone?
----- Update -----
I guess I need to somehow check if I'm still facing outside the safe zone - if not, then reverse he statics.
Just separate two things: map and view.
Map is your level, you keep there objects with coordinates.
View is part of map you see on screen.
View has 4 properties: x, y, widht and height, where widht and height most likely is your canvas size.
If your game start with view on map point (0,0) in the middle of screen, then your view (x,y) coordinates should be (-view.width/2, -view.height/2).
How to draw your character and objects in a view?
In first place, draw only thing that are in the view rectangle.
So loop over all objects and check if
object.x >= view.x && object.x <= view.x + view.width && object.y >= view.y && object.y <= view.y + view.height
(you probably should take into account objects boundaries too).
If object is in view area then draw it at position (object.x - view.x, object.y - view.y).
And that's all about drawing things.
Moving character and view area with him.
Now when your character collides with boundary, in example (colliding with right border)
character.x >= view.x + view.width
then move view to the right by incrementing view.x with some value (that might be character.width/2).
-- UPDATE --
I see that you are not using OOP in your game (actually you are because everything in JS is an object, but you are not using it on purpose).
OOP in JS is a lot of explaining, so I'll try to make it short.
You can make objects like your Character, Map and View using JSON format.
character = {
x: 0,
y: 0,
xspeed: 0,
yspeed: 0,
speed: 0,
radius: 20,
}
map = {
objects: [
{sprite: 'tree.jpg', x: 100, y: 50},
{sprite: 'stone.jpg', x: 20, y: 30},
],
}
view = {
width: canvas.width,
height: canvas.height,
x: -this.width/2,
y: -this.height/2,
}
These are objects that you can use like in your functions like that:
for (var i=0; i++, i<map.objects.length) {
if (map_obj.x >= view.x && map_obj.x <= view.x + view.width && map_obj.y >= view.y && map_obj.y <= view.y + view.height) {
var map_obj = map.objects[i];
draw_sprite(map_obj.sprite, map_obj.x - view.x, map_obj.y - view.y);
}
}
It's not the best way, but it's still much better than yours right now. When you understand what OOP is about you will make it better for your own.
The problem here is that you're waiting for the character to go out of bounds, then moving the map instead. But the flag has already been tripped, and now the character movement is static no matter what direction you go in, because you're already out of bounds.
You could instead detect when a character is going to cross the boundary and prevent it by moving the map instead:
//UP
if(this.isUpKey){
// save the x and y offset to prevent needless recalculation
var xOffset = this.speed * Math.cos(this.characterRotation),
yOffset = this.speed * Math.sin(this.characterRotation);
//Character movement
if( boundaryCheck(xOffset, yOffset) ){
this.drawX = this.drawX + xOffset;
this.drawY = this.drawY + yOffset;
}
else{
mapPositionX = mapPositionX - xOffset
mapPositionY = mapPositionY - yOffset;
}
then boundaryCheck takes the x and y delta's and figures out if they're still in bounds. If the character will still be in bounds, return true and the character will move, otherwise the map will move.
function boundaryCheck(xOffset, yOffset){
// variables set and other stuff done...
if(this.drawX + xOffset > paddingLeft && this.drawX + xOffset < paddingRight && this.drawY + yOffset > paddingTop && this.drawY + yOffset < paddingBottom){
return true;
}
else{
console.debug("Out Of Boundary!");
return false;
}
};
This way you don't have to figure out whether an out of bounds character is moving toward the boundary or not. Instead, you pre-determine where the character is going, and adjust accordingly, always keeping him in boundaries.
Without full code this isn't testable, of course, but I think it should work with what you've given.