Advertisement

A Functional Impass - Advise me on how to proceed? (C#)

Started by January 18, 2019 03:32 AM
21 comments, last by duke_meister 5 years, 9 months ago

G'Day....

I have been working on a text adventure game. You can follow my progress on my Blog (link in signature at bottom of post) Anyway, I have been chugging through the, frankly, tedious work of building my word lists and stuff as well as competing the 1st step in the phraser.. which is single command recognition and execution.  The problem I am running into is how do I go about implementing area specific code and conditions, in my very generic and general purpose classes.

Class Structure As it is Now

(Obviously I have striped this list to make my problem clearer)

  • Level Class - Contains a variable List<Area> Level [][]. This is a 2 dimensional array creating a X/Y reference values for any given spot on the array.
  • Player Class - Contains a variable PosX and PosY... these are modified by movement and on .getPosition() returns the value of List<Area>[][]. This is how I am acessing the correct place in my game world based on the player position.
  • Area Class - This contains a given area. These are placed inside the List<Area>[][].
  • Item Class -  This contains individual items sitting in the level to be interacted with.

So a Level -> Area -> Items.

The Problem

The problem I have having is I can not work out how to go about having unique things inside these Areas and Items. Like for example a Ball can not be opened but a box can. So they need a different variable. I can not use the same class for both Box and Sphere. An area might have nothing in it, but it also may have a table that needs a ball put on it and then something is activated.

What I am getting at here is that the structure is basically working using these generic classes but I am not sure how I can code exciting and interesting unique things in these areas or on these items with out fundamentally changing things, or adding a mass of variables to the cases that are only used in some cases.

Say I have a Box in the scene.

I want the players to see it, and then try to pick it up.. but they can't. So the examine it more closely and find that it is actually got a button on it. The press it and something happens.

The questions is where are the methods to do these unique things and edit the item parameters going to be put. If I put them in the Item or Area sections I need to basically have a unique class for everyone .. maybe that is what I am supposed to do. Thing is I wrote a really handy data txt file importer for areas and files that I do not want to scrap. That only really works on these generic ideas. To get really interesting events and natural language responses there needs to be a ton of unique things going on ... but I have kinda designed everything to be very generic.

Possible Solution

I know that a List<type> can contain classes as long as all the classes in the list are inherited form the same base class. So I was thinking of a new class that populates a simular kind of list to Level<area>[][]. This will be something like List<gameLogic>[][]. In here I put a all the methods for the individual areas. The Key values like the other list is pulled form the player positions. So if I need a unique method I could call it from Player.GetPostions().gameLogic.SetOpen();... this then would trigger bools and change description text and stuff in the more generic objects. The problem with this is I still need a series of generic method calls. So far example area.item[9] might be a Hat... if I type GETHAT it trigges a generic get() method on the hat, that ALL items have.. but it then actually dose a unique bit of logic in the logic class for that specific room.. so item.get(prams) triggers gameLogic.get(prams).. and those prams allow me to do specific "gets" per-object..

Is that making any sense?

So yeah.. I'm kinda stuck

Yeah.. so I am kinda stuck here about what to do. The crux of my issue is that I have a game loop and a series of objects.. level, areas in the level and items in the area. These are all generic so the I can be easily build the level during initiation of the game with out tons of unique code making editing and changes hard. So the exact way to continue these simple loops, but access unique code is confusing me. I mean as long as everything was very simple I could get wawya with it. but as I wanted to add more complex stuff I was finding the need for additional variables and checks.

Any thoughts? (sorry if I am not clear)

 

Quick demo of you... this shows the start screen and the first room. (pressing spacebar skips the text printout to finished)

So the very first time the game loops and finds you in a area it tests a variable in that area to see if you have ever been there before.. if not it prints a "cinematic".. witch is a long story focused text description, that you only see once. (it is also added to a log for reading latter). Then if you type the look command... it finds the area look description and adds it to the print buffer, then finds all the items (there are only two at the moment) and appends their descriptions and then prints out that look.

All pretty cool and I ma happy with it.

This is just to show something in the thread.. I seem to have noticed anything with a youtube video in it gets way more comments hahaah

: GameDev.Net Blog: Open Blog; Read Blog : My uTube Chans : TragicTableTopTragicDeskTop

I'm no expert in formal methodologies but I like to think about the data itself before I try to think about things as individual objects(classes).

So, as I understand it(hopefully correctly) it seems you are having trouble deciding how to organize the item data that you are needing to store and access. 

Let me throw a dart at the board across the room:

You're writing a text/phrase parser are you not?

Why not include a small array/list of item attributes in string form into your Item class?

You could have one for descriptive info: "type:box", "color:blue"

You could have another for actionable attributes: "opens:front","button:top","explodes:30"

This would then be a blue box that has a button on top and opens in the front, which explodes 30 seconds after opening... ;)

