保持 Flash Player 背景運作 FPS 技巧

作者: nick 分类: as, flash 发布时间: 2012-03-28 03:47 ė 6没有评论

最近處裡另一個時間上的問題時,偶然發現的技巧
可以讓背景模式下 Flash Player Plugin 維持原本 FPS 運作
突破 2 FPS (或 8 FPS) 的節流限制 (Throttle Mode)
參考之前的文章 Flash Player 10.1 維持 8 FPS 小技巧

這次一樣是用 Sound 物件來做到的
只有一點不同,就是要自己合成聲音資料
測試程式如下:

package {
 import flash.display.Sprite;
 import flash.events.Event;
 import flash.events.SampleDataEvent;
 import flash.events.TimerEvent;
 import flash.media.Sound;
 import flash.media.SoundChannel;
 import flash.net.URLRequest;
 import flash.utils.getTimer;

 [SWF(width = "300", height = "200", frameRate = "30")]
 public class Test extends Sprite {
  
  public var snd:Sound = new Sound();
  public var sndCh:SoundChannel;
  
  public function Test() {
   
   addEventListener(Event.ENTER_FRAME, onEnterFrameHandler);
   
   /*/ 強迫至少保持 8 FPS
   snd = new Sound(new URLRequest(""));
   snd.play();
   snd.close();
   //*/
   
   //*/ 強迫保持原本設定 FPS
   snd.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleDataHandler, false, 0, true);
   sndCh = snd.play();
   //*/
   
  }
  
  public function onSampleDataHandler(e:SampleDataEvent):void{
   e.data.position = e.data.length = 4096 * 4;
  }
  
  
  public var lastFrameTime:int = getTimer();
  
  public function onEnterFrameHandler(e:Event):void{
   var currFrameTime:int = getTimer();
   var timeElapsed:int = currFrameTime - lastFrameTime;
   trace("[", e.type, "] elapsed:", timeElapsed, "ms, fps:",  ~~(1000 / timeElapsed + 0.5));
   lastFrameTime = currFrameTime;
  }
  
 }
}

編譯測試以上的程式,然後用瀏覽器執行,並切換到背景
從輸出 Log 可以發現一直都能保持原本設定的 30 FPS 左右
假如把聲音程式註解掉,跑起來就會變成這樣了

[ enterFrame ] elapsed: 47 ms, fps: 21
[ enterFrame ] elapsed: 26 ms, fps: 38
[ enterFrame ] elapsed: 49 ms, fps: 20
[ enterFrame ] elapsed: 20 ms, fps: 50
[ enterFrame ] elapsed: 32 ms, fps: 31
[ enterFrame ] elapsed: 33 ms, fps: 30
[ enterFrame ] elapsed: 34 ms, fps: 29
[ enterFrame ] elapsed: 32 ms, fps: 31
[ enterFrame ] elapsed: 35 ms, fps: 29
// 切換到背景
[ enterFrame ] elapsed: 510 ms, fps: 2
[ enterFrame ] elapsed: 507 ms, fps: 2
[ enterFrame ] elapsed: 508 ms, fps: 2
...

讓聲音一直在那邊跑感覺很怪,所以寫成公用類別
加上一些自動偵測的功能,不需要用的時候可以停止
等到將來 Flash Player 提供 Throttle Event 時候
就不需要自己偵測了

package com.ticore.time.utils {
 import flash.display.Shape;
 import flash.events.Event;
 import flash.events.SampleDataEvent;
 import flash.media.Sound;
 import flash.media.SoundChannel;
 import flash.utils.getTimer;

 /**
  * 利用 Sound SampleData 事件
  * 迫使 Flash Player 背景執行時,仍以指定 FPS 運作
  * 
  * 使用方式
  * 
  * 1. 建立 FPSUnthrottler 實體,直接呼叫 activate 啟動
  * 
  * 或是
  * 
  * 2. 將 FPSUnthrottler 實體加入到 DisplayList
  *   它會自動偵測目標 FPS 與實際 FPS,決定啟動或是停止
  * 
  * @author Ticore Shih
  */
 public class FPSUnthrottler extends Shape {
  
  
  public function FPSUnthrottler() {
   snd.addEventListener(SampleDataEvent.SAMPLE_DATA, onSameplDataHandler, false, 0, true);
   
   addEventListener(Event.ADDED_TO_STAGE, onAddStageHandler, false, 0, true);
   addEventListener(Event.REMOVED_FROM_STAGE, onRemoveStageHandler, false, 0, true);
  }
  
  
  protected function onAddStageHandler(e:Event):void{
   addEventListener(Event.ENTER_FRAME, onEnterFrameHandler, false, 0, true);
  }
  
  protected function onRemoveStageHandler(e:Event):void{
   removeEventListener(Event.ENTER_FRAME, onEnterFrameHandler, false);
  }
  
  
  protected var recentFrameTimes:Array = [];
  
  protected function onEnterFrameHandler(e:Event):void{
   
   var throttleFrameTime:int = 100;
   var targetFrameTime:int = 1000 / stage.frameRate;
   
   // 原本設定的 FPS 就已經低於 Throttle FPS 了
   if (targetFrameTime > throttleFrameTime - 20) {
    deactivate();
    return;
   }
   
   var currTime:int = getTimer();
   var lastFrameTime:int = currTime - recentFrameTimes[0];
   
   recentFrameTimes.unshift(currTime);
   var maxSampleLen:int = 300;
   var sampleLen:int = recentFrameTimes.length = Math.min(recentFrameTimes.length, maxSampleLen);
   var avgFrameTimeTotal:int = (recentFrameTimes[0] - recentFrameTimes[sampleLen - 1]) / sampleLen;
   
   // trace("FrameTimes:", targetFrameTime, lastFrameTime, avgFrameTimeTotal);
   
   if (lastFrameTime > throttleFrameTime) {
    // 最後一次影格事件突然小於 2 FPS
    activate();
   } else if (avgFrameTimeTotal <= targetFrameTime) {
    // 連續平均影格事件 FPS 小於等於目標 FPS
    deactivate();
   }
  }
  
  
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
  
  protected var snd:Sound = new Sound();
  protected var sndCh:SoundChannel;
  
  protected function onSameplDataHandler(e:SampleDataEvent):void{
   e.data.position = e.data.length = 4096 * 4;
  }
  
  
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
  
  public function activate():void{
   if (sndCh) return;
   trace("FPSUnthrottler.activate();");
   sndCh = snd.play();
  }
  
  
  public function deactivate():void{
   if (!sndCh) return;
   trace("FPSUnthrottler.deactivate();");
   sndCh.stop();
   sndCh = null;
  }
  
  
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 }
}

以上的技巧,大略在 Windows 7 + Chrome + Flash Player 11.1 測試過 OK
假如其它環境有問題,也請告知一下

除此之外,好像還可以用 Flash Player mm.cfg 設定方式取消 Throttle Mode
Flash Player 10.1でバックグラウンド動作をとめない方法

FullFramerateWhenInvisible=1

2012/03/28 更新
發現根本不需要花力氣寫入聲音資料
直接把 data 長度拉成 4096 * 4,然後 position 移動到最後面就好了

相關連結:
Flash Player 10.1 維持 8 FPS 小技巧
轉載請註明出處 http://ticore.blogspot.com/2012/03/maintain-fps-throttle-mode.html

本文出自 传播、沟通、分享,转载时请注明出处及相应链接。

本文永久链接: https://www.nickdd.cn/?p=1899

发表评论

您的电子邮箱地址不会被公开。

Ɣ回顶部