Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I would concur and add that the main risk one runs in deploying any new, widespread abstraction to game code is an architectural meltdown, where perf bottlenecks are appearing everywhere, too many code paths have been locked in by the abstraction so you can't iterate on them fast anymore, you have race conditions you can't analyze properly, etc.

Iteration times across the whole development lifecycle are hard to optimize because in the near term the iteration problem is specific to writing basic functionality, then later on it's adding and adjusting a wide variety of assets, and then even later it's debugging and optimizing. Care too much about one and you mess up the others.

If you soak up pain up front to keep the core of the code in an unadorned imperative style, crunching on plain data, avoiding deeply nested callstacks or polymorphism, you get a huge assurance that you can handle anything that happens later on - it follows the grain of the tooling and the happy path of the optimizer.

The way in which I last approached the live-reload problem was to write a data model, and then bolt a scripting language on to generate data. Then reload the scripts - you get the fast iteration, without being particularly invasive on the main loop.

Edit: And similarly I've focused most of my abstraction braincycles on making it easier to access and swap out assets. One of the biggest recurring problems in engine code is deriving runtime assets from combinations of static assets and parameters. This has led me down the road of developing an addressing scheme.



I've just started to program a game engine and to my surprise the most difficult to structure aspect of the programming has been essentially, I'm not really sure how to word this, but mapping things to other things.

Making sure that a keypress eventually leads to the right function being called is a good example. It seemed really simple at first but then I figured that the keypress shouldn't directly call the function because then you'd need to recode or reparameterize some giant state machine because in certain contexts a keypress can do many things or control many different objects in the game (even controlling multiple objects at the same time, and what if you want to be able to control the game remotely or from two controllers at once?).

Before I started I thought the 3d movement and physics stuff would be the hardest... but I've found it was actually pretty simple, I just iterate through a list of objects and just call their respective member functions to make the movement happen and it seems to work fine for now.


See my simple Tetris Game, https://github.com/jstimpfle/tetris-on-a-plane/blob/master/t..., at the very bottom.

This implements a state machine in Javascript. It's a very simple case with only 2 states, "playing" and "paused". Each state registers its own event handlers on entrance, and unregisters them on leave.

This approach avoids the need to answer the question "where am I? what should I do?" every time an event handler is called.

While I think the approach is nice, the code is not so nice from a superficial point of view. The more general problem is that event-style sucks. Look up CSP by Tony Hoare, and Occam the language. What we need is better formalisms for sequential programs that include non-determinism like user events (i.e. view the user as part of the program).

(And, of course, also parallel execution of these sequential threads, and also sub-states that can access parent states in a way. Look up Harel's Statecharts. A good example would be a form with two text input boxes. We could view them as parallel sub-threads of the form thread).


Nice, I implemented something very similar, except in my game not every controllable entity a) implements every control, and b) implements controls the same way, so I store what a control would do as a member function of each object and store an array with the names of these functions in each object. The indices of the array with the names of the functions match the indices of an array that stores the key codes I listen for, from there I match the indices, grab the name of the function and call it from the object being controlled.


In Javascript, you have to install event handlers. There is no control when they are executed, which is a problem.

When writing a game engine, you typically create events yourself at the top of the game loop (reading the input sources in a non-blocking fashion). You have better control what handler is called and when, but what handler should be called probably still shouldn't be decided in a central location. So I figure your object-based approach is a good solution. (The individual handlers can rely on the fact that they are only called at the start of the loop).

I realize a Javascript app could easily emulate the game loop style by letting handlers write to a queue.


Yeah. There is a lot of routing/binding action going on in an engine when you get past the "hardcode everything for a single context" stage. I'm currently mixing about four big ideas together in my engine - they haven't all been deployed in the actual game project I have right now, but they're getting developed alongside what's there:

* Unix-esque hierarchical path routing - the addressing system I alluded to in parent comment. It's used as a mapping substrate - you can swap out parts of the route to quickly change what's being addressed, and it stores just a little bit of data so that leaf nodes mostly act as keys into other containers. I changed it from int-per-node to int-array-per-node today, which may or may not have been a great idea, but came up because it was irritating to have to make a special store for string keys.

* Micro-database system for allocating various types of engine data. This is the biggest source of friction in the engine right now - each data type has a custom container, and in theory, when allocated it'll automatically map to a path address. Engine scenes are currently loaded up through a script saying "allocate these things, relate these things together", but the addressing isn't present, you just access the containers more-or-less directly(ad-hoc per container).

* J. Paul Morrison-style "classic FBP" to compile data graphs. This is more of a "I want to program with Lego Bricks" kind of goal - it's the most directly interesting one for the keybinding problem, and it's used in existing engines for aspects of rendering graphics and sound. But it ultimately depends on having the addressing system functioning on real assets, so it's on the backburner.

* A "stack of small compilers" as in the VPRI STEPS project. This is used to turn game design assets(event scripts etc.) into runtime data by way of a behavior tree compiler that emits opcodes for a small VM interpreter. This is the stuff I'm really using the most, right now, and it actually inspired the addressing system, as compiling all that stuff created a need for it.


