Sorry your browser is not supported!

You are using an outdated browser that does not support modern web technologies, in order to use this site please update to a new browser.

Browsers supported include Chrome, FireFox, Safari, Opera, Internet Explorer 10+ or Microsoft Edge.

Newcomers AppGameKit Corner / [SOLVED] types in types? (entity component system systems)

Author
Message
MadTinkerer
7
Years of Service
User Offline
Joined: 21st Nov 2010
Location:
Posted: 9th May 2018 00:43 Edited at: 9th May 2018 00:51
I'm trying to figure out the best way to implement an ECS system in AGK. ECS systems are a particular programming method that is usually implemented as an alternative to object oriented. In a nutshell: Entities are containers for components. Components are data. Systems are algorithms.

Now there's a bunch of different ways to do this, but here's specifically what I'm trying to do:

Systems will be functions that get called as often as once per iteration of the main loop.
Components will be types (AGK makes this a no-brainer).
Entities are often implemented as lists of components, with a master list of entities that is iterated through by the systems.

It seems that AppGameKit does not support lists as a data type, but does allow you to do things to arrays that in other languages you can only do to lists. Such as the insert and remove commands. That's great.

The only problem is: can I add a type to a type? If I make entities arrays, that could mean needing to loop through each entity to find where in the array the component is contained. I'm already looping through the entity array, so if I have to loop through a second array just to find a component, that seems critically inefficient to me. I'd like to be able to do something like this:

type identity
name as string
race as string
endtype

type entity
endtype

masterArray as entity[]

function AddGoblin
newGoblin = entity
newGoblin.insert identity
newGoblin.identity.race = goblin
newGoblin.identity.name = RandomGoblinName()
endfunction newGoblin

masterArray.insert AddGoblin()

Which would result in one entity with an identity type with values for race (goblin) and name (some random name). So masterArrray[0].identity.race == goblin.

Something like that.

P.S. My profile says "7 years of service" because that's how long ago I registered on the forums. But I haven't actually been using AppGameKit that long.

The author of this post has marked a post as an answer.

Go to answer

MadTinkerer
7
Years of Service
User Offline
Joined: 21st Nov 2010
Location:
Posted: 9th May 2018 02:26
So after a bit more experimentation, it seems like I can make an array of any particular type, but I can't make an array of more than one type. Nor can I make an array of arrays. If I could at least make an array of arrays, each one containing a different type, that could work. But it doesn't seem like I can?

I suppose what i really want is a stack or a queue, but it doesn't seem like they are available either.
Ortu
DBPro Master
10
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 9th May 2018 02:31 Edited at: 9th May 2018 02:49


or

http://games.joshkirklin.com/sulium

A single player RPG featuring a branching, player driven storyline of meaningful choices and multiple endings alongside challenging active combat and intelligent AI.
MadTinkerer
7
Years of Service
User Offline
Joined: 21st Nov 2010
Location:
Posted: 9th May 2018 16:54 Edited at: 9th May 2018 16:59
That works, with just the two caveats that masterArray needs to be passed by reference to the AddGoblin function, and AppGameKit won't be happy until I actually define what the RandomGoblinName function does, or just comment it out.

But there's still one major problem.

Let's say I wanted to make goblins in exactly that way. Fine. Great. I could even do that with different kinds of creatures. But what if I wanted to make an entity that was a sword or a doorway or had some other combination of components?

Thinking about it more, I realized that the problem I have is that I need an array of pointers or references to the entities. AppGameKit already makes you number each image, sprite, and 3D object as you make them. I'm not sure how to do that with types.

It's not a solution, but a workaround could be just to not have an entity type, but define a bunch of different statically defined types as collections of components and have a different array for each. Creatures would have identities and positions and inventories. Containers would have positions and inventories, but not identities. Inventories would be a new component that would be arrays of items.

But then you get into the problem that all items have to be defined using the same data. Ranged and melee weapons have to be defined using the same data type as potions, tools, and so on. You certainly couldn't have containers inside containers.

It seems like I'm so close. But without the ability to just make a container type (or list / queue / stack) that can hold or reference any arbitrary type of data, it doesn't seem like this is going to work.
Ortu
DBPro Master
10
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 9th May 2018 19:02 Edited at: 9th May 2018 19:23
I'm not sure you should really want to mix 'creature ' entities alongside 'door' entities in a single super array anyway. Why? These are fundamentally different types of entities which will be handled in completely different ways.

I keep an array of character entities, and have a characters update process that uses it.

I have a separate array and update process (module actually) for managing interactable objects such as doors.

I'm not sure what would be gained by trying to stick these into one master type, which would actually make organization and modularization much more complicated and difficult.
http://games.joshkirklin.com/sulium

