Open ngscheurich opened 6 years ago
I wonder if starting with an ECS is a bit much for a newbie intro to roguelikes. Or is it actually simpler (in complexity) but just weird to wrap your head around?
It's a good and important question.
Simply speaking, an ECS is a way to manage complexity, like all other patterns. It means entities are just bags of components, components are bags of properties, and systems operate based on what components are on an entity.
There's a few factors to keep in mind:
One of the good news about what we're doing is that it's turn-based so we don't have to build a full-fledged ECS.
I'm going to check out the Nethack codebase and see how much I can map what we have of this book to what is in there.
Here's a super simple, super dumb, not-at-all-thought-out(*) sketch for an ECS in Ruby:
class Entity
attr_accessor :components
def initialize(components = [])
@components = components
end
def find(klass)
@components.find {|x| x.class == klass }
end
end
class Human < Entity
def initialize(components)
super(components)
end
end
# These are probably better as Structs
class Stats
attr_accessor :int
attr_accessor :wis
attr_accessor :str
end
class Resources
attr_accessor :health
attr_accessor :mana
end
class Poison
attr_accessor :health_mod
end
module PoisonSystem
def call(entities)
entities.filter!(&:poisoned?)
entities.each do |entity|
resources = entity.find(Resources)
poison = entity.find(Poison)
resources.health -= poison.health_mod
end
end
private
def poisoned?(entity)
entity.components.any? { |x| x.class == Poison }
end
end
human = Human.new
human_stats = Stats.new
human_stats.int = 12
human_resources = Resources.new #har, har
human_resources.health = 5
human.components << human_stats
human.components << human_resources
poison = Poison.new
poison.health_mod = 2
human.components << poison
PoisonSystem.call([human])
*: where not-at-all-thought-out means "I've spent over a year thinking about what an ECS needed to be like so the pattern's kinda clear in my head now"
Here's the logic for "poisoned" in Nethack: https://github.com/NetHack/NetHack/blob/024e9e122576db664e37df0937cfb4c06c436e0c/src/attrib.c#L255
It's readable, but it's also fairly procedural.
Right above it is this function:
/* feedback for attribute loss due to poisoning */
void
poisontell(typ, exclaim)
int typ; /* which attribute */
boolean exclaim; /* emphasis */
{
void VDECL((*func), (const char *, ...)) = poiseff[typ].delivery_func;
const char *msg_txt = poiseff[typ].effect_msg;
/*
* "You feel weaker" or "you feel very sick" aren't appropriate when
* wearing or wielding something (gauntlets of power, Ogresmasher)
* which forces the attribute to maintain its maximum value.
* Phrasing for other attributes which might have fixed values
* (dunce cap) is such that we don't need message fixups for them.
*/
if (typ == A_STR && ACURR(A_STR) == STR19(25))
msg_txt = "innately weaker";
else if (typ == A_CON && ACURR(A_CON) == 25)
msg_txt = "sick inside";
(*func)("%s%c", msg_txt, exclaim ? '!' : '.');
}
If I read this correctly, it's impossible to get to the max value of at least strength or constitution without using those items, so checking for max value means you're using the items, and therefore the value can't change, and therefore you get a different message.
As I thought about architectural patterns, and what is appropriate for the book, and what the example project will look like, I had a couple of thoughts:
Something that came to mind as a good way to determine the answer to "is an ECS a bit much for our readers" was:
Perhaps I could write up a quick excerpt, as it might appear in the book, explaining ECSs and we could use that as a barometer for how easy the concept is to elucidate to our readers.
I soon thereafter realized that there's a pretty important question that I feel has sort of been implicitly answered but deserves a but more discussion: Who are our readers? Who is the target audience for this book?
Do we expect that they have some familiarity with programming? With OOP concepts? With Ruby? I know that the existing bits of the book hint at the audience, but we should solidify a good answer to "Who is this book for?" (We should also include a brief explanation of this in the book itself!)
I assume we expect that a reader who follows along with the book will end up with a fully functional game. So, to ensure a functional end product, do we develop the game and then write the book? Write the book as we write the game? I've no experience with a book/project combo like this so I'd be interested to hear what y'all think.
I think a good approach is to assume Ruby knowledge, and some exposure to programming so not absolute beginners.
Writing the game beforehand seems to me the most sensible approach. That being said, we could tap into libtcod
(https://bitbucket.org/libtcod/libtcod/wiki/Features) as it might make it easier for newcomers to roguelikes. Then again, maybe people interested in the book want to do those things themselves.
The major draws for this book for me were:
I think as much as possible we should build a simple version of things ourselves because coming to this book, I think people will expect to not be handed any magic libraries.
On Fri, Jan 19, 2018, 08:48 Bruno Antunes notifications@github.com wrote:
I think a good approach is to assume Ruby knowledge, and some exposure to programming so not absolute beginners.
Writing the game beforehand seems to me the most sensible approach. That being said, we could tap into libtcod ( https://bitbucket.org/libtcod/libtcod/wiki/Features) as it might make it easier for newcomers to roguelikes. Then again, maybe people interested in the book want to do those things themselves.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/thoughtbot/write-yourself-a-roguelike/issues/37#issuecomment-358970656, or mute the thread https://github.com/notifications/unsubscribe-auth/AAEJSWaC5Avn0ICc4eAbpf7yegYKr1qlks5tMJ0zgaJpZM4Rja4R .
I tend to agree with @Trevoke re: libtcod
; although it's a great library, it will abstract away many of the mechanics that I was hoping this book would cover.
Upon further reflection, I also agree with @sardaukar's sentiment that writing the game, or at least the basic systems that will be in place, before we start writing the book makes the most sense.
Writing the game, or at least a skeleton of it, before writing the whole book might make sense, but I'd love to capture any dead ends and wrong assumptions as they happen.
Some of the most valuable insights in other programming books and video series that have focussed on building a project to completion have been the ones where you build something out a little bit, realise a shortcoming and refactor it into a more coherent whole.
Tying this back in to the ECS discussion - the chapters currently written about how to manage the player character stats and such seem good. I'd be very interested in a later chapter that shows the shortcomings of this method and refactor what's already there into an ECS based approach (maybe once status effects are introduced? I'm just making a guess here as to what ECS is meant to help with). I'd be much less interested in going back to these initial chapters and rework them to already have hit this solution. The first way will show as well as tell about the benefits of adapting the code to fit this framework while the second way does something similar to dropping in libtcod
* out of nowhere - telling the reader something is valuable without truly doing the legwork to show why.
* in my own noodling in this space I've found libtcod
to be a great library and we could certainly take a lot of inspiration from it in terms of what this book is here to teach!
How shall we proceed with the planning for the game, then?
The primary value that ECS derives is to split data from logic. And we can choose to limit how we use it, but one example of code flow would be, given that this is a turn-based game:
I think the answer to @sardaukar 's question on how to proceed planning the game is roughly this:
Step 1: Decide if we want to keep the code that's currently there (I would strongly vote in favour of this!) Step 2: Finish a dungeon generator - we can use something like the famous python roguelike tutorial as a base, or take Nethack's methods or whichever Step 3: LoS, functional navigation of the play space by the player character
After this step, we just basically go down the list of planned chapters as on the current home page:
I've left off the future projects and potential chapters stuff, if that's something we'd want to get into I personally feel it might be better to do those through refactors and additions to the above, not by adding them in from the start. To re-iterate my previous point, a large chunk of learning from coding books and videos for me has always been to see how people adapt existing code to do more stuff, not see it spring pristine from a copy/paste buffer somewhere.
I think it might be worth making sure that as the game is built, there's extra focus on keeping the commits both small and very well documented. This would be in service of structuring the write-up about these parts of development along the lines of how development actually worked and also to conserve useful mis-steps for detailing in the book (after all, if this group makes a wrong assumption about how to implement something, it stands to reason a reader might as well).
I agree with this overall direction, and I also think we should keep the current code.
I'm all for keeping the current code; I really like @Trevoke's assertion about watching the code evolve over time.
As @Trevoke pointed out, using a more modern architectural pattern than that used in NetHack could likely make the book easier to follow as well as to write. (Not to mention, I don't believe anyone here has much familiarity with the NetHack source code.)
Entity-component-systems were introduced by game developers in the late 90s to solve the problem of reasoning about and managing complex, intertwined game systems. Since then ECS has become, I'd venture to say, the de facto implementation for games.
Thoughts on moving over to an ECS before we get much further on this project?