Smoothest following animation in canvas - javascript

I want to make a coordinate (coord1) follow another coordinate (coord2) as smoothly as possible with these following conditions:
coord2 is able to move.
coord1 follows coord2 in the smoothest way possible (arc fashion).
coord1 follows at a constant speed.
I have an example here but it only succeeds condition 1 and 3 not 2. In the example, you can move the ball with your arrow keys.
Click here to go to the example
Here is my code for following:
Obstacle.prototype.follow = function () {
this.y += this.vSpeed
this.x += this.hSpeed
if (this.x < ball.x - 9) {
this.hSpeed = 1;
}
if (this.x > ball.x - 10) {
this.hSpeed = -1;
}
if (this.y > ball.y - 10) {
this.vSpeed = -1;
}
if (this.y < ball.y - 9) {
this.vSpeed = 1;
}
}
Anyone have a solution that succeeds all three conditions?

To follow an object create a vector from tone object to the other. A vector has direction and length. The length is the speed and the direction is where it is going.
I have forked your fiddle to show this working. The only change is in the follow function. https://jsfiddle.net/blindman67/ksu518cg/2/
// obj one
var x1 = 100;
var y1 = 100;
// object to follow
var x2 = 300;
var y2 = 200;
Every animation frame the distance
var dist = Math.sqrt(Math.pow(x2-x1,2) + Math.pow(y2-y1,2)); // distance
Create a vector with length 1 pixel
var dx = (x2-x1)/dist;
var dy = (y2-y1)/dist;
Multiply by the speed you want to move
dx *= speed;
dy *= speed;
THen add to the objects position
x2 += dx;
y2 += dy;

Related

Gravity implementation causing Obj to continue through object

I am having an issue with my implementation of gravity and collision on many objects on a canvas...I understand where I have gone wrong I just cannot for the life of me think of a solution to fix it.
I am checking if dropped objects reach a y position on the canvas and then reverse the velocity * friction to bounce the object...This works fine with one object or many objects. However, because of my conditional when the objects stack on top of each other the velocity never decreases as it is always being increased by the gravity, meaning the objects slowly pass through each other after they have finished bouncing around.
I have tried checking if "this" object is colliding however this will not work as the objects should be able to bounce until friction causes the velocity to decrease or increase to zero(or very close in this case).
Here is the function for gravity.
gravity() {
//If the objects y + the height and delta are greater than the canvases height reverse the velocity * friction also apply
//same friction to x to stop the objects from sliding
if (this.y + this.height + this.delta_y >= this.canvas.height) {
this.delta_y = -this.delta_y * this.FRICTION;
this.delta_x = this.delta_x * this.FRICTION;
//If the delta_y does not push the objects y over the canvas height increase the delta by the gravitational pull
} else {
this.delta_y += this.GRAVITY;
}
//Apply reversed velocity over x - works because no constant gravity being added
if (this.x + this.width >= this.canvas.width || this.x - this.width <= 0) {
this.delta_x = -this.delta_x * this.FRICTION;
}
//update the x and y coordinates with the new delta value
this.x += this.delta_x;
this.y += this.delta_y;
//set the new centre for collision.
this.center = {
"x": this.x + this.width / 2,
"y": this.y + this.height / 2
};
}
I also thought I maybe able to run a timer on the y variable for a few milliseconds and if it has not changed outside of a bound then I could stop movement. However, this is not ideal and im not sure if a timeout would be good as javascript is single threaded? I would be at home in python but I am struggling here.
I have enclosed the calling function here
gameLoop(timeStamp) {
// Calculate how much time has passed // was thinking I could use this to check y after some time
//but stopped and decided to post to stack
let secondsPassed = (timeStamp - this.oldTimeStamp) / 1000;
this.oldTimeStamp = timeStamp;
if (this.gameObjects.length > 0) {
//update all objects and pass all objects to update to check for collision
for (let i = 0; i < this.gameObjects.length; i++) {
this.gameObjects[i].update(this.gameObjects);
}
//clear the canvas ready for next draw
this.clearCanvas();
//draw the next iteration of the gameobjects
for (let i = 0; i < this.gameObjects.length; i++) {
this.gameObjects[i].draw();
}
}
window.requestAnimationFrame((timeStamp) => this.gameLoop(timeStamp));
}
Finally here is the update function for completeness
update(gameObjects) {
//impose gravity on this object
this.gravity();
//loop through objects and check for collision
for (let i = 0; i < gameObjects.length; i++) {
// if this object is the same as the iterated object skip it
if (this === gameObjects[i]){continue;}
//test variable to check collision on this object
this.colliding = this.collisionDetection(gameObjects[i]);
//if colliding resolve the collision
if (this.colliding) {
this.resolveCollision(this, gameObjects[i]);
}
}
}
Thank you, vanilla JS only please.
I think the problem is that, when the object is out of bounds, you reduce its speed, so it doesn't have enough speed to come back in 100%.
Maybe you can apply your "friction" in the next update, not the one with the collision.
For anyone that gets stuck on this I was able to get it to working by passing the game objects to the gravity method and then adding a check in the conditional to see if a collision has occured. Its not perfect and there are a few other things to sort but for now its progress.
code
gravity(objects) {
//initialize collision to false
let thisCollide = false;
//loop through all objects
for(let i = 0; i < objects.length; i++){
//If the iteration is on this object then skip
if(this === objects[i]) continue;
//if there is a collision && that collision is on the top of the current object
if(this.collisionDetection(objects[i]) && (this.y - objects[i].y <= 1)){
//set the collision to true and break
thisCollide = true;
}
}
//If the objects y + the height and delta are greater than the canvases height or the object has collide on the top
//reverse the velocity * friction also apply same friction to x to stop the objects from sliding
if (this.y + this.height + this.delta_y >= this.canvas.height || thisCollide) {
this.delta_y = -this.delta_y * this.FRICTION;
this.delta_x = this.delta_x * this.FRICTION;
//If the delta_y does not push the objects y over the canvas height increase the delta by the gravitational pull
} else {
this.delta_y += this.GRAVITY;
}
//Apply reversed velocity over x - works because no constant gravity being added
if (this.x + this.width >= this.canvas.width || this.x - this.width <= 0) {
this.delta_x = -this.delta_x * this.FRICTION;
}
//update the x and y coordinates with the new delta value
this.x += this.delta_x;
this.y += this.delta_y;
//set the new centre for collision.
this.center = {
"x": this.x + this.width / 2,
"y": this.y + this.height / 2
};
}
I would recommend this order:
Apply forces
Update Velocity
Update Position
In your case:
//apply acceleration
this.delta_y += this.GRAVITY;
//apply friction
this.delta_x -= this.delta_x * this.FRICTION;
this.delta_y -= this.delta_y * this.FRICTION;
//update position
this.x += this.delta_x;
this.y += this.delta_y;
//collision
if (this.x + this.width >= this.canvas.width || this.x - this.width <= 0) {
this.delta_x = -this.delta_x;
}
if (this.y + this.height >= this.canvas.height) {
this.delta_y = -this.delta_y;
}

Move canvas image across circular path

I am currently working on a 2D javascript game and I am doing the movement mechanics right now. I want players to be able to move forward and backward, towards or away from the mouse, while also having the ability to strafe.
I got the mouse following down pretty well but I am stuck on how to implement the strafing. I need my player to move along a dynamic circular path that changes sizes depending on how far away the player is from the mouse.
ex: If the mouse was the red X, I want to move the player along the green circular path.
This path of course will be changing size based on how far away the player is from the mouse.
I am updating the players position by moving whenever the movement keys are pressed so I am really looking for an equation to move the player in the correct circular path that can be implemented in a "position updated every second" sort of way.
I know that for a circular path, the coordinates can be found by:
x = centerX * radius * Math.cos(theta);
y = centerY * radius * Math.sin(theta);
But I am having trouble implementing. Here is some of my framework, but I am afraid all the solutions I have tried haven't even gotten me close so I will not post the broken math I have already deleted
Player.prototype.update = function(delta){
this.playerCenter = [this.x+this.width/2, this.y+this.height/2];
let dX = (GAME.mouse.position.x - this.playerCenter[0]),
dY = (GAME.mouse.position.y - this.playerCenter[1]);
radius = Math.sqrt(dX * dX + dY * dY);
// Movement Forward
if(GAME.keyDown[87] && radius >= 50){
this.x += (dX / radius) * this.movementSpeed * delta;
this.y += (dY / radius) * this.movementSpeed * delta;
}
// Movement Backward
if(GAME.keyDown[83]){
this.x -= (dX / radius) * this.movementSpeed * delta;
this.y -= (dY / radius) * this.movementSpeed * delta;
}
// Strafe left
if(GAME.keyDown[65]){
}
// Strafe right
if(GAME.keyDown[68]){
}
}
You almost have the solution.
You need to go at 90 deg to the forward vector. To rotate a vector 90cw you swap the x,and y and negate the new x.
EG
dx = ?; // forward direction
dy = ?;
// rotate 90 clockwise
dx1 = -dy;
dy1 = dx;
Thus you can update your code as follows
var dX = (GAME.mouse.position.x - this.playerCenter[0]);
var dY = (GAME.mouse.position.y - this.playerCenter[1]);
var radius = Math.sqrt(dX * dX + dY * dY);
//normalise
if(radius > 0){
dX = (dX / radius) * this.movementSpeed * delta;
dY = (dY / radius) * this.movementSpeed * delta;;
}else{
dX = dY = 0; // too close need to set this or you will get NaN propagating through your position variables.
}
if(GAME.keyDown[87] && radius >= 50){ // Movement Forward
this.x += dX;
this.y += dY;
}
if(GAME.keyDown[83]){ // Movement Backward
this.x -= dX;
this.y -= dY;
}
if(GAME.keyDown[65]){ // Strafe left
this.x += -dY; // swap x and y negate new x to rotate vector 90
this.y += dX;
}
if(GAME.keyDown[68]){ // Strafe right
this.x -= -dY; // swap x and y negate new x to rotate vector 90
this.y -= dX;
}

Mouse click angle

So I'm creating a brick breaker game, and I need some help finding an angle.
Pretty much the game consists of blocks that, when hit, will cause you to lose 1 health. The point of the game is to hit the blocks with the balls to break them before they reach the bottom. If the ball hits a wall or a block, its trajectory is reversed.
I want the user to be able to click someone within the html canvas. Then the balls, which start in the center of the screen at the bottom of the canvas, will follow that angle. In other words, the user will click and the balls will move to that spot and then continue until it hits something.
I have some code here, But it probably won't help on how to achieve the angle thing.
function animate(callback) {
window.requestAnimationFrame(function() {
window.setTimeout(callback, 1000/60);
});
}
// canvas
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
// variables
var ballList = [];
var maxBalls = 1;
var checkAmount = 0;
var interval;
// onload/refresh/update/render
window.onload = function() {
refresh();
}
function refresh() {
update();
render();
animate(refresh);
}
function update() {
document.addEventListener("click", spawn);
for(var i = 0; i < ballList.length; i++) {
ballList[i].move();
}
}
function render() {
context.fillStyle = '#000';
context.fillRect(0, 0, canvas.width, canvas.height);
for(var i = 0; i < ballList.length; i++) {
ballList[i].show();
}
}
// ball
function Ball() {
this.x = canvas.width / 2;
this.y = canvas.height - 50;
this.width = 10;
this.height = 10;
this.xVel = 5;
this.yVel = -10;
this.show = function() {
context.fillStyle = '#fff';
context.fillRect(this.x, this.y, this.width, this.height);
}
this.move = function() {
this.x += this.xVel;
this.y += this.yVel;
if(this.x >= canvas.width || this.x <= 0) {
this.xVel *= -1;
}
if(this.y >= canvas.height || this.y <= 0) {
this.yVel *= -1;
}
}
}
function spawn(event) {
var xVel = (event.clientX - canvas.width / 2) / 90;
if(ballList.length < maxBalls) {
if(checkAmount < maxBalls) {
interval = setInterval(function() {
ballList.push(new Ball((event.clientX)));
checkAmount++;
if(checkAmount > maxBalls) {
clearInterval(interval);
checkAmount = 0;
}
}, 10);
}
}
}
Thanks in advance.
Unit Vectors
To move an object from one point towards another you use a vector. A vector is just two numbers that represent a direction and a speed. It can be polar in that one number is an angle and the other is a distance, or cartesian that represent the vector as the amount of change in x and y.
Cartesian unit vector
For this you can use either but I prefer the cartesian vector and a particular type called a unit vector. The unit vector is 1 unit long. In computer graphics the unit is normally the pixel.
So we have a point to start at
var startX = ?
var startY = ?
And a point the we want to head towards
var targetX = ?
var targetY = ?
We want the unit vector from start to target,
var vectorX = targetX - startX;
var vectorY = targetY - startY;
The vector's length is the distance between the two points. This is not so handy so we will turn it into a unit vector by dividing both the x and y by the length
var length = Math.sqrt(vectorX * vectorX + vectorY * vectorY);
var unitVectX = vectorX / length;
var unitVectY = vectorY / length;
Now we have a one pixel long unit vector.
The Ball will start at start
var ballX = startX
var ballY = startY
And will move at a speed of 200 pixels per second (assuming 60fps)
var ballSpeed = 200 / 60;
Now to move the ball just add the unit vector times the speed and you are done. Well till the next frame that is.
ballX += unitVectX * ballSpeed;
ballY += unitVectY * ballSpeed;
Using the cartesian makes it very easy to bounce off of walls that are aligned to the x or y axis.
if(ballX + ballRadius > canvas.width){
ballX = canvas.width - ballRadius;
unitVectX = - unitVectX;
}
Polar vector
You can also use polar coordinates. As we use a unit vector the polar unit vector just needs the direction. You use the trig function atan2
// get the direction in radians
var polarDirection = Math.atan2(targetY - startY, targetX - startX);
The direction is in radians, many poeple don't like radians and convert to degrees, but there is no need to know which way it is going just as long as it goes in the correct direction. To remember radians is easy. 360 degrees is 2 radian 180 is 1 randian 90 is 0.5. The actual units used are PI (not many people know many of the digits of pi but you don't need to). So 270 degree is 1.5 radians or as a number 1.5 * Math.PI.
The angles start at the 3 o'clock point (pointing to the right of screen) as 0 radians or 0 deg then clockwise 90deg is at 6 o'clock 0.5 radian, and 180deg 1 radian at 6 o'clock and so on.
To move the ball with the polarDirection you need to use some more trig.
// do this once a frame
ballX += Math.cos(polarDirection) * ballSpeed;
ballY += Math.sin(polarDirection) * ballSpeed;
// note that the cos and sin actually generate the cartesian unit vector
/**
* #param {number} x1 - x coordinate of the first point
* #param {number} y1 - y coordinate of the first point
* #param {number} x2 - x coordinate of the second point
* #param {number} y2 - y coordinate of the second point
* #return {number} - the angle (between 0 and 360)
*/
function getDirection(x1, y1, x2, y2) {
// might be negative:
var angle = Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI;
// correct, positive angle:
return (angle + 360) % 360;
}
I wrote this function for a similar purpose. Don't forget that you might have to negate x.

