tags:

views:

444

answers:

12

Pardon my noobness. I'm making a game in which several characters have relationships with each other and they need to be able to interact with each other and store some relationship data regarding how they feel about each other.

I have an object for each character. Is it bad for each of those character objects to have an array of all the other character objects in order to perform these interactions? Is there a better way to do this?

EDIT to answer questions: My language is C#.

What I was thinking is that the setup class would make an array of all characters and then pass that array as a parameter to the constructor of each character.

As for the types of relationships, there are five characters, but I would like to make it extendable in case more are added later. The characters have conversations with each other. What they say is affected by their mood and how they feel about the character. Also, what another character says to them affects their mood and how they feel about the character. They can have one-on-one conversations or talk in groups, so character A could say something to character B, C, D, E, and F all at once.

By the way, I'm learning so much for from this thread. Thank you SO much, everyone!

A: 

Not necessarily, but it depends on how you're defining and referencing the relationships. For instance, what happens if you want to replace one character object with another? Will you have to go update all objects that reference that one?

Harold1983-
+4  A: 

No, this is not a bad practice at all. Obviously you could do it in such a way as to make it difficult to maintain or understand, but there's nothing inherently wrong with references between objects.

Though I would recommend whatever collection type your language provides rather than an array, there's nothing wrong with what you propose.

Adam Robinson
A: 

I have an object for each character. Is it bad for each of those character objects to have an array of all the other character objects in order to perform these interactions? Is there a better way to do this?

Interface in one word is the riposte for this. Else you will end up in mess updating all references at a point of time.

Vinothbabu
I don't get how interfaces help here.
Mathias
+5  A: 

It's generally good to avoid unnecessary references between objects, but the determination of what is unnecessary is very subjective and depends on the problem.

If every character has a unique identifier (e.g., a unique name), you could merely store, for each character, a collection of character identifiers and the "feeling" about that character. Using some other CharacterManager type, you could lookup a character based on the identifier.

Another option (may be preferable if opinions are just one minor facet of the game) is to have a separate "RelationshipManager" class, that can answer queries such as "How does chararacter X feel about character Y"? This might be a better design since that responsibility is managed separately from your core character. This manager could directly reference characters, or it could use the identifiers.

Uri
+2  A: 

I've noticed with object oriented design that if it sounds logical to someone who doesn't code, then you're probably good.

Say you have a taxi...the taxi should know what company it work for right?

Or a kid should know who its parent is right?

Obviously keep it within reason.

CheesePls
+2  A: 

One approach, when you have a dozen different objects that all reference each other, is to put something in the middle - a mediator - which holds all the references. Everyone then references other characters through the mediator. You reduce your reference count from n^2 to 2n - everyone knows the mediator, and it knows everyone.

Tesserex
+10  A: 

It's not bad practice for objects to retain references to each other - it's not uncommon for nodes in a tree graph to keep a reference to their child nodes, and for the child nodes to keep a reference to their parent node.

One thing that might become an issue is what you describe as each character keeping references to all other characters. That's an NxN matrix, and will explode your memory use as the number of characters increases.

An alternative to storing a list of all character objects in each character object might be to set up an associative list or dictionary where the key is (character 1, character 2) and the content is the relationship. This list could be global and store all relationships between all characters. The advantage here is that it grows linearly with the number of actual relationships, not exponentially with the number of characters in the system.

dthorpe
Depending on the number of characters to types of interactions this too could explode memory as the project scales. I agree that references are the way to go but persistient storage in an external datastore (Database) might be necessary to allow for scaling to larger populations of characters.
Jeremy E
His game won't have billions of interactable characters. Storing data in RAM is going to be infinitely more practical than in a database for a non-persistent application. I think that his solution is the right answer.
DeadMG
Hmm, interesting. It's a good point that measures for many, many characters might be unnecessary for a small number. Still, I want it to be extendable and it's probably good practice for the future to be able to make it work for many. Unless someone has a good reason to disagree?On another note, I'd prefer not to be referred to as "he." >_<
lala
It seems overkill to pass the array of all characters into each character constructor. For one thing, the array will be empty when you pass it to the first character constructor, so the first character won't be able to do much with the others because they haven't been initialized yet. You'll need to construct all the character objects first, populating the list, then go back and notify each character that the character list is complete. The character list can just be a global var so that all characters can get to it as needed.
dthorpe
+1  A: 

One alternative you could look into is the idea of a message centre. You have some central object whose job is to dispatch messages to other objects. Other objects express their interest in receiving messages by registering with the notification object. The benefit of this approach is that you decouple objects from each other, and also avoid memory issues related to cyclic reference retention. One disadvantage may be a bottleneck at the notification object if you are sending a tonne of messages.

darren
+2  A: 

If the relationship is long-lasting it would make sense to do so. I don't know how your game works, but it seems unlikely that all characters can interact with each other at any point in time. In more temporary situations you might want to implement some sort of a 'visit', where the game leader hands out references to the parties who meet.

You can also choose to reveal different levels of details through the usage of interfaces: If a Ghoul meets a Wizard he could just get a Human-reference to the Wizard, since the Ghoul doesn't treat different types of humans differently. By using this 'on need to know basis', you reduce the amount of coupling in your code, and you can reuse code better: You don't need to write any new code if a Ghoul visits a Farmer.

disown
+3  A: 

I have an object for each character. Is it bad for each of those character objects to have an array of all the other character objects in order to perform these interactions? Is there a better way to do this?

It really depends on the interactions between characters, which fall into two categories:

1) Actions - A mutates some state on B

In this case, you'd probably write something like:

class Player {
    Player[] players;

    ...

    public void ExchangeItem(Item item, string playerName) {
        Player target = players.First(x => x.Name = playerName);
        target.Items.Add(item);
        this.Items.Remove(item);
    }
}

2) Reactions - B listens to a change of state in A

You would not write:

public class Player {
    Player[] players;

    public void Die() {
        foreach(Player p in players)
            p.Mourn();
    }
}

Its about single-responsibility. Objects should only know and care about their own state and behaviors, they shouldn't know about others. In this case, the Die method seems to know too much about the way other players handle it.

Instead, you might re-write it as an event:

class Player {
    public event EventHandler PlayerDied;

    public void Die() {
        if (PlayerDied != null)
            PlayerDied(this, EventArgs.Empty);
    }
}

Other objects can hook on to the PlayerDied event and react the way they want.

Juliet
Even in the first example, wouldn't it be 'better' to have `ExchangeItem(Item item, Player player)` and then a `PlayerRegistry` object to handle `PlayerRegistry::getPlayerFromName(string playerName)` or even `PlayerRegistry::getAllPlayers()`
gnarf
gnarf: sure, why not :) Sample code is sample code, right ;)
Juliet
+2  A: 

In OO design, a common approach to a problem like this is the Mediator Pattern

A problem with the approach you describe is how will the relationships be updated? For example, if character A is removed from the game, how are other characters updated to remove character A from their relationships?

I would suggest having another Object that manages the relationships. This object can then be responsible for updating relationships. Using the same example, when character A is removed, the relationship manager simple removes all relationships involving that character.

Donal Boyle
Thank you for naming the Mediator pattern. Others mentioned the concept, but giving it a name helped me find more info on it.
lala
A: 

The problem that you described can be naturally modeled as a graph, where nodes are your game characters and relationships between them are edges of the graph. The edges can be directed or undirected depending upon the nature of relationships between your game characters. You could consider using a graph database like Neo4j if it suits your problem.

As far as keeping references, I think its important to keep in mind, the design principle of having loose coupling as far as possible in your designs.

abhinav