2012-05-29 4 views
8

다음은 OCaml의 간단한 게임 루프입니다. 상태가 표시되고 입력이 수신되며 상태가 진행됩니다. 각 루프의 0.025 초 동안 스레드를 지연 시키면 초당 프레임 수는 40이됩니다.OCaml의 스레드 지연 및 키보드 이벤트

main.ml :이 예를 들어

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 기능은 단순히 창에 키 입력을 인쇄합니다.

input.ml : 쉽게 테스트

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

메이크 :

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 $< 

이 대부분 작동하지만, 키가 매우 빠르게 누를 때 프로그램이 오류와 충돌 :

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

나는 그것이 Thread.delay과 (과) 관련이 있습니다. 이 문제의 원인은 무엇이며 지속적인 FPS를 달성하는 가장 좋은 방법은 무엇입니까?

답변

9

무슨 일이 일어나고 있는지 정확히 알 수는 없습니다. Thread.delay의 구현에 따라 달라집니다. 모르겠습니다. 그러나 오류 2는 Unix.EAGAIN이며 이는 커널 리소스의 일시적인 부족을 나타냅니다. 이름에서 알 수 있듯이 Thread.delay를 다시 시도해야 할 것입니다. try ... with을 사용하여 Unix.Unix_error 예외를 잡으면 EAGAIN이 전달되는 것 외에는 다른 오류가 표시되지 않습니다. 방금 메시지를 인쇄하고 계속하면 프로그램이 작동하는 것처럼 보입니다. 적어도 문자를 창에 계속 표시하고 충돌을 일으키지 않습니다. 저는 OS X 10.7 (Lion)에서 일하고 있습니다. 다르게 작동 할 수도 있습니다.

편집

코드와 다른 가능한 문제를 Sys.time() 복귀 프로세서 처리는 실시간 연산을 수행하는 동안 만 증가 시간. 프로세스가 입력을 기다리는 동안 증가하지 않습니다. 즉, 키 누르기 사이에 오랜 시간 동안 기다렸더라도 (항상 당황 스럽습니다) 지연이 항상 호출됩니다. 벽시계 시간을 반환하는 Unix.gettimeofday()을 사용하는 것이 더 좋을 수 있습니다.

좀 더 많은 연구와 테스트 후 편집 2

는, 전체 지연이 일부 이벤트에 의해 중단 된 것을 Unix.EAGAIN 오류가 당신을 말하고 있다고 생각합니다. 귀하의 경우에는 방해 사건은 성격의 도착입니다 (저는 믿습니다). 따라서 전체 시간 동안 기다리려면 Thread.delay() 번의 단일 통화를 루프로 대체해야합니다.

이 당신에게 당신의 주요 코드에 대해 다음과 같은 것을 제공 :

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 
;; 

(당신이 당신의 지연을 할 Unix.select를 사용하는 경우, 당신이 스레드에 대한 의존도를 제거 할 수 있습니다하지만 당신은 그들을 어쨌든해야 할 수도 있습니다. 다른 이유는 오류가 EAGAIN이 아니라 EINTR이라는 점을 제외하면 코드는 동일하게 보입니다.)

+2

Jeffrey가 모든 계정에서 정상입니다. Thread에 대한 정보를 sligthly 더 얻을 수 있습니다.지연 [here] (http://ocamlunix.forge.ocamlcore.org/threads.html#htoc63). –