簡単なゲームをOCamlで作ります。
この記事はML Advent Calendar 2014の5日目の記事です。
そういうことで(どういう事だ?)、OCamlでゲームを作ってみました。 ボス的な動きを作り込もうという事で、色々弄ってみてたってだけですけど。
ビルド方法
$ ocamlopt -I `ocamlfind query lablgl` lablgl.cmxa lablglut.cmxa d.ml -o d $ ./d
lablglとlablglutがインストールされていれば、上のように書いていろいろできます。
遊び方
let enemy = Enemy.new_ () let enemy = Boss.new_ () let enemy = Enemy3.new_ () let enemy = Boss2.new_ () let enemy = Boss3.new_ ()
ソースのこの辺を弄ると、表示される敵が変わります。
ソースコード
d.ml
open Printf let width = 400.0 let height = 400.0 let pi = 4.0 *. (atan 1.0) let normalize v = v +. ((2.0 *. pi) *. (if v > pi then (-1.) else if v < (-. pi) then 1. else 0.)) type state = | Title | GameOver | Game let state = ref Title let gstring str x y size = let getChar t1' = match t1' with | '0' -> [ "0000000 "; "0 0 "; "0 0 0 "; "0 0 "; "0 0 "; "0000000 " ] | '1' -> [ " 0 "; " 0 "; " 0 "; " 0 "; " 0 "; " 0 " ] | '2' -> [ "0000000 "; " 0 "; "0000000 "; "0 "; "0 "; "0000000 " ] | '3' -> [ "0000000 "; " 0 "; "0000000 "; " 0 "; " 0 "; "0000000 " ] | '4' -> [ "0 0 "; "0 0 "; "0000000 "; " 0 "; " 0 "; " 0 " ] | '5' -> [ "0000000 "; "0 "; "0000000 "; " 0 "; " 0 "; "0000000 " ] | '6' -> [ "0000000 "; "0 "; "0000000 "; "0 0 "; "0 0 "; "0000000 " ] | '7' -> [ "0000000 "; " 0 "; " 0 "; " 0 "; " 0 "; " 0 " ] | '8' -> [ "0000000 "; "0 0 "; "0000000 "; "0 0 "; "0 0 "; "0000000 " ] | '9' -> [ "0000000 "; "0 0 "; "0000000 "; " 0 "; " 0 "; " 0 " ] | 'a' -> [ " 0000 "; " 0 0 "; " 000000 "; "0 0 "; "0 0 "; "0 0 " ] | 'b' -> [ "000000 "; "0 0 "; "000000 "; "0 0 "; "0 0 "; "000000 " ] | 'c' -> [ " 00000 "; " 0 "; "0 "; "0 0 "; "0 0 "; " 00000 " ] | 'd' -> [ "000000 "; "0 0 "; "0 0 "; "0 0 "; "0 0 "; "00000 " ] | 'e' -> [ "0000000 "; "0 "; "000000 "; "0 "; "0 "; "0000000 " ] | 'f' -> [ "0000000 "; "0 "; "000000 "; "0 "; "0 "; "0 " ] | 'g' -> [ " 00000 "; " 0 "; "0 0000 "; "0 0 "; "0 0 "; " 00000 " ] | 'h' -> [ "0 0 "; "0 0 "; "0000000 "; "0 0 "; "0 0 "; "0 0 " ] | 'i' -> [ " 00000 "; " 0 "; " 0 "; " 0 "; " 0 "; " 00000 " ] | 'j' -> [ " 000 "; " 0 "; " 0 "; " 0 "; "0 0 "; " 0000 " ] | 'k' -> [ "0 00 "; "0 00 "; "000 "; "0 0 "; "0 0 "; "0 00 " ] | 'l' -> [ "0 "; "0 "; "0 "; "0 "; "0 "; "0000000 " ] | 'm' -> [ "0 0 "; "000 000 "; "0 0 0 "; "0 0 "; "0 0 "; "0 0 " ] | 'n' -> [ "000 0 "; "0 0 0 "; "0 0 0 "; "0 00 "; "0 0 "; "0 0 " ] | 'o' -> [ " 00000 "; "0 0 "; "0 0 "; "0 0 "; "0 0 "; " 0000 " ] | 'p' -> [ "0000000 "; "0 0 "; "000000 "; "0 "; "0 "; "0 " ] | 'q' -> [ " 00000 "; "0 0 "; "0 0 "; "0 0 0 "; "0 0 "; " 0000 0 " ] | 'r' -> [ "0000000 "; "0 0 "; "000000 "; "0 0 "; "0 0 "; "0 0 " ] | 's' -> [ " 000000 "; "0 "; "000000 "; " 0 "; "0 0 "; " 0000 " ] | 't' -> [ "0000000 "; "0 0 0 "; " 0 "; " 0 "; " 0 "; " 00000 " ] | 'u' -> [ "0 0 "; "0 0 "; "0 0 "; "0 0 "; "0 0 "; " 0000 " ] | 'v' -> [ "0 0 "; "0 0 "; "0 0 "; "0 0 "; "0 0 "; "000 " ] | 'w' -> [ "0 0 "; "0 0 "; "0 0 0 "; "0 0 0 0 "; "00 00 "; "0 0 " ] | 'x' -> [ "00 00 "; " 0 0 "; " 0 "; " 0 0 "; " 0 0 "; "0 0 " ] | 'y' -> [ "00 00 "; " 0 0 "; " 0 "; " 0 "; " 0 "; " 000 " ] | 'z' -> [ "0000000 "; " 0 "; " 00000 "; " 0 "; " 0 "; "0000000 " ] | _ -> [ " "; " "; " "; " "; " "; " " ] in let gchar c x y size = ignore (List.fold_left (fun t1' t2' -> match (t1', t2') with | (n, s) -> let num = ref 0 in (String.iteri (fun t1' t2' -> match (t1', t2') with | (i, c) -> if c = '0' then incr num else if !num > 0 then (let i = float_of_int (x + (i * size)) in let n = float_of_int (y + (n * size)) in let s = float_of_int size in (GlDraw.rect ((i -. ((float_of_int (!num - 1)) *. s)), n) ((i +. s), (n +. s)); num := 0)) else ()) s; n + 1)) 0 (getChar c)) in String.iteri (fun t1' t2' -> match (t1', t2') with | (i, c) -> gchar c ((i * size) * 7) size (size - 1)) str module Key = struct let up = ref false let down = ref false let left = ref false let right = ref false let z = ref false let tz = ref false let onSpKeyDown ~key ~x ~y = match key with | Glut.KEY_UP -> up := true | Glut.KEY_DOWN -> down := true | Glut.KEY_LEFT -> left := true | Glut.KEY_RIGHT -> right := true | _ -> () let onSpKeyUp ~key ~x ~y = match key with | Glut.KEY_UP -> up := false | Glut.KEY_DOWN -> down := false | Glut.KEY_LEFT -> left := false | Glut.KEY_RIGHT -> right := false | _ -> () let onKeyDown ~key ~x ~y = match char_of_int key with | 'z' -> (tz := true; z := true) | _ -> () let onKeyUp ~key ~x ~y = match char_of_int key with | 'z' -> z := false | _ -> () let reset () = tz := false let init () = (Glut.specialFunc onSpKeyDown; Glut.specialUpFunc onSpKeyUp; Glut.keyboardFunc onKeyDown; Glut.keyboardUpFunc onKeyUp) end type actor = | BG | Enemy of (((actor -> actor) ref) * (float ref) * (float ref)) | Enemy2 of (((actor -> actor) ref) * (float ref) * (float ref) * ((int ref) * (float ref))) | Enemy3 of (((actor -> actor) ref) * (float ref) * (float ref) * ((int ref) * (float ref))) | Child of ((float ref) * (float ref)) | Boss of (((actor -> actor) ref) * (float ref) * (float ref) * ((float ref) * (((float * float) list) ref) * ((actor list) ref))) | Boss2 of (((actor -> actor) ref) * (float ref) * (float ref) * ((float ref) * (((float * float) list) ref) * ((actor list) ref))) | Child3 of ((float ref) * (float ref)) | Boss3 of (((actor -> actor) ref) * (float ref) * (float ref) * ((float ref) * (((float * float) list) ref) * ((actor list) ref))) module Actor = struct let move e = match e with | BG -> (printf "BG\n"; e) | Enemy (m, _, _) -> !m e | Enemy2 (m, _, _, _) -> !m e | Enemy3 (m, _, _, _) -> !m e | Child (_, _) -> e | Child3 (_, _) -> e | Boss (m, _, _, _) | Boss2 (m, _, _, _) -> !m e | Boss3 (m, _, _, _) -> !m e let rec draw e = match e with | BG -> () | Enemy (m, x, y) -> (GlDraw.color (1.0, 0.0, 0.0); let rsize = 10. in GlDraw.rect ((!x), (!y)) ((!x +. rsize), (!y +. rsize))) | Child (x, y) -> (GlDraw.color (1.0, 0.0, 0.0); let rsize = 10. in GlDraw.rect ((!x -. rsize), (!y -. rsize)) ((!x +. rsize), (!y +. rsize))) | Enemy2 (m, x, y, _) | Enemy3 (m, x, y, _) -> (GlDraw.color (1.0, 0.0, 0.0); let rsize = 10. in GlDraw.rect ((!x), (!y)) ((!x +. rsize), (!y +. rsize))) | Boss (m, x, y, (cnt, _, cs)) -> (GlDraw.color (1.0, 0.0, 0.0); let rsize = 10. in (GlDraw.rect ((!x -. rsize), (!y -. rsize)) ((!x +. rsize), (!y +. rsize)); List.iter (fun t1' -> match t1' with | c -> draw c) !cs)) | Boss2 (m, x, y, (cnt, _, cs)) -> (GlDraw.color (1.0, 0.0, 0.0); let rsize = 20. in (GlDraw.rect ((!x -. rsize), (!y -. rsize)) ((!x +. rsize), (!y +. rsize)); List.iter (fun t1' -> match t1' with | c -> draw c) !cs)) | Child3 (x, y) -> (GlDraw.color (1.0, 0.0, 0.0); let rsize = 15. in GlDraw.rect ((!x -. rsize), (!y -. rsize)) ((!x +. rsize), (!y +. rsize))) | Boss3 (m, x, y, (cnt, _, cs)) -> (GlDraw.color (0.7, 0.7, 0.7); let h = 10. in let w = 240. in (GlDraw.rect ((!x -. w), (!y -. h)) ((!x +. w), (!y +. h)); GlDraw.color (0.8, 0.8, 0.8); let h = 50. in let w = 40. in (List.iter (fun t1' -> match t1' with | Child3 (x, y) -> GlDraw.rect ((!x -. w), (!y -. h)) ((!x +. w), (!y +. h))) !cs; List.iter (fun t1' -> match t1' with | (Child3 (x, y) as c) -> draw c) !cs))) end module BG = struct let new_ () = BG end module Enemy = struct let rec new_ () = Enemy ((ref move1), (ref 0.), (ref 0.)) and move1 e = match e with | Enemy (m, x, y) -> (x := !x +. 5.; if !x > height then m := move2 else (); e) | _ -> assert false and move2 e = match e with | Enemy (m, x, y) -> (x := !x -. 5.; if !x <= 0. then m := move1 else (); e) | _ -> assert false end module Child = struct let rec new_ x y = Child ((ref x), (ref y)) end module Boss = struct let poses = ref let rec new_ () = let rec list_make t1' t2' t3' = match (t1', t2', t3') with | (0, ls, v) -> ls | (n, ls, v) -> list_make (n - 1) ((v ()) :: ls) v in let n = 10 in let childs = list_make n [] (fun t1' -> match t1' with | () -> Child.new_ 10. 10.) in let lasts = list_make (3 * n) [] (fun t1' -> match t1' with | () -> (10., 10.)) in Boss ((ref move1), (ref 10.), (ref 10.), ((ref 0.), (ref lasts), (ref childs))) and moveChilds ls childs = let _ = List.fold_left (fun t1' t2' -> match (t1', t2') with | (n, Child (x, y)) -> let _ = (match List.nth ls n with | (x1, y1) -> (x := x1; y := y1)) in n + 3 | _ -> assert false) 0 childs in () and move1 this = match this with | Boss (m, x, y, (c, ls, childs)) -> (c := !c +. 0.1; ls := (List.tl !ls) @ [ ((!x), (!y)) ]; x := !x +. 5.; y := !y +. ((sin !c) *. 10.); if !x > (width -. 20.) then m := move2 else (); moveChilds !ls !childs; this) | _ -> assert false and move2 this = match this with | Boss (m, x, y, (c, ls, childs)) -> (c := !c +. 0.1; ls := (List.tl !ls) @ [ ((!x), (!y)) ]; x := !x -. 5.; y := !y +. ((sin !c) *. 10.); if !x <= 10. then m := move1 else (); moveChilds !ls !childs; this) | _ -> assert false end module Shots = struct let shots = ref [] let add x y = if (List.length !shots) < 16 then shots := (x, y) :: !shots else () let move () = let ss = List.map (fun t1' -> match t1' with | (x, y) -> (x, (y -. 20.))) !shots in shots := List.filter (fun t1' -> match t1' with | (x, y) -> y > 0.) ss let draw () = let rsize = 2.0 in (GlDraw.color (1.0, 1.0, 0.0); List.iter (fun t1' -> match t1' with | (x, y) -> GlDraw.rect ((x -. rsize), (y -. rsize)) ((x +. rsize), (y +. rsize))) !shots) end module Child3 = struct let rec new_ x y = Child3 ((ref x), (ref y)) end module Boss3 = struct let rec new_ () = let lasts = [ ((-80.), (-20.)); ((-30.), 20.); (30., 20.); (80., (-20.)) ] in let n = 3 in let childs = [ Child3.new_ (-0.) 0.; Child3.new_ 0. 0.; Child3.new_ 0. 0.; Child3.new_ 0. 0. ] in Boss3 ((ref move1), (ref 10.), (ref 10.), ((ref 0.), (ref lasts), (ref childs))) and moveChilds xa ya ls childs = let _ = List.fold_left (fun t1' t2' -> match (t1', t2') with | (n, Child (x, y)) -> let _ = (match List.nth ls n with | (x1, y1) -> (x := xa +. x1; y := ya +. y1)) in n + 1 | (n, Child3 (x, y)) -> let _ = (match List.nth ls n with | (x1, y1) -> (x := xa +. x1; y := ya +. y1)) in n + 1 | _ -> assert false) 0 childs in () and move1 this = match this with | Boss3 (m, x, y, (c, ls, childs)) -> (c := !c +. 0.1; x := !x +. 5.; y := !y +. ((sin !c) *. 10.); if !x > (width -. 20.) then m := move2 else (); moveChilds !x !y !ls !childs; this) | _ -> assert false and move2 this = match this with | Boss3 (m, x, y, (c, ls, childs)) -> (c := !c +. 0.1; x := !x -. 5.; y := !y +. ((sin !c) *. 10.); if !x <= 10. then m := move1 else (); moveChilds !x !y !ls !childs; this) | _ -> assert false end module Ship = struct let rsize = 25. let x = ref ((width -. rsize) /. 2.) let y = ref (height -. (rsize *. 2.0)) let speed = 5. let bllets = ref let move () = let m = 5 in let m = if !Key.left then m - 1 else m in let m = if !Key.right then m + 1 else m in let m = if !Key.up then m - 3 else m in let m = if !Key.down then m + 3 else m in (if !Key.z then Shots.add !x !y else (); Shots.move (); let speed = match m with | 1 | 3 | 7 | 9 -> speed /. 1.4 | _ -> speed in let (nx, ny) = match m with | 1 -> ((!x -. speed), (!y -. speed)) | 2 -> ((!x), (!y -. speed)) | 3 -> ((!x +. speed), (!y -. speed)) | 4 -> ((!x -. speed), (!y)) | 5 -> ((!x), (!y)) | 6 -> ((!x +. speed), (!y)) | 7 -> ((!x -. speed), (!y +. speed)) | 8 -> ((!x), (!y +. speed)) | 9 -> ((!x +. speed), (!y +. speed)) | _ -> ((!x), (!y)) in (x := min (max nx 0.) (width -. rsize); y := min (max ny 0.) (height -. rsize))) let draw () = (GlDraw.rect ((!x -. (rsize /. 2.)), (!y -. (rsize /. 2.))) ((!x +. (rsize /. 2.)), (!y +. (rsize /. 2.))); Shots.draw ()) end module Boss2 = struct let speed = 3.0 let r = 0.025 let span = 15 let fabs a = if a < 0. then -. a else a let poses = ref let move m rad x y x1 y1 next = let rad2 = atan2 (y1 -. !y) (x1 -. !x) in let rad2 = if (normalize (!rad -. rad2)) <= 0. then !rad +. r else !rad -. r in let rad2 = normalize rad2 in (x := !x +. ((cos rad2) *. speed); y := !y +. ((sin rad2) *. speed); if ((fabs (!x -. x1)) < 10.) && ((fabs (!y -. y1)) < 10.) then m := next else (); rad := rad2) let rec new_ () = let rec list_make t1' t2' t3' = match (t1', t2', t3') with | (0, ls, v) -> ls | (n, ls, v) -> list_make (n - 1) ((v ()) :: ls) v in let x = 200. in let y = (-10.) in let n = 14 in let childs = list_make n [] (fun t1' -> match t1' with | () -> Child.new_ x y) in let lasts = list_make (span * n) [] (fun t1' -> match t1' with | () -> (x, y)) in Boss2 ((ref move1), (ref x), (ref y), ((ref 0.), (ref lasts), (ref childs))) and moveChilds ls childs = let _ = List.fold_left (fun t1' t2' -> match (t1', t2') with | (n, Child (x, y)) -> let _ = (match List.nth ls n with | (x1, y1) -> (x := x1; y := y1)) in n + span | _ -> assert false) 0 childs in () and move1 this = match this with | Boss2 (m, x, y, (rad, ls, childs)) -> (ls := (List.tl !ls) @ [ ((!x), (!y)) ]; move m rad x y 20. 380. move2; moveChilds !ls !childs; this) | _ -> assert false and move2 this = match this with | Boss2 (m, x, y, (rad, ls, childs)) -> (ls := (List.tl !ls) @ [ ((!x), (!y)) ]; move m rad x y !Ship.x !Ship.y move1; moveChilds !ls !childs; this) | _ -> assert false end module Enemy3 = struct let speed = 6.0 let r = 0.15 let fabs a = if a < 0. then -. a else a let move m rad cnt mx x y x1 y1 next = let rad2 = atan2 (y1 -. !y) (x1 -. !x) in let rad2 = if (normalize (!rad -. rad2)) <= 0. then !rad +. r else !rad -. r in let rad2 = normalize rad2 in (x := !x +. ((cos rad2) *. speed); y := !y +. ((sin rad2) *. speed); cnt := !cnt + 1; if (!cnt > (mx * 10)) || (((fabs (!x -. x1)) < 10.) && ((fabs (!y -. y1)) < 10.)) then (cnt := 0; m := next) else (); rad := rad2) let rec new_ () = Enemy3 ((ref move1), (ref 0.), (ref 300.), ((ref 0), (ref 0.))) and move1 e = match e with | Enemy3 (m, x, y, (cnt, rad)) -> (move m rad cnt 60 x y 310. 30. move2; e) | _ -> assert false and move2 e = match e with | Enemy3 (m, x, y, (cnt, rad)) -> (move m rad cnt 50 x y 200. 300. move3; e) | _ -> assert false and move3 e = match e with | Enemy3 (m, x, y, (cnt, rad)) -> (move m rad cnt 50 x y (-20.) (-20.) move4; e) | _ -> assert false and move4 e = match e with | Enemy3 (m, x, y, (cnt, rad)) -> (x := 0.; y := 300.; rad := 0.; cnt := 0; m := move1; e) | _ -> assert false end module Enemy2 = struct let speed = 6.0 let r = 0.15 let fabs a = if a < 0. then -. a else a let move m rad cnt mx x y x1 y1 next = let rad2 = atan2 (y1 -. !y) (x1 -. !x) in let rad2 = if (normalize (!rad -. rad2)) <= 0. then !rad +. r else !rad -. r in let rad2 = normalize rad2 in (x := !x +. ((cos rad2) *. speed); y := !y +. ((sin rad2) *. speed); cnt := !cnt + 1; if (!cnt > (mx * 10)) || (((fabs (!x -. x1)) < 10.) && ((fabs (!y -. y1)) < 10.)) then (cnt := 0; m := next) else (); rad := rad2) let rec new_ () = Enemy2 ((ref move1), (ref 0.), (ref (-30.)), ((ref 0), (ref 0.))) and move1 e = match e with | Enemy2 (m, x, y, (cnt, rad)) -> (move m rad cnt 60 x y 10. 350. move2; e) | _ -> assert false and move2 e = match e with | Enemy2 (m, x, y, (cnt, rad)) -> (move m rad cnt 50 x y 300. 30. move3; e) | _ -> assert false and move3 e = match e with | Enemy2 (m, x, y, (cnt, rad)) -> (move m rad cnt 50 x y 300. 400. move4; e) | _ -> assert false and move4 e = match e with | Enemy2 (m, x, y, (cnt, rad)) -> (x := 0.; y := (-30.); rad := 0.; cnt := 0; m := move1; e) | _ -> assert false end module Game = struct let enemy = Enemy.new_ () let enemy = Boss.new_ () let enemy = Enemy3.new_ () let enemy = Boss2.new_ () let enemy = Boss3.new_ () let draw () = (GlClear.clear [ `color ]; GlDraw.color (1.0, 0.0, 0.0); (match !state with | Game -> (Actor.draw enemy; Ship.draw ()) | Title -> gstring "push z" 0. 0. 6 | _ -> ()); Glut.swapBuffers ()) let rec move ~value = (Glut.postRedisplay (); (match !state with | Title -> if !Key.z then state := Game else () | _ -> (Ship.move (); let _ = Actor.move enemy in ())); Glut.timerFunc ~ms: 15 ~cb: move ~value: 1) let resize ~w ~h = let h = max h 1 in (GlDraw.viewport ~x: 0 ~y: 0 ~w: w ~h: h; GlMat.mode `projection; GlMat.load_identity (); let ortho = GlMat.ortho ~z: (1.0, (-1.0)) in let x = (0.0, width) in let y = (height, (-0.0)) in (ortho ~x: x ~y: y; GlMat.mode `modelview; GlMat.load_identity ())) let main = let _ = Glut.init Sys.argv in (Glut.initDisplayMode ~double_buffer: true ~alpha: true (); Glut.initWindowSize ~w: (int_of_float width) ~h: (int_of_float height); let _ = Glut.createWindow "Game01" in (Glut.displayFunc draw; Glut.reshapeFunc resize; Key.init (); Glut.timerFunc ~ms: 33 ~cb: move ~value: 1; GlClear.color ~alpha: 1.0 (0.1, 0.1, 0.1); Glut.mainLoop ())) end
明日は、masquerade0324さんのSMLの記事です。