ease-caml Documentation

ease-caml is a game framework agnositc library for easing and tweening. A tween takes a floating point value from one value continuously over time to another value via an easing function. Tweens are a common method for animating entities in games. ease-caml provides mechanisms for easily creating and managing the tweens in your favorite OCaml game framework.

A Simple Example

We will use the raylib-ocaml game framework to demonstrate how to use ease-caml. However, any game framework with a sufficiently decent game loop should suffice.

Consider the example presented in the raylib-ocaml README:

let setup () =
  Raylib.init_window 800 450 "raylib [core] example - basic window";
  Raylib.set_target_fps 60

let rec loop () =
  if Raylib.window_should_close () then Raylib.close_window ()
  else (
    let open Raylib in
    begin_drawing ();
    clear_background Color.raywhite;
    end_drawing ();
    loop ()
  )

let () = setup () |> loop

Currently, a white screen is displayed. We will adapt this example to have a ball bounce on the screen.

First we create a ball just outside the screen (add this to the top of the example):

type circle =
{
  r: float;
  x: float;
  y: float ref;
}

let ball : circle = { r = 40.0; x = 400.0; y = ref ~-.40.0 }

This will create a ball just outside the viewport. Note that y is a float ref since we will be making the ball fall in the y direction (and thus y must also be mutable!).

Now we will create a tween to move the ball, right below let ball : circle = ... add

let ty = Tween.make_tween ball.y 225.0 ~ef:Easers.bounce 1.0

ball.y is the value we will be tweening. 225.0 is the value that ball.y will ultimately become. And we ease (~ef is short for ease_function) via the provided Easers.bounce function for a duration of 1.0.

Also, we will need a tween_manager that updates all of our tweens for us. Right below add:

let tm = Tween.new_manager ()

and in setup (well, anywhere really!) we need to add ty to the tween manager tm with:

Tween.add ty tm

tm will update ty which will update y. It might seem a little silly to have tm, but you will likely have more than 1 tween in a game and will want a manager to schedule each one of them instead of manually updating each tween individually. Lastly, we need tm to update everything. Right below let open Raylib in add:

    Tween.update tm (get_frame_time ());

All in all you should have:

type circle =
{
  r: float;
  x: float;
  y: float ref;
}

let ball : circle = { r = 40.0; x = 400.0; y = ref (0.0 -. 40.0) }
let ty = Tween.make_tween ball.y 225.0 ~ef:Easers.bounce 1.0
let tm = Tween.new_manager ()

let setup () =
  Raylib.init_window 800 450 "simple_tween";
  Raylib.set_target_fps 60;
  Tween.add ty tm

let rec loop () =
  if Raylib.window_should_close () then Raylib.close_window ()
  else (
    let open Raylib in
    Tween.update tm (get_frame_time ());
    begin_drawing ();
    clear_background Color.raywhite;
    draw_circle_v (Vector2.create ball.x !(ball.y)) ball.r Color.maroon;
    end_drawing ();
    loop ()
  )

let () = setup () |> loop

Running this code should result in a ball bouncing on the screen.

Recap

To make the ball bounce, we

You will likely need far fewer managers than tweens so most of the time will be spent on the first two steps.

When You're Done With A Tween

Your ball has bounced, now you want to move on and resume executing something else for your game. This can be done with a callback. A callback : unit -> unit is a field for your tween that executes once the tween has finished. For instance, after the ball has bounced, we may want to finish the game. In the setup function add

  Tween.set_callback ty (fun () -> print_endline "Game over!")

The ball will bounce, then the console will print Game over!. Alternatively, instead of Tween.set_callback, you could use the binary operator $+. That is

  ty $+ (fun () -> print_endline "Game over!")

Sophisticated Tweens

Chaining Tweens

Suppose we want a ball that moves back and forth. Referring to the previous example, instead of

type circle =
{
  r: float;
  x: float;
  y: float ref;
}

let ball : circle = { r = 40.0; x = 400.0; y = ref (0.0 -. 40.0) }
let ty = Tween.make_tween ball.y 225.0 ~ef:Easers.bounce 1.0

We change it to

type circle =
{
  r: float;
  x: float ref;
  y: float;
}

let ball : circle = { r = 40.0; x = ref 200.0; y = 225.0 }
let left = Tween.make_tween ball.x 600.0 ~ef:Easers.quad 1.0
let right = Tween.make_tween ball.x ~sv:600.0 200.0 ~ef:Easers.quad 1.0

Let's analyze whats been changed:

The Wrong Way

It might be tempting to put

  Tween.add left tm
  Tween.add right tm

But this will not work because it will run both tweens left and right at the same time for each iteration of the game loop. We need to perform left then right.

The Right Way

We now address this issue. To do so we make a new tween:

let left_right = Tween.extend left right

The Tween.extend a b makes a tween that first performs a and then follows it with b. Thus, running this will cause the ball to move from left to right then right to left. (Alternatively, extend is equivalent to the binary operator $>, so you could do let left_right = left $> right.)

Rinse and Repeat

You might want to repeat this left-to-right-right-to-left animation over and over again. You could make a bunch of tweens to do this, but ease-caml provides a repeat a n function to do this automatically for you. To do so, we make yet another tween

let repeat = Tween.repeat left_right 2

This will repeat the left-to-right-right-to-left animation twice. By changing 2 you can change the number of times the tween repeats. Furthermore, if you want to repeat this indefinitely, you may supply a value of -1. That is,

let repeat = Tween.repeat left_right ~-1

API