package elm_playground_gamekits

  1. Overview
  2. Docs
Genre kits for Playground games: platformers, racing, shoot 'em ups, fighting and more

Install

dune-project
 Dependency

Authors

Maintainers

Sources

0.3.6.tar.gz
md5=7c636383d146d30ac6f2fa234a6253c8
sha512=c79f3823c5f8f57e5038eb640d487c61168b84aa07c61999d6622ef9fd0c890e2b03b4c6a7cdbbe9352a49e25dda00ac7bb14693cee8e3d7beeed251351a2af0

doc/src/elm_playground_gamekits.kit_brawler3d/Skeleton.ml.html

Source file Skeleton.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
(* Claude Code
 *
 * Copyright (C) 2026 Yoann Padioleau
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public License
 * (LGPL) as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 *)
open Playground
open Playground3d

(* See Skeleton.mli *)

type limb = { pitch : number; yaw : number; bend : number }

type pose = {
  lean : number;
  turn : number;
  front_arm : limb;
  back_arm : limb;
  front_leg : limb;
  back_leg : limb;
}

let limb ?(yaw = 0.) ?(bend = 0.) (pitch : number) : limb = { pitch; yaw; bend }

let stand : pose =
  { lean = 0.;
    turn = 0.;
    front_arm = limb ~yaw:8. 8.;
    back_arm = limb ~yaw:(-8.) 4.;
    front_leg = limb ~bend:6. 4.;
    back_leg = limb ~bend:6. (-4.) }

(*****************************************************************************)
(* Animating *)
(*****************************************************************************)

let mix (a : number) (b : number) (t : number) : number = a +. ((b -. a) *. t)

let lerp_limb (a : limb) (b : limb) (t : number) : limb =
  { pitch = mix a.pitch b.pitch t; yaw = mix a.yaw b.yaw t; bend = mix a.bend b.bend t }

let lerp (a : pose) (b : pose) (t : number) : pose =
  { lean = mix a.lean b.lean t;
    turn = mix a.turn b.turn t;
    front_arm = lerp_limb a.front_arm b.front_arm t;
    back_arm = lerp_limb a.back_arm b.back_arm t;
    front_leg = lerp_limb a.front_leg b.front_leg t;
    back_leg = lerp_limb a.back_leg b.back_leg t }

let at (keys : (int * pose) list) (frame : int) : pose =
  let rec go = function
    | [] -> stand
    | [ (_, p) ] -> p
    | (f1, p1) :: ((f2, p2) :: _ as rest) ->
        if frame <= f1 then p1
        else if frame >= f2 then go rest
        else lerp p1 p2 (float_of_int (frame - f1) /. float_of_int (f2 - f1))
  in
  go keys

(*****************************************************************************)
(* Drawing *)
(*****************************************************************************)

(* Stickman's proportions, so that the two figures are the same
 * fighter: the legs half the height, the torso 0.3 of it, an arm 0.34,
 * and the head what is left. *)
let leg_of (h : number) = h *. 0.5
let torso_of (h : number) = h *. 0.3
let arm_of (h : number) = h *. 0.34
let head_of (h : number) = h *. 0.2
let thick_of (h : number) = h *. 0.085

(* A limb, hanging from the origin: the upper part reaches down
 * [upper], the lower part carries on from its end, bent by [bend].
 *
 * The lower part is rotated by the bend and *then* moved to the elbow,
 * which is what puts it in the upper part's frame; the pair is then
 * turned by the shoulder's own angles, and the lower part goes along
 * without knowing it. That is the whole of hierarchical transforms,
 * and the only interesting thing in this file. *)
let jointed ?held (color : color) (thick : number) (upper : number) (lower : number) (l : limb) : shape3d =
  let part (len : number) = box color thick len thick |> move_y3d (-.len /. 2.) in
  (* what the hand holds is one more level down: moved to the end of the
   * lower part, and from there carried by the elbow and the shoulder *)
  let held = match held with Some s -> [ s |> move_y3d (-.lower) ] | None -> [] in
  group3d [ part upper; group3d (part lower :: held) |> rotate3d l.bend 0. 0. |> move_y3d (-.upper) ]
  |> rotate3d l.pitch l.yaw 0.

