Read up on the compiler changes in the monthly release notes.

Game Releases #

Im proud to announce that two games have been released this month! We’ve mentioned both of these before so please check them out.


Streamer Cakez tried out Odin #

Awesome-Odin #

I’d like to mention awesome-odin as another collection of projects, summarized by categories!

Projects shared this month #

Project Name Description
ncurses Ncurses bindings
Edo Minimal text editor
box2d box2d 3.0 bindings
timealloc time management app
performance_aware performance aware course solutions
graphql-parser graphql-parser currently only schema
websocket-client websocket client with SSL

Project Showcase #

We’re going with the same layout as last month, let’s see how it goes again.

RePico #

RePico is small tool that allows user to replace visual shell of PNG carts used by PICO-8 fantasy console. It’s my project made for the sake of practice writing something useful with Odin language and Raylib. It was actually inspired by another project that did the same thing made with Godot engine: https://www.lexaloffle.com/bbs/?tid=50563

I didn’t really bother with styling that thing. It’s just basic Raygui theme. You can drag-and-drop images into the slots and convert the cart! There’s also a rudimental file explorer and the whole command line interface if you don’t want to touch mouse and click GUI buttons!

What do you mean “replace visual shell”? #

Everyone who’s familiar with PICO-8 fantasy console knows that the game carts it can read and write can be PNG images. Not everyone knows how it stores date inside these images. The core of data storage here is a technique called “steganography”. Basically it means “store some information inside other information”. In case of the image we can embed some binary data inside the pixels of the image itself! PICO-8 carts use 2 least significant bits of every channel of the image to store ROM data. And the trick is to get another image that’s similar in size and replace it’s 2 least significant bits with the ones from original cart.

Why even bother if such tool already exists? #

For practice of course! You don’t need to make something unique if you’re just learning stuff.

Why Odin? #

Because I found it really cool and that’s it actually. I really like Odin language and don’t have much experience beside one small game for some small game jam. I wanted to try make a somewhat “useful” tool with it.

My two main programming languages are GDScript for games and Golang for everything else (also a bunch of HTML/JS for web). I want to explore the low level worlds a bit more and get some understanding of how things work here.

What i liked about coding with Odin? #

Kinda everything! From the perspective of someone who only used garbage-collected and interpreted languages Odin is somewhat easy to jump in and write useful code. And Raylib makes it really easy to create simple GUI applications or games. Maybe I should’ve used Microui but it doesn’t really matter right now. Just initialize window, grab input events, do some stuff with that and draw something! I’ve even learned a lot about how C code works, library linking and memory management. I’ve never wrote a line of working code in C or C++ before.

Also the performance you get from writing native code is just awesome. The applications starts instantly even on some really old machines and works flawlessly!

I’ve made first iteration of RePico in 2 hours and it was able to load images dropped onto the window and replace the shell to the file with hardcoded name.

Then there were some QoL features I wanted to try to implement. The first one was CLI mode and it was much easier than the original GUI one (who would have guessed). And then there was file explorer that was made just because I wanted one.

The quirks of raygui #

The first thing i’ve added is the ability to write your own filename for the output. And the problem was with the fact that the raylib.GuiTextBox procedure takes cstring as an argument to store the value of the input box you’re writing in. And if you’ll just write something like that:

// In the head of file
myInput : cstring

// In main loop
raylib.GuiTextBox({0, 0, 200, 30}, myInput, 255, true)

It will segfault the moment you’ll try to type or delete a letter. And you can’t just pass a pointer to this string — the compiler wouldn’t let you.

But the friendly community helped me out with that case. All I need to do is create a buffer memory for that string before using it:

maxSize := 255
buf: [maxSize]byte
str := cstring(&buf[0])

// In main loop
raylib.GuiTextBox({0, 0, 200, 30}, str, maxSize, true)

There’s also a way with dynamic allocation but I didn’t bother to try that :shrug:

Another problem was with file explorer. Raylib has two methods of getting the contents of directory:

  • LoadDirectoryFiles which just gets the path and returns a bunch of file paths
  • LoadDirectoryFilesEx which can also filter for some extensions and scan directories recursively.