Changing my Jquery project to plain javascript

I created a breakout game for a school project using jquery and a helpful online tutorial.
The working fiddle is here: http://jsfiddle.net/Kinetic915/kURvf/
EDIT revised fiddle: http://jsfiddle.net/Kinetic915/nVctR/
I have changed most to javascript but am having problems changing the jquery code that renders the ball to javascript.
I have Marked and left spaces in the areas where there are problems.
Thank you very much for any help given!!
//***********************************************************************************
// START CODE
//***********************************************************************************
// VARIABLES and other initializing functions are here
function start() {
//******************************************************************************
//JQUERY
// HERE IS THE MAIN PROBLEM!!!!!!
// HERE IS THE MAIN PROBLEM!!!!!!
Cir = $('#canvas')[0].getContext("2d");
//JQUERY
//changing to Cir = canvas.getContext("2d"); causes the code to FAIL.
return setInterval(drawCIRCLE, 10);
}
function windowsize() {
//success with javascript
WIDTH.width = window.innerWidth;
HEIGHT.height = window.innerHeight;
WIDTH = window.innerWidth;
HEIGHT = window.innerHeight;
//Previous JQUERY:
// WIDTH = $("#canvas")[0].width = $(window).width();
// HEIGHT = $("#canvas")[0].height = $(window).height();
}
windowsize();
var x = WIDTH / 2 - 30; //divide by 2 start in middle of window
var y = HEIGHT / 2;
//THIS DRAWS THE CIRCLE
function circle() {
//Cir.clearRect(0, 0, WIDTH, HEIGHT);
Cir.beginPath();
Cir.arc(x, y, 10, 0, Math.PI * 2, true);
Cir.closePath();
Cir.fill();
}
// Initialization of the Block array, rendering of the gutter area and coordinate box
were here
//*********************************************************
// HERE IS THE CODE THAT RENDERS THE BALL MOVEMENT ETC.
//draw a circle
function drawCIRCLE() {
clear();
circle();
drawPADDLE(); //calls draw paddle function
drawGUTTER(); // calls draw gutter function
drawCOORBOX(); // calls draw coordinate box function
drawBRICKS(); //calls the function to draw the boxes
//have we hit a brick?
rowheight = brickheight + padding;
colwidth = brickwidth + padding;
row = Math.floor(y / rowheight);
col = Math.floor(x / colwidth);
//if so, reverse the ball and mark the brick as broken
if (y < numrows * rowheight && row >= 0 && col >= 0 && bricks[row][col] == 1) {
dy = -dy;
bricks[row][col] = 0;
}
if (x + dx > WIDTH || x + dx < 0) dx = -dx;
if (y + dy < 0) dy = -dy;
else if (y + dy > ((HEIGHT - paddleh) - ppoffset) || y + dy > HEIGHT) {
if (x > paddlex && x < paddlex + paddlew)
//switch! once first is true, then second goes
dy = -dy;
else if (y + dy > ((HEIGHT - paddleh) - ppoffset) && y + dy > HEIGHT) {
clearInterval(intervalId);
}
}
x += dx;
y += dy;
if (rightpress) paddlex += 5;
else if (leftpress) paddlex -= 5;
}
function clear() {
Cir.clearRect(0, 0, WIDTH, HEIGHT);
//Is this jquery? I suspect this part of the code making the circle rendering fail.
}
start();
init_paddle();
initbricks();
Ages ago I wrote a similar code in pure JavaScript here
this code uses pure javascript and no library.The code is well commented(I think :))
I generally attached events like this
document.onkeydown = function(e)
{
e = e || window.event;
switch (e.keyCode) { // which key was pressed?
case 32: // release ball.
if(!game.ball.isFree)
{
game.ball.isFree = true;
game.ball.directionX = game.ball.directionY = 1;
game.ball.x = game.ball.offsetLeft;
game.ball.y = game.screen.offsetHeight - game.ball.offsetTop;
}
break;
case 37: // left, rotate player left
game.bar.direction = -1;
break;
case 39: // right, rotate player right
game.bar.direction = 1;
break;
}
}
document.onkeyup = function(e)
{
e = e || window.event;
switch (e.keyCode)
{
case 37:
case 39:
game.bar.direction = 0;
break;
}
}
},
Ofcourse you have many other places which might need porting so a helpful break down of questions would be easier to answer :)
Hope this helps

help me improve my basic point to point move animation algorithm

Greetings,
With javascript, I am trying to make a very easy animation, an image moves from one X and Y coordination to another X Y coordination.
I have 4 constant such as:
var startX = 0; //starting X of an image
var startY = 0; //starting Y of an image
var endX = 100; //ending X of an image
var endY = 200; //ending Y of an image
//these 2 are used for keeping the "current" position of animated image,
var currentX = startX ;
var currentY = startY ;
//every 150 ms, updates the location of the coordinates
function move(){
if( (currentX == endX) && (currentY == endY) )
break;
if(currentX < endX){
currentX = currentX + step_amount;
}
if(currentX > endX){
currentX = currentX - step_amount;
}
if(currentY < endY){
currentY = currentY + step_amount;
}
if(currentY > endY){
currentY = currentY - step_amount;
}
setInterval("move()", 150);
}
This does the job, however it is not smooth, I will be grateful if you help me improve my naive algorithm for a better move function that is always going for the "shortest path".
Sounds like you need (a variation of) the Bresenham line drawing algorithm.
The shortest distance between two points is a straight line. So you should probably move along that.
What that would imply is that in your code, you should not use the same step amount for both X and Y coordinates. Instead compute Y step based on X step and the slope of the shortest path line.
slope = (startY - endY) / (startX - endX);
Y_step = X_step * slope;
Secondly, in your current algorithm, once your image reaches the destination point, it'll continue to oscillate about it. I think you should get rid of the statements that take a negative step.
Since you are always moving two coordinates together, you only need to check against one of them, e.g.
if (currentX < endX) {
currentX += xStep;
currentY += yStep;
}
Try something like this to move the object in a straight line:
var numberOfSteps = 100;
var stepDX = (endX - startX) / numberOfSteps;
var stepDY = (endY - startY) / numberOfSteps;
var step = 0;
Inside the move() function:
if (step <= numberOfSteps) {
currentX = startX + stepDX * step;
currentY = startY + stepDY * step;
step++;
}
Cast currentX/currentY to integer before applying to the object you want to move.
This is my implementation, many thanks to Frederik The Fool
Compute slope:
if(this.x === target.x){
this.slope = 1;
}else{
this.slope = (this.y - target.y)/(this.x - target.x);
}
Ystep:
if(this.y > this.target.y){
this.y = Math.max(this.target.y, this.y - Math.abs(this.slope * distance));
}else if(this.shape.y < this.target.y){
this.y = Math.min(this.target.y, this.y + Math.abs(this.slope * distance));
}

Categories