The idea:
Hi, this is my first experiment with canvas animation. I'm not sure if this concept already exists but I've dubbed my version "two-state animation". The idea is this: A map is created with three sub-maps for each object on-screen. One of the sub-map holds the current state properties (of an animating object), the second holds the final destination (of an animating object), and the third for divided increments to ensure the object (on-screen) reaches its final destination in the number of frames set in `framesForChange.
The problem:
Notice, as the animation progresses, the objects are gradually pushed down to the bottom right but the numbers for x and y axis continue to grow. I'm aware that I'm generating random number to confine it within the width (x) and height (y) of the viewport. Why is this happening? How can I fix it?
The code:
If you have any suggestions for how I could make this more efficient (or, more primarily, fix the problem [mentioned above]), please mention it in your answer or in the comments. Thank you.
window.addEventListener('load', initializeCanvasTwo());
function initializeCanvasTwo() {
var canvas, c, map = {},
framesForChange = 360;
initializeCanvas();
resizeCanvas();
initializeMapping();
draw();
window.addEventListener('resize', function() {
resizeCanvas();
});
function initializeCanvas() {
canvas = document.getElementById('canvasTwo');
c = canvas.getContext('2d');
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
c.width = window.innerWidth;
c.height = window.innerHeight;
}
function initializeMapping() {
var n = Math.floor(window.innerWidth * window.innerHeight / 20000);
for (var i = 0; i < n; i++) {
map[i] = {
current: {
startX: getRandomX(),
startY: getRandomY(),
endX: getRandomX(),
endY: getRandomY()
},
final: {
startX: getRandomX(),
startY: getRandomY(),
endX: getRandomX(),
endY: getRandomY()
},
increment: {
}
};
map[i].increment.startXIncrement = (map[i].final.startX - map[i].current.startX) / framesForChange;
map[i].increment.startYIncrement = (map[i].final.startY - map[i].current.startY) / framesForChange;
map[i].increment.endXIncrement = (map[i].final.endX - map[i].current.endX) / framesForChange;
map[i].increment.endYIncrement = (map[i].final.endY - map[i].current.endY) / framesForChange;
}
}
function getRandomX() {
return Math.floor(Math.random() * window.innerWidth);
}
function getRandomY() {
return Math.floor(Math.random() * window.innerHeight);
}
function updateMap() {
var n = Object.keys(map).length;
for (var i = 0; i < n; i++) {
if (map[i].final.startX - map[i].current.startX < map[i].increment.startXIncrement) {
//startX transition complete: set new destination...
map[i].final.startX = getRandomX();
map[i].increment.startXIncrement = (map[i].final.startX - map[i].current.startX) / framesForChange;
} else {
map[i].current.startX += map[i].increment.startXIncrement;
}
if (map[i].final.startY - map[i].current.startY < map[i].increment.startYIncrement) {
//startY transition complete: set new destination...
map[i].final.startY = getRandomY();
map[i].increment.startYIncrement = (map[i].final.startY - map[i].current.startY) / framesForChange;
} else {
map[i].current.startY += map[i].increment.startYIncrement;
}
if (map[i].final.endX - map[i].current.endX < map[i].increment.endXIncrement) {
//endX transition complete: set new destination...
map[i].final.endX = getRandomX();
map[i].increment.endXIncrement = (map[i].final.endX - map[i].current.endX) / framesForChange;
} else {
map[i].current.endX += map[i].increment.endXIncrement;
}
if (map[i].final.endY - map[i].current.endY < map[i].increment.endYIncrement) {
//endY transition complete: set new destination...
map[i].final.endY = getRandomY();
map[i].increment.endYIncrement = (map[i].final.endY - map[i].current.endY) / framesForChange;
} else {
map[i].current.endY += map[i].increment.endYIncrement;
}
}
}
function draw() {
c.clearRect(0, 0, window.innerWidth, window.innerHeight);
c.strokeStyle = 'blue';
var n = Object.keys(map).length;
for (var i = 0; i < n; i++) {
c.beginPath();
c.moveTo(map[i].current.startX, map[i].current.startY);
c.lineTo(map[i].current.endX, map[i].current.endY);
c.stroke();
c.closePath();
}
updateMap();
requestAnimationFrame(draw);
}
}
html,
body {
margin: 0;
padding: 0;
}
canvas {
width: 100%;
height: 100%;
margin-bottom: -4px;
}
<canvas id="canvasTwo"></canvas>
The major problem is with the if conditions.
You check if the final - current is less than the increment. But when the x tries to move leftwards or the y upwards (which means start > final) then you create new values for them. So it is only allowed to go to the right and bottom. (to solve this you should use Math.abs on both sides of the if, but that would degrade performance even further)
As slight improvements you should use an array for the map and since all elements take the same frames to complete their animation you should not check if each point has reached its destination, but count the frames and update the end positions all at once.
window.addEventListener('load', initializeCanvasTwo());
function initializeCanvasTwo() {
var canvas, c, map = [],
framesForChange = 360,
currentFrame = 360;
initializeCanvas();
resizeCanvas();
initializeMapping();
draw();
window.addEventListener('resize', function() {
resizeCanvas();
});
function initializeCanvas() {
canvas = document.getElementById('canvasTwo');
c = canvas.getContext('2d');
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
c.width = window.innerWidth;
c.height = window.innerHeight;
}
function initializeMapping() {
var n = Math.floor(window.innerWidth * window.innerHeight / 20000);
for (var i = 0; i < n; i++) {
map[i] = {
current: {
startX: getRandomX(),
startY: getRandomY(),
endX: getRandomX(),
endY: getRandomY()
},
final: {
startX: getRandomX(),
startY: getRandomY(),
endX: getRandomX(),
endY: getRandomY()
},
increment: {
}
};
map[i].increment.startXIncrement = (map[i].final.startX - map[i].current.startX) / framesForChange;
map[i].increment.startYIncrement = (map[i].final.startY - map[i].current.startY) / framesForChange;
map[i].increment.endXIncrement = (map[i].final.endX - map[i].current.endX) / framesForChange;
map[i].increment.endYIncrement = (map[i].final.endY - map[i].current.endY) / framesForChange;
}
}
function getRandomX() {
return Math.floor(Math.random() * window.innerWidth);
}
function getRandomY() {
return Math.floor(Math.random() * window.innerHeight);
}
function updateMap() {
var n = map.length;
currentFrame = --currentFrame || framesForChange;
if (currentFrame == framesForChange){
// create new values
for (var i = 0; i < n; i++) {
map[i].final.startX = getRandomX();
map[i].final.startY = getRandomY();
map[i].final.endX = getRandomX();
map[i].final.endY = getRandomY();
map[i].increment.startXIncrement = (map[i].final.startX - map[i].current.startX) / framesForChange;
map[i].increment.startYIncrement = (map[i].final.startY - map[i].current.startY) / framesForChange;
map[i].increment.endXIncrement = (map[i].final.endX - map[i].current.endX) / framesForChange;
map[i].increment.endYIncrement = (map[i].final.endY - map[i].current.endY) / framesForChange;
}
}
for (var i = 0; i < n; i++) {
map[i].current.startX += map[i].increment.startXIncrement;
map[i].current.startY += map[i].increment.startYIncrement;
map[i].current.endX += map[i].increment.endXIncrement;
map[i].current.endY += map[i].increment.endYIncrement;
}
}
function draw() {
c.clearRect(0, 0, window.innerWidth, window.innerHeight);
var n = map.length;
c.strokeStyle = 'blue';
for (var i = 0; i < n; i++) {
c.beginPath();
c.moveTo(map[i].current.startX, map[i].current.startY);
c.lineTo(map[i].current.endX, map[i].current.endY);
c.stroke();
c.closePath();
}
updateMap();
requestAnimationFrame(draw);
}
}
html,
body {
margin: 0;
padding: 0;
}
canvas {
width: 100%;
height: 100%;
margin-bottom: -4px;
}
<canvas id="canvasTwo"></canvas>
Related
I would like to stop drawing new particles after 5 seconds and restart drawing on click anywhere.
Here is the code https://codepen.io/Jeremboo/pen/ENVaMY for the animation.
Right now it keeps on drawing until you click on the screen to restart it again.
What I want is to stop drawing after 5 seconds and then on click anywhere restart it from zero particles. I tried the following to stop but I am not able to start it again.
this is the js code
/**/ const canvas = document.createElement('canvas');
/**/ const context = canvas.getContext('2d');
/**/ let windowWidth = canvas.width = window.innerWidth;
/**/ let windowHeight = canvas.height = window.innerHeight;
/**/ canvas.id = 'canvas';
/**/ document.body.insertBefore(canvas, document.body.firstChild);
/**/ /* ---- CORE END ---- */
/* ---- CREATING ZONE ---- */
/* ---- SETTINGS ---- */
const numberParticlesStart = 1000;
const particleSpeed = 0.3;
const velocity = 0.9;
const circleWidth = 50;
/* ---- INIT ---- */
let particles = [];
const getRandomFloat = (min, max) => (Math.random() * (max - min) + min);
/* ---- Particle ---- */
function Particle (x, y) {
this.x = x;
this.y = y;
this.vel = {
x : getRandomFloat(-20, 20)/100,
y : getRandomFloat(-20, 20)/100,
min : getRandomFloat(2, 10),
max : getRandomFloat(10, 100)/10
}
this.color = 'rgba(255, 255, 255, 0.05)';
}
Particle.prototype.render = function() {
context.beginPath();
context.fillStyle = this.color;
context.arc(this.x, this.y, 1, 0, Math.PI * 2);
context.fill();
};
Particle.prototype.update = function() {
const forceDirection = {
x: getRandomFloat(-1, 1),
y: getRandomFloat(-1, 1),
};
if (Math.abs(this.vel.x + forceDirection.x) < this.vel.max) {
this.vel.x += forceDirection.x;
}
if (Math.abs(this.vel.y + forceDirection.y) < this.vel.max) {
this.vel.y += forceDirection.y;
}
this.x += this.vel.x * particleSpeed;
this.y += this.vel.y * particleSpeed;
if (Math.abs(this.vel.x) > this.vel.min) {
this.vel.x *= velocity;
}
if (Math.abs(this.vel.y) > this.vel.min) {
this.vel.y *= velocity;
}
this.testBorder();
};
Particle.prototype.testBorder = function() {
if (this.x > windowWidth) {
this.setPosition(this.x, 'x');
} else if (this.x < 0) {
this.setPosition(windowWidth, 'x');
}
if (this.y > windowHeight) {
this.setPosition(this.y, 'y');
} else if (this.y < 0) {
this.setPosition(windowHeight, 'y');
}
};
Particle.prototype.setPosition = function(pos, coor) {
if (coor === 'x') {
this.x = pos;
} else if (coor === 'y') {
this.y = pos;
}
};
/* ---- Functions ----*/
function loop() {
let i;
const length = particles.length;
for (i = 0; i < length; i++) {
particles[i].update();
particles[i].render();
}
requestAnimationFrame(loop);
}
/* ---- START ---- */
function init() {
let i;
for (i = 0; i < numberParticlesStart; i++) {
const angle = Math.random() * 360;
particles.push(new Particle(
windowWidth * 0.5 + (Math.cos(angle) * circleWidth),
windowHeight * 0.5 - (Math.sin(angle) * circleWidth),
));
}
}
init();
window.onresize = () => {
windowWidth = canvas.width = window.innerWidth;
windowHeight = canvas.height = window.innerHeight;
particles = [];
context.clearRect(0,0, windowWidth, windowHeight);
init();
}
window.addEventListener('click', () => {
particles = [];
context.clearRect(0,0, windowWidth, windowHeight);
init();
});
loop();
i replaced the function loop() with this to stop it after 5 seconds -
function loop(stp) {
let i;
const length = particles.length;
for (i = 0; i < length; i++) {
particles[i].update();
particles[i].render();
}
if (stp > 5000) return;
requestAnimationFrame(loop);
}
Try to rely on system clock, as setTimeout and setInterval are not accurate
let start;
function loop(startTime) {
let i;
if(startTime){
// Set the current time in milliseconds from 1st of jenuary 1970
start = new Date().getTime();
}
const length = particles.length;
for (i = 0; i < length; i++) {
particles[i].update();
particles[i].render();
}
// Comparison from current dateTime with the stored one
// as setInterval and setTimeout are not accurate
if (start - (new Date().getTime() >= 5000) return;
requestAnimationFrame(loop);
}
loop(true);
I found a canvas animation with requestAnimationFrame that im trying to change to my needs and i had a huge issue with cpu usage.. going up to 80% and i started to shave off things i dont want or need. Im down to 40-50% cpu usage now so i would like some help with what i could do to optimize this code and reduce the cpu usage.
I have read about requestAnimationFrame and that it runs at 60fps or even as high as possible and mabye that has a big part in the performance, perhaps there is something i could do there?
/**
* Stars
* Inspired by Steve Courtney's poster art for Celsius GS's Drifter - http://celsiusgs.com/drifter/posters.php
* by Cory Hughart - http://coryhughart.com
*/
// Settings
var particleCount = 40,
flareCount = 0,
motion = 0.05,
tilt = 0.05,
color = '#00FF7B',
particleSizeBase = 1,
particleSizeMultiplier = 0.5,
flareSizeBase = 100,
flareSizeMultiplier = 100,
lineWidth = 1,
linkChance = 75, // chance per frame of link, higher = smaller chance
linkLengthMin = 5, // min linked vertices
linkLengthMax = 7, // max linked vertices
linkOpacity = 0.25; // number between 0 & 1
linkFade = 90, // link fade-out frames
linkSpeed = 0, // distance a link travels in 1 frame
glareAngle = -60,
glareOpacityMultiplier = 0.05,
renderParticles = true,
renderParticleGlare = true,
renderFlares = false,
renderLinks = false,
renderMesh = false,
flicker = false,
flickerSmoothing = 15, // higher = smoother flicker
blurSize = 0,
orbitTilt = true,
randomMotion = true,
noiseLength = 1000,
noiseStrength = 1;
var canvas = document.getElementById('stars'),
context = canvas.getContext('2d'),
mouse = { x: 0, y: 0 },
m = {},
r = 0,
c = 1000, // multiplier for delaunay points, since floats too small can mess up the algorithm
n = 0,
nAngle = (Math.PI * 2) / noiseLength,
nRad = 100,
nScale = 0.5,
nPos = {x: 0, y: 0},
points = [],
vertices = [],
triangles = [],
links = [],
particles = [],
flares = [];
function init() {
var i, j, k;
// requestAnimFrame polyfill
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function( callback ){
};
})();
// Size canvas
resize();
mouse.x = canvas.clientWidth / 2;
mouse.y = canvas.clientHeight / 2;
// Create particle positions
for (i = 0; i < particleCount; i++) {
var p = new Particle();
particles.push(p);
points.push([p.x*c, p.y*c]);
}
vertices = Delaunay.triangulate(points);
var tri = [];
for (i = 0; i < vertices.length; i++) {
if (tri.length == 3) {
triangles.push(tri);
tri = [];
}
tri.push(vertices[i]);
}
// Tell all the particles who their neighbors are
for (i = 0; i < particles.length; i++) {
// Loop through all tirangles
for (j = 0; j < triangles.length; j++) {
// Check if this particle's index is in this triangle
k = triangles[j].indexOf(i);
// If it is, add its neighbors to the particles contacts list
if (k !== -1) {
triangles[j].forEach(function(value, index, array) {
if (value !== i && particles[i].neighbors.indexOf(value) == -1) {
particles[i].neighbors.push(value);
}
});
}
}
}
// Animation loop
(function animloop(){
requestAnimFrame(animloop);
resize();
render();
})();
}
function render() {
if (randomMotion) {
n++;
if (n >= noiseLength) {
n = 0;
}
nPos = noisePoint(n);
}
if (renderParticles) {
// Render particles
for (var i = 0; i < particleCount; i++) {
particles[i].render();
}
}
}
function resize() {
canvas.width = window.innerWidth * (window.devicePixelRatio || 1);
canvas.height = canvas.width * (canvas.clientHeight / canvas.clientWidth);
}
// Particle class
var Particle = function() {
this.x = random(-0.1, 1.1, true);
this.y = random(-0.1, 1.1, true);
this.z = random(0,4);
this.color = color;
this.opacity = random(0.1,1,true);
this.flicker = 0;
this.neighbors = []; // placeholder for neighbors
};
Particle.prototype.render = function() {
var pos = position(this.x, this.y, this.z),
r = ((this.z * particleSizeMultiplier) + particleSizeBase) * (sizeRatio() / 1000),
o = this.opacity;
context.fillStyle = this.color;
context.globalAlpha = o;
context.beginPath();
context.fill();
context.closePath();
if (renderParticleGlare) {
context.globalAlpha = o * glareOpacityMultiplier;
context.ellipse(pos.x, pos.y, r * 100, r, (glareAngle - ((nPos.x - 0.5) * noiseStrength * motion)) * (Math.PI / 180), 0, 2 * Math.PI, false);
context.fill();
context.closePath();
}
context.globalAlpha = 1;
};
// Flare class
// Link class
var Link = function(startVertex, numPoints) {
this.length = numPoints;
this.verts = [startVertex];
this.stage = 0;
this.linked = [startVertex];
this.distances = [];
this.traveled = 0;
this.fade = 0;
this.finished = false;
};
// Utils
function noisePoint(i) {
var a = nAngle * i,
cosA = Math.cos(a),
sinA = Math.sin(a),
rad = nRad;
return {
x: rad * cosA,
y: rad * sinA
};
}
function position(x, y, z) {
return {
x: (x * canvas.width) + ((((canvas.width / 2) - mouse.x + ((nPos.x - 0.5) * noiseStrength)) * z) * motion),
y: (y * canvas.height) + ((((canvas.height / 2) - mouse.y + ((nPos.y - 0.5) * noiseStrength)) * z) * motion)
};
}
function sizeRatio() {
return canvas.width >= canvas.height ? canvas.width : canvas.height;
}
function random(min, max, float) {
return float ?
Math.random() * (max - min) + min :
Math.floor(Math.random() * (max - min + 1)) + min;
}
// init
if (canvas) init();
html,
body {
margin: 0;
padding: 0;
height: 100%;
}
body {
background: #375848;
background-image: linear-gradient(-180deg, rgba(0,0,0,0.00) 0%, #000000 100%);
}
#stars {
display: block;
position: relative;
width: 100%;
height: 16rem;
height: 100vh;
z-index: 1;
position: absolute;
}
<script src="https://rawgit.com/ironwallaby/delaunay/master/delaunay.js"></script>
<script src="http://requirejs.org/docs/release/2.1.15/minified/require.js"></script>
<canvas id="stars" width="300" height="300"></canvas>
I had two things to suggest, one of which someone already mentioned in a comment -- only resize your canvas on resize events.
The other was to compare the time of each call to your animation loop with the time of the last call, and only render again if a certain amount of time has passed -- say 16 milliseconds for about a 60 fps rate.
var lastRender = 0;
(function animloop(){
requestAnimFrame(animloop);
var now = Date.now();
if (now >= lastRender + 16) {
render();
lastRender = now;
}
})();
The Problem
I am creating a game where you have to move away from or dodge projectiles coming towards you, I have enabled the user to move the image they are controlling but at the moment the user can place the image anywhere on the canvas.
The Question
How can I only allow the user to move along designated part of the canvas and only along the X axis? for example:
Here is my game, "working progress":
The user is in control of the ship, they should only be able to move left or right like this for example:
The Code
<script>
var game = create_game();
game.init();
//music
var snd = new Audio("menu.mp3");
snd.play();
document.getElementById('mute').addEventListener('click', function (evt) {
if ( snd.muted ) {
snd.muted = false
evt.target.innerHTML = 'mute'
}
else {
snd.muted = true
evt.target.innerHTML = 'unmute'
}
})
function create_game() {
debugger;
var level = 1;
var projectiles_per_level = 1;
var min_speed_per_level = 1;
var max_speed_per_level = 2;
var last_projectile_time = 0;
var next_projectile_time = 0;
var width = 600;
var height = 500;
var delay = 1000;
var item_width = 30;
var item_height = 30;
var total_projectiles = 0;
var projectile_img = new Image();
var projectile_w = 30;
var projectile_h = 30;
var player_img = new Image();
var c, ctx;
var projectiles = [];
var player = {
x: 200,
y: 400,
score: 0
};
function init() {
projectile_img.src = "apple.png";
player_img.src = "basket.png";
level = 1;
total_projectiles = 0;
projectiles = [];
c = document.getElementById("c");
ctx = c.getContext("2d");
ctx.fillStyle = "#ff6600";
ctx.fillRect(0, 0, 500, 600);
c.addEventListener("mousemove", function (e) {
//moving over the canvas.
var bounding_box = c.getBoundingClientRect();
player.x = (e.clientX - bounding_box.left) * (c.width / bounding_box.width) - player_img.width / 2;
player.y = (e.clientY - bounding_box.top) * (c.height / bounding_box.height) - player_img.height / 2;
}, false);
setupProjectiles();
requestAnimationFrame(tick);
}
function setupProjectiles() {
var max_projectiles = level * projectiles_per_level;
while (projectiles.length < max_projectiles) {
initProjectile(projectiles.length);
}
}
function initProjectile(index) {
var max_speed = max_speed_per_level * level;
var min_speed = min_speed_per_level * level;
projectiles[index] = {
x: Math.round(Math.random() * (width - 2 * projectile_w)) + projectile_w,
y: -projectile_h,
v: Math.round(Math.random() * (max_speed - min_speed)) + min_speed,
delay: Date.now() + Math.random() * delay
}
total_projectiles++;
}
function collision(projectile) {
if (projectile.y + projectile_img.height < player.y + 74) {
return false;
}
if (projectile.y > player.y + 74) {
return false;
}
if (projectile.x + projectile_img.width < player.x + 177) {
return false;
}
if (projectile.x > player.x + 177) {
return false;
}
return true;
}
function maybeIncreaseDifficulty() {
level = Math.max(1, Math.ceil(player.score / 10));
setupProjectiles();
}
function tick() {
var i;
var projectile;
var dateNow = Date.now();
c.width = c.width;
for (i = 0; i < projectiles.length; i++) {
projectile = projectiles[i];
if (dateNow > projectile.delay) {
projectile.y += projectile.v;
if (collision(projectile)) {
initProjectile(i);
player.score++;
} else if (projectile.y > height) {
initProjectile(i);
} else {
ctx.drawImage(projectile_img, projectile.x, projectile.y);
}
}
}
ctx.font = "bold 24px sans-serif";
ctx.fillStyle = "#ff6600";
ctx.fillText(player.score, c.width - 50, 50);
ctx.fillText("Level: " + level, 20, 50);
ctx.drawImage(player_img, player.x, player.y);
maybeIncreaseDifficulty();
requestAnimationFrame(tick);
}
return {
init: init
};
}
</script>
JSFiddle (Broken)
https://jsfiddle.net/3oc4jsf6/10/
If your ship is tied to mouse movement, and you want to only allow movement across the X-axis you can simply avoid changing its .y property in your mousemove listener.
Remove this line:
player.y = (e.clientY - bounding_box.top) * (c.height / bounding_box.height) - player_img.height / 2;
So I am in the process of making this rhythm game with canvas, all that it does up to this point is render the receivers (the point the blocks are going to collide with so the user can press the corresponding buttons and get points) and it renders the dancer animation. For some reason though after the page is open for a while the dancer slows down significantly and continues to gradually slow.
I can't figure out why or how to fix it. Anyone have any ideas?
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 500;
canvas.height = 600;
document.body.appendChild(canvas);
var spritesheet = null;
var dancer = {
time:0,
speed:0,
image:null,
x:0,
y:0,
currentFrame:0,
width:50,
height:100,
ready:false
}
function onload() {
spritesheet = new Image();
spritesheet.src = "danceSheet.png";
spritesheet.onload = initiate;
}
function initiate() {
game.startTime = new Date().getTime() / 1000;
dancer.x = (canvas.width / 2) - dancer.width;
dancer.y = 120;
game.initiateReceivers();
main();
}
var game = {
startTime:0,
currentTime:0,
receivers:[],
senders:[],
lanes:[],
drawDancer: function() {
ctx.drawImage(spritesheet, dancer.width * dancer.currentFrame, 0, dancer.width, dancer.height, dancer.x, dancer.y, dancer.width, dancer.height );
},
clearWindow: function() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
},
initiateReceivers: function() {
var distanceRate = canvas.width / 4;
var position = 30;
for(initiates = 0; initiates < 4; initiates++) {
this.receivers[initiates] = new receivers;
this.receivers[initiates].x = position;
this.receivers[initiates].y = 300;
position += distanceRate;
}
}
}
var gameUpdates = {
updateMovement: function() {
game.currentTime = new Date().getTime() / 1000;
dancer.time = game.currentTime - game.startTime;
if(dancer.time >= 0.1) {
game.startTime = new Date().getTime() / 1000;
dancer.currentFrame += 1;
if(dancer.currentFrame == 12) dancer.currentFrame = 0;
}
},
collision: function(shapeA, shapeB) {
// get the vectors to check against
var vX = (shapeA.x + (shapeA.width / 2)) - (shapeB.x + (shapeB.width / 2)),
vY = (shapeA.y + (shapeA.height / 2)) - (shapeB.y + (shapeB.height / 2)),
// add the half widths and half heights of the objects
hWidths = (shapeA.width / 2) + (shapeB.width / 2),
hHeights = (shapeA.height / 2) + (shapeB.height / 2);
// if the x and y vector are less than the half width or half height, they we must be inside the object, causing a collision
if (Math.abs(vX) < hWidths && Math.abs(vY) < hHeights) {
return true;
}
return false;
}
}
function receivers() {
this.x = 0;
this.y = 0;
this.width = 60;
this.height = 10;
}
function senders() {
this.x = 0;
this.y = 0;
this.width = 60;
this.height = 10;
this.lane = 0;
this.status = true;
}
function update() {
gameUpdates.updateMovement();
}
function render() {
game.clearWindow();
game.drawDancer();
game.receivers.forEach( function(receiver) {
ctx.rect(receiver.x,receiver.y,receiver.width,receiver.height);
ctx.fillStyle = "red";
ctx.fill();
}
)
}
function main() {
update();
render();
requestAnimationFrame(main);
}
I'm trying to get an idea of the slowness you're seeing so I adjusted your code to get a frames-per-second. Haven't found the cause yet for the drop.
Update
I found that the frames were dropping from drawing the rectangles. I've altered the code to use fillRect put the fill style outside of the loop. This seems to have fixed the frame drop.
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 500;
canvas.height = 600;
document.body.appendChild(canvas);
var spritesheet = null;
var dancer = {
time: 0,
speed: 0,
image: null,
x: 0,
y: 0,
currentFrame: 0,
width: 50,
height: 100,
ready: false
}
function onload() {
spritesheet = document.createElement('canvas');
spritesheet.width = (dancer.width * 12);
spritesheet.height = dancer.height;
var ctx = spritesheet.getContext('2d');
ctx.font = "30px Arial";
ctx.fillStyle = "black";
ctx.strokeStyle = "red";
for (var i = 0; i < 12; i++) {
var x = (i * dancer.width) + 10;
ctx.fillText(i,x,60);
ctx.beginPath();
ctx.rect(i*dancer.width,0,dancer.width,dancer.height);
ctx.stroke();
}
initiate();
}
function initiate() {
game.startTime = new Date().getTime() / 1000;
dancer.x = (canvas.width / 2) - dancer.width;
dancer.y = 120;
game.initiateReceivers();
main();
}
var game = {
startTime: 0,
currentTime: 0,
receivers: [],
senders: [],
lanes: [],
drawDancer: function() {
ctx.drawImage(spritesheet, dancer.width * dancer.currentFrame, 0, dancer.width, dancer.height, dancer.x, dancer.y, dancer.width, dancer.height);
//ctx.strokeStyle="red";
//ctx.beginPath();
//ctx.lineWidth = 3;
//ctx.rect(dancer.x,dancer.y,dancer.width,dancer.height);
//ctx.stroke();
},
clearWindow: function() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
},
initiateReceivers: function() {
var distanceRate = canvas.width / 4;
var position = 30;
for (initiates = 0; initiates < 4; initiates++) {
this.receivers[initiates] = new receivers;
this.receivers[initiates].x = position;
this.receivers[initiates].y = 300;
position += distanceRate;
}
}
};
var gameUpdates = {
updateMovement: function() {
game.currentTime = new Date().getTime() / 1000;
dancer.time = game.currentTime - game.startTime;
if (dancer.time >= 0.1) {
game.startTime = new Date().getTime() / 1000;
dancer.currentFrame += 1;
if (dancer.currentFrame == 12) dancer.currentFrame = 0;
}
},
collision: function(shapeA, shapeB) {
// get the vectors to check against
var vX = (shapeA.x + (shapeA.width / 2)) - (shapeB.x + (shapeB.width / 2)),
vY = (shapeA.y + (shapeA.height / 2)) - (shapeB.y + (shapeB.height / 2)),
// add the half widths and half heights of the objects
hWidths = (shapeA.width / 2) + (shapeB.width / 2),
hHeights = (shapeA.height / 2) + (shapeB.height / 2);
// if the x and y vector are less than the half width or half height, they we must be inside the object, causing a collision
if (Math.abs(vX) < hWidths && Math.abs(vY) < hHeights) {
return true;
}
return false;
}
}
function receivers() {
this.x = 0;
this.y = 0;
this.width = 60;
this.height = 10;
}
function senders() {
this.x = 0;
this.y = 0;
this.width = 60;
this.height = 10;
this.lane = 0;
this.status = true;
}
function update() {
gameUpdates.updateMovement();
}
function render() {
game.clearWindow();
game.drawDancer();
ctx.fillStyle = "red";
game.receivers.forEach(function(receiver) {
ctx.fillRect(receiver.x, receiver.y, receiver.width, receiver.height);
});
ctx.fillText(fps,10,10);
}
var fps = 0;
var frames = 0;
function getFps() {
fps = frames;
frames = 0;
setTimeout(getFps,1000);
}
getFps();
function main() {
update();
render();
frames++;
requestAnimationFrame(main);
}
onload();
canvas {
border:1px solid blue;
}
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
};
}