Hi I've been looking at cannonball physics and how to implement them in a game.
I'd like to apply gravity to the cannonball and x and y velocity relative to the mouse
cursor position. So that the cannonball travels toward your mouse position.
Further away the mouse cursor (from the cannon) the higher the velocity.
The angle should match the position of the cursor on the canvas.
I've hunted this out for quite a few days now, so now I'm asking.
Who knows of a nice cannonball physics using javascript and canvas?
units = 10,
pixelsPerMeter = stage.width / units,
startlaunch = Math.PI/4,
launchAngle = startlaunch;
bang = {
lastTime: 0,
gravity: 9.81,
applyGravity: function (elapsed) {
bullet1.velocityY = (this.gravity * elapsed) -
(launchVelocity * Math.sin(launchAngle));
},
updateBulletPosition: function (updateDelta) {
bullet1.left += bullet1.velocityX * (updateDelta) * pixelsPerMeter;
bullet1.top += bullet1.velocityY * (updateDelta) * pixelsPerMeter;
},
execute: function (bullet1, time) {
var updateDelta,
elapsedFlightTime;
if(bulletInFlight){
elapsedFrameTime = (time - this.lastTime)/1000;
elapsedFlightTime = (time - launchTime)/1000;
this.applyGravity(elapsedFlightTime);
this.updateBulletPosition(elapsedFrameTime);
}
this.lastTime = time;
}
}
cannon.rotation = cannon.on("tick", function(event) {
var angle = Math.atan2(stage.mouseY - cannon.y, stage.mouseX - cannon.x );
angle = angle * (180/Math.PI);
// The following if statement is optional and converts our angle from being
// -180 to +180 degrees to 0-360 degrees. It is completely optional
if(angle < 0){
angle = 360 - (-angle);}
// Atan2 results have 0 degrees point down the positive X axis, while our image is pointed up.
// Therefore we simply add 90 degrees to the rotation to orient our image
// If 0 degrees is to the right on your image, you do not need to add 90
cannon.rotation = 90 + angle;
});
var fire = false, gravity = 6, vy = 3, vx = 3;
oneback.on("click", function(e) {
bullet1.x = cannon.x;
bullet1.y = cannon.y;
scene1.addChild(bullet1);
fire = true;
//e.preventDefault();
bullet1.rotation = cannon.rotation;
if(fire == false){
bullet1.vx = Math.cos(bullet1.x-stage.mouseX) * this.vx;// used to be... this.bullet_speed'.
bullet1.vy = Math.sin(bullet1.y-stage.mouseY) * this.vy;
}
});
oneback.on("click", function(e){
e.preventDefault();
if(fire == true) {
loc = (stage.mouseX, stage.mouseY);
lastMouse.left = loc.x;
lastMouse.top = loc.y;
deltaX = Math.abs(lastMouse.left - bullet1.left);
deltaY = Math.abs(lastMouse.top - bullet1.top);
launchAngle = Math.atan(parseFloat(deltaY) / parseFloat (deltaX));
launchVelocity = 4 * deltaY / Math.sin (launchAngle) / pixelsPerMeter;
}
});
bullet1.on("tick", function(event){
if(fire == true){
bullet1.y += vy - gravity;
bullet1.x += vx - gravity;
// bullet1.y -= stage.mouseY;
//bullet1.x -= stage.mouseX;
//bullet1.x = direction - gravity;
//bullet1.y = direction - gravity;
}
});
Related
I have 8 handle graphics that represent 5 different states (closed, flow rate 1, flow rate 2, flow rate 3, flow rate 4). Handle graphics 6,7, and 8 also represent flow rate 1, 2, and 3. The images depict a buret handle that rotates around a center point. For each handle state, I need to show the matching texture. I need the user to be able to drag the handle and have it move through the different graphics as the mouse moves around the center point. I also need the user to be able to click on the right side to increase the flow rate and click on the left side to decrease the flow rate.
I have looking into using getBounds() from the image and using that as a hit box but that seems like it won't work because i am removing the old texture and adding a new one depending on the mouse position when dragging. not to mention the images all have similar dimensions.
I have also though about creating 16 hit boxes (2 for each of the 8 images, 1 on the left side for decreasing flow rate, one on the right side for increasing flow rate) and adding and removing the hit boxes with the texture but this seems overly tedious and i don't think it will work with dragging.
Let me know if you have any ideas!
Thanks
Drag a rotating switch
Assuming you get a mouse coord that is relative to the valve eg mouse event pageX, pageY properties.
You can create a function that takes the element, number valve steps, and mouse coords and spits out the values you want.
function getValueSetting(x, y, valveSteps, valveElement) {
const bounds = valveElement.getBoundingClientRect();
const centerX = (bounds.left + bounds.right) / 2;
const centerY = (bounds.top + bounds.bottom) / 2;
const left = x < centerX;
const distance = Math.hypot(x - centerX, y - centerY);
const pos = (Math.atan2(y - centerY, x - centerX) + Math.PI) / (Math.PI * 2);
return {
left,
right: !left,
distance,
pos: Math.round(pos * valveSteps - (valveSteps / 4)),
};
}
If the valve positions step by 1 hour on the clock make valveSteps = 12
Call the function const valveState = getValueSetting(mouseEvent.pageX, mouseEvent.pageY, 12, valveElment);
The object returned will have bools for left and right of the center, and pos will be one of 12 positions starting at 12 o'clock pos = 0 to 11 o'clock pos === 11. The distance property is the distance from the valve.
In the function the angle position subtracts (valveSteps / 4) because Math.atan2 return 0 at the 3 o'clock mark. The subtract (valveSteps / 4) rotate back 1 quarter turn to set 0 at 12 o'clock.
Example
The example draws 5 valve positions.
Move the mouse over the valve handle (red) and the cursor will change to a pointer. Click and drag the mouse to turn the valve. Once dragging the mouse will hold the valve until you release the button.
If not over the handle, but near the valve clicks left and right will message appropriate message.
const size = 64; // size of image
const valveSteps = 12; // total number of angle steps
const valveStep = (Math.PI * 2) / valveSteps; // angle steps in radians
const startAngle = -valveStep * 2; // visual start angle of handle
const valveStart = 1; // starting pos of valve
setTimeout(() => {
const valves = [
createValve(64, startAngle),
createValve(64, startAngle + valveStep),
createValve(64, startAngle + valveStep * 2),
createValve(64, startAngle + valveStep * 3),
createValve(64, startAngle + valveStep * 4),
];
setValve(valves[0]);
var dragging = false;
var currentPos = 0;
var level = 0;
mouse.onupdate = () => {
const valveSetting = getValueSetting(mouse.x, mouse.y, valveSteps, valveA);
if (valveSetting.distance < size && valveSetting.pos - valveStart === currentPos) {
document.body.style.cursor = "pointer";
} else {
document.body.style.cursor = "default";
}
if (mouse.button && (valveSetting.distance < size || dragging)) {
if (valveSetting.distance < size / 2 && valveSetting.pos - valveStart === currentPos) {
if (valveSetting.pos >= valveStart && valveSetting.pos < valveStart + valves.length) {
dragging = true;
}
}
console.clear()
if (dragging) {
let pos = valveSetting.pos - valveStart;
pos = pos < 0 ? 0 : pos > valves.length - 1 ? valves.length - 1 : pos
setValve(valves[pos]);
currentPos = pos;
console.log("Valve pos: " + pos);
} else if (valveSetting.left) {
level --;
console.log("Turn down " + level);
mouse.button = false;
} else if (valveSetting.right) {
level ++;
console.log("Turn up " + level);
mouse.button = false;
}
} else {
dragging = false;
}
}
},0);
function setValve(image) {
valveA.innerHTML = "";
$$(valveA, image); // appends image to element valveA
}
function getValueSetting(x, y, valveSteps, valveElement) {
const bounds = valveElement.getBoundingClientRect();
const centerX = (bounds.left + bounds.right) / 2;
const centerY = (bounds.top + bounds.bottom) / 2;
const left = x < centerX;
const distance = Math.hypot(x - centerX, y - centerY);
const pos = (Math.atan2(y - centerY, x - centerX) + Math.PI) / (Math.PI * 2);
return {
left,
right: !left,
distance,
pos: Math.round(pos * valveSteps - (valveSteps / 4)),
};
}
function createValve(size, angle) {
const canvas = $("canvas", {width: size, height: size});
const ctx = canvas.getContext("2d");
const r = size * 0.4;
const c = size / 2;
ctx.strokeStyle = "red";
ctx.lineCap = "round";
ctx.lineWidth = 8;
ctx.beginPath();
ctx.lineTo(Math.cos(angle) * r + c, Math.sin(angle) * r + c);
ctx.lineTo(-Math.cos(angle) * r * 0.2 + c, -Math.sin(angle) * r * 0.2 + c);
ctx.stroke();
ctx.beginPath();
ctx.arc(c, c, 8, 0, Math.PI * 2);
ctx.strokeStyle = "black";
ctx.lineWidth = 2;
ctx.stroke();
return canvas;
}
// Boiler plate
const $ = (tag, props = {}) => Object.assign(document.createElement(tag), props);
const $$ = (p, ...sibs) => sibs.reduce((p,sib) => (p.appendChild(sib), p), p);
const mouse = {x : 0, y : 0, button : false}
function mouseEvents(e){
mouse.x = e.pageX;
mouse.y = e.pageY;
mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
mouse.onupdate && mouse.onupdate();
}
["down","up","move"].forEach(name => document.addEventListener("mouse" + name,mouseEvents));
.valveContainer {
position: absolute;
top: 30px;
left 30px;
border: 2px solid white;
}
<div id="valveA" class="valveContainer"></div>
I have programed a program with JavaScript and the DOM that shows a ball bouncing in a box; it is a canvas element. The only things that do not work correctly are the angles. This is not noticeable until the angles are small, when it is clear the ball did not bounce back correctly from the box. The angle may be coming to a box gradually, and then bounce very steeply back. This seems like perhaps instead of the angle-in=angle-out, the angle that was being headed from was what was output angle. This would be equivalent of an angle in and its compliment out. The problem seems to happen with only half the types of bounces: it might not happen on one wall coming in a direction, but would on another wall coming in a direction.
: http://i.stack.imgur.com/WviEd.gif
I have posted all the code for ability for the test of the code, and a gradual angle is used, so the problem can be seen, but the angles that are the problem are in the checkAngle function.
<!doctype html>
<script src="code/chapter/15_game.js"></script>
<script src="code/game_levels.js"></script>
<script src="code/chapter/16_canvas.js"></script>
<canvas width="400" height="400"></canvas>
<script>
var cx = document.querySelector("canvas").getContext("2d");
var lastTime = null;
function frame(time) {
if (lastTime != null)
updateAnimation(Math.min(100, time - lastTime) / 1000);
lastTime = time;
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
var x = y = 200//, angle = 2 * Math.PI * Math.random();
var angle = 2 * Math.PI / 40 + Math.PI;
function checkAngle(angle) {
if(x + 10 >= 400) {
if(angle <= Math.PI)
return angle = (Math.PI/2) + ((Math.PI / 2) - reduceAngle(angle));
else if(angle > Math.PI)
return angle = (3 * Math.PI / 2) - ((Math.PI / 2) - reduceAngle(angle));
}else if(x - 10 <= 0) {
if(angle <= Math.PI)
return angle = Math.PI/2 - reduceAngle(angle);
else if(angle > Math.PI)
return angle = 3* Math.PI/2 + (Math.PI/2 - reduceAngle(angle));
}else if(y - 10 <= 0) {
if(angle >= 3 * Math.PI /2)
return angle = Math.PI/2 - reduceAngle(angle);
else if(angle < 3 * Math.PI/2)
return angle = Math.PI - (Math.PI / 2 - reduceAngle(angle));
}else if(y + 10 >= 400) {
if(angle <= Math.PI/2)
return angle = 2*Math.PI - (Math.PI / 2 - reduceAngle(angle));
else if(angle > Math.PI/2)
return angle = Math.PI + (Math.PI / 2 - reduceAngle(angle));
}else
return angle;
}
function reduceAngle(angle) {
if(angle < Math.PI / 2) {
return angle;
}else{
angle = angle - (Math.PI / 2);
return reduceAngle (angle);
}
}
function updateAnimation(step) {
cx.clearRect(0, 0, 400, 400);
cx.lineWidth = 4;
cx.strokeRect(0, 0, 400, 400);
angle = checkAngle(angle);
x += Math.cos(angle) * step * 200;
y += Math.sin(angle) * step * 200;
cx.lineWidth = 2;
cx.beginPath();
cx.arc(x, y, 20, 0, 7);
cx.stroke();
}
</script>
When dealing with reflections in a non-rotated box you don't really need to deal with angles when reflecting. Just define an initial vector based on angle.
var vector = {
x: speed * Math.cos(angle),
y: speed * Math.sin(angle)
};
Then you simply check bounds for x and y separately and inverse the slope-value for each axis:
if (x - radius <= 0 || x + radius>= 400) {
vector.x = -vector.x; // reflect x
}
if (y - radius<= 0 || y + radius> 400) {
vector.y = -vector.y; // reflect y
}
You can always adjust the angle on the fly by adding another vector with the delta-angle.
If your bounce box would not be 0° rotated, then check out this answer for vector-reflection.
For example
Using your code as a basis, this would be implemented like this:
var cx = document.querySelector("canvas").getContext("2d");
var lastTime = null;
var x, y;
// calculate an initial vector
var angle = 20 / 180 * Math.PI; // angle -> radians
var speed = 5;
var vector = {
x: speed * Math.cos(angle),
y: speed * Math.sin(angle)
};
x = y = 200;
function frame(time) {
if (lastTime != null)
updateAnimation(Math.min(100, time - lastTime) / 1000);
lastTime = time;
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
function checkAngle() {
var dlt = 10 + 2 + 4; //include radius and line-widths;
if (x - dlt <= 0 || x + dlt >= 400) {
vector.x = -vector.x; // reflect x
}
if (y - dlt <= 0 || y + dlt > 400) {
vector.y = -vector.y; // reflect y
}
}
function updateAnimation(step) {
cx.clearRect(0, 0, 400, 400);
cx.lineWidth = 4;
cx.strokeRect(0, 0, 400, 400);
x += vector.x; // use our vector
y += vector.y;
checkAngle(); // test for hits
cx.lineWidth = 2;
cx.beginPath();
cx.arc(x, y, 20, 0, 7);
cx.stroke();
}
<canvas width="400" height="400"></canvas>
Judging by what I've seen in your drawings and demonstrations, the angles are being taken off the wrong axis. If approaching at 20 degrees, you'd expect the ball to leave at 180-20 degrees. Instead what it's doing is leaving at 90+20 degrees.
While I can't find the precise place in your code that makes this error, I felt I had to point this out in the hopes that someone can improve upon it.
This is my class
function Missile(drawX, drawY) {
this.srcX = 0;
this.srcY = 0;
this.width = 16;
this.r = this.width * 0.5;
this.height = 10;
this.drawX = drawX;
this.drawY = drawY;
this.img = planeHomingMissile;
this.rotation = -90;
}
Missile.prototype.draw = function () {
ctxHM.save();
ctxHM.translate(this.drawX, this.drawY);
ctxHM.rotate(this.rotation);
ctxHM.drawImage(this.img, -this.r, -this.r);
ctxHM.restore();
}
And this is my JavaScript logic:
function shootHomingMissile() {
//if(!missileOut) {
hMissile = new Missile(player1.drawX + 22, player1.drawY + 32);
missileOut = true;
//}
}
function updateHomingMissile() {
if(missileOut) {
var targetX = 500 - hMissile.drawX;
var targetY = 50 - hMissile.drawY;
//The atan2() method returns the arctangent of the quotient of its arguments, as a numeric value between PI and -PI radians.
//The number returned represents the counterclockwise angle in radians (not degrees) between the positive X axis and the point (x, y)
var rotations = Math.atan2(targetY, targetX) * 180 / Math.PI;
hMissile.rotation = rotations;
var vx = bulletSpd * (90 - Math.abs(rotations)) / 90;
var vy;
if (rotations < 0)
vy = -bulletSpd + Math.abs(vx);
else
vy = bulletSpd - Math.abs(vx);
hMissile.drawX += vx;
hMissile.drawY += vy;
}
}
function drawHomingMissile() {
ctxHM.clearRect(0,0,575,800);
hMissile.draw();
}
I want my missile(facing upwards) to target (500, 50) and face it while targeting.
the movement seems to work but its not facing the target its just keeps rotating but the missile is going to the target.
I'm a starting developer so my code is kind of mess, please help me :)
You are converting your angle from radians to degrees and is using that for your rotation() which require radians.
Try this in your Missile.prototype.draw:
ctxHM.rotate(this.rotation * Math.PI / 180);
(or simply keep the angle in radians at all stages)
I'd like to make a ball bounce angle change each time it hits a wall.
It will change based on how near the middle of the wall it hits...
Right now, I'm hard coding the change in X and Y when it hits a surface... My goal is to get the degrees from current X and Y, apply a change to the degrees (Right now I'm add a random number to the degrees), then calculate the new incrementing values for X and Y. I know how to get the newX and newY, but not how to get the incrementing values.
The green is the starting x y of (5,5)... the blue is the next frame of (4,4).
So I calculated the degrees to be 45 based on that.
Then added a random number to the degrees.
Then, I want to get the new x and y coordinates. So I followed this method...
currX (5) - wallX (0) = distX (5)
currY (5) - wallY (0) = distY (5)
Take the cosine of my angle + random increment, we'll say 55 degrees, * distX
cos(55 degrees) = .5735... .5735 x distX (5) = 2.86
And sin of my angle * distY
sin(55 degrees) = .8191... .8191 x distY (5) = 4.09
newX = cos result (2.86) + originX (5) = 7.86
newY = sin result (4.09) + originY (5) = 9.09
newX, newY = (7.86, 9.09)
Okay... so I have my new coordinates...
But those don't equate to what my new incrementing value of x and y should be based on my angle in incidence.
Code snippet: You can see that I'm hard coding the x,y increments (dragger.x += 2; )
function tick() {
var rand = Math.floor((Math.random()*10)+1);
console.log("ticking..." + rand);
if (dragger.x >= 400-20) {
dragger.xDir = "right";
}
if (dragger.x < 20) {
dragger.xDir = "left";
}
if (dragger.y >= 150-20) {
dragger.yDir = "up";
}
if (dragger.y < 20) {
dragger.yDir = "down";
}
var oldX = dragger.y;
var oldY = dragger.x;
if (dragger.xDir == "left") {
dragger.x += 2;
}
else {
dragger.x -= 2;
}
if (dragger.yDir == "up") {
dragger.y -= 2;
}
else {
dragger.y += 2;
}
//post update...
var newX = dragger.y;
var newY = dragger.x;
var angle = getAngle(newX, oldX, newY, oldY)
angle+=rand;
$('#getAngle').empty();
$('#getAngle').append("bounce angle (degrees): " + angle);
//console.log(xDir);
// update the stage:
stage.update();
}
function getAngle(x2, x1, y2, y1) {
var deltaX = Math.abs(x2-x1);
var deltaY = Math.abs(y2-y1);
var radians = Math.atan2(deltaX, deltaY);
var degrees = radians * (180/Math.PI);
return degrees;
}
This is a pretty interesting problem due to it's specificity.
Making a ball bounce in a programming language can be done quite easily. Like this example.
But clearly, your question is not about 'making it work'; you want explicit control over the coordinates and the angles such that you can alter them for whatever purpose you had in mind.
Because I am quite vulnerable to nerd sniping, I dusted off my geometric skills and came up with the following scrap of pseudocode (I made this from scratch to make sure I have total control):
Intuition
Pseudocode
theta = starting angle
a = current x-coordinate of ball
b = current y-coordinate of ball
quadrant = quadrant-direction to which ball is moving
/> Determine number between 1 and 360: theta
/> Calculate quadrant
.> 0-90 : quadrant 1: horizontal: 90-a vertical: b alpha: 90 - theta
.> 90-180: quadrant 4: horizontal: 90-a vertical: 30-b alpha: theta - 90
.> 180-270: quadrant 3: horizontal: a vertical: 30-b alpha: 270 - theta
.> 270-360: quadrant 2: horizontal: a vertical: b alpha: theta - 270
/> Calculate distance to side |
/> Calculate distance to top/bottom |
.> to side: n(alpha) = horizontal/cos(alpha)
.> to top/bottom: m(alpha) = vertical /sin(alpha)
/> Determine where ball is going to hit (n = side, m = top/bottom)
.> n >= m : bounces at top/bottom
.> m >= n : bounces at side
.> switch (quadrant)
.> 1 : n = right side m = top
.> 2 : n = left side m = top
.> 3 : n = left side m = bottom
.> 4 : n = right side m = bottom
/> Calculate coordinates of hit
/> Define new angle
// Normally, angle of impact = angle of reflection
// Let's define the angle of impact with respect to the origin (0,0)
.> switch (quadrant)
.> 1 :
.> n >= m (at top/bottom) : x = a + vertical*tan(alpha) y = 0 theta = 180-theta
.> m >= n (at side) : x = 90 y = b - horizontal*tan(alpha) theta = 270+alpha
.> 2 :
.> n >= m (at top/bottom) : x = a - vertical/tan(alpha) y = 0 theta = 270-alpha
.> m >= n (at side) : x = 0 y = b - horizontal*tan(alpha) theta = 90-alpha
.> 3 :
.> n >= m (at top/bottom) : x = a - vertical/tan(alpha) y = 30 theta = 270+alpha
.> m >= n (at side) : x = 0 y = b + horizontal*tan(alpha) theta = 90+alpha
.> 4 :
.> n >= m (at top/bottom) : x = a + vertical/tan(alpha) y = 30 theta = 90-alpha
.> m >= n (at side) : x = 90 y = b + horizontal*tan(alpha) theta = 270-alpha
/> Define new coordinates (for reusage of function)
.> a = x
.> b = y
.> (optional) if you would like the angles to differ, enter extra term here:
.> extra = ...
.> theta = theta + extra
Implementing this code will allow you to work with the easiness of degrees and still be able to determine the coordinates.
It works as follows:
First determine the initial position of the ball (a,b) and it's initial direction (theta)
Now the program will calculate:
Where the ball is going to hit
What the coordinates of the ball at impact are
What the new angle of reflection is (this is the part you want to change)
And then it starts over again to calculate the new hit.
In JavaScript, the code would look like this:
Code
var width = 500;
var height = 200;
var extra = 0;
var a;
var b;
var x;
var y;
var angle;
var n;
var m;
var quadrant;
var horizontal;
var vertical;
var alpha;
var side;
var topbottom;
var sides;
var i = 1;
var txt=document.getElementById("info");
txt.innerHTML="x: "+a+"<br>y: "+b+"<br>angle: "+angle+"<br>quadrant: "+quadrant;
function buttonClick()
{
if (i == 1)
{
a = 75;
b = 75;
//determine first angle randonmly
angle = Math.floor((Math.random()*360)+1);;
} else
{
a = xcoord();
b = ycoord();
}
var oldAngle = angle;
angle = findNewCoordinate(a, b, angle);
sides = hitWhere();
var txt=document.getElementById("info");
txt.innerHTML="x: "+a+"<br>y: "+b+"<br>horizontal: "+horizontal+"<br>vertical: "+vertical+"<br>n: "+n+"<br>m: "+m+"<br>angle: "+oldAngle+"<br>alpha: "+alpha+"<br>quadrant: "+quadrant+"<br>side: "+topbottom+side+"<br>"+sides+"<br>"+i;
i++;
}
function findNewCoordinate(a, b, angle)
{
if (angle >= 0 && angle < 90) { quadrant = 1; horizontal = width-a; vertical = b; alpha = (90 - angle); }
else if (angle >= 90 && angle < 180) { quadrant = 4; horizontal = width-a; vertical = height-b; alpha = (angle-90); }
else if (angle >= 180 && angle < 270) { quadrant = 3; horizontal = a; vertical = height-b; alpha = (270-angle); }
else if (angle >= 270 && angle <= 360) { quadrant = 2; horizontal = a; vertical = b; alpha = (angle-270); }
var cosa = Math.cos(alpha * Math.PI / 180);
var sina = Math.sin(alpha * Math.PI / 180);
var tana = Math.tan(alpha * Math.PI / 180);
var tant = Math.tan(angle * Math.PI / 180);
n = horizontal/cosa;
m = vertical/sina;
switch (quadrant)
{
case 1:
if (m >= n) //hit at side
{
y = b - horizontal*tana;
x = width;
angle = 270+alpha;
} else
{
y = 0;
x = a + vertical*tant;
angle = 180-angle;
}
side = "right side"; topbottom = "top";
break;
case 2:
if (m >= n) //hit at side
{
y = b-horizontal*tana;
x = 0;
angle = 90-alpha;
} else
{
y = 0;
x = a - vertical/tana;
angle = 270-alpha;
}
side = "left side"; topbottom = "top";
break;
case 3: side = "left side"; topbottom = "bottom";
if (m >= n) //hit at side
{
x = 0;
y = b + tana*horizontal;
angle = 90+alpha;
} else
{
y = height;
x = a - vertical/tana;
angle = 270+alpha;
} break;
case 4: side = "right side"; topbottom = "bottom";
if (m >= n) //hit at side
{
y = b+horizontal*tana;
x = width;
angle = 270-alpha;
} else
{
y = height;
x = a + vertical/tana;
angle = 90-alpha;
} break;
}
//add extra degrees to the angle (optional)
angle += extra;
context.beginPath();
context.arc(a, b, 5, 0, Math.PI*2, true);
context.stroke();
context.closePath();
context.fill();
drawLine(a,b,x,y);
return angle;
}
Important
Note that there are many more ways to make a bouncing program. But, because I tackled the question geometrically and without 'shortcuts', the unique characteristics of my program make it very easy for you to alter it to your likings:
You can give an extra angle to the bounce angle easily (use var extra).
You can change the movement of the ball at any time (at bounce, after bounce etc.)
You have explicit access to the coordinates of the ball
All units are conventional (in degrees and coordinates; hence easy to understand and intuitive).
Also note that I did not make the program very concise because this simply wasn't my goal. I wanted to create a bouncing ball program that, although lenghty, is an exact realisation of the geometric intuition behind it.
Demo
You can find a demo of my program in this JSFiddle.
Note that the beginning angle is determined randomly. Hence restarting the program will give a different angle.
Well, that's about it.
Good luck with building the rest of your program!
We know that
distance = average velocity x time //if acceleration is constant
Hence
time = distance / average velocity
Applying this knowledge to a two dimensional field (distance) means we have to do two things:
Apply Pythagoras theorem to find distance to new coordinates
Calculate the 'new' velocity
Before we apply the Pythagoras theorem, we have to know the direction of the move:
Now to find the distance to the new coordinates, we apply pythagoras theorem:
Pseudocode
//Change in coordinates
dx = Math.abs(newX - oldX);
dy = Math.abs(newY - oldY);
//Distance to travel
distance = Math.sqrt( Math.pow(dx, 2) + Math.pow(dy,2) );
//Units per increase
// time = distance / average velocity
velocity = ?;
time = distance / velocity;
//Now to find x+= .. and y+= .. we apply our knowledge of direction
//Together with our knowledge of the time it takes
case north east: x += (dx / time); y += (dy / time);
case south east: x += (dx / time); y -= (dy / time);
case north west: x -= (dx / time); y -= (dy / time);
case south west: x -= (dx / time); y += (dy / time);
Now note that the x and y represent the coordinates of the moving ball.
This means that we must repeat x += .. and y += .. value of time times to reach the new coordinate.
Hence you can do something like:
for (int i = 0; i < time; i ++)
{
switch (direction)
{
case "north east": x += (dx / time); y += (dy / time); break;
case "south east": x += (dx / time); y -= (dy / time); break;
case "north west": x -= (dx / time); y -= (dy / time); break;
case "south west": x -= (dx / time); y += (dy / time); break;
}
}
Also note that velocity = ? is yet to be specified by you. You can let it have a constant velocity (friction = 0), or you can implement some kind of model to mimick friction.
I hope this answers your question.
PS. This answer is actually a derivative of my other answer as I already specify direction and pixel distance in my other answer hence the step to x += .. and y += .. is actually pretty small/ straightforward.
depends on the angle it came in at.. so basically for making the ball bounce off the wall, just inverse the angle it came in at, e.g. if using velocity, if it was 3, then make it -3 when it collides with the wall, therefore the ball will bounce off the wall at the same angle as it was before it collided with the wall...
I hope this helps... Good luck
When I click on paper I store the position in lastX and lastY values:
lastX = e.screenX;
lastY = e.screenY;
On mousemove I update the currentX and currentY values:
currentX = e.screenX;
currentY = e.screenY;
Can I determine somehow what is the degree between this two coordinates? I think the x line is the 0 degree. But here stopped my science.
Assuming the origin is the point (lastX, lastY), and zero degrees is the positive x-axis,
the degree to the point (currentX, currentY) would be:
function degreesToPoint(origin, endP){
if(typeof origin != typeof [] or typeof endP != typeof [])
return false;
else {
var slope = {
x: origin[0] - endP[0],
y: origin[1] - endP[1]
};
var degrees = Math.atan(slope.y / slope.x) * 180 / Math.PI;
if(slope.x < 0 && slope.y >= 0){
degrees += 180;
} else if (slope.x < 0 && slope.y < 0) {
degrees -= 180;
}
return degrees;
}
}
After some other google searches:
var radian = Math.atan((currentY-lastY)/(currentX-lastX));
var degree = radian * (180/Math.PI);