Jump to content
  • Advertisement
  • entries
    14
  • comments
    2
  • views
    2107

Project: ABx

Friend Foe identification

Sign in to follow this  
trill41

836 views

The game mechanics doesn't allow to use damage skills on allies and heal or protection skills on foes. This makes it necessary that the game can identify allies and foes. A simple if statement seems to be sufficient for that, but it's not. There can be many different combinations of relations between the groups in a game, see the image.

friend_foe.png.90b84f89e2cef83f6be1d56ba3a6565a.png

 

So there are different groups which have different relations to each other. So how to solve this problem? One could have the idea to maintain a list of friend and foe groups, but well, not really, because that's probably the most inefficient way to do this. Instead I chose to use a Bit mask which encodes the friends as well as the foes. The Actor class was extended as shown bellow:

class Actor
{
private:
    /// Lower 16 bit friends, upper 16 bit foes
    /// This gives us 3 types of relation: (1) friend, (2) foe and (3) neutral
    /// and (4) in theory, both but that would be silly.
    unsigned groupMask_{ 0 };
    /// Get lower 16 bits of the group mask
    unsigned GetFriendMask() const { return groupMask_ & 0xffff; }
    /// Get upper 16 bits of the group mask
    unsigned GetFoeMask() const { return groupMask_ >> 16; }
public:
    void AddFriendFoe(unsigned frnd, unsigned foe)
    {
        groupMask_ |= (frnd | (foe << 16));
    }
    void RemoveFriendFoe(uint32_t frnd, uint32_t foe)
    {
        groupMask_ &= ~(frnd | (foe << 16));
    }
    bool IsAlly(const Actor* other) const
    {
        // Return true if they have matching bits in the friend mask
        return ((GetFriendMask() & other->GetFriendMask()) != 0);
    }
    bool IsEnemy(const Actor* other) const
    {
        // Return true if we have a matching bit of our foe mask in their friend mask
        return ((GetFoeMask() & other->GetFriendMask()) != 0);
    }
};

This makes it possible to have 16 different groups on one map, which have different relations to each other. To define who is friend with whom and who is an enemy of whom, only the AddFriendFoe() method has to be called when the Actor is created:

void CreateActorsForGame()
{
    static const unsigned GROUPMASK_NONE = 0;
    static const unsigned GROUPMASK_1 = 1;
    static const unsigned GROUPMASK_2 = 1 << 1;
    static const unsigned GROUPMASK_3 = 1 << 2;
    static const unsigned GROUPMASK_4 = 1 << 3;
    static const unsigned GROUPMASK_5 = 1 << 4;
    static const unsigned GROUPMASK_6 = 1 << 5;
    static const unsigned GROUPMASK_7 = 1 << 7;
    static const unsigned GROUPMASK_8 = 1 << 8;
    // Can have up to 8 more
    static const unsigned GROUPMASK_ALL = 65536;

    Actor trojan1;
    trojan1.AddFriendFoe(GROUPMASK_1, GROUPMASK_2 | GROUPMASK_3);
    Actor trojan2;
    trojan2.AddFriendFoe(GROUPMASK_1, GROUPMASK_2 | GROUPMASK_3);

    Actor pacifist;
    pacifist.AddFriendFoe(0, 0);

    Actor roman;
    roman.AddFriendFoe(GROUPMASK_2, GROUPMASK_1 | GROUPMASK_3);

    Actor player;
    player.AddFriendFoe(GROUPMASK_3, GROUPMASK_1 | GROUPMASK_2);
}

 

Sign in to follow this  


0 Comments


Recommended Comments

Quote

One could have the idea to maintain a list of friend and foe groups, but well, not really, because that's probably the most inefficient way to do this.

In terms of? There are tradeoffs to different methods, that might be worth mentioning/exploring. The list method may allow for a generic implementation of "relationships" between groups. Have you actually benchmarked/measured any of it? Even if yours is faster, don't under-estimate the value of using collections, in any language that has expressive methods/functions for them. Coding efficiency must also be balanced and taken into consideration.

Also, as you listed, your system has the potentially invalid/nonsensical state where two groups could be both friend and foe. In general, the possibility for invalid state should be avoided.

Share this comment


Link to comment
59 minutes ago, Scienthsine said:

In terms of? There are tradeoffs to different methods, that might be worth mentioning/exploring. The list method may allow for a generic implementation of "relationships" between groups. Have you actually benchmarked/measured any of it?

You are right, the wording was a bit bad. I can imagine situations where a list maybe the way to go. I did not benchmark any of those, but I imagine some bit operations should be faster than looking up an item in a list (if lookup is not O(1)). Another point was, I'm a "lazy typer", and this are just some lines of code. And I didn't want to mess up the Actor class to much, since it's already complicated.

59 minutes ago, Scienthsine said:

Also, as you listed, your system has the potentially invalid/nonsensical state where two groups could be both friend and foe. In general, the possibility for invalid state should be avoided.

I wouldn't say invalid, nonsense, yes, but that could also lead to funny results 😁. But even if you have 2 lists for friends and foes, it has the same problem. Groups can be in both of them, if you don't take care. However, I agree that my method may be more error prone.

I was looking for solutions how other games solved this problem. I think this must be a common problem in games, but I didn't find much (maybe I didn't look hard enough). So I thought I throw it in and see what others think.

Thank you for the comment.

Edited by trill41

Share this comment


Link to comment
1 hour ago, Scienthsine said:

Also, as you listed, your system has the potentially invalid/nonsensical state where two groups could be both friend and foe. In general, the possibility for invalid state should be avoided.

Or better, an uneasy alliance between two groups.

Share this comment


Link to comment
1 hour ago, trill41 said:

I wouldn't say invalid, nonsense, yes, but that could also lead to funny results 😁. But even if you have 2 lists for friends and foes, it has the same problem. Groups can be in both of them, if you don't take care. However, I agree that my method may be more error prone.

True, true, and probably not.

Looks good to me. :) 

The only real downside I see is that it's less scalable than if you used a more naive, possibly less performant approach. Changing the underlying code to increase the number of groups will be nasty, especially if you exceed the double-word size of your platform. If the game design doesn't require/allow/warrant more though, then it's irrelevant.

Historically, I imagine older games used bitmasks quite a bit for this sort of thing. Nowdays, I would imagine most engines leverage collection/set library or language features.

There certainly _are_ a few instances where bitmasks may be much, much better. For instance, if you draw enemies as red, and friendlies as their team color, then sending this bit mask to the shader seems much easier.

Anyway, looks good to me. Maybe replace those magic number with some properly defined constants.  

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!