2014-09-03 60 views
4

我想在F#中使用Observables編寫一個基本的「遊戲循環」。基本上,我概念化了事件的基本輸入流,因爲兩條流合併在一起:用戶的按鍵(遊戲只用鍵盤開始)和遊戲的常規滴答(例如每秒60次)。爲什麼訂單在Observable.merge的這種用法中很重要?

我的問題似乎源於這樣一個事實,即所觀察的序列之一即滴答,也是在窗口上調用DispatchEvents()以允許其處理其輸入並激發按鍵事件的循環,因此一個流事件實際上是由另一方驅動的,如果這是有道理的。下面是代碼:

open System; 
open System.IO 
open SFML.Window 
open SFML.Graphics 
open System.Reactive 
open System.Reactive.Linq 
open System.Diagnostics 

type InputEvent = 
| Tick of TimeSpan 
| KeyPressed of Keyboard.Key 

[<EntryPoint;STAThread>] 
let main _ = 

    use window = new RenderWindow(VideoMode(640u, 480u), "GameWindow") 
    window.SetVerticalSyncEnabled(true) 

    let displayStream = 
     Observable.Create(
      fun (observer:IObserver<TimeSpan>) -> 
       let sw = Stopwatch.StartNew() 
       while (window.IsOpen()) do 
        window.DispatchEvents() // this calls the KeyPressed event synchronously 
        window.Display() // this blocks until the next vertical sync 
        window.Clear() 
        observer.OnNext sw.Elapsed 
        sw.Restart() 
       observer.OnCompleted(); 
       { new IDisposable with member this.Dispose() =()})  

    let onDisplay elapsedTime = 
     // draw game: code elided 

    let inputEvents = Observable.merge 
          (window.KeyPressed |> Observable.map (fun key -> KeyPressed(key.Code))) 
          (displayStream |> Observable.map (fun t -> Tick(t))) 
    use subscription = 
     inputEvents.Subscribe(fun inputEvent -> match inputEvent with 
               | Tick(t) -> onDisplay(t) 
               | KeyPressed(key) -> printfn "%A" key) 

    0 

然而這工作,如果我改變參數的順序Observable.merge:

let inputEvents = Observable.merge 
          (displayStream |> Observable.map (fun t -> Tick(t))) 
          (window.KeyPressed |> Observable.map (fun key -> KeyPressed(key.Code))) 

隨後的比賽呈現(onDisplay的叫法),但我不」 t請參閱打印到控制檯的KeyPressed事件。這是爲什麼?

(如果你想知道什麼是SFML,這裏是link)。

回答

6

在僞代碼,什麼合併所做的是:

firstStream.Subscribe(...); 
secondStream.Subscribe(...); 

傳遞給Observable.create的訂閱功能是同步的,從來沒有得到控制返回給調用者。這意味着merge本身被阻止嘗試訂閱displayStream之後的任何流。當您重新排列流以便displayStream爲首位時,您可以防止其訂閱KeyPressed流。這就是爲什麼你看到你看到的行爲。

在某些方面,您的displayStream表現不佳。 Subscribe方法不應該阻塞。

因此,要麼確保displayStream是列表中的最後一項,要麼對代碼進行一些重構。您可以使用Subject代替displayStream。然後訂閱所有內容,最後啓動「顯示循環」,在此執行目前在您的displayStream定義中的循環,並且每次循環時只需調用OnNext就可以瞭解該主題。

+0

+1 - 偉大的觀察 - 我沒有注意到那裏的阻塞環:( – Carsten 2014-09-08 07:16:00

相關問題