Javascript game; slowing down and freezing! How to solve it? - javascript

I'm starting to program a javascript tower defense; so far i have the movement of the minions over a trajectory. But I have a very big trouble, the game suddenly freezes for a couple of seconds. I'm guessing that is the garbage collector doing its job, any ideas on how i can solve this will be very good since i plan on adding a lot more of elements to the game and i don't want to keep coding till i get this flowing perfectly!
The code so far is pretty simple; you can check it out here
Here's the code:
<html>
<head>
<style>
#game{
background:red;
width:500px;
height:500px;
position:relative;
}
.mostro {
background:black;
width:15px;
height:15px;
position:absolute;
}
</style>
</head>
<body>
<div id="game">
<script type="text/javascript">
waypoint_x = [40, 140, 140, 220, 220, 80, 80, 340, 340, 420, 420];
waypoint_y = [140, 140, 60, 60, 240, 240, 320, 320, 100, 100, -20];
delay = 25;
new_monster = 0;
monsters_placed = 0;
monsters = [];
var d = new Date();
dist_x = 0;
dist_y = 0;
angle = 0;
mostro="";
total_monsters = 5;
function runGame() {
if (monsters_placed<total_monsters) {
new_monster++;
}
if (new_monster == delay) {
new_monster = 0;
document.getElementById("game").innerHTML = document.getElementById("game").innerHTML + '<div class="mostro" id="mostro-'+monsters_placed+'"></div>';
monsters_placed++;
}
for (i=0;i<monsters_placed;i=i+1) {
mostro = monsters[i];
dist_x = waypoint_x[mostro.point_to_reach] - mostro._x;
dist_y = waypoint_y[mostro.point_to_reach] - mostro._y;
if ((Math.abs(dist_x) + Math.abs(dist_y)) < 1) {
monsters[i].point_to_reach++;
}
angle = Math.atan2(dist_y, dist_x);
mostro._x = mostro._x + mostro.speed * Math.cos(angle);
mostro._y = mostro._y + mostro.speed * Math.sin(angle);
monsters[i]._rotation = angle/Math.PI*180-90
document.getElementById("mostro-"+i).style.left = Math.ceil(mostro._x) + "px";
document.getElementById("mostro-"+i).style.top = Math.ceil(mostro._y) + "px";
}
}
function setUpGame(){
for(i=0;i<=total_monsters;i++){
monsters[i] = new Object();
monsters[i].point_to_reach = 0;
monsters[i].speed = 1;
monsters[i]._x = 0;
monsters[i]._y = 0;
}
}
setUpGame();
setInterval(runGame,10);
</script>
</body>
</html>

Its not the garbage collector doing the job but in your code when you try to set the top and left positions, at a particuar time the value that you try to set in not a number. So the code breaks....
I think this occurs when the moving div crosses the top of the container with red background.

Yes, thats right: the delay is because when there are too many monsters, there are too many position updates that need to be done. This causes the "redraw" delay..
I see that there is a DOM element for each monster(as should be the case). But, you are updating their positions one by one.
Tips to reduce this lag:
Firstly, it would be a better stategy to update their positions en masse:
<div id='monster-container'>
<div id='monstser-1'></div>
<div id='monstser-2'></div>
<div id='monstser-3'></div>
</div>
So update the position of '#monster-container' when the monsters move. This way redraw time will definitely be minimized. What I say is from a primitive understanding of your game. You may need to modify this approach depending upon the path of the monsters. My approach will work directly only if the monsters only move in a straight line.
Secondly, if you are using img's for the monsters, consider using div's, and set the images as backgrounds of the div. This has given faster redraw performance in many of my pet games.
Thirdly, if you are using individual images for the monsters, consider using a composite image and CSS spriting.
Wish you luck with your game! Cheers!!
jrh

Yes, that's definitely a garbage collector. I am developing a JavaScript game myself, and I spent last few days trying to get rid of this problem. So far I can say that it's impossible.
However, I would like to note that different browsers have different garbage collectors, and for example, in Safari 4, your example runs perfectly smooth.
And here is interesting link on this topic: Reducing freezing with Object Pooling
Honestly, I think, that technique described in that article is not very helpful, because even in your example, that doesn't have any variables needed to be cleared, freezing is really noticeable.
Also I've rewritten your example, to test if global variables ruined performance.
You can see the difference yourself

