Optimizing html5 canvas game - javascript

Right now I've got two game loops in a game I'm making. A draw loop that loops through an array of objects on screen and a logic loop that does game logic. I have the logic loop running about 10 more frames than the draw loop. I have this set up like this because doing game logic may take longer and I don't want it to interfere with the draw loop.
I have the logic loop set up like this:
vs.logicloop = function(){
vs.Gameloop();
//do the updating of object scripts
if(vs.windowActive){
var l = vs.scenegraph.length;
var i = 0;
while(i < l){
vs.scenegraph[i].logicScript();
i++;
}
}
//restart loop
setTimeout(vs.logicloop, 1000/(vs.fps+10));
};
and the draw loop like this:
vs.drawloop = function(){
//clear the screen
vsd.clr();
//goes through everything in the scene
//graph and draws it and runs each object's
//personal draw code
if(vs.windowActive){
var l = vs.scenegraph.length;
var i = 0;
while(i < l){
vs.ctx.save();
vs.scenegraph[i].update();
vs.scenegraph[i].draw();
vs.scenegraph[i].drawScript();
vs.ctx.restore();
i++;
}
}
//restart loop
setTimeout(vs.drawloop, 1000/vs.fps);
};
I'm using setTimeout because I heard that setInterval will cause loops to overlap if one isn't finished yet. Are there any optimizations I can do to really get some speed? Especially optimizing the game loops.
I've heard of some javascript engines getting thousands of objects on screen at once. I can't imagine how they do that, at most mine can get up to 100 objects on screen on a very old computer and about 700 on a reasonably stocked computer. And that's without a lot of game code running in the background and before I've worked out how to do pixel perfect collision detection and physics.
My process is to draw a background color fillRect over the canvas every draw loop, then looping through all the objects and drawing their draw code. Also it doesn't try to draw objects out of view.
This is my first paying job, and I really want to impress. Plus I can keep ownership of the engine once I'm done with the game.
Thanks A Lot

if you use floating values for sprite coordinates, try converting them to integers. that will cost you losing subpixel rendering but you will gain a lot of speed.
don't use images with odd widths. always use widths as powers of 2.
this one is hard to implement in some cases but if your game is suitable, don't clear screen and redraw everything each frame. instead, draw changed parts.
if you have to clear the canvas, don't draw a blank rectangle. try setting the canvas width/height again with the same size. that should reset the pixels faster than rectangle drawing.
rest of the methods i can suggest are not HTML5 canvas dependent but general subjects like using bit shiftings, inverse loops, and operator instead of modulo when possible, precalculations etc.

Oh geez, I could probably write you a full sonnet here if you posted all your code. Here's a quick rundown:
Read Emir's answer. All of those are good except the last one which is very dependent on the situation. Setting canvas.width = canvas.width to clear the canvas can be faster on some browsers but it also clobbers all canvas state (ie the last set fill and font) which can slow things down because setting those properties is actually painfully slow.
Read my articles on Canvas performance: http://simonsarris.com/blog/tag/performance
I have a lot of other tips saved up in a private document that I'm writing a small ebook out of. If you want early access to it I can probably allow that.
Pick up High performance JavaScript by Zakas and read it.
Don't use save() and restore() like you are in the quoted code unless you must. They are just slowing things down.
For the timer see http://paulirish.com/2011/requestanimationframe-for-smart-animating/
Multiple canvases for foreground-background-middleground can definitely help. Caching things on in-memory canvases can definitely help. It all depends on what you're drawing.
A lot of performance problems are due to death by a thousand tiny cuts. For instance:
vs.scenegraph[i].update();
vs.scenegraph[i].draw();
vs.scenegraph[i].drawScript();
To
var scene = vs.scenegraph[i];
scene.update();
scene.draw();
scene.drawScript();
Will help a minute amount. How many opportunities for minute-amount stuff you have I don't know - we'd need to see a lot more code.

I heard that setInterval will cause loops to overlap if one isn't finished yet.
This is false, JavaScript is single threaded. This means that if your first interval is still running when it hits the next step, the next step will be delayed until the first step has finished computing. This also means that you can't rely on setInterval to be accurate if you start doing a lot of computations.

I'd echo much of all has been said by other people. Integer values, use request animation frame, etc. If drawing text be careful how often you set fonts. Also you might find using an object pool can help if you are creating lots of temporary objects per frame.
As a general read on game loops I'd recommend this: http://www.koonsolo.com/news/dewitters-gameloop/

Related

Is there any way to find the color of a pixel in JavaScript WITHOUT using getImageData()?

I'm making a small online multiplayer game with JavaScript and the native Canvas API (WebGL). I want to find the color of pixels on the screen as a form of collision detection, I figure it'd save resources to not have to process every shape every frame, but rather to simply check if the color of a pixel at a certain position is such that it is contacting a shape. (I hope that makes sense)
I ran some tests and I have an average frame delay of about 4-5 milliseconds without collision detection, and then when I make a single call to my canvas context's .getImageData() method, suddenly that frame delay shoots up to 19-20 milliseconds...
As far as I can find online getImageData() is the only means of checking the color of a given pixel, but I have to think there's some other way that doesn't introduce such a huge amount of lag.
I tried running getImageData() on a small section of the screen vs larger sections, and a 1x1 pixel request introduces 10ms latency, where a 600x600 pixel request is about 15ms... So the issue isn't the amount/size of the request, but rather just the request itself is extremely slow, so there's no potential for optimization here, I NEED another way.
Also, caching the image data is also not an option. I need to poll these pixels every single frame, I can't cache it (because the player and the object it needs to collide with are all constantly moving, and they're being controlled over the internet so there's also no way of predicting where they'll be at any given time... I NEED to poll every frame with no exceptions)
To be clear, I'm not asking how to write collisions or how to make pixel-perfect collision detection systems... I'm asking ONLY how to get the color of a pixel on the canvas without having to use .toImageData() because .toImageData() is far too slow for my use case.
Standard collision detection may end up being a better option. Pixel-perfect checking works well for perfect collision detection with complex objects but can get expensive with lots of pixels.
What I would probably recommend instead is to use standard collision detection with simple shapes. If done right, it should have good performance, even for a more complex game.
If you really do want to use pixel-perfect collision, you'll want to check the rectangular bounding boxes of the two objects first to ensure they aren't far away from each other. If their bounding boxes intersect, you could then use bitmasks of your sprites to quickly check each pixel for overlap. This question has a bit more info. It's not JS, but the concept would be the same.

Gameloop wont match css animation

(I'm trying to make a basic space invaders game)I'm very new to coding so this might be a total mess, however, I'm using gameloop(s). One of them is moving the hero, one is moving the missiles, and drawing them. However, I can't seem to split them. I want to split them up so I can change the setTimeout, but it doesn't want to split up.
Tried to split them up by making a gameloop_1();, but that wouldn't move the missiles... sooo... I'm lost
var missiles = [];
function drawMissiles() {
document.getElementById('missiles').innerHTML = ""
for (var i = 0; i < missiles.length; i++) {
document.getElementById('missiles').innerHTML += `<div
class='missile1' style='left:${missiles[i].left}px;
top:${missiles[i].top}px'></div>`;
}
}
function moveMissiles() {
for (var i = 0; i < missiles.length; i++) {
missiles[i].top = missiles[i].top - 15
}
}
function gameLoop() {
setTimeout(gameLoop, 950)
moveMissiles();
drawMissiles();
moveFiende();
drawFiende();
collisionDetection();
}
function gameLoop_1() {
setInterval(gameLoop_1, 100)
moveMissiles();
drawMissiles();
}
gameLoop();
gameLoop_1();
What happens when I split them up, is as I said; The missiles won't then shoot.
Disclaimer:
If you expect an answer for your specific code here, you'll be disappointed, so if you do not intend to start from scratch or something like that, I'm sorry for the whole text.
I think you're misundertanding the purpose of a Game Loop.
There are some things wrong with your code, but I'll stick with the basis of your question, which also happen to be the basis of every game, the Game Loop.
Frames
Let's start with Frames. You probably know what a Frame is. It is like a drawing on the screen of a single moment in time. For example, when you watch a movie, you're actually watching a lot of pictures (frames) being drawn very fast, one after another, which gives the illusion of movement (usually 30 "pictures" per second).
The Game Loop
The Game Loop is the responsible for drawing the frames of the game, one after another, it executes functions, methods, changes variables and based on the results, it finally draws on the screen what is happening on the game.
The Game loop is basically what makes everything in the game possible to happen, it's like what time is for us in real life: without time passing, there would be no past, present or future, we would be stuck in a single frame forever.
The same happens on a game, withou a Game Loop, there would be no iterations, nor changes or frame updates.
Extremely basic game structure
Knowing that, the first thing you must do is always having a "central" game loop, and never split it. Always name other methods as "updates" and call them from the master game loop.
So a most correct structure for your code would be (this is just pseudocode similar to js, not any language in particular):
SpaceInvaders = {
GameLoop(){
UpdatePlayer();
UpdateProjectile();
UpdateEnemies();
setTimeout(GameLoop, 16) // to call the loop each 16ms, thus giving you 60fps
}
UpdatePlayer(){
// do stuff like move the player and fire
}
UpdateProjectile(){
// do stuff like move the projectile and check if it hitted something
}
UpdateEnemies(){
// do stuff like move the enemies
}
}
SpaceInvaders.GameLoop();
If you want to have a more in depth look on Game Loops, please check this amazing article, it helped me a lot when I was starting my first HTML5 canvas game:
https://isaacsukin.com/news/2015/01/detailed-explanation-javascript-game-loops-and-timing
You'll be presented with the hardware compensation problem that comes as consequence of the Game Loop and some other interesting stuff.
Your situation
Now that the Game Loop principle is better explained, let's talk about your specific situation a little more. There are 3 main things that pop into my eye:
First of all, I don't think that it is going to be a good experience to try making a game using only HTML concatenation and CSS, if you really want to do it from scratch all by yourself, I hardly encourage you to try making a game using HTML5 canvas. For me it was a great experience to learn the very basics of game development. As I look back now, tho, I see how my code sucks (used vanilla js, and compensated the hardware by hand on each frame in all moving entities). If you want to see this project done:
https://github.com/diguifi/littlejsworld
Second, you're not working with objects, that's going to make the code very messy in later stages of development and very hard to work with (even if it's just space invaders). What I mean by objectifying your game is, for example: instead of loose methods for "drawMissiles" and "moveMissiles" you should have an object called "Missile" and another called "Player".
The Missiles would have such methods ("draw" and "move", accesible via "Missile.draw()") and the Player would have a list of Missiles (this is just an example, you must use objects for everything in your game, in order to have a well designed project).
And third, seems to me that concatenating divs on innerHtml of an element from the DOM is not a good thing to do, it's very costly, ugly and time consuming.
My suggestions
If you want to make this game as an exercice for learning the basis of game development, I think you should start from scratch using only a canvas element and center your code efforts on the javascript, not messing with html elements to create the game. There are many articles and docs to help you in this task, such as the one I mentioned that talks about game loop.
If your goal is just to make a game, without having to understand basic problems, you should try a game engine, such as:
Godot: a free, easy and lightweight engine to create games, no need to worry about game loop here.
Phaser: code with js, without worrying about game loop management and many other basic stuff.
Unity: code in C# but not so much code is needed
Game Maker Studio: no coding needed, only basic programming logic
Construct 3: same as Game Maker, maybe easier

Two different loops for calculation and drawing

So i have been looking into ways to improve motion smoothness and performance on my canvas animations.
And a quick google on some game development forum suggests using two different loops one for calculating and one for drawing... the example code was shown like this:
function mainLoop() {
// all of your logic goes here, moving positions, checking inputs, etc...
// I guess it's proper to put all drawing to "off screen" canvases here too.
...
setTimeout(mainLoop, 1000 / desiredFPS);
}
function drawingLoop() {
//only drawing commands to the visible canvas goes here
...
requestAnimationFrame(drawingLoop);
}
//when your assets are loaded you just simply make a
//call to each loop to get them started...
mainLoop();
drawingLoop();
Is this actually the best way to do it ?
I currently have all my logic and draw in the same loop. I presumed two different loops would actually slow the frame rate down aswell as the smoothness of it all ?
My current approach is more like this:
function loop() {
calculate(); // calculate everything
draw(); // draw everything
requestAnimationFrame(loop);
}
loop();
Why is it suggested to do two different loops are there any pros and cons doing it that way?
If you want to improve performance and decouple calculations from the actual rendering, you should have a look at WebWorkers on MDN or HTML5 rocks. They are basically sorts of threads, which let you offload some work from the main thread.
But when it comes to perf, you should probably have a look at the perf tools in Chrome, which might help you debunk other non-trivial parts of your code. Have a look at the doc for the CPU profiling

Improving Html5 canvas with double buffering?

I'm writing a game in javascript canvas, and I experience lag sometimes. I think this is because I draw pretty much to the canvas.
My background for example isn't static, it moves from the right to the left, so each time, I have to clear the entire canvas. And I'm not clearing with just a color, I'm clearing with an image that moves every cycle of the gameloop.
I think this is an expensive operation and I was thinking if there is something to make this operation less expensive, like for example using double buffering for clearing the background (no idea if you can use double buffering just for clearing the background?).
Or should I use double buffering in the entire game?
(I don't think so because I've read that the browser already does that for you)
Since you experience your lag 'sometimes', and it's not a low frame rate issue, i would rather move my eyes towards the evil garbage collector : whenever it triggers, the application will freeze for up to a few milliseconds, and you'll get a frame miss.
To watch for this, you can use google profiling tools, in TimeLine / Memory / press the record button : you can see that a GC occurred when there's a sudden drop in the memory used.
Beware when using this tool, that it slows down a bit the app, and it creates garbage on its own (!)...
Also, since any function call for example creates a bit of garbage, so you can't have a full flat memory line.
Below i show the diagram of a simple game i made before i optimized memory use,
there's up to 5 GC per second :
And here the diagram of the very same game after memory optimisation : there's something like 1 GC per second. Because of the limitations i mentioned above, it is in fact the best we can get, and the game suffers no frame drop and feels more responsive.
To avoid creating garbage
- never create object, arrays or functions.
- never grow or reduce an array size.
- watch out for hidden object creation : Function.bind or Array.splice are two examples.
You can also :
- Pool (recycle) your objects
- use a particle engine to handle objects that are numerous / short lived.
I made a pooling lib (here) and a particle engine (here) you might use if you're interested.
But maybe first thing to do is to seek where you create objects, and have them created only once. Since js is single-threaded, you can in fact use static objects for quite a few things without any risk.
Just one small expl :
function complicatedComputation(parameters) {
// ...
var x=parameters.x, y = parameters.y, ... ...
}
// you can call creating an object each time :
var res = complicatedComputation ( { x : this.x, y : this.y, ... ... } );
//... or for instance define once a parameter object :
complicatedComputation.parameters = { x :0, y:0, ... ... };
// then when you want to call :
var params = complicatedComputation.parameters;
params.x = this.x ; params.y = this.y; ... ...
var res = complicatedComputation(params);
It has the drawback that previous calls parameters remains, so you don't get undefined if you don't
set them, but previous value, so you might have to change bit your function.
But on the other hand, if you call several times the function with similar parameters, it comes very handy.
Happy memory hunting !
Double buffering is a technique to reduce flickering: You draw to one buffer while another buffer is displayed, and then swap them out in a single operation, so the user doesn't see any of the drawing in a partial state.
However, it does not really help for performance problems at all, as there is still the same amount of drawing. I would not try to use double buffering if you don't have a problem with flickering, as it requires more memory and flickering may be implicitly prevented by the system by similar or other means.
If you think drawing the background is too expensive, there are several things that you could look into:
Do you scale the background image down while drawing? If so, create a downscaled version once and use this for clearing the background, reducing the drawing costs per iteration.
Remember the "dirty" areas and just draw the portions of the background that were obscured. This will add some management overhead but reduce the number of pixels that need to be touched significantly
Make the background the background image of the canvas DOM element and just clear the canvas to transparency in each iteration. If the canvas is very large, you could make this faster by remembering the "dirty" areas and just clearing them.
Are you sure painting the background is the main cause of lag? Do you still have lag when you only repaint the background and do not do much else?

HTML5 Canvas Collision Detection "globalCompositeOperation" performance

Morning,
Over the past few months I have been tinkering with the HTML5 Canvas API and have had quite a lot of fun doing so.
I've gradually created a number of small games purely for teaching myself the do's and don'ts of game development. I am at a point where I am able to carry out basic collision detection, i.e. collisions between circles and platforms (fairly simple for most out there but it felt like quite an achievement when you first get it working, and even better when you understand what is actually going on). I know pixel collision detection is not for every game purely because in many scenarios you can achieve good enough results using the methods discussed above and this method is obviously quite expensive on resources.
But I just had a brainwave (It is more than likely somebody else has thought of this and I am way down the field but I've googled it and found nothing)....so here goes....
Would it possible to use/harness the "globalCompositeOperation" feature of canvas. My initial thoughts were to set its method to "xor" and then check the all pixels on the canvas for transparency, if a pixel is found there must be a collision. Right? Obviously at this point you need to work out which objects the pixel in question is occupied by and how to react but you would have to do this for other other techniques.
Saying that is the canvas already doing this collision detection behind the scenes in order to work out when shapes are overlapping? Would it be possible to extend upon this?
Any ideas?
Gary
The canvas doesn't do this automatically (probably b/c it is still in its infancy). easeljs takes this approach for mouse enter/leave events, and it is extremely inefficient. I am using an algorithmic approach to determining bounds. I then use that to see if the mouse is inside or outside of the shape. In theory, to implement hit detection this way, all you have to do is take all the points of both shapes, and see if they are ever in the other shape. If you want to see some of my code, just let me know
However, I will say that, although your way is very inefficient, it is globally applicable to any shape.
I made a demo on codepen which does the collision detection using an off screen canvas with globalCompositeOperation set to xor as you mentioned. The code is short and simple, and should have OK performance with smallish "collision canvases".
http://codepen.io/sakri/pen/nIiBq
if you are using a Xor mode fullscreen ,the second step is to getImageData of the screen, which is a high cost step, and next step is to find out which objects were involved in the collision.
No need to benchmark : it will be too slow.
I'd suggest rather you use the 'classical' bounding box test, then a test on the inner BBOxes of objects, and only after you'd go for pixels, locally.
By inner bounding box, i mean a rectangle for which you're sure to be completely inside your object, the reddish part in this example :
So use this mixed strategy :
- do a test on the bounding boxes of your objects.
- if there's a collision in between 2 BBoxes, perform an inner bounding box test : we are sure there's a collision if the sprite's inner bboxes overlaps.
- then you keep the pixel-perfect test only for the really problematic cases, and you need only to draw both sprites on a temporary canvas that has the size of the bigger sprite. You'll be able to perform a much much faster getImageData. At this step, you know which objects are involved in the collision.
Notice that you can draw the sprites with a scale, on a smaller canvas, to get faster getImageData at the cost of a lower resolution.
Be sure to disable smoothing, and i think that already a 8X8 canvas should be enough (it depends on average sprite speed in fact. If your sprites are slow, increase the resolution).
That way the data is 8 X 8 X 4 = 256 bytes big and you can keep a good frame-rate.
Rq also that, when deciding how you compute the inner BBox, you can allow a given number of empty pixels to get into that inner BBox, trading accuracy for speed.

Categories