How to change angle when a ball collides with a rectangle? - javascript

If a circle hits a rectangle and needs to bounce, I need to calculate its new direction.
This is what I have
function tick() {
var dx = Math.cos(ball.direction * (Math.PI / 180))*ball_md.speed;
var dy = Math.sin(ball.direction * (Math.PI / 180))*ball_md.speed;
ball.x += dx;
ball.y -= dy;
drawGame(); // refresh board
//console.log(ball);
paddles.some(function(paddle) {
var circle={x:ball.x+ball_md.radius, y:ball.y+ball_md.radius, r:ball_md.radius};
var rect={x:paddle.x, y:paddle.y, w:game_md.paddle.width, h:game_md.paddle.height};
var hit = RectCircleColliding(circle, rect);
if (hit) {
if (Math.floor(ball.y) + ball_md.radius*2 <= paddle.y || Math.ceil(ball.y) >= paddle.y + game_md.paddle.height) { // hit on top or below paddle
ball.direction = 360 - ball.direction;
} else { // hit left or right side
ball.direction = 180 - ball.direction;
}
return true;
}
});
if (ball.y < 0 || ball.y+ball_md.radius*2 >= game_md.height) { // hit top or bottom wall
ball.direction = 360 - ball.direction;
}
if (ball.x < 0 || ball.x+ball_md.radius*2 >= game_md.width) { // hit left or right wall
ball.direction = 180 - ball.direction;
}
}
but it doesn't seem to always work. Does anyone know why?
Cases when it fails. In this case, it zigzags really fast on the paddle surface.
DEMO: https://jsfiddle.net/3ok2farw/2/

You need to treat the corner collision separately. When it hits the corner the velocity of the ball doesn't change perpendicular to either the horizontal or vertical axis, but along the line connecting the center of the ball and the corner. I'm adding a drawing to make it a bit clearer. If the collision is perfectly elastic (no energy loss), then the v_normal component gets replaced by its negative, -v_normal. If the collision is perfectly plastic (maximum energy loss), the exit velocity is just v_tangential. Hope this helps!

Related

Javascript ball bouncing back and forth indefinitely

I'm writing a simple JS function that draws a ball in canvas and just bounces it back and forth between the left and right borders of the screen indefinitely. I currently have it starting on the left side and going right. How I have it currently, it goes to the right but then gets stuck on the right edge; it doesn't bounce back. Any tips?
var dx=4;
var dy=4;
var y=150;
var x=10;
function draw(){
context= myCanvas.getContext('2d');
context.clearRect(0,0,htmlCanvas.width,htmlCanvas.height);
context.beginPath();
context.fillStyle="#0000ff";
context.arc(x,y,50,0,Math.PI*2,true);
context.closePath();
context.fill();
if(x<htmlCanvas.width)
x+=dx
if(x>htmlCanvas.width)
x-=dx;
}
setInterval(draw,10);
Change this:
if(x<htmlCanvas.width)
x+=dx
if(x>htmlCanvas.width)
x-=dx;
to this:
// move the ball
x += dx;
if (x > htmlCanvas.width - 50) {
// if we hit the right edge, get back in bounds
x = htmlCanvas.width - 50;
// and reverse direction
dx *= -1;
} else if (x < 50) {
// if we hit the left edge, get back in bounds
x = 50;
// and reverse direction
dx *= -1;
}

CreateJS: Collision implementing problems

So I have this little javascript labyrinth game I've been working on. The player controls a character using the mouse cursor.
I have a semi-transparent .png bitmap for walls (all in one file), and the player.
The problem here is that I can't seem to make a working collision system.
Here's the essential part of the code:
// Calculate the distances between the cursor and the player
var xDistance = stage.getStage().mouseX - player.x;
var yDistance = stage.getStage().mouseY - player.y;
// Move the player
if (distance > 1) {
player.x += xDistance * easingAmount;
player.y += yDistance * easingAmount;
}
// Check collision for player (bounds)
var mapCollision = collisionMethod(bounds, mapLines, 1);
if (mapCollision) {
if (xDistance > 0 || xDistance < 0)
player.x -= xDistance * easingAmount;
else if (yDistance > 0 || yDistance < 0)
player.y -= yDistance * easingAmount;
}
CollisionMethod is actually ndgmr.checkPixelCollision.
The collision should slide along the walls. This collision only works when the player collides with Y-axis walls.
I am still pretty new with JS, and there's probably a better way to create collision for this kind of situation, so any kind of help or information would be greatly appreciated.
Thanks!

Determine hit on vertical or horizontal wall on a rectangle

Currently i am working on bouncing a ball of a wall, in classic 2D-geometry.
The wall can be hit on both vertical and horizontal sides, and the result of the reflection depends on which side the ball hits.
I have tried some different solutions, but they just make me more confused.
How can i determine whether the ball is hitting a vertical or horizontal side of the wall?
PseudoCode for the overview:
iterate through each wall
if (collision between ball and wall)
determine if vertical/horizontal hit
calculate new velocity for ball
I use this code for collision detection and it works like a charm:
source: Circle-Rectangle collision detection (intersection)
var isCollision = function (_projectile) {
if(direction != undefined){
var circleDistance = {};
circleDistance.x = Math.abs(_projectile.getCenter().x - centerX);
circleDistance.y = Math.abs(_projectile.getCenter().y - centerY);
if (circleDistance.x > (width/2 + _projectile.getRadius())) { return false; }
if (circleDistance.y > (height/2 + _projectile.getRadius())) { return false; }
if (circleDistance.x <= (width/2)) { return true; }
if (circleDistance.y <= (height/2)) { return true; }
var cornerDistance_sq = square(circleDistance.x - width/2) + square(circleDistance.y - height/2);
return (cornerDistance_sq <= (square(_projectile.getRadius())));
}
return false;
};
var square = function(_value){
return _value * _value;
};
Thank you!
update after question update
Asuming the ball has a direction/velocity vector dx,dy and has a radius of r (ball.x, ball.y are the ball positions of the ball center) do sth like this (have used similar in a billiard game, it is basic geometry and physics):
if (ball.x+ball.dx+r > wallV.x) ball.dx = -ball.dx // ball bounces off the
vertical wall
if ( ball.y+ball.dy+r > wallH.y ) ball.dy = -ball.dy // ball bounces off horizontal wall
do similar for opposite walls if needed, since velocity vector changes, the new ball position after bouncing (or anyway) will be ball.x += ball.dx; ball.y += ball.dy;
Bonus if you add some friction factor (meaning the magnitudes of dx dy eventually fade away to zero, the ball eventually stops after following a path)
To solve properly a collision, you can't just test on X => if collision solve and return THEN test on y => if collision solve on y.
You have to check which axe (x OR y) collides first, and there's also the case when the ball hits two walls at the same time.
So you can't just reason with space : you have to deal with time, and compute an ETA -Estimated Time Arrival- for all walls, based on the ball speed (assuming walls are still).
pseudo code :
minETA = a too big number;
colliderList = [];
for (each wall in walls) {
thisWallETA = computeETA(ball, wall);
if (thisWallETA > minETA) continue;
if (thisWallETA < minETA) {
minETA = thisWallETA;
colliderList.length = 0;
colliderList.push(wall);
continue;
}
if (thisWallETA == minETA) {
colliderList.push(wall);
continue;
}
}
Then when the colliderList has only one item => solve on corresponding axe. If it has two items => solve on both axes.
Then increase the current time by minETA, and try again until the currentTime + minETA you found is > to thisCycleTime + thisCycleDt.
If you're interested, i might clarify some things up.
Good luck !

Movement for out-of-bound objects, canvas

I have a space ship in a canvas. It has velocities, ship.vx and ship.vy. When it's 30px away from the canvas borders I set ship.vx & ship.vy to 0 and move the background objects in ship's opposite direction. At this moment the ship is stuck at a point. That's all good. Now if I try to move it left-right(stuck at top/bottom) or top-down(stuck at left/right) it doesn't since it is stuck in the point where vx & vy are set to 0.
If i accelerate in it's opposite direction it takes like 5 seconds to pick it's velocity (around 2), so it's basically at the same point for 5 seconds.
I tried not to set vy to 0 when out of x-axis and vice-versa but the ship keeps moving slowly in the other axis.
So what i'm trying to achieve is the ship'll get stuck when it's 30 px from border, but if i try to move or accelerate in other 3 directions it'll pretend as if it's not stuck.
Any of you know any mechanisms?
Thanks.
function stuckShip(){
if(
(ship.x - ship.width < 0) ||
(ship.x + ship.width > w) ||
(ship.y + ship.height > h) ||
(ship.y - ship.height < 0))
{
ship.vx = 0;
ship.vy = 0;
}
}
function againAndAgain(){
var angle = ship.rotation;
var x = Math.cos(angle);
var y = Math.sin(angle);
var ax = x*thrust,
ay = y*thrust;
ship.vx += ax;
ship.vy += ay;
stuckShip();
ship.draw(context);
}
document.addEventListener('keydown', function(e){
switch(e.keyCode){
case 38:
thrust = 0.35;
break;
case 37:
ship.rotation -= 3;
break;
case 39:
ship.rotation += 3;
break;
}
}
Manipulating the velocity of an object based on it's position on the screen for display-purposes is never the best idea.
Often you use Parent-based systems, so you have one main-container and all objects (including the ship) are child to that container and move relatively to the main-container. Now you can update the container's position, if the ship's global position is in that 30px-band, to make it "lock" on the edge of the screen.
Ha ha, simple, just set ship's position to 31px from border,
if(ship.x <= 30){
ship.x = 30 + 1;
}
What it does is, when the ship is 30px from the left, it'll set ship.x to 31, so it'll never be stuck, just swinging 1px back and forth. i am not sure if it's a perfect solution, but it doesn't pull back the ship for 5 seconds.

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