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.
Related
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);
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.
I have a KeyboardEvent that switches Boolean, if true an FR var drops -2/frame to a bottom of 10, if false it rises +2/frame to a top of 60. It works fine going up and down but whenever I add "stage.frameRate = FR;" to an ENTER_FRAME function and play it, it freezes the control over the window. Animation still plays but I can't turn it off unless via Ctrl-Alt-Del.
1.Why is this happening?
2.Is it possible to change frameRate every frame?
3.If yes, how?
Um, I am not sure playing with the frame rate on the fly is a good thing to be doing. What are you trying to do? Perhaps there is a better way...
This is a total guess, but perhaps when you set the frame rate to the stage it initialises 'something' in the background. Then since you have it happening every frame, it'll try to initialises on every frame, causing it to be locked up.
Updating answer to include an example of using a Timer object:
import flash.utils.Timer;
import flash.events.TimerEvent;
// the timer takes in milliseconds, so for 30 frames/second, you would have a frame tick at every 1000 / 30, or 33.333ms
var tickSpeed:int = 1000 / 30;
var frameTick:Timer = new Timer(tickSpeed, 0);
function enterFrameListener(inputEvent:Timer):void {
// this method will run on every timer tick
}
frameTick.addEventListener(TimerEvent.TIMER, enterFrameListener);
// can change the tick speed of the timer like so (setting it to 1000 means 1 frame tick every second)
frameTick.delay = 1000;
// according to the api doc however, a tickspeed of less then 20ms is not recommended
// 20ms would be equal to 60 fps
By offloading it to a timer, you shouldn't need to mess around with the overall framerate with another advantage of other objects can be affected by different timers at the same time. Timer class also has a few helpful methods in itself as well: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/utils/Timer.html
I think your app is freezing because you are trying to set the frame rate on every frame, i would suggest you should check your frame rate in your ENTER_FRAME section. if your frame rate goes up/down from you desired frame rate, then you should set your frame rate to FR.
Try this can help you.
Is it possible to do completely smooth scrolling in Flash (ActionScript 3)? In the following test I am creating a bitmap consisting of random noise, then moving it to the left periodically. I have no heavy tasks running in the background. What I am looking for is smoothness that would be on par with my Amiga 500 from 1987 :-)
package {
import flash.display.*;
import flash.events.TimerEvent;
import flash.utils.Timer;
public class Game extends Sprite {
var noiseBitmap;
public function Game() {
var noiseBitmapData = new BitmapData(stage.stageWidth * 3, stage.stageHeight);
noiseBitmapData.noise(0, 0, 255, 7, true);
noiseBitmap = new Bitmap(noiseBitmapData);
addChild(noiseBitmap);
var t = new Timer(1000/30, 999999);
t.addEventListener("timer", function (e:TimerEvent) {
noiseBitmap.x--;
});
t.start();
}
}
}
The "rendering code" takes <1 millisecond to run on my computer (2.4 GHz Mac), but still the movement will occasionally get stuck for a frame or two, making the movement appear jerky.
FPS is set to 30 in Flash. I have tried running it both using "test movie" and in the browser (Chrome). Target is Flash Player 11.2. I have also tried calling e.updateAfterEvent() to force a redraw. I have Also played around setting the delay and FPS to slightly different values, but no improvement.
This is different from not smooth scrolling in AS3 because I am already using a BitmapData. Also I have tried using the ENTER_FRAME event instead of a timer as suggested in a reply to that question, but it did not help.
When using a timer, you will not be exactly synced with the frame rate. As you mention, the frame rate fluctuates a little making your timer sometimes fire twice during one frame or skipping a frame. To be sure to be more in sync with the frame rate, you should listen to the Event.ENTER_FRAME event.
An example:
this.addEventListener(Event.ENTER_FRAME, updateFrame);
⋮
function updateFrame(e:Event):void {
noiseBitmap.x--;
}
I suggest you try out Greensock's TweenLite. It is a highly optimized engine to do all sorts of tweening through code and is available for AS2 and AS3. You can find it here.
Walkietokyo's solution is still frame based and would not eliminate the issues you run into. Instead use time-based animation (which TweenLite actually implements). For more info refer to this article.
I'm creating a Flash game which is based on the old Pacman and I'm not sure which is the best way to control the animation.
As I understand it these type of games were originally dependent on a game loop which ran faster or slower depending on the CPU, which is why I imagine that the most similar to use would be the ENTER_FRAME event.
This however presents the problem of having to have a specific frame rate and changing it later is out of the question, not to mention being limited to very few different "speeds" (see below). An example could be that the sprite has to move 12 pixels before the next move is determined. If the speed is then 4 pixels per frame, the math is quite simple:
[...]
public var stepCount:uint = 0;
[...]
function enterFrameHandler(e:Event):void
{
if(stepCount==0) {
//Some code to evaluate next move. Let's say it evaluates to MOVE RIGHT
}
if(MOVE_RIGHT)
{
x += 4;
}
stepCount++;
if(stepCount > 2)
{
stepCount = 0; //Now ready to evaluate direction again.
}
}
This all works fine, but let's say that I want the sprite to move 5 pixels per frame. Then the number of frames before making the next evaluation would not compute. The stepSize would have to be a multiple of 12, which limits the different possible speeds (1,2,3,4 and 6 pixels per frame).
This is why I attempted to base the movement on a Timer instead, which I also managed to get to work, but the movement was somewhat erratic and it seemed like the Timer was using far more memory than the ENTER_FRAME event. Instead of an even movement the Timer made the sprite slow down and speed up and slow down again.
Another possible solution could be the Tween class, but it seems extravagant.
Does anyone have experience with what works best in other games?
Morten Twellmann
You have several separate issues here. Your first question is, should you execute your game loop in a frame event or a timer event? The answer is easy - you should do it in a frame event. The reason is that regardless of how you move your characters, the screen is updated precisely once per frame. So any time you're calling your game loop more than once per frame you're wasting CPU, and any time you call it less than once per frame, you're sacrificing visual quality. So this one is easy, don't bother with timer events at all.
The next question is whether your game's movement should be tied to frames or miliseconds, and the answer is that it depends on the game. Ask yourself this: suppose that some user is playing your game, and their spaceship (or whatever) is flying along at a given speed. Suddenly, the user's anti-virus package does something heavy, and the CPU spike causes Flash to stop updating for one second. Once the spike is over, do you want the spaceship to continue moving from where it was when the spike started? Or do you want it to jump forwards to where it would be if it had continued moving during the spike? If you want the former, you should tie your movement to frames; if you want the latter, you should tie it to miliseconds. But which one is best depends on how you want your game to work.
The final question is, how exactly should you move the characters in your game? Based on what you wrote, I'd do it as follows. For frame-based movement (i.e. the first approach described earlier):
// the ship moves 25 pixels per second
var shipSpeed:Number = 25;
// the number of seconds per frame, based on the published framerate
var frameTime:Number = 1 / stage.frameRate;
// game loop called each frame:
function gameLoop() {
// ...
playerShip.x += shipSpeed * frameTime;
// ....
}
This way, the ship's movement on screen is constant, regardless of what framerate you publish your SWF at. Using a higher framerate simply makes the movement smoother. Likewise, to tie your movement to time instead of frames, simply change "frameTime" in the code above to refer to the time elapsed since the previous frame, as described in Allan's answer.
Yes frame rates will vary depending on CPU amongst other things. Therefore you need to take this into account with your game loop. What I like to do is get the time difference between the current frame and the old frame and use that value in my calculations. So if it happens that there is a delay the larger difference value will then make up for the fact less frames ran.
var _previousTime:Number;
//gameLoop is the function called on ENTER_FRAME
public function gameLoop(e:Event):void
{
var currentTime:Number = getTimer();
var difference:Number = currentTime - _previousTime;
_previousTime = currentTime;
//use difference variable with calculations involving movement
}