detect collision between two circles and sliding them on each other - javascript

I'm trying to detect collision between two circles like this:
var circle1 = {radius: 20, x: 5, y: 5}; //moving
var circle2 = {radius: 12, x: 10, y: 5}; //not moving
var dx = circle1.x - circle2.x;
var dy = circle1.y - circle2.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < circle1.radius + circle2.radius) {
// collision detected
}else{
circle1.x += 1 * Math.cos(circle1.angle);
circle1.y += 1 * Math.sin(circle1.angle);
}
Now when collision is detected I want to slide the circle1 from on the circle2 (circle1 is moving) like this:
--circle1---------------------------------circle2-------------------------
I could do this by updating the angle of circle1 and Moving it toward the new angle when collision is detected.
Now My question is that how can I detect whether to update/increase the angle or update/decrease the angle based on which part of circle2 circle1 is colliding with ?? (circle one comes from all angles)
I would appreciate any help

This will depend a bit on how you are using these circles, and how many will ever exist in a single system, but if you are trying to simulate the effect of two bodies colliding under gravity where one roles around to the edge then falls off (or similar under-thrust scenario), then you should apply a constant acceleration or velocity to the moving object and after you compute it's movement phase, you do a displacement phase where you take the angle to the object you are colliding with and move it back far enough in that direction to reach circle1.radius + circle2.radius.
[edit] To get that redirection after falling though (not sure if you intended this or if it's just your sketch), there is probably going to be another force at play. Most likely it will involve a "stickiness" applied between the bodies. Basically, on a collision, you need to make sure that on the next movement cycle, you apply Normal Movement, then movement towards the other body, then the repulsion to make sure they don't overlap. This way it will stick to the big circle until gravity pulls way at enough of a direct angle to break the connection.
[edit2] If you want to make this smoother and achieve a natural curve as you fall away you can use an acceleration under friction formula. So, instead of this:
circle1.x += 1 * Math.cos(circle1.angle);
circle1.y += 1 * Math.sin(circle1.angle);
You want to create velocity properties for your object that are acted on by acceleration and friction until they balance out to a fixed terminal velocity. Think:
// constants - adjust these to get the speed and smoothness you desire
var accelerationX = 1;
var accelerationY = 0;
var friction = 0.8;
// part of physics loop
circle1.velX += (accelerationX * Math.cos(circle1.angle)) - (friction * circle1.velX);
circle1.velY += (accelerationY * Math.sin(circle1.angle)) - (friction * circle1.velX);
circle1.x += circle1.velX;
circle1.y += circle1.velY;
This way, when things hit they will slow down (or stop), then speed back up when they start moving again. The acceleration as it gets back up to speed will achieve a more natural arc as it falls away.

You could get the tangent of the point of contact between both circles, which would indicate you how much to change your angle compared to the destination point (or any horizontal plane).

Related

Fixing simple collision detection with velocity based 2d movement

In this codePen demo you can move "player" square with arrows, place a light with space and are supposed to be stopped going over blue lines from any direction by being pushed to the opposite direction. "player" uses x and y velocity variables to create movement and multiply them by -1 (+some value) if collision detected.
The problem is that after being pushed away from the wall "player" gets stuck in a position where only moving backward from the wall is possible while appearing stuck on a perpendicular axis to that. (for example - if the wall is on top of player you can move only to bottom and not left or right after hitting the wall)
Theoretically, I would want a smooth sliding collision detection where player stuck at the wall would slowly slide down the left or right side
depending if left or right arrow pressed. (playing around I am able to achieve this but always one direction would "flow" making player slide down certain direction) I thought about using rays or some others way to detect hits, but they seem to require more computational time than just plain approach. Would appreciate any input and any recommendations of building scalable collision detections,
Here is my basic code for movement and collision detection from the demo:
let xVelocity = 0;
let yVelocity = 0;
var blockedMapGrid = [[0,30],[0,50],[0,100],[0,150],[0,200],[0,250],
[50,0],[100,0],[150,0],[200,0],[250,0],[300,0]];
var animate = function() {
if (keyState[37]) {
xVelocity -= 1;
}
if (keyState[38]) {
yVelocity += 1;
}
if (keyState[39]) {
xVelocity += 1;
}
if (keyState[40]) {
yVelocity -= 1;
}
for (var i = 0; i < blockedMapGrid.length; i++) {
if (Math.abs(player.position.x - blockedMapGrid[i][0]) +
Math.abs(player.position.y - blockedMapGrid[i][1]) < 36) {
xVelocity = -xVelocity * 1.2;
yVelocity = -yVelocity * 1.2;
console.log("Blocked by " + blockedMapGrid[i][0])
};
}
player.position.x = player.position.x + xVelocity;
player.position.y = player.position.y + yVelocity;
yVelocity *= 0.80;
xVelocity *= 0.80;
camera.position.x = player.position.x;
camera.position.y = player.position.y;
requestAnimationFrame(animate);
renderer.render(scene, camera);
};
This part of your detector is wrong:
Math.abs(player.position.x - blockedMapGrid[i][0]) +
Math.abs(player.position.y - blockedMapGrid[i][1]) < 36
Basically, here, you approximate distance from player to the point on grid by using added absolute values instead of root of sum of squares. The truth is, you don't need such a complex grid (repeating lines) and distance.
It looks like you are doing Axis-Aligned Bounding Box (AABB) detection. There is plenty of resources on internet how to optimize it.
But general approach would be like this. Your grid array should consist of boxes with (x,y,w,h) measures. Could be thin, long, square, anything.
Let's also assume your player has a bounding box (player.x, player.y, player.w, player.h), then
for (var i = 0; i < grid.length; i++) {
if (player.x < grid[i].x + grid[i].w &&
player.x + player.w > grid[i].x &&
player.y < grid[i].y + grid[i].h &&
player.y + player.h > grid[i].y) {
//collision detected! move player to previous known position
break;
}
}
You can vary, what you do when collision is detected, but finding if two boxes overlap using 4 conditions is the key here.
Update
Another problem arising from the code in the question is "bouncing" or "getting stuck" after collision is detected.
As a rule of thumb, you should never use velocity = -velocity after collision without also making sure the character gets back into the "clear", i.e. player's bounding box is not overlapping with any obstacles. Otherwise you will be stuck in infinite loop collision? -> vel = -vel, pos += vel*t -> collision -> ... with velocity bouncing from negative to positive and back without ever allowing player to get out of the wall.
The easiest way to fix it is to calculate new position of the player in temporary variables first, check if new position is not colliding, and only then make it permanent and call render(), otherwise simply ignore it and render without moving the player.
Another way is to remember last known "good" position and only give back control of the character, when it is returned to this previous position, possibly after animation or a bunch of uncontrollable moves.
There are more elaborate ways, mostly involving some kind of physics emulation to let the character bounce of multiple obstacles, assuming control inputs do not overpower inertia - think of a car on a slippery road or a boat hitting multiple trees. But either way, after you detect collision and before calling "render()" you have to place the character to a physically possible position, or it will be famously "stuck in textures".

N-Body Gravity / Solar System Javascript Simulation

Good day, I am trying to create a simple 2D solar system model in javascript, but am having some trouble understanding how to go about calculating where planets will be for the next frame, aswell as a few other bits which I'll go into detail with soon.
After watching this very nice video and a whole bunch of his others, I made a quick MS paint image to try and simplify my situation.
With the second scene, you can see that the new position is calulated using the velocity, gravitational pull, and the angle between these two 'directions'?
I cannot get my head around how to figure this all out.
Below is a JS fiddle of my code. You'll notice I'm trying my best to use real NASA given data to keep it accurate.
You'll want to look specifically at lines 138 which is where all the calculations for its next move are made.
https://jsfiddle.net/c8eru7mk/9/
attraction: function(p2) {
// Distance to other body
var dx = p2.position.x - this.position.x;
var dy = p2.position.y - this.position.y;
var d = Math.sqrt(dx ** 2 + dy ** 2); // Possibly correct
// Force of attracrtion
this.f = G * (this.mass * p2.mass) / (d ** 2); // Possibly Correct
// Direction of force, If you read it hard enough you should be able to hear my screams of pain
// Not sure if this is correct, most likely not.
var theta = Math.atan2(dy, dx);
var fx = Math.cos(theta) * this.f;
var fy = Math.sin(theta) * this.f;
this.velocity.x += fx / this.mass;
this.velocity.y += fy / this.mass;
this.position.x += this.velocity.x;
this.position.y += this.velocity.y;
}
The problems I'm currently facing are
If I am to use NASA values, the distance between planets is so big, they won't fit on the screen, and I can't simply scale the distances down by multiplying them by 0.0002 or whatever, as that'll mess with the gravitational constant, and the simulation will be completely off.
I have no idea how to caluclate the next position and my brain has imploded several times this past week trying to attempt it several times.
I have no idea on how to check if my configuration data of planets is wrong, or if the simulation is wrong, so I'm pretty much just guessing.
This is also my first time actually coding anything more complex than a button in javascript too, so feedback on code layout and whatnot is welcome!
Many thanks
Using NASA values is not a problem when using separate coordinates for drawing. Using an appropriate linear transfomration from real coordinates to screen coordinatees for displaying does not influence the physical values and computations.
For simulating the motion of a planet with iterative updates one can assume that the gravitational force and the velocity are constant for a small portion of time dt. This factor dt is missing in your conversions from accelration to velocity and from velocity to distance. Choosing an appropriate value for dt may need some experiments. If the value is too big the approximation will be too far off from reality. If the value is too small you may not see any movement or rounding errors may influence the result.
For the beginning let us assume that the sun is always at (0,0). Also for a start let us ignore the forces between the planets. Then here are the necessary formulas for a first not too bad approximation:
scalar acceleration of a planet at position (x,y) by the gravitational force of the sun (with mass M): a = G*M/(d*d) where d=sqrt(x*x+y*y). Note that this is indepent of the planet's mass.
acceleration vector: ax = -a*x/d, ay = -a*y/d (the vector (-x,-y) is pointing towards the sun and must be brought the length a)
change of the planet's velocity (vx,vy): vx += ax*dt, vy += ay*dt
change of the planet's position: x += vx*dt, y += vy*dt

How to make an object bounce of edge

I'm making a simple game in JavaScript and using the Phaser library. I'm new to this, so hopefully this is not a silly question.
I have made it all work perfectly but I would love to know how to get the rocks to bounce of the walls, rather than go through them and appear on the other side.
It has something to do with this function:
I was told by someone to
"If it hits Width: 940 then x = 940 and you start going back 939, i--, etc. Height will continue as normal. Rather than resetting i.e shot.reset(x, y);.
If you hit the bottom or top then do the same to height, keeping width the same."
However, I am not sure how to implement this into the code. I have tried but failed :) Its very frustrating, so any help on this matter would be amazing.
Thanks.
Usually, I create a velocity vector, wich represents the "speed" of my objects.
On each frame, I add that velocity vector to the position vector. When I want my object to move to the opposite direction, I multiply my vector by -1.
Create a vector like that, and when your object collid an edge, multiply it by -1.
You can make a lot of things with this type of vector, such as smooth speed decrease, inspace-like movements etc...
e.g:
//on init
var velocity = {x: 10; y: 10};
var pos = {x: 10; y:10};
//on frame update
pos.x += velocity.x;
pos.y += velocity.y
//on edge collision
velocity.x = velocity.x * -1;
velocity.y = velocity.y * -1;

