#4 - Following Lispy conventions

following-lispy-conventions-01

I was adding new Lisp functions to my game engine when I noticed that I had functions that had a naming scheme that were inconsistent with others. For example, I had ones that create objects like sprite_create and shader_create but this one function I named make_vec3. I proceeded to rename make_vec3 to vec3_create. Not only is it consistent with other names but it made me realize that having a pattern of object_verb makes it easy to parse the function and what it does.

This made me wonder if there are other ways I could improve which led me to this page about variable naming conventions for Scheme. I learned that the language employs a rather effective yet simple naming convention for functions and variables. I've noticed them before but never really thought about their usefulness.

For example, adding a ? prefix easily indicates that the function, when called, will always return a boolean value. I looked at my code and I had the function is_key_down. Changing it to key_down? looked weird at first but I liked how it made the function name shorter and the ? prefix made it easy to spot and parse.

Okay, cool! What's next?

Adding a ! indicates a function that mutates data. Most commonly used for setting a variable. I saw I had variables like set_vec3_x, to which I changed to vec3_x!.

This went on as I continue to find improvements. Here's a list of all the naming convention changes that I've made:

The change From To
: infix for namespaces. vec3_create vec3:create
? postfix for boolean functions key_down? key:down?
! postfix for destructive functions set_camera_position camera:position!
% postfix for low level functions free free%
% prefix and postfix for low level variables shader-pointer %shader-pointer%
* prefix and postfix for global variables cube-shader *cube-shader*

Again, these new changes felt weird at first but I quickly became accustomed the more of these functions I changed. The code became easier to scan as there are now key characters for my eyes to easily latch onto. Something to appreciate especially with multi-level nested expressions.

Here's how the code now looks like with the new functions:

(define MOVEMENT_SPEED 0.001)

(define *cube*)
(define *cube-shader*)
(define *cube-positions*)

(define (init)
  (set! *cube*
    (cube:create "assets/textures" "awesomeface.png"))
  (set! *cube-shader*
    (shader:create "shaders/simple-3d.vs" "shaders/simple.fs"))
  (set! *cube-positions*
    (list (vec3:create 0 0 0)
          (vec3:create 1.25 0 0)
          (vec3:create -1.25 0 0))))

(define (update)
  (window:clear)

  (let* ((main-camera (camera:main))
     (current-projection (camera:projection main-camera))
     (camera-pos (camera:position main-camera)))

    (when (key:up? KEY_C)
      (if (= current-projection PERSPECTIVE)
      (camera:projection! main-camera ORTHOGRAPHIC)
      (camera:projection! main-camera PERSPECTIVE)))

    (when (key:down? KEY_A)
      (vec3:x! camera-pos
          (+ (vec3:x camera-pos) MOVEMENT_SPEED)))
    (when (key:down? KEY_E)
      (vec3:x! camera-pos
          (- (vec3:x camera-pos) MOVEMENT_SPEED)))
    (when (key:down? KEY_COMMA)
      (vec3:z! camera-pos
          (+ (vec3:z camera-pos) MOVEMENT_SPEED)))
    (when (key:down? KEY_O)
      (vec3:z! camera-pos
          (- (vec3:z camera-pos) MOVEMENT_SPEED))))

  (for-each
   (lambda (position)
     (let ((%tint% (vec3:create% 1 0 1)))
       (cube:draw *cube* position 1 1 %tint% *cube-shader*)
       (free% %tint%)))
   *cube-positions*)

  (window:swap))

I also changed my C functions to reflect a NamespaceVerb convention. I could have used namespace::Verb but the FFI that I use to communicate with C cannot parse C namespaces. So instead of Shader::Create, I am left with ShaderCreate. This is unfortunate, but I'm fine with it since the lisp scripting side of the engine will be the most prominently used (Plus, Raylib also uses this convention for their functions).

I am happy that I was able to do these changes early. Because of this, readability of my code has increased, something I worried about when I first started implementing scripting.

#3 - Rebel Game Engine now works on different platforms

