在Flash上​​跳帧

fel*_*aia 8 flash actionscript-3

有没有办法在Flash上​​本机启用帧跳过?

当您开发游戏时,您可以开发动画以匹配游戏的速度,并以目标帧速率(通常为Flash的24-40fps)进行.但是如果用户的电脑太慢,并且无法保持目标帧率,Flash会自动降低fps,使应用程序播放缓慢.

如果你使用基于时间的逻辑,基于帧的渲染将与基于时间的逻辑不匹配,并且事情会很奇怪,并且会有很多极端情况需要解决.

事实上我知道有些游戏使用这种跳帧逻辑,比如Popcap的Zuma Blitz.他们是自己实施跳帧吗?

除非我能以某种方式重新实现MovieClip类并使其易于框架化,否则我无法在项目的这么晚实现这一点.另外,自己控制动画(覆盖Flash原生MovieClip控件)的开销是不是太大了?

Pla*_*eon 9

使用的方法是考虑每秒像素的变化,而不是每帧像素.然后,您可以在EnterFrame上运行主循环.在enterframe中,调用getTimer()以毫秒为单位获取系统时间.并将其与上次运行脚本时的值进行比较.这将让您确切知道自上一帧以来经过的时间量.使用该金额来确定如何移动东西.

下面是一个示例,无论帧速率如何,都会以相同的速率移动圆圈:

{
    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.utils.getTimer;

    /**
     * ...
     * @author Zachary Foley
     */
    public class Main extends Sprite 
    {
        private var lastFrame:int = 0;
        private var thisFrame:int;
        private var pixelsPerSecond:Number = 200;
        private var circle:Shape
        private var percentToMove:Number; 
        public function Main():void 
        {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
            circle = new Shape();
            circle.graphics.beginFill(0xFF0000);
            circle.graphics.drawCircle(0, 0, 25);
            circle.x = 50;
            addChild(circle);
            addEventListener(Event.ENTER_FRAME, onEnterFrame);
        }

        private function onEnterFrame(e:Event):void 
        {
            // Get the miliseconds since the last frame
            thisFrame = getTimer();
            percentToMove = (thisFrame - lastFrame) / 1000;
            // Save the value for next frame.
            lastFrame = thisFrame;
            // Update your system based on time, not frames.
            circle.x += pixelsPerSecond * percentToMove;

        }

        private function init(e:Event = null):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            // entry point
        }

    }

}
Run Code Online (Sandbox Code Playgroud)

如果您发现低帧速率影响碰撞计算的准确性,则使用计时器单独运行它们.这只是绘图循环,它不可避免地与帧速率相关联.

只是你知道,你不能在flash中跳帧.它由玩家处理.最好的方法是调整帧渲染技术以适应可变帧速率.


Sen*_*mes 3

好吧,我意识到您并不是在寻找以下“解决方案”,该解决方案不起作用,因为所有帧仍会播放:

stage.frameRate = new_frame_rate;
Run Code Online (Sandbox Code Playgroud)

唯一的解决方案是在 EnterFrame 回调中跳过帧:

// Defined elsewhere.
var current_frame:Number = 0;
var frame_skip_factor:uint; // Skip one frame every 'x' frames.
var is_frame_skip_active:Boolean;

function on_enter_frame(event:Event):void
{
   if ( is_frame_skip_active && current_frame++ % frame_skip_factor == 0) { return; }

   // Game logic/rendering code...
}
Run Code Online (Sandbox Code Playgroud)

(假设,根据最佳实践,您已将游戏逻辑/动画集中在单个回调中),但是当运行任何异步回调时,这可能会出现竞争条件/并发症。

没有办法在运行时级别实际跳过帧;这是 AVM 合同的一部分 - 无论如何,不​​会跳过任何帧,所有代码都会运行。如果性能是一个问题,您可以尝试异步代码执行。有几种方法可以做到这一点:

1) 将一些计算卸载到 Pixel Bender,以便可以异步和/或并行处理 (http://www.adobe.com/devnet/flex/articles/flashbuilder4_pixelbender.html)

2)将冗长操作的执行分散到多个帧上(需要状态保存和状态恢复,一种备忘录模式)。详细信息(和一个很好的阅读)在这里:http ://www.senoptic.com/flash/tutorials/asyncoperations/

无论如何,我(首先也是最重要的)建议使用 Flash Builder Profiler 或 Stats 类(Mr. Doob)等工具来明确识别性能瓶颈。

Blitting 也可能是解决方案(为动画的每个帧创建一个带有图块的 spritesheet)。但无论如何,我认为您需要做的是子类化 MovieClip 并重写 play()、stop() 和 gotoAndPlay() 方法。你的类应该看起来像这样:

public class MyMovieClip extends MovieClip
{
   override public function play():void
   {
      addFrameSkipListener();

      super.play();
   }

   override public function gotoAndPlay(frame:Object, scene:String = null):void
   {
      addFrameSkipListener();

      super.gotoAndPlay(frame, scene);
   }

   // ....
}
Run Code Online (Sandbox Code Playgroud)

如果需要,跳帧侦听器将根据当前帧速率或帧时间跳帧。当然,当动画结束时,您还需要删除frameSkipListener。

虽然 MovieClip 覆盖解决方案可能会执行您在纸上想要的操作,但如果您有很多对象,这实际上可能会降低性能,因为额外的 ENTER_FRAME 侦听器会增加一些开销。