Hope it helps!

Advertisement

This would require you expanding the functionality of your parser to perform game-logic tasks not related directly to user-input.  Like deciphering what an object is and determining how to describe it to the player, but that might be beneficial to your process, and the development of the parser itself. ;)  It could also make it easier to save and load(to/from file) objects using JSON or some other simple serialization method.

Again, this is mostly a shot in the dark.  Hope it helps somewhat.

Maybe talking about the specifics of my program was confusing the question... I may edit the OP.

What I am asking is how do you insert unique code into a gameloop that uses generic classes... like at what point do you start using unique classes and how do these get incorporated. 

I'm just having a hard time working out a way to put unique checks and events into scenes.

: GameDev.Net Blog: Open Blog; Read Blog : My uTube Chans : TragicTableTopTragicDeskTop

I suggest making your item class a container for behaviours and then make these behaviours your unique classes.

Each behaviour would have verbs (look, take, open, etc) that determine how the player interacts with it. The box would then have a "container" behaviour that lets it be opened/closed and items taken in or out of it.

I agree with Irusan, and I'd like to go into a little more detail about his suggestion. It's one of my favorite topics in game dev, the entity component system (ECS) pattern - an incredibly simple yet powerful approach to solve exactly the sort of problem as you have it in a very elegant way.

The basic idea is: Instead of modeling things in your world as a collection of separate explicit classes (perhaps related to each other through inheritance), you make everything a generic "entity" that has "components" attached to it. In the purest form, an entity does really nothing other than storing a list of components, while components are objects of different classes that can store different data and define different behaviors. The simulation of the world (in your case mostly the interaction of the player with items) is implemented by so-called "systems" with process entities on the component level and depending on which  types of components they have.

An example:

