Using this code:
x = x + (canvas.height/250);
which happens every 1 millisecond should add an amount to x in proportion to the canvas size. x is then drawn so therefore x should move down the canvas at the same speed on different screen sizes (the canvas changes size according to the screen size). However the x moves down at different speeds on my ipod and my pc.
If you want to know the full source code and html file the html is here and the javascript file linked to it is here.
First, let's think about what this line does:
x += canvas.height / 250;
The speed of the object is canvas.height / 250. The distance is canvas.height. We can say that:
distance = speed * time
We already have distance and speed, so:
time = distance / speed = canvas.height / (canvas.height / 250) = 250 ms
So the object always reaches its destination in 2.5 seconds. In order to make that happen, you change the speed based on the screen size.
If you want the speed to be the same in all devices, it shouldn't depend on canvas.height.
The change in traversal time is due to a change in draw time.
If the device is, say, two times slower to draw, the increase
will get done two times less per second, and your object will be
in mid-screen in the slow device when it will have crossed screen in
the other.
To have consistent behavior you must handle your game time.
Then you just have to decide of your objects speed in pixels per milliseconds,
and use the classical equation :
pos = pos + speed * (time step)
the code looks like :
var x = 0;
var speed = canvas.width / 1000 ; // speed in pixels per milliseconds.
var lastTime = 0;
requestAnimationFrame(launchAnimate);
function animate() {
requestAnimationFrame(animate);
var now = Date.now();
var dt = now - lastTime ;
lastTime = now ;
// draw everything
draw();
// update everything with this frame time step.
update(dt);
}
function launchAnimate() {
lastTime = Date.now() ;
requestAnimationFrame(animate);
}
Edit : you can't draw faster than your screen, so on a 60Hz screen, you'll draw at best every 16.666 ms.
Don't be scared by RequestAnimationFrame, it just means : "when the screen is ready, draw the callback that i gave you.".
So you have to re-arm it every time to make your game live.
So for your game, it's time to organize a bit :
have an update function where you put all the code that updates all things.
have a draw function that draws everything.
do not draw in the update (remove the draw calls in moveTrees), and
do not update in the draw. SEPARATION OF CONCERNS, :-)
have each update depend on frame time (dt) for all that's related to time (position, speed, acceleration, forces).
function update( dt ) {
x = x + speed * dt ;
moveTrees(dt);
}
function draw () {
drawTrees();
drawHangGlider();
drawTrees();
}
You'll notice i changed the animate function to call draw and update, still you have a bit of work to re-organize things.
Edit 2 : RequestAnimationFrame is widely ok now :
http://caniuse.com/requestanimationframe
(Chrome for Android is ok).
Rq :
// to use rAF, call this function before your game launches
polyFillRAFNow();
// insert the function in your code.
function polyFillRAFNow() {
// requestAnimationFrame polyfill
var w = window;
var foundRequestAnimationFrame = w.requestAnimationFrame || w.webkitRequestAnimationFrame || w.msRequestAnimationFrame || w.mozRequestAnimationFrame || w.oRequestAnimationFrame || function (cb) { setTimeout(cb, 1000 / 60); };
window.requestAnimationFrame = foundRequestAnimationFrame;
}
Rq2 : you might be interested in this article i wrote on the game loop http://gamealchemist.wordpress.com/2013/03/16/thoughts-on-the-javascript-game-loop/
So basically you want a relative absolute speed depending on the screen size, which would then appear to be the same on different displays. In order to do this you should use percentages, for example:
var pixels_in_1_percent = canvas.height/100;
x += pixels_in_1_percent
That will increase the speed by 1% of canvas height, if you want more speed then you have to multiply it
x += pixels_in_1_percent * <number_of_percent_to_increase>
Hope that helps.
You said in your question that you are calling the function every 1 millisecond. However, some browsers limit the speed of window.setInterval so if you made the function call once every 50 milliseconds it would be the same on all devices.
window.setInterval(function(){
/// call your function here e.g. add1toxfunction();
}, 50);
Related
I am trying to get a simple random "rotation" based on the angle property, and would like to achieve the following.
Random Rotation, say "rotate" to 500 degrees
Start with a high speed and when getting nearer to the "rotated" degrees, lower the "rotating" speed so that the stop is not instant.
I put a basic concept of what I am trying to achieve on this link:
https://www.pixiplayground.com/#/edit/yalRPEN~6tg3seIHq5hbI
In the animate function, if I put the let degrees = 2000; as a Math.Random value it screws up the animation as it seems that this animate function is called numerous times?
Also, I tried using the speed property which would start off with a high value and starts to get lower with "rotations", but it seems to do nothing? Also tried using animationSpeed since I am using the angle property to change degrees, but I see no speed difference.
Any feedback would be appreciated.
Thanks
One approach is to change the angle and not the speed. Something like this:
var angleStep = 40;
let rotateSpeed = Math.floor(Math.random() * 300)
function animate() {
bunny.angle += angleStep;
angleStep = angleStep - angleStep/rotateSpeed;
if ((angleStep.toFixed(1) <= 0.0)) {
console.log("stopped rotation")
bunny.angle = bunny.angle;
return bunny.angle;
} else {
requestAnimationFrame(animate);
}
}
I am new to EaselJs.
I am rotating a wheel with 9x numbers and 3 (0x), the total of 12 numbers. I am able to rotate the wheel by calling function, but I want to stop it on predefined specific point/number of the wheel.
var _oContainer;
this._init = function(iXPos,iYPos){
_oWheel = s_oSpriteLibrary.getSprite("wheel_circle");
_oContainer = new createjs.Container;
_oContainer.x = CANVAS_WIDTH - 200;
_oContainer.y = CANVAS_HEIGHT - 350;
s_oStage.addChild(_oContainer);
img = createBitmap(_oWheel);
img.regX = _oWheel.width / 2;
img.regY = _oWheel.height / 2;
_oContainer.addChild(img);
}
this.spin = function(b, a){
//var h = new createjs.Bitmap(s_oSpriteLibrary.getSprite('wheel_circle'));
createjs.Tween.get(_oContainer).to({
rotation: _oContainer.rotation + a
}, 2600, createjs.Ease.quartOut).call(function() {
_oContainer.rotation %= 360;
})
}
I am calling the spin function as this.spin(5, 1131.7511808994204); on every time button is clicked.
Right now it is spinning and stopping randomly on every button click. How can stop it on a specific number/position on the wheel?
What value should I give in rotation:?
There are a lot of factors in play to do something like this. I made a quick demo to show how I would do it:
Draw the wheel (at center) with segments. It is important to know how many segments you have so you can choose a place to "end"
Start spinning. Just increment the rotation each tick depending on how fast you want it to go.
When "stopping", you have to do math to determine where to land
To get a realistic "slow down", make sure the remaining rotation in the tween is enough so it doesn't speed up or slow down too rapidly
Here is the fiddle: https://jsfiddle.net/lannymcnie/ych1qt8u/1/
// Choose an end number. In this case, its just a number between 0 and the number of segments.
var num = Math.random() * segments | 0,
// How many degrees is one segment?
// In my demo I use radians for `angle`, so I have to convert to degrees
angleR = angle * 180/Math.PI,
// Take the current rotation, add 360 to it to take it a bit further
// Note that my demo rotates backwards, so I subtract instead of add.
adjusted = c.rotation - 360,
// Determine how many rotations the wheel has already gone since it might spin for a while
// Then add the new angle to it (num*angleR)
// Then add a half-segment to center it.
numRotations = Math.ceil(adjusted/360)*360 - num*angleR - angleR/2;
Then I just run a tween to the new position. You can play with the duration and ease to get something you like.
createjs.Tween.get(c)
.to({rotation:numRotations}, 3000, createjs.Ease.cubicOut);
Technically, I should change the duration depending on the actual remaining spin, since depending on the result, it might not be super smooth. This came close enough, so I left it as-is.
Hope that helps! Let me know if I can clarify anything further.
I need to scale lots of text nodes in the browser (support of all modern desktop and mobile browsers).
If I am right there are two options that offer good performance: scaling text objects in Canvas or scaling text nodes in the DOM using transform:matrix.
I have created a scenario to test both versions but the results are inconclusive. Uncomment testDOM() or testCanvas() function to start the test. (I am using JQuery and CreateJS framework because it was convenient. It is possible to use vanilla JS but I don't think that is the bottleneck here). (It matters what portion of the screen you actually see so please switch to full screen view in codepen).
http://codepen.io/dandare/pen/pEJyYG
var WIDTH = 500;
var HEIGHT = 500;
var COUNT = 200;
var STEP = 1.02;
var MIN = 0.1;
var MAX = 10;
var stage;
var canvas;
var bg;
var canvasTexts = [];
var domTexts = [];
var domMatrix = [];
var dom;
function testDOM() {
for (var i = 0; i < COUNT; i++) {
var text = $("<div>Hello World</div>");
var scale = MIN + Math.random() * 10;
var matrix = [scale, 0, 0, scale, Math.random() * WIDTH, Math.random() * HEIGHT];
text.css("transform", "matrix(" + matrix.join(',') + ")");
domTexts.push(text);
domMatrix.push(matrix);
}
dom = $('#dom');
dom.append(domTexts);
setTimeout(tickDOM, 1000);
}
function tickDOM() {
for (var i = 0; i < domTexts.length; i++) {
var text = domTexts[i];
var matrix = domMatrix[i];
var scale = matrix[0];
scale *= STEP;
if (scale > MAX)
scale = MIN;
matrix[0] = matrix[3] = scale;
text.css("transform", "matrix(" + matrix.join(',') + ")");
}
requestAnimationFrame(tickDOM);
}
function testCanvas() {
$('#dom').hide();
stage = new createjs.Stage('canvas');
createjs.Touch.enable(stage);
createjs.Ticker.timingMode = createjs.Ticker.RAF;
canvas = stage.canvas;
devicePixelRatio = window.devicePixelRatio || 1;
stage.scaleX = devicePixelRatio;
stage.scaleY = devicePixelRatio;
console.log('devicePixelRatio = ' + devicePixelRatio);
stage.mouseMoveOutside = true;
stage.preventSelection = false;
stage.tickEnabled = false;
stage.addChild(bg = new createjs.Shape());
bg.graphics.clear();
bg.graphics.f('#F2F2F2').drawRect(0, 0, 2 * WIDTH, HEIGHT);
canvas.width = 2 * WIDTH * devicePixelRatio;
canvas.height = HEIGHT * devicePixelRatio;
canvas.style.width = 2 * WIDTH + 'px';
canvas.style.height = HEIGHT + 'px';
stage.update();
for (var i = 0; i < COUNT; i++) {
var text = new createjs.Text("Hello World", "10px", "#333333");
text.scaleX = text.scaleY = MIN + Math.random() * 10;
text.x = Math.random() * WIDTH;
text.y = Math.random() * HEIGHT;
stage.addChild(text);
canvasTexts.push(text);
}
stage.update();
setTimeout(tickCanvas, 1000);
}
function tickCanvas() {
for (var i = 0; i < canvasTexts.length; i++) {
var text = canvasTexts[i];
text.scaleX = text.scaleY *= STEP;
if (text.scaleX > MAX)
text.scaleX = text.scaleY = MIN;
}
stage.update();
requestAnimationFrame(tickCanvas);
}
testDOM();
//testCanvas();
My questions:
Is it possible to improve the performance of my tests? Am I doing something wrong?
The first 5-10 seconds are significantly slower but I don't understand why. Does the browser somehow cashes the text objects after some time? If yes, is the test unusable for real world scenario testing where the objects don't zoom in a loop for longer period of time?
According to the Chrome Profiling tool the DOM version leaves 40% more idle time (is 40% more faster) than the Canvas version but the Canvas animation looks much smoother (after the initial 5-10 seconds of lagging), how should I interpret the Profiling tool results?
In the DOM version I am trying to hide the parent of the text nodes before I apply the transformations and then unhide it but it probably does not matter because transform:matrix on absolutely positioned element does not cause reflow, am I right?
The DOM text nodes have some advantages over the Canvas nodes like native mouse over detection with cursor: pointer or support for decorations (you can not have underlined text in Canvas). Anything else I should know?
When setting the transform:matrix I have to create a string that the compiler must to parse back to numbers, is there a more efficient way of using transform:matrix?
Q.1
Is it possible to improve the performance of my tests? Am I doing
something wrong?
Yes and no. (yes improve and no nothing inherently wrong (ignoring jQuery))
Performance is browser, and device dependent, for example Firefox handles objects better than arrays, while Chrome prefers arrays. There is a long list of differences just for the javascript.
Then the rendering is a dependent on the hardware, How much memory, what capabilities, and the particular drivers. Some hardware hates state changes, while others handle them at full speed. Limiting state changes can improve the speed on one machine while the extra code complexity will impact devices that don't need the optimisation.
The OS also plays a part.
Q.2
The first 5-10 seconds are significantly slower but I don't understand
why. Does the browser somehow cashes the text objects after some time?
If yes, is the test unusable for real world scenario testing where the
objects don't zoom in a loop for longer period of time?
Performance testing in Javascript is very complicated and as a whole application (like your test) is not at all practical.
Why slow?
Many reasons, moving memory to the display device, javascript optimising compilers that run while the codes runs and will recompile if it sees fit, this impacts the performance Un-optimised JS is SLOOOOOWWWWWWWW... and you are seeing it run unoptimised.
As well. In an environment like code pen you are also having to deal with all its code that runs in the same context as yours, it has memory, dom, cpu, GC demands in the same environment as yours and thus your code can not be said to be isolated and profiling results accurate.
Q.3
According to the Chrome Profiling tool the DOM version leaves 40% more
idle time (is 40% more faster) than the Canvas version but the Canvas
animation looks much smoother (after the initial 5-10 seconds of
lagging), how should I interpret the Profiling tool results?
That is the nature of requestAnimationFrame (rAF), it will wait till the next frame is ready before it calls your function. Thus if you run 1ms past 1/60th of a second you have missed the presentation of the current display refresh and rAF will wait till the next is due 1/60th minus 1ms before presentation and the next request is called. This will result in ~50% idle time.
There is not much that can be done than make you render function smaller and call it more often, but then you will get extra overhead with the calls.
rAF can be called many times during a frame and will present all renders during that frame at the same time. That way you will not get the overrun idle time if you keep an eye on the current time and ensure you do not overrun the 1/60th second window of opportunity.
Q.4
In the DOM version I am trying to hide the parent of the text nodes
before I apply the transformations and then unhide it but it probably
does not matter because transform:matrix on absolutely positioned
element does not cause reflow, am I right?
Reflow will not be triggered until you exit the function, hiding the parent at the start of a function and then unhiding it at the end will not make much difference. Javascript is blocking, that means nothing will happen while you are in a function.
Q.5
The DOM text nodes have some advantages over the Canvas nodes like
native mouse over detection with cursor: pointer or support for
decorations (you can not have underlined text in Canvas). Anything
else I should know?
That will depend on what the intended use is. DOM offers a full API for UI and presentation. Canvas offers rendering and pixel manipulation. The logic I use is if it takes more code to do it via DOM then canvas, then it is a canvas job and visa versa
Q.6
When setting the transform:matrix I have to create a string that the
compiler must to parse back to numbers, is there a more efficient way
of using transform:matrix?
No. That is the CSS way.
My explanation may be unclear and if so, please leave a comment for further clarification.
I am trying to make the character to jump to the height of the screen.
So far, this is my code:
var limit = 0;
function jump()
{
ball.posy -=10;
limit++;
if(limit>screenheight)
{
//if character reached top of screen stop this jump() function
isjump = false;
limit = 0;
}
}
Above code will enable the character to jump up to the screen height. But if the screen height is smaller (for example, in some mobile device), character jump will be faster.
If there are any game developers who have idea, would you be able to guide me? :) Thank you.
ok the problem is that you need to know the speed of your jump first. now its is doing it depending of your screen for example:
function jump()
myjump += 10 // this is what you are doing now
{
so lets fix that function, how?
lets make that 10 variable depending on the screen height
lets say your screen now is 800px heigh so for a 800px height screen you will add 10px, but for a 400 heigth screen you only need to add 5px!!.
so how we do it?
with this simple formula
var scaleJump = 10 * (screenHeigth/800)
function jump()
myjump += scaleJump
{
so now if your screen heigth is 800 your var scaleJump will be = 10 * ( 800 / 800 ) = 10
and if your screen heigth is 400 scaleJump will be = 10 * ( 400 / 800 ) = 5
so problem solved!! it will increase 5 if screen is 400 and 10 if screen is 800, and scale as needed if anyonther heigth
to fix time
ok how do we know the time it takes to draw a frame?
since: fps = frames / second
so: 1/fps = seconds / frame
this is a differential equation where you have a deltaTime and a deltaDistance
DeltaTime is equals to:
1/fps
and DeltaDistance would be this, if fps were always the same, but like they are not I explain below:
10 * (screenHeigth/800)
now lets set a base speed wich you can change acording to your needs as I dont have your code I am not sure if this will move fast or slow, but you can test it and change it as you need it.
lets set speed to 1 (I dont know if it is fast or slow, just change it latter)
since speed = DeltaDistance / DeltaTime
so lets get the distance you have to add so the speed is the same:
DeltaDistance = Speed * DeltaTime
now your code will look like this
var DeltaDistance;
var DeltaTime = (1 / fps)
var mySpeed = 1
function jump()
DeltaDistance = mySpeed * DeltaTime
myJump += DeltaDistance
{
I miss college :(
http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
If I move the slider on this page, the blurring is very smooth.
But if I try and automate the animation:
var speed = 1250;
var blur = 100;
var interval = speed/blur;
setInterval(function(){
blur--;
stackBlurImage(image, canvas, blur);
}, interval);
The steps are very obvious and it doesn't even work at high speed.
Could anyone suggest an alternative approach to this?
Pre-render the different blur levels, store them and cycle through them as frames. Its possible that whatever stackBlurImage does just can't be done fast enough to look like an animation.
Edit: I take that back, exactly what you did: http://jsfiddle.net/nwellcome/27QUM/ that looks great to me in Chrome, what browser are you using?
Edit 2: Try the second approach in this fiddle: http://jsfiddle.net/nwellcome/27QUM/4/, rather than decreasing the blur radius by 1 each time, fix the fps at something setInterval can handle and manipulate the amount you decrease the blur radius each frame.
var fps = 30;
var blur = 100;
var blurTime = 0.5; // seconds
var interval = 1000 / fps;
var step = blur/ (fps * blurTime);
var anim = setInterval(function(){
blur-= step;
if (blur < 0) {
clearInterval(anim);
}
stackBlurImage(image, canvas, blur);
}, interval);
Edit 3: just for the fun of it, with the HTML5 file API, you can upload your own image to blur: http://jsfiddle.net/nwellcome/27QUM/12/