2011-10-28 43 views
8

有沒有辦法在Flash上​​本機啓用跳幀?Flash上​​的幀跳過

當您開發遊戲時,您會開發動畫以匹配遊戲玩法的速度,並以目標幀率(通常爲Flash的24-40fps)進行動畫製作。但是如果用戶的電腦速度太慢,並且無法保持目標幀速率,Flash會自動降低fps,使應用程序播放緩慢。

如果您使用基於時間的邏輯,則基於幀的渲染將不匹配基於時間的邏輯,而且事情會很奇怪,並且會有很多拐角情況需要解決。

事實上,我知道有些遊戲使用這種跳幀邏輯,比如Popcap的Zuma Blitz。他們是在自己實現跳幀嗎?

我不能在項目中實現這麼晚,除非我能夠以某種方式重新實現MovieClip類,並使其可以輕鬆進行框架滑動。另外,不會自己控制動畫的開銷(覆蓋Flash本機MovieClip控件)的開銷太多了嗎?

+0

「我的遊戲在屏幕上有600多個複雜的影片剪輯,只有100個附加到可以直接控制它的as3類。」您的問題是,一旦將對象添加到舞臺上,您就無法找到對象的引用,或者gotoAndStop()會破壞藝術家放置的時間線代碼?如果您需要跳過x個幀並保持時間軸代碼的完整性(您沒有測試過這個atm,但是對工作流程不夠了解,您可能會檢查是否已經過了太多時間,並且可能會檢查gotoAndPlay(currentframe + x)肯定地說一種或另一種) –

回答

3

好吧,我知道你不是在尋找以下「解決方案」,這是行不通的,因爲所有的幀都將仍然播放:

stage.frameRate = new_frame_rate; 

唯一的解決方法就是跳幀在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... 
} 

(假定,以達到最佳效果,你在一個單獨的回調集中的遊戲邏輯/動畫),但是當任何異步回調的運行可能會出現競爭條件/併發症。

沒有辦法在運行時級別實際跳過幀;這是AVM合同的一部分 - 不會跳幀,所有代碼都會運行,無論如何。如果性能問題,您可以嘗試異步代碼執行。有幾種方法可以做到這一點:

1)將一些計算卸載到Pixel Bender,以便它可以異步和/或並行處理(http://www.adobe.com/devnet/flex/)文章/ flashbuilder4_pixelbender.html)

2)展開冗長的操作執行多個幀(需要狀態保存和狀態恢復,lamento模式)。詳情(以及精彩閱讀)在這裏:http://www.senocular.com/flash/tutorials/asyncoperations/

在任何情況下,我都會(首先和最重要的)推薦使用類似Flash Builder Profiler或Stats類(Doob先生)的工具來確定性能瓶頸。

Blitting可能是解決方案(爲動畫的每個幀創建一個包含tile的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); 
    } 

    // .... 
} 

凡跳幀監聽器會根據當前幀速率或幀時間跳幀,如果需要的話。當然,你還需要在動畫結束時刪除frameSkipListener。

雖然MovieClip覆蓋解決方案可能會在紙上做到您想要的,但如果您有很多對象,則可能會降低性能,因爲額外的ENTER_FRAME偵聽器會增加一些開銷。

+0

「無法在運行時級別實際跳過幀;這是AVM合同的一部分 - 不會跳過幀,所有代碼都會運行,無論如何。」非常有趣,你能找到我說可靠的鏈接嗎? 我似乎無法在這裏找到它http://www.adobe.com/content/dam/Adobe/en/devnet/actionscript/articles/avm2overview.pdf – felipemaia

+0

當然可以 - (半)着名的文章( http://ted.onflash.org/2005/07/flash-player-mental-model-elastic。PHP和AVM2特定的更新在這裏:http://ted.onflash.org/2008/04/flash-player-mental-model-elastic.php)比較Flash和彈性跑道說: 「在一個意識到玩家永遠不會失去它,它會一直做你所要求的所有工作,永遠不會延遲到一個較晚的幀週期。「 至於其可靠性/可信度,請注意,本文直接引用Adobe的「爲Adobe Flash平臺優化性能」文檔,這也是一個很好的閱讀。 –

+0

這些鏈接的TYVM! – felipemaia

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 
     } 

    } 

} 

