What is your opinion and how would you do a structure that has multyple functionalities

Started by
10 comments, last by Servant of the Lord 9 years, 11 months ago

Hello.

I was thinking how to make a structure know its functionalities in my 2d top down game.

The first thought i had is make bit flags and turn on and off what the structure does:
For example:


enum StructureFunctions{

        SFStorage = 0x01,//Used to drop resources
        SFRest = 0x02,//Used to sleep at night
        SFTownHall = 0x04,//Used to get actions from
        SFBarrack = 0x08,//Used for training/ getting battle/scout/guard actions for day
        SFOnWater = 0x10,//This structure is on water
        SFOnLand = 0x20,//This structure is on land
        SFOperationable = 0x40,//Uses people to operate, instead of automatic
        SFProduces = 0x80,//Produces resources

    };
class Structure
{
public:
       int Functions;
};
 
//Somewhere in cpp
Structure Farm;
Structure FishingDocks;
Structure Church;
Structure Barrack;
 
Farm.Functions = SFProduces | SFOperationable | SFOnLand | SFStorage;
FishingDocks.Functions = SFProduces | SFOperationable | SFOnLand | SFOnWater | SFStorage;
Church.Functions = SFRest | SFOnLand | SFProduces;
Barrack.Functions = SFRest | SFBarrack | SFOnLand;

The big question is:
I require more flag states, and i never worked with them so i got no experience in that field except tutorials and examples i saw.

How do i make specific structures have attributes as such that would be efficient?

I need to be able to make a object of structure and the structure stats with base value "Is on land", then i will add crafting table intro the structure i will turn "Crafting" on for that structure, if i add barrel intro it i will add "Storage"

Then when i require something as


//A unit just finished harvesting wood, find a place to drop resources
for(all structures)
{
    if(Structure.Function has Storage)
        //Check if storage has space left
        //Check if storage can take type the unit is carrying (Barrel, is made for liquid or small type objects as fish, not for wood storage)
}
//A unit has decided to go rest
for(all structures)
{
    if(Structure.Function has Rest)
        //If not occupied
}

Have you done anything like this before? would you be willing to trow some ideas this way?

If you didn't do something like this before? would you be willing to trow some ideas this way.

Advertisement

Slightly non-standard (de-jure), but with most compilers implementing it the same way (de-facto), you could use anonymous structs in a union holding bitfields.


union StructureFlags
{
     uint32_t bits = 0; //Holds all the bits, for when you save/load the game.
     struct
     {
          bool locatedOnWater : 1; //Use only a single bit.
          bool locatedOnLand : 1;
          bool canSleepHere : 1;
          bool canOperate : 1;
          
     };
};

struct Structure
{
     std::string displayName = "Unnamed";
     StructureFlags flags;
     int remainingHealth = 100;

     int numberOfPeopleInside = 0;
     
     int foodProductionPerMinute = 0;
     int ironProductionPerMinute = 0;
     int woodProductionPerMinute = 0;
};

bools normally use more than a single bit, but if you use bit fields, you can compact multiple of them into the same bytes.

You can read more of bit fields here.

If you want to continue using enums and bit flags, which is also a great option and more commonly done (and better supported), your function:


if(Structure.Function has Storage)

Goes like this:


if(Structure.Function & SFStorage)

Note that you use a single &, not a double &&.

Slightly non-standard (de-jure), but with most compilers implementing it the same way (de-facto), you could use anonymous structs in a union holding bitfields.


union StructureFlags
{
     uint32_t bits = 0; //Holds all the bits, for when you save/load the game.
     struct
     {
          bool locatedOnWater : 1; //Use only a single bit.
          bool locatedOnLand : 1;
          bool canSleepHere : 1;
          bool canOperate : 1;
          
     };
};

struct Structure
{
     std::string displayName = "Unnamed";
     StructureFlags flags;
     int remainingHealth = 100;

     int numberOfPeopleInside = 0;
     
     int foodProductionPerMinute = 0;
     int ironProductionPerMinute = 0;
     int woodProductionPerMinute = 0;
};

bools normally use more than a single bit, but if you use bit fields, you can compact multiple of them into the same bytes.

You can read more of bit fields here.

If you want to continue using enums and bit flags, which is also a great option and more commonly done (and better supported), your function:


if(Structure.Function has Storage)

Goes like this:


if(Structure.Function & SFStorage)

Note that you use a single &, not a double &&.

That is quite useful, i did not even know such a thing existed.

Well this pretty much solves every problem i had cool.png thanks, not the first time you helped big time, i will make sure to get big flashy thank you in my game!

I cant figure out how to use it simple as


//This is the syntax i want to use to set the flags.
objFarm = SFStorage | SFRest;//The objFarm in main will now have these two flags set on
 
enum StructureFunctions

{

    SFStorage = 0x01,//Used to drop resources

    SFRest = 0x02,//Used to sleep at night

    SFTownHall = 0x04,//Used to get actions from

    SFBarrack = 0x08,//Used for training/ getting battle/scout/guard actions for day

    SFOnWater = 0x10,//This structure is on water

    SFOnLand = 0x20,//This structure is on land

    SFOperationable = 0x40,//Uses people to operate, instead of automatic

    SFProduces = 0x80,//Produces resources

};



struct holder

{

    holder();

    bool a : 1;

    bool b : 1;

    bool c : 1;

};

holder::holder()

{

    a = 0;

    b = 0;

    c = 0;

}



int main()

{

    holder objFarm;

    objFarm = SFStorage & SFRest;
//...

}

And a line like this for the operator =


holder operator=(StructureFunctions strucFunc);

dosent allow me to write code like


//Case 1
objFarm = SFStorage & SFRest;

But i can


//Case 2
objFarm = SFStorage;

Which is understandable to me why it does that, but how would i be able to assign the values like in case 1. what do i need to do?

I think the problem is because when you try to use a bitwise operator on two enum values, it returns an int. I searched on google and found this stackoverflow post about using bitwise operators with enums. eidolon's solution was to override the operator for your enum, so it'd look like this:


inline StructureFunctions operator|(StructureFunctions a, StructureFunctions b) {
	return static_cast<StructureFunctions>(static_cast<int>(a) | static_cast<int>(b));
}



inline StructureFunctions operator&(StructureFunctions a, StructureFunctions b) {
	return static_cast<StructureFunctions>(static_cast<int>(a) & static_cast<int>(b));
}

Edit: I just realized that using these operators could return a number not defined by a member of the enum. I don't know how enums in C++ work though so I don't know if this is a problem or not. There were some answers on the stack overflow question saying to use int, rather than StructureFunctions as the type of your variables using it.

Falling block colour flood game thing I'm making: http://jsfiddle/dr01d3k4/JHnCV/

Sorry, I didn't explain fully.

Use the | to bitwise 'OR' two flags together.
Use the & to bitwise 'AND' two flags together.

This is similar to how || and && work on bools, except it works on a bit level. (bitwise operators)

These aren't specifically designed for holding flags - they are used for manipulating bits in bytes and can be used for whatever purpose.

We can manipulate the bits to use the bits as separate flags, which is what we are doing here (and why it's slightly more confusing at first than what should be a simple task - it's a general-purpose tool).


objFarm = SFStorage | SFRest; //Add two flags together.

if(objFarm & SFStorage)
{
   //...The farm has 'SFStorage' on it.
}

objFarm |= SFProduction; //Add another flag into it.

objFarm &= ~(SFProduction); //Remove a flag.
objFarm &= ~(SFProduction|SFStorage); //Remove multiple flags.

objFarm = SFRest; //Remove every flag, and add 'SFRest'.

Note: For this to work, the flags must each have their own bit, and can't overlap with other flags' bits. This means they must have the values 1, 2, 4, 8, 16, 32, etc... (which is why the hexadecimal values you are using go 0x01, 0x02, 0x04, etc...).

1 = 00001

2 = 00010

4 = 00100

etc...

I personally would avoid the bit flags. Unless you're going to be hurting for space because you have 100,000, or a million objects, then I'd suggest just using something simple, like a set, and I'd suggest you load all these attributes via a data file.

For example, you could have a json data file like this:

{"Structures" :
 {
  "Farm" :
   ["Produces", "Operationable", "OnLand", "Storage"],
  "FishingDocks" :
   ["Produces", "Operationable", "OnLand", "OnWater", "Storage"]
 } 
}

Then in your code, you can just do this:

 
// load the objects into a simple structure like this
class Structure
{
  public:
    std::string Name;
    std::set<std::string> Attributes;
}
 
std::vector<Structure> Structures;
 
// use a json library, jsoncpp or jansson, to load the structures and attributes
 
// Now just look for each attribute in your list of Structures
for (uint32_t i = 0; i < Structures.size(); ++i) {
  if (structures[i].Attributes.find("Produces") {
    // do producing thing
  }
}
 

Of course, all of this looks a lot like a component based entity system. So, it might be easier to use a library already created to handle this kind of thing for you. While I have a partially completed library, I think entitiyx looks good.

Either way, good luck, and have fun.

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

Sorry, I didn't explain fully.

Use the | to bitwise 'OR' two flags together.
Use the & to bitwise 'AND' two flags together.

This is similar to how || and && work on bools, except it works on a bit level. (bitwise operators)

These aren't specifically designed for holding flags - they are used for manipulating bits in bytes and can be used for whatever purpose.

We can manipulate the bits to use the bits as separate flags, which is what we are doing here (and why it's slightly more confusing at first than what should be a simple task - it's a general-purpose tool).


objFarm = SFStorage | SFRest; //Add two flags together.

if(objFarm & SFStorage)
{
   //...The farm has 'SFStorage' on it.
}

objFarm |= SFProduction; //Add another flag into it.

objFarm &= ~(SFProduction); //Remove a flag.
objFarm &= ~(SFProduction|SFStorage); //Remove multiple flags.

objFarm = SFRest; //Remove every flag, and add 'SFRest'.

Note: For this to work, the flags must each have their own bit, and can't overlap with other flags' bits. This means they must have the values 1, 2, 4, 8, 16, 32, etc... (which is why the hexadecimal values you are using go 0x01, 0x02, 0x04, etc...).

1 = 00001

2 = 00010

4 = 00100

etc...

For some reason following line was not allowed by the compiler i had error. I hope it was a mistake on my part.


objFarm = SFStorage | SFRest; //Add two flags together.

That is why you see in the following i used ampersand i couldn't get it to compile. I will try to write it again and see what i got.


int main()
{
    holder objFarm;
    objFarm = SFStorage & SFRest;

But all in all, bit fields are great!, i did not know they even existed until this post!
And id really like to control them with enum types.

I personally would avoid the bit flags. Unless you're going to be hurting for space because you have 100,000, or a million objects, then I'd suggest just using something simple, like a set, and I'd suggest you load all these attributes via a data file.

For example, you could have a json data file like this:

{"Structures" :
{
"Farm" :
["Produces", "Operationable", "OnLand", "Storage"],
"FishingDocks" :
["Produces", "Operationable", "OnLand", "OnWater", "Storage"]
}
}

Then in your code, you can just do this:


// load the objects into a simple structure like this
class Structure
{
public:
std::string Name;
std::set<std::string> Attributes;
}

std::vector<Structure> Structures;

// use a json library, jsoncpp or jansson, to load the structures and attributes

// Now just look for each attribute in your list of Structures
for (uint32_t i = 0; i < Structures.size(); ++i) {
if (structures.Attributes.find("Produces") {
// do producing thing
}
}

Of course, all of this looks a lot like a component based entity system. So, it might be easier to use a library already created to handle this kind of thing for you. While I have a partially completed library, I think entitiyx looks good.

Either way, good luck, and have fun.

Those libraries are great, worth bookmarking, thanks.

But the reason i don't load from external file the structures is because they are dynamic, i don' know a farm is farm.

To know if X posses a farm i check if he has Land(Grow crops, trees...) or livestock and a building(with tools inside for cultivating land, livestock inside...).

There fore i decide what a building is depending on what is inside on fly.

It seems like you are trying to implement a component system - I agree that you don't really need bit fields to do that - why don't you do something like make a base struct called Component (or something similar) and then make all of your other structs inherit from that struct?

In your objects you could do something like have a vector of pointers to Component structs.. then you can add and remove components dynamically

You could either create virtual serialize and deserialize functions in your base component struct that inheriting structs must define for writing and reading their contents to and from file - or if you make all parts of your struct components constant sized (ie no std::string or pointers or anything like that) you could directly read and write them to file as lump memory blocks ( outFile.write((char*) myComponent, sizeof(MyComponent)) )

Just an idea


For some reason following line was not allowed by the compiler i had error. I hope it was a mistake on my part.
objFarm = SFStorage | SFRest; //Add two flags together.
That is why you see in the following i used ampersand i couldn't get it to compile. I will try to write it again and see what i got.

This works if objFarm is an int. I'm not quite sure what you're wanting to achieve with your holder struct, but you add an "int type" field or something, and then do

objFarm.type = SFStorage | SFRest;

Hello to all my stalkers.

This topic is closed to new replies.

Advertisement