How to optimize createjs web game? - html

I created a web game with EaselJS, which have a few (2-6) Sprites that are moving on the canvas. Also they may scale up and down according to mouse events.
Here is how I move these Sprites:
container.enemies.forEach(function(drop) {
drop.x += drop.vx;
drop.y += drop.vy;
checkHitWall(drop);
var collided = drop.checkCollision(container.enemies);
if (collided) {
distributeVelocity(drop, collided);
}
});
Here is how I change their size:
growingObject = function(obj) {
if (obj.radius > canvasWidth / 4) {
return;
}
var rate = 1.01;
obj.gotoAndStop("growing");
obj.radius *= rate;
obj.scaleX = obj.scaleY = obj.scale * rate;
obj.scale = obj.scaleX;
}
and this function is called every tick when mouse is down.
The background is cached when it is created.
I don't know if there's best practice for moving/growing sprites with EaselJS.
The game runs find on a desktop browser, but is very laggy on a android phone.
I think these Sprites are not a heavy load for a phone. The profiling result shows draw method consumes most CPU time. How could I optimize this?

One idea: You can try reducing your framerate to reach a balance between smooth animation and acceptable speed. In my experience you can go to surprisingly low framerates on mobile before the game starts looking unacceptable. Try something like this, and adjust your velocity to simulate the same kind of speed you had at your previous fps. Obviously the exact tweaking is up to you.
createjs.Ticker.setFPS(12);

Related

How to get when user change the screen resolution in Adobe AIR?

