13 ms·
Making the item system polymorphic was ultimately a mistake, but that was a big one. Q: Why was this was a mistake? A: When you declare a class that’s a kind
by hprotagonist 5y ago
Making the item system polymorphic was ultimately a mistake, but that was a big one.
Q: Why was this was a mistake?
A: When you declare a class that’s a kind of item, it locks you into that structure much more tightly than if you just have member elements. It’s nice to be able to use virtual functions and that kind of thing, but the tradeoffs are just too much. I started using a “tool” item in the hierarchy, which started to get various functionality, and can now support anything from a stepladder to a beehive to a mortar (and pestle, separately, ha ha), and it just feels more flexible, and I wish every crafted item in the game were under that umbrella.
We do a lot of procedural generation, and if we wanted to, say, generate an item that acts partially like one thing and partially like another, it’s just way harder to do that when you are locked down in a class hierarchy.
Preach:
https://ericlippert.com/2015/04/27/wizards-and-warriors-part-one/ https://ericlippert.com/2015/04/27/wizards-and-warriors-part...
https://ericlippert.com/2015/04/30/wizards-and-warriors-part-two/ https://ericlippert.com/2015/04/30/wizards-and-warriors-part...
https://ericlippert.com/2015/05/04/wizards-and-warriors-part-three/ https://ericlippert.com/2015/05/04/wizards-and-warriors-part...
https://ericlippert.com/2015/05/07/wizards-and-warriors-part-four/ https://ericlippert.com/2015/05/07/wizards-and-warriors-part...
https://ericlippert.com/2015/05/11/wizards-and-warriors-part-five/ https://ericlippert.com/2015/05/11/wizards-and-warriors-part...
I have certainly committed all of these sins, too.
- mushishi 5y ago> We no longer have the problem of trying to fit “a wizard can only use a staff or dagger” into the type system of the C# language. We have no reason to believe that the C# type system was designed to have sufficient generality to encode the rules of Dungeons & Dragons, so why are we even trying? Good point! Skimmed the blog posts, seemed to have useful enumeration of techniques with considerations.
- adamrezich 5y agothis is a pretty good series of posts but I was rather surprised at where the series ended, with more (hypothetical) OOP abstraction instead of less. trying to fit game rules into a language's type system is a common thing for novice programmers to attempt, because it seems like the perfect logical application of these type system rules you just learned about when learning the language you're using. I kept expecting the article series to get around to reducing the system down to something like a Player class with a PlayerClass enum and member (and ditto for Weapon), then branching logic based on that, instead of trying to pack it into the type system.
- truetraveller 5y ago"Tool" is still a class, with perhaps very generic polymorphic methods (e.g. do_default_action() ). The problem is not polymorphism per se, but rather about having a deep class hierarchy aka lasagna code. My policy: OOP is like salt. Use little and that's great. I only allow a single inheritance layer, and ideally no inheritance at all.
- AstralStorm 5y agoFunny thing, it's just the object model in play that causes the problem. Something more similar to Smalltalk (or Objective-C) works pretty well for things this dynamical, though you do need to factor out the properties and messages reasonably well still. The trouble is C++ and similar object models poor support for composition and implicitly penalizing uniform object structure. (E.g. call multiple base methods in a subclass - pain ensues. Add virtual calls to the mix, it gets really iffy.) Even Python and Ruby with explicit mixins but the old model get hairy.
- mountainriver 5y agoThis is one of the things I like about Go. Just structs with methods. You can embed them but it seems to hedge against deep nesting and creates simple code
- blacktriangle 5y agoThen how would you model composing behaviors in Go. Say for example I have a representation of Food and a representation of Animal. I then want an ability that will "animate" things, so I can animate Food to give it the behaviors of Animal. I'm not being critical either, I'm seriously curious how somebody would implement this behavior in Go. Like you say, just struct with methods is a very appealing mental model since you have fewer moving parts, but how does it deal with a scenario like this which is very common in game dev?
- dilap 5y agoGo has interfaces, but not inheritence. I'm slightly confused about what you're trying to describe, but maybe it's something like this? https://play.golang.org/p/smPyWvBvWWp https://play.golang.org/p/smPyWvBvWWp We can make both Food and Animal statisfy the "Animateable" interface. Go also has a syntactic shortcut which is similar to inheritence, but more explicit. If you include a field in a struct without any name, it is considered "embedded", and you can call methods on the field directly, without referencing its name, which ends up looking the same, at the call site, as inheritence. https://play.golang.org/p/cIMqQqE0eVc https://play.golang.org/p/cIMqQqE0eVc But everything is still explicitly laid out in the struct -- it's just a simple struct, no inheritence shenanigans. I think interheritence is a mistake, but interfaces are the bees-knees. Even in C# (which is my main language these days, 'cuz that's what you use if you're making a game in unity), I just use interfaces, not implementation inheritence. (And even interfaces I don't use much -- but when you need it, you need it!)
- TickleSteve 5y agocomposition over inheritance... Its the fragility of large inheritance hierarchies. They work well for very rigidly defined real world structures but not so well in most real-world usages.
- h0l0cube 5y agoI came to the comments to say exactly this. I think like any ideological stance, it's extreme and there are exceptions to the rule, but for games it's great for a lot of reasons. Tarn was wrong about this though: > Using a single object with different allocated subobjects is almost certainly worse for cache misses It depends a little on the context, but having object pools is exactly the right thing to do when you're trying to scale. It allows you to group together similar data, reducing cache misses when batch processing. You can even cache-align your primitives, or even run SIMD and multi-core jobs to batch update an entire set of attributes that belong to multiple objects.
- blacktriangle 5y agoHickey nails this one in his talks. When you make something a class, that's NOT an abstraction, that's a concretion. You make a Tool class, you haven't abstracted what a tool is, you've made a fixed decision about what it is. For games that want this level of complex interaction between components, entity component systems are the way to go.
- oxfeed65261 5y agoI’d love a reference to this talk.
- j-pb 5y ago"Maybe Not" talks about these compositional ideas: https://www.youtube.com/watch?v=YR5WdGrpoug https://www.youtube.com/watch?v=YR5WdGrpoug
- blacktriangle 5y agohttps://youtu.be/2V1FtfBDsLU?t=2268 https://youtu.be/2V1FtfBDsLU?t=2268 Goes until 43:25. The whole talk is worth watching. It's basically about the higher level problems of business app development, how to address them, and how Clojure does address them.
- imtringued 5y agoI would argue that if you have a "Item" class then it is the set of all concretions/specializations. By creating a tool class you haven't extended the set, you've instantiated a subset of the set of items that contains the items that are tools but nothing else. It's obvious why it is so difficult to represent a tool that is also e.g. a consumable item. The set of items that are both tools and consumables is a superset of the set of tools and set of items. You can't represent it through a second layer of inheritance because it only lets you create a subset of the items that are tools. You obviously have to use multiple inheritance for something like this because it will let you form the set of items that are both tools and consumables. Of course, multiple inheritance is very messy so you should try to avoid it.
- Kinrany 5y ago
- NoOneNew 5y agoSorry but a lot of the complaints from the article and what you linked are... well weird. Look, I'm no grand ninja guru wizard programmer, but after a decade of programming on and off as a job... wtf are you all smoking? Theres nothing to preach but to check your hubris. A majority of problems stem not from OOP or whatever language being used, it's from over abstracting. This is mostly due to trying to pre-build for a scale that will 99.8% never happen or to account for some wild potential esoteric function in the ether that'll never happen as well. There's some weird dick measuring contest out there on the internet that I wasn't invited to where everyone is trying to out over complicate each other. They never stopped to properly learn any real design patterns, so their classes end up all over the place. "Its OOP's fault!" And hell, sometimes you used a hammer when a screwdriver was more appropriate. No big deal, we all make mistakes in lines of design logic. It ain't OOP's fault you made an oops.
- filleduchaos 5y agoLet's just say that there's a reason game dev has tended towards ECS/data-oriented design. A program that puts a handful of form widgets, consumes a bunch of text or spends most of its time waiting for I/O is far from the same domain.
- rawoke083600 5y agoExactly ! Apart from the "Mental model" and "superior ?" organisation of your "objects erm entities", there is a definite "cpu execution" advantage as well. Having the different system "execute" over items which is basically just data, helps keeping the data local and in the cache.
- vishnugupta 5y agoPrefer delegation over inheritance was one of the first OOP lessons I learnt. I read it in a C++ blog around 2004, it was explained very well with geometric shapes examples. The only time I used inheritance was while implementing a job execution framework. It fit the pattern nicely.
- deleted 5y ago[deleted]
- tralarpa 5y agoThat's very interesting, because I had observed exactly the same when I tried to implement a rogue-like in Java some years ago. For example, I had to decide whether there should be different subclasses for spell books, the different weapon types (e.g bows vs swords), drinks, etc. or just one big Item class. Closely related to that, another decision I had to make was whether object or character properties should be implemented as class members or as entries in a hashmap (where the latter is a class member). At the end, I had the feeling that I was implementing my own class/object system on top of Java's one. I guess in other languages, like lisp, this is not really an issue. IIRC I already wrote that here on HN, but I think implementing a simple rogue-like is an excellent exercise to get familiar with any programming language.
- suzzer99 5y agoMattias Johansson uses basically this example in his argument for composition over inheritance. You have humans, robots, and dogs all fitting into your nice class hierarchy. But now you need a robot dog that breaks everything. https://www.youtube.com/watch?v=wfMtDGfHWpA https://www.youtube.com/watch?v=wfMtDGfHWpA I think inheritance makes the most sense when your problem domain has been known for decades - like airline reservations. But for new blue sky projects - you always seem to wind up in these snafus that break your rigid class hierarchy.
- breck 5y ago> You have humans, robots, and dogs all fitting into your nice class hierarchy. But now you need a robot dog that breaks everything. I like the way you put that. Though humans and dogs are 85% similar at a DNA level, so makes sense for them to inherit from a common parent. And my guess is robots and robot dogs would be ~85% similar at a building block level, so dog would inherit from robot. I think go easy on levels of inheritance but have really strong root classes that most things inherit from.
- suzzer99 5y agoHeh, I actually had that wrong. I probably should have watched the video instead of going by my recollection. He has a mix of robots and animals. Same concept.
- tinus_hn 5y agoIn the LPMud LPC language objects could inherit from multiple other objects, so they could combine behaviors. I haven’t really seen that in other languages.
- shagie 5y agoThe "about inheritance in LPC" - https://www.mars.org/home/rob/docs/IntermediateLPC/chapter6.html https://www.mars.org/home/rob/docs/IntermediateLPC/chapter6.... and a bit about multiple inheritance in LPC - http://graphcomp.com/info/mud/mudos/lpc/constructs/inherit.html http://graphcomp.com/info/mud/mudos/lpc/constructs/inherit.h... And another bit - http://www.geas.de/tutorial/lpc_57.html http://www.geas.de/tutorial/lpc_57.html As to seeing it in other languages... this gets into a mixing - https://en.wikipedia.org/wiki/Mixin https://en.wikipedia.org/wiki/Mixin An example of that in Scala - https://docs.scala-lang.org/tour/mixin-class-composition.html https://docs.scala-lang.org/tour/mixin-class-composition.htm... or https://www.baeldung.com/scala/class-composition-mixins https://www.baeldung.com/scala/class-composition-mixins The issue is that for most situations, the additional conceptual load of multiple inheritance gets difficult.
- lupire 5y agolike C++?
- tinus_hn 5y agoFor some reason I actually did not know this!
- dkbrk 5y agoThe Baader-Meinhof phenomenon has hit me hard. I read an excellent blog post yesterday on this exact subject. Well worth a read, even if Rust isn't your thing: https://kyren.github.io/2018/09/14/rustconf-talk.html https://kyren.github.io/2018/09/14/rustconf-talk.html
- broodbucket 5y agoThis took half an hour to read so I don't know how the author expected it to be a half an hour talk! Thanks for sharing.
- uDontKnowMe 5y agoThanks for that great series! It reminded me a lot of this demo for O'Doyle Rules, a Clojure rules engine library, wherein the author demos a dungeon crawler style video game written top to bottom using only the rules engine for logic https://youtu.be/XONRaJJAhpA?t=1577 https://youtu.be/XONRaJJAhpA?t=1577 He also goes on to show a text editor written entirely in the rules engine (which he uses to develop the game in), really cool stuff!