canvas pendulum animation - canvas translating

I'm trying to make simple pendulum in HTML5 Canvas but I'm stuck. I want to swing it for 25 degrees to the left and to the right, so I calculated I should translate every frame about -3.5 px in y axis (and 3.5 px when swings to the right). I'm using below code
var rotation = Math.PI/180, //rotate about 1deg
translation = -3.5,
counter = 0; //count rotations
function draw() {
var element = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.clearRect(0,0,element.width,element.height);
ctx.translate(0, translation);
ctx.rotate(rotation);
//function draws all objects
objects(element,ctx);
if (counter == 25) {
rotation *= -1;
translation *= -1;
counter = -25;
}
counter += 1;
window.requestAnimationFrame(draw);
}
Everything looks good but when pendulum is changing direction then everything is translating in also x axis and after few seconds disappears from screen.. What is wrong in this code? Or maybe I was miss something in my calculations? My code here https://jsfiddle.net/qskxjzv9/2/
Thanks in advance for your answers.
The problem is that when there is rotation involved, then translation, the x and y's will be translated in a different direction than what may seem logic.
To get around this we don't actually have to involve translation more than using it for placing pivot (point of rotation) and then use absolute rotation based on a different way of calculating the pendulum movement.
For example, this will take care of both the translation problem as well as smoothing the pendulum movement:
Change the draw method to draw the pendulum with origin (0,0) - it's just a matter of changing the initial coordinates so they evolve around (0,0)
Translate to pivot point of screen - this is where the rotation will take place.
Rotate using sin() as a factor - this will create a smooth animation and look more like a pendulum and it will restrict the movement to angle as range is [-1,1]
Use counter to move sin() instead - this acts as a frequency-ish factor (you can later convert this into an actual frequency to say, have the pendulum move n number of times per minute etc.). To keep it simple I have just used the existing counter variable and reduced its step value.
The main code then:
var maxRot = 25 / 180 * Math.PI, // max 25° in both directions
counter = 0,
// these are better off outside loop
element = document.getElementById('canvas');
ctx = element.getContext('2d');
function draw() {
// reset transform using absolute transformation. Include x translation:
ctx.setTransform(1,0,0,1,element.width*0.5,0);
// clear screen, compensate for initial translate
ctx.clearRect(-element.width*0.5,0,element.width,element.height);
// rotate using sin() with max angle
ctx.rotate(Math.sin(counter) * maxRot);
// draw at new orientation which now is pivot point
objects(element, ctx);
// move sin() using "frequency"-ish value
counter += 0.05;
window.requestAnimationFrame(draw);
}
Fiddle
Additional
Thanks to #Blindman67 for providing additional improvements:
To control frequency in terms of oscillations you could do some minor changes - first define frequency:
var FREQUENCY = 3;
Define a function that will do the conversion:
function sint(time) {
return Math.sin(FREQUENCY * time * Math.PI * 0.002); // 0.002 allow time in ms
}
If you now change the draw() method to take a time parameter instead of the counter:
function draw(time) {
...
}
Then you can call rotation like this:
ctx.rotate(sint(time) * maxRot);
you need to translate the origin to the point you want to rotate around:
ctx.translate(element.width / 2, 0);
Then, the rotation as you suggest:
ctx.rotate(rotation);
And finally, translate back:
ctx.translate(- element.width / 2, 0);
See this commented fork of your fiddle.

