Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Show HN: A Clojure game library (github.com/oakes)
93 points by gw on Jan 20, 2014 | hide | past | favorite | 31 comments


Nice one, great job! I'll be checking out the code and see if I can "steal" some nice ideas, really well done. At the moment I'm also working on a fully concurrent game engine written in Clojure, https://github.com/Morgawr/Cloister-engine I'm wrapping around a few lwjgl calls, it's definitely a fun and interesting project. (Still far far from being done).

Kudos to you OP, good job.


I'm really curious what you mean by "fully concurrent" and how you accomplish it. I've been deliberating over how to write a game loop for my pet project [1] that allows multiple threads to concurrently update the world state without causing inconsistencies.

For example, let's take the age-old problem of two people in the game (potentially both AI-controlled) both deciding to pick up an item. If two separate threads are handling the logic, both having a view of the world with the item on the ground, and both of them end up trying to update the world state such that the item ends up in somebody's inventory, what is a clean and elegant way to resolve that?

I've considered using STM, having a ref to each entity in the game, and wrapping each bit of game logic in a transaction, thus rolling back entity updates that conflict with previously committed entity updates. I'm concerned about the performance implications of this, though. I keep imagining it becoming way too contentious and having more rollbacks than completed transactions.

[1] https://github.com/willismichael/terra-incognita


I've been writing on and off about my engine design on my blog http://morgawr.github.io/ but it's mostly just the introduction, I wanted to finish the actual engine (or, rather, make it usable) and make a small game as a demo in it (so far I only have a pong clone).

I don't know how feasible my design is, but my idea is to use Clojure's principles of values/identities and reftypes, every entity in the game is an agent with its own thread (scheduled in a thread pool), inside the agent there are refs (I intended to use atom but for race conditions and synchronized updating, the example you mentioned, I decided to go with refs). Entities can query a global database of entities (tagged, component based) and retrieve their state by dereferencing. If they want to update the state of other entities they can either send a message via the agent (ordered asynchronous update) or act directly onto the inner ref (via transaction) for a synchronized/concurrent update.

It's tricky to explain, and it's probably going to give me a lot of troubles along the way but it's mostly a fun experiment for a private project.


Thanks! Same here in terms of "mostly a fun experiment". A while ago Brian Carper blogged about early attempts at developing a little RPG in clojure: http://briancarper.net/blog/520/making-an-rpg-in-clojure-par...

His post indicated that he initially tried the "every entity is an agent" idea, and had performance problems with it - although maybe his real problem was that the entire world was a single ref, and all of the agents would bash on that ref.

Another question, do you have any kind of reverse-lookup table implementation? I see that you're using component-based entities, do you have any way to quickly query your world for entities that match some arbitrary criteria, rather than iterate over all entities in the entire world? I ask because my game idea would involve potentially tens of thousands of entities, and I'm trying to figure out a way to build game logic mini-systems that can quickly find a subset of game entities and perform some function on them. For example "In the spring time find all fruit-bearing trees of a minimum age and spawn unripe fruit on them", or "every 15 seconds find all poisoned characters and deduct 1 health point"


although maybe his real problem was that the entire world was a single ref, and all of the agents would bash on that ref.

Having the whole world inside a ref sounds bad... I mean, I don't really know but if you want to take the godmap approach you really need to pass around the state and apply the parallelism somewhere else (aka in the way the state transformations are applied, like pmap for example), not have entities as external agents that hammer on the poor state.

do you have any kind of reverse-lookup table implementation?

Yes and no. I don't have anything fancy at the moment, I can query the "database" of entities (aka the individual game screens) for specific tags, lookup operations are very quick in Clojure since the entity's state is a map with tag -> value, the lookup is close to O(1). The downside is that I need to query through all the entities contained in a specific screen (or all screens) to look for the tags I'm looking for, but I don't see another feasible way to do it and maintain concurrency. I could sort entities by tags (what I tried to do on my old and half-failed attempt: https://github.com/Morgawr/CloisterJS ClojureScript engine) but I would lose the advantage of concurrency. If the performance impact gets too much, then I'll try to engineer something around the problem but so far I have more important issues (like, having an engine at all).


One way that I prototyped a component-entity system that worked well for quick lookups was more like this:

  {:health   {entity-id-1 32
              entity-id-2 23}
   :location {entity-id-1 [32 16 7]
              entity-id-2 [33 13 7]}
   ... more stuff ...}
So the entity data structure is organized first by component tag, then by entity id. I've heard this referred to as "inside-out objects". That way functions can quickly query things like "find all entities that have a location" or "find all entities that are poisoned". The downside is that there isn't a convenient way to atomically update multiple components of a single entity.


Yes, that's exactly how my ClojureScript prototype worked, the problem is that it wouldn't allow me to use concurrent entities. (Although you could use refs for tag-types instead of individual entitites... an interesting approach nonetheless)


In regards to your specific question about how to cleanly resolve the two AI problem, I can think of two ways to deal with that situation.

1) Lock-based "update the world state" kind of solution. Whichever thread manages to grab the lock for being able to pick up an item is the winner, and actually gets the item. The world is modified so that the item no longer exists. When the other AI gets the lock and tries to pick up the item, it will get some kind of error letting it know that the item no longer exists. Or it can double-check for the existence of the item, and abort the pick up attempt before an error can be generated.

