I have a 2D animation in Javascript, and I need a sprite to move from one point (x1, y1) to another point (x2, y2) on a set speed and direction. For example,
function update(speedX, speedY){
x1 += speedX;
y1 += speedY;
if("(x1, y1) reach (x2, y2)"){
// do other stuff
}
}
In most cases, speedX and speedY are not equal, nor do they factor evenly into the distance needed to travel for each axis. I calculate the speedX and speedY values using a tangent function to compute the necessary velocity for a given speed and angle.
My question is, is there an algorithm that can do this? I would prefer something efficient since this has to be done 30 times a second, and it's float addition! Thanks!
Usually (I developed some games in the past, nothing special btw) I used to see my sprites as rectangles and then I check for the overlapping of the two sprites using a very simple formula (look at this very good explanation).
Let's suppose you have to check collision between a point and a rectangle. You can apply the same formula, simplified, to check if the point stays between the rectangle area. In the case, read here.
SOLUTION:
Okay, with some work I figured it out. First, I defined a function
function getSign(val) { ... }
that simply returns -1, 0, or 1 depending on whether the number passed in is negative, 0, or positive, respectively. Then, I did the following at the beginning (before any updates happened):
var xSign = getSign(x1-x2);
var ySign = getSign(y1-y2);
Finally, to check whether or not the point is currently at or just past the target (x2, y2), just check with this code:
function update(speedX, speedY){
x1 += speedX;
x2 += speedY;
var curXsign = getSign(x1-x2);
var curYsign = getSign(y1-y2);
if( curXsign != xSign || curYsign != ySign ||
(curXsign == 0 && curYsign == 0)){
// do other stuff
}
}
Basically, if either sign of the relative vector distance between X or Y changes sign, that means that axis went past the point. If both curXsign and curYsign are 0, that means it's right on the target point. Either of these three cases mean that you are on or just went past the target point. Tested and works.
Another possible solution would be to test the distance between the start point and the current location (curDist), and compare it to the distance between the start point and the target point (dist). Once curDist >= dist, you know you are at or past the target point.
It is in the $1F port of the VIC-II:
PRINT PEEK(53279)
:-)
Related
In a 2D game, i need to find if an object is above or under a diagonale line.
Anyone knows how to do this ?
(i use createJS framework)
OK, scrap my previous answer and use line intersection instead. Shoot a line from the point to test straight up. If there is an intersection the point is below, if none, the point is either above or to the side of the line.
To avoid side cases (no pun), extend the original line using interpolation.
Here is a function to do line intersection. To do linear interpolation of the original line simply use some extreme values:
var tx1 = x1 + (x2-x1) * -51000;
var ty1 = y1 + (y2-y1) * -51000;
var tx2 = x1 + (x2-x1) * 53200;
var ty2 = y1 + (y2-y1) * 53200;
Update I was a bit in a hurry this morning so here's a small update. As blindman67 points out, you can use just the d in the linked intersection function and check s/t if they are in the normalized range (or just use cross product - see his answer it that is a better fit).
Build a triangle using the upper coordinates to create a shape. For example, if your line look like:
You can create a shape of if using x2 and y1:
Now simply add the triangle to the path and do a isPointInPath(x, y), if true it's above, if false it's below.
If you need to check below reverse the process.
(wowa! a lot of arrows there... but you'll get the idea :) )
Edge cases (pun intended): if point is very close to one of the ends -> just extend the line, or make polygon extending (x1,y1) up to edge of the area.
Actually, thinking about it: triangles may not be so suitable, rather, use the upper edge of the canvas as a segment of polygon, then the next segment would be vertical line down to the end of the diagonal line, the the final segment from the beginning of the diagonal line to the upper left side of the canvas. I'm just too lazy to redo the graphics but you get the idea..
Use the cross product of the point and the line.
You need to move the whole coord system to the start of the line and then get the cross product of the line and the point. If the result is negative then the point is left of the line, if positive then the point is right of the line, if zero then the point is on the line.
// the point
var px = 100;
var py = 100;
// the line
var lx1 = 20;
var ly1 = 20;
var lx2 = 320;
var ly2 = 120;
// move line end and point so that line start is at 0,0
lx2 -= lx1;
ly2 -= ly1;
px -= lx1;
py -= ly1;
// get cross product
var cross = lx2 * py - ly2 * px;
if(cross < 0){ // point is to the left (anticlockwise)
}else if(cross > 0){ // point is to the right (clockwise)
}else{ // cross must be zero then point is on the line
}
I cant seem to be able to solve a math problem related to collision detection.
I got the basic idea. Find the difference between two opposite x and y values. Calculate the distance by multiplying the two results by itself to get the distance and then a check to see if the two balls collide. Then checking if any part of the angles intersect. Honestly it works somewhat but is still broken.
Problem: When starting the game and I let the ball collide with the visible half then the collison works and when the ball enters the non visible area the collision returns false. At the other hand, when restarting and letting it collide with the non visible area the collision returns true, then false when it enters the visible part of the semicircle.
What could it be? I suspect something due to my rotation. This is honestly weird. Here a demo:
http://jsfiddle.net/2rz296tf/17/
Below the essential formula that makes it "kind of" work:
var dx = a - b;
var dy = a - b;
var semi = Math.atan2(dx, dy)
var distance = Math.sqrt(dx * dx + dy * dy);
var hit = (distance < radiusA + radiusB) && (semi >= 0 && semi < angleB);
You're comparing an angle to a length: semi < radiusB. That doesn't make any sense. You should compare semi to the orientation of your shield, not with its radius.
Looking at your code, you should do something like semi >= angle - Math.PI && semi <= angle instead. You'll need to implement a way to deal with the fact that angles run from -pi to pi and then loop around.
I have a circle in my canvas. The mouse position is calculated in relation to the canvas. I want the circle to move when the mouse is at <=100px distance from it. The minimum distance to start moving is 100px, at 0.5px/tick. It goes up to 2px/tick at 20px distance.
Basically, the closer the mouse is to the circle, the faster the circle should move.
What I have so far moves the circle when distance is less or equal to 100 -- (I'm using easeljs library)
function handleTick() {
distance = calculateDistance(circle, mX, mY);
if (distance<=100) {
circle.x += 0.3;
stage.update();
}
}
What I want
function handleTick() {
distance = calculateDistance(circle, mX, mY);
if (distance<=100) {
circleSpeed = // equation that takes distance and outputs velocity px/tick.
circle.x += circleSpeed;
stage.update();
}
}
So I thought this was a mathmatical problem and posted it on math exchange, but so far no answers. I tried googling several topics like: "how to come up with an equation for a relation" since I have the domain (100, 20) and the range (0.5, 2). What function can relate them?
Thing is I'm bad at math, and these numbers might not even have a relation - I'm not sure what I'm looking for here.
Should I write a random algorithm "circleSpeed = 2x + 5x;" and hope it does what I want? Or is it possible to do as I did - "I want these to be the minimum and maximum values, now I need to come up with an equation for it"?
A pointer in the right direction would be great because so far I'm shooting in the dark.
If I understand it correctly, you want circleSpeed to be a function of distance, such that
circleSpeed is 0.5 when distance is 100.
circleSpeed is 2 when distance is 20.
There are infinity functions which fulfill that, so I will assume linearity.
The equation of the line with slope m and which contains the point (x₀,y₀) is
y = m (x-x₀) + y₀
But in this case you have two points, (x₁,y₁) and (x₂,y₂), so you can calculate the slope with
y₂ - y₁
m = ───────
x₂ - x₁
So the equation of the line is
y₂ - y₁
y = ─────── (x - x₁) + y₁
x₂ - x₁
With your data,
0.5 - 2
y = ──────── (x - 20) + 2 = -0.01875 x + 2.375
100 - 20
Therefore,
circleSpeed = -0.01875 * distance + 2.375
I assume you want a linear relation between the distance and speed?
If so, you could do something like circleSpeed = (2.5 - 0.5(distance/20)).
That would, however set the speed linearly from 0 to 2.5 on the range (100 to 0), but by using another if like this if (distance < 20) circleSpeed = 2 you would limit the speed to 2.0 at 20 range.
It's not 100% accurate to what you asked for, but pretty close and it should look ok I guess. It could possibly also be tweaked to get closer.
However if you want to make the circle move away from the mouse, you also need to do something to calculate the correct direction of movement as well, and your problem gets a tiny bit more complex as you need to calculate speed_x and speed_y
Here is a simple snippet to animate the speed linearly, what that means is that is the acceleration of the circle will be constant.
if distance > 100:
print 0
elseif distance < 20:
print 2
else:
print 2 - (distance -20 ) * 0.01875
Yet other relationships are possible, (other easings you might call them) but they will be more complicated, hehe.
EDIT: Whoops, I’d made a mistake.
I have a test application where I am creating collison detection between to rectangles using Raphael.js.
I am able to get collison detection working properly, but I have to drag it around slooowly.... the issue arises when I move the mouse too quickly. It seems that it is not refreshing quick enough to detect the draggable rect.
The purple square is the only one that drags.
JS Fiddle
I guess my question is how can I improve detection/fix my issue?
Thanks in advance.
Since move is getting called on each pixel move, you don't have time to do much in the way of calculations to keep it smooth. First, I replaced you function for determining overlap with a more standard one:
var rect_collision = function (x1, y1, size1, x2, y2, size2) {
var a = {top: y1, bottom: y1+size1, left: x1, right: x1+size1};
var b = {top: y2, bottom: y2+size2, left: x2, right: x2+size2};
// this is the general way to figure out if two rects are overlapping
return !(a.left >= b.right || a.right <= b.left ||
a.top >= b.bottom || a.bottom <= b.top);
};
This just checks to see if one rectangle is completely to the left, right, top, or bottom of the other one. If it isn't, then they must be overlapping. Since this just gives a true or false value, I still had to figure out which side the collision occurred on.
To figure that out, I broke the collisions into two components, an x collision and a y collision by pretending that first only dx changed then only dy changed. Once I knew which direction was causing the overlap I could then use the change in direction to determine which side the overlap occurred on. For example, if x caused the collision and the previous dx was greater than the current dx then the collision was on the right side.
// check the x and y directions separately
var x_collide = rect_collision(r2_x, r2_y, rectSize, x, r1_y, rectSize);
// see if we are currently overlapping
if (!x_collide) {
// not colliding, update our x position normally
this.attr({x:x});
this.pdx = dx;
}
else {
// we are, stick the moving rect to the correct side of the stationary one
// based on the drag direction that got us stuck
this.attr({x: this.pdx > dx ? r2_x + rectSize + 1 : r2_x - rectSize - 1});
}
I then added a little bit of extra logic to match the functionality that you had which prevent the user from dragging the rectangle directly through the stationary one. Basically I ended up just seeing if the move would place the moving rectangle directly on the opposite side of the stationary one, and if so, prevent it.
I also cleaned up your checks for the border to get rid of all of the Math.min and Math.max calls since you didn't really need those. That's more of a preference thing though since I doubt there were causing much of the performance issues.
You can see the results at http://jsfiddle.net/X7H9G/3/. I'm not sure if this is the best solution, but it seems to do the job.
I wrote a very simple collision detection demo:
http://jsfiddle.net/colintoh/UzPg2/5/
As you can see, the objects sometimes doesn't connect at all but yet the collision is being triggered. The radius for the balls are 10px so the algo triggered the collision whenever the distance between two balls center is less than 20px. I reduced it to 18px for a better visual but the empty collision still happens randomly. Am I doing something wrong?
It looks like you are not using the right formula for distance between two points. See http://www.purplemath.com/modules/distform.htm for a full explanation.
You are doing this:
this.ballCollide = function(balli) {
if (Math.abs((this.x) - (balli.x)) < (2*radius - buffer)) {
if (Math.abs((this.y) - (balli.y)) < (2*radius - buffer)) {
// Do collision
}
}
};
That's a square bounding box, not a circular one. To get a circular bounding box, you can do something like this, based on the formula in the referenced web page:
this.ballCollide = function(balli) {
var deltax = this.x - balli.x;
var deltay = this.y - balli.y;
if (Math.sqrt(deltax * deltax + deltay * deltay) < 2 * radius - buffer) {
// Do collision
}
};
See http://jsfiddle.net/UzPg2/14/ for a working example.
Note that a perfect circular bounding box is a much slower algorithm than a square bounding box approximation.
Following Jarrod Roberson's point (a perfect circle is always inside a perfect square), you'd do that by basically combining your original code with the code I posted, like this (and you could combine them both into one conditional switch if you wanted to):
var deltax = this.x - balli.x;
var deltay = this.y - balli.y;
var dist = 2 * radius - buffer;
if (Math.abs(deltax) < dist && Math.abs(deltay) < dist) {
if (Math.sqrt(deltax * deltax + deltay * deltay) < dist) {
// Do collision
}
}
See http://jsfiddle.net/UzPg2/21/ for a working example (I've left the buffer as your variable is called at 2, but I personally think it looks better with a value of 1).
There are also many other ways you can optimize this for speed if you need to, but Jarrod's suggestion gives you the biggest immediate speed boost.
You're only checking for collisions on two axis, x and y. You need to use Pythagoras' theorem to detect on all axis at the cost of efficiency. For example.
Your algorithm will detect a collision around the point where these two balls are, since if you draw a tangent line along the x or y axis from one ball it goes through the other ball: http://jsfiddle.net/XpXzW/1/
Here you can see where they should actually collide:
http://jsfiddle.net/wdVmQ/1/
If you change your collision detection algorithm to check for perfect collisions (it will be less efficient) you can get rid of your buffer too:
http://jsfiddle.net/ucxER/
(Using Pythagoras' theorem the formula for a collision is:
Math.sqrt((this.x - balli.x)*(this.x - balli.x)
+ (this.y - balli.y)*(this.y - balli.y)) < 2*radius
Also what Jarrod commented is very smart. You can speed it up by using a technique like that. Since the square root is only calculated when the balls are close to each other:
http://jsfiddle.net/bKDXs/