OCamlSDL Tutorial

Author
Volker Grabsch
Proofreading
Frank Prößdorf
Additional information for Mac OSX
anonymous contributor

Contents

Introduction

If you want to create your own game with 2D graphics and sound, the SDL library is your friend. SDL is available for many operating systems, and SDL has bindings to many programming languages including Objective Caml (OCaml) through the OCamlSDL project.

An excellent library and an excellent programming language, that leave nothing to be desired. Really? You might have guessed it: There are no SDL tutorials for OCaml. This little howto is no compensation for that, but at least it will assist you in getting started with SDL and Objective Caml.

It would be nice to have an SDL tutorial translated from C to OCaml, as it was done for the GTK+ tutorial. Maybe in the future someone will do that work.

Installation

Debian and Ubuntu

All you need is already available. Just install the packages:

aptitude install ocaml-tools libsdl-ocaml-dev

Mac OSX

For OSX, install macports. Then install libsdl from the ports:

sudo port install libsdl libsdl_image libsdl_mixer libsdl_ttf

Now install godi. Use godi_console (remember to run with sudo) to install the following packages:

The MacOS packages seem to be incompatible with the usual ones. So when you follow this howto, you need to add the following lines to the Makefile:

PACKS = extlib sdl.sdlimage sdl.sdlmixer sdl.sdlttf sdl
THREADS = true
OCAMLLDFLAGS = -cclib "-framework Cocoa"

Other

For other systems there are RPM packages at rpmseek.

If those don't work, download the OCamlSDL sources and compile them yourself.

testsdl_1 – Hello, World!

Create an empty directory for your application. This directory will contain 3 files:

If you have a system wide installed OCamlMakefile, just symlink it.

The command for Debian and Ubuntu is:

ln -s /usr/share/ocaml-tools/OCamlMakefile .

If you don't have an OCamlMakefile on your system, download it from the OCamlMakefile project:

wget http://www.ocaml.info/ocaml_sources/ocaml-make-6.24.8/OCamlMakefile

Now create a Makefile for your project:

RESULT     = testsdl_1
SOURCES    = testsdl_1.ml
LIBS       = bigarray sdl
INCDIRS    = +sdl

include OCamlMakefile

Don't omit the bigarray library. It is needed by SDL.

Now it's time for the OCaml source file testsdl_1.ml:

let main () =
    Sdl.init [`VIDEO];
    Sdlvideo.set_video_mode 200 200 [];
    Sdltimer.delay 2000;
    Sdl.quit ()

let _ = main ()

Compile and run it:

make
./testsdl_1

This small application opens a black window of the size 200x200 pixel, which stays for 2 seconds (2000ms), and then quits.

It is very important that you don't forget to call

Sdl.quit ()

at the end of your program.

testsdl_2 – Pictures, text and music

This example shows SDL's ability to handle pictures, text and music.

Create another directory and populate it with an OCamlMakefile as you did in the last section.

Now create an OCaml source file testsdl_2.ml:

let image_filename = "testsdl_2.jpg"
let font_filename  = "testsdl_2.ttf"
let music_filename = "testsdl_2.mp3"

let run () =
    let screen = Sdlvideo.set_video_mode 400 400 [`DOUBLEBUF] in
    let image = Sdlloader.load_image image_filename in
    let font = Sdlttf.open_font font_filename 24 in
    let text = Sdlttf.render_text_blended font "Enjoy!" ~fg:Sdlvideo.white in
    let music = Sdlmixer.load_music music_filename in
    let position_of_image = Sdlvideo.rect 0 0 300 300 in
    let position_of_text = Sdlvideo.rect 300 0 300 300 in
    Sdlvideo.blit_surface ~dst_rect:position_of_image ~src:image ~dst:screen ();
    Sdlvideo.blit_surface ~dst_rect:position_of_text ~src:text ~dst:screen ();
    Sdlvideo.flip screen;
    Sdlmixer.fadein_music music 1.0;
    Sdltimer.delay 1000; (* fade in *)
    Sdltimer.delay 6000; (* play *)
    Sdlmixer.fadeout_music 2.0;
    Sdltimer.delay 2000; (* fade out *)
    Sdlmixer.halt_music ();
    Sdlmixer.free_music music

let main () =
    Sdl.init [`VIDEO; `AUDIO];
    at_exit Sdl.quit;
    Sdlttf.init ();
    at_exit Sdlttf.quit;
    Sdlmixer.open_audio ();
    at_exit Sdlmixer.close_audio;
    run ()

let _ = main ()

The code ensures that even in the case of an exception the functions Sdl.quit, Sdlttf.quit and Sdlmixer.close_audio are called. They are the necessary counterparts to Sdl.init, Sdlttf.init and Sdlmixer.open_audio.

This little program shows an image, shows a text and plays a sound file, so put your favourite picture, font and music into the directory and name them:

You can also use a PNG graphic testsdl_2.png and a MOD music file testsdl_2.mod if you change the source code accordingly.

The Makefile is the same as in the last example, except that you need to include some additional libraries:

RESULT     = testsdl_2
SOURCES    = testsdl_2.ml
LIBS       = bigarray sdl sdlloader sdlttf sdlmixer
INCDIRS    = +sdl

include OCamlMakefile

The extra libraries are:

sdlloader
load images other than BMP
sdlttf
load fonts and render text
sdlmixer
load and play sound files other than WAV

By now, your directory contains 6 files:

Last but not least, compile and run your program:

make
./testsdl_2

You will see the picture, see the text and hear the music. Enjoy!

testsdl_3 – Event handling

This example demonstrates SDL's ability to handle user input.

Create yet another directory with an OCamlMakefile and a simple Makefile:

RESULT     = testsdl_3
SOURCES    = testsdl_3.ml
LIBS       = bigarray sdl
INCDIRS    = +sdl

include OCamlMakefile

Now create an OCaml source file testsdl_3.ml:

open Sdlevent
open Sdlkey

let rec wait_for_escape () =
    match wait_event () with
    | KEYDOWN {keysym=KEY_ESCAPE} ->
        print_endline "You pressed escape! The fun is over now."
    | event ->
        print_endline (string_of_event event);
        wait_for_escape ()

let main () =
    Sdl.init [`VIDEO];
    at_exit Sdl.quit;
    ignore (Sdlvideo.set_video_mode 200 200 []);
    wait_for_escape ()

let _ = main ()

This code shows how well the event handling plays together with OCaml's pattern matching.

Compile and run the program:

make
./testsdl_3

As usual, this opens a window. Press some keys, move the mouse and click into the window. You'll see that every event is reported at the command line. Play with this until you are bored, then press the Escape key.

Note: In OCaml it is generally not advisable to use open without a good reason, as it is poisoning your program's namespace. However, the modules Sdlevent and Sdlkey are good exceptions:

open Sdlevent
open Sdlkey

Without these lines, the code would suffer readability:

let rec wait_for_escape () =
    match Sdlevent.wait_event () with
    | Sdlevent.KEYDOWN {Sdlevent.keysym=Sdlkey.KEY_ESCAPE} ->
        print_endline "You pressed escape! The fun is over now."
    | event ->
        print_endline (Sdlevent.string_of_event event);
        wait_for_escape ()

testsdl_4 – Advanced event handling

This example shows how to implement your own simple text input field with SDL.

Once again, create a directory and populate it with

where OCamlMakefile is the usual one and testsdl_4.ttf your exciting new favourite font.

The Makefile is not so exciting:

RESULT     = testsdl_4
SOURCES    = testsdl_4.ml
LIBS       = bigarray sdl sdlttf
INCDIRS    = +sdl

include OCamlMakefile

The OCaml source file testsdl_4.ml looks like this:

open Sdlevent
open Sdlkey

let read_string ?(default="") show_string =
    let rec read_more_of s =
        show_string s;
        match wait_event (), s with
        | KEYDOWN {keysym=KEY_ESCAPE}, _ ->
            None
        | KEYDOWN {keysym=KEY_RETURN}, s ->
            Some s
        | KEYDOWN {keysym=KEY_BACKSPACE}, "" ->
            read_more_of ""
        | KEYDOWN {keysym=KEY_BACKSPACE}, s ->
            read_more_of (String.sub s 0 (String.length s - 1))
        | KEYDOWN {keycode='a'..'z'|'A'..'Z'|'0'..'9'|' ' as keycode}, s ->
            let string_of_key = String.make 1 keycode in
            read_more_of (s ^ string_of_key)
        | _, s ->
            read_more_of s
    in
    read_more_of default

let simple_show_string screen font s =
    let s = match s with
        | "" -> " " (* render_text functions don't like empty strings *)
        | _  -> s in
    let background_color = match s with
        | "SDL" -> Sdlvideo.yellow
        | _     -> Sdlvideo.white in
    let text = Sdlttf.render_text_blended font s ~fg:Sdlvideo.black in
    Sdlvideo.fill_rect screen (Sdlvideo.map_RGB screen background_color);
    Sdlvideo.blit_surface ~src:text ~dst:screen ();
    Sdlvideo.flip screen

let run () =
    let screen = Sdlvideo.set_video_mode 600 50 [`DOUBLEBUF] in
    let font = Sdlttf.open_font "testsdl_4.ttf" 24 in
    let show_string = simple_show_string screen font in
    match read_string show_string with
    | Some s ->
        show_string ("You entered: '" ^ s ^ "'");
        Sdltimer.delay 1000
    | None ->
        show_string "You escaped the entry field. Wimp!";
        Sdltimer.delay 2000

let main () =
    Sdl.init [`VIDEO];
    at_exit Sdl.quit;
    Sdlttf.init ();
    at_exit Sdlttf.quit;
    Sdlkey.enable_unicode true;
    Sdlkey.enable_key_repeat ();
    run ()

let _ = main ()

Compile and run the program:

make
./testsdl_4

It opens a window where you can enter text. The keys Return, Escape and Backspace work as you'd expect. Some special behaviour is implemented when you are entering "SDL". Have fun!

Fork me on GitHub