(* Where the end of such a limb lands, the same two rotations done with
 * arithmetic instead of boxes. [rotate3d] turns about X first, which
 * swings a hanging limb towards -z, and then about Y, which is what
 * the yaw does -- so a pitch of 90 points the limb the way the figure
 * faces, and the two agree by construction. *)
let jointed_end (upper : number) (lower : number) (l : limb) : number * number * number =
  let radians (d : number) = d *. Float.pi /. 180. in
  let p = radians l.pitch and b = radians (l.pitch +. l.bend) and y = radians l.yaw in
  let forward = (upper *. sin p) +. (lower *. sin b) in
  let down = (upper *. cos p) +. (lower *. cos b) in
  (-.forward *. sin y, -.down, -.forward *. cos y)

(* the figure, built facing -z (the playground's heading 0), its feet
 * on y = 0 *)
let figure ?front_hand ?back_hand ~(body : color) ~(back : color) ~(skin : color) (h : number) (p : pose) : shape3d =
  let leg = leg_of h and torso = torso_of h and arm = arm_of h and head = head_of h in
  let thick = thick_of h in
  let side ?held (l : limb) (color : color) (upper : number) (lower : number) (at_y : number) (across : number) =
    jointed ?held color (thick *. 0.95) upper lower l |> move3d (across *. thick *. 0.9) at_y 0.
  in
  let trunk =
    group3d
      [ box body (thick *. 2.4) torso (thick *. 1.6) |> move_y3d (torso /. 2.);
        box skin (head *. 0.8) head (head *. 0.8) |> move_y3d (torso +. (head /. 2.));
        (* the nose says which way it faces, which boxes otherwise do not *)
        box skin (head *. 0.3) (head *. 0.25) (head *. 0.3)
        |> move3d 0. (torso +. (head /. 2.)) (-.head *. 0.5);
        side ?held:back_hand p.back_arm back (arm *. 0.5) (arm *. 0.5) (torso -. (thick *. 0.3)) (-1.);
        side ?held:front_hand p.front_arm body (arm *. 0.5) (arm *. 0.5) (torso -. (thick *. 0.3)) 1. ]
    (* the lean and the twist carry the arms and the head with them,
     * because they are inside the group before it turns *)
    |> rotate3d p.lean p.turn 0.
    |> move_y3d leg
  in
  group3d
    [ trunk;
      side p.back_leg back (leg *. 0.5) (leg *. 0.5) leg (-1.);
      side p.front_leg body (leg *. 0.5) (leg *. 0.5) leg 1. ]

let draw ?front_hand ?back_hand ~(body : color) ~(back : color) ~(skin : color) (h : number) (heading : number)
    (p : pose) : shape3d =
  figure ?front_hand ?back_hand ~body ~back ~skin h p |> rotate3d 0. (-.heading) 0.

(* the same rotation, by hand: [rotate3d 0 (-heading) 0] takes (x, z)
 * to (x cos h + z sin h, -x sin h + z cos h) *)
let turned (heading : number) ((x, y, z) : number * number * number) : number * number * number =
  let a = -.heading *. Float.pi /. 180. in
  ((x *. cos a) +. (z *. sin a), y, (-.x *. sin a) +. (z *. cos a))

let hand (h : number) (heading : number) (p : pose) : number * number * number =
  let arm = arm_of h in
  let x, y, z = jointed_end (arm *. 0.5) (arm *. 0.5) p.front_arm in
  let shoulder = leg_of h +. torso_of h -. (thick_of h *. 0.3) in
  turned heading (x +. (thick_of h *. 0.9), shoulder +. y, z)

let foot (h : number) (heading : number) (p : pose) : number * number * number =
  let leg = leg_of h in
  let x, y, z = jointed_end (leg *. 0.5) (leg *. 0.5) p.front_leg in
  turned heading (x +. (thick_of h *. 0.9), leg +. y, z)