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.
All you need is already available. Just install the packages:
aptitude install ocaml-tools libsdl-ocaml-dev
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"
For other systems there are RPM packages at rpmseek.
If those don't work, download the OCamlSDL sources and compile them yourself.
Create an empty directory for your application. This directory will contain 3 files:
OCamlMakefile
Makefile
testsdl_1.ml
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.
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:
testsdl_2.jpg
testsdl_2.ttf
testsdl_2.mp3
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:
By now, your directory contains 6 files:
OCamlMakefile
Makefile
testsdl_2.ml
testsdl_2.jpg
testsdl_2.ttf
testsdl_2.mp3
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!
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 ()
This example shows how to implement your own simple text input field with SDL.
Once again, create a directory and populate it with
OCamlMakefile
Makefile
testsdl_4.ml
testsdl_4.ttf
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!