Related

Exporting canvas as GIF/PNG in p5js

I use Atom Editor. I want to make 20 seconds GIF with my canvas.
saveFrames() has a limitation(I guess). It enables to save .png files to short gifs(3-5 seconds), even if I type saveFrames("aa","png",15,22);
I discovered CCapture.js but I could not find any code example to export canvas.
It does not have to be exported as GIF; but I want to at least save .png snaps of
my animation in canvas limitlessly. How can I do it?
My animation code in p5.js:
var x = 0;
var speed = 10;
var d1 = 100;
var d2 = 100;
function setup() {
createCanvas(600, 400);
background(0);
//saveFrames("aa","png",15,22);
}
function draw() {
stroke(random(100, 255), 0, random(100, 190));
strokeWeight(1.5);
ellipse(x, 100, d1, d1);
x = x + speed;
d1 = d1 - 0.6;
if (x > width || x < 0) {
speed = speed * -1;
fill(speed * 51);
}
ellipse(x, 300, d1, d1);
ellipse(x, 200, 50, 50);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.1/p5.min.js"></script>
I've been working on a new library which supports GIF exports, p5.createLoop.
This will run the sketch and then render a GIF at the same frame rate.
function setup() {
createCanvas(600, 400);
background(0);
frameRate(22)
createLoop({duration:15,gif:true})
}
Here's a codePen with the full example. It'll take about two minutes and is over 50MB, totally worth it
After a full day of research, surprizingly, I found a video uploaded youtube 2 weeks ago:
CCapture Video
Dont forget that CCapture exports (gif size=canvas sizex2).
>= 1.5.0
P5 has saveGif as of 1.5.0, which lets you write n seconds or frames to a downloadable gif. See the docs for usage.
< 1.5.0
I want to at least save .png snaps of my animation in canvas limitlessly. How can I do it?
You can do this directly in p5.js using save(`${name}.png`) after each frame you want to snap. This normally pops a dialog prompt asking you where you want to save the file, but you can disable this in browsers (as of 2021) so the images get automatically sent to the default download location.
For example, in Chrome (Version 92.0.4515.159 at the time of writing this post), navigate to chrome://settings/downloads and disable "Ask where to save each file before downloading":
With the prompt disabled, I found that p5.js's default framerate was too high for the browser to keep up with the downloads, so I added frameRate(4); to setup so that only 4 frames run per second. You can speed this up a bit depending on your needs and compute power, but some throttling helped give me a clean, sequential download of each of the frames into separate PNGs.
Clearly, this procedure isn't at all user-friendly (or particularly elegant). It's intended for turning your sketches into gifs yourself rather than an interface feature of a public-facing website. That said, the process does open up ideas for interactivity using buttons and other triggers for controlling the next frame to export in a creative way. You could use a button to start/stop the animation, for example, or programmatically determine specific conditions to emit the next frame.
Here's the two relevant lines added to your sketch. You'll want to run this locally rather than as a Stack snippet, which runs in a sandboxed frame that won't have download access:
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js">
</script>
</head>
<body>
<script>
var x = 0;
var speed = 10;
var d1 = 100;
var d2 = 100;
function setup() {
createCanvas(600, 400);
background(0);
frameRate(4); // <-- feel free to adjust
}
function draw() {
stroke(random(100, 255), 0, random(100, 190));
strokeWeight(1.5);
ellipse(x, 100, d1, d1);
x = x + speed;
d1 = d1 - 0.6;
if (x > width || x < 0) {
speed = speed * -1;
fill(speed * 51);
}
ellipse(x, 300, d1, d1);
ellipse(x, 200, 50, 50);
save(`frame_${frameCount}.png`); // <--
}
</script>
</body>
</html>
Once you have the frames downloaded, you can use your favorite PNG -> GIF creator. I used ffmpeg as described here: ffmpeg -f image2 -framerate 60 -i frame_%d.png -loop 0 out.gif.
Here's the result on the first few frames of your sketch (scaled down 50% with ffmpeg's -vf scale=300x200 flag to keep bandwidth for this page reasonable):

Laser Animation with TweenJS and EaselJS

So, I'm trying to create a laser effect, similar to the one located at http://map.norsecorp.com/
For this example, I have a 500x500 canvas. The current javascript part of the solution is located below:
function shootLaser(x, y) {
var beam = new createjs.Shape();
beam.graphics.beginFill("red");
beam.graphics.moveTo(0,1.5).lineTo(70,0).lineTo(70,3).closePath();
beam.x = 80;
beam.y = 50;
stage.addChild(beam);
beam.setBounds(0,0,70,3);
createjs.Tween.get(beam,{ loop: true, onChange: beamUpdate })
.to({ x: 400 }, 1000, createjs.Ease.linear());
}
function beamUpdate(e) {
var beam = e.currentTarget.target;
var targetX = e.currentTarget._curQueueProps.x;
if( targetX - beam.x < beam.getBounds().width ) {
beam.scaleX = (targetX - beam.x) / targetX;
} else {
beam.scaleX = 1;
}
}
This draws the line the way that I want to. However the scaleX method doesn't quite work (not even close really, it just gets extremely small very fast).
The problem is that I can't find a way to shrink the "laser" once it hits it's target. If I shoot it from 0px to 250px. It should hit 250px and begin shrinking until the 250th pixel has "consumed" it for lack of a better term. Any help is greatly appreciated.
P.S. I'm also open to doing this with other libraries or tools. I just haven't found them yet.

Spine multiple animations

So I have 2, or more skeletons for an animation (so, 2 or more json files). I want them to play at the same time and 10 seconds after, to play another animation.
Problem is that there is only one animation playing, and the second isn't displayed.
The way I'm doing it is the following:
<canvas id="animationCanvas" width="240" height="240"></canvas>
<script>
var first = true,
rendererFirst = new spine.SkeletonRenderer('http://someurl.com/images/'),
spineAFirst = /* My JSON Code */,
parsedFirst = JSON.parse(spineAFirst);
rendererFirst.scale = 0.2;
rendererFirst.load(spineAFirst);
rendererFirst.state.data.defaultMix = 1.0;
for (var i in parsedFirst.animations) {
if (first) {
first = false;
rendererFirst.state.setAnimationByName(0, i, true);
} else {
rendererFirst.state.addAnimationByName(0, i, true, 10);
}
}
rendererFirst.skeleton.x = 120;
rendererFirst.skeleton.y = 120;
rendererFirst.animate('animationCanvas');
</script>
And, of course, I'm doing it twice (or more). I tried as well with a single SkeletonRenderer, just loading (and setting or adding) animations as many times as I need, and it didn't worked.
It seems that the renderer is cleaning the canvas each time it is called, the better way to achieve this seems to create a canvas for each animation, and bind a renderer to each one.

Plotting with HTML5 Canvas

I decided to day to embark on element and I can say so far it have been nightmare to get it work. All I want is to plot a sine graph. So after good reading I still cannot either get origins nor get it plot. Below is what I have tried (my first time ever with that tag so excuse my ignorance). What makes me wonder is the guy here have it but the codes are hard to understand for beginner like me.
HTML
<!DOCTYPE html>
<html>
<head>
<title>Graphing</title>
<link type="text/css" rel="Stylesheet" href="graph.css" />
<script type="text/JavaScript" src="graph.js" ></script>
</head>
<body>
<canvas id="surface">Canvas not Supported</canvas>
</body>
</html>
CSS
#surface
{
width:300;
height:225;
border: dotted #FF0000 1px;
}
JavScript
window.onload = function()
{
var canvas = document.getElementById("surface");
var context = canvas.getContext("2d");
arr = [0,15, 30,45,60, 90,105, 120, 135, 150, 165, 180 ];
var x=0;
var y = 0;
for(i=0; i<arr.length; i++)
{
angle = arr[i]*(Math.PI/180); //radians
sine = Math.sin(angle);
context.moveTo(x,y);
context.lineTo(angle,sine);
context.stroke();
//set current varibles for next move
x = angle;
y = sine;
}
}
Since the range of sin x is [-1,1], it will only return numbers between -1 and 1, and that means all you will be drawing is a dot on the screen.
Also I see that you have an array ranging from 0 to 180. I believe you are trying to draw the curve with x from 0 degree to 180 degree? You don't really need to do this (anyway 12 points are not enough to draw a smooth line). Just do it with a for loop, with lines being the number of fragments.
First we start off by moving the point to the left of the canvas:
context.moveTo(0, 100 /*somewhere in the middle*/); //initial point
In most cases the first point won't be in the middle. But for sine it is. (You might want to fix it later though.)
for (var i = 0; i < lines; i++) {
//draw line
}
That's the loop drawing the curve. But what should we put inside? Well you can just take the number returned by the sine function and scale it up, flip it upside down, and shift it down half the way. I do that because the coordinate system in JavaScript is 0,0 in the top left instead of in the bottom left.
var sine = Math.sin(i/scale*2)*scale;
context.lineTo(i*frag, -sine+scale);
//i * frag = the position of x scaled up
//-sine + scale = the position of y, flipped, scaled, shifted down
//i/scale*2 = random scale I put in... you might want to figure out the
// correct scale with some math
So that's it. Viola, you have successfully plotted a graph in JavaScript.
Oh yes, don't forget to actually tell it to draw it on the canvas after the for loop has done its job:
context.stroke();
The demo: http://jsfiddle.net/DerekL/hK5rC/
PS: I see that you are trying to resize the canvas using CSS. Trust me, it won't work. :) You will have to define the dimension in HTML.