A single player RPG featuring a branching, player driven storyline of meaningful choices and multiple endings alongside challenging active combat and intelligent AI.
MadTinkerer
7
Years of Service
User Offline
Joined: 21st Nov 2010
Location:
Posted: 9th May 2018 20:50 Edited at: 10th May 2018 21:56
Well the "why" is "that's apparently what ECS is all about". The point of the massive array is to have everything in one place to iterate through once. Once you have that, you implement some kind of filter so that each system is only operating on the entities that they need to operate on each iteration.

This allows you to, for example, grab everything in a level and save the state of the level when the player leaves the level. If you've checked every entity in the master array, then that's everything! Clear the array of everything that doesn't persist into the next level, and load the next level. Then, if the player immediately turns around and goes back... you have to reload everything. But you only have to look in one place for everything that must be loaded.

As a point of comparison, GameMaker Studio uses instances of objects for everything that does something, and every active instance has an index. Also, every instance exists in a room. To do anything in GM:S you must have at least one room with one active instance in it. Rather than having sprites be their own thing, if you want to do something with a sprite, you make an instance and set that instance's sprite. But, to preserve legacy code, all of these instances come with built-in variables that aren't necessarily used in every instance. Trying to implement ECS in GM:S is either redundant or impossible, depending on how you look at it, because GM:S is designed to be event-driven and doesn't have support for user-defined types. But, from another perspective, GM:S is halfway there because everything (EDIT: everything that does something, not everything that is something) is an instance in a room and going from room to room is one of the most basic things you can do in GM:S.

According to the AppGameKit Official Tutorial Guide p420 "Fields can also be defined without explicitly defining the field's type." So I thought I had a workaround.



But that gives the error "main.agc:71: error: Cannot assign Type to Integer"

If there was just some way to point at things without resorting to Tier II...
Richard_6
1
Years of Service
User Offline
Joined: 3rd Feb 2017
Location:
Posted: 10th May 2018 14:10
Perhaps if you change newEntity as entityContainer to newEntity as entityContainer[].
BatVink
Moderator
15
Years of Service
User Offline
Joined: 4th Apr 2003
Location: Gods own County, UK
Posted: 10th May 2018 14:51
Quote: "AppGameKit already makes you number each image, sprite, and 3D object as you make them"


Are you using the syntax that auto-allocates an ID:




Quote: "Fields can also be defined without explicitly defining the field's type"

This means you can create variables like so:

global x // integer
global x# //float
global x$ //string


My approach is to use arrays which are a user defined type (e.g Character Type and Weapon Type)
In the Character type, you have an integer named WeaponId. This is the element number in the Weapon Array.

It can get more complex:
WeaponId[] can be an array of Weapons within the character type.
The Weapon array can be a set of blueprints, which you copy across to the character and customise (health, strength etc)

There is no theoretical limit to the number of sub-arrays you can have within your types, and sub-types within types. I have 4 levels in one of my structures, each level being an array of types.
You nest your arrays by declaring them as an array in your type. e.g



Here I have unlimited characters with unlimited weapons with unlimited ammo. The characters, weapons and ammo can all have their own characteristics.

This gives you all of the flexibility you need.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Quidquid latine dictum sit, altum sonatur
TutCity is being rebuilt
MadTinkerer
7
Years of Service
User Offline
Joined: 21st Nov 2010
Location:
Posted: 10th May 2018 21:42 Edited at: 10th May 2018 22:04
Quote: "Are you using the syntax that auto-allocates an ID:"


Yes, but I meant that "under the hood" every image, sprite, and object has concrete ID tracked by the system. This is nearly identical to the instance IDs in GM:S except GM:S also makes you give everything a name but lets you have multiples of instances with those names so you can tell it things like "Every thing with this name that currently exists in this room moves +7 pixels vertically". But then it also has an OO-like object hierarchy and no user-defined types, which is one of the main reasons I'm here*.

*The other main reason is that GM:S doesn't do 3D even as well as DarkBasic Classic.

Quote: "My approach is to use arrays which are a user defined type (e.g Character Type and Weapon Type)
In the Character type, you have an integer named WeaponId. This is the element number in the Weapon Array.

It can get more complex:
WeaponId[] can be an array of Weapons within the character type.
The Weapon array can be a set of blueprints, which you copy across to the character and customise (health, strength etc)

There is no theoretical limit to the number of sub-arrays you can have within your types, and sub-types within types."


Aha! So I could have something like



I haven't tested this yet, but I think it will work.
MadTinkerer
7
Years of Service
User Offline
Joined: 21st Nov 2010
Location:
Posted: 11th May 2018 00:16
After looking at Skyrim (Just a handy example that happens to be installed) and thinking about how they might implement item data in similar circumstances, this is what I have to start:



So first I'd make a bunch of constants for the kinds of objects and names of stats. Then the MainStat does different things depending on what the item is: damage bonus for weapons, armor bonus for armor, duration for potions and so on. Other qualities or statistics would be put in the stat[] array. If there's an object in 3D space that is associated with the item, the ID will be stored in the object integer.

Creatures will be handled similarly, with more default stats and inventory arrays ( inventory as item[] ).

So... This isn't quite ECS according to what I've read. Proper ECS could have an entity that is both a creature and an item (as one common example), and components could be any type defined. Maybe I'll figure out something closer to the original concept. Or maybe a string for a name and an array of integer-pairs is all I really need to implement everything. It's certainly close enough to start.
Ortu
DBPro Master
10
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 11th May 2018 00:59 Edited at: 11th May 2018 01:00
You're on the right track I really wouldnt get too hung up on trying to follow any design system / convention to exact specifications across languages, work with what a given language offers and adapt to what it lacks. You will find clever ways to fill the gaps and ultimately be better for it across all languages.

One thing to be mindful of though if you are going to store references to array indices such as the fields where you note points to position in array, if those index positions change at all, such as through a sort or a removal, you will have a bunch of broken references. There are a number of ways to handle this, and depending on how static your array data is, you may not need to worry about it, butt its worth keeping in mind early in design.
http://games.joshkirklin.com/sulium

A single player RPG featuring a branching, player driven storyline of meaningful choices and multiple endings alongside challenging active combat and intelligent AI.
MadTinkerer
7
Years of Service
User Offline
Joined: 21st Nov 2010
Location:
Posted: 11th May 2018 02:38 Edited at: 11th May 2018 02:41
Quote: "You're on the right track "




After reading a bit more, I did finally figure out how to do proper ECS structures:



This allows me to add any number of any kind of atoms to a component, any number of any kind of components to an entity, and any number of entities to the master list! (The entity list in the component type is not meant to be recursive, but rather for containers such as a treasure chest entity that has an inventory component that can contain entities. Although, now that I think about it, the array could be used for some kind of recursive gameplay thing.)

There's just the rather large drawback that AppGameKit Basic is very much not designed to deal with this sort of thing, so I'd have to come up with a ton of new functions just to construct and parse my exotic data structures. Also, I'd need to keep track of constants for the names of all components and all atoms. If this was C or LISP or another language that's designed to let you define your own language inside it, that'd make things easier, but that's obviously beyond the scope of AGK.

So I'm probably going to use the simpler method I came up with before and just have separate arrays for the broad categories of items, creatures, interactive world objects (I already have a simple structure for non-interactive objects), and so on.
Ortu
DBPro Master
10
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 11th May 2018 03:09
Quote: "So I'm probably going to use the simpler method I came up with before and just have separate arrays for the broad categories of items, creatures, interactive world objects (I already have a simple structure for non-interactive objects), and so on."


I'd still argue that this is a better approach anyway, it lends itself well to modularization
http://games.joshkirklin.com/sulium

A single player RPG featuring a branching, player driven storyline of meaningful choices and multiple endings alongside challenging active combat and intelligent AI.
MadTinkerer
7
Years of Service
User Offline
Joined: 21st Nov 2010
Location:
Posted: 15th May 2018 00:14 Edited at: 15th May 2018 01:13
This post has been marked by the post author as the answer.
Okay, my brain wouldn't let this go, so here's a much better solution I thought of:

Entities are supposed to possibly contain as much as one of each kind of component, and as few as zero. My inexperience with AppGameKit led me to try to statically define what needed to be dynamically defined. I ended up turning components into containers, when they're supposed to be statically defined. But then I finally realized that there is a much more obvious and simple solution. I'm used to thinking of arrays as something to use when I definitely need more than one of something. But in this case, they also work when I sometimes need less than one.



The downside of this method is I absolutely have to remember to define a new empty array in the entity type every time I define a new component. The upside is... everything else. This is basically how it's supposed to work. In fact, I don't even have to define any extra "filter" variables, because I can easily check whether an array is empty or has an entry in position 0. I knew there had to be a simple solution!

Anyway, thanks, Ortu! I probably would not have thought of this without you.
Ortu
DBPro Master
10
Years of Service
User Offline
Joined: 21st Nov 2007
Location: Austin, TX
Posted: 15th May 2018 03:13
Happy to help

Now post a project WIP so we can follow the progress!
http://games.joshkirklin.com/sulium

A single player RPG featuring a branching, player driven storyline of meaningful choices and multiple endings alongside challenging active combat and intelligent AI.
MadTinkerer
7
Years of Service
User Offline
Joined: 21st Nov 2010
Location:
Posted: 17th May 2018 21:48
Soon, hopefully.

Login to post a reply

Server time is: 2018-08-20 21:44:01
Your offset time is: 2018-08-20 21:44:01