So I currently have this Entity type which is the base structure of every entity in my game(items, players, NPC's, mobs) and so far I had the structure of:
Entity* = ref object
x*, y*: int
chr*: char
color*: Color
name*: string
blocks*: bool
renderOrder*: RenderOrder
fighter*: Option[Fighter]
If it is a player i'd add initialize the fighter component and if it's a NPC for example i'd set it to non, I did this mostly as a quick hack without thinking much, to solve a recursive dependency problem. However as my game grows I think this will be insufficient.
So my question basically is how would I tackle composition best where an entity has-property and where some don't? The Nim tutorial mentions it briefly but doesn't give an example: https://nim-lang.org/docs/tut2.html (or does it and I'm just missing it?).
Maybe try something like this -
type
EntityType* = enum
etFighter, etMage
Fighter* = object
Mage* = object
Color* = object
RenderOrder* = object
Entity* = ref object
x*, y*: int
chr*: char
color*: Color
name*: string
blocks*: bool
renderOrder*: RenderOrder
case entityType: EntityType
of etFighter:
fighter*: Fighter
of etMage:
mage*: Mage
Of course - if this becomes unwieldy, inheritance might be an option.
This is something that I think is worth discussing a bit, because as someone who does a lot of reading about game dev, you see a lot of strong opinions expressed, but not always with a lot of explanation about the reasoning.
For instance, I think the idea to prefer composition instead of inheritance more revolves around not trying to force complex inheritance trees, rather than just avoiding inheritance as a rule. In this case, if you have multiple types of entities that all share the same traits (location, graphical representation etc.) I think having a base class that everything inherits from makes sense (in fact, to me this seems like one of the best fits for inheritance).
To me, composition would have more to do with differentiating the sub-entities. For instance, something like:
type
Entity = ref object of RootObj
x*, y* : int
chr*: char
color*: Color
name*: string
blocks*: bool
renderOrder*: RenderOrder
Fighter = ref object of Entity
baseAttack*: Attack
Mage = ref object of Entity
baseAttack*: Attack
magicAttacks*: seq[MagicAttack]
NPC = ref object of Entity
So in this way each sub-type of entity would essentially be defined by its components which could be added as needed. Anything common to all (or nearly so) could instead be added to the base Entity class.
That is actually what I sortof settled on after a brief talk with @carterza on gitter
type
RenderOrder* = enum
CORPS, ITEMS, ACTORS
Entity* = ref object of RootObj
x*, y*: int
chr*: char
color*: Color
name*: string
blocks*: bool
renderOrder*: RenderOrder
Orc = ref object of Entity
Kobold = ref object of Entity
Item* = ref object of Entity
So I understand the hierarchy with the current type structure however most of my rendering-to-screen code and instantiating code is getting all spaghetti. I'm rewriting most of it and want to do it properly. I'm stumbling upon a roadblock(mostly because I don't know much about inheritance).
my
proc render(w: World, entities: seq[Entities]
is the main pipeline in rendering but now that most entities are either a (single) Player or a Mob my seq[Entities] is invalid. What would be a way to solve this? Should I make a something like:
proc renderPlayer() = discard
proc renderMob() = discard
I'm not positive I understand you, but are you saying your player is held in its own variable, while your mobs are in a seq? Something like:
var player: Enity
var mobs: seq[Entity]
Is that correct? Actually, as long as all your subtypes inherit from Entity, then they can all held in a big seq and iterated over. Anything that inherits from Entity can be used where an Entity is required. And you can also check to see if an Entity is a subtype of interest when needed. So, this should work:
type
Attack* = object
RenderOrder* = object
Entity = ref object of RootObj
x*, y* : int
chr*: char
name*: string
blocks*: bool
renderOrder*: RenderOrder
Fighter = ref object of Entity
baseAttack*: Attack
NPC = ref object of Entity
proc render(ents: seq[Entity]) =
for entity in ents:
echo entity.chr
if entity of NPC:
echo "NPC"
elif entity of Fighter:
echo "Fighter"
var
entities: seq[Entity] = @[]
testFighter = Fighter()
testNPC = NPC()
testFighter.chr = '@'
testNPC.chr = 'N'
entities.add(testFighter)
entities.add(testNPC)
render(entities)
That is the nice flexibility that inheritance gives!
@nucky9 this gives me an error though I had did
var
player = newEntity()
entities: seq[Entity] = @[player]
Error goes away if I just set it to @[] and add them manually huhWhat error did you get? This code works for me:
proc newEntity(): Entity =
result = new Entity
result.chr = '@'
var
player = newEntity()
entities: seq[Entity] = @[player]
render(entities)
The error was something along the line with
expected seq[Entity] but got seq[Player]
because my newEntity proc is something along the line
func newEntity*[T](x, y: int, chr: char, color: Color,
name: string, blocks = false, renderOrder = CORPS): T =
result = new(T)
result.x = x
result.y = y
result.chr = chr
result.color = color
result.name = name
result.blocks = blocks
result.renderOrder = renderOrder
if T is Player: result.fighter = initFighter(12,20,30)
if T is Orc: result.fighter = initFighter(10, 2, 3)
if T is Kobold: result.fighter = initFighter(4, 2, 1)
(self: Entity, target: Entity)
proc I need to coerce it to
, all the time. However all is well now and I can go on to add an inventory :D.