Let's say you have things in the world that can be picked up by the player, and things that can't. In order to allow the player to be able to pick up an item and store it in her inventory, you assign a "PickUpComponent" to it, and the whole pick up logic is implemented in a "PickUpSystem" which will simply ignore all items/entities that don't have a PickUpComponent. This pattern can be applied to almost the entire game logic and leads to very clean, flexible and elegant code. Discovering and understanding it was probably the biggest game dev epiphany ever for me, that's why I'm sort of an "evangelist" for it :D. The implementation is crazily simple and short, especially in relation to the great benefits it brings with it. The difficulty lies in the application. It takes a while to really understand how to use the concept properly and how to design your components and systems and how everything interacts. I'm using it since a bit less than two years now, and I still discover new fascinating tricks frequently (I'm just a small on-off hobby game dev though).

There are many different implementations of ECS for different languages, game engines and frameworks. I have written my own very very basic one, which still provides a huge advantage over how i did things before. The largest part of the power of ECS really lies in its very basic concept/architecture, which can be implemented with just a few lines of code. There are several different opinions/philosophies about how it should be done the "right" way, some more "extremist" than others.

After two years of experimenting and fixing bugs and flaws while still keeping the core code *very* simple all the time, I arrived at something with the following core characteristics:

- An entity is an object/class that contains an array/list of component objects. Entities are self-sufficient and do not require some kind of "entity manager" to be usable and "alive". Other than the list of components, entities hold no other data.

- Components are objects/classes that implement a Component interface with is only used to store them together in an entity's components list. Other than that, they can be anything. Some purists say that components should contain only data and no logic. My approach is less strict, but I try to follow the rule that logic in a component class should only handle the component and it's own data and not know about other component types, systems or any other parts of the game. This definitely helps to keep things loosely coupled and reusable. Some ultra-purists even say that components should be immutable and that whenever component data changes, an entire new component object should be created. I ignore that.

- Components are stores in an entity in a hash map with the Component's class type as the key. So, an entity can only have one component of each type. Also, inheritance is not used for defining component classes. I have experimented with that, but only ran into more problems than benefits.

- As a very useful helper tool to manage entities, I have an EntityList class which wraps a list of entities and provides a method to filter entities by their components ownership, i.e. "entityList.getEntititiesWith(ComponentClass1, ComponentClass2, etc.)". This method returns another EntityList, providing the same interface and allowing further filtering. EntityLists are used everywhere. Usually, a game has one "primary" EntityList that basically holds the entire game world (unless splitting it up into multiple lists is advisable for performance reasons). Other places where EntityLists are used are things like player inventory, and for filtering in "systems" that only operate on entities that have specific components.

- While "systems" are a core concept in my solution, they are *not* part of the library implementation at all. A system is basically just an object or function that operates on entities that have specific components (like a "GravitySystem" on entities that have a "PhysicsComponent" with properties like mass, velocity etc.). How exactly systems are implemented can vary and depends on the particular game.

You can take a look at my tiny ECS (actually just "EC" without the "S", I guess) library here: https://github.com/sebastian-bechtold/manfred-ecs-kotlin/blob/master/src/ManfredEcs.kt

As I said, it is really super small. It's written in Kotlin, but you could easily port it to any language you use.

Advertisement

Although Wurstbrot (guter Name!) is correct that ECS is, in general, an excellent approach to game architecture,  I would say in this particular case since it is for a fairly simple text based adventure, full ECS is overkill and I wouldn't bother with the 'S' part and instead treat the behaviours (components) as full objects with the logic as well as the data encapsulated.

7 hours ago, Irusan, son of Arusan said:

Although Wurstbrot (guter Name!) is correct that ECS is, in general, an excellent approach to game architecture,  I would say in this particular case since it is for a fairly simple text based adventure, full ECS is overkill and I wouldn't bother with the 'S' part and instead treat the behaviours (components) as full objects with the logic as well as the data encapsulated.

That's why I emphasized that ECS can actually be very simple and minimal while still providing a huge advantage. I think we are thinking and saying mostly the same.

I just recommend to give it a try and build the whole gameplay logic in the ECS pattern, since I have made very good experiences with that. At least with my small, simple games where I never had to care about performance or other things where my ultra-low-tech ECS approach might get in the way.

P.S.: Dankeschön :)

One way to go about it is to code what to do when the user enters a verb in a separate object, a Behavior object. Such a behavior object decides what should be done for a single item. Each item thus has a behavior object for each verb it responds to.

If the user types "open chest", the system looks for an 'open' behavior object in the chest item, and runs it if it exists. One Behavior class may always open the item, while another class would first look for a key Item in the inventory, for example.

Thus if you give the Chest Item a AlwaysOpenBehavior object, it would always open, while if you give it a OpenWithKeyBehavior object, you will need a key to open the chest item. This means that for every kind of 'open' you need to code a class, but that's unavoidable anyway (somewhere you do have to specify all possible variations that you want to have), but for different items with the same open behavior, you can re-use the same behavior class to make another 'open' object.

The next puzzle you'll run into is that you want to have a zillion items, each with a zillion behaviors. I would suggest you store such information in a textfile, so you can easily edit it, At startup, you load the textfile into the program, and build all items with the correct behaviors into your Level/Area.

I'd like to thank everyone that has been chatting in this thread. I only haven't responded as I didn't want to stifle  any responses.

On 1/18/2019 at 10:08 PM, wurstbrot said:

