7 ms·
> It does read kind of nicely IMO... And this one line is why you're willing to setup all these crap boilerplate supporter classes and take a huge hit in runti
by jstimpfle 8y ago
> It does read kind of nicely IMO...
And this one line is why you're willing to setup all these crap boilerplate supporter classes and take a huge hit in runtime performance?
- jryan49 8y agoA lot of languages are made for this kind of interaction and can inline stuff hopefully, but that's the job for the VM or compiler. I didn't say it was the "right" way to do it. Also programming is the art of tradeoffs. If this does actually give bad performance, maybe it's a trade off the programmer is willing to make. Just like the article started off with "I don't think there's a silver bullet". OOP is a tool just like any other with pluses, and minuses. Also the Hit class may be a Singleton and only instantiated once.
- jcelerier 8y ago> And this one line is why you're willing to setup all these crap boilerplate supporter classes and take a huge hit in runtime performance? at least in C++, why would they ? it'll all gets inlined and you can do it so that there isn't any memory allocations if you know all of your cases at compile time (of course most of the time you want to be able to add new weapons, behaviours, etc, at runtime but in game engines you generally use scripting languages for this anyways).
- jstimpfle 8y agoIt's nice that some C++ compilers can optimize this out. It means that it only costs maintainer cycles (less straightforward invocation), and compiler cycles. As every C++ programmer knows, there is an unlimited amount of them.
- Novashi 8y ago>And this one line is why you're willing to setup all these crap boilerplate supporter classes and take a huge hit in runtime performance? Hits don't usually happen that often in games relative to everything else the engine is doing unless you're writing something like MMO server code that processes hits from thousands players. Yours is a classic case of over-optimizing, I think.
- jstimpfle 8y agoOver-optimizing as in, write the one obvious line instead of 15 lines of crap that are also slower?
- Novashi 8y agoUntil you get a decent combat system in and now your hit has to go through 30+ calculations. It's almost never as simple as "monster.HP - damage". Besides, if we really want to be snarky, why haven't you wrote it in assembly for performance?
- jstimpfle 8y agoNo, I'm not talking about one arithmetic CPU operation. I'm talking about simple, procedural hit(monster, weapon, damage). Why not in assembly? Because it's not worth the effort. You won't notice any difference in speed. Be pragmatic.
- Novashi 8y ago>I'm talking about simple, procedural hit(monster, weapon, damage). The moment your game needs to know anything else about that hit event, you're going to start creating side effects from that procedure and that QUICKLY snowballs into spaghetti code. Not to mention, just from the signature, that's going to be an absolutely huge procedure. >Because it's not worth the effort. You won't notice any difference in speed. Be pragmatic. That's the exact same case I'm making. A hit object every so often will have no impact on performance (certainly not a huge impact) like you originally said and you gain developer productivity.
- jstimpfle 8y ago(monster, weapon, damage) is "the object". And yes, the procedure probably has "side effects". Because that's the point. > Not to mention, just from the signature, that's going to be an absolutely huge procedure. How do you intend to write less code with OOP?
- matthewmacleod 8y agoI'm pretty confused by how you perceive this. I don't see "crap boilerplate supporter classes" – I see objects that encapsulate events and actors for all the usual OOP reasons. I wonder if some of this disagreement is down to the way that different people abstract this problem in their heads.
- jstimpfle 8y ago// player.hits(monster).with(weapon) add to class Player { // XXX: Bad coupling Player -> Monster // XXX: Bad coupling Player -> PlayerMonsterHit method hits(Monster monster) { return PlayerMonsterHit(player, monster) } } class PlayerMonsterHit { member Player player; member Monster monster; construct PlayerMonsterHit(Player player, Monster monster) { this.player = player; this.monster = monster; } method with(Weapon weapon) { doTheActualFrigginHit(this.player, this.monster, weapon); // this line is all we REALLY need } } I guess 15 lines (that do NOTHING) was not an overestimation.
- matthewmacleod 8y agoThey don't "do nothing" - they define structure that can then be used to handle more implementation later. It's a ridiculous, contrived example, and you know that. Yes – this code is stupid if all you need to implement is a single line with the ability for a player to hit a monster. But that's never what you are implementing, is it?
- jstimpfle 8y agoIf you want to delay the hit, i.e. transactioning / buffering / delaying: This is sound engineering! But that part is not even included in above boilerplate. It would be: struct PlayerMonsterWeaponHit { Player player; Monster monster; Weapon weapon; } Array<PlayerMonsterWeaponHit> playerMonsterWeaponHits; doTheActualFrigginHit(Player player, Monster monster, Weapon weapon) { playerMonsterWeaponHits.emplace(player, monster, weapon); } In case it isn't obvious you can do that with either implementation, the above OOP boilerplate or the above imperative 1 line of code. > Yes – this code is stupid if all you need to implement is a single line No, it's stupid because it doesn't do anything. The data structures are only for temporaries that will never have any other use. In the end it's going to be PlayerMonsterWeaponHit. So the other classes are just stupid, and no amount of context will change that.