The problem is that the output is not sorted in any way! And there’s also no way to set filter to include .png files and directories.

So I’ve implemented my own kind of wrapper for the first method and merged it with the functionality of current changing directory:

// Some trick from community to get "dynamic array" without dynamic allocation
filesBuf : [1024*8]cstring
files : [dynamic]cstring

// We need that thing to put as an argument to raylib.GuiListViewEx
filesPtr : [^]cstring
filesCount : i32 = 0

cd :: proc(dir: cstring) -> (result: bool) {
  result = rl.ChangeDirectory(dir)
  rl.UnloadDirectoryFiles(fileList)
  clear(&files)
  fileList = rl.LoadDirectoryFiles(".")
  for fname in fileList.paths[:fileList.count] {
    if rl.DirectoryExists(fname) || rl.GetFileExtension(fname) == ".png" {
      append(&files, fname)
    }
  }

  // Funky sorting procedure to show directories first
  slice.sort_by(files[:], proc(i,j:cstring) -> bool {
    a := string(i)
    b := string(j)
    adir := rl.DirectoryExists(i)
    bdir := rl.DirectoryExists(j)
    if (adir && bdir) || (!adir && !bdir) {
      return strings.compare(a, b) == -1
    }
    return adir && !bdir
  })
  filesPtr = raw_data(files)  
  filesCount = auto_cast len(files)
  fileListScrolling = 0
  fileListActive = -1
  fileListFocus = -1
}

There’s a lot of potential problems with that code but hey! It works fine for me now.

Also this file explorer is not very comfortable to use because you can’t just double-click an item to open it. Raygui does not provide it out of the box and I haven’t tried to implement it yet.

Afterthoughts #

Odin took a special place in my heart and I really wanna explore it more. I think I need to finish that project and then I wanna start making a game with Raylib. There are many things that are still not easy to understand for me in the world of low level programming with manual memory management but Odin really helped me out with that.

The not-so-pretty source code of RePico is available at Github. I’m planning to release it on Itch after cleaning things up. Or maybe i’ll try to replace Raylib with MicroUI, who knows (:


Soap Glider #

Soap Glider is a game where you’re a kid in the bathtub, playing with the soap, tossing it around and leaving trails of bubbles (and sometimes you’re an octopus). It’s inspired by an old Android game called Run and borrows somewhat from Osu. On the gameplay side, my goal is to explore what makes rhythm games interesting and fun, and on the graphical side, I seek to learn how to employ real-time ray-tracing for drawing the scenes while keeping it responsive enough for the fast-paced gameplay. When it’s done, Soap Glider will be released on Itchio along with the source code.

The game uses the OpenGL and glfw packages for rendering and miniaudio for sound.

Platforming #

The platforms are constructed in GLSL shader code by projecting and extruding 2D SDFs onto the surface of a cylinder. To get the geometry of the scene for the gameplay logic procedure, I place a cheap camera beneath the soap and take a low-res photo, which is copied from the OpenGL context to a multi-dimensional array and parsed to determine how much of the soap is over a platform and update its polar coordinates accordingly. I use array programming extensively to update the positions of the soap and the camera.

Why Odin #

I discovered Odin through my research into Data Oriented Programming. This is the 3rd game I’ve programmed in Odin. What I like about Odin is how a lot of the tedious work you had to do manually in C is delegated to the compiler or built into the language—there’s no need for function declarations, no need to worry about the order of definitions, no need to name all the source files and all the libraries as arguments to the build command. A great example of this is the explicit procedure overloading: you can refer to a set of procedures by the same name and the compiler can determine which one you wanted to call by the arguments you provided, or panic otherwise.

One weakness is that much of the standard library is missing human-language descriptions of what procedures do and examples of how to use them, but they are quite sensibly named and whatever you can’t infer from the type schema and the name, you can find out from reading the source code and Odin code is quite readable.

Testing #

I used the testing package to write a simple procedure to benchmark the game’s performance. In just 5 lines of code I would read the best bench from disk, test for statistically-significant improvement from best bench to current bench, write the better bench to disk, and log the results.