4 ms·
Show HN: A Clojure game library
- overviewerror 13y agoReminds me of LÖVE https://love2d.org https://love2d.org, Very nice.
- Morgawr 13y agoNice 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 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.
- willismichael 13y agoI'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 https://github.com/willismichael/terra-incognita
- Morgawr 13y agoI've been writing on and off about my engine design on my blog http://morgawr.github.io/ 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.
- willismichael 13y agoThanks! 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-part-one-of-many 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"
- Morgawr 13y ago
- FreezerburnV 13y agoIn 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?
- Morgawr 13y agoWith 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.
- bitwize 13y agoSo it's basically LambdaNative for a lesser lisp. Nice?
- jheriko 13y agoas 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. :)
- Morgawr 13y agosmall 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.
- gw 13y agoI 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 https://github.com/oakes/play-clj-examples
- Mikeb85 13y ago> 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).
- steveolsen 13y agoI'd really love to see a library that handles wrapping libgdx without being tied to a specific entity system.
- coolsunglasses 13y agoI 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?
- gw 13y agoHe 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.
- coolsunglasses 13y ago> 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.
- gw 13y agoFair 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.
- coolsunglasses 13y agoI 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 :)
- jplur 13y agoVery 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.
- woah 13y agoAren't there issues with Clojure on Android?
- Jach 13y agoThe 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.friendlyvillagers.ballz&hl=en https://play.google.com/store/apps/details?id=com.friendlyvi...)