I agree with Irusan, and I'd like to go into a little more detail about his suggestion. It's one of my favorite topics in game dev, the entity component system (ECS) pattern - an incredibly simple yet powerful approach to solve exactly the sort of problem as you have it in a very elegant way.

I think for my particular case this "esc" pattern is really interesting, but might be to advanced for me right now as well as kinda requiring me to rewrite the entire project in a new and unfamiliar way. I ordered a book from ebay called "Game Engine Architecture" which was recommended in a number of threads and stackexchanges and stuff I read while googling this thing.

15 hours ago, Alberth said:

One way to go about it is to code what to do when the user enters a verb in a separate object, a Behavior object. Such a behavior object decides what should be done for a single item. Each item thus has a behavior object for each verb it responds to.

If the user types "open chest", the system looks for an 'open' behavior object in the chest item, and runs it if it exists. One Behavior class may always open the item, while another class would first look for a key Item in the inventory, for example.

This makes a lot of sense to me and also sounds like it can be done without making massive modifications to what I have already got. It is kinda a better version of my basic idea of having a "gameLogic" class as I spoke of above. 

It also occurs to me that this might have been what Irusan was saying at the start but the chat

On 1/18/2019 at 5:49 PM, Irusan, son of Arusan said:

I suggest making your item class a container for behaviours and then make these behaviours your unique classes.

Each behaviour would have verbs (look, take, open, etc) that determine how the player interacts with it. The box would then have a "container" behaviour that lets it be opened/closed and items taken in or out of it.

I am not sure why I didn't understand him. Maybe it was the talk of design patterns and stuff that confused me. Not that wurstbot didn't give good points, but my googling sent me to very complex idea used in large programs.

15 hours ago, Alberth said:

Thus if you give the Chest Item a AlwaysOpenBehavior object, it would always open, while if you give it a OpenWithKeyBehavior object, you will need a key to open the chest item. This means that for every kind of 'open' you need to code a class, but that's unavoidable anyway (somewhere you do have to specify all possible variations that you want to have), but for different items with the same open behavior, you can re-use the same behavior class to make another 'open' object.

The next puzzle you'll run into is that you want to have a zillion items, each with a zillion behaviors. I would suggest you store such information in a textfile, so you can easily edit it, At startup, you load the textfile into the program, and build all items with the correct behaviors into your Level/Area.

I have already written a working text file importer. I am using it at the mo to build the area's and items as well as setting some variables like bools and text descriptions and stuff. For example in the area file I have a list of item names, these names are read and used as file name to load data txt files for items and then they are read into a item class and that class is added to a list<Items> in the area. 

One of the goals of my project was to be able tom "write" the text adventure in text file with out editing individual code.


//--AREA_NAME: CommandTestRoom

//LIST_OF_ITEMS_IN_AREA--START
item1
item2
item3
item4
item5
//LIST_OF_ITEMS_IN_AREA--END

Basically the data reader takes lines from the txt file, reads a start and end line (I'm calling brackets) and then reads each individual line between those lines. I also built a way to read a single line with a keyword at the start. So in the example above it retrieves just "CommandTestRoom" and "item1" "item2" etc etc.

TL:DR

The question is how do I define "behaviour" classes in a text file? I can retrieve "names" from a file with the code I have in the datareader, but not sure how to translate that as "behaviours"

What I am thinking at the moment is a extension to the data reader for behaviours that is basically a giant switch statement. that adds different "behaviour" classes based on what string it is reading. 

So player types "open box"... the command processor sees that OPEN is available on that object, but when it triggers that particular item's "open" it is loading a unique behaviour.

So this would require an interface ... so I have to boil down all actions into some kind of base action.. like Open, Move, Take and stuff, and those actions trigger but are picking up unique code instead of all the same open code.

This might not be the best way I only thought this up as I wrote this post... I'm open to suggestions.

: GameDev.Net Blog: Open Blog; Read Blog : My uTube Chans : TragicTableTopTragicDeskTop

This topic is closed to new replies.

Advertisement