Reputation: 906
Forgive my complete noobness here as very new to JS. I've created a really simple canvas with one rectangle being controlled by keydownevents and one just moving around the canvas in rAF.
I want to now have if the moving ball hits the users rectangle to collide and bounce back but to bounce at the right angle that it hits not just in reverse like i have tried and failed at.
I know i need to calculate where it hits on the users rect, but I have no idea where to start or how Can someone point me in the right direction or give me a small snippet of what to do?
I'll leave out the Keycode functions for space as they're not needed.
function init() {
canvas = document.getElementById("canvasdemo");
ctx = canvas.getContext("2d");
canvasWidth = canvas.width;
canvasHeight = canvas.height;
drawSquare();
}
function drawSquare() {
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
// user controlled square
ctx.fillStyle = "blue";
ctx.fillRect(rect.x, rect.y, rect.w, rect.h);
requestAnimationFrame(drawSquare);
if (rect.x + rect.vx > canvasWidth - 20 || rect.x + rect.vx < 0)
rect.vx = -rect.vx;
if (rect.y + rect.vy > canvasHeight - 20 || rect.y + rect.vy < 0)
rect.vy = -rect.vy;
rect.x += rect.vx;
rect.y += rect.vy;
// Moving Square 1 (left-right):
requestAnimationFrame(squareTwo);
if (dir1 == "right") {
if (xpos < canvasWidth - 35) {
xpos += 2;
}else{
dir1 = "left";
}
}
if (dir1 == "left") {
if (xpos>0) {
xpos -= 2;
}else{
dir1 = "right";
}
}
}
// Second square
function squareTwo() {
ctx.fillStyle = "black";
ctx.fillRect(xpos, ypos, 35, 35);
ctx.fill();
}
Upvotes: 4
Views: 2608
Reputation:
To calculate the reflection you can do -
First, define the normal as a vector based on the pads angle:
function getNormal(a) {
return {
x: Math.sin(a), // this will be 90° offset from the
y: -Math.cos(a) // incoming angle
}
}
Then use dot-product to calculate the incident angle with the incoming vector:
function reflect(n, v) {
var d = 2 * dot(v, n); // calc dot product x 2
v.x -= d * n.x; // update vectors reflected by
v.y -= d * n.y; // normal using product d
return v
}
// helper, calc dot product for two vectors
function dot(v1, v2) {
return v1.x * v2.x + v1.y * v2.y
}
Then you need a hit-test to trigger the reflection. When hit, reflect the incoming vector on the normal which is 90° tangent to the pad.
A simple demo:
function getNormal(a) {
return {
x: Math.sin(a),
y: -Math.cos(a)
}
}
function reflect(n, v) {
var d = 2 * dot(v, n);
v.x -= d * n.x;
v.y -= d * n.y;
return v
}
function dot(v1, v2) {
return v1.x * v2.x + v1.y * v2.y
}
// --- for demo only ---
var ctx = document.querySelector("canvas").getContext("2d"),
balls = [], padAngle = 0, angleDlt = 0.005;
function Ball() { // a Ball object for demo (replace or use existing)
var me = this;
init();
this.update = function() {
if (this.posX < 0 || this.posX > 500 || this.posY < -4 || this.posY > 150) init();
this.posX += this.x;
this.posY += this.y;
ctx.rect(this.posX - 2, this.posY - 2, 4, 4);
};
function init() {
me.posX = Math.random() * 100 + 200;
me.posY = -4;
me.x = Math.random() - 0.5;
me.y = 1;
me.hit = false;
}
}
// init some balls
for(var i = 0; i < 4; i++) balls.push(new Ball());
// animate demo
(function loop() {
ctx.clearRect(0, 0, 500, 150);
// speeds up frames but preserves some accuracy
for(var subframes = 0; subframes < 3; subframes++) {
ctx.beginPath();
// render pad
padAngle += angleDlt;
if (padAngle < -Math.PI * 0.2 || padAngle > Math.PI * 0.2) angleDlt = -angleDlt;
drawPad(padAngle);
// get normal
var normal = getNormal(padAngle);
// hit test using the pad's path - this is where we do the reflection
for(var i = 0, ball; ball = balls[i++];) {
if (!ball.hit && ctx.isPointInPath(ball.posX, ball.posY)) {
ball.hit = true;
reflect(normal, ball);
}
}
// update balls
for(var i = 0, ball; ball = balls[i++];) ball.update();
}
ctx.fill();
requestAnimationFrame(loop)
})();
function drawPad(angle) {
ctx.translate(250, 100);
ctx.rotate(angle);
ctx.rect(-50, -3, 100, 6);
ctx.setTransform(1,0,0,1,0,0);
}
<canvas width=500></canvas>
Upvotes: 7