2012-05-29 20 views
8

İşte size OCaml'deki basit bir oyun döngüsü. Durum görüntülenir, giriş alınır ve durum ilerletilir. Saniyedeki kare sayısı, her döngüde 0,025 saniye boyunca iplik geciktirilerek 40'a düşürülür.OCaml'de gösterim gecikmesi ve klavye olayları

main.ml: Bu örnek için

let rec main (* state *) frame_time = 
    (* Display state here. *) 
    Input.get_input(); 
    (* Advance state by one frame here. *) 
    (* If less than 25ms have passed, delay until they have. *) 
    if((Sys.time()) < (frame_time +. 0.025)) then 
    Thread.delay ((frame_time +. 0.025) -. (Sys.time())); 
    main (* next_state *) (Sys.time()) 
;; 

let init = 
    Graphics.open_graph " 800x500"; 
    let start_time = (Sys.time()) in 
    main (* start_state *) start_time 
;; 

, get_input fonksiyon sadece pencereye tuş vuruşlarını yazdırır.

input.ml: Kolay test için

let get_input() = 
    let s = Graphics.wait_next_event 
    [Graphics.Key_pressed] in 
    if s.Graphics.keypressed then 
    Graphics.draw_char s.Graphics.key 
;; 

Makefile:

main: input.cmo main.cmo 
    ocamlfind ocamlc -o [email protected] unix.cma -thread threads.cma graphics.cma $^ 
main.cmo: main.ml 
    ocamlfind ocamlc -c $< -thread 
input.cmo: input.ml 
    ocamlfind ocamlc -c $< 

Bu çoğunlukla çalışır, ancak tuşlar çok hızlı basıldığında, program, bu hata ile çöker:

Fatal error: exception Unix.Unix_error(2, "select", "")

Thread.delay ile ilgili. Bu soruna neden olan nedir ve sürekli bir FPS elde etmenin en iyi yolu nedir?

cevap

9

Neler olup bittiğinden tam olarak emin değilim (bilmiyorum Thread.delay uygulamasına bağlı). Ancak, 2. hata, çekirdek kaynaklarının geçici bir eksikliğini temsil eden Unix.EAGAIN'dur. Adından da anlaşılacağı gibi, muhtemelen sadece Thread.delay'ınızı yapmaya çalışmalısınız. Unix.Unix_error istisnasını yakalamak için try ... with kullanırsam EAGAIN teslim edilmek dışında başka bir hata göremiyorum. Bir mesaj yazıp devam edersem, program işe yarıyor gibi görünüyor. En azından, pencereye karakterleri yankılamaya devam ediyor ve çökmez. OS X 10.7'de (Aslan) çalışıyorum. Senin için farklı çalışabilir.

Düzenleme

bu kodla Başka bir olası sorun olduğunu Sys.time() döner işlemci süreci gerçek hesaplama yapıyor iken sadece artar sefer. İşlem giriş için beklerken artmaz. Bu, tuşa basma arasında uzun bir süre bekleseniz bile gecikmenin her zaman çağrıldığı anlamına gelir (bir süre kafamı karıştırıyordu). Duvar saati zamanını döndüren Unix.gettimeofday() kullanmak daha iyi olabilir.

biraz daha araştırma ve testlerden sonra Düzen 2

, tam gecikme bazı olay ile kesintiye olduğunu Unix.EAGAIN hata söylüyorum olduğuna inanıyoruz. Sizin durumunuzda kesinti olayı bir karakterin gelişidir (inanıyorum). Tam zamanı beklemek istiyorsanız, tek aramayı bir döngü ile Thread.delay()'a değiştirmeniz gerekir.

Bu size ana kodu için aşağıdaki gibi bir şey veriyor:

let rec main (* state *) frame_time = 
    (* Display state here. *) 
    Input.get_input(); 
    (* Advance state by one frame here. *) 
    (* If less than 25ms have passed, delay until they have. *) 
    let rec delay() = 
    let duration = frame_time +. 0.025 -. Unix.gettimeofday() in 
    if duration > 0.0 then 
     try 
     Thread.delay duration 
     with Unix.Unix_error (Unix.EAGAIN, _, _) -> delay() 
    in 
    delay(); 
    main (* next_state *) (Unix.gettimeofday ()) 
;; 

let init = 
    Graphics.open_graph " 800x500"; 
    let start_time = (Unix.gettimeofday ()) in 
    main (* start_state *) start_time 
;; 

(size geciktirme yapmak Unix.select kullanırsanız, iş parçacığı olan bağımlılığı kaldırabilir Ama onları yine de gerekebilir. Diğer nedenler: Kod, EAGAIN yerine EINTR dışında aynı görünecektir.)

+2

Jeffrey tüm hesaplarda haklıdır. Konu hakkında daha fazla bilgi alabilirsiniz.Gecikme [burada] (http://ocamlunix.forge.ocamlcore.org/threads.html#htoc63). –