Algorithm for moving an object horizontally in javascript

I am currently working on a game using javascript and processing.js and I am having trouble trying to figure out how to move stuff diagonally. In this game, there is an object in the center that shoots other objects around it. Now I have no problem moving the bullet only vertically or only horizontally, however I am having difficulty implementing a diagonal motion for the bullet algorithm.
In terms of attempts, I tried putting on my math thinking cap and used the y=mx+b formula for motion along a straight line, but this is what my code ends up looking like:
ellipse(shuriken.xPos, shuriken.yPos, shuriken.width, shuriken.height); //this is what I want to move diagonally
if(abs(shuriken.slope) > 0.65) {
if(shuriken.targetY < shuriken.OrigYPos) {
shuriken.yPos -= 4;
} else {
shuriken.yPos += 4;
}
shuriken.xPos = (shuriken.yPos - shuriken.intercept)/shuriken.slope;
} else {
if(shuriken.targetX < shuriken.OrigXPos) {
shuriken.xPos -= 4;
} else {
shuriken.xPos += 4;
}
shuriken.yPos = shuriken.slope * shuriken.xPos + shuriken.intercept;
}
The above code is very bad and hacky as the speed varies with the slope of the line.
I tried implementing a trigonometry relationship but still in vain.
Any help/advice will be greatly appreciated!
Think of it this way: you want the shuriken to move s pixels. If the motion is horizontal, it should move s pixels horizontally; if vertical, s pixels vertically. However, if it's anything else, it will be a combination of pixels horizontally/vertically. What's the correct combination? Well, what shape do you get if you project s distance in any direction from a given point? That's right, a circle with radius s. Let's represent the direction in terms of an angle, a. So we have this picture:
How do we get the x and the y? If you notice, we have a triangle. If you recall your trigonometry, this is precisely what the sine, cosine, and tangent functions are for. I learned their definitions via the mnemonic SOHCAHTOA. That is: Sin (a) = Opposite/Hypotenuse, Cos(a) = Adjacent/Hypotenuse, Tan(a) = Opposite/Adjacent. In this case, opposite of angle a is y, and adjacent of angle a is x. Thus we have:
cos(a) = x / s
sin(a) = y / s
Solving for x and y:
x = s * cos(a)
y = s * sin(a)
So, given the angle a, and that you want to move your shuriken s pixels, you want to move it s * cos(a) horizontally and s * sin(a) vertically.
Just be sure you pass a in radians, not degrees, to javascript's Math.sin and Math.cos functions:
radians = degrees * pi / 180.0
This may be why your trigonometric solution didn't work as this has bitten me a bunch in the past.
If you know the angle and speed you are trying to move at, you can treat it as a polar coordinate, then convert to cartesian coordinates to get an x,y vector you would need to move the object by to go in that direction and speed.
If you don't know the angle, you could also come up with the vector by taking the difference in X and difference in Y (this I know you can do as you are able to calculate the slope between the 2 points). Then take the resulting vector and divide by the length of the vector to get a unit vector, which you can then scale to your speed to get a final vector in which you can move your object by.
(This is what probably what kennypu means by sticking with vectors?)

Categories

Resources