12 ms·
Game Development in Go
- zamalek 12y agoDid you at all find that the printf methodology of debugging got in the way of the gamedev? I've been keeping an eye on Go, but have been hesitant to approach it seriously because of that debugging approach.
- falcolas 12y agoI have worked with GDB and Go before; it's not intractable, just more difficult to interpret. Printf frequently works very well for most debugging, especially server side. I use the regular logging module pointed at stdout instead of printf specifically, and increasing the debugging level works most of the time. Its also nice that with a bit of metaprogramming and goroutines/channels, you can make log printing nearly free. I once saw a technique where you simply write your really detailed log messages to a ring buffer (using a lockfree algorithm), and spit them out when you fail. This is even easier to do in Go, since you can just send the messages over a channel and not have to worry about properly implementing a lock free algorithm, and spit them out in a `recover()` function surrounding your main (or look at them in GDB).
- rudolf0 12y ago> once saw a technique where you simply write your really detailed log messages to a ring buffer (using a lockfree algorithm), and spit them out when you fail. This is even easier to do in Go, since you can just send the messages over a channel and not have to worry about properly implementing a lock free algorithm, and spit them out in a `recover()` function surrounding your main (or look at them in GDB). Do you know of a library that facilitates this?
- falcolas 12y agoI'm afraid not; I found this originally on a blog which specialized in lockfree concurrency, and adapted it for use in my Go programs. EDIT: The gist of the non-go formula was to have an array of fixed size character buffers, with a next-write pointer into that array. To write, you copy the pointer and use CAS to increment the next-write pointer (taking care to wrap at the end of the array), and write your data. If you have a particularly small buffer, you may have to be concerned about two threads writing to the same location due to the buffer wrapping, but it could be resolved with other mechanisms (or the buffer size increased). Thanks to the Go `sync/atomic` package, you could implement the same thing, or just set up a goroutine which just reads from a channel into that ring buffer.
- zamalek 12y ago> I once saw a technique where you simply write your really detailed log messages to a ring buffer While this is incredibly cool, my initial concern was mostly this: > I have worked with GDB and Go before; it's not intractable, just more difficult to interpret. > Printf frequently works very well for most debugging, especially server side. The thing about your non-niche dev is that, unless you are debugging prod (gasp), you can control the influx of data/output of data. E.g. If you want to debug a specific webpage you can simply hit that webpage in the browser yourself, and you are guaranteed to only see code executing that has to do with that request. The problem with gamedev is that things are happening 60 times a second. There is a firehose of data and there is nothing you can do about that. Logging is definitely used, but it is more useful on client machines once you have actually shipped a working product. I guess what you could do is force a fail once a condition is met, to access the last data in the ringbuffer (and pray that it is still there, 60 times a second is 60 times a second).
- falcolas 12y agoAll that makes sense to me. That said, thanks to goroutines and channels, you can be writing out that firehose and not have to worry too much about loosing your 60 FPS (with caveats, of course, but with buffering you can do, say, 6000 log writes a second and not have to worry about overwhelming your disks too much). > and pray that it is still there, 60 times a second is 60 times a second Great thing about memory, most gaming and development machines have gobs of it. You could allocate upwards of 600mb to the ring buffer and not feel the pinch. That's a lot of data, especially if you want to get creative and store pointers in there to other large structures in memory (such as a copy of a texture or mesh). Ultimately, though, I agree. Full GDB support would be better; logging like this is just a stopgap.
- zamalek 12y ago> You could allocate upwards of 600mb to the ring buffer and not feel the pinch. Touche. > Ultimately, though, I agree. Full GDB support would be better; logging like this is just a stopgap. Looks like one of us will need to man up one of these days and make a decent debugging experience. I'm just worried that the people at the helm of Go aren't taking this seriously enough. I've seen one or two quotes with them indicating that they believe printf is enough. If you want Windows developer mindshare (keeping in mind that a fair amount of gamedevs are Windows/VS users) you're going to need some competitive tooling, they will give you tons of leniency, but if you ask them to printf they will go running back into the arms of the Visual Studio debugger.
- joelgwebber 12y agoI'll be honest -- I hate working without a debugger. Go's gdb support was never great, and the team appears to have decided it's a fool's errand. On the other hand, the Delve project looks like it's serious about building a "real" debugger for Go (https://news.ycombinator.com/item?id=8595407 https://news.ycombinator.com/item?id=8595407). Supposedly it works on Linux, but I'm waiting on Mac support, which I understand is held up on Linux/Mach/Darwin differences. In the meantime, I've gotten pretty good at printf() debugging (I worked on embedded systems in a past life, so it's a skill I've had to develop). I'm also considering adding some more structured log/trace/metrics stuff (perhaps exposed via a simple web UI) that would allow me to escape the "tyranny of the ever-scrolling console". But in the end, I'll consider Go dead for game development (at least for me) if the debugger situation doesn't get fixed. I'm just betting that it will.
- tshannon 12y agoIn my hobby game programming, the first thing I got nailed down was writing messages to the screen. Not as good as debugger, but invaluable when you need to see the specific value (position, etc) of an object right next to the object on screen. I'd also usually have a key bound to spit out a frequently change buffer of debug information that I could call up at any time.
- prottmann 12y ago"Over Quota This application is temporarily over its serving quota. Please try again later." Lol, last time i have seen such a message was ten years ago ;-)
- joelgwebber 12y agoDamn, that was fast. Thought billing was enabled for that site, but I guess not. Fixed! Edit: Or at least it should be. Might take a couple of minutes to clear up.
- wsc981 12y agoCurrently the site is unreachable (for me at least). But here I've got a Coral Cache version of the content: http://www.j15r.com.nyud.net/blog/2015/01/25/Game_Development_in_Go http://www.j15r.com.nyud.net/blog/2015/01/25/Game_Developmen...
- Thaxll 12y agoI already asked here before but what is the status of the GC regarding real-time games? ( for a gameserver )
- thecatspaw 12y agoMinecraft seems to be doing okay. The key is to have many small GC pauses instead of few big pauses.
- rudolf0 12y agoGo is also completely overhauling their GC for the next release (1.5). See http://llvm.cc/t/go-1-4-garbage-collection-plan-and-roadmap-golang-org/33 http://llvm.cc/t/go-1-4-garbage-collection-plan-and-roadmap-...
- eropple 12y agoMinecraft's also had a ton of time invested into racing the beam with regards to the JVM GC. Not disagreeing with the viability of it, I personally use the CLR because I'm comfortable with that tradeoff and doing my work there too, but it is worth noting that a sufficiently complicated game will spend a lot of time dealing with memory issues. I'd use either the JVM or the CLR long before Go, though.
- sehugg 12y agoThe JVM also has probably the most man-years of effort into GC optimization. One thing the CLR has going for it is value types, which make arrays-of-struct possible (instead of arrays-of-refs-to-objects). I assume Go supports this too.
- joelgwebber 12y agoSee my earlier comment (and some bits of the original post). Go does indeed support arrays-of-structs, as well as taking pointers to the middle of arrays, and directly to struct fields. This gives you a lot more control over memory layout, and lets you avoid creating garbage if you're willing to put just a bit more work into it.
- karka91 12y agoI was experimenting with the idea of learning game dev with golang but shortly after starting I realized that there's very little tooling for that and I'll probably drop that project midway. Needing to make my own engine/tools/bindings on every step drained all the enthusiasm. Would love to see something like LÖVE made available for golang
- deleted 12y ago[deleted]
- MoOmer 12y agoBut, but, love + Lua is just beautiful. I've only messed around with it some, but it is really a thing of beauty. Haxe is one of my biggest current interests, and Snokit + Luxe is looking better every day.
- otikik 12y agoEvery language deserves LÖVE-like package. It is so good. Programming a LÖVE-like lib is an excellent way of learning a language. I did a javascript one some time ago, and it worked very well. You can find it at https://github.com/kikito/luv.js https://github.com/kikito/luv.js
- lloeki 12y agoWhen I decided to take part in LD48 #29 (which was the first time I did a gamejam), I went with golang. I did some research beforehand on the language, a few basic libraries and got me back on track with OpenGL which I did not use for years (and only ever as a toy). I maintained a log[0] and fed twitter regularly about my progress. To sum up I was 1) on the move, 2) in a new language, 3) in a domain I only superficially know. Not precisely the best conditions to say the least, but I managed to read and map keyboard and mouse input, draw sprites, animated water (screenshot missing), and (almost) working point physics. Were I not aiming for the realistic physics but some crude old skool implementation, I'd definitely have a (very basic) platform game out as the last screenshot. So, even at the lowest levels and using a few techniques I gleaned from watching Notch's Twitch stream on LD48 #28 it was loads of fun and extremely productive, even without support libraries or a dedicated engine. Hint: binding a reload hack thingy[1] to cmd+R[2] was the best thing I did, being stubborn about the Real physics thing was the worst. [0]: https://github.com/lloeki/ld48-29/blob/master/log.mdown https://github.com/lloeki/ld48-29/blob/master/log.mdown [1]: https://github.com/lloeki/ld48-29/blob/master/ld48-29.go#L31 https://github.com/lloeki/ld48-29/blob/master/ld48-29.go#L31 [2]: https://github.com/lloeki/ld48-29/blob/master/ld48-29.go#L65 https://github.com/lloeki/ld48-29/blob/master/ld48-29.go#L65
- joelgwebber 12y agoNice work. Assuming my experiments convince me that writing games in Go is worthwhile, I'd love for the ecosystem to evolve to a point that it's trivially easy to get up and running. The fast iteration time is very nice for these competitions (now I just need a debugger). I think it shouldn't be too much trouble to get to the point where we have a set of basic composable libraries for loading and rendering meshes and other graphics, as well as sound/input/etc. that doesn't require so much wiring. I haven't done the exercise yet, but I'm also hopeful that SWIG will provide good enough bindings to Bullet Physics, so that you won't stub your toe on "real physics" again :)
- agmcleod 12y agoMy concern with using go is definitely the GC. I mostly build games in Javascript, which has a nasty GC. But to invest in a new language, I feel like the gains need to be better than that. Java definitely has better perf than JS no doubt, and there's good tooling around it. But I'm happy working in the web space of things. The only thing im considering looking at for gamedev is Rust, but i need to let that grow a bit first.
- joelgwebber 12y agoSee my comment elsewhere on this thread, about techniques for limiting garbage in Go. I'm far from proving that this is sufficient for avoiding significant GC pauses, but I'm tentatively hopeful. I'll post more as I get more data on large scenes. I did play with Rust a bit, and I do find a lot to like there. Unfortunately, I quickly found myself dealing with an overwhelming explosion of type parameters (both of the garden variety, and the 'lifetime' variety). Some of this may have been my own naïveté in the language, and some bad library design (the graphics library I was using ended up forcing me to pollute nearly every type with three or for type parameters). But that, coupled with my own Go experience, a slow-ish (though better than C++) compiler, and no better debugging support than Go, led me to stick with the latter for the time being.
- hedgehog 12y agoGenerating garbage slowly will reduce the frequency of collections but it won't reduce the amount of time each individual collection takes. If you need to reduce collection times then reduce the amount of data that the garbage collector needs to traverse each time, that is move some of your data off-heap. The concurrent GC in 1.5 should help as long as the STW deadline can be reduced enough (10ms is too long for a game) but going to a generational algorithm may be necessary to get GC overhead as low as you'd want.
- joelgwebber 12y agoYes, sort of. Not generating a large amount of garbage can still give the runtime an opportunity to reduce the amount of work done per collection pass. A naïve heuristic for determining when to run GC will give you the typical "sawtooth" pattern, because you generate garbage until hitting a fixed ceiling threshold, collect it all, then wash, rinse, repeat. But a game that's not completely pegging the CPU will have some idle time that can be used for small collections, so limiting the amount of garbage produced on each frame should put an upper bound on the amount of work done during each frame. When I was helping Rovio port Angry Birds to the web a few years ago, we ran into serious frame hitches that were being triggered by GC pauses in Chrome. Two things fixed this -- the first was fixing a bug that caused it to run a full mark/sweep far too aggressively; but the second was when V8 committed an incremental collector (not concurrent, just able to spread the work out more by being able to run a partial mark/sweep and resume it later). After that, the GC pauses disappeared into tiny ~N00µs pauses that never impacted the game.
- pjmlp 12y agoUnity is really making a disservice to games development in safer languages by bundling that prehistoric Mono runtime. Anyone that isn't aware of that will assume C# in Unity == other runtimes.
- niix 12y agoInteresting article, and I'm glad to see some progress has been made. I look forward to the repo when its available.
- mseepgood 12y agoThere are also these debugging tools in the works: https://github.com/golang/debug https://github.com/golang/debug
- 4ydx 12y agoI have also been developing something in my free time with go-gl. The GC issue does bother me conceptually, but I have yet to make anything so complex that stop-the-world GC makes me curse the screen. I tried writing something minecraft-like in c and then c++ a couple of years ago, disregarding the fact that I don't have deep professional experience in either, but I eventually hit a place where my confidence dropped off too precipitously. Considering that learning modern opengl/openal is at the forefront of my concerns, using a simple language that is compiled felt like a good fit. If I were to choose a different language, it would probably end up being C#. Incidentally do you find that compiling is actually disappointingly slow? My current project takes about 2.5 seconds to build and that feels very long.