I can get screens resolution when my application starts, but can I know when the user has changed the resolution?
Something like a event (example):
Screen.mainScreen.addEventListener("CHANGE_RESOLUTION", ...)
I've tried using a setInterval to monitor the resolution, but is this the best way to do this?
var resolution:Rectangle = Screen.mainScreen.bounds;
setInterval(function():void {
if(!Screen.mainScreen.bounds.equals(resolution)) {
trace("changed!");
}
}, 1000);
As far as I know, polling (what you're currently doing) is the only way in AS3 to detect screen resolution changes.
However, if you are in a maximized or fullscreen window, the window (or stage if set to NO_SCALE) will fire a resize event on a resolution change. (see the answer from Diniden). Though I'd recommend listening on the stage.nativeWindow object instead of stage itself.
I'd say that you are doing it a perfectly acceptable way currently.
Keep in mind though, that if this is a desktop application, the user could have your program on a monitor that is not the primary one (Screen.mainScreen). To support that scenario, you'd want to do something like the following:
//a constant to use for a custom event
const RESOLUTION_CHANGE:String = "resolution_change";
var resolution:Rectangle = curScreen.bounds;
//Adobe recommends using timers instead of setInterval - using ENTER_FRAME may be too expensive for your needs, but would afford the quickest reaction time.
var resolutionTimer:Timer = new Timer(1000); //poll every second (adjust to optimum performance for your application)
resolutionTimer.addEventListener(TimerEvent.TIMER, pollScreenResolution);
resolutionTimer.start();
//get the screen that the window is on
function get curScreen():Screen {
//get the first screen [0] that the current window is on
return Screen.getScreensForRectangle(stage.nativeWindow.bounds)[0];
}
function pollScreenResolution(e:TimerEvent) {
if(!curScreen.bounds.equals(resolution)) {
trace("changed!");
//dispatch our custom event
stage.dispatchEvent(new Event(RESOLUTION_CHANGE));
resolution = curScreen.bounds; //set the resolution to the new resolution so the event only dispatches once.
}
}
Now you can listen for that event in other parts of your application.
stage.addEventListener(MovieClip(root).RESOLUTION_CHANGE, doSomething);
This is how I handle window resize events. You look at the stage of the given window for seeing how it's being resized. The other properties is to make it resize in a sensible way.
this.stage.scaleMode = StageScaleMode.NO_SCALE;
this.stage.align = StageAlign.TOP_LEFT;
stage.addEventListener(Event.RESIZE, stageResized);

How to lock FPS with requestAnimationFrame?

I used script from Paul Irish
https://gist.github.com/paulirish/1579671
to create animation loop inside html site.
It works although it's faster in fullscreen mode than in browser window.
Also, I observed different speeds depending on canvas size and depending on browser I use.
Question: How can I ensure stable frame rate using the script?
Code is available here (Beginning WebGL, chapter 1 by Brian Danchilla):
https://github.com/bdanchilla/beginningwebgl/blob/master/01/2D_movement.html
Something like this should work. If the time delta between two frames is shorter than your FPS limit, the update function returns and waits for the next frame. But this will only limit the updates from happening too quickly; like emackey said, there's always the possibility the update loop will run more slowly.
var updateId,
previousDelta = 0,
fpsLimit = 30;
function update(currentDelta) {
updateId = requestAnimationFrame(update);
var delta = currentDelta - previousDelta;
if (fpsLimit && delta < 1000 / fpsLimit) {
return;
}
/* your code here */
previousDelta = currentDelta;
}
To embellish what #emackey said,
The short answer is you can't. You could ask the computer to do an infinite amount of work each frame. I can't promise to do that work in a finite amount of time.
On top of that each computer has a different amount of power. A cheap integrated GPU has much less power than a high end graphics card. An intel i3 is much slower than an i7.
You also mentioned changing the canvas size. Drawing a 300x150 canvas is only 45000 pixels worth of work. Drawing a 1920x1080 canvas would be 2,073,600 pixels of work or 46x more work
The best you can do is do the least amount of work possible, and or remove features on slow hardware either automatically or by user choice. Most games do this. They graphics setting options where the user can choose resolution, texture res, anti-alising levels and all kinds of other things.
That said, you can try to do your computations so things in your app move at a consistent speed relative to time. The framerate might slower on a slow machine or with a larger canvas but the distance something moves per second will remain the same.
You can do this by using the time value passed into requestAnimationFrame
function render(time) {
// time is time in milliseconds since the page was loaded
...do work...
requestAnimationFrame(render);
}
requestAnimationFrame(render);
For example here is NON framerate independent animation
function render(time) {
xPosition = xPosition + velocity;
...
requestAnimationFrame(render);
}
requestAnimationFrame(render);
and here is frame rate independent animation
var then = 0;
function render(time) {
var timeInSeconds = time * 0.001;
var deltaTimeInSeconds = timeInSeconds - then;
then = timeInSeconds;
xPosition = xPosition + velocityInUnitsPerSecond * deltaTimeInSeconds;
...
requestAnimationFrame(render);
}
requestAnimationFrame(render);
Note: The time passed into requestAnimationFrame is higher resolution than Date.now()
Here's an article on it with animations
You can't enforce a stable frame rate directly. Your page is not the only app running on the user's platform, and platform capabilities vary widely. requestAnimationFrame runs as fast as it can, not exceeding the display update interval on the target device, but potentially much slower depending on available CPU, GPU, memory, and other limitations.
The standard practice here is to measure the amount of time that has elapsed since the previous animation frame, typically with Date.now(), and each frame advance the animation by that amount of time. To the human eye, this makes the resulting animation run at a consistent speed, even if the frame rate is highly variable.
For example, sites such as Shadertoy and GLSL Sandbox run full-screen GLSL shaders and pass in a uniform called time (or iGlobalTime), which is a float representing the number of seconds elapsed since the shader started. This time value increases at irregular intervals depending on how long each animation frame took to render, but the result is that the float appears to count upwards at a stable 1.0 per second. In this way, shader playback based on this time value can appear consistent.

Moving text across screen smoothly

For a long time I've been searching for a solution to this problem, so I decided to post a tread instead when the search didn't clarify anything.
I have a textfield that is supposed to move across the screen. I've solved this by adding a speed to its x-value dynamically through an "enter-frame function". However, the movement is very "laggy" and consists of sudden "jumps" in the movement. I've tried a couple of possible solutions to this, all of them without luck.
embedding fonts
changing the textfield's antiAliasType
using BitmapData like this:
bmd = new BitmapData (myTextField.width, myTextField.height, true, 0);
bmd.draw (myTextField);
bm = new Bitmap (bmd);
bm.x = myTextField.x;
bm.y = myTextField.y;
bm.cacheAsBitmap = true;
bm.smoothing = true;
this.addChild(bm);`
And then moving the "bm" instance
None of these methods worked.
EDIT: By request, I am adding the relevant code for the actual movement of the text.
stage.addEventListener(Event.ENTER_FRAME, time);
private function time(evt:Event):void
{
bm.x-= textSpeed;
}
The variable textSpeed is defined as a public static var. Its value is 2.
*EDIT2: I've prepared a clean fla-file with nothing but moving text. The same lag occurs for me also here. The code is in the actions panel. Download link
the way Flash IDE works, is that setting the framerate is actually the 'maximum' framerate. That is, it doesn't force the animation to run at that rate - it can vary depending on the machine and available resources.
As far as I know, there's no way to force Flash to run at a certain framerate - the best way to make animations 'smooth' is to use Tween classes like TweenLite.
If you NEED to animate by incrementing position values, then I suggest making it time based instead, for example:
var fps = 24;
var moveTimer:Timer = new Timer(1000/fps);
moveTimer.addEventListener(TimerEvent.TIMER, onMoveTimer);
moveTimer.start();
function onMoveTimer(e:TimerEvent){
bm.x -= 1;
}
Again, this doesn't solve the smoothness of the animation, but it will be much more reliable across different machines than using enter frame.
Try increasing the framerate. Because you naturally try to read text as it animates, you can generally notice the gaps between frames at 24fps. Try setting stage.frameRate to 30, 48, or 60 (60 being the max) and see if that solves your issues. I've had similar issues with animating text in the past and increasing frame rate has fixed them.
I would also recommend only increasing it as needed. You are much more likely to drop frames with a higher frame rate (makes logical sense; each frame has less time to calculate as frame rate increases), so you might want to do something like:
stage.frameRate = 48;
// run animations here
stage.frameRate = 24; // in an Event.COMPLETE handler
That will make sure your animations are smooth while giving the rest of your application the best shot of running well on lesser devices. If you are running a lot of animations, you might consider keeping it elevated permanently.
You should also look into using the Greensock animation library (TweenLite/TweenMax) instead of Flash's built-in tweening. Greensock has a vastly superior API, both in terms of features and performances, especially on mobile.

Having trouble with 2D texture movement

I'm having trouble with smooth character movement for a 2d game I'm making.
It looks like the character is double when moving. Kinda like this (its just one pixel though):
( ()
/ /{}\ ==>
| ||
The game runs on a solid 60 FPS and my monitor is not the problem (I have tested this on several monitors).
I'm using starling at the moment, but I've had this since I first stared making games (using openGL). I was hoping someone could tell me what I'm missing. Here is my code:
private var _x:Number = 20, _y:Number;
public function update(delta:Number):void
{
if(gravity){
_y += delta * 120;
}
if(_y + skin.image.height > game.stage.stageHeight){
_y = game.stage.stageHeight - skin.image.height;
gravity = false;
}
if(right && left){
skin.playAnimation("none");
}else if(left){
_x -= delta * speed;
skin.playAnimation("left");
}else if(right){
_x += delta * speed;
skin.playAnimation("right");
}
//update skin
skin.update(delta, Math.round(_x), Math.round(_y));
}
skin update method:
public function update(delta:Number, x:int, y:int):void
{
image.x = x;
image.y = y;
if(currentAnimation){//this is texture switching (I tried without, still happens)
currentAnimation.update(delta);
}else{
image.texture = textures[4];
}
}
Here is the game.
Based on my own experimentation, as well as all the data in the comments, I think the ghosting is dependent on the frame rate and the computer itself.
First, and most importantly, when I tried the game in the link, it had the described ghosting at the full frame rate of 60. However, when I tried to film a screencast for further analysis, that occupied some CPU, dropping the frame rate down to around 35 or 40. At that point, the ghosting stopped.
Second, because different people are reporting different experiences with the described ghosting, I'd also think that it has a little to do with the individual computers themselves, probably rooted in their available memory and CPU, though that's just a theory, as I don't have any benchmarks to go off of.
Third, I think part of it is also rooted in neuroscience, as different people perceive animation differently.
People always talk about wanting a higher frame rate, but honestly, to fix this, I would recommend LOWERING the frame rate. Flash Professional CS6 has a default frame rate of 24 FPS, and I have never experienced a ghosting problem to my memory. It is worth mentioning that Flash animators usually work in 24 FPS.
Then again, appearance-wise, it really isn't a major deal. It is one of those graphics flaws that people tend to ignore, similar to the motion blur you get on 24 FPS films, until they changed it to 48.
It seems to be something that all games have (I actually never noticed it on some games where I do notice it now) and I just have to live with it. Its not my hardware, its not my monitor. I tried it on a lot of computers (good to bad hardware) and it looks the same on all computers. It looks worse on the computers with bad hardware, but there is not a huge difference.

Actionscript-3 - How to fix display frame rate to 60?

There are 2 different FPS:
background FPS for sprite update
goes as fast as CPU allows
more or less constant 60 FPS (cheap sprite update)
display FPS (visible with Fraps)
goes up to 60 when sprites move wild around
drops to 0 when no sprite moves
I want to make the display FPS stay at 60 FPS. If Adobe made it impossible to change that I need to know or ...
How do I make the display FPS stay at 60 ?
To clear misunderstandings: Fraps hooks the WinApi function SwapBuffers. So every time a game window displays a new rendered scene by swapping buffers, Fraps has the chance to copy buffer content and put some FPS counter on it. Flash is optimized to save GPU time. When nothing changes nothing is drawn to the other buffer and no buffer swapping is performed. Actually this is a good thing. But I still want to know if I can disable this optimization. I'm not trying to disable vsync but to equal the GPU FPS with the CPU FPS.
This is how I update:
public class Main extends MovieClip
{
public function Main()
{
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(event: Event): void
{
//updateSprites();
}
}
Example:
import flash.display.MovieClip;
import flash.events.Event;
[SWF(frameRate="60",backgroundColor="0xffffff",width="960",height="540")]
public class SimpleSprite extends MovieClip
{
public function SimpleSprite()
{
for (var i: int = 0; i < 32; i++)
{
var angle: Number = 2 * Math.PI * Math.random();
var color: uint = 0x1000000 * Math.random();
var length: Number = 1024;
graphics.lineStyle(2, color);
graphics.moveTo(-Math.sin(angle) * length, -Math.cos(angle) * length);
graphics.lineTo(Math.sin(angle) * length, Math.cos(angle) * length);
}
x = stage.stageWidth / 2;
y = stage.stageHeight / 2;
stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(event: Event): void
{
rotation += 0.002;
}
}
I found an unsatisfying way to accomplish what I asked for:
Add a sprite to the stage that is invisible by alpha and let it move on the screen. If it leaves the screen the frame rate will drop again.
If you add an ENTERFRAME event listener, it will fire with each frame (up to requested FPS rate), regardless if there's any redrawing going on or not. If it drops below that, then it just means it didn't manage to render frames that fast. A wild guess? I'd say it's code performance. :-)
Also, Timer documentation states that you shouldn't use it for intervals shorter than 20ms, and setting it lower than 16.6ms will mess with flash inner workings.
Timer Documentation
I would suggest refactoring your app to use ENTERFRAME events instead of Timer events, and using actual elapsed time between frames to calculate animation positions.
Try using Adobe Scout. I it will show you when flash frames are running and when the display is updated. You can get a lot more detail on how your application is working. Why do you want Flash to update the physical display when nothing has changed? It won't accomplish anything. If you really want to force this, modify some bits on the display and set them back again. Scout will show exactly which bits are updated in each frame.
Do not use timers. It's not meant to do what you're trying to get it to.
Set your SWF framerate to 60 during compilation, either on the SWF header meta tags or your compiler settings.
Use an ENTER_FRAME event to run your code (e.g. updateSprites()).
You may skip some frames depending on how complex the animation is. That is inevitable. Trying to "force" the framerate won't work and will only make things worse. If you need something to run 60 times a second you can check getTimer() inside your ENTER_FRAME event and make sure something is being done that number of times. But it'll still be rendered at a lower framerate.