I’ve been working on a game called Bayaya, which is now nearing its Early Access release on Steam.
Before this, I was lead programmer and co-founder of Bohemia Interactive, working on titles like Operation Flashpoint, Arma, and Arma 2, all programmed in C++.
I’ve recently written a post reflecting on my experience using Scala in game development. You can read it here:
The functional programming advocated by Scala was something we liked from the start. Imagine how delighted we were when we saw that solving data access concurrency issues was possible by completely avoiding them using immutable objects! We have experienced the thrill of writing code which is beautiful and elegant, while also simple and efficient.
Hope you find it interesting!
14 Likes
Do you use a custom engine? Some known libraries? What is the architecture looking like?
1 Like
I use a custom engine, for the rendering I use custom port of three.js to Scala JVM. The list of open source libraries I use is quite extensive.
Some of the more prominent are:
- LWJGL for OpenGL
- Flying Saucer and GLGraphics2D for HTML rendering (contemplating switching to Ultralight)
- Rhino to run scripts
- Borer for JSON/CBOR serialization
- Quicklens
- ScalaTest / ScalaCheck
I really like being part of Open Source communities.
Some fun facts - while working on the project I have:
- implemented about 100 of Pull Requests which got accepted (Three.js, Quicklens, Borer, Flying Saucer)
- reported about 50 Scala 3 issues
- reported uncounted number of JetBrains Scala plugin issues
7 Likes
As for the architecture, the scene representation is dictated by Three.js. For the representation of the world we use immutable state, with static and dynamic objects being stored in separate quad-trees. Using immutable state allows us to run simulation in parallel with rendering and simulation of individual entities in parallel to each other.
To define entity behaviour we use “plans” which are slightly modifed futures running on custom executors. Here is an example which defines how to “care about a flower bed”:
protected override def process: ProcessAction = { (npc, options) =>
// gardenPlants contain representatives only
// all items (flowers, carrots...) can be found in actionClusters
// note: we assume no hiding here
val allItems = place.actionClusters.flatMap(item => item.highlight.include.toSeq.map(path => item -> path))
val itemCount = allItems.size
val rng = scala.util.Random
val plan = npc.plan
val todoCount = rng.between(5 min itemCount, 20 min itemCount + 1)
@tailrec
def processRandomly(count: Int, todo: Set[Int], done: List[Int]): List[Int] = {
if (count <= 0) done // reverse does not matter, order is random anyway
else {
val next = rng.nextInt(todo.size)
val value = todo.view.drop(next).head
processRandomly(count - 1, todo - value, value :: done)
}
}
val todo = processRandomly(todoCount, (0 until itemCount).toSet, Nil)
plan.repeatForEach(todo) { index =>
if (!plan.value.finishAsSoonAsPossible(plan.state)) {
val (workItem, path) = allItems(index)
val model = plan.state.currentModel(item)
val pos = ApproachPosFunc.onCircle(model, item.pos, workItem)(plan.state, plan.value, plan.value.pos).get
plan.goto(pos, true, Some("approaching")).whenDone {
touchItem(item, path, workItem.cfg, plan.value)
}.recover() // ignore any failures (most likely inaccessible location?)
} else plan.futureDone
}
}
Video of garden work
3 Likes
Cool, do you see any chance, to release the engine open source one day? 
1 Like
My current plan is to open source the three.js port after the game is released. As for the game engine itself, this is yet to be decided, depending on what interest there will be about the game and the concept.
1 Like
Awesome! What about GC pauses and memory consumption?
I have not observed this as an issue. Modern concurrent GC seems to be working fine it this respect. Game is currently running on Java 21, which means G1 (default since Java 9 I think).
I have observed it now with largest possible visibility:
- GC throughput is about 500 MB/s.
- typical VM pause seems to be about 2-5 ms and it happens about each second. There also exist rare pauses up to 10 ms.
It might be perhaps possible to finetune this, but so far subjectively the game seems to be running smooth so I never had a reason to spend my time on it.
3 Likes