I'm trying to make a simple (or so I thought) memory game. Unfortunately it does not update state of cards when user clicks on them. I'm running out of ideas, probably because it's my first javascript game. I suppose there is a problem with game loop. Could anyone at least point me in the right direction and help me understand what needs to be changed/rewritten?
//HTML5 Memory Game implementation
//main variables
var cards = [1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8];
var exposed = [makeArray("false",16)];
var first_card = 0;
var second_card = 0;
var moves = 0;
var WIDTH = 800;
var HEIGHT = 100;
var state = 0;
var mouseX = 0;
var mouseY = 0;
//creating canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = WIDTH;
canvas.height = HEIGHT;
document.getElementById("game").appendChild(canvas);
//filling empty array with number,character,object
function makeArray(value, length) {
var newArray = [];
var i = 0;
while (i<length) {
newArray[i] = value;
i++;
}
return newArray;
}
//shuffling algorithm
function shuffle(array) {
var copy = [];
var n = array.length;
var i;
while (n) {
i = Math.floor(Math.random() * n--);
copy.push(array.splice(i, 1)[0]);
}
return copy;
}
//where user clicks
function getClickPosition(event) {
var X = event.pageX - canvas.offsetLeft;
var Y = event.pageY - canvas.offsetTop;
return mouse = [X, Y];
}
//read click position
function readPos(event) {
mousePos = getClickPosition(event);
mouseX = mousePos[0];
mouseY = mousePos[1];
}
//initializing
function init() {
state = 0;
moves = 0;
exposed = [makeArray("false",16)];
cards = shuffle(cards);
}
//drawing cards
function draw() {
ctx.clearRect(0, 0, WIDTH, HEIGHT);
for (var i in cards) {
if (exposed[i] === true) {
ctx.fillStyle = "rgb(250, 250, 250)";
ctx.font = "50px Courier New";
ctx.fillText(cards[i], (i*50+12), 65);
} else {
ctx.strokeStyle = "rgb(250, 0, 0)";
ctx.fillStyle = "rgb(0, 0, 250)";
ctx.fillRect(i*50, 0, 50, 100);
ctx.strokeRect(i*50, 0, 50, 100);
}
}
};
//update cards
function update() {
if (exposed[parseInt(mouseX / 50)] === false) {
if (state == 0) {
state = 1;
first_card = parseInt(mouseX / 50);
exposed[parseInt(mouseX / 50)] = true;
} else if (state == 1) {
state = 2;
second_card = parseInt(mouseX / 50);
exposed[parseInt(mouseX / 50)] = true;
} else {
if (cards[first_card] != cards[second_card]) {
exposed[first_card] = false;
exposed[second_card] = false;
}
state = 1;
first_card = parseInt(mouseX / 50);
exposed[parseInt(mouseX / 50)] = true;
}
}
}
addEventListener('click', readPos, false);
setInterval(function() {
update();
draw();
}, 16);
I would check your addEventListener method: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget.addEventListener
I also recommend you look into using jQuery.
After copy and pasting your code I found a couple of things:
You didn't add an event listener to anything, you should add it to something so I added it to document.
You initialize the exposed array with values "false" and later check if they are false. These are not the same, the string "false" isn't the Boolean false.
You initializes the exposed array as a multi dimensional array [[false,false,false ...]] this should be a single dimension array because later you check exposed[1] (1 depending on the mouse x position.
No need to call draw and update every 16 milliseconds, you can call it after someone clicked.
Wrapped the whole thing up in a function so there are no global variables created.
Here is the code after changing these obvious errors. There might be room for optimization but for now I've gotten the problems out.
<!DOCTYPE html>
<html>
<head>
<title>test</title>
</head>
<body>
<div id="game"></div>
<script type="text/javascript">
(function(){
//HTML5 Memory Game implementation
//main variables
var cards = [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8];
var exposed = makeArray(false, 16);
var first_card = 0;
var second_card = 0;
var moves = 0;
var WIDTH = 800;
var HEIGHT = 100;
var state = 0;
var mouseX = 0;
var mouseY = 0;
//creating canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = WIDTH;
canvas.height = HEIGHT;
document.getElementById("game").appendChild(canvas);
//filling empty array with number,character,object
function makeArray(value, length) {
var newArray = [];
var i = 0;
while (i < length) {
newArray.push(value);
i++;
}
return newArray;
}
//shuffling algorithm
function shuffle(array) {
var copy = [];
var n = array.length;
var i;
while (n) {
i = Math.floor(Math.random() * n--);
copy.push(array.splice(i, 1)[0]);
}
return copy;
}
//where user clicks
function getClickPosition(event) {
var X = event.pageX - canvas.offsetLeft;
var Y = event.pageY - canvas.offsetTop;
return mouse = [X, Y];
}
//read click position
function readPos(event) {
mousePos = getClickPosition(event);
mouseX = mousePos[0];
mouseY = mousePos[1];
update();
draw();
}
//initializing
function init() {
state = 0;
moves = 0;
exposed = makeArray(false, 16);
cards = shuffle(cards);
}
//drawing cards
function draw() {
ctx.clearRect(0, 0, WIDTH, HEIGHT);
for (var i in cards) {
if (exposed[i] === true) {
ctx.fillStyle = "rgb(150, 150, 150)";
ctx.font = "50px Courier New";
ctx.fillText(cards[i], (i * 50 + 12), 65);
} else {
ctx.strokeStyle = "rgb(250, 0, 0)";
ctx.fillStyle = "rgb(0, 0, 250)";
ctx.fillRect(i * 50, 0, 50, 100);
ctx.strokeRect(i * 50, 0, 50, 100);
}
}
};
//update cards
function update() {
if (exposed[parseInt(mouseX / 50)] === false) {
if (state == 0) {
state = 1;
first_card = parseInt(mouseX / 50);
exposed[parseInt(mouseX / 50)] = true;
} else if (state == 1) {
state = 2;
second_card = parseInt(mouseX / 50);
exposed[parseInt(mouseX / 50)] = true;
} else {
if (cards[first_card] != cards[second_card]) {
exposed[first_card] = false;
exposed[second_card] = false;
}
state = 1;
first_card = parseInt(mouseX / 50);
exposed[parseInt(mouseX / 50)] = true;
}
}
}
document.body.addEventListener('click', readPos, false);
init();
draw();
})();
</script>
</body>
</html>
Your overall logic was good.
The point that was 'bad' was the way you handle the event :
the event handler should store some valuable information that
the update will later process and clear.
Here you mix your update with event handling, which cannot work
especially since the event will not fire on every update.
So i did a little fiddle to show you, the main change is
the click event handler, which update the var last_clicked_card :
http://jsfiddle.net/wpymH/
//read click position
function readPos(event) {
last_clicked_card = -1;
mousePos = getClickPosition(event);
mouseX = mousePos[0];
mouseY = mousePos[1];
// on canvas ?
if ((mouseY>100)||(mouseX<0)||(mouseX>WIDTH)) return;
// now yes : which card clicked ?
last_clicked_card = Math.floor(mouseX/50);
}
and then update is the processing of this information :
//update cards
function update() {
// return if no new card clicked
if (last_clicked_card == -1) return;
// read and clear last card clicked
var newCard = last_clicked_card;
last_clicked_card=-1;
// flip, store it as first card and return
// if there was no card flipped
if (state==0) { exposed[newCard] = true;
first_card = newCard;
state = 1 ;
return; }
// just unflip card if card was flipped
if ((state = 1) && exposed[newCard]) {
exposed[newCard]=false ;
state=0;
return;
}
// we have a second card now
second_card = newCard;
exposed[second_card] = true;
draw();
// ... i don't know what you want to do ...
if (cards[first_card] == cards[second_card]) {
alert('win'); }
else {
alert('loose'); }
exposed[first_card]=false;
exposed[second_card]=false;
state=0;
}
Related
I am trying to toggle a particle emitter to on or off.
I have some Reactjs code that creates rain/snow particles with the canvas element.
Every notion i have came up with to properly kill the animation still results in memory leaks (in this case i think it is just projecting a new canvas over the old and still running the original animation in memory).
Here is the Codepen.
Here is the page code:
class CanvasComponent extends React.Component {
componentDidMount() {
this.BtnOnOff();
}
constructor() {
super();
this.toggleState = this.toggleState.bind(this);
this.state = {
isActive : false
}
}
toggleState() {
this.setState({isActive:!this.state.isActive});
this.BtnOnOff();
//console.log(this.state.isActive);
}
snowMaker() {
// Ref canvas & get context
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// Weather Types/////////////
var liteSnow = 13; //code 13 for snow light fluries.
var snow = 16; //code 13 for snow fluries.
var hevySnow = 41; //code 41 for Heavy snow.
var hevySnow2 = 43; //code 43 for Heavy snow.
var DRain = 9; //code 9 for drizzel
var HRain = 3; //code 3/4 for Thunderstorms/severe thunderstorms
var Code = 43; // Code will take in the current weather code.
// Resize canvas
let width = canvas.width = window.innerWidth;
let height = canvas.height = window.innerHeight;
// Variables
if (Code === 13) { /// Make it snow (light fluries)
var drops = [];
var dropColour = "rgba(255,255,255,1)";
var dropLengths = [3, 3, 3, 3, 3, 3, 3];
var dropSkews = [-2, -1, 0, 1, 2];
var maxDrops = 100;
var velocity = 8;
var flutter = 5;
}
else if (Code === 16){ /// Make it snow (fluries)
var drops = [];
var dropColour = "rgba(255,255,255,1)";
var dropLengths = [3, 3, 3, 3, 3, 3, 3];
var dropSkews = [-2, -1, 0, 1, 2];
var maxDrops = 500;
var velocity = 7;
var flutter = 5;
}
else if (Code === 41||Code === 43){ /// Make it Heavy snow
var drops = [];
var dropColour = "rgba(255,255,255,1)";
var dropLengths = [3, 2, 3, 2, 3, 2, 3];
var dropSkews = [-3, -1, 0, 1, 3];
var maxDrops = 800;
var velocity = .5;
var flutter = 8;
}
else if (Code === 9){ /// Make it rain
var drops = [];
var dropColour = "rgba(255,255,255,0.41)";
var dropLengths = [4, 5, 3, 6, 2, 3, 3];
var dropSkews = [0, 0.2, 0, 0, 0.1];
var maxDrops = 100;
var velocity =1;
var flutter = 1;
}
else if (Code === 3||Code === 4){ /// Make it ThunderStorms
var drops = [];
var dropColour = "rgba(255,255,255,0.5)";
var dropLengths = [10, 8, 8, 8, 7, 15, 9];
var dropSkews = [-0.2, -0.3, -0.2, -0.2, 0.1];
var maxDrops = 1000;
var velocity = .8;
var flutter = .1;
}
// Raindrop class
class Droplet {
constructor(x, y, length, skew) {
this.x = x;
this.y = y;
this.length = length;
this.skew = skew;
}
// Move method
move() {
// Increment x & y
this.y += this.length / velocity;
this.x += this.skew / flutter;
// Set limits
if (this.y > height) {
this.y = 0;
}
if (this.x > width || this.x < 0) {
this.y = 0;
this.x = Math.floor(Math.random() * width);
}
}
// Draw method
draw(ctx) {
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.lineTo(this.x + this.skew, this.y + this.length);
ctx.strokeStyle = dropColour;
ctx.stroke();
}
}
// Create drops and push to array
for (let i = 0; i < maxDrops; i++) {
let instance = new Droplet(
Math.floor(Math.random() * width),
Math.floor(Math.random() * height),
randVal(dropLengths),
randVal(dropSkews)
);
drops.push(instance);
}
// Animation loop
function loop() {
// Clear Canvas
ctx.clearRect(0, 0, width, height);
// Draw / Move drops
for (let drop of drops) {
drop.move();
drop.draw(ctx);
}
// Animation Frame
requestAnimationFrame(loop)
}
// Begin animation
loop();
// Resize canvas - responsive
window.addEventListener('resize', resize);
function resize() {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
}
// Function for random array values
function randVal(array) {
return array[Math.floor(Math.random() * array.length)];
}
}////////End of update canvas
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
snowKiller() {
// Ref canvas & get context
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
var Code = 13;
// Resize canvas
let width = canvas.width = window.innerWidth;
let height = canvas.height = window.innerHeight;
// Variables
if (Code === 13) { /// Make it snow (light fluries)
var drops = [];
var dropColour = "";
var dropLengths = [0];
var dropSkews = [0];
var maxDrops = 0;
var velocity = 0;
var flutter = 0;
}
// Raindrop class
class Droplet {
constructor(x, y, length, skew) {
this.x = x;
this.y = y;
this.length = length;
this.skew = skew;
}
// Move method
move() {
// Increment x & y
this.y += this.length / velocity;
this.x += this.skew / flutter;
// Set limits
if (this.y > height) {
this.y = 0;
}
if (this.x > width || this.x < 0) {
this.y = 0;
this.x = Math.floor(Math.random() * width);
}
}
// Draw method
draw(ctx) {
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.lineTo(this.x + this.skew, this.y + this.length);
ctx.strokeStyle = dropColour;
ctx.stroke();
}
}
// Create drops and push to array
for (let i = 0; i < maxDrops; i++) {
let instance = new Droplet(
Math.floor(Math.random() * width),
Math.floor(Math.random() * height),
randVal(dropLengths),
randVal(dropSkews)
);
drops.push(instance);
}
// Animation loop
function loop() {
// Clear Canvas
ctx.clearRect(0, 0, width, height);
// Draw / Move drops
for (let drop of drops) {
drop.move();
drop.draw(ctx);
}
// Animation Frame
requestAnimationFrame(loop)
}
// Begin animation
loop();
// Resize canvas - responsive
window.addEventListener('resize', resize);
function resize() {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
}
// Function for random array values
function randVal(array) {
return array[Math.floor(Math.random() * array.length)];
}
}////////End of update canvas
BtnOnOff(){
const OnOff =$('#Button').attr('class');
if(OnOff=== "active"){
// alert('this is on!')
this.snowMaker();
}else {
this.snowKiller();
// alert('this is off!');
}
console.log(OnOff);
}
render() {
return (
<div>
<button id="Button" className={this.state.isActive ? 'inactive' : 'active'} onClick ={this.toggleState}>{this.state.isActive ? 'STOP' : 'START'}</button>
<canvas id="canvas"/>
</div>
);
}
}
ReactDOM.render(<CanvasComponent/>, document.getElementById('app'));
Idealy the solution would be something like clearing the requestAnimationFrame but i can't seem to figure it out.
Help would be very much apreciated.
Thanks
(Code tl;dr) you can always use the request-ID returned by requestAnimationFrame(), like this:
var reqID;
function loop() {
// rain
reqID = requestAnimationFrame(loop);
}
// start loop same way
reqID = requestAnimationFrame(loop);
Then when you want to stop it:
cancelAnimationFrame(reqID);
Calling it with an empty "reqID" is safe.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I am creating a game (using HTML5 canvas) that involves catching falling apples, i know, how original! I am having trouble finding a way to make it so multiple apples fall?
Here is the code in JSFiddle: https://jsfiddle.net/pgkL09j7/12/
var apple_x = 100;
var apple_y = 0;
var basket_x = 100;
var basket_y = 100;
var points = 0;
var basket_img = new Image();
basket_img.src = "http://s18.postimg.org/h0oe1vj91/basket.png";
var Countable = function () {}
//Background colour of canvas
var c = document.getElementById("c");
var ctx = c.getContext("2d");
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, 500, 500);
//Here is the event listener
c.addEventListener("mousemove", seenmotion, false);
//////////////////////
function seenmotion(e) {
//This is the code for the mouse
//moving over the canvas.
var bounding_box = c.getBoundingClientRect();
basket_x = (e.clientX - bounding_box.left) * (c.width / bounding_box.width) - basket_img.width / 2;
basket_y = (e.clientY - bounding_box.top) * (c.height / bounding_box.height) - basket_img.height / 2;
}
function start_game() {
setInterval(game_loop, 50);
}
function game_loop() {
// The code above is called every 50ms and is a
// frame-redraw-game-animation loop.
c.width = c.width;
// Below is the code that draws the objects
draw_apple(apple_x, apple_y);
draw_basket(basket_x, basket_y);
// Below is the code that updates the balloons location
apple_x++;
if (apple_y > c.height) {
apple_y = 0;
}
//Here is the collision detection code
if (collision(apple_x, apple_y, basket_x, basket_y)) {
points -= 0.5;
}
//Here is the code for the point system
points += 1;
// and let's stick it in the top right.
var integerpoints = Math.floor(points); // make it into an integer
ctx.font = "bold 24px sans-serif";
ctx.fillText(integerpoints, c.width - 50, 50);
}
context.clearRect(0, 0, 500, 500);
function collision(basket_x, basket_y, apple_x, apple_y) {
if (apple_y + 85 < basket_y) {
return false;
}
if (apple_y > basket_y + 91) {
return false;
}
if (apple_x + 80 < basket_x) {
return false;
}
if (apple_x > basket_x + 80) {
return false;
}
return true;
}
// Code to stop the game when we're finished playing
function stop_game() {
}
//Code for the ball
function draw_app
le(x, y) {
var apple_img = new Image();
apple_img.src = "http://s15.postimg.org/3nwjmzsiv/apple.png";
ctx.drawImage(apple_img, x, y);
}
//Code for the basket
function draw_basket(x, y) {
ctx.drawImage(basket_img, x, y);
}
Change the section
apple_x++;
if (apple_x > c.width) {
apple_x = 0;
}
to use vertical instead of horizontal...
apple_y++;
if (apple_y > c.height) {
apple_y = 0;
}
You've already accepted the answer, but this looked like fun. Check out this fiddle.
https://jsfiddle.net/h82gv4xn/
Improvements include:
Fixed scoreboard
Added level progression (Level increases every 10 apples)
Allowance for many many more apples on screen (play to level 9).
Apples will fall at different speeds and speed up as the levels increase.
Uses the animation frame system for much smoother animations.
Relaxed collision handling (The center of the bucket must touch the apple)
It all gets really silly as the levels wind upwards, but it should be a nice example to improve upon. The relevant javascript follows (this would go into your onLoad function):
var game = create_game();
game.init();
function create_game() {
debugger;
var level = 1;
var apples_per_level = 1;
var min_speed_per_level = 1;
var max_speed_per_level = 2;
var last_apple_time = 0;
var next_apple_time = 0;
var width = 500;
var height = 500;
var delay = 1000;
var item_width = 50;
var item_height = 50;
var total_apples = 0;
var apple_img = new Image();
var apple_w = 50;
var apple_h = 50;
var basket_img = new Image();
var c, ctx;
var apples = [];
var basket = {
x: 100,
y: 100,
score: 0
};
function init() {
apple_img.src = "http://s15.postimg.org/3nwjmzsiv/apple.png";
basket_img.src = "http://s18.postimg.org/h0oe1vj91/basket.png";
level = 1;
total_apples = 0;
apples = [];
c = document.getElementById("c");
ctx = c.getContext("2d");
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, 500, 500);
c.addEventListener("mousemove", function (e) {
//moving over the canvas.
var bounding_box = c.getBoundingClientRect();
basket.x = (e.clientX - bounding_box.left) * (c.width / bounding_box.width) - basket_img.width / 2;
basket.y = (e.clientY - bounding_box.top) * (c.height / bounding_box.height) - basket_img.height / 2;
}, false);
setupApples();
requestAnimationFrame(tick);
}
function setupApples() {
var max_apples = level * apples_per_level;
while (apples.length < max_apples) {
initApple(apples.length);
}
}
function initApple(index) {
var max_speed = max_speed_per_level * level;
var min_speed = min_speed_per_level * level;
apples[index] = {
x: Math.round(Math.random() * (width - 2 * apple_w)) + apple_w,
y: -apple_h,
v: Math.round(Math.random() * (max_speed - min_speed)) + min_speed,
delay: Date.now() + Math.random() * delay
}
total_apples++;
}
function collision(apple) {
if (apple.y + apple_img.height < basket.y + 50) {
return false;
}
if (apple.y > basket.y + 50) {
return false;
}
if (apple.x + apple_img.width < basket.x + 50) {
return false;
}
if (apple.x > basket.x + 50) {
return false;
}
return true;
}
function maybeIncreaseDifficulty() {
level = Math.max(1, Math.ceil(basket.score / 10));
setupApples();
}
function tick() {
var i;
var apple;
var dateNow = Date.now();
c.width = c.width;
for (i = 0; i < apples.length; i++) {
apple = apples[i];
if (dateNow > apple.delay) {
apple.y += apple.v;
if (collision(apple)) {
initApple(i);
basket.score++;
} else if (apple.y > height) {
initApple(i);
} else {
ctx.drawImage(apple_img, apple.x, apple.y);
}
}
}
ctx.font = "bold 24px sans-serif";
ctx.fillStyle = "#2FFF2F";
ctx.fillText(basket.score, c.width - 50, 50);
ctx.fillText("Level: " + level, 20, 50);
ctx.drawImage(basket_img, basket.x, basket.y);
maybeIncreaseDifficulty();
requestAnimationFrame(tick);
}
return {
init: init
};
}
I am creating a game (using HTML5 canvas) that involves catching falling apples, i know, how original! I am having trouble finding a way to make it so multiple apples fall?
Here is the link to the JSFiddle: https://jsfiddle.net/pgkL09j7/14/
var apple_x = 100;
var apple_y = 0;
var basket_x = 100;
var basket_y = 100;
var points = 0;
var basket_img = new Image();
basket_img.src = "http://s18.postimg.org/h0oe1vj91/basket.png";
var Countable = function() {}
//Background colour of canvas
var c = document.getElementById("c");
var ctx = c.getContext("2d");
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, 500, 500);
//Here is the event listener
c.addEventListener("mousemove", seenmotion, false);
//////////////////////
function seenmotion(e) {
//This is the code for the mouse
//moving over the canvas.
var bounding_box = c.getBoundingClientRect();
basket_x = (e.clientX - bounding_box.left) * (c.width / bounding_box.width) - basket_img.width / 2;
basket_y = (e.clientY - bounding_box.top) * (c.height / bounding_box.height) - basket_img.height / 2;
}
function start_game() {
setInterval(game_loop, 50);
}
function game_loop() {
// The code above is called every 50ms and is a
// frame-redraw-game-animation loop.
c.width = c.width;
// Below is the code that draws the objects
draw_apple(apple_x, apple_y);
draw_basket(basket_x, basket_y);
// Below is the code that updates the balloons location
apple_y++;
if (apple_x > c.width) {
apple_x = 0;
}
//Here is the collision detection code
if (collision(apple_x, apple_y, basket_x, basket_y)) {
points -= 0.5;
}
//Here is the code for the point system
points += 1;
// and let's stick it in the top right.
var integerpoints = Math.floor(points); // make it into an integer
ctx.font = "bold 24px sans-serif";
ctx.fillText(integerpoints, c.width - 50, 50);
}
context.clearRect(0, 0, 500, 500);
function collision(basket_x, basket_y, apple_x, apple_y) {
if (apple_y + 85 < basket_y) {
return false;
}
if (apple_y > basket_y + 91) {
return false;
}
if (apple_x + 80 < basket_x) {
return false;
}
if (apple_x > basket_x + 80) {
return false;
}
return true;
}
// Code to stop the game when we're finished playing
function stop_game() {
}
//Code for the ball
function draw_apple(x, y) {
var apple_img = new Image();
apple_img.src = "http://s15.postimg.org/3nwjmzsiv/apple.png";
ctx.drawImage(apple_img, x, y);
}
//Code for the basket
function draw_basket(x, y) {
ctx.drawImage(basket_img, x, y);
}
I posted this on your previous question, so I'll repeat it here. You will need to maintain an array of apples, but you will also want to check out requestAnimationFrame in order to improve performance. Things are going to get janky for you, and you probably already noticed it when you move the bucket around. I've modified your fiddle to demonstrate exactly how you might modify your program to support multiple apples falling at different rates of speed. (Set apples_per_level to 2 or more to immediately see multiple apples -- or just play the game, and watch as they accumulate!).
https://jsfiddle.net/h82gv4xn/
Improvements include:
Fixed scoreboard
Added level progression (Level increases every 10 apples)
Allowance for many many more apples on screen (play to level 9).
Apples will fall at different speeds and speed up as the levels increase.
Uses the animation frame system for much smoother animations.
Relaxed collision handling (The center of the bucket must touch the apple)
It all gets really silly as the levels wind upwards, but it should be a nice example to improve upon. The relevant javascript follows (this would go into your onLoad function):
var game = create_game();
game.init();
function create_game() {
debugger;
var level = 1;
var apples_per_level = 1;
var min_speed_per_level = 1;
var max_speed_per_level = 2;
var last_apple_time = 0;
var next_apple_time = 0;
var width = 500;
var height = 500;
var delay = 1000;
var item_width = 50;
var item_height = 50;
var total_apples = 0;
var apple_img = new Image();
var apple_w = 50;
var apple_h = 50;
var basket_img = new Image();
var c, ctx;
var apples = [];
var basket = {
x: 100,
y: 100,
score: 0
};
function init() {
apple_img.src = "http://s15.postimg.org/3nwjmzsiv/apple.png";
basket_img.src = "http://s18.postimg.org/h0oe1vj91/basket.png";
level = 1;
total_apples = 0;
apples = [];
c = document.getElementById("c");
ctx = c.getContext("2d");
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, 500, 500);
c.addEventListener("mousemove", function (e) {
//moving over the canvas.
var bounding_box = c.getBoundingClientRect();
basket.x = (e.clientX - bounding_box.left) * (c.width / bounding_box.width) - basket_img.width / 2;
basket.y = (e.clientY - bounding_box.top) * (c.height / bounding_box.height) - basket_img.height / 2;
}, false);
setupApples();
requestAnimationFrame(tick);
}
function setupApples() {
var max_apples = level * apples_per_level;
while (apples.length < max_apples) {
initApple(apples.length);
}
}
function initApple(index) {
var max_speed = max_speed_per_level * level;
var min_speed = min_speed_per_level * level;
apples[index] = {
x: Math.round(Math.random() * (width - 2 * apple_w)) + apple_w,
y: -apple_h,
v: Math.round(Math.random() * (max_speed - min_speed)) + min_speed,
delay: Date.now() + Math.random() * delay
}
total_apples++;
}
function collision(apple) {
if (apple.y + apple_img.height < basket.y + 50) {
return false;
}
if (apple.y > basket.y + 50) {
return false;
}
if (apple.x + apple_img.width < basket.x + 50) {
return false;
}
if (apple.x > basket.x + 50) {
return false;
}
return true;
}
function maybeIncreaseDifficulty() {
level = Math.max(1, Math.ceil(basket.score / 10));
setupApples();
}
function tick() {
var i;
var apple;
var dateNow = Date.now();
c.width = c.width;
for (i = 0; i < apples.length; i++) {
apple = apples[i];
if (dateNow > apple.delay) {
apple.y += apple.v;
if (collision(apple)) {
initApple(i);
basket.score++;
} else if (apple.y > height) {
initApple(i);
} else {
ctx.drawImage(apple_img, apple.x, apple.y);
}
}
}
ctx.font = "bold 24px sans-serif";
ctx.fillStyle = "#2FFF2F";
ctx.fillText(basket.score, c.width - 50, 50);
ctx.fillText("Level: " + level, 20, 50);
ctx.drawImage(basket_img, basket.x, basket.y);
maybeIncreaseDifficulty();
requestAnimationFrame(tick);
}
return {
init: init
};
}
Your next logical step would be to create an apple Object with appropriate properties. Following that, you can store them in an Array and animate multiple apples.
As Gerard said, you should create an Array of apple Objects. You can take a look at this example with balls instead of apples.
One you are done with that, I would recommend that you draw all the apples that fall at the same speed in different offscreen canvases and that you animate those instead of going apple by apple if you are planning to add many of them. If they are all going to fall at the same speed, use just one offscreen canvas and move all at once.
Also take a look at http://www.html5rocks.com/en/tutorials/canvas/performance/ if you have trouble keeping your FPS at an appropriate rate.
This is a game I am making. I cant figure out why it is not working. I have the JS fiddle here http://jsfiddle.net/aa68u/4/
// Create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 512;
canvas.height = 480;
document.body.appendChild(canvas);
// Background image
var bgReady = false;
var bgImage = new Image();
bgImage.onload = function () {
bgReady = true;
};
bgImage.src = "http://6269-9001.zippykid.netdna-cdn.com/wp-content/uploads/2013/11/Woods-Wallpaper.jpg";
// Ship image
var shipReady = false;
var shipImage = new Image();
shipImage.onload = function () {
shipImage = true;
};
shipImage.src = "http://s29.postimg.org/3widtojzn/hero.png";
// Astroid image
var astroidReady = false;
var astroidImage = new Image();
astroidImage.onload = function () {
astroidReady = true;
};
astroidImage.src = "http://s29.postimg.org/4r4xfprub/monster.png";
// Game objects
var ship = {
speed: 256;
};
var astroid = {};
var health = 100;
window.keyStates = {};
addEventListener('keydown', function (e) {
keyStates[e.keyCode || e.key] = true;
e.preventDefault();
e.stopPropagation();
}, true);
addEventListener('keyup', function (e) {
keyStates[e.keyCode || e.key] = false;
e.preventDefault();
e.stopPropagation();
}, true);
var reset = function () {
astroid.width = 10;
astroid.height = 10;
astroid.x = 32 + (Math.random() * (canvas.width - 64));
astroid.y = 32 + (Math.random() * (canvas.height - 64));
ship.speed = 256;
for (var p in keyStates) keyStates[p]= false;
};
// Update game objects
function update (modifier) {
if (keyStates[38] ==true) { // Player holding up
astroid.x -= ship.speed * modifier;
}
if (keyStates[40]==true) { // Player holding down
astroid.x += ship.speed * modifier;
}
if (keyStates[37]==true) { // Player holding left
astroid.y -= ship.speed * modifier;
}
if (keyStates[39]==true) { // Player holding right
astroid.y += ship.speed * modifier;
}
if (astroid.width) < 200{
astroid.width +=10;
astroid.height += 10;
}
if (astroid.width) > 200{
reset();
}
// Are they touching?
if (keyStates[32] == true && ship.x <= (astroid.x + 32) && astroid.x <= (ship.x + 32) && ship.y <= (astroid.y + 32) && astroid.y <= (ship.y + 32)) {
monstersCaught += 1;
reset();
}
};
// Draw everything
var render = function () {
if (bgReady) {
ctx.drawImage(bgImage, 0, 0);
}
if (shipReady) {
ctx.drawImage(heroImage, hero.x, hero.y);
}
if (astroidReady) {
ctx.drawImage(monsterImage, monster.x, monster.y);
}
// Score
ctx.fillStyle = "rgb(250, 250, 250)";
ctx.font = "24px Helvetica";
ctx.textAlign = "left";
ctx.textBaseline = "top";
ctx.fillText("Your Health: " + health, 32, 32);
};
// The main game loop
var main = function () {
var now = Date.now();
var delta = now - then;
if (delta > 20) delta = 20;
update(delta / 1000);
render();
then = now;
};
// Let's play this game!
reset();
var then = Date.now();
setInterval(main, 1); // Execute as fast as possible
The game is supposed to be a ship(shoe image) that is avoiding astroids that get bigger(ants) but when you move your ship(shoe) stays in the same place and the astroids(ants) move. The ants/astroids also get bigger like you are going close to them.
var ship = {
speed: 256;
};
Remove the ; after the value.
if astroid.width < 200{
and
if astroid.width > 200{
Need parentheses around the if conditions.
The error console is helpful! But now it's just stuck in an infinite loop of monsterImage is not defined. Just... go back, write your code more carefully, and use the error console! It's there for a reason!
Is there a way to keep count of the number of shapes drawn on a canvas
I'm using a brush of sorts to draw a string of circles on a canvas and would like to find a way to count how many are present
var mainCanvas = document.getElementById('draw1');
mainContext = mainCanvas.getContext('2d');
var CircleBrush = {
iPrevX: 0,
iPrevY: 0,
// initialization function
init: function () {
mainContext.globalCompositeOperation = 'source-over';
mainContext.lineWidth = 1;
mainContext.strokeStyle = '#4679BD';
mainContext.lineWidth = 1;
mainContext.lineJoin = 'round';
},
startCurve: function (x, y) {
this.iPrevX = x;
this.iPrevY = y;
mainContext.fillStyle = '#4679BD';
},
draw: function (x, y) {
var iXAbs = Math.abs(x - this.iPrevX);
var iYAbs = Math.abs(y - this.iPrevY);
var rad = 6;
if (iXAbs > 10 || iYAbs > 10) {
mainContext.beginPath();
mainContext.arc(this.iPrevX, this.iPrevY, rad, Math.PI * 2, false);
mainContext.fill();
mainContext.stroke();
this.iPrevX = x;
this.iPrevY = y;
}
}
};
var circleCounter = [0];
mainContext.font = '21pt Arial';
mainContext.fillStyle = '#262732';
mainContext.textBaseline = 'top';
mainContext.fillText(circleCounter, 20, 20);
CircleBrush.init();
$('#draw1').mousedown(function (e) { // mouse down handler
cMoeDo = true;
var canvasOffset = $('#draw1').offset();
var canvasX = Math.floor(e.pageX - canvasOffset.left);
var canvasY = Math.floor(e.pageY - canvasOffset.top);
CircleBrush.startCurve(canvasX, canvasY);
circleCounter ++1;
});
$('#draw1').mouseup(function (e) { // mouse up handler
cMoeDo = false;
});
$('#draw1').mousemove(function (e) { // mouse move handler
if (cMoeDo) {
var canvasOffset = $('#draw1').offset();
var canvasX = Math.floor(e.pageX - canvasOffset.left);
var canvasY = Math.floor(e.pageY - canvasOffset.top);
CircleBrush.draw(canvasX, canvasY);
circleCounter ++1;
}
})
Demo fiddle http://jsfiddle.net/A2vyY/
Thanks in advance
You need to clear the space for the counter and redraw the count. In order to do so I put the counter and text drawing in the draw function like so
draw: function (x, y) {
var iXAbs = Math.abs(x - this.iPrevX);
var iYAbs = Math.abs(y - this.iPrevY);
var rad = 6;
if (iXAbs > 10 || iYAbs > 10) {
mainContext.beginPath();
mainContext.arc(this.iPrevX, this.iPrevY, rad, Math.PI*2, false);
mainContext.fill();
mainContext.stroke();
this.iPrevX = x;
this.iPrevY = y;
circleCounter ++;
mainContext.clearRect(0,0,50,25);
mainContext.fillText(circleCounter, 5, 5);
}
}
Updated jsFiddle (I moved the counter some so that there is more room for the dots)
You can put the counter in a separate div and just update the text
<div id="content">
<div id="counter">0</div>
<canvas id="draw1" height="500" width="500"></canvas>
</div>
Return true when a circle is drawn, false if not
draw: function (x, y) {
/* ... */
if (iXAbs > 10 || iYAbs > 10) {
/* ... */
return true;
}
return false;
}
increment and show as necessary
if (CircleBrush.draw(canvasX, canvasY)) {
++circleCounter;
$('#counter').text(circleCounter);
}
See modified JSFiddle