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.
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 () |> loopCurrently, 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.0ball.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 tmtm 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 () |> loopRunning this code should result in a ball bouncing on the screen.
To make the ball bounce, we
circle type.circle type.You will likely need far fewer managers than tweens so most of the time will be spent on the first two steps.
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!")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.0We 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.0Let's analyze whats been changed:
x field so we make x: float ref.left should make the ball move from left to right.right should make the ball move from right to left.It might be tempting to put
Tween.add left tm
Tween.add right tmBut 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.
We now address this issue. To do so we make a new tween:
let left_right = Tween.extend left rightThe 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.)
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 2This 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 ~-1Tween Datatypes and functions used to make tweens. Tweens are used to continuously change one float value to another float value over time. See examples/simple_tween.ml for a simple example of how this is done in conjunction with the Raylib game library. For common continuous functions used to change the values over time, see the Easers.Easers A collection of common easing functions for tweens adapted to OCaml from https://easings.net/