我想在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)。
+1 - 偉大的觀察 - 我沒有注意到那裏的阻塞環:( – Carsten 2014-09-08 07:16:00