2) The other resolution I can think of is to have a queue of state updates. Basically, both AI broadcast to a queue that they wish to pick up an item. Whichever AI queues first, gets the item. Some logic runs to give the item to that AI, (thus removing it from the world) and then sees that the second attempt to get the item is no longer valid and aborts the attempt.

I think those two ideas make sense at a high level. (I'm sure the nitty gritty is complicated) Also they don't deal with having to roll back state to a previous point in time. (I think) Any thoughts?


With Clojure it's a bit different cause you want to handle non-locking using atoms/refs/agents/whatever. In my engine you can either a) have the first entity send a message to the object it wants to pick up, then have the object handle a queue of transformation functions on its state. The first function that is run on the object's thread is the one that changes its state or b) if you want immediate response have the first entity actually act on the object to pick up, due to atom (or refs) locking/transactions only one of the two entities can act on the object, hence the second one will fail to pick it up.


as much as i disagree with this approach it is actually quite fantastic to see a cross platform solution that isn't either using native code directly or Unity.

small tip: make the engine to fit a game, building an engine without a game is often a one way trip to a programmer wankfest that results in lots of code and cool tech but nothing usable

also interesting to see other approaches to the problem of games in general... this is quite high level stuff and the choice of tech has cut a lot of the implementation details out of the code (although imo still way to much in each file, but there we go) - quite different to what I normally consider 'game' or 'engine' programming - although obviously it is.

will fav and try to actually use when i get home from work. :)


> as much as i disagree with this approach it is actually quite fantastic to see a cross platform solution that isn't either using native code directly or Unity.

In that case check out Haxe. It's a managed language along the lines of C# or Java (in Haxe's case, compiles down to Neko bytecode), which can then be compiled into C++, Javascript, C#, Java, and PHP. If you use the OpenFL/Lime framework, you can compile an entire app for Linux/Windows/Mac (either native or NekoVM), Android and iOS, HTML5 and Flash.

The language itself is great, it's object oriented with static typing, type inference, pattern matching, lambdas and macros (!!). It comes out of the flash game developer community, but has become a fantastic way to generate native games (via C++) on multiple platforms (3D options are limited right now, but on the way).


It actually came out from a French (yes, them again) game studio before being opened to the community. ;)

http://motion-twin.com/


Sure did. I mentioned it in the other thread.

The compiler is pure magic. It does all sorts of strange and mysterious optimizations, I have no idea how it works but it seems to work very well.


small tip: make the engine to fit a game, building an engine without a game is often a one way trip to a programmer wankfest that results in lots of code and cool tech but nothing usable

Good tip, but don't forget that some people enjoy making engines and new technologies without having to make games.


Still, developing a "demo" game really helps building your engine.


On those cases, do a demo a la demoscene instead.


I did actually make a few simple games while making this library. None of them are complete, but they definitely helped to make things more concrete.

https://github.com/oakes/play-clj-examples


Just curious - any particular reasons you dislike Unity?


I use Clojure at work and I'm an active participant in the Clojure open source community.

The mention of Carmack is somewhat misplaced in that he decided languages like Lisp and Scheme that didn't offer the leverage of types like Haskell.

So perhaps languages like ML/Haskell, maybe even Rust, are more to the point?


He did indeed say he preferred static typing, but I wonder if his opinion would change if he learned about core.typed. He would probably still prefer Haskell, because he doesn't like things like that to be optional, but nonetheless I mentioned him purely as an endorsement of functional game programming, not of Clojure in particular.


> but I wonder if his opinion would change if he learned about core.typed.

As a user of Clojure and Haskell, no, not at all.

core.typed is unpleasant to use and not very sophisticated at all compared to Haskell.

Also doesn't do anything to separate pure and impure functions.


Fair enough; I haven't used Haskell but I know intuitively that a Clojure library won't be able to replicate the sophistication of its type system. On the other hand, the dynamic nature of Clojure is what makes REPL-driven game development possible.

I'm willing to give up some type system sophistication in exchange for that, along with access to the JVM, macros, and so on. Carmack may not agree with that trade-off, but like I said, I mentioned him specifically for his endorsement of functional game programming.


I'm actually trying to prototype a language that gets you the best of both:

* Clojure style dynamic development * A Lisp (with proper macros, homoiconicity etc.) * Runs on JVM * Can use Clojure libraries unmodified * Static type system (similar to Typed Clojure, but as part of the compiler and driving real optimisations, not just as a separate static analysis tool)

Still an early stage experiment right now, but I think it is the right idea: https://github.com/mikera/kiss


Nice work. Have you looked at Shen? It's highly praised by many.


I was an avid Common Lisp and Clojure user for a long time, two years ago I would've agreed with you.

You should learn Haskell :)


Reminds me of LÖVE https://love2d.org, Very nice.


Very exciting! libGDX is the best game library I've used, and I'm spending a lot of time with clojure these days. I'm excited to see what this can enable me to make.


I'd really love to see a library that handles wrapping libgdx without being tied to a specific entity system.


Aren't there issues with Clojure on Android?


The only real issues I'm aware of are slow startup times and a bigger app distributable. (Try out Bounce Away: https://play.google.com/store/apps/details?id=com.friendlyvi...)


So it's basically LambdaNative for a lesser lisp.

Nice?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: