I placed 1000 players on canvas but their movement is not smooth. Is there a way for canvas to handle that much players? What options do I have?
Position of players is sent 20 times per second using socket.IO. When event reach client it draws players on canvas.
var images = {};
images.spriteSheet = new Image();
images.spriteSheet.src = "/images/spriteSheet.png";
socket.on('drawing', function(data){
ctx.clearRect(0,0,1000,500);
for(var i = 0; i < data.length; i++){
ctx.drawImage(images['spriteSheet'], data[i].sx, data[i].sy, 100, 100, data[i].x, data[i].y, 70, 70);
}
});
I placed 1000 players on canvas but their movement is not smooth. Is there a way for canvas to handle that much players?
I assume you mean you're drawing 1,000 sprites to a canvas 20x per second. No doubt, that's going to be tough.
Position of players is sent 20 times in a second using socket.IO.
Surely you can send a position and vector instead, and let the client update per-frame locally, predictively, until the next real update. That's how pretty much all other games work, and for a reason. This idea that you can even get data 20 times a second isn't always going to be the case.
Without more details it's hard to give a specific answer, but in the general case I'd suggest looking into SVG. Then, you can update individual elements and let the browser worry about compositing and what not, which is likely going to be faster than what you can pull off in the canvas. You'll have to experiment though for your specific use case.
Related
I have a total of 1 to 64 blobs and they all move to my mouse position. But i want them to not go into each other, in other words circle collision detection. However i can't seem to make it smooth and also push new objects after they move for the first time?
Tried checking each blob for collision with other blobs. If collosion is true, then set the distance between the blobs to their accumulated radiuses.
This is how i wrote the colliding function, but this way of doing it makes the resetting of positions too fast. I want it to be a smooth, but fast transition. Like instead of now 1 frame, lets say 10 frames. And another problem is when two objects' are distanced to their radiuses, they might collide into new ones and that will cause this code to run again, and then all blobs go crazy.
this.collide = function() {
var length = this.blobs.length; // How many blobs?
this.blobs.forEach(function(item, index) {
for (var i = 0; i < length; i++) {
// Get absolute distance between two vectors
var v0 = vectorFromTo(blob.blobs[i].pos.x, blob.blobs[i].pos.y, //[x2, y2]
item.pos.x, item.pos.y); //[x1, y1]
// if colliding, set distance between to their accumulated radiuses
if (magnitude(v0) < blob.blobs[i].r + item.r) {
item.pos.add(v0.setMag(magnitude(v0) - (blob.blobs[i].r + item.r)));
}
}
});
}
I haven't tried to code another way of doing this yet because i haven't learned about vectors in school, but i do understand them quite a bit. But what i think would work is if i checked for collision, and if they collide they go opposite directions 50% of the deficit distance, and then they check if they hit new blobs. But this would require physics right? Cause then it would have to do something with the mass and speed of the blob as well to know whats gonna happen to the new blob it crashes into?
EDIT:
This is what im looking for: https://youtu.be/QvlhRGtlcsw
This is what it currently looks like: https://youtu.be/QEpHnCgomqY
I'm displaying a live video stream in a HTML5 canvas which works fine.
Now, what I need to do is to check for any "motion" in the camera which is being displayed in the HTML5 Canvas.
During my research, I found out that this can be done by checking the previous frame with the current frame that's being displayed in the canvas.
So I tried this code within a setInterval function:
var c = document.querySelector('.mycanv');
var ctx = c.getContext("2d");
var imageData = ctx.getImageData(0, 0, 200, 200);
var data = imageData.data.length;
console.log(data);
However, when I look in the console, the Number that the variable data outputs is always the same which is 16000! and it wont change even if there is a movemnet infront of the camera.
I'm not entirely sure if I am on a right track.
Could someone please advice on this issue and point me in a right direction please?
Thanks in advance.
Here is a minified and simple example:
https://jsfiddle.net/2648xwgz/
Basically, the code draws a frame of the video in the canvas.
Now, what I need to do is to check if the previous frame/image in the canvas is the same as the current frame/image.
Second EDIT:
Okay, so I've taken all the advice in the comments on board and tried to come up with something that actually doesn't check for all the random pixels in the images as that is heavy and not a good practic...
So, I tried something like this:
https://jsfiddle.net/qpxjcv3a/6/
The above code will run fine in Firefox and with a local Video file. but on JSFIDDLE, you will get a cross origion error.
Anyway, The key point in the code above is using the ctx.globalCompositeOperation = 'difference'; i guess.
And then doing a "scrore" calculation taken from here to detect some changes.
However, when I run my code, I always get: console.log('we have motion'); in the console! Even when the video is paused and there is no more new frames.
So I did console.log(imageScore); and it is always adding up by 10000 even when the video is paused or ended! I'm not sure why that is and whether this calculation is correct at all. But that is where I am at the moment.
Any pointers and help appreciated.
As Daniel said you need to check the pixels, the legnth would be the same for all iterations.
You should look into image hashing algorithms. At every interval you can calculate a hash, and store that in a global variable to compare at the next interval. This would also give you the option to set a threshold, so minor changes would not trigger motion detection.
This page explains image hashing in more details: https://jenssegers.com/61/perceptual-image-hashes
You can start by implementing average hash. It is quite simple. You would reduce your canvas size to 8x8 pixels.
ctx.drawImage(video, 0, 0, video.width, video.height, 0, 0, 8, 8);
var imageData = ctx.getImageData(0, 0, 8, 8);
var data = imageData.data;
Then you iterate over the image data and calculate the brightness.
var brightnessdata = []
for(var i = 0; i<data.length; i+=4){
brightnessdata.push((data[i]+data[i+1]+data[i+2])/3);
}
The rest is simply calculating the average brightness and comparing each pixel brightness to the average brightness to calculate the hash.
So I want to make a small JavaScript game.
And in order to do that I need to animate a few things.
I went researching and found about setInterval and requestAnimationFrame.
I can use either of those 2 for making my game work, however I understood that requestAnimationFrame is the better alternative there.
The problem I see with this is that while the function has its benefits , you are unable to set a framerate , or an update rate for it easily.
I found another thread that explained a way of making this work however it seemed somewhat complicated.
Controlling fps with requestAnimationFrame?
Is there an easier way of animating with a set framerate ?
Is there an easier way of animating with a set framerate ?
Simply put: no. Since rendering is one of the most computation heavy process for a browser, which can be triggered in various ways, it is not possible to foretell how long an update will take, since it can range from drawing one circle on a canvas up to a complete replace of all the visible content of the page.
To overcome this, browser offer a way to call a function a often as possible and the developer is responsible to make his animation sensitive to different time deltas/time steps.
One way to tackle that is to use the concept of velocity. velocity = distance / time. If you want an asset to have a constant velocity you can do the following, since distance = velocity * time follows:
var
asset_velocity = 1; //pixel per millisecond
last = new Date().getTime()
;
(function loop () {
var
now = new Date().getTime(),
delta = now - last,
distance = asset_velocity * delta
;
//update the asset
last = now;
window.requestAnimationFrame(loop)
})();
I've got an issue with an experiment I'm working on.
My plan is to have a beautiful and shining stars Background on a whole page.
Using that wondeful tutorial (http://timothypoon.com/blog/2011/01/19/html5-canvas-particle-animation/) I managed to get the perfect background.
I use a static canvas to display static stars and an animated canvas for the shining ones.
The fact is it's very memory hungry! On chrome and opera it runs quite smoothly, but on firefox IE or tablet, it was a total mess 1s to render each frame etc... It is worse on pages where HEIGHT is huge.
So i went into some optimisations:
-Using a buffer canvas, the problem was createRadialGradient which was called 1500 times each frame
-Using a big buffer canvas, and 1 canvas for each stars with an only call to createRadialGradient at init.
-Remove that buffer canvas and drawing every stars canvas to the main one
That last optimisation was the best i could achieve so i wrote a fiddle displaying how is the code right now.
//Buffering the star image
this.scanvas = document.createElement('canvas');
this.scanvas.width=2*this.r;
this.scanvas.height=2*this.r;
this.scon=this.scanvas.getContext('2d');
g = this.scon.createRadialGradient(this.r,this.r,0,this.r,this.r,this.r);
g.addColorStop(0.0, 'rgba(255,255,255,0.9)');
g.addColorStop(this.stop, 'rgba('+this.color.r+','+this.color.g+','+this.color.b+','+this.stop+')');
g.addColorStop(1.0, 'rgba('+this.color.r+','+this.color.g+','+this.color.b+',0)');
this.scon.fillStyle = g;
this.scon.fillRect(0,0,2*this.r,2*this.r);
That's the point where I need you:
-A way to adjust the number of shining stars according to the user perfomance
-Optimisation tips
Thanks in advance to everyone minding to help me and I apologize if I made grammar mistakes, my english isn't perfect.
EDIT
Thanks for your feedbacks,
Let me explains the whole process,
Every stars has it's own different gradient and size, that's why I stored it into a personal canvas, the shining effect is only done by scaling that canvas on the main one with drawImage.
I think the best would be to prerender 50 or 100 different stars in a buffer canvas then picking and drawing a random one, don't you think?
EDIT2
Updated fiddle according to Warlock great advises, one prerendered star, scaled to match the current size. The stars are less pretty, but the whole thing runs a lot smoother.
EDIT3
Updated fiddle to use a sprite sheet. Gorgeous!!!!
//generate the star strip
var len=(ttlm/rint)|0;
scanvas = document.createElement('canvas');
scanvas.width=len*2*r;
scanvas.height=2*r;
scon=scanvas.getContext('2d');
for(var i=0;i<len;i++){
var newo = (i/len);
var cr = r*newo;
g = scon.createRadialGradient(2*r*i+r,r,0,2*r*i+r,r,(cr <= 2 ? 2 : cr));
g.addColorStop(0.0, 'rgba(200,220,255,'+newo+')');
g.addColorStop(0.2, 'rgba(200,220,255,'+(newo*.7)+')');
g.addColorStop(0.4, 'rgba(150,170,205,'+(newo*.2)+')');
g.addColorStop(0.7, 'rgba(150,170,205,0)');
scon.fillStyle = g;
scon.fillRect(2*r*i,0,2*r,2*r);
}
EDIT 4(Final)
Dynamic stars creations
function draw() {
frameTime.push(Date.now());
con.clearRect(0,0,WIDTH,HEIGHT);
for(var i = 0, len = pxs.length; i < len; i++) {
pxs[i].fade();
pxs[i].draw();
}
requestAnimationFrame(draw);
if(allowMore === true && frameTime.length == monitoredFrame)
{
if(getAvgTime()<threshold && pxs.length<totalStars )
{
addStars();
}
else
{
allowMore=false;
static=true;
fillpxs(totalStars-pxs.length,pxss);
drawstatic();
static=false;
}
}
}
Here is the updated and final fiddle, with spritesheet, dynamic stars creation and several optimisations. If you see anything else i should update don't hesitate.
POST EDIT Reenabled shooting stars/Prototyped object/got rid of Jquery
http://jsfiddle.net/macintox/K8YTu/32/
Thanks everyone who helped me, that was really kind and instructive, and I hope it will help somebody sometimes.
Aesdotjs.
PS: I'm so happy. After testing, that script run smoothly on every browser even IE9. Yatta!!
Adopting to browser performance
To measure capability of the user's setup you can implement a dynamic star creator which stops at a certain threshold.
For example, in your code you define a minimum number of stars to draw. Then in your main loop you measure the time and if the time spent drawing the stars are less than your max threshold you add 10 more stars (I'm just throwing out a number here).
Not many are aware of that requestAnimationFrame gives an argument (DOMHighResTimeStamp) to the function it calls with time in milliseconds spent since last request. This will help you keep track of load and as we know that 60 fps is about 16.7 ms per frame we can set a threshold a little under this to be optimal and still allow some overhead for other browser stuff.
A code could look like this:
var minCount = 100, /// minimum number of stars
batchCount = 10, /// stars to add each frame
threshold= 14, /// milliseconds for each frame used
allowMore = true; /// keep adding
/// generate initial stars
generateStarts(minCount);
/// timeUsed contains the time in ms since last requestAnimationFrame was called
function loop(timeUsed) {
if (allowMore === true && timeUsed < threshold) {
addMoreStars(batchNumber);
} else {
allowMore = false;
}
/// render stars
requestAnimationFrame(loop);
}
Just note that this is a bit simplified. You will need to run a few rounds first and measure the average to have this work better as you can and will get peak when you add stars (and due to other browser operations).
So add stars, measure a few rounds, if average is below threshold add stars and repeat.
Optimizations
Sprite-sheets
As to optimizations sprite-sheets are the way to go. And they don't have to just be the stars (I'll try to explain below).
The gradient and arc is the costly part of this applications. Even when pre-rendering a single star there is cost in resizing so many stars due to interpolation etc.
When there becomes a lot of costly operations it is better to do a compromise with memory usage and pre-render everything you can.
For example: render the various sizes by first rendering a big star using gradient and arc.
Use that star to draw the other sizes as a strip of stars with the same cell size.
Now, draw only half of the number of stars using the sprite-sheet and draw clipped parts of the sprite-sheet (and not re-sized). Then rotate the canvas 90 degrees and draw the canvas itself on top of itself in a different position (the canvas becoming a big "sprite-sheet" in itself).
Rotating 90 degrees is not so performance hungry as other degrees (0, 90, 180, 270 are optimized). This will give you the illusion of having the actual amount of stars and since it's rotated we are not able to detect a repetitive pattern that easy.
A single drawImage operation of canvas is faster than many small draw operations of all the stars.
(and of course, you can do this many times instead of just once up to a point right before where you start see patterns - there is no key answer to how many, what size etc. so to find the right balance is always an experiment).
Integer numbers
Other optimizations can be using only integer positions and sizes. When you use float numbers sub-pixeling is activated which is costly as the browser need to calculate anti-alias for the offset pixels.
Using integer values can help as sub-pixeling isn't needed (but this doesn't mean the image won't be interpolated if not 1:1 dimension).
Memory bounds
You can also help the underlying low-lowel bitmap handling a tiny bit by using sizes and positions dividable on 4. This has to do with memory copy and low-level clipping. You can always make several sprite-sheet to variate positions within a cell that is dividable on 4.
This trick is more valuable on slower computers (ie. typical consumer spec'ed computers).
Turn off anti-aliasing
Turn off anti-aliasing for images. This will help performance but will give a little more rough result of the stars. To turn off image anti-aliasing do this:
ctx.webkitEnableImageSmoothing = false;
ctx.mozEnableImageSmoothing = false;
ctx.enableImageSmoothing = false;
You will by doing this see a noticeable improvement in performance as long as you use drawImage to render the stars.
Cache everything
Cache everything you can cache, being the star image as well as variables.
When you do this stars.length the browser's parser need to first find stars and then traverse that tree to find length - for each round (this may be optimized in some browsers).
If you first cache this to a variable var len = stars.length the browser only need to traverse the tree and branch once and in the loop it will only need to look up the local scope to find variable len which is faster.
Resolution reduction
You can also reduce resolution in half, ie. do everything at half the target size. In the final step draw your render enlarged to full size. This will save you initially 75% render area but give you a bit low-res look as a result.
From the professional video world we often use low-resolution when things are animated (primarily moving) as the eye/brain patch up (or can't detect) so much details when objects are moving and therefor isn't so noticeable. If this can help here must be tested - perhaps not since the stars aren't actually moving, but worth a try for the second benefit: increased performance.
How about just creating a spritesheet of a star in its various stages of radial glow.
You could even use canvas to initially create the spritesheet.
Then use context.drawImage(spritesheet,spriteX,spriteY,starWidth,starHeight) to display the star.
Spritesheet images can be drawn to the screen very quickly with very little overhead.
You might further optimize by breaking the spritesheet into individual star images.
Good luck on your project :)
1. Minimize operations, related to the DOM;
In the LINE 93 you are creating canvas:
this.scanvas = document.createElement('canvas');
You need only one canvas instead of this. Move canvas creation to the initialization step.
2. Use integer coordinates for canvas;
3. Use Object Pool design pattern to improve performance.
4. In for loops cache the length variable:
for(var i = 0; i < pxs.length; i++) {...
}
Better:
for(var i = 0, len = pxs.length; i < len; i++) {
...
}
Note: don't mix jquery with native js.
Leading up from this question Detecting mouse coordinates with precision, I have learnt quite a bit in the past few days. Here are what I picked as best learning resources on this topic:
http://gamedev.tutsplus.com/tutorials/implementation/quick-tip-use-quadtrees-to-detect-likely-collisions-in-2d-space/
http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/quadtrees-r1303
http://jsfiddle.net/2dchA/2/
The code in (3) works in JSFiddle but breaks at this section in my testing environment (VS2012):
var myTree = new Quadtree({
x: 0,
y: 0,
width: 400,
height: 300
});
with the message Quadtree is undefined in IE. FF & Chrome just gloss over it and display an empty page. I couldn't sort it out. Question 1: Can someone help out with that?
My main question:
I have a region (parcels of land like a map) with about 1500 parcels drawn in html5, not jpg or png images. It is a lot of lines of code to complete that but the rendering is great, so I am keeping it that way. I intend to have a mouseover event tell me which parcel I am standing on when the mouse stops. As you will see in the previous question referred my previous attempts were not impressive. Based on the learning I have been doing, and thanks to Ken J's answer/comments, I would like to go with this new approach of slicing up my canvas into say 15 quads of 100 objects each. However, I would like some guidance before I take another wild dive the wrong way.
Question 2: Should I slice it up at creation or should the slicing happen when the mouse is over a region, ie, trail the mouse? The latter sounds better to me but I think I can do with some advice and, if possible, some start out code. The quadtree concept is completely new to me. Thanks.
Can't help with question 1.
You should definitely build the tree as early as possible, given that the objective is to get the page to respond as quick as possible once the user clicks somewhere.
Keep the tree for as long as the user interacts with the 2d area. Updating a quad tree shouldn't be too hard, so even if the area changes contents, you should be able to reuse the existing tree (just update it).
Given the fact that your draw area is well know i see no advantage in a QuadTree over a spacial hash function. This function will give you an integer out of an (x,y) point.
var blocWidth = 20;
var blocHeight = 20;
var blocsPerLine = ( 0 | ( worldWidth / blocWidth) ) + 1 ;
function hashPoint(x,y) {
return ( 0 | (x/blocWidth)) + blocsPerLine*(0|(y/blocHeight));
}
once you built that, hash all your parcels within an array :
parcelHash = [];
function addHash(i,p) {
if (!parcelHash[i]) { parcelHash[i]=[ p ]; return; }
if (parcelHash[i].indexOf(p) != -1 ) return;
parcelHash[i].push(p);
}
function hashParcel (p) {
var thisHash = hashPoint(p.x,p.y); // upper left
addHash( thisHash, p);
thisHash = hashPoint(p.x+width, p.y); // upper right
addHash(thisHash, p);
thisHash = hashPoint(p.x, p.y+p.height); // lower left
addHash(thisHash, p);
thisHash = hashPoint(p.x+width, p.y+p.height); // lower right
addHash(thisHash, p);
};
for (var i=0; i<allParcels.length; i++) { hashParcel(allParcels[i]) };
now if you have a mouse position, you can retrieve all the parcels in the
same block with :
function getParcels(x,y) {
var thisHash = hashPoint(x,y);
return parcelHash[thisHash];
}
I'll just give you few tips in addition to what others have said.
... have a mouseover event tell me which parcel I am standing on ...
From your other messages I conclude that parcels will have irregular shapes. Quadtrees in general work with rectangles, so you'd have to calculate the bounding rectangle around the shape of the parcel and insert that rectangle in the quadtree. Then are when you want to determine whether mouse is over a parcel, you'll query the quadtree which will give you a set of parcels that might be under the mouse, but you'll have to then do a more precise check on your own to see if it indeed is.
... when the mouse stops.
From your other questions I saw that you try to detect when the mouse has "stopped". Maybe you should look at it this way: mouse cursor is never moving, it's teleporting around the screen from previous point to next. It's always stopped, never moving. This might seem a bit philosophical, but it'll keep your code simpler. You should definitely be able to achieve what you intended without any setTimeout checks.
... slicing up my canvas into say 15 quads of 100 objects each.
... Should I slice it up at creation or should the slicing happen when the mouse is over a region
You won't (and can't) do slicing, quadtree implementation does that automatically (that's its purpose) when you insert or remove items from it (note that moving the item is actually removing then re-inserting it).
I didn't look into the implementation of quadtree that you're using, but here are two MX-CIF quadtree implementations in case that one doesn't work out for you:
https://github.com/pdehn/jsQuad
https://github.com/bjornharrtell/jsts/tree/master/src/jsts/index/quadtree
The problem in question 1 probably happens because jsfiddle (http) page is trying access quadtree.js which is on https