I've build my own flip command and, well its slow and takes forever. I would like to know if javascript has a blit or memcpy style command. Right now I'm going through item by item with for loops to do a copy and it takes "forever".
Here is a example use of my flip function. I'm running 3 layers, only 1 if full height, with 3 simple animations and the fps it topped out at about 35 FPS. Ideally 3 layes should be topped out at far far higher FPS, in the 200+ range I would expect.
v:36.8 l0:36.8 l1:57.8 l2:36.8 The layer's FPS are the rendering to their buffers, the v is the rendering to the canvas with the flip function. (These FPS are from Chrome on a mac)
v = the screen update, the main flip function listed below.
l0 = The bottom fire, its a full height layer
l2 = The static noise, its a 1/2 height layer
l3 = The top fire, its a 1/4 height layet
Imagine having 9 or 10 layers, the FPS would drop like a stone. In FF version 12 its already unusable... not even double digit FPS rates. Opera is at least double digets.
v:4.2 l0:4.2 l1:4.2 l2:4.2 (FF 12 OSX)
v:15.5 l0:15.5 l1:15.5 l2:15.5 (Opera latest OSX)
My flip function
flip : function() {
var fps = '';
// Combine the layers onto the back buffer
for (var l = 0; l < this.layers.length; l++)
{
fps += 'l' + l + ':' + this.layers[l].fps.toFixed(1) + ' ';
var layerWidth = this.layers[l].options.width;
var layerHeight = this.layers[l].options.height;
for (var x = 0; x < layerWidth; x++)
{
for (var y = 0; y < layerHeight; y++)
{
var index = (y*this.layers[l].options.width + x)*4;
var r = this.layers[l].buffer[index+0];
var g = this.layers[l].buffer[index+1];
var b = this.layers[l].buffer[index+2];
var a = this.layers[l].buffer[index+3];
if (r|g|b|a != 0) {
this.buffer.data[index+0] = r;
this.buffer.data[index+1] = g;
this.buffer.data[index+2] = b;
this.buffer.data[index+3] = a;
}
}
}
}
fps = 'v:' + this.fps.toFixed(1) + ' ' + fps;
this.$fps.html(fps);
// blit the buffer
this.context.putImageData(this.buffer, 0, 0);
// Calculate fps
var now = new Date;
var thisFrameFPS = 1000 / (now - this.last);
this.fps += (thisFrameFPS - this.fps) / 50;
this.last = now;
var t = this;
setTimeout(function() {t.flip.apply(t);}, this.speed);
}
There is a memcpy.js that uses TypedArray.prototype.subarray() if available.
The browser support is good and even IE10 have subarray.
function memcpy (src, srcOffset, dst, dstOffset, length) {
var i
src = src.subarray || src.slice ? src : src.buffer
dst = dst.subarray || dst.slice ? dst : dst.buffer
src = srcOffset ? src.subarray ?
src.subarray(srcOffset, length && srcOffset + length) :
src.slice(srcOffset, length && srcOffset + length) : src
if (dst.set) {
dst.set(src, dstOffset)
} else {
for (i=0; i<src.length; i++) {
dst[i + dstOffset] = src[i]
}
}
return dst
}
Your code can be improved, but I doubt that the speedup will be significant.
Here's what I came up with, note it is untested. I have assumed that the order of processing the layers is not significant, if it is replace the first for loop with your version.
function flip () {
var fps = '';
// Combine the layers onto the back buffer
for (var l = this.layers.length; l--;) {
fps += 'l' + l + ':' + this.layers[l].fps.toFixed (1) + ' ';
var layerWidth = this.layers[l].options.width;
var layerHeight = this.layers[l].options.height;
for (var index = 0, x = layerWidth; x--;) {
for (var y = layerHeight; y--; index += 4) {
var r = this.layers[l].buffer[index+0];
var g = this.layers[l].buffer[index+1];
var b = this.layers[l].buffer[index+2];
var a = this.layers[l].buffer[index+3];
if (r|g|b|a != 0) {
this.buffer.data[index+0] = r;
this.buffer.data[index+1] = g;
this.buffer.data[index+2] = b;
this.buffer.data[index+3] = a;
}
}
}
}
};
On the assumption that the r,g,b and a are all 8 bit quantiites you could consider packing them into a single int. That would reduce the processing in the inner loop. Even more efficient would be to use the new arrayBuffer facilities
Related
Basically i been trying to create a game using multiple slot machine rollers. I have tried many versions to obtain my goals, and all work perfectly on pc, but as soon as i put them on a mobile device, they are laggy. this is mainly because i was manipulating dom elements, as i found out.
I found a function on the net, i have replicated it and run it in an app and it works perfectly.
Now im trying to write this function into my actual app, with my own variables.
My problem is this:
There is a variable called "NOW", that is passed to the function animate(); I am trying to figure out where it comes from and or how to dynamically create it myself. There is a requestAnimationFrame request in this function and after hours and hours of research, i still cant find anything.
here is a fiddle where the code is located:
https://codepen.io/indamix/pen/lLxcG
var sm = (function(undefined){
var tMax = 3000,
height = 210,
speeds = [],
r = [],
reels = [
['coffee maker', 'teapot', 'espresso machine'],
['coffee filter', 'tea strainer', 'espresso tamper'],
['coffee grounds', 'loose tea', 'ground espresso beans']
],
$reels,
$msg,
start;
function init(){
$reels = $('.reel').each(function(i, el){
el.innerHTML = '<div><p>' + reels[i].join('</p><p>') + '</p></div><div><p>' + reels[i].join('</p><p>') + '</p></div>'
});
$msg = $('.msg');
$('button').click(action);
}
function action(){
if (start !== undefined)
return;
for (var i = 0; i < 3; ++i) {
speeds[i] = Math.random() + .5;
r[i] = (Math.random() * 3 | 0) * height / 3;
}
$msg.html('Spinning...');
animate();
}
function animate(now){
if (!start) start = now;
var t = now - start || 0;
for (var i = 0; i < 3; ++i)
$reels[i].scrollTop = (speeds[i] / tMax / 2 * (tMax - t) * (tMax - t) + r[i]) % height | 0;
if (t < tMax)
requestAnimationFrame(animate);
else {
start = undefined;
check();
}
}
function check(){
$msg.html(
r[0] === r[1] && r[1] === r[2] ?
'You won! Enjoy your ' + reels[1][ (r[0] / 70 + 1) % 3 | 0 ].split(' ')[0] : 'Try again');}
return {init: init}
})();$(sm.init);
I've been at this for a while now, like days. I figured out that the Now variable has something to do with the requestAnimationFrame function to determine where the animation frame ends up,But this is only speculation for me.. I can't see it.
You can use performance.now() as an argument value to your own animate function call
I am implementing a selection algorithm that selects an object based on a probability proportional to its score value. This makes higher-scoring objects more likely to be chosen.
My implementation is as follows:
var pool = [];
for (var i = 0; i < 100; i++)
pool.push({ Id: i, Score: Math.random() * 100 << 0 });
const NUM_RUNS = 100000;
var start = new Date();
for( var i = 0; i < NUM_RUNS; i++ )
rouletteSelection(pool);
var end = new Date();
var runningTime = (end.getTime() - start.getTime()) / 1000;
var avgExecutionTime = ( runningTime / NUM_RUNS ) * Math.pow(10,9);
console.log('Running Time: ' + runningTime + ' seconds');
console.log('Avg. Execution Time: ' + avgExecutionTime + ' nanoseconds');
function rouletteSelection(pool) {
// Sum all scores and normalize by shifting range to minimum of 0
var sumScore = 0, lowestScore = 0;
pool.forEach((obj) => {
sumScore += obj.Score;
if (obj.Score < lowestScore)
lowestScore = obj.Score;
})
sumScore += Math.abs(lowestScore * pool.length);
var rouletteSum = 0;
var random = Math.random() * sumScore << 0;
for (var i in pool) {
rouletteSum += pool[i].Score + lowestScore;
if (random < rouletteSum)
return pool[i];
}
//Failsafe
console.warn('Could not choose roulette winner, selecting random');
return pool[Math.random() * pool.length];
};
When run, the performance isn't bad: on my machine, each call to rouleteSelection() takes about 2500-3200 nanoseconds. However, before being used in production, I want to optimize this and shave off as much overhead as I can, as this function could be potentially called tens of millions of times in extreme cases.
An obvious optimization would be to somehow merge everything into a single loop so the object array is only iterated over once. The problem is, in order to normalize the scores (negative scores are shifted to 0), I need to know the lowest score to begin with.
Does anyone have any idea as to how to get around this?
At the risk of stating the obvious: just don't do the normalisation in every call to rouletteSelection. Do it once, after you constructed the pool.
i was trying to implement the A* algorithm and followed the wikipedia pseudo code to make this.
when i pass a predefined object pixel to the a funtion getG() it says that the object is null. I'm sorry if i am not pointing to a specific problem but i am not even sure how to really specify the problem by name. i have tried commenting out the code to increase readability.
git repository link of the whole project - https://github.com/NirobNabil/WhirlWind
(things are a little messy here because i didn't use github at first and i uploaded it just a little ago for posting the problem)
[ i'm actually making this to use a* to find path for my bot which is powered by arduino. thats why i'm using involt. ]
here goes the code,
$(function() {
// define the height, width and bot size in centemeter
total_width = 200;
total_height = 200;
bot_size = 20;
total_box = (total_height / bot_size) * (total_width / bot_size);
box_in_x = total_width / bot_size;
box_in_y = total_height / bot_size;
//populating the pixels array
populate(total_width / bot_size, total_height / bot_size, "UND");
pathfind(pixels, pixels[13], pixels[pixels.length - 1]);
})
var pixels = []; //an array to hold all the objects(the grid)
var obstacles = []; //array to hold the obstacles
function pixel(X, Y, obs) {
this.X_co_ordinate = X;
this.Y_co_ordinate = Y;
this.state = obs; //availale states OPN, UND, OBS, DIS, NULL
this.g = 0;
this.h = 0;
this.f = 0;
this.last = null;
} //every block in the grid is a pixel
//01719372596
function populate(height, width, obs_val = "UND") {
pixels[0] = new pixel(0, 10, obs_val);
for (h = height, i = 0; h >= 0; h--) {
for (w = 0; w < width; w++, i++) {
var temp_obs = new pixel(w, h, obs_val);
temp_obs.last = pixels[0];
pixels[i] = temp_obs; //saving temp_pixel object to pixels array
}
}
} //populating the grid AKA pixels with pixel objects or blocks
// this funtion is where the problem shows up
function getG(current, start) {
let g = 1;
while (current != start && current.last != start && current) {
current = current.last;
g++;
}
return g;
} //get the g val(cost to come to this pixel from the start) of the current pixel
function getH(current, end) {
let I = Math.abs(current.X_co_ordinate - end.X_co_ordinate) + Math.abs(current.Y_co_ordinate - end.Y_co_ordinate);
return I;
} //get the h val(heuristic) of the current pixel
function getF(start, current, end) {
let G = getG(current, start);
let H = getH(current, end);
return G + H;
} //get the f val(total) of the current pixel
function lowFinArray(arr, start, end) {
// here arr is the grid/pixel
let current_low = arr[0];
for (let i = 0; i < arr.length; i++) {
let getF1 = getF(start, current_low, end);
let getF2 = getF(start, arr[i], end);
if (getF1 < getF2) {
current_low = arr[i];
}
}
console.log("current low");
console.log(current_low);
return current_low;
}
function getneighbours(grid, current) {
let neightbours = [];
neightbours.push(grid[getIndex(current.X_co_ordinate - 1, current.Y_co_ordinate)]);
neightbours.push(grid[getIndex(current.X_co_ordinate + 1, current.Y_co_ordinate)]);
neightbours.push(grid[getIndex(current.X_co_ordinate, current.Y_co_ordinate - 1)]);
neightbours.push(grid[getIndex(current.X_co_ordinate, current.Y_co_ordinate + 1)]);
/*
for(i=0; i<neightbours.length; i++){
neightbours[i].last = current;
}*/
console.log("neightbours");
console.log(neightbours);
return neightbours;
} //get the neighbour pixels of the current pixel
//main algo
function pathfind(grid, start, end) {
let closedSet = [];
let openSet = [];
openSet.push(start);
let current = start;
//trying to debug
console.log("low F in arr");
console.log(lowFinArray(grid, start, end));
console.log(start);
console.log(current);
console.log(end);
console.log(grid);
let x = 0;
while (openSet.length > 0) {
//trying to debug
console.log("executed " + (x++));
console.log("openset");
console.log(openSet);
current = lowFinArray(grid, start, end); //assigning the pixel with lowest f val to current
console.log("current");
console.log(current);
if (current == end) {
console.log(getPath(current));
}
let neighbours = getneighbours(grid, current);
for (let i = 0; i < neighbours.length; i++) {
let neighbour = neighbours[i];
if (closedSet.includes(neighbour)) {
continue;
}
if (!openSet.includes(neighbours)) {
openSet.push(neighbours);
}
//console.log(current);
let getg = getG(current, start);
let geth = getH(current, end);
//console.log(getg);
let tGscore = getg + geth; //here getH is being used as a distance funtion
if (tGscore >= getg) {
continue;
}
neighbour.last = current;
neighbour.g = tGscore;
neighbour.f = getF(neighbour);
}
if (x > 10) {
return 0;
}; //the loop was running forever so i tried this to stop the loop after 10 iterations
}
}
function getPath(current) {
let path = [current];
while (current.last != null) {
path.push(current.last);
}
return path;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
and here is what the console says,
Uncaught TypeError: Cannot read property 'last' of null
at getG (app.js:226)
at getF (app.js:241)
at lowFinArray (app.js:249)
at pathfind (app.js:292)
at HTMLDocument.<anonymous> (app.js:92)
at mightThrow (jquery-3.1.1.js:3570)
at process (jquery-3.1.1.js:3638)
You're doing your checks in the wrong order:
while (current != start && current.last != start && current) {
There's no point in using && current after you've already used current.last.
Perhaps changing the order would solve the problem. It will at least get rid of your current error:
while (current && current != start && current.last != start) {
Regarding the title of this question:
In javascript, after i pass a non null object to a funtion it says the object is null
It may very well be non-null 100% of the time you pass it into the function, but you are repeatedly overwriting its parameter within the function, so all bets are off.
I'm having an issue where bitmaps seem to be loading after they are created and added to the stage. You can see all this code working right here. You can look at the console log for the log statements I'm getting (also shown below).
I create the bitmaps in a function here (I know this code is working properly -- you don't have to read this but note how game.update is set to true at the end).
for (i = -r; i <= r; i++){
var min = Math.max(-r, -r - i);
var max = Math.min(r, r - i);
for (j = min; j <= max; j++){
k = -1 * (i + j);
//var a = length)Math.floor(Math.random() * t_imgs.;
//var a = mountain;
var a;
if(game.grid[i][j].height == null){
a = mountain;
} else if (game.grid[i][j].height > .5){
a = plain;
} else{
a = water;
}
var b = Math.floor(Math.random() * (game.t_imgs[a].length ));
if(game.t_imgs[a][b] == null){
console.log("NULL:", a, b);
}
bitmap = new createjs.Bitmap(game.t_imgs[a][b]);
container.addChild(bitmap);
bitmap.regX = game.hexwidth/2;
bitmap.regY = game.hexheight/2;
bitmap.x = game.xcenter + game.hexwidth * .75 * i;
bitmap.y = game.ycenter + game.hexheight * .5 * (j - k);
game.grid[i][j].termap = bitmap;
}
}
document.getElementById("loader").className = "";
createjs.Ticker.addEventListener("tick", tick);
console.log("finished loading images and creating bitmaps. Game update set to true");
game.update = true;
}
My problem lies in the tick function, which is called 60 times a second. I only want the map to update if something is changed (when game.update == true).
function tick(event) {
if(game.update == true){
game.update = false;
var i,j,k;
var r = game.radius;
for (i = -r; i <= r; i++){
var min = Math.max(-r, -r - i);
var max = Math.min(r, r - i);
for (j = min; j <= max; j++){
k = -1 * (i + j);
if(game.grid[i][j].termap != null){
game.grid[i][j].termap.x = game.xcenter + game.hexwidth * .75 * i;
game.grid[i][j].termap.y = game.ycenter + game.hexheight * .5 * (j - k);
} else {
console.log("A bitmap is null.");
game.update = true; //still needs to be updated
}
}
}
game.stage.update(event);
console.log("UPDATED!");
}
}
game.update is only set to true after all the bitmaps are created. As you can see if you load the website, (here is the link again), the console log shows all the bitmaps to be created before the stage is updated. This is also shown in the console print statements. If you click in the center of the screen and drag your mouse, you can force an update by moving the map, which changes game.update to true and properly forces the stage to update. I can't figure out why the bitmaps aren't showing immediately, as the game.update is clearly called only after they are created and added to the stage. Bitmaps do have a visible property, but the default value is true anyways.
I answered this here
http://community.createjs.com/discussions/easeljs/5440-bitmaps-not-showing-immediately-after-being-loaded
The short answer is that the images are created, but not actually loaded. They can be added to the DOM to be considered with the document.load (which I believe was the poster's solution), but a better approach is to properly preload them after the document load, using something like PreloadJS (http://createjs.com/preloadjs).
I am trying to simulate shaking by changing the position of img tags within a div.
I have it working for one img at a time.
Is there a way to change the style of all the img tags within a div at once?
This is what I am currently doing:
bird = document.createElement('img');
bird.setAttribute('src',birdie);
bird.setAttribute('class', 'birdie');
bird.setAttribute('id', id);
bird.setAttribute('onLoad', 'shakeAll()');
map.appendChild(bird);
birds++;
if(birdmap[0] == 0){
birdmap = [id];
}else{
birdmap+=[,id];
}
this ShakeAll function is also at onLoad of body:
function shakeAll(){
if (birdmap[0] == 0) return;
i = 1;
while(i <= birds){
shakeIt(birdmap[i]);
i++;
}
setTimeout("shakeAll()",initialSpeed);
}
Note: the imgs are absolute
function shakeIt(id){
shake = document.getElementById(id);
j=1;
while(j<4){
if (j==1){
shake.style.top=parseInt(shake.style.top)+distance+"px";
}
else if (j==2){
shake.style.left=parseInt(shake.style.left)+distance+"px";
}
else if (j==3){
shake.style.top=parseInt(shake.style.top)-distance+"px";
}
else{
shake.style.left=parseInt(shake.style.left)-distance+"px";
}
j++;
}
//setTimeout("shakeIt(id)", 50);
}
I couldn't really tell what you were trying to do in your code. Here's some code that shows the basics of moving some images back and forth:
// quadratic easing in/out - acceleration until halfway, then deceleration
// t = time into the animation
// d = duration of the total animation
// b = base value
// c = max change from base value (range)
var easeInOutQuad = function (t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t + b;
return -c/2 * ((--t)*(t-2) - 1) + b;
};
var linearTween = function (t, b, c, d) {
return c*t/d + b;
};
// cubic easing in/out - acceleration until halfway, then deceleration
var easeInOutCubic = function (t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t*t + b;
return c/2*((t-=2)*t*t + 2) + b;
};
function shakeAll() {
var container = document.getElementById("container");
var imgs = container.getElementsByTagName("img");
// save away original position
for (var i = 0; i < imgs.length; i++) {
imgs[i].basePos = parseInt(imgs[i].style.left, 10);
}
var numShakes = 0;
var maxShakes = 10;
var range = 100;
var direction = 1;
var duration = 300; // ms
var startTime = (new Date()).getTime();
var deltas = [];
function shakeImgs() {
var now = (new Date()).getTime();
var elapsed = Math.min(now - startTime, duration);
var delta = Math.round(easeInOutQuad(elapsed, 0, range, duration));
delta *= direction;
for (var i = 0; i < imgs.length; i++) {
var basePos = imgs[i].basePos;
if (direction < 0) {
basePos += range;
}
imgs[i].style.left = (basePos + delta) + "px";
}
if (now - startTime >= duration) {
startTime = now;
direction *= -1;
++numShakes;
}
if (numShakes < maxShakes) {
setTimeout(shakeImgs, 10);
}
}
shakeImgs();
}
Working demo and HTML shown here: http://jsfiddle.net/jfriend00/ED5yA/
And, here's a fun one that adds some random shakiness (jitter) to the animation: http://jsfiddle.net/jfriend00/jM8jx/.
The basics of obtaining the list of images to operate on is this part:
var container = document.getElementById("container");
var imgs = container.getElementsByTagName("img");
This gets the container object and then gets all image objects in that container. You can see the corresponding HTML in the jsFiddle. This code implements a positioning scheme that slows the velocity down at the edges and goes the fastest in the middle of the range. The rest of the motion is controlled by the intial value of the variables declared in shakeAll(). These can be edited or can be changed to be passed into the function.
give a common class name to all imgs that you wanna shake. and then, user getElementsByClassName() instead of getElementById() to return an array of the elements which have the specific class name. then use a loop to animate each.
but if you want ALL imgs to be animated, use element.getElementsByTagName() or document.getElementsByTagName() instead.