After finishing the integration of Chicken-scheme scripting for my custom game engine I decided I wanted to tackle another hard problem, and that is making it cross-platform. At the very least, my engine should be able to deploy to Linux, Windows, and MacOSX.

rebel-game-engine-now-works-on-different-platforms-01

It might seem silly to be working on this while the engine is still in its early stages, but I think it is better to get this out of the way before the codebase becomes huge. With a small codebase I was able to easily identify the causes of the problems as I was porting them.

It still wasn't a walk in the park, however. Being inexperienced with developing C programs on other platforms (I've only done it mostly in Linux) I had to do research and do a lot of trial and error. I learned so much about cross-compilers, portable makefiles, and the quirks of different package managers.

After a week of this I was finally able to get the engine working on all three platforms. To make sure others can be able to do the same, I also documented the steps which can now be read on the project's wiki.

I was surprised that the hardest one to get working was for Windows. Because I didn't want to create a separate build system that uses Visual Studio solution files, I had to find a simple way to make it work and that involved the use of an excellent building platform for Windows called Msys2.

I am quite happy that I was able to structure the whole system so that you only need one Makefile for all three platforms. I'm not sure if this wil change in the future, but I'll do my best for it to stay like this as long as it's possible.

If you are interested to try it out here's the project's Github page, depending on your platform here are the instructions on how to build for each one: Linux, MacOSX and Windows.

#2 - Implemented basic Scheme scripting for Rebel Game Engine

When I first learned about Chibi, an embeddable scheme that allows scripting on C programs, I immediately tried it out on my game engine. I was able to make it work with my existing APIs but I kept on running against segfaults and memory issues. The community was helpful in answering my questions as I tried to track down the cause of the bug, but I eventually gave up out of frustration.

I then learned about Chicken Scheme, a somewhat competitor to Chibi that does the same thing but with a larger community and more documentation. I checked it out and liked it so I went ahead and implemented it.

implemented-basic-scheme-scripting-for-rebel-game-engine-01

Thankfully I have not experienced any segfaults anymore. It's not because Chicken is better (I don't know well enough of the two to make a good comparison) but because I've come to better understand how to properly structure my code after two implementations.

And my code did change a lot. The biggest change is switching from an object oriented approach to functional style so that it would suit Lisp scripting. I also paid special attention in making sure that whatever change I made would make sense when the game engine is used without scripting. My guiding principle is that implementing a game on both C and Scheme should be structurally identical, aside from the difference in syntaxes, of course.

Here's a simple program wher you could move a colored box using the keyboard:

(define box-sprite)
(define box-pos)

(define (init)
  (set! box-sprite (sprite_create assets/textures tile.png))
  (set! box-pos (make_vec3 400 300 0))
  #t)

(define (update)
  (window_clear)

  (when (is_key_down KEY_COMMA)
    (set_vec3_y box-pos (+ (get_vec3_y box-pos) 1)))
  (when (is_key_down KEY_O)
    (set_vec3_y box-pos (- (get_vec3_y box-pos) 1)))
  (when (is_key_down KEY_E)
    (set_vec3_x box-pos (+ (get_vec3_x box-pos) 1)))
  (when (is_key_down KEY_A)
    (set_vec3_x box-pos (- (get_vec3_x box-pos) 1)))

  (let ((tint (make_vec3 1 0 1)))
    (sprite_draw box-sprite box-pos 50 50 tint #f))

  (window_swap)
  #t)

Implementing Lisp, and coding with it has been very fun too. I've had the most fun when I implemented the FFI. Every time I go through the wiki I find a better way of doing things so I'd go and re-implement them again. Each iteration I learn something new and it's such a joy exploring.

Playing with the garbage collector has also made me think about memory management more. This resulted in having two ways to call certain API functions: one will return a pointer that will automatically get tracked by the GC. The other you'd have to free manually. I figured this is a good approach for situations when having control over memory and references is crucial.

I've already spent a lot of time on this project and it was mostly spent on the scripting side of the engine. There are still more to do for it to be considered useful but I'm happy I was able to learn a lot.

If you are curious about the engine you could check it out here.