gameQuery collision detection

it is the first time for me to explore jQuery and gameQuery for building games using JavaScript, so am asking about sth that might look very naive, but really i cant get it.
i am developing a game like Space Invader, the detection for collision between player missile and enemies not working.
This is my code:
the definition for my Enemy class
function Enemy(node){
this.node = $(node);
this.pts_value = 0;
return true;
}
this is the code i use to add ten enemy sprite next to each other. the enemies move together to the left and the right
$.each(new Array(10), function(index, value) {
$("#enemy_group").addSprite("enemy2_"+index,{animation: enemies[2],
posx: index * 55, posy: 0, width: 48, height: 48})
$("#enemy2_"+index).addClass("enemy");
$("#enemy2_"+index)[0].enemy = new Enemy($("#enemy2_"+index));
$("#enemy2_"+index)[0].pts_value = 150;
});
so when i need to move the enemies, i move the enemies together, i move the group that includes all the sprites "#enemy_group"
if(ENEMY_TO_RIGHT){
var enemiesNewPos = (parseInt($("#enemy_group").css("left"))) + ENEMY_SPEED;
if(enemiesNewPos < PLAYGROUND_WIDTH - 550){
$("#enemy_group").css("left", ""+enemiesNewPos+"px");
} else {
ENEMY_TO_RIGHT = false;
}
} else {
var enemiesNewPos = (parseInt($("#enemy_group").css("left"))) - ENEMY_SPEED;
if(enemiesNewPos > 0){
$("#enemy_group").css("left", ""+enemiesNewPos+"px");
} else {
ENEMY_TO_RIGHT = true;
}
}
finally for collision detection, i want to remove the enemy sprite that the players missile has hit, each missile sprite has an added class names ".playerMissiles"
$(".playerMissiles").each(function(){
var posy = parseInt($(this).css("top"));
if(posy < 0){
$(this).remove();
return;
}
$(this).css("top", ""+(posy - MISSILE_SPEED)+"px");
//Test for collisions
var collided = $(this).collision(".enemy, .group");
if(collided.length > 0){
//An enemy has been hit!
collided.each(function(){
$(this).setAnimation(enemies[0], function(node){$(node).remove();});
})
}
});
i was following the documentation tutorial on the gameQuery website.
any help appreciated, thanks,
I can't see any problem with your code. I can only give you a few pointers:
Did you create "enemy_group" with the addGroup function?
Is "enemy_group" nested in something special like a custom div ? for the collision detection to work you need a chain of parent composed only of sprites and groups (and tiles map)
Is "enemy_group" nested in a sprite, if so it's a bad idea because you will need to add the selector for this sprite in your methode call and this sprite will be included in the colliding element list.
The same goes for the ".playerMissiles"
Just to be sure what version of gameQuery and jQuery do you use? The last version from gitHub is unstable and I wouldn't recomend using it, user 0.5.1 instead.
You could use jquery collision plugin, so you avoid doid the logic by yourself.
Hope this helps. Cheers

Categories

Resources