Hehe looks like I need to get some visually interesting demos up, I've been pretty lazy about that. I started looking to input and event propagation and got lost in a sea of frp and data-flow stuff for a while.
For Clojure enthusiasts, I highly recomment Zach Oakes' environment in the same vein, Nightmod (his Nightcode IDE specialized with his play-clj library). It's an experimental platform that is a tidy, simple way to experience game programming in a functional style.
Not to take anything away from that, because it's awesome, but that can also be accomplished in C. In fact pretty much any good game engine supports both dynamic shader recompilation, hot swapping the renderer and dynamically reloading the game code. A classic example where this technique is implemented is Quake 2. Basically the renderer and game are linked into the client as dynamic libraries and reloaded on demand or when a change is detected.
The big downside with this is that you can't use function pointers or addresses to static memory (which means need to be careful with e.g. string literals). This is a pretty heavy restriction, and is too much of a limitation for a lot of cases.
Not to say it's not valuable, it's great. I wouldn't exactly say it's common though.
Of course, but in practice that doesn't really matter. Recompilation time for small changes is negligible and if you have a sane build system setup is pretty much invoked the same way you would load new code into a repl.
In the case of shader compilation, compiling and linking actually is exposed as library functions, so you have complete control over how to do that.
Most of the dynamic features a repl provides, like introspection and so on is also available with a good debugger, depending on the scope of the project you can also just use Lua as a scripting language to get the majority of the benefits that Lisp has.
Moreover nested parentheses sort of pale, if you can visualize most of your state in much more powerful ways on the screen.
Since you have complete control over memory, you can implement time travelling debugging pretty easily, you just need a good ordinary debugger, a way to record all input to the game (easily done if you have clean separation between game and platform code) and a way to snapshot the game state at some point in time (also easy if you allocate all memory ahead of time and only let the game code use your memory allocators).
Could you redefine a class/structure and update every instance of it at runtime?
For example, let's say you are currently representing complex numbers in rectangular form. Is it possible to convert every existing instance to polar form, at runtime, without breaking any code?
That's a pretty ridiculous use case. I'd think if you wanted to make a change this drastic, having to reload the game wouldn't be a huge deal. I mean, you'd want to make sure everything up to the point you were at still works anyway. (Besides, this is an awful way to represent 2D points in practice).
FWIW, It's certainly possible to write code that reorders struct fields on changes, but it requires either boilerplate, macro hell, or parsing part of your C code for structure declarations (I've heard this sounds worse than it is). Arbitrary mappings are more difficult and IMO not worth the trouble.
Not to say this technique doesn't have downsides. It's downsides are so huge that IMO it's benefits aren't worth it, unless you were already going to program in that style to begin with.
- No pointers to static memory (globals, vtables, and pointers to string literals or constant arrays are out)
- No pointers to functions (most other ways of emulating dynamic dispatch are out).
- You need to use custom allocators that work out of your game state block. And since you have no global or thread local state... you need to hope that any library you want to use allows you to provide a context for any memory allocation it wants to do. Most don't.
- etc, etc, etc... you get the picture. Essentially, you need to write your game like its 1995.
And sure, maybe you can get this in lisp without any limitations (I don't know, but I believe it if you say you can), but the reality we live in is that it's unrealistic to write production-quality game engines in lisp. I've heard it's used for scripting and AI in some places (probably just Naughty Dog, tbh), but most of the code that goes into a game isn't in script, unless it's a slow game.
There are many different versions of lisp, some of them are used for scripting, but most implementations of Common Lisp could be used for almost anything.
> most of the code that goes into a game isn't in script, unless it's a slow game.
Write version 0 in a high level language. Figure out the design and representation. Then rewrite the slow parts in a more efficient language.
That's very interesting, and would definitely solve a lot of the problems with hot-reloading code.
>> There are many different versions of lisp, some of them are used for scripting, but most implementations of Common Lisp could be used for almost anything.
I was talking specifically talking about uses inside of production quality, high performance (e.g. AAA-quality) game engines. The only usage of lisp that I know of is inside Naughty Dog (Last of Us, Uncharted, Jak and Daxter, etc), who have used it internally for a long time. I hear they're lisp nuts, but even they don't try to write the game engine in it.
> Write version 0 in a high level language. Figure out the design and representation. Then rewrite the slow parts in a more efficient language.
For something small, retro, or 2D then maybe this could work. For anything else, this would be setting yourself up for failure.
You'll end up rewriting all or most in C or C++. This will cause you to miss deadlines and generally people will shit on your game on the internet.
Maybe it will be fast enough at this point, but odds are it won't. It will probably have the same problem as most game engines written in a high-level style, even if they're in C or C++. You'll fire up a profiler but there won't be any optimization targets. The whole program will be more or less equally slow. This is because you didn't design with memory access patterns in mind. 90% of the code will be spent waiting for memory to load during a cache miss.
Eventually, the game will be released and will struggle to hit 30fps. People will continue to shit on the game online, and that's if anybody bothers to play it.
Since you're starting now, it will probably be at least 4-5 year in the future, so even if 60fps expected by everybody yet, the oculus rift will be out, and anybody who plays your game on that will have a bad time. On the oculus, the framerate needs to be at least 90fps, or you risk inducing nausea. That means you have 9ms to update, and do two renders of the game (one for each eye), and if you can't make this target, your game isn't just slow, it's actively harmful to the users.
The only way to avoid this is to think about memory usage, access, and the cache from the very beginning. At that point, maybe you could still write it in lisp, but any benefit it would have given you is gone.
I've seen most of this first hand (on engines that were written in C++, but ignored the cache), and it really sucks.
I've heard it's used for scripting and AI in some places (probably just Naughty Dog, tbh), but most of the code that goes into a game isn't in script, unless it's a slow game.
Game logic is the biggest use case for scripting. I wouldn't even think about writing the rendering code in one.
Well no, the representation of game state and input have to remain the same ^1. I guess in lisp you could more easily write a function that maps the previous representation into the new one.
Admittedly it is more of a hack than in a Common Lisp system, but still.
1) EDIT: At least in the most simple design. Nothing prevents you from optionally stopping the simulation, serialize a snapshot of the sate to disk, change the representation of complex numbers or whatever, together with the data serialization routines and continue.
> I guess in lisp you could more easily write a function that maps the previous representation into the new one.
Lisp provides a simple way redefine classes. There is a generic function (basically a function that is extensible), update-instance-for-redefined-class[0], which is called on instances of the old representation to map them to the new one. All a programmer has to do is extend it with the behavior they want. Coincidentally, the link I gave shows how to do this to change the representation of a point from Cartesian coordinates to polar coordinates.
You can do that in Java with the DCEVM but the latest release is slightly out of date so you have to compile the whole OpenJDK yourself. I blame Oracle for not integrating it in the OpenJDK.
[1] http://www.youtube.com/playlist?list=PL2VAYZE_4wRKKr5pJzfYD1...
[2] https://github.com/cbaggers/varjo