module Rect : sig type t = { x: int; y: int; w: int; h: int; } val make1 : int * int * int * int -> t val make2 : pos:int * int -> dims:int * int -> t val make4 : int -> int -> int -> int -> t val has_intersection : t -> t -> bool end = struct type t = { x: int; y: int; w: int; h: int; } let make1 (x, y, w, h) = { x; y; w; h } let make2 ~pos:(x, y) ~dims:(w, h) = { x; y; w; h } let make4 x y w h = { x; y; w; h } let has_intersection a b = (* Horizontal intersection *) let a_min = a.x in let a_max = a_min + a.w in let b_min = b.x in let b_max = b_min + b.w in let a_min = if (b_min > a_min) then b_min else a_min in let a_max = if (b_max < a_max) then b_max else a_max in if (a_max <= a_min) then false else (* Vertical intersection *) let a_min = a.y in let a_max = a_min + a.h in let b_min = b.y in let b_max = b_min + b.h in let a_min = if (b_min > a_min) then b_min else a_min in let a_max = if (b_max < a_max) then b_max else a_max in if (a_max <= a_min) then (false) else (true) ;; end let width, height = (24, 16) let tile_size = 32 let initial_pos = (2 * 32, 6 * 32) type pos = int * int type player_dir = { left: bool; right: bool; up: bool; down: bool; } type game_state = { pos: pos; dir: player_dir; blocks: (int * int * int * int * bool) array; y_velocity: int; player_tile: int * int; texture: Canvas.image; mutable texture_loaded: bool; } type key_change = KeyDown | KeyUp let canvas = Canvas.getElementById Canvas.document "my_canvas" let ctx = Canvas.getContext canvas "2d" let red = "#F00" let green = "#0F0" let blue = "#39F" let black = "#000" let player_tiles = [| (144, 176); (160, 176); (144, 192); (160, 192); (144, 208); (160, 208); (144, 224); |] let rand_take ar = let n = Array.length ar in ar.(Random.int n) let fill_rect color (x, y) = Canvas.fillStyle ctx color; Canvas.fillRect ctx x y 20 20; ;; let display_game state = let bg_color = blue in let tex = state.texture in (* background *) Canvas.fillStyle ctx bg_color; Canvas.fillRect ctx 0 0 (width * 32) (height * 32); let draw_tex tex (sx, sy) (sw, sh) (dx, dy) = let dw, dh = (32, 32) in Canvas.drawImage8 ctx tex sx sy sw sh dx dy dw dh; in (* blocks *) Array.iter (fun (x, y, tx, ty, _) -> let dxy = (x * 32, y * 32) in let txy = (tx * 16, ty * 16) in draw_tex tex txy (16, 16) dxy ) state.blocks; (* player *) let s_xy = state.player_tile in draw_tex tex s_xy (16, 16) state.pos; ;; let update_state ( { pos; dir; blocks; y_velocity; player_tile; _ } as state) = let r_blocks = Array.map (fun (x, y, _, _, coll) -> let pos = (x * 32, y * 32) in Rect.make2 ~pos ~dims:(32, 32) , coll ) blocks in let x, y = pos in let y_velocity = if dir.up then (-14) else (y_velocity) in let y_dest = (y + y_velocity) in let next = if y_velocity > 0 then succ else pred in let rec y_loop y = if y = y_dest then (y, y_velocity) else let y_prev = y in let y = next y in let player = Rect.make2 ~pos:(x, y) ~dims:(32, 32) in let has_intersection = Array.fold_left (fun res (r_block, coll) -> if coll then res || Rect.has_intersection player r_block else res ) false r_blocks in if has_intersection then (y_prev, 0) else y_loop y in let y, y_velocity = y_loop y in let x_dest = if dir.left then (x - 6) else if dir.right then (x + 6) else (x) in let next = if x_dest > x then succ else pred in let rec x_loop x = if x = x_dest then (x) else let x_prev = x in let x = next x in let player = Rect.make2 ~pos:(x, y) ~dims:(32, 32) in let has_intersection = Array.fold_left (fun res (r_block, coll) -> if coll then res || Rect.has_intersection player r_block else res ) false r_blocks in if has_intersection then (x_prev) else x_loop x in let x = x_loop x in let y_velocity = y_velocity + 1 in (* let pos = (x, y) in *) let pos, player_tile = if y > 28 * 32 then (initial_pos, rand_take player_tiles) else (x, y), player_tile in let dir = if dir.up then { dir with up = false } else dir in { state with pos; dir; blocks; y_velocity; player_tile; } let blocks = let _, _blocks = Array.fold_left (fun (i, acc) t -> if t = 0 then (succ i, acc) else let x = i mod 24 in let y = i / 24 in let _t = t - 1 in let tx = _t mod 14 in let ty = _t / 14 in let collide = match t with |1|2|3|4|5|6|7|8|9|15|16|17|18|19|20|21|22|23 |29|30|31|32|33|34|35|36|37|45|46|81 -> true | _ -> false in (succ i, (x, y, tx, ty, collide) :: acc) ) (0, []) [| (* 72;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 15;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;106;0;0;0;0;0;0;43; 2;0;0;0;0;47;0;0;0;0;0;0;0;205;1;1;1;0;0;0;86;0;0;57; 15;0;0;0;1;1;1;0;0;0;0;0;1;1;15;0;0;0;0;1;1;0;0;71; 15;25;0;0;0;0;15;1;0;0;0;0;15;0;0;0;0;0;0;0;15;0;0;15; 15;1;1;0;0;0;0;15;1;1;1;0;0;0;119;0;0;1;0;0;0;0;0;15; 15;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;0;15;1;0;0;0;0;16; 16;0;0;1;1;1;0;0;0;0;0;0;0;0;0;0;0;15;15;0;0;0;0;15; 15;177;0;0;0;15;1;0;0;0;162;0;0;1;0;0;0;0;0;1;1;0;0;15; 15;1;0;0;0;0;16;1;0;0;1;1;0;0;0;0;11;0;0;0;0;0;1;15; 16;15;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;0;0;0;0;1;2; 15;15;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;15;15; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;2;15; 0;0;0;1;1;1;1;1;0;25;0;0;0;0;0;0;0;1;1;1;15;0;0;0; 131;131;131;15;16;15;15;15;1;1;1;1;1;0;0;11;0;0;0;0;0;0;0;0; 145;145;145;15;15;15;2;15;15;2;15;15;15;1;1;1;1;1;1;1;131;131;131;131 *) 72;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 15;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;106;0;0;0;0;0;0;43; 2;0;0;0;0;47;0;0;0;0;0;0;0;205;1;1;1;0;0;0;86;0;0;57; 15;0;0;0;1;1;1;0;0;0;0;0;1;1;15;0;0;0;0;1;1;0;0;71; 15;25;0;0;0;0;15;1;0;0;0;0;15;0;0;0;0;0;0;0;15;0;0;15; 15;1;1;0;0;0;0;15;1;1;1;0;0;0;119;0;0;1;0;0;0;0;0;15; 15;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;0;15;1;0;0;0;0;16; 16;0;0;1;1;1;0;0;0;0;0;0;0;0;0;0;0;15;15;0;120;0;0;15; 15;177;0;0;0;15;1;0;0;0;162;0;0;1;0;0;0;0;0;1;1;0;0;15; 15;1;0;0;0;0;16;1;0;0;1;1;0;0;0;0;11;0;0;0;0;0;1;15; 16;15;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;0;0;0;0;1;2; 15;15;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;15;15; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;2;15; 0;0;0;1;1;1;1;1;0;25;0;0;0;0;0;0;0;1;1;1;15;0;0;0; 131;131;131;15;16;15;15;15;1;1;1;1;1;0;0;11;0;0;0;0;0;0;0;0; 145;145;145;15;15;15;2;15;15;2;15;15;15;1;1;1;1;1;1;1;131;131;131;131 |] in (Array.of_list _blocks) let level = let init = (0, 0, 0, 0, false) in let level = Array.make_matrix width height init in Array.iter (fun ((x, y, _, _, _) as block) -> level.(x).(y) <- block; ) blocks; (level) let _ = level let () = Random.self_init (); let tiles_tex = "./assets/four_seasons_platformer_tileset_16x16.png" in let texture = Canvas.newImage () in Canvas.setImgSrc texture tiles_tex; let player_tile = rand_take player_tiles in let initial_state = { pos = initial_pos; blocks; y_velocity = 0; dir = { left = false; right = false; up = false; down = false; }; player_tile; texture; texture_loaded = false; } in let state = ref initial_state in Canvas.imgOnload texture (fun () -> !state.texture_loaded <- true; ); let keychange_event key_change ev = let dir = !state.dir in let player_tile = !state.player_tile in let dir, player_tile = match key_change, ev.Canvas.keyCode with | KeyDown, 37 -> { dir with left = true }, player_tile | KeyDown, 39 -> { dir with right = true }, player_tile | KeyDown, 38 -> { dir with up = true }, player_tile | KeyDown, 40 -> { dir with down = true }, player_tile | KeyUp, 37 -> { dir with left = false }, player_tile | KeyUp, 39 -> { dir with right = false }, player_tile | KeyUp, 38 -> { dir with up = false }, player_tile | KeyUp, 40 -> { dir with down = false }, player_tile | KeyDown, 67 | KeyDown, 66 -> (dir, rand_take player_tiles) | _ -> (dir, player_tile) in state := { !state with dir; player_tile } in let animate () = state := update_state !state; display_game !state; () in Canvas.imageSmoothingEnabled ctx false; Canvas.addKeyEventListener Canvas.window "keydown" (keychange_event KeyDown) true; Canvas.addKeyEventListener Canvas.window "keyup" (keychange_event KeyUp) true; let _ = Canvas.setInterval animate (1000/20) in () ;;