Have you seen the Game Programming Patterns book (http://gameprogrammingpatterns.com/). It was originally a blog that later become a book, and I think you can browse most of the content online. It's full of interesting lessons and patterns and deals with coupling and state management. It won't have the answers to copy and paste, but it gives you a lot of food for thought and it's really well written.

I'm not a game programmer by trade, but I'm interested in that stuff and I really enjoyed it all the same. It made me rethink some stuff about enterprise patterns too.


A keypress should never do much other than to update some game state to say that whatever key was pressed and it is mapped to whatever game concept in your input mapping. Nearly any code I've ever seen with an if/keypress, do this is an anti-pattern. Once you have your state, you just need to handle it somewhere later during a game tick, and keep in mind that might be even multiple places.

I would recommend thinking in loops, so it sounds you're already on the right track, you just need to expand that more to everything else. You generally want to do the same "thing" to many "things." Since you are newer to programming engines, I won't get too technical and I'll say that the gist is that the CPU likes this. The bi-product is if you take this approach, you'll tend to have better architecture as well as performance and solve some of the problems you listed. This is really the basis of an entity system approach if you want an example architecture.

Whether using entity systems or something else, my recommendation would be to separate out functionality and make them rather agnostic to each other. Again, this fits in with the loops paradigm. You still often need things to communicate as your problem suggests, but the way to do that is to often take some state, update it with new state, and pass it down the chain to see if anyone cares. That chain itself can be a loop, i.e. a bunch of systems in your game loop calling a method like update(tick, state, ... ). That said, you also need to be careful what and how much state gets passed around for performance reasons (ex: avoid excessive copying) and to prevent weird things from happening if you start introducing concurrent and/or parallel programming. To that end, I tend to keep things minimal as I can, or at least have some sort of way to hand things just what they need, not everything.

Queues also relate to all of this and can be your friend. A common communication approach is to use mailboxes for example and tends to be more straight forward to debug and optimize than using callbacks and event-based approaches (they tend to murder the cache and your stack traces). Queues also play nicer for being building blocks for concurrent programming.

Also keep in mind you may need to flip your thinking. Sometimes you don't actually need some action that happens during a tick to be processed the same tick. You can often or will often want to defer things to the next tick, or even several ticks after that. There's a Naughty Dog presentation on Last of Us port to PS4 that relates to this and how they flipped even the way frames are processed on an existing codebase to get better performance. The point is not for you to do what they did, rather to start thinking in terms of what the user will see as the end result. The user often doesn't care that you didn't finish processing some action last tick if it had no bearing on the gameplay.


Thanks, right now every game tick I check a sort of register to see if any keys have been pressed, held, or released and then immediately call the associated functions to implement the controls. If I'm interpreting what you said correctly and I understood the Naughty Dog presentation correctly (I did see that one!) I guess I should instead add those function calls to a queue and then call them at my convenience?

That makes sense to me, I could split up the queue and send it to different threads to be worked on or perhaps delay doing things in the queue if a frame was taking too long.


I wrote a long comment, but was rate limited at the time.

I'll summarize instead with a simple list:

- It's better to map your input to some game actions than pass around the raw value

- You don't need to do anything creative like queue function pointers themselves, just the raw values.

- You can have an input "queue" but that is typically different from a queue that is going to act on those inputs.

- You need to deal with multiple inputs potentially, like someone pounding a button. Hopefully your input lib deals with this some already, but always be aware of validating the input and deciding which one matters most. To that end, priority queues are good sometimes. You also sometimes want to cancel inputs. If you want to read more about it all, I'd read some about "intention systems" as related to input.

- Once your input is mapped, you can operate it in multiple pieces of codes, systems, etc. that might be downstream in your game loop

- You can transform an action into yet other new state as you go along

- Queues are indeed great for doing multi-threading, just be sure you select the appropriate kinds of queues. Mostly this ends up being thread-safe queues that are lockless. But sometimes you want to just use queues for FIFO, and these queues are generally faster and/or more flexible if they use a non-thread safe approach.

- You can have many queues in your code as mailboxes to distinct systems. Again, think something that is agnostic to the world around it and just receives some state it needs, and has its own "inbox" of things it might need to process this frame. It certainly could be that you can't process them all that frame.

- Keep in mind with queues generally once you pop something off, that's it. So you need other ways of hanging on to state and things that aren't ready. To that end, simple arrays are your friend, especially if you can pull things out quickly by index in relation to some id (ex: entity id) and keep them tightly packed. Dynamic vectors are garbage and mostly not used in serious game engines unless the developer had a wtf moment or the use case specifically calls for it. That's a longer topic though.

- Attaching functions directly to input again is a pretty bad approach. If you need more than one function, you'll have to implement something more complex. If order of function calls matter, you again need yet more. The order should be well-defined in the game loop itself.

- Regarding order, it also relates to communication as well. Note that you don't need to always process every new input or piece of state every tick. Sometimes you actually don't want to do this and defer it to a later time. There's no 100% rule, but an easy thing that helps is to think if the player or game sim will suffer because you didn't process that state during that tick. For some physics related things, that can be bad, but for other things, it's more of a "as long as this happens very soon, it's alright."

- There's no 100% rules for any of this.




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

Search: