I have a number input which i want to it to function as a real-time update of the speed of the pong ball, however every method I've tried hasn't worked it all comes back as "NaN". The speed is originally set to 7 but once the ball resets and a player scores id like the ball to change to the number of the input. Thank you
// select canvas element
const canvas = document.getElementById("pong");
// getContext of canvas = methods and properties to draw and do a lot of thing to the canvas
const ctx = canvas.getContext("2d");
// get the value of the user input
var userSpeedInput = parseInt(document.getElementById("user-speed").value);
// speed btn
const speedBtn = document.getElementById("speed-btn");
// load sounds
let hit = new Audio();
let wall = new Audio();
let userScore = new Audio();
let comScore = new Audio();
hit.src = "sounds/hit.mp3";
wall.src = "sounds/wall.mp3";
comScore.src = "sounds/comScore.mp3";
userScore.src = "sounds/userScore.mp3";
// Ball object
const ball = {
x: canvas.width / 2,
y: canvas.height / 2,
radius: 10,
velocityX: 5,
velocityY: 5,
speed: userSpeedInput, // speed: userSpeedInput;
color: "WHITE",
};
// User Paddle
const user = {
x: 0, // left side of canvas
y: (canvas.height - 100) / 2, // -100 the height of paddle
width: 10,
height: 100,
score: 0,
color: "WHITE",
};
// COM Paddle
const com = {
x: canvas.width - 10, // - width of paddle
y: (canvas.height - 100) / 2, // -100 the height of paddle
width: 10,
height: 100,
score: 0,
color: "WHITE",
};
// NET
const net = {
x: (canvas.width - 2) / 2,
y: 0,
height: 10,
width: 2,
color: "WHITE",
};
// draw a rectangle, will be used to draw paddles
function drawRect(x, y, w, h, color) {
ctx.fillStyle = color;
ctx.fillRect(x, y, w, h);
}
// draw circle, will be used to draw the ball
function drawArc(x, y, r, color) {
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();
}
// listening to the mouse
canvas.addEventListener("mousemove", getMousePos);
function getMousePos(evt) {
let rect = canvas.getBoundingClientRect();
user.y = evt.clientY - rect.top - user.height / 2;
}
// when COM or USER scores, we reset the ball
function resetBall() {
if (!ball) return;
ball.x = canvas.width / 2;
ball.y = canvas.height / 2;
ball.velocityX = -ball.velocityX;
ball.speed = userSpeedInput;
alert(ball.speed);
}
// draw the net
function drawNet() {
for (let i = 0; i <= canvas.height; i += 15) {
drawRect(net.x, net.y + i, net.width, net.height, net.color);
}
}
// draw text
function drawText(text, x, y) {
ctx.fillStyle = "#FFF";
ctx.font = "75px fantasy";
ctx.fillText(text, x, y);
}
// collision detection
function collision(b, p) {
p.top = p.y;
p.bottom = p.y + p.height;
p.left = p.x;
p.right = p.x + p.width;
b.top = b.y - b.radius;
b.bottom = b.y + b.radius;
b.left = b.x - b.radius;
b.right = b.x + b.radius;
return (
p.left < b.right && p.top < b.bottom && p.right > b.left && p.bottom > b.top
);
}
// update function, the function that does all calculations
function update() {
// change the score of players, if the ball goes to the left "ball.x<0" computer win, else if "ball.x > canvas.width" the user win
if (ball.x - ball.radius < 0) {
com.score++;
comScore.play();
resetBall();
} else if (ball.x + ball.radius > canvas.width) {
user.score++;
userScore.play();
resetBall();
}
// the ball has a velocity
ball.x += ball.velocityX;
ball.y += ball.velocityY;
// computer plays for itself, and we must be able to beat it
// simple AI
com.y += (ball.y - (com.y + com.height / 2)) * 0.1;
// when the ball collides with bottom and top walls we inverse the y velocity.
if (ball.y - ball.radius < 0 || ball.y + ball.radius > canvas.height) {
ball.velocityY = -ball.velocityY;
wall.play();
}
// we check if the paddle hit the user or the com paddle
let player = ball.x + ball.radius < canvas.width / 2 ? user : com;
// if the ball hits a paddle
if (collision(ball, player)) {
// play sound
hit.play();
// we check where the ball hits the paddle
let collidePoint = ball.y - (player.y + player.height / 2);
// normalize the value of collidePoint, we need to get numbers between -1 and 1.
// -player.height/2 < collide Point < player.height/2
collidePoint = collidePoint / (player.height / 2);
// when the ball hits the top of a paddle we want the ball, to take a -45degees angle
// when the ball hits the center of the paddle we want the ball to take a 0degrees angle
// when the ball hits the bottom of the paddle we want the ball to take a 45degrees
// Math.PI/4 = 45degrees
let angleRad = (Math.PI / 4) * collidePoint;
// change the X and Y velocity direction
let direction = ball.x + ball.radius < canvas.width / 2 ? 1 : -1;
ball.velocityX = direction * ball.speed * Math.cos(angleRad);
ball.velocityY = ball.speed * Math.sin(angleRad);
// speed up the ball everytime a paddle hits it.
ball.speed += 0.1;
}
}
// render function, the function that does al the drawing
function render() {
// clear the canvas
drawRect(0, 0, canvas.width, canvas.height, "#000");
// draw the user score to the left
drawText(user.score, canvas.width / 4, canvas.height / 5);
// draw the COM score to the right
drawText(com.score, (3 * canvas.width) / 4, canvas.height / 5);
// draw the net
drawNet();
// draw the user's paddle
drawRect(user.x, user.y, user.width, user.height, user.color);
// draw the COM's paddle
drawRect(com.x, com.y, com.width, com.height, com.color);
// draw the ball
drawArc(ball.x, ball.y, ball.radius, ball.color);
}
function game() {
update();
render();
}
// number of frames per second
let framePerSecond = 50;
//call the game function 50 times every 1 Sec
let loop = setInterval(game, 1000 / framePerSecond);
You'll need an event listener on the user-speed textbox that changes the value of the global variable userSpeedInput when the user types something. Right now that variable is only set once, when the page first loads.
document.getElementById("user-speed").addEventListener("change", (e) => {
userSpeedInput = parseInt(e.target.value);
});
Related
Can somebody fix it script to make it works properly?
What I expects:
Run script
Click at the canvas to set target (circle)
Object (triangle) starts to rotate and move towards to target (circle)
Change target at any time
How it works:
Sometimes object rotates correctly, sometimes isn't
Looks like one half sphere works well, another isn't
Thanks!
// prepare 2d context
const c = window.document.body.appendChild(window.document.createElement('canvas'))
.getContext('2d');
c.canvas.addEventListener('click', e => tgt = { x: e.offsetX, y: e.offsetY });
rate = 75 // updates delay
w = c.canvas.width;
h = c.canvas.height;
pi2 = Math.PI * 2;
// object that moves towards the target
obj = {
x: 20,
y: 20,
a: 0, // angle
};
// target
tgt = undefined;
// main loop
setInterval(() => {
c.fillStyle = 'black';
c.fillRect(0, 0, w, h);
// update object state
if (tgt) {
// draw target
c.beginPath();
c.arc(tgt.x, tgt.y, 2, 0, pi2);
c.closePath();
c.strokeStyle = 'red';
c.stroke();
// update object position
// vector from obj to tgt
dx = tgt.x - obj.x;
dy = tgt.y - obj.y;
// normalize
l = Math.sqrt(dx*dx + dy*dy);
dnx = (dx / l);// * 0.2;
dny = (dy / l);// * 0.2;
// update object position
obj.x += dnx;
obj.y += dny;
// angle between +x and tgt
a = Math.atan2(0 * dx - 1 * dy, 1 * dx + 0 * dy);
// update object angle
obj.a += -a * 0.04;
}
// draw object
c.translate(obj.x, obj.y);
c.rotate(obj.a);
c.beginPath();
c.moveTo(5, 0);
c.lineTo(-5, 4);
c.lineTo(-5, -4);
//c.lineTo(3, 0);
c.closePath();
c.strokeStyle = 'red';
c.stroke();
c.rotate(-obj.a);
c.translate(-obj.x, -obj.y);
}, rate);
This turned out to be a bit more challenging than I first thought and I ended up just re-writing the code.
The challenges:
Ensure the ship only rotated to the exact point of target. This required me to compare the two angle from the ship current position to where we want it to go.
Ensure the target did not rotate past the target and the ship did not translate past the target. This required some buffer space for each because when animating having this.x === this.x when an object is moving is very rare to happen so we need some room for the logic to work.
Ensure the ship turned in the shortest direction to the target.
I have added notes in the code to better explain. Hopefully you can implement this into yours or use it as is. Oh and you can change the movement speed and rotation speed as you see fit.
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 400;
let mouse = { x: 20, y: 20 };
let canvasBounds = canvas.getBoundingClientRect();
let target;
canvas.addEventListener("mousedown", (e) => {
mouse.x = e.x - canvasBounds.x;
mouse.y = e.y - canvasBounds.y;
target = new Target();
});
class Ship {
constructor() {
this.x = 20;
this.y = 20;
this.ptA = { x: 15, y: 0 };
this.ptB = { x: -15, y: 10 };
this.ptC = { x: -15, y: -10 };
this.color = "red";
this.angle1 = 0;
this.angle2 = 0;
this.dir = 1;
}
draw() {
ctx.save();
//use translate to move the ship
ctx.translate(this.x, this.y);
//angle1 is the angle from the ship to the target point
//angle2 is the ships current rotation angle. Once they equal each other then the rotation stops. When you click somewhere else they are no longer equal and the ship will rotate again.
if (!this.direction(this.angle1, this.angle2)) {
//see direction() method for more info on this
if (this.dir == 1) {
this.angle2 += 0.05; //change rotation speed here
} else if (this.dir == 0) {
this.angle2 -= 0.05; //change rotation speed here
}
} else {
this.angle2 = this.angle1;
}
ctx.rotate(this.angle2);
ctx.beginPath();
ctx.strokeStyle = this.color;
ctx.moveTo(this.ptA.x, this.ptA.y);
ctx.lineTo(this.ptB.x, this.ptB.y);
ctx.lineTo(this.ptC.x, this.ptC.y);
ctx.closePath();
ctx.stroke();
ctx.restore();
}
driveToTarget() {
//get angle to mouse click
this.angle1 = Math.atan2(mouse.y - this.y, mouse.x - this.x);
//normalize vector
let vecX = mouse.x - this.x;
let vecY = mouse.y - this.y;
let dist = Math.hypot(vecX, vecY);
vecX /= dist;
vecY /= dist;
//Prevent continuous x and y increment by checking if either vec == 0
if (vecX != 0 || vecY != 0) {
//then also give the ship a little buffer incase it passes the given point it doesn't turn back around. This allows time for it to stop if you increase the speed.
if (
this.x >= mouse.x + 3 ||
this.x <= mouse.x - 3 ||
this.y >= mouse.y + 3 ||
this.y <= mouse.y - 3
) {
this.x += vecX; //multiple VecX by n to increase speed (vecX*2)
this.y += vecY; //multiple VecY by n to increase speed (vecY*2)
}
}
}
direction(ang1, ang2) {
//converts rads to degrees and ensures we get numbers from 0-359
let a1 = ang1 * (180 / Math.PI);
if (a1 < 0) {
a1 += 360;
}
let a2 = ang2 * (180 / Math.PI);
if (a2 < 0) {
a2 += 360;
}
//checks whether the target is on the right or left side of the ship.
//We use then to ensure it turns in the shortest direction
if ((360 + a1 - a2) % 360 > 180) {
this.dir = 0;
} else {
this.dir = 1;
}
//Because of animation timeframes there is a chance the ship could turn past the target if rotating too fast. This gives the ship a 1 degree buffer to either side of the target to determine if it is pointed in the right direction.
//We then correct it to the exact degrees in the draw() method above once the if statment defaults to 'else'
if (
Math.trunc(a2) <= Math.trunc(a1) + 1 &&
Math.trunc(a2) >= Math.trunc(a1) - 1
) {
return true;
}
return false;
}
}
let ship = new Ship();
class Target {
constructor() {
this.x = mouse.x;
this.y = mouse.y;
this.r = 3;
this.color = "red";
}
draw() {
ctx.strokeStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2, false);
ctx.stroke();
}
}
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillRect(0, 0, canvas.width, canvas.height);
ship.draw();
ship.driveToTarget();
if (target) {
target.draw();
}
requestAnimationFrame(animate);
}
animate();
<canvas id="canvas"></canvas>
I'm trying to make an animation inside a canvas: here, a numbered circle must be drawn and move from left to right one single time, disappearing as soon as it reaches the end of animation.
For now I managed to animate it in loop, but I need to animate at the same time (or with a set delay) multiple numbered circles, strating in different rows (changing y position) so they wont overlap.
Any idea how can I manage this? my JS code is the following:
// Single Animated Circle - Get Canvas element by Id
var canvas = document.getElementById("canvas");
// Set Canvas dimensions
canvas.width = 300;
canvas.height = 900;
// Get drawing context
var ctx = canvas.getContext("2d");
// Radius
var radius = 13;
// Starting Position
var x = radius;
var y = radius;
// Speed in x and y direction
var dx = 1;
var dy = 0;
// Generate random number
var randomNumber = Math.floor(Math.random() * 60) + 1;
if (randomNumber > 0 && randomNumber <= 10) {
ctx.strokeStyle = "#0b0bf1";
} else if (randomNumber > 10 && randomNumber <= 20) {
ctx.strokeStyle = "#f10b0b";
} else if (randomNumber > 20 && randomNumber <= 30) {
ctx.strokeStyle = "#0bf163";
} else if (randomNumber > 30 && randomNumber <= 40) {
ctx.strokeStyle = "#f1da0b";
} else if (randomNumber > 40 && randomNumber <= 50) {
ctx.strokeStyle = "#950bf1";
} else if (randomNumber > 50 && randomNumber <= 60) {
ctx.strokeStyle = "#0bf1e5";
}
function animate3() {
requestAnimationFrame(animate3);
ctx.clearRect(0, 0, 300, 900);
if (x + radius > 300 || x - radius < 0) {
x = radius;
}
x += dx;
ctx.beginPath();
ctx.arc(x, y, 12, 0, Math.PI * 2, false);
ctx.stroke();
ctx.fillText(randomNumber, x - 5, y + 3);
}
// Animate the Circle
animate3();
<canvas id="canvas"></canvas>
Here is a solution which doesn't use classes as such and separates the animation logic from the updating - which can be useful if you want more precise control over timing.
// Some helper functions
const clamp = (number, min, max) => Math.min(Math.max(number, min), max);
// Choose and remove random member of arr with equal probability
const takeRandom = arr => arr.splice(parseInt(Math.random() * arr.length), 1)[0]
// Call a function at an interval, passing the amount of time that has passed since the last call
function update(callBack, interval) {
let now = performance.now();
let last;
setInterval(function() {
last = now;
now = performance.now();
callBack((now - last) / 1000);
})
}
const settings = {
width: 300,
height: 150,
radius: 13,
gap: 5,
circles: 5,
maxSpeed: 100,
colors: ["#0b0bf1", "#f10b0b", "#0bf163", "#f1da0b", "#950bf1", "#0bf1e5"]
};
const canvas = document.getElementById("canvas");
canvas.width = settings.width;
canvas.height = settings.height;
const ctx = canvas.getContext("2d");
// Set circle properties
const circles = [...Array(settings.circles).keys()].map(i => ({
number: i + 1,
x: settings.radius,
y: settings.radius + (settings.radius * 2 + settings.gap) * i,
radius: settings.radius,
dx: settings.maxSpeed * Math.random(), // This is the speed in pixels per second
dy: 0,
color: takeRandom(settings.colors)
}));
function drawCircle(circle) {
ctx.strokeStyle = circle.color;
ctx.beginPath();
ctx.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2, false);
ctx.stroke();
ctx.fillText(circle.number, circle.x - 5, circle.y + 3);
}
function updateCircle(circle, dt) {
// Update a circle's position after dt seconds
circle.x = clamp(circle.x + circle.dx * dt, circle.radius + 1, settings.width - circle.radius - 1);
circle.y = clamp(circle.y + circle.dy * dt, circle.radius + 1, settings.height - circle.radius - 1);
}
function animate() {
ctx.clearRect(0, 0, settings.width, settings.height);
circles.forEach(drawCircle);
requestAnimationFrame(animate);
}
update(dt => circles.forEach(circle => updateCircle(circle, dt)), 50);
animate();
<canvas id="canvas" style="border: solid 1px black"></canvas>
Here I transformed your sample code into a class ...
We pass all the data as a parameter, you can see that in the constructor, I simplified a lot of your code to keep it really short, but all the same drawing you did is there in the draw function
Then all we need to do is create instances of this class and call the draw function inside that animate3 loop you already have.
You had a hardcoded value on the radius:
ctx.arc(x, y, 12, 0, Math.PI * 2, false)
I assume that was a mistake and fix it on my code
var canvas = document.getElementById("canvas");
canvas.width = canvas.height = 300;
var ctx = canvas.getContext("2d");
class Circle {
constructor(data) {
this.data = data
}
draw() {
if (this.data.x + this.data.radius > 300 || this.data.x - this.data.radius < 0) {
this.data.x = this.data.radius;
}
this.data.x += this.data.dx;
ctx.beginPath();
ctx.arc(this.data.x, this.data.y, this.data.radius, 0, Math.PI * 2, false);
ctx.stroke();
ctx.fillText(this.data.number, this.data.x - 5, this.data.y + 3);
}
}
circles = []
circles.push(new Circle({radius:13, x: 10, y: 15, dx: 1, dy: 0, number: "1"}))
circles.push(new Circle({radius:10, x: 10, y: 50, dx: 2, dy: 0, number: "2"}))
function animate3() {
requestAnimationFrame(animate3);
ctx.clearRect(0, 0, canvas.width, canvas.height);
circles.forEach(item => item.draw());
}
animate3();
<canvas id="canvas"></canvas>
Code should be easy to follow let me know if you have any questions
I'm attempting to write a JavaScript program where the ball ignores hitting a green obstacle but ends the game when a red obstacle indicates game over. However in my code the game ends when the ball hits an obstacle of any color.
My definition of the obstacle is:
function Obstacle(x, size, horizon, color) {
this.x = x;
this.y = horizon - size;
this.size = size;
this.color = color;
this.onScreen = true;
}
/**
* handle x and onScreen values
*/
Obstacle.prototype.update = function(speed) {
/* check if offscreen */
this.onScreen = (this.x > -this.size);
/* movement */
this.x -= speed;
};
Obstacle.prototype.draw = function() {
fill(this.color);
stroke(255);
strokeWeight(2);
rect(this.x, this.y, this.size, this.size);
};
Obstacle.prototype.hits = function(ball) {
var halfSize = this.size / 2;
var minimumDistance = halfSize + (ball.radius); // closest before collision
/* find center coordinates */
var xCenter = this.x + halfSize;
var yCenter = this.y + halfSize;
var distance = dist(xCenter, yCenter, ball.x, ball.y); // calculate distance from centers
return (distance < minimumDistance); // return result
};
This is the code for the game:
function setup() {
createCanvas(600, 200);
textAlign(CENTER);
horizon = height - 40;
score = 0;
obstacleSpeed = 6;
var size = 20;
ball = new bol(size * 2, height - horizon, size);
textSize(20);
}
function draw() {
background(51);
drawHUD();
handleLevel(frameCount);
ball.update(horizon);
handleObstacles();
//handlepowerups();
}
/**
* draws horizon & score
*/
function drawHUD() {
/* draw horizon */
stroke(255);
strokeWeight(2);
line(0, horizon, width, horizon);
/* draw score */
noStroke();
text("Score: " + score, width / 2, 30);
ball.draw();
}
/**
* updates, draws, and cleans out the obstacles
*/
function handleObstacles() {
for (var i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].update(obstacleSpeed);
obstacles[i].draw();
if (obstacles[i].hits(ball)) // if there's a collision
{
if (obstacles[i].color = color(225, 0, 0))
endGame();
else
obstacles[i].color = color(0, 0, 225)
}
if (!obstacles[i].onScreen) // if it's no longer showing
obstacles.splice(i, 1); // delete from array
}
}
/**
* speeds game up, pushes new obstacles, & handles score
*/
function handleLevel(n) {
if (n % 100 === 0) { // every 0.5 seconds
var n = noise(n);
if (n > 0.5)
newObstacle(n); // push new obstacle
if (n % 120 === 0) // every 2 seconds
obstacleSpeed *= 1.05; // speed up
}
score++;
}
/**
* pushes random obstacle
*/
function newObstacle(n) {
var col1 = color(225, 0, 0);
var col2 = color(0, 225, 0);
var size = random(25) + 20;
var obs = new Obstacle(width + size, size, horizon, col2);
var obs2 = new Obstacle(width + size, size, horizon, col1);
var cases = random(1);
if (cases < 0.5)
obstacles.push(obs);
else
obstacles.push(obs2)
}
function keyPressed() {
if ((keyCode === UP_ARROW || keyCode === 32) && ball.onGround) // jump if possible
ball.jump();
boingaudio.play()
}
function endGame() {
noLoop();
noStroke();
textSize(40);
text("GAME OVER", width / 2, height / 2);
if (highscore !== null) {
if (score > highscore) {
localStorage.setItem("highscore", score);
}
} else {
localStorage.setItem("highscore", score);
}
textSize(20);
text("Highscore: " + highscore, width / 2, height / 2 + 20);
gameoveraudio.play()
}
I don't understand why the ball keeps ending game for no matter what colored ball appears. Can you help me figure out what's wrong?
You seem to be assigning a value to obstacles[i].color rather than testing it:
if (obstacles[i].color = color(225, 0, 0))
endGame();
else
obstacles[i].color = color(0, 0, 225)
This should probably read:
if (obstacles[i].color == color(225, 0, 0))
endGame();
Without your else, which seemed to serve no purpose.
What i am trying to do is to click a position on a canvas and have a dot follow a path directly to the position i have selected.
this is my code so far.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var rect = canvas.getBoundingClientRect()
//
var clickedChords = {
x: 0,
y: 0
};
var player = {
x: 100,
y: 100
};
canvas.addEventListener("mousedown", getChords, false);
function getChords(e) {
clickedChords.x = e.pageX - rect.left;
clickedChords.y = e.pageY - rect.top;
}
function draw() {
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.fillStyle= 'black';
ctx.beginPath();
ctx.arc(clickedChords.x,clickedChords.y,3,0,360);
ctx.arc(player.x,player.y,3,0,360);
ctx.fill();
if(player.x > clickedChords.x && player.x !== clickedChords.x){
player.x -= 1;
}
if(player.x < clickedChords.x && player.x !== clickedChords.x){
player.x += 1;
}
if(player.y < clickedChords.y && player.y !== clickedChords.y){
player.y += 1;
}
if(player.y > clickedChords.y && player.y !== clickedChords.y){
player.y -= 1;
}
}setInterval(draw, 10);
Right now all i have managed to do was to get the dot to follow a 45 degree angle then hit and follow the x or y value of my clicked position. I have tried to use the slope formula but i am clueless on how to implement it into my code.
Thank you for helping as i am truly stuck.
You can use interpolation for this:
var t = 0, // will be in range [0.0, 1.0]
oldPlayerX, // need these for interpolation
oldPlayerY;
function getChords(e) {
clickedChords.x = e.pageX - rect.left;
clickedChords.y = e.pageY - rect.top;
oldPlayerX = player.x, // copy these as we need them as-is
oldPlayerY = player.y;
draw();
}
function draw() {
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.fillStyle= 'black';
ctx.beginPath();
ctx.arc(clickedChords.x,clickedChords.y,3,0,360);
ctx.arc(player.x,player.y,3,0,360);
ctx.fill();
if (t <= 1) {
player.x = oldPlayerX + (clickedChords.x - oldPlayerX) * t;
player.y = oldPlayerY + (clickedChords.y - oldPlayerY) * t;
t += 0.05; // determines speed
requestAnimationFrame(draw); // use this instead of setInterval
}
}
The interpolation itself happens on these lines:
player.x = oldPlayerX + (clickedChords.x - oldPlayerX) * t;
player.y = oldPlayerY + (clickedChords.y - oldPlayerY) * t;
It basically do a difference between new and old position. t will determine how much of that difference is added to the original position, ie. if t=0 then nothing, if t=1 then full difference is added to old position which obviously equals the new position. Anything in between (for t) will add a percentage of that allowing you to animate the point.
requestAnimationFrame allow you to sync the animation to monitor refresh. A setInterval value of 10 ms is way to short for the browser to handle (minimum is 16.7ms for a screen refresh # 60 Hz).
Hope this helps!
We can just use some basic trigonometry:
var angle = Math.atan2(clickedChords.y - player.y, clickedChords.x - player.x);
player.x += Math.cos(angle);
player.y += Math.sin(angle);
Here is a fiddle:
http://jsfiddle.net/AniHouse/w2vCc/1/
I'm trying to achieve multiple animations in a game that I am creating using Canvas (it is a simple ping-pong game). This is my first game and I am new to canvas but have created a few experiments before so I have a good knowledge about how canvas work.
First, take a look at the game here.
The problem is, when the ball hits the paddle, I want a burst of n particles at the point of contact but that doesn't came right. Even if I set the particles number to 1, they just keep coming from the point of contact and then hides automatically after some time.
Also, I want to have the burst on every collision but it occurs on first collision only. I am pasting the code here:
//Initialize canvas
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
W = window.innerWidth,
H = window.innerHeight,
particles = [],
ball = {},
paddles = [2],
mouse = {},
points = 0,
fps = 60,
particlesCount = 50,
flag = 0,
particlePos = {};
canvas.addEventListener("mousemove", trackPosition, true);
//Set it's height and width to full screen
canvas.width = W;
canvas.height = H;
//Function to paint canvas
function paintCanvas() {
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = "black";
ctx.fillRect(0, 0, W, H);
}
//Create two paddles
function createPaddle(pos) {
//Height and width
this.h = 10;
this.w = 100;
this.x = W/2 - this.w/2;
this.y = (pos == "top") ? 0 : H - this.h;
}
//Push two paddles into the paddles array
paddles.push(new createPaddle("bottom"));
paddles.push(new createPaddle("top"));
//Setting up the parameters of ball
ball = {
x: 2,
y: 2,
r: 5,
c: "white",
vx: 4,
vy: 8,
draw: function() {
ctx.beginPath();
ctx.fillStyle = this.c;
ctx.arc(this.x, this.y, this.r, 0, Math.PI*2, false);
ctx.fill();
}
};
//Function for creating particles
function createParticles(x, y) {
this.x = x || 0;
this.y = y || 0;
this.radius = 0.8;
this.vx = -1.5 + Math.random()*3;
this.vy = -1.5 + Math.random()*3;
}
//Draw everything on canvas
function draw() {
paintCanvas();
for(var i = 0; i < paddles.length; i++) {
p = paddles[i];
ctx.fillStyle = "white";
ctx.fillRect(p.x, p.y, p.w, p.h);
}
ball.draw();
update();
}
//Mouse Position track
function trackPosition(e) {
mouse.x = e.pageX;
mouse.y = e.pageY;
}
//function to increase speed after every 5 points
function increaseSpd() {
if(points % 4 == 0) {
ball.vx += (ball.vx < 0) ? -1 : 1;
ball.vy += (ball.vy < 0) ? -2 : 2;
}
}
//function to update positions
function update() {
//Move the paddles on mouse move
if(mouse.x && mouse.y) {
for(var i = 1; i < paddles.length; i++) {
p = paddles[i];
p.x = mouse.x - p.w/2;
}
}
//Move the ball
ball.x += ball.vx;
ball.y += ball.vy;
//Collision with paddles
p1 = paddles[1];
p2 = paddles[2];
if(ball.y >= p1.y - p1.h) {
if(ball.x >= p1.x && ball.x <= (p1.x - 2) + (p1.w + 2)){
ball.vy = -ball.vy;
points++;
increaseSpd();
particlePos.x = ball.x,
particlePos.y = ball.y;
flag = 1;
}
}
else if(ball.y <= p2.y + 2*p2.h) {
if(ball.x >= p2.x && ball.x <= (p2.x - 2) + (p2.w + 2)){
ball.vy = -ball.vy;
points++;
increaseSpd();
particlePos.x = ball.x,
particlePos.y = ball.y;
flag = 1;
}
}
//Collide with walls
if(ball.x >= W || ball.x <= 0)
ball.vx = -ball.vx;
if(ball.y > H || ball.y < 0) {
clearInterval(int);
}
if(flag == 1) {
setInterval(emitParticles(particlePos.x, particlePos.y), 1000/fps);
}
}
function emitParticles(x, y) {
for(var k = 0; k < particlesCount; k++) {
particles.push(new createParticles(x, y));
}
counter = particles.length;
for(var j = 0; j < particles.length; j++) {
par = particles[j];
ctx.beginPath();
ctx.fillStyle = "white";
ctx.arc(par.x, par.y, par.radius, 0, Math.PI*2, false);
ctx.fill();
par.x += par.vx;
par.y += par.vy;
par.radius -= 0.02;
if(par.radius < 0) {
counter--;
if(counter < 0) particles = [];
}
}
}
var int = setInterval(draw, 1000/fps);
Now, my function for emitting particles is on line 156, and I have called this function on line 151. The problem here can be because of I am not resetting the flag variable but I tried doing that and got more weird results. You can check that out here.
By resetting the flag variable, the problem of infinite particles gets resolved but now they only animate and appear when the ball collides with the paddles. So, I am now out of any solution.
I can see 2 problems here.
your main short term problem is your use of setinterval is incorrect, its first parameter is a function.
setInterval(emitParticles(particlePos.x, particlePos.y), 1000/fps);
should be
setInterval(function() {
emitParticles(particlePos.x, particlePos.y);
}, 1000/fps);
Second to this, once you start an interval it runs forever - you don't want every collision event to leave a background timer running like this.
Have an array of particles to be updated, and update this list once per frame. When you make new particles, push additional ones into it.