I'm trying to get the character to rotate the character object based on where the mouse is.
So far I got it to rotate incrementally without mouse position. I was checking if it effected my zombie's chasing capabilities.
My script
let player, zombie, mouseX, mouseY;;
let bgCanvas = document.getElementById('backgroundCan');
function startGame() {
document.getElementById("startScreen").style.display = "none";
player = new playerComponent(350, 220);
zombie = new zombieComponent(750, 220);
gameArea.start();
}
let gameArea = {
canvas : document.createElement("canvas"),
start : function() {
this.canvas.width = 800;
this.canvas.height = 500;
this.canvas.style = "position: absolute";
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[2]);
this.interval = setInterval(updateArea, 20);
window.addEventListener('keydown', function (e) {
gameArea.keys = (gameArea.keys || []);
gameArea.keys[e.keyCode] = true;
})
window.addEventListener('keyup', function (e) {
gameArea.keys[e.keyCode] = false;
});
this.canvas.addEventListener("mousemove", function(e){
mouseX = e.clientX - ctx.canvas.offsetLeft;
mouseY = e.clientY - ctx.canvas.offsetTop;
});
this.canvas.addEventListener("mousedown", function(e){
let gShot = new Audio('assets/shot.mp3');
gShot.play();
var mX = e.clientX - ctx.canvas.offsetLeft;
var mY = e.clientY - ctx.canvas.offsetTop;
if(mX >= zombie.x && mX < zombie.x+zombie.w && mY >= zombie.y && mY < zombie.y+zombie.h){
if(zombie.health > 0){
zombie.health += -1;
zombie.speedX += 10;
zombie.newPos();
zombie.update();
}
else {
zombie.status = "dead";
}
}
});
},
clear : function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function playerComponent(x, y){
this.x = x;
this.y = y;
this.speedX = 0;
this.speedY = 0;
this.health = 10;
this.status = "alive";
let rotNum = 1;
this.update = function(){
ctx = gameArea.context;
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(rotNum * Math.PI / 180);
playerSprite = new Image();
playerSprite.src = "assets/playerGun.png";
ctx.drawImage(playerSprite, 0, 0);
ctx.restore();
rotNum++;
}
this.newPos = function() {
this.x += this.speedX;
this.y += this.speedY;
}
}
function updateArea() {
gameArea.clear();
if(player.status == "alive"){
player.speedX = 0;
player.speedY = 0;
if (gameArea.keys && gameArea.keys[65]) {
if(player.x > 20){
player.speedX = -3;
}
}
if (gameArea.keys && gameArea.keys[68]) {
if(player.x < 740){
player.speedX = 3;
}
}
if (gameArea.keys && gameArea.keys[87]) {
if(player.y > 20){
player.speedY = -3;
}
}
if (gameArea.keys && gameArea.keys[83]) {
if(player.y < 445){
player.speedY = 3;
}
}
player.newPos();
player.update();
}
if(zombie.status == "alive"){
if(zombie.x > player.x){
zombie.speedX = -1;
}
else{
zombie.speedX = 1;
}
if(zombie.y > player.y){
zombie.speedY = -1;
}
else{
zombie.speedY = 1;
}
zombie.newPos();
zombie.update();
}
else{
zombie.update();
}
}
So far, I have the mouse position on the canvas and am able to rotate the character, but I just don't how to connect the two. How should use the mouse position and the character position to rotate towards the mouse? The character is initially facing right (i think?), at least the sprite initially is.
Here's an image illustrating the situation:
You have the mouseY, playerY and mouseX, playerX
Therefore you can calculate the height and base of the triangle,
Thus the angle with
However, since in the second and third quadrants y/x will return an angle in the first and fourth quadrants, you need to use the Math.atan2(y,x) function in Javascript, not Math.atan(y/x). This will give you an angle between -180 and 180 instead of between -90 and 90.
Atan Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2
Then all you have to do is rotate based on the angle!
(P.S. Remember that you will have to convert between radians and degrees)
Related
I am very new to coding. I am trying to make a game using java in notepad++. I cant seem to get it so that when the red square (player) hits the purple (TopEdge) the red square will not stop completely but also wont travel through the purple. Like a wall. Right now when the red square hits the purple, you cant move it anymore. I have tried almost everything i could find on the internet. Here is my code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta charset="utf-8">
<style>
canvas {
border:4px solid #000000;
background-color: #1FB026;
}
</style>
</head>
<body onload="startGame()">
<script>
var player;
var TopEdge;
function startGame() {
player = new component(30, 30, "red", 10, 120);
TopEdge = new component(10000, 300, "purple", 0, -200);
myGameArea.start();
}
var myGameArea = {
canvas : document.createElement("canvas"),
start : function() {
this.canvas.width = 1150;
this.canvas.height = 500;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas,
document.body.childNodes[0]);
this.interval = setInterval(updateGameArea, 10);
window.addEventListener('keydown', function (e) {
myGameArea.keys = (myGameArea.keys || []);
myGameArea.keys[e.keyCode] = (e.type == "keydown");
})
window.addEventListener('keyup', function (e) {
myGameArea.keys[e.keyCode] = (e.type == "keydown");
})
},
clear : function(){
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
},
stop : function(){
clearInterval(this.interval);
}
}
function component(width, height, color, x, y) {
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.update = function() {
ctx = myGameArea.context;
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
this.newPos = function() {
this.X += this.speedX;
this.Y += this.speedY;
let playerX = this.X + this.speedX;
let playerY = this.Y + this.speedY;
if (playerX >= 0 && this.width + playerX <= this.gamearea.canvas.width)
{
this.X = playerX;
}
if (playerY >= 0 && this.height + playerY <= this.gamearea.canvas.height)
{
this.Y = playerY;
}
}
this.crashWith = function(otherobj) {
var myleft = this.x;
var myright = this.x + (this.width);
var mytop = this.y;
var mybottom = this.y + (this.height);
var otherleft = otherobj.x;
var otherright = otherobj.x + (otherobj.width);
var othertop = otherobj.y;
var otherbottom = otherobj.y + (otherobj.height);
var crash = true;
if ((mybottom < othertop) ||
(mytop > otherbottom) ||
(myright < otherleft) ||
(myleft > otherright)) {
crash = false;
}
return crash;
}
}
function stopY() {
player.speedY = 0;
}
function updateGameArea() {
if (player.crashWith(TopEdge)) {
} else {
myGameArea.clear();
TopEdge.update();
player.x += player.speedX;
player.y += player.speedY;
player.update();
}
TopEdge.update();
player.speedX = 0;
player.speedY = 0;
if (myGameArea.keys && myGameArea.keys[65]) {player.speedX = -2.5; }
if (myGameArea.keys && myGameArea.keys[68]) {player.speedX = 2.5; }
if (myGameArea.keys && myGameArea.keys[87]) {player.speedY = -2.5; }
if (myGameArea.keys && myGameArea.keys[83]) {player.speedY = 2.5; }
player.newPos();
player.update();
}
</script>
</body>
</html>
The problem starts with your updateGameArea() function.
If your the player crashes with the TopEdge then you do nothing. Your creating a problem with your else-part over there. It will never reach the else like this, so nothing while change, so next time it still won't move and again it will not reach the else.....
How about removing the else part. Just check for everything all the time, but don't allow it to move up when it reached the TopEdge.
This should help.
function updateGameArea() {
// I'd prefer these calls at the end of the function block
// but right now your program would start kinda buggy then
myGameArea.clear();
player.newPos();
player.update();
TopEdge.update();
// In default state (nothing pressed) player shouldn't move up or down
player.speedX = 0;
player.speedY = 0;
// If the player does press a key set correct speed/direction
if (myGameArea.keys[65]) {player.speedX = -2.5; }
if (myGameArea.keys[68]) {player.speedX = 2.5; }
if (myGameArea.keys[87]) {player.speedY = -2.5; }
if (myGameArea.keys[83]) {player.speedY = 2.5; }
// Check if player reached the top
// If it did, then it shouldn't be able to climb any higher.
// So even if the player presses the up-button,
// the vertical speed should still not be aloud to change.
if (player.crashWith(TopEdge) && myGameArea.keys[87]) {
player.speedY = 0;
}
// Move the position of the player
player.x += player.speedX; //
player.y += player.speedY;
}
I created a collision detection between Snake and BasicEnemy. I created a for loop to make five different enemies but the collision detection doesn't get called on any of the enemies that were created from the for loop. The collision only works with the one BasicEnemy object. Why isn't collision function being called for all of the enemies inside the array? Thank you.
Sketch.js
var snake;
var food;
var basicEnemy;
var scl = 20;
var enemies = [];
function setup() {
createCanvas(600, 500);
snake = new Snake();
basicEnemy = new BasicEnemy();
//** CREATE FIVE ENEMIES **
for (var i = 0; i < 5; i++) {
enemies[i] = new BasicEnemy();
}
}
// **FUNCTION WHEN SNAKE HITS ENEMY**
function collision() {
console.log("hit!");
}
function draw() {
background(51);
//Draw snake
snake.update();
snake.show();
//Draw basicEnemy
basicEnemy.update();
basicEnemy.show();
//** LOOP THROUGH ENEMIES AND UPDATE AND SHOW **
for (var i = 0; i < enemies.length; i++) {
enemies[i].show();
enemies[i].update();
if (enemies[i].hits(snake)) {
collision();
}
}
}
function keyPressed() {
if (keyCode === UP_ARROW){
snake.dir(0, -1);
} else if (keyCode === DOWN_ARROW) {
snake.dir(0, 1);
} else if (keyCode === LEFT_ARROW) {
snake.dir(-1 , 0);
} else if (keyCode === RIGHT_ARROW) {
snake.dir(1 , 0);
}
}
BasicEnemy.js
function BasicEnemy() {
this.x = random(700);
this.y = random(700);
this.velX = 15;
this.velY = 15;
}
//** FUNCTION TO CHECK IF ENEMY AND SNAKE ARE IN THE SAME LOCATION **
this.hits = function (pos) {
var = d = dist(this.x, this.y, pos.x, pos.y);
if(d < 1) {
return true;
} else {
return false;
}
}
this.show = function () {
fill(255, 0, 100);
rect(this.x, this.y, scl, scl);
}
Snake.js
function Snake() {
this.x = 0;
this.y = 0;
this.xspeed = 1;
this.yspeed = 0;
this.update = function() {
this.x = this.x + this.xspeed * scl;
this.y = this.y + this.yspeed * scl;
this.x = constrain(this.x, 0, width - scl);
this.y = constrain(this.y, 0, height - scl);
}
this.show = function() {
fill(255);
rect(this.x, this.y, scl, scl);
}
this.dir = function (x , y) {
this.xspeed = x;
this.yspeed = y;
}
}
Because you're essentially checking for the distance between the top left corners of the snake and the enemy, this'll only return true, if they completely overlap.
Use an AABB collision detection instead:
return this.x + scl >= pos.x && this.x <= pos.x + scl && this.y + scl >= pos.y && this.y <= pos.y + scl;
This returns true, if the first rectangle contains the second rectangle.
MDN says:
One of the simpler forms of collision detection is between two rectangles that are axis aligned — meaning no rotation. The algorithm works by ensuring there is no gap between any of the 4 sides of the rectangles. Any gap means a collision does not exist.
I am a new in javascript and trying to find out how to make a collision with ball and plank which will stop the game and alert player with something like "You lost". But I only want red balls to hit the plank and blue to pass on without touching. Here is code that I am working on. (I dont mind if you could help to do collision only with both balls)
var spawnRate = 100;
var spawnRateOfDescent = 2;
var lastSpawn = -10;
var objects = [];
var startTime = Date.now();
function spawnRandomObject() {
var t;
if (Math.random() < 0.50) {
t = "red";
} else {
t = "blue";
}
var object = {
type: t,
x: Math.random() * (canvas.width - 30) + 15,
y: 0
}
objects.push(object);
}
function animate() {
var time = Date.now();
if (time > (lastSpawn + spawnRate)) {
lastSpawn = time;
spawnRandomObject();
}
for (var i = 0; i < objects.length; i++) {
var object = objects[i];
object.y += spawnRateOfDescent;
ctx.beginPath();
ctx.arc(object.x, object.y, 8, 0, Math.PI * 2);
ctx.closePath();
ctx.fillStyle = object.type;
ctx.fill();
}
}
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var paddleHeight = 10;
var paddleWidth = 60;
var paddleY = 480
var paddleX = (canvas.width-paddleWidth)/2;
var rightPressed = false;
var leftPressed = false;
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
function keyDownHandler(e) {
if(e.keyCode == 39) {
rightPressed = true;
}
else if(e.keyCode == 37) {
leftPressed = true;
}
}
function keyUpHandler(e) {
if(e.keyCode == 39) {
rightPressed = false;
}
else if(e.keyCode == 37) {
leftPressed = false;
}
}
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddleX, paddleY, paddleWidth, paddleHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawPaddle();
animate();
if(rightPressed && paddleX < canvas.width-paddleWidth) {
paddleX += 3;
}
else if(leftPressed && paddleX > 0) {
paddleX -= 3;
}
}
setInterval(draw, 10);
Thanks!
If you have an object like this:
let ball = { type: 'red', x: 10, y: 10, width: 10, height: 10 };
You might want to consider adding a method to this to check if it overlaps any other rectangle:
ball.overlapsBall = function( otherBall ){
return !(
otherBall.x + otherBall.width < this.x
&& otherBall.y + otherBall.height < this.y
&& otherBall.y > this.y + this.height
&& otherBall.x > this.x + this.height
);
}
You do this by checking if it does not overlap, which is only true if one box is entirely outside of the other (have a read through the if statement and try to visualise it, its actually rather simple)
In your draw function you could now add a loop to see if any overlap occurs:
var overlap = objects.filter(function( ball ) { return paddle.overlapsBall( ball ) });
You could even place an if statement to check it's type! (The filter will take you entire array of balls and check the overlaps, and remove anything from the array that does not return true. Now you can use overlaps.forEach(function( ball ){ /* ... */}); to do something with all the balls that overlapped your paddle.)
One last thing, if you are planning on doing this with many objects you might want to consider using a simple class like this for every paddle or ball you make:
class Object2D {
constructor(x = 0, y = 0;, width = 1, height = 1){
this.x = x;
this.y = x;
this.width = width;
this.height = height;
}
overlaps( otherObject ){
!( otherObject.x + otherObject.width < this.x && otherObject.y + otherObject.height < this.y && otherObject.y > this.y + this.height && otherObject.x > this.x + this.height );
}
}
This allows you to this simple expression to create a new object that automatically has a method to check for overlaps with similar objects:
var paddle = new Object2D(0,0,20,10);
var ball = new Object2D(5,5,10,10);
paddle.overlaps( ball ); // true!
On top of that, you are ensured that any Object2D contains the values you will need for your calculations. You can check if this object is if the right type using paddle instanceof Object2D (which is true).
Note Please note, as #Janje so continuously points out in the comments below, that we are doing a rectangle overlap here and it might create some 'false positives' for all the pieces of rectangle that aren't the circle. This is good enough for most cases, but you can find the math for other overlaps and collisions easily ith a quick google search.
Update: Simple Implementation
See below for a very simple example of how overlaps work in action:
var paddle = { x: 50, y: 50, width: 60, height: 20 };
var box = { x: 5, y: 20, width: 20, height: 20 };
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
document.body.appendChild( canvas );
canvas.width = 300;
canvas.height = 300;
function overlaps( a, b ){
return !!( a.x + a.width > b.x && a.x < b.x + b.width
&& a.y + a.height > b.y && a.y < b.y + b.height );
}
function animate(){
ctx.clearRect( 0, 0, canvas.width, canvas.height );
ctx.fillStyle = overlaps( paddle, box ) ? "red" : "black";
ctx.fillRect( paddle.x, paddle.y, paddle.width, paddle.height );
ctx.fillRect( box.x, box.y, box.width, box.height );
window.requestAnimationFrame( animate );
}
canvas.addEventListener('mousemove', function(event){
paddle.x = event.clientX - paddle.width / 2;
paddle.y = event.clientY - paddle.height / 2;
})
animate();
I know you can fillRect right? And you can clearRect. But what happens if there's an animation and you have to remove an object although it would be redrawn from setInterval. How would you remove the fillRect?
Here's an example:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var width = canvas.width;
var height = canvas.height;
var circle = function (x, y, radius, fillCircle, color) {
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(x, y, radius, 0, Math.PI * 2, false);
if (fillCircle) {
ctx.fill();
} else {
ctx.stroke();
}
};
var drawRect = function (x, y, color) {
ctx.fillStyle = color;
ctx.fillRect(x, y, 20, 20)
}
var Object = function (xPos, yPos) {
this.x = xPos;
this.y = yPos;
}
// The Ball constructor
var Ball = function () {
this.x = width / 2;
this.y = height / 2;
this.xSpeed = 0;
this.ySpeed = 0;
this.radius = 10;
};
// Update the ball's position based on its speed
Ball.prototype.move = function () {
this.x += this.xSpeed;
this.y += this.ySpeed;
if (this.x < 11) {
this.x = 11;
} else if (this.x > width - 11) {
this.x = width - 11;
} else if (this.y < 11) {
this.y = 11;
} else if (this.y > height - 11) {
this.y = height - 11;
}
};
// Draw the ball at its current position
Ball.prototype.draw = function () {
circle(this.x, this.y, 10, true, "Black");
};
Object.prototype.draw = function () {
drawRect(this.x, this.y, "Black")
}
Object.prototype.drawKey = function (color) {
drawRect(this.x, this.y, "Yellow")
}
Object.prototype.checkCollision = function (direction) {
return (ball.x-ball.radius < this.x + 20)
&& (ball.x+ball.radius > this.x)
&& (ball.y-ball.radius < this.y + 20)
&& (ball.y+ball.radius > this.y)
;
}
function draw() {
ctx.clearRect(0, 0, width, height);
ball.draw();
object1.draw("Blue");
object2.draw();
object3.draw();
object4.draw();
object5.draw();
key.drawKey();
ctx.strokeRect(0, 0, width, height);
}
function simulate() {
for (z = 0; z < 5; z++) {
var prev_ball_x = ball.x;
var prev_ball_y = ball.y;
ball.move();
// handle collision here
if (object1.checkCollision() || object2.checkCollision() || object3.checkCollision() || object4.checkCollision() || object5.checkCollision()) {
ball.setDirection('stop');
// reset ball's position so they do not overlap
ball.x = prev_ball_x;
ball.y = prev_ball_y;
}
if (key.checkCollision()) {
ball.x = prev_ball_x;
ball.y = prev_ball_y;
}
}
$("body").keyup(function (event) {
ball.setDirection('stop');
})
}
setInterval(function () {
// separate drawing and simulating phases
simulate();
draw();
}, 30);
// Set the ball's direction based on a string
Ball.prototype.setDirection = function (direction) {
if (direction === "up") {
this.xSpeed = 0;
this.ySpeed = -1;
} else if (direction === "down") {
this.xSpeed = 0;
this.ySpeed = 1;
} else if (direction === "left") {
this.xSpeed = -1;
this.ySpeed = 0;
} else if (direction === "right") {
this.xSpeed = 1;
this.ySpeed = 0;
} else if (direction === "stop") {
this.xSpeed = 0;
this.ySpeed = 0;
}
};
// Create the ball object
var ball = new Ball();
var object1 = new Object(50, 0);
var object2 = new Object(50, 20);
var object3 = new Object(50, 40);
var object4 = new Object(50, 60);
var object5 = new Object(50, 80);
var key = new Object(70, 70);
// An object to convert keycodes into action names
var keyActions = {
37: "left",
38: "up",
39: "right",
40: "down"
};
// The keydown handler that will be called for every keypress
$("body").keydown(function (event) {
var direction = keyActions[event.keyCode];
ball.setDirection(direction);
});
<script src="https://code.jquery.com/jquery-2.1.0.js"></script>
<canvas id="canvas" width="800" height="200"></canvas>
You move around a ball with your arrow keys. When I collide with the yellow block, I want it to disappear. Using clearRect would not work simply because it would be redrawn in the setInterval. How would I make it disappear?
Generally when you have several items in a game you place them into a sort of objects array, then when you draw you loop through and call .draw() on each item. Doing it this way allows you to remove items you do not want (such as key), and as such it will no longer be drawn. In your case one thing we could do (assuming there is only a single key) is give your ball a hasKey property. And on collision set it from false to true. Then inside draw, if you wish to also remove the collisions you would do !ball.hasKey && key.checkCollision() inside your collision conditional for the key:
if(!ball.hasKey) key.drawKey();
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var width = canvas.width;
var height = canvas.height;
var circle = function (x, y, radius, fillCircle, color) {
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(x, y, radius, 0, Math.PI * 2, false);
if (fillCircle) {
ctx.fill();
} else {
ctx.stroke();
}
};
var drawRect = function (x, y, color) {
ctx.fillStyle = color;
ctx.fillRect(x, y, 20, 20)
}
var Object = function (xPos, yPos) {
this.x = xPos;
this.y = yPos;
}
// The Ball constructor
var Ball = function () {
this.x = width / 2;
this.y = height / 2;
this.xSpeed = 0;
this.ySpeed = 0;
this.radius = 10;
this.hasKey = false;
};
// Update the ball's position based on its speed
Ball.prototype.move = function () {
this.x += this.xSpeed;
this.y += this.ySpeed;
if (this.x < 11) {
this.x = 11;
} else if (this.x > width - 11) {
this.x = width - 11;
} else if (this.y < 11) {
this.y = 11;
} else if (this.y > height - 11) {
this.y = height - 11;
}
};
// Draw the ball at its current position
Ball.prototype.draw = function () {
circle(this.x, this.y, 10, true, "Black");
};
Object.prototype.draw = function () {
drawRect(this.x, this.y, "Black")
}
Object.prototype.drawKey = function (color) {
drawRect(this.x, this.y, "Yellow")
}
Object.prototype.checkCollision = function (direction) {
return (ball.x-ball.radius < this.x + 20)
&& (ball.x+ball.radius > this.x)
&& (ball.y-ball.radius < this.y + 20)
&& (ball.y+ball.radius > this.y)
;
}
function draw() {
ctx.clearRect(0, 0, width, height);
ball.draw();
object1.draw("Blue");
object2.draw();
object3.draw();
object4.draw();
object5.draw();
if(!ball.hasKey) key.drawKey();
ctx.strokeRect(0, 0, width, height);
}
function simulate() {
for (z = 0; z < 5; z++) {
var prev_ball_x = ball.x;
var prev_ball_y = ball.y;
ball.move();
// handle collision here
if (object1.checkCollision() || object2.checkCollision() || object3.checkCollision() || object4.checkCollision() || object5.checkCollision()) {
ball.setDirection('stop');
// reset ball's position so they do not overlap
ball.x = prev_ball_x;
ball.y = prev_ball_y;
}
if (!ball.hasKey && key.checkCollision()) {
ball.x = prev_ball_x;
ball.y = prev_ball_y;
ball.hasKey = true;
}
}
$("body").keyup(function (event) {
ball.setDirection('stop');
})
}
setInterval(function () {
// separate drawing and simulating phases
simulate();
draw();
}, 30);
// Set the ball's direction based on a string
Ball.prototype.setDirection = function (direction) {
if (direction === "up") {
this.xSpeed = 0;
this.ySpeed = -1;
} else if (direction === "down") {
this.xSpeed = 0;
this.ySpeed = 1;
} else if (direction === "left") {
this.xSpeed = -1;
this.ySpeed = 0;
} else if (direction === "right") {
this.xSpeed = 1;
this.ySpeed = 0;
} else if (direction === "stop") {
this.xSpeed = 0;
this.ySpeed = 0;
}
};
// Create the ball object
var ball = new Ball();
var object1 = new Object(50, 0);
var object2 = new Object(50, 20);
var object3 = new Object(50, 40);
var object4 = new Object(50, 60);
var object5 = new Object(50, 80);
var key = new Object(70, 70);
// An object to convert keycodes into action names
var keyActions = {
37: "left",
38: "up",
39: "right",
40: "down"
};
// The keydown handler that will be called for every keypress
$("body").keydown(function (event) {
var direction = keyActions[event.keyCode];
ball.setDirection(direction);
});
<script src="https://code.jquery.com/jquery-2.1.0.js"></script>
<canvas id="canvas" width="800" height="200"></canvas>
I am trying to create a animation where circles run from right to left. The circles' colours are selected randomly by a function. I have created a fiddle where one circle runs from right to left. Now my function creates a random colour. This function is executed every second and the circle changes its colour every second, instead of a new circle with the random picked colour become created. How can I change it so that it draws a new circle every second on the canvas and doesn't only change the colour of the circle?
This is my function:
function getRandomElement(array) {
if (array.length == 0) {
return undefined;
}
return array[Math.floor(Math.random() * array.length)];
}
var circles = [
'#FFFF00',
'#FF0000',
'#0000FF'
];
function drawcircles() {
ctx.beginPath();
ctx.arc(ballx * 108, canvasHeight / 2, x*5, 0, 2*Math.PI, false);
ctx.fillStyle = getRandomElement(circles);
ctx.fill();
ctx.closePath;
}
Comments on your question:
You are changing the color of the fillStyle to a random color at each frame. This is the reason why it keeps "changing" color. Set it to the color of the circle:
context.fillStyle = circle.color;
make circles with x, y, diameter, bounciness, speed, and color using an array
draw and update them with requestAnimationFrame (mine is a custom function)
My answer:
I made this just last night, where some circles follow the cursor and "bounce" off the edges of the screen. I tested the code and it works.
I might post a link later, but here is all of the code right now...
<!DOCTYPE html>
<html>
<body>
<canvas id="canvas"></canvas>
<script>
var lastTime = 0;
function requestMyAnimationFrame(callback, time)
{
var t = time || 16;
var currTime = new Date().getTime();
var timeToCall = Math.max(0, t - (currTime - lastTime));
var id = window.setTimeout(function(){ callback(currTime + timeToCall); }, timeToCall);
lastTime = currTime + timeToCall;
return id;
}
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
canvas.width = window.innerWidth - 20;
canvas.height = window.innerHeight - 20;
canvas.style.width = canvas.width + "px";
canvas.style.height = canvas.height + "px";
var circles = [];
var mouse =
{
x: 0,
y: 0
}
function getCoordinates(x, y)
{
return "(" + x + ", " + y + ")";
}
function getRatio(n, d)
{
// prevent division by 0
if (d === 0 || n === 0)
{
return 0;
}
else
{
return n/d;
}
}
function Circle(x,y,d,b,s,c)
{
this.x = x;
this.y = y;
this.diameter = Math.round(d);
this.radius = Math.round(d/2);
this.bounciness = b;
this.speed = s;
this.color = c;
this.deltaX = 0;
this.deltaY = 0;
this.drawnPosition = "";
this.fill = function()
{
context.beginPath();
context.arc(this.x+this.radius,this.y+this.radius,this.radius,0,Math.PI*2,false);
context.closePath();
context.fill();
}
this.clear = function()
{
context.fillStyle = "#ffffff";
this.fill();
}
this.draw = function()
{
if (this.drawnPosition !== getCoordinates(this.x, this.y))
{
context.fillStyle = this.color;
// if commented, the circle will be drawn if it is in the same position
//this.drawnPosition = getCoordinates(this.x, this.y);
this.fill();
}
}
this.keepInBounds = function()
{
if (this.x < 0)
{
this.x = 0;
this.deltaX *= -1 * this.bounciness;
}
else if (this.x + this.diameter > canvas.width)
{
this.x = canvas.width - this.diameter;
this.deltaX *= -1 * this.bounciness;
}
if (this.y < 0)
{
this.y = 0;
this.deltaY *= -1 * this.bounciness;
}
else if (this.y+this.diameter > canvas.height)
{
this.y = canvas.height - this.diameter;
this.deltaY *= -1 * this.bounciness;
}
}
this.followMouse = function()
{
// deltaX/deltaY will currently cause the circles to "orbit" around the cursor forever unless it hits a wall
var centerX = Math.round(this.x + this.radius);
var centerY = Math.round(this.y + this.radius);
if (centerX < mouse.x)
{
// circle is to the left of the mouse, so move the circle to the right
this.deltaX += this.speed;
}
else if (centerX > mouse.x)
{
// circle is to the right of the mouse, so move the circle to the left
this.deltaX -= this.speed;
}
else
{
//this.deltaX = 0;
}
if (centerY < mouse.y)
{
// circle is above the mouse, so move the circle downwards
this.deltaY += this.speed;
}
else if (centerY > mouse.y)
{
// circle is under the mouse, so move the circle upwards
this.deltaY -= this.speed;
}
else
{
//this.deltaY = 0;
}
this.x += this.deltaX;
this.y += this.deltaY;
this.x = Math.round(this.x);
this.y = Math.round(this.y);
}
}
function getRandomDecimal(min, max)
{
return Math.random() * (max-min) + min;
}
function getRoundedNum(min, max)
{
return Math.round(getRandomDecimal(min, max));
}
function getRandomColor()
{
// array of three colors
var colors = [];
// go through loop and add three integers between 0 and 255 (min and max color values)
for (var i = 0; i < 3; i++)
{
colors[i] = getRoundedNum(0, 255);
}
// return rgb value (RED, GREEN, BLUE)
return "rgb(" + colors[0] + "," + colors[1] + ", " + colors[2] + ")";
}
function createCircle(i)
{
// diameter of circle
var minDiameter = 25;
var maxDiameter = 50;
// bounciness of circle (changes speed if it hits a wall)
var minBounciness = 0.2;
var maxBounciness = 0.65;
// speed of circle (how fast it moves)
var minSpeed = 0.3;
var maxSpeed = 0.45;
// getRoundedNum returns a random integer and getRandomDecimal returns a random decimal
var x = getRoundedNum(0, canvas.width);
var y = getRoundedNum(0, canvas.height);
var d = getRoundedNum(minDiameter, maxDiameter);
var c = getRandomColor();
var b = getRandomDecimal(minBounciness, maxBounciness);
var s = getRandomDecimal(minSpeed, maxSpeed);
// create the circle with x, y, diameter, bounciness, speed, and color
circles[i] = new Circle(x,y,d,b,s,c);
}
function makeCircles()
{
var maxCircles = getRoundedNum(2, 5);
for (var i = 0; i < maxCircles; i++)
{
createCircle(i);
}
}
function drawCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].draw();
ii++;
}
}
}
function clearCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].clear();
ii++;
}
}
}
function updateCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].keepInBounds();
circles[i].followMouse();
ii++;
}
}
}
function update()
{
requestMyAnimationFrame(update,10);
updateCircles();
}
function draw()
{
requestMyAnimationFrame(draw,1000/60);
context.clearRect(0,0,canvas.width,canvas.height);
drawCircles();
}
function handleError(e)
{
//e.preventDefault();
//console.error(" ERROR ------ " + e.message + " ------ ERROR ");
}
window.addEventListener("load", function()
{
window.addEventListener("error", function(e)
{
handleError(e);
});
window.addEventListener("resize", function()
{
canvas.width = window.innerWidth - 20;
canvas.height = window.innerHeight - 20;
});
window.addEventListener("mousemove", function(e)
{
mouse.x = e.layerX || e.offsetX;
mouse.y = e.layerY || e.offsetY;
});
makeCircles();
update();
draw();
});
</script>
</body>
</html>