The Breath of the Wild game design has a concept called the "Chemistry Engine". In that game engines usually have a physics engine to figure out how things interact in a motion sense. The chemistry engine figures out how materials interact in an alchemy sense. It's kinda like a rules-based engine to figure out how different things interact with different other things, so you get surprising interactions between everything in the world, like being able to light arrows on fire, because arrows are a "wood" material.
It seems like most rule-based stuff in games is just hand-coded, as it's relatively simple and doesn't need to be general. When I asked the author of Baba is You, if he implemented a datalog engine for it, he said 'no'. I suspect it's the same for Breath of the Wild.
But still, I've often wondered if that sort of chemistry engine would be best implemented in a logic language like Prolog or Datalog, for fast experimentation. Just like how we use SQL to keep the flexibility of our queries, and we end up just shipping that. I'm sure, back in the day, lots of people lamented how slow SQL queries were. The flexibility was useful enough that we ended up pouring man-centuries into making them fast. Now, we think that's just the way you ship things, and (almost) never think, "I can hand-roll imperative code that will be faster than this query".
Noita is an amazingly complex game, from the alchemy reactions, to the cryptographic secrets [1], to the wand building mechanic -- which is itself like a small programming language.
Man, I wish I was good enough after 110 hours to experience the other half of this game's content. I'm still stuck dying in Hiisi base... Still fun tho!
That object system it's the literal design of the ZMachine. Objects can atributes the same way objects and methods in OOP programming.
Also, probably, MUDs.
What graphical games are trying do to at something 'revolutionary' text games did that before 40 years ago with interactive fiction (more in the 90's with the reinassance of astounding amatur games such as Curses/Jigsaw/Anchorhead/Devours/Spider and Werb than the former Infocom games); roguelikes like Nethack/Slashem and CDDA:Bright Nights and online MUDs far beyond the usual fantasy settings, such as cyberpunk or scifi settings with weird materials and interactions.
The best game creators such as Warren Spector surely played with libre games/indie/geeky games too to bring new ideas to the gameplay.
I think referencing Nethack in the original Deus Ex wasn't just a happy easter egg, but a homage to the emergent gameplay probably inspired by it.
The best about Deus Ex weren't neither the graphics or lore, but what you could do in-game, breaking the linear settings on FPS' and making them accesible unlike System Shock 1 and 2, where they tried and failed because the game difficulty didn't scaled well and the gameplay wasn't as polished.
Ditto with Arx Fatalix (now Libertatis) and Ultima Underworld.
Reminds me of dwarf fortress. Silver hammers make more damage because silver is heavier. Wooden furniture will burn. Liquids will deposit in your clothes from rain or blood, which you can then drink. So much interesting complexity arises from simple mechanics.
(With the caveat I haven't read OP article yet) I was enamored of using relational programming for a games engine for many years. Ultimately I concluded it would be better to code such an engine in C++ and write custom solvers for just the aspects you want to be relational, rather than do the whole game in a general purpose relational language.
The resolution algorithm(s) implemented by a general purpose logic programming or relational language are not the only ones possible, and being more general may not be efficient for the type of problem you want the game engine to solve efficiently. Conversely extending a logic programming language to natively include these features may invalidate simplifying assumptions or require a change in evaluation order that makes the language impossible to implement as simply or efficiently.
A concrete example (of extending logic programming with game specific features):
I wanted to both use logic programming relations and also linear constraints, so that you could say something like "A <= (B-10.0) .OR. B <= (A-10.0)" which you could picture as constraining the position of the centers of two width=10.0 game objects so that their boundaries do not overlap, but you don't care which is in front of the other. (Statements like this would be used to build up a scene or more complex game object qualitatively, without pinning down exact coordinates rather letting the linear constraint solver pick them.)
Since running the linear constraint solver to resolve something like "f(A) <= g(B)" actually (potentially) updates all of the interrelated variables (A or B might also have constraints against C,D,E,F), running it as soon as you pick the LHS or RHS of the logic expression "prop(X) .OR. prop(Y)" could potentially invalidate propositions elsewhere previously committed to. So what you probably want to do is, rather than interpreting the "<=" on linear variables as a test with a boolean result, interpret it as a command to add "f(A) <= g(B)" to the global store of linear constraints, and then at some later time run the solver on the complete matrix of linear variables.
That leads to design questions like how does the language know when it's done adding constraints and time to solve, what if we really do need a logic clause to depend on testing a value not constraining it, etc. But all of that's just a distraction from the real issue, which is that for the case of BOTH the linear constraint solving and the logic programming, in the context of a game engine we really need to have more control over both when lengthy computation is run and how long it runs.
That is, even if we design a good way for the extension features to run from the POV of the logic language, from the POV of the outer system we still have the problem of sometimes the search time to resolve part of a logic program blows up and it's difficult for the programmer to always predict when/where that will happen. In this sense it's not even required that hand-rolled imperative code be faster, it can be slower - as long as it's predictable.
And in reality you wouldn't even necessarily be hand-rolling it; what I'm talking about is whether the rules engine is a solver that is externally driven (and pre-emptible) by imperative game code, or the whole game runs within the "solver" (i.e. relational language). As tempting as it is to imagine what could be done if literally "the whole" game were relational, the fact is using that technology for a whole game implies a magic relation solver that doesn't actually exist.
Comments
The Breath of the Wild game design has a concept called the "Chemistry Engine". In that game engines usually have a physics engine to figure out how things interact in a motion sense. The chemistry engine figures out how materials interact in an alchemy sense. It's kinda like a rules-based engine to figure out how different things interact with different other things, so you get surprising interactions between everything in the world, like being able to light arrows on fire, because arrows are a "wood" material.
The Youtube link is here: https://www.youtube.com/watch?v=QyMsF31NdNc&t=2354s
It seems like most rule-based stuff in games is just hand-coded, as it's relatively simple and doesn't need to be general. When I asked the author of Baba is You, if he implemented a datalog engine for it, he said 'no'. I suspect it's the same for Breath of the Wild.
But still, I've often wondered if that sort of chemistry engine would be best implemented in a logic language like Prolog or Datalog, for fast experimentation. Just like how we use SQL to keep the flexibility of our queries, and we end up just shipping that. I'm sure, back in the day, lots of people lamented how slow SQL queries were. The flexibility was useful enough that we ended up pouring man-centuries into making them fast. Now, we think that's just the way you ship things, and (almost) never think, "I can hand-roll imperative code that will be faster than this query".
Noita is a game based around the concept of a chemistry engine running at the pixel level. It uses a custom rules engine written in C++.
It's one of the more interesting games I have ever played.
https://store.steampowered.com/app/881100/Noita/
https://www.youtube.com/watch?v=prXuyMCgbTc
Noita is an amazingly complex game, from the alchemy reactions, to the cryptographic secrets [1], to the wand building mechanic -- which is itself like a small programming language.
Here is someone using a (really complex) wand to beat the game in 2 seconds (some spoilers): https://youtu.be/YYTB5_zBANg?feature=shared&t=309
[1] https://noita.wiki.gg/wiki/Eye_Messages
Man, I wish I was good enough after 110 hours to experience the other half of this game's content. I'm still stuck dying in Hiisi base... Still fun tho!
That object system it's the literal design of the ZMachine. Objects can atributes the same way objects and methods in OOP programming.
Also, probably, MUDs.
What graphical games are trying do to at something 'revolutionary' text games did that before 40 years ago with interactive fiction (more in the 90's with the reinassance of astounding amatur games such as Curses/Jigsaw/Anchorhead/Devours/Spider and Werb than the former Infocom games); roguelikes like Nethack/Slashem and CDDA:Bright Nights and online MUDs far beyond the usual fantasy settings, such as cyberpunk or scifi settings with weird materials and interactions.
The best game creators such as Warren Spector surely played with libre games/indie/geeky games too to bring new ideas to the gameplay.
I think referencing Nethack in the original Deus Ex wasn't just a happy easter egg, but a homage to the emergent gameplay probably inspired by it.
The best about Deus Ex weren't neither the graphics or lore, but what you could do in-game, breaking the linear settings on FPS' and making them accesible unlike System Shock 1 and 2, where they tried and failed because the game difficulty didn't scaled well and the gameplay wasn't as polished. Ditto with Arx Fatalix (now Libertatis) and Ultima Underworld.
Reminds me of dwarf fortress. Silver hammers make more damage because silver is heavier. Wooden furniture will burn. Liquids will deposit in your clothes from rain or blood, which you can then drink. So much interesting complexity arises from simple mechanics.
(With the caveat I haven't read OP article yet) I was enamored of using relational programming for a games engine for many years. Ultimately I concluded it would be better to code such an engine in C++ and write custom solvers for just the aspects you want to be relational, rather than do the whole game in a general purpose relational language.
The resolution algorithm(s) implemented by a general purpose logic programming or relational language are not the only ones possible, and being more general may not be efficient for the type of problem you want the game engine to solve efficiently. Conversely extending a logic programming language to natively include these features may invalidate simplifying assumptions or require a change in evaluation order that makes the language impossible to implement as simply or efficiently.
A concrete example (of extending logic programming with game specific features):
I wanted to both use logic programming relations and also linear constraints, so that you could say something like "A <= (B-10.0) .OR. B <= (A-10.0)" which you could picture as constraining the position of the centers of two width=10.0 game objects so that their boundaries do not overlap, but you don't care which is in front of the other. (Statements like this would be used to build up a scene or more complex game object qualitatively, without pinning down exact coordinates rather letting the linear constraint solver pick them.)
Since running the linear constraint solver to resolve something like "f(A) <= g(B)" actually (potentially) updates all of the interrelated variables (A or B might also have constraints against C,D,E,F), running it as soon as you pick the LHS or RHS of the logic expression "prop(X) .OR. prop(Y)" could potentially invalidate propositions elsewhere previously committed to. So what you probably want to do is, rather than interpreting the "<=" on linear variables as a test with a boolean result, interpret it as a command to add "f(A) <= g(B)" to the global store of linear constraints, and then at some later time run the solver on the complete matrix of linear variables.
That leads to design questions like how does the language know when it's done adding constraints and time to solve, what if we really do need a logic clause to depend on testing a value not constraining it, etc. But all of that's just a distraction from the real issue, which is that for the case of BOTH the linear constraint solving and the logic programming, in the context of a game engine we really need to have more control over both when lengthy computation is run and how long it runs.
That is, even if we design a good way for the extension features to run from the POV of the logic language, from the POV of the outer system we still have the problem of sometimes the search time to resolve part of a logic program blows up and it's difficult for the programmer to always predict when/where that will happen. In this sense it's not even required that hand-rolled imperative code be faster, it can be slower - as long as it's predictable.
And in reality you wouldn't even necessarily be hand-rolling it; what I'm talking about is whether the rules engine is a solver that is externally driven (and pre-emptible) by imperative game code, or the whole game runs within the "solver" (i.e. relational language). As tempting as it is to imagine what could be done if literally "the whole" game were relational, the fact is using that technology for a whole game implies a magic relation solver that doesn't actually exist.