Skip to content

Warning

Use the template at https://github.com/pfeodrippe/vybe-games as your start point, try to run the commands in that README. If you try to use the vybe github project directly, you would have to build everything from scratch, but the deps used in vybe-games use the clojars version, which contains all the dynamic libs you need. Have fun \o/

Getting Started

This is the minimal amount of code to have physics (with Jolt) + ECS (with Flecs) + rendering (with Raylib) using Vybe. We will talk about each of the parts later, check the code in full below.

(ns vybe.example.minimal
  "Example with minimal setup, it will load a builtin GLTF (.glb) model with
  which contains a cube."
  (:require
   [vybe.flecs :as vf]
   [vybe.game :as vg]
   [vybe.raylib.c :as vr.c]
   [vybe.raylib :as vr]
   [vybe.type :as vt]))

(defn draw
  [w delta-time]
  ;; For debugging
  #_(def w w)

  ;; Progress the systems (using Flecs).
  (vf/progress w delta-time)

  ;; Update physics (using Jolt).
  (vg/physics-update! w delta-time)

  ;; Add some lights (from the blender model).
  (vg/draw-lights w)

  ;; Render stuff into the screen (using Raylib) using a built-in effect.
  (vg/with-drawing-fx w (vg/fx-painting w)
    (vr.c/clear-background (vr/Color [255 20 100 255]))

    ;; Here we do a query for the active camera (it's setup when loading the model).
    (vf/with-query w [_ :vg/camera-active
                      camera vt/Camera]
      (vg/with-camera camera
        (vg/draw-scene w)))

    (vr.c/draw-fps 510 570)))

(defn init
  []
  (let [w (vf/make-world)]
    ;; If you want to enable debugging (debug messages + clerk + flecs explorer),
    ;; uncomment line below.
    #_(vg/debug-init! w)

    (vg/start! w 600 600 #'draw
               (fn [w]
                 (-> w
                     ;; Load model (as a resource).
                     ;; We are going to load a bultin model, but you can use any .glb
                     ;; resource you have.
                     (vg/model :my/model (vg/resource "com/pfeodrippe/vybe/model/minimal.glb")))))))

#_ (init)

(defn -main
  "This is used for testing, don't bother."
  [& _args]
  ;; We start `init` in a future so it's out of the main thread,
  ;; `vr/-main` will be in the main thread and it will loop the game draw
  ;; function for us.
  (future (init))

  ;; Exit app after some time (for testing).
  (future
    (try
      (Thread/sleep 5000)
      (System/exit 0)
      (catch Exception e
        (println e))))

  ;; Start main thread.
  (vr/-main))

draw

The draw function receives a world (more on it later) and a delta time (time since the last iteration). You won't call this function directly, the Vybe runtime will be calling it for you.

  ;; Progress the systems (using Flecs).
  (vf/progress w delta-time)

  ;; Update physics (using Jolt).
  (vg/physics-update! w delta-time)

We are calling vf/progress, which will advance the Flecs (https://www.flecs.dev/flecs/) ECS system (w is a Flecs world), Flecs is a well-written C engine and the core of the Vybe framework. vg/physics-update! handles the physics using Jolt (https://jrouwe.github.io/JoltPhysics/), a C++ physics engine (we use a C wrapper to interact with it).

  ;; Add some lights (from the blender model).
  (vg/draw-lights w)

  ;; Render stuff into the screen (using Raylib) using a built-in effect.
  (vg/with-drawing-fx w (vg/fx-painting w)
    (vr.c/clear-background (vr/Color [255 20 100 255]))

    ;; Here we do a query for the active camera (it's setup when loading the model).
    (vf/with-query w [_ :vg/camera-active
                      camera vt/Camera]
      (vg/with-camera camera
        (vg/draw-scene w)))

    (vr.c/draw-fps 510 570)))

Backed by Raylib (https://www.raylib.com/, we are able to draw some simple lightning (using a custom shader, see the implementation), apply some effects to the screen (vg/with-drawing-fx will render the result to a render texture and draw it into the screen, vg/with-drawing is the version of it that doesn't apply any effect).

The namespaces with a 3rd party lib + .c (e.g. vybe.raylib.c, vybe.flecs.c, vybe.jolt.c) are clj wrappers for the respective libs, e.g. vr.c/clear-background will call raylib's ClearBackground (cheatsheet for raylib at https://www.raylib.com/cheatsheet/cheatsheet.html). We use jextract (https://github.com/openjdk/jextract) for these native libs and then read the generated Java classes, it's a fantastic tool made by the same people who have worked in the Panama project.

After clearing the background, we have that vf/with-query there, what is it for? We will go more in depth in one of the Flecs sections, but it's iterating over all of the ECS entities that have a :vg/camera-active tag (an identifier, here managed by Vybe) and that have a vt/Camera component (components are like structs types in C, from CLJ we have it reified and can inspect it type, construct an instance for it, generating a native MemorySegment). A query can have as many terms (a term is a binding) as you need, and we have 2 in the case here, we use the retrieved camera (which is the active one and can there only be one in any given time) and call game/raylib related functions to draw a scece using the camera data.

Finally, we draw the FPS using raylib's DrawFPS.

Yeah, I know, there is a lot to unpack here, but you will see that things integrate with each other very well and with CLJ.

init

This function will setup the world, the windows size, pass the draw var to Vybe's runtime and have a initial function that's called from the main thread.

Main thead

It's important that you initiate any raylib's functionality, specially drawing-related, inside this initial function as we may have (specially in OSX) some issues related to graphics being rendered outside the main thread.

The initial function is loading a GLTF model (.glb is the binary version of it), you can generate a .glb from Blender, for example, it should load things correctly, try it!