4 ms·
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 sur
by s_m_t 10y ago
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.
- jstimpfle 10y agoSee my simple Tetris Game, https://github.com/jstimpfle/tetris-on-a-plane/blob/master/tetris.js 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).
- s_m_t 10y agoNice, 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.
- jstimpfle 10y agoIn 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.
- optionalparens 10y agoA 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.
- buzzybee 10y agoYeah. 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.
- adrianoconnor 10y agoHave you seen the Game Programming Patterns book (http://gameprogrammingpatterns.com/ 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.