I made a little game where there are 3 objects
Player: Can move using WASD
Bullet: Moved by an event listener of click
Enemy: Follows the player like a zombie.
How do I make it so the angle of the zombie is the direction he is moving, Rather than always facing the player object?
Fiddle
tick: function() {
enemy.angle = Math.atan2(player.y - enemy.y, player.x - enemy.x);// * (180 / Math.PI);
},
above is the code I use to make the enemy face the player
I understand what you're trying to ask, but really, right now the zombie is always going directly to player, so it would actually be weird if it looked some other way. What you need to do is to actually make zombie slowly turn and walk on it's own, rather than just reducing euclidean distance between it and the player.
First stem, one that I'll do for you is calculating the rotation. Instead of just getting to the correct angle instantly, we have to slowly turn every tick:
tick: function() {
// 1 degree per tick
const rotationSpeed = (1/180)*Math.PI;
// the angle we want - facing the player
const desiredAngle = Math.atan2(player.y - enemy.y, player.x - enemy.x)
// transition angle will be explained below
enemy.angle = transitionAngle(enemy.angle, desiredAngle,rotationSpeed );
},
This is not as easy as it sounds, because you need to calculate whether it's faster to turn left or right to face the player again. Fortunately this is easy to google once you know what you need. I based my function on this answer:
function transitionAngle(fromAngle, toAngle, speed) {
// normalize the angles to 0-360 range
const rad360 = 2*Math.PI;
fromAngle = fromAngle % rad360;
toAngle = toAngle % rad360;
if (fromAngle < toAngle) {
if (Math.abs(fromAngle - toAngle) < Math.PI)
fromAngle += speed;
else fromAngle -= speed;
}
else {
if (Math.abs(fromAngle - toAngle) < Math.PI)
fromAngle -= speed;
else fromAngle += speed;
}
return fromAngle;
}
With that, the zombie slows slowly, and it already looks a little better.
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var bounds = canvas.getBoundingClientRect();
var mouseX = 0.0;
var mouseY = 0.0;
var pressingDown = false;
var pressingUp = false;
var pressingLeft = false;
var pressingRight = false;
var player = {
x: 210,
y: 250,
radius: 17.5,
angle: 0.0,
tick: function() {
this.angle = Math.atan2(mouseY - this.y, mouseX - this.x);
},
draw: function() {
context.fillStyle = "darkred";
context.strokeStyle = "black";
context.translate(this.x, this.y);
context.rotate(this.angle);
context.beginPath();
context.moveTo(this.radius, 0.0);
context.lineTo(-0.5 * this.radius, 0.5 * this.radius);
context.lineTo(-0.5 * this.radius, -0.5 * this.radius);
context.lineTo(this.radius, 0.0);
context.fill();
context.stroke();
context.rotate(-this.angle);
context.translate(-this.x, -this.y);
},
updatePlayerPosition: function() {
if (pressingRight)
player.x += 1;
if (pressingLeft)
player.x -= 1;
if (pressingDown)
player.y += 1;
if (pressingUp)
player.y -= 1;
}
}
var bullet = {
x: player.x,
y: player.y,
dx: 0.0,
dy: 0.0,
radius: 5.0,
tick: function() {
this.x += this.dx;
this.y += this.dy;
if (this.x + this.radius < 0.0 || this.x - this.radius > canvas.width || this.y + this.radius < 0.0 || this.y - this.radius > canvas.height) {
this.dx = 0.0;
this.dy = 0.0;
}
},
render: function() {
context.fillStyle = "darkcyan";
context.strokeStyle = "white";
context.beginPath();
context.arc(this.x, this.y, this.radius, 0.0, 2.0 * Math.PI, false);
context.fill();
context.stroke();
}
};
var enemy = {
x: 200,
y: 300,
radius: 17.5,
angle: 0.0,
tick: function() {
// 1 degree per tick
const rotationSpeed = (1/180)*Math.PI;
const desiredAngle = Math.atan2(player.y - enemy.y, player.x - enemy.x)
enemy.angle = transitionAngle(enemy.angle, desiredAngle,rotationSpeed );
},
draw: function() {
context.fillStyle = "Green";
context.strokeStyle = "darkgreen";
context.translate(this.x, this.y);
context.rotate(this.angle);
context.beginPath();
context.moveTo(this.radius, 0.0);
context.lineTo(-0.5 * this.radius, 0.5 * this.radius);
context.lineTo(-0.5 * this.radius, -0.5 * this.radius);
context.lineTo(this.radius, 0.0);
context.fill();
context.stroke();
context.rotate(-this.angle);
context.translate(-this.x, -this.y);
},
drawEnemy: function(something){
var diffX = player.x - something.x;
var diffY = player.y - something.y
if (diffX > 0)
something.x += .3
else
something.x -= .3;
if (diffY > 0)
something.y += .3
else
something.y -= .3;
}
}
function Refresh() {
context.clearRect(0, 0, canvas.width, canvas.height);
bullet.render();
bullet.tick();
player.draw();
player.tick();
player.updatePlayerPosition();
enemy.draw();
enemy.tick();
enemy.drawEnemy(enemy);
}
setInterval(Refresh, 0)
window.onmousemove = function(e) {
mouseX = e.clientX - bounds.left;
mouseY = e.clientY - bounds.top;
}
document.onkeydown = function(event) {
if (event.keyCode === 83) //s
pressingDown = true;
else if (event.keyCode === 87) //w
pressingUp = true;
else if (event.keyCode === 65) //a
pressingLeft = true;
else if (event.keyCode === 68) //d
pressingRight = true;
}
document.onkeyup = function(event) {
if (event.keyCode === 83) //s
pressingDown = false;
else if (event.keyCode === 87) //w
pressingUp = false;
else if (event.keyCode === 65) //a
pressingLeft = false;
else if (event.keyCode === 68) //d
pressingRight = false;
}
window.onmousedown = function(e) {
// The mouse pos - the player pos gives a vector
// that points from the player toward the mouse
var x = mouseX - player.x;
var y = mouseY - player.y;
// Using pythagoras' theorm to find the distance (the length of the vector)
var l = Math.sqrt(x * x + y * y);
// Dividing by the distance gives a normalized vector whose length is 1
x = x / l;
y = y / l;
// Reset bullet position
bullet.x = player.x;
bullet.y = player.y;
// Get the bullet to travel towards the mouse pos with a new speed of 10.0 (you can change this)
bullet.dx = x * 10.0;
bullet.dy = y * 10.0;
}
function transitionAngle(fromAngle, toAngle, speed) {
// normalize the angles to 0-360 range
const rad360 = 2*Math.PI;
fromAngle = fromAngle % rad360;
toAngle = toAngle % rad360;
if (fromAngle < toAngle) {
if (Math.abs(fromAngle - toAngle) < Math.PI)
fromAngle += speed;
else fromAngle -= speed;
}
else {
if (Math.abs(fromAngle - toAngle) < Math.PI)
fromAngle -= speed;
else fromAngle += speed;
}
return fromAngle;
}
<canvas id="canvas" style="border:2px solid darkred" width="700" height="500"></canvas>
The second part of the job - and I'm leaving that to you - is to make zombie actually walk in the direction it's facing. To do that, assign it a walking speed and then use sin and cos of the zombie's angle multiplied with the speed to get X and Y offsets. Again, google is your friend, just type "move by speed and angle javascript".
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>
How do I make the green triangle face towards/look at the tip of the red triangle?
Fiddle
rotation: function() {
enemy.angle = Math.atan2(player.y - enemy.y, player.x - enemy.x) * (180 / Math.PI);
},
green triangle is under enemy.
I use this function but I can tell I am doing it all wrong.
If you can help explain what you did and how you did it, I would be greatful thanks!
You need to add this line to enemy.tick function
enemy.angle = Math.atan2(player.y - this.y, player.x - this.x);
<html>
<head>
<title>Black Friday</title>
</head>
<body>
<style>
</style>
<canvas id="canvas" style="border:2px solid darkred" width="700" height="500"></canvas>
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var bounds = canvas.getBoundingClientRect();
var mouseX = 0.0;
var mouseY = 0.0;
var pressingDown = false;
var pressingUp = false;
var pressingLeft = false;
var pressingRight = false;
var player = {
x: 210,
y: 250,
radius: 17.5,
angle: 0.0,
tick: function() {
this.angle = Math.atan2(mouseY - this.y, mouseX - this.x);
},
draw: function() {
context.fillStyle = "darkred";
context.strokeStyle = "black";
context.translate(this.x, this.y);
context.rotate(this.angle);
context.beginPath();
context.moveTo(this.radius, 0.0);
context.lineTo(-0.5 * this.radius, 0.5 * this.radius);
context.lineTo(-0.5 * this.radius, -0.5 * this.radius);
context.lineTo(this.radius, 0.0);
context.fill();
context.stroke();
context.rotate(-this.angle);
context.translate(-this.x, -this.y);
},
updatePlayerPosition: function() {
if (pressingRight)
player.x += 1;
if (pressingLeft)
player.x -= 1;
if (pressingDown)
player.y += 1;
if (pressingUp)
player.y -= 1;
}
}
var bullet = {
x: player.x,
y: player.y,
dx: 0.0,
dy: 0.0,
radius: 5.0,
tick: function() {
this.x += this.dx;
this.y += this.dy;
if (this.x + this.radius < 0.0 || this.x - this.radius > canvas.width || this.y + this.radius < 0.0 || this.y - this.radius > canvas.height) {
this.dx = 0.0;
this.dy = 0.0;
}
},
render: function() {
context.fillStyle = "darkcyan";
context.strokeStyle = "white";
context.beginPath();
context.arc(this.x, this.y, this.radius, 0.0, 2.0 * Math.PI, false);
context.fill();
context.stroke();
}
};
var enemy = {
x: 200,
y: 300,
radius: 17.5,
angle: 0.0,
tick: function() {
enemy.angle = Math.atan2(player.y - this.y, player.x - this.x);
},
draw: function() {
context.fillStyle = "Green";
context.strokeStyle = "darkgreen";
context.translate(this.x, this.y);
context.rotate(this.angle);
context.beginPath();
context.moveTo(this.radius, 0.0);
context.lineTo(-0.5 * this.radius, 0.5 * this.radius);
context.lineTo(-0.5 * this.radius, -0.5 * this.radius);
context.lineTo(this.radius, 0.0);
context.fill();
context.stroke();
context.rotate(-this.angle);
context.translate(-this.x, -this.y);
}
}
function Refresh() {
context.clearRect(0, 0, canvas.width, canvas.height);
bullet.render();
bullet.tick();
player.draw();
player.tick();
player.updatePlayerPosition();
enemy.draw();
enemy.tick();
}
setInterval(Refresh, 0)
window.onmousemove = function(e) {
mouseX = e.clientX - bounds.left;
mouseY = e.clientY - bounds.top;
}
document.onkeydown = function(event) {
if (event.keyCode === 83) //s
pressingDown = true;
else if (event.keyCode === 87) //w
pressingUp = true;
else if (event.keyCode === 65) //a
pressingLeft = true;
else if (event.keyCode === 68) //d
pressingRight = true;
}
document.onkeyup = function(event) {
if (event.keyCode === 83) //s
pressingDown = false;
else if (event.keyCode === 87) //w
pressingUp = false;
else if (event.keyCode === 65) //a
pressingLeft = false;
else if (event.keyCode === 68) //d
pressingRight = false;
}
window.onmousedown = function(e) {
// The mouse pos - the player pos gives a vector
// that points from the player toward the mouse
var x = mouseX - player.x;
var y = mouseY - player.y;
// Using pythagoras' theorm to find the distance (the length of the vector)
var l = Math.sqrt(x * x + y * y);
// Dividing by the distance gives a normalized vector whose length is 1
x = x / l;
y = y / l;
// Reset bullet position
bullet.x = player.x;
bullet.y = player.y;
// Get the bullet to travel towards the mouse pos with a new speed of 10.0 (you can change this)
bullet.dx = x * 10.0;
bullet.dy = y * 10.0;
}
</script>
</body>
</html>
Hope it helps.
I've been working on some studies with animation and with the help of some jquery I've created an animation where when a square that is moved by the users mouse through a mousemove jquery event collides with another square on the screen (square2) thats been hit will move a certain length and if the hitbox is struck near its edges than the object is expected to rotate. The problem ive been running into is that when the object rotates it creates a pseudo afterimage of the outline of the square. At first I thought that I could remove the afterImage by using a clearRect() method to encompass a larger area around square2, but doing this not only leaves my problem unresolved, but also makes a part of my first square invisible which is undesired. If anybody could hep me figure out where ive gone wrong in this code, would definitely appreciate it fellas.
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
canvas.width = 1000;
canvas.height = 400;
var width = canvas.width;
var height = canvas.height;
var particles = [];
var mouseSize = 50;
var isColliding = false;
var mouseX;
var mouseY;
var animationForward = false;
function particle() {
var particle = {
originX: width / 2,
originY: height / 2,
x: width / 2,
y: height / 2,
size: 30,
centerPointX: this.x + this.size / 2,
centerPointY: this.y + this.size / 2,
decay: .98,
vx: 0,
vy: 0,
rotate: 0,
vr: 0,
draw: function() {
ctx.fillStyle = "white";
ctx.fillRect(this.x, this.y, this.size, this.size)
// rotation logic
// method found at http://stackoverflow.com/questions/2677671/how-do-i-rotate-a-single-object-on-an-html-5-canvas
function drawImageRot(x, y, width, height, deg) {
ctx.clearRect(x, y, width, height);
//Convert degrees to radian
var rad = deg * Math.PI / 180;
//Set the origin to the center of the image
ctx.translate(x + width / 2, y + height / 2);
//Rotate the canvas around the origin
ctx.rotate(rad);
//draw the image
ctx.fillRect(width / 2 * (-1), height / 2 * (-1), width, height);
//reset the canvas
ctx.rotate(rad * (-1));
ctx.translate((x + width / 2) * (-1), (y + height / 2) * (-1));
}
//check for collision
if (mouseX < particles[0].x + particles[0].size &&
mouseX + mouseSize > particles[0].x &&
mouseY < particles[0].y + particles[0].size &&
mouseSize + mouseY > particles[0].y) {
isColliding = true;
} else {
isColliding = false;
}
//controlling velocity dependending on location of collision.
if (isColliding) {
//x axis
animationForward = true;
// checking below to see if mouseRect is hitting near the center of particleRect.
// if it hits near center the vy or vx will not be as high depending on direction and if it //does not than we will make square rotate
if (mouseX < this.x) {
this.vr = 3 + Math.random() * 10
if (mouseX + mouseSize / 2 > this.x) {
this.vx = 0 + Math.random() * 2;
} else {
this.vx = 3 + Math.random() * 3;
}
} else {
this.vr = -3 - Math.random() * 10
if (mouseX + mouseSize / 2 < this.x + this.size) {
this.vx = 0 - Math.random() * 2;
} else {
this.vx = -3 - Math.random() * 3;
}
}
//y axis checking
if (mouseY < this.y) {
if (mouseY + mouseSize / 2 > this.y) {
this.vy = 0 + Math.random() * 2;
} else {
this.vy = 3 + Math.random() * 3;
}
} else {
if (mouseY + mouseSize / 2 < this.y + this.size) {
this.vy = 0 - Math.random() * 2;
} else {
this.vy = -3 - Math.random() * 3;
}
}
}
//decay all motions each frame while animation is forward
if (animationForward) {
this.vx *= this.decay;
this.vy *= this.decay;
this.vr *= this.decay;
}
//when animation is done, set all velocities to 0
if (this.x != this.originX && Math.abs(this.vx) < .1 && this.y != this.originY && Math.abs(this.vy) < .1) {
animationForward = false;
this.vx = 0;
this.vy = 0;
this.vr = 0
}
//x check to see if animation over. if it is slowly put square back to original location
if (this.x != this.originX && !animationForward) {
if (this.x > this.originX) {
this.vx = -1;
}
if (this.x < this.originX) {
this.vx = 1;
}
if (this.x > this.originX && this.x - this.originX < 1) {
this.vx = 0;
this.x = this.originX;
}
if (this.x < this.originX && this.originX - this.x < 1) {
this.vx = 0;
this.x = this.originX;
}
}
// end x collison
// y check to see if animation over
if (this.y != this.originX && !animationForward) {
if (this.y > this.originY) {
this.vy = -1;
}
if (this.y < this.originY) {
this.vy = 1;
}
if (this.y > this.originY && this.y - this.originY < 1) {
this.vy = 0;
this.y = this.originY;
}
if (this.y < this.originY && this.originY - this.y < 1) {
this.vy = 0;
this.y = this.originY;
}
}
// end y collison
//check rotation
if (this.rotate != 0 && !animationForward) {
this.rotate = Math.round(this.rotate);
if (this.rotate < 0) {
if (this.rotate < -300) {
this.rotate += 10
} else if (this.rotate < -200) {
this.rotate += 7
} else if (this.rotate < -125) {
this.rotate += 5
} else if (this.rotate < -50) {
this.rotate += 3
} else {
this.rotate++;
}
} else {
if (this.rotate > 300) {
this.rotate -= 10;
} else if (this.rotate > 200) {
this.rotate -= 7
} else if (this.rotate > 125) {
this.rotate -= 5
} else if (this.rotate > 50) {
this.rotate -= 3
} else {
this.rotate--;
}
}
}
// move the rect based off of previous set conditions and make square rotate if edges hit
this.x += this.vx;
this.y += this.vy;
this.rotate += this.vr;
drawImageRot(this.x, this.y, this.size, this.size, this.rotate);
// boundary control
if (this.x + this.size > width || this.x < 0) {
this.vx = -this.vx * 2
}
if (this.y + this.size > height || this.y < 0) {
this.vy = -this.vy * 2
}
}
}
return particle;
}
function createParticles() {
particles.push(particle())
//wouldnt be too hard to put more particles. would have to go back and change the isColliding and animationForward global variable and make each object have their own to check. also would have to go back and implement for loops wherever i mention an element in my array
}
createParticles();
function draw() {
console.log(particles[0].rotate);
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = 'white';
ctx.fillRect(mouseX, mouseY, mouseSize, mouseSize);
particles[0].draw();
requestAnimationFrame(draw);
}
$("#canvas").mousemove(function(event) {
mouseX = event.pageX;
mouseY = event.pageY;
})
window.onload = draw();
I think your problem is right here:
draw: function() {
ctx.fillStyle = "white";
ctx.fillRect(this.x, this.y, this.size, this.size)
....
You are drawing 2nd shape here. Comment this lines and pseudo image should be gone.
so I've put some simple collision detection code on a canvas: if my obstacle car sprites touch my user car sprite, the obstacle car stops. For some reason, when the cars are close, the collision is only detected if I am pressing the keys that my user car uses to move (up, down, left, and right arrow keys). How can I get this function to work all the time, regardless of if I am pressing down the keys to move?
collision detection code:
//Collide test
function firstObstaclecollideTest () {
if (Math.abs(x1 - (usercar.width / 2) - x) < usercar.width && Math.abs(y1 - (usercar.height / 2) - y) < usercar.height) {
mod1 = 0;
speed1 = 0;
}
requestAnimationFrame(firstObstaclecollideTest);
}
requestAnimationFrame(firstObstaclecollideTest);
function secondObstaclecollideTest () {
if (Math.abs(x2 - (usercar.width / 2) - x) < usercar.width && Math.abs(y2 - (usercar.height / 2) - y) < usercar.height) {
mod2 = 0;
speed2 = 0;
}
requestAnimationFrame(secondObstaclecollideTest);
}
requestAnimationFrame(secondObstaclecollideTest);
Full Code: http://jsbin.com/dofihiwize/1/edit?output
Your code is a bit messy i fear :
- You are triggering 4 animation loops : have only one loop to avoid headaches.
- You are duplicating quite some code : go for a Car class to clean things up.
- There are several confusion of concern : for instance, the function drawing the car is clearing the canvas, and also drawing the time elapsed. The function names are also misleading (gameStart is a game loop, ... ).
updated fiddle is here :
http://jsbin.com/bafulazose/1/edit?js,output
//Setting the canvas and context
var canvas = document.getElementById('background');
var context = canvas.getContext('2d');
//================
// CAR Class
//================
//Uploading obstacle car
var carImage = new Image();
carImage.src = "http://www.i2clipart.com/cliparts/f/e/3/a/128135fe3a51f073730a8d561282d05b4f35ab.png";
function Car(x, y, speed, mod, angle) {
this.x = x; // x center of car
this.y = y; // y center of car
this.speed = speed;
this.mod = mod;
this.angle = angle;
this.move = function () {
this.x += (this.speed * this.mod) * Math.cos(Math.PI / 180 * this.angle);
this.y += (this.speed * this.mod) * Math.sin(Math.PI / 180 * this.angle);
if (this.y > context.canvas.height + 150) {
this.y = -carImage.height;
this.x = Math.floor(Math.random() * canvas.width);
}
};
this.draw = function () {
context.save();
context.translate(this.x, this.y);
context.rotate(this.angle* Math.PI / 180);
context.drawImage(carImage, -(carImage.width / 2), -(carImage.height / 2));
context.strokeRect(-(carImage.width / 2), -(carImage.height / 2), carImage.width , carImage.height);
context.restore();
};
this.testCollision = function(other) {
var dx = Math.abs(this.x - other.x );
var dy = Math.abs(this.y - other.y );
if ( dx < carImage.width && dy < carImage.height) {
this.mod = 0;
this.speed = 0;
}
};
}
//================
//ENTER: USER CAR
//================
var userCar = new Car(450, 550, 10, -1, -90);
setupKeys(userCar);
//=====================
//ENTER: OBSTACLE CAR 1
//=====================
var obstacleCar1 ;
//======================
//ENTER: OBSTACLE CAR 2
//======================
var obstacleCar2 ;
function setupGame () {
obstacleCar1 = new Car(200, 5, 5, 1, 90);
obstacleCar2 = new Car(340, 5, 5, 1, 90);
gameOver = false;
startTime = Date.now();
score = 0;
}
//=========================
//Properties for score keep
//=========================
var score;
var startTime;
var gameOver;
var spaceBarPressed = false;
//=========================
// Launch the game
//=========================
setupGame () ;
var gameLoopInterval = setInterval(gameLoop, 30);
//===========================
//Draw Final and Elasped Time
//===========================
function drawElapsedTime() {
context.save();
context.fillStyle = "black";
context.font = "30px Verdana";
context.fillText(parseInt((Date.now() - startTime) / 1000) + " secs", canvas.width - 120, 40);
context.restore();
}
function drawFinalScore() {
context.save();
context.fillStyle = "black";
context.font = "30px Verdana";
context.fillText("Game Over: " + score + " secs", 100, 100);
context.font = "12px Verdana";
context.fillText("Press space to restart", 190, 150);
context.restore();
}
//========================
// Game loop
//========================
function gameLoop() {
context.clearRect(0, 0, canvas.width, canvas.height);
if (gameOver) {
drawFinalScore();
if (spaceBarPressed) {
setupGame ();
}
return;
}
obstacleCar1.move();
obstacleCar2.move();
obstacleCar1.testCollision(userCar);
obstacleCar2.testCollision(userCar);
if (obstacleCar1.speed===0 && obstacleCar2.speed===0) {
score = parseInt((Date.now() - startTime) / 1000);
gameOver = true;
spaceBarPressed = false;
}
obstacleCar1.draw();
obstacleCar2.draw();
userCar.draw();
drawElapsedTime();
}
//========================
// Keys handling
//========================
function setupKeys(target) {
var cancelledKeys = [32, 37, 38, 39, 40];
function keyUpHandler(event) {
if (event.keyCode == 38 || event.keyCode == 40) {
mod = 0;
}
}
function keyDownHandler(event) {
var keyCode = event.keyCode;
if (keyCode == 37) {
target.x -= target.speed;
}
if (keyCode == 39) {
target.x += target.speed;
}
if (keyCode == 32) {
spaceBarPressed = true;
}
// space and arrow keys
if (cancelledKeys.indexOf(keyCode) > -1) {
event.preventDefault();
}
}
//Event listeners for keys
window.addEventListener("keydown", keyDownHandler, false);
window.addEventListener("keyup", keyUpHandler, false);
}
Edit :
Morning coffee improvements (:-)) :
- moves are smooth ( requestAnimationFrame + position += speed * time elapsed)
- keys are handled properly
- cars have a clean spawn function
- cars are now in a 'scene graph' (an array) so we can test intersection
- road !! (with roadPos, roadSpeed)
http://jsbin.com/zujecerehe/1/edit?js,output
I am trying to modified this effect to work within my canvas. However, I can't seem to get the mouse position to work properly. The hover area isn't contained to my canvas.
Here's a CSSdeck of how i tried to implement it: http://cssdeck.com/labs/ukktjtis
Effect:
function hoverText(){
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
keyword = "MacroPlay Games",
imageData,
density = 3,
mouse = {},
hovered = false,
colors = ["0,120,232", "8,200,255", "30,140,255"],
minDist = 20,
bounceFactor = 0.7;
var W = window.innerWidth, H = window.innerHeight;
canvas.width = W;
canvas.height = H;
document.addEventListener("mousemove", function(e) {
mouse.x = e.pageX-50;
mouse.y = e.pageY+200;
}, false);
// Particle Object
var Particle = function() {
this.w = Math.random() * 10.5;
this.h = Math.random() * 10.5;
this.x = -W;
this.y = -H;
this.free = false;
this.vy = -5 + parseInt(Math.random() * 10) / 2;
this.vx = -4 + parseInt(Math.random() * 8);
// Color
this.a = Math.random();
this.color = colors[parseInt(Math.random()*colors.length)];
this.setPosition = function(x, y) {
this.x = x;
this.y = y;
};
this.draw = function() {
ctx.fillStyle = "rgba("+this.color+","+this.a+")";
ctx.fillRect(this.x, this.y, this.w, this.h);
}
};
var particles = [];
// Draw the text
function drawText() {
ctx.clearRect(0, 0, W, H);
ctx.fillStyle = "#000000";
ctx.font = "100px 'Arial', sans-serif";
ctx.textAlign = "center";
ctx.fillText(keyword, W/2, H/2);
}
// Clear the canvas
function clear() {
ctx.clearRect(0, 0, W, H);
}
// Get pixel positions
function positionParticles() {
// Get the data
imageData = ctx.getImageData(0, 0, W, W);
data = imageData.data;
// Iterate each row and column
for (var i = 0; i < imageData.height; i += density) {
for (var j = 0; j < imageData.width; j += density) {
// Get the color of the pixel
var color = data[((j * ( imageData.width * 4)) + (i * 4)) - 1];
// If the color is black, draw pixels
if (color == 255) {
particles.push(new Particle());
particles[particles.length - 1].setPosition(i, j);
}
}
}
}
drawText();
positionParticles();
// Update
function update() {
clear();
for(i = 0; i < particles.length; i++) {
var p = particles[i];
if(mouse.x > p.x && mouse.x < p.x + p.w && mouse.y > p.y && mouse.y < p.y + p.h)
hovered = true;
if(hovered == true) {
var dist = Math.sqrt((p.x - mouse.x)*(p.x - mouse.x) + (p.y - mouse.y)*(p.y - mouse.y));
if(dist <= minDist)
p.free = true;
if(p.free == true) {
p.y += p.vy;
p.vy += 0.15;
p.x += p.vx;
// Collision Detection
if(p.y + p.h > H) {
p.y = H - p.h;
p.vy *= -bounceFactor;
// Friction applied when on the floor
if(p.vx > 0)
p.vx -= 0.1;
else
p.vx += 0.1;
}
if(p.x + p.w > W) {
p.x = W - p.w;
p.vx *= -bounceFactor;
}
if(p.x < 0) {
p.x = 0;
p.vx *= -0.5;
}
}
}
ctx.globalCompositeOperation = "lighter";
p.draw();
}
}
(function animloop(){
requestAnimFrame(animloop);
update();
})();
}
It's highly advised you use jquery (or some js lib) to avoid cross-browser issues like getting the mouse position.
You can easily get the mouse position in any browser using jquery like this:
// get the position of the canvas relative to the web page
var canvasOffset=$("#canvas").offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
// then in the mouse handler, get the exact mouse position like this:
function handleMouseDown(e){
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// Put your mousedown stuff here
}
// tell the browser to send mousedown events to the handleMouseDown function
$("#canvas").mousedown(function(e){handleMouseDown(e);});
I personally prefer a library like hammer.js. I've use it for all my projects - check it out, it's pretty good.