如果發現低幀率正在影響碰撞計算的準確性,用計時器分別運行它們。這只是繪製循環,不可避免地與幀速率相關聯。

只是你知道,你不能在flash中跳幀。它由玩家處理。最好的方法是調整幀渲染技術以適應可變幀頻。

+0

你好,我知道如何使用自上一幀以來的時間(這就是我所說的基於時間的邏輯對我的問題)的時間來控制遊戲的邏輯。我的問題與控制動畫本身有關(由藝術家製作的動畫片段上的動畫)。 – felipemaia

+0

所以使用gotoAndStop()並使用gotoAndStop(Math.floor(percentToMove * framesPerSecondOfTheMovieClip))計算正確的幀;這樣,您的剪輯將根據時間的流逝轉到合適的幀。在blitting中採用相同的方法可能會在某些情況下提高性能。 –

+0

但是後來我失去了在Flash時間軸上控制動畫的能力,讓藝術家很難自行開發資產,每一項資產都需要擁有自己的控制類。 我的遊戲在屏幕上有600多個複雜的影片剪輯,只有大約100個連接到可直接控制它的as3類。 – felipemaia

2

你最大的問題是你不是blitting。藝術家可能仍然在時間軸上製作動畫並導出精靈表。我會花一些時間認真瞭解圍繞基於Flash播放器時間軸的動畫的性能限制。這裏有幾個鏈接,讓你開始:

正如你可以看到,使用gotoAndStop()從時間軸控制動畫;對於動畫來說,更糟糕的方法是關於你正在使用的精靈數量。

你也可以考慮用新的小題大做2D圖形API的使用,德裕框架

直接解決你的問題,我想第二個解決方案已經討論過,進行緩衝的渲染精靈張在運行時。根據任何時間點所需的精靈數量,您可以將低動畫性能換成內存限制。

祝你好運。

1

我最近碰到類似的東西;在某種程度上,也許以下幫助:

  • gotoAndStop(幀)將立即在框架
  • 執行該動作可以執行多個gotoAndStop一個ENTER_FRAME事件(或任何其他事件)中調用。

作爲測試,我在Flash Pro CS5.5中創建了一個新的AS3.0項目。我創建了一個新的MovieClip並將時間線擴展到了23幀。

在第一幀添加以下代碼:

import flash.events.Event; 

// do something every frame 
this.addEventListener(Event.ENTER_FRAME, handleEnterFrame); 
// let event handler change the playHead 
this.stop(); 

// advance playhead 2 frames every frame 
function handleEnterFrame(anEvent: Event): void 
{ 
    trace('* ENTER_FRAME'); 
    this.gotoNextFrame(); 
    this.gotoNextFrame(); 
} 

// advance to next frame, show playheads position 
function gotoNextFrame(): void 
{ 
    // at last frame? 
    if (this.currentFrame < this.totalFrames) 
    { 
    // no, advance to next frame 
    var before: int = this.currentFrame; 
    this.gotoAndStop(this.currentFrame + 1); 
    trace('before: ' + before + ', current: ' + this.currentFrame); 
    } 
    else 
    { 
    // last frame, stop updating the playhead 
    this.removeEventListener(Event.ENTER_FRAME, handleEnterFrame); 
    } 
} 

在框架5我創建了一個關鍵幀和添加的代碼:

this.gotoAndPlay(10); 

在框架14我創建了一個關鍵幀和添加的代碼:

this.gotoAndPlay(16); 

運行後我得到了以下跟蹤輸出:

* ENTER_FRAME 
before: 1, current: 2 
before: 2, current: 3 
* ENTER_FRAME 
before: 3, current: 4 
before: 4, current: 10 
* ENTER_FRAME 
before: 11, current: 12 
before: 12, current: 13 
* ENTER_FRAME 
before: 13, current: 16 
before: 16, current: 17 
* ENTER_FRAME 
before: 17, current: 18 
before: 18, current: 19 
* ENTER_FRAME 
before: 19, current: 20 
before: 20, current: 21 
* ENTER_FRAME 
before: 21, current: 22 
before: 22, current: 23 
* ENTER_FRAME 

我不知道如何高效或耗時上述雖然。我只能用一個MovieClip來使用它(在開發的後期階段,如果主角也可以運行,它會很方便,但是沒有運行動畫的預算;該角色包含時間軸中的腳步生成腳本以便正確定時)。

在你的情況下,你可以檢查自上一次ENTER_FRAME以來經過了多少時間,然後提前正確的幀數。

您可以創建MovieClip的子類並將其用作基類(快速執行此操作,選擇需要此庫的所有影片剪輯,右鍵單擊,屬性,調整基類)。對於已經鏈接到動作腳本的那些動畫片段,.as文件可以通過從新的MovieClip類擴展來更新。

1

據我所知,沒有什麼內置於AS3中以在給定的時間間隔跳過movieClip幀,或者通過時間軸動畫比正常情況更快地前進。像祖瑪這樣的遊戲可能要麼手動做,要麼開始動畫而不使用時間線。

做手工,你有兩個主要選項:

時間軸選項:(可能不值得的,下面說明理由)如果你真的,絕對依賴於時間軸動畫,更改幀率會造成很大的問題,您沒有時間或技術自由來重做動畫代碼,您可以使用Event.ENTER_FRAME監聽器來調用每個函數用於檢查stage.frameRate的movieClip幀,如果frameRate下降,則跳過下一幀或兩幀。

例如:

var preferredFrameRate = 24; //use whatever your timeline animations are set to 
function skipFramesIfNeeded() { 
    var currentFrameRate = stage.frameRate; 
    var speedDifference = 1 - (currentFrameRate/preferredFrameRate);//value between 0-1 
    if(.5 <= speedDifference < .66) { 
     gotoAndPlay(this.currentFrame+1); 
    } else if(.66 <= speedDifference) { 
     gotoAndPlay(this.currentFrame+2); 
    } 
} 

沒有與做這種方式的幾大問題,但最糟糕的是,你只能跳過整個幀 - 跳過每隔一幀將增加一倍,你的速度,每兩幀跳過將其三倍,等等。所以這不會像使用代碼而不是時間軸來驅動動畫一樣順暢或高效。從每個動畫幀執行代碼也可能進一步減慢速度。

僅提供代碼:如果你能找到時間,那麼採取其他人的建議會更好。將時間線上的動畫關閉,並用代碼驅動它們。保留動畫和轉換速度的設置或乘數,或者每幀有多少像素對象移動。

然後,您可以存儲您的首選frameRate,將其與stage.frameRate進行比較,並相應地調整乘數。這可以使速度更平滑。

例如:

var preferredFrameRate = 24; //or whatever you want this to run at 
var defaultSpeedMultiplier = 1; 
var speedMultiplier = 1; //this is the multiplier we'll update 

function updateAnimationSpeed() { 
    var currentFrameRate = stage.frameRate; // 18, hypothetically 
    var speedDifference = 1 - (currentFrameRate/preferredFrameRate); //.25, or a difference of 25% 
    speedMultiplier = defaultSpeedMultiplier + extraSpeed; 
    //speed multiplier increases from 1 to 1.25 to make up for less frequent frames 
} 

對於藝術家創建的視覺狀態,你可能加載編號組位圖,並在該命令,你會從一幀移動到另一個他們之間切換。在理論上,你也可能只是檢測到變化,並將frameRate改回來,但如果用戶的機器跟不上,Flash可能會忽略它。

1

您可以在舞臺根部的某處創建一個長達100幀的空影片剪輯,放置一個長達十秒鐘的完全無聲的聲音剪輯,並設置聲音的流和循環。

這實際上給你一個Flash中的無障礙可變幀速率,其中每個時間軸和基於代碼的動畫文件中的任何位置將始終運行準確的預期時間,在必要時跳過幀,但仍然執行所有代碼甚至可以從視覺上跳過的幀中查看時間線。這是一個非常簡單且無憂無慮的解決方案,我感到驚訝的是,更多的人似乎並不瞭解這一點。