githubEdit

AttachableList

Introduction

An AttachableList is a list which contains instances to IAttachablesarrow-up-right. Instances of IAttachablesarrow-up-right added to an AttachableList share a two-way relationship with the AttachableList by default. Common AttachableLists include the PositionedObjectListarrow-up-right and SpriteListarrow-up-right classes.

A short discussion about two-way relationships

While this article goes into extensive detail about two-way relationships, this paragraph provides a quick explanation of what it means. In short, a two-way relationship means that IAttachables (this includes PositionedObjectsarrow-up-right such as Spritearrow-up-right, Shapesarrow-up-right, and Entitiesarrow-up-right) keep track of all PositionedObjectListsarrow-up-right that they are a part of. The reason that this relationship exists is so that objects can remove themselves from all PositionedObjectListsarrow-up-right that they are a part of automatically when removing them through the engine. Any "Remove" method offered by FlatRedBall managers will remove the argument object from all of its lists - at least for all PositionedObjectarrow-up-right-inheriting objects. A simple code example shows this functionality:

Sprite sprite = SpriteManager.AddSprite("redball.bmp");
PositionedObjectList<Sprite> first = new PositionedObjectList<Sprite>();
PositionedObjectList<Sprite> second = new PositionedObjectList<Sprite>();
PositionedObjectList<Sprite> third = new PositionedObjectList<Sprite>();

first.Add(sprite);
second.Add(sprite);
third.Add(sprite);

// at this point, the Sprite is part of all 3 PositionedObjectLists.

SpriteManager.RemoveSprite(sprite);

// The RemoveSprite method removes the Sprite from the FlatRedBall engine,
// as well as any PositionedObjectLists that the Sprite is a part of:
bool isSpritePartOfAnyList = first.Contains(sprite) || second.Contains(sprite) || third.Contains(sprite);

// isSpritePartOfAnyList will be false

Two Way Relationships

A two way relationship is a relationship between an object that implements the IAttachablearrow-up-right interface and a class that inherits from the AttachableList class. Normally when objects are added to lists, the list stores any number of objects, but the objects themselves are not aware of which lists they belong to. These are known as "one-way" relationships: the knowledge of membership only exists in the list object. A two-way relationship, on the other hand, is one where the list stores all of its contained objects, and all of the contained objects are aware of all of the lists they belong to. When an IAttachablearrow-up-right is added to an AttachableList, the following occurs:

And similarly, when an IAttachablearrow-up-right is removed from an AttachableList, the following occurs:

Reason for Two-Way Relationships

Two way relationships exist to eliminate scope problems when removing an IAttachable completely. The FlatRedBall Engine often uses AttachableLists internally to categorize Sprites. For example, Sprites which belong to the SpriteManager can be either automatically updated or manually updated. Each is stored in a different array. Similarly, it is common practice to create AttachableLists to organize objects by their behavior. Common examples include placing Polygons which represent triggers in a level in a PositionedObjectListarrow-up-right or to organize player bullets and enemies in separate SpriteListsarrow-up-right. A bullet and enemy SpriteListarrow-up-right example clearly shows the kind of problems which can occur in the absence of two-way relationships. When a bullet collides with an enemy, it should be completely removed from the game. Not only does it have to be removed from any internal SpriteListsarrow-up-right (such as lists in the SpriteManagerarrow-up-right), but also from the programmer-created list of bullets that it belongs to. In the absence of a two-way relationship, at least two calls would be required:

  1. Remove the Sprite from the engine

  2. Remove the Sprite from bullet list

This is inconvenient at best. It also requires code to be written in pairs (or more if an object is part of more lists). While the previous example highlights inconveniences, there is another practical reason for having two-way relationships. Sometimes the code that creates a PositionedObject and the code that destroys a PositionedObject may exist in two different areas of your project. The code that destroys a PositionedObject may not have access to all of the PositionedObjectLists that a given PositionedObject is a part of. The automatic removal enabled by two-way relationships enables removal without having to track down all lists. The two-way relationships between IAttachablesarrow-up-right and AttachableLists enables any method to remove objects from all lists both engine and user created through the use of globally-visible static managers such as the SpriteManagerarrow-up-right and TextManagerarrow-up-right. Simply calling Remove on an object's manager automatically removes it from all lists that it shares two-way relationships with. The following code creates a Spritearrow-up-right, adds it to the SpriteManagerarrow-up-right, adds it to a SpriteListarrow-up-right, then removes it from the SpriteManagerarrow-up-right. Next the SpriteListarrow-up-right is checked to see if it still has a reference to the Spritearrow-up-right. If it does, the application exits. Since it does not, your application will not exit.

One-Way Methods

IAttachablesarrow-up-right can also be added to AttachableLists creating one-way relationships using the AddOneWay method. In a one-way relationship, the AttachableList knows about its contained IAttachablesarrow-up-right, but the IAttachablesarrow-up-right do not know about their list. This is similar to how normal Arrays and Lists work. Usually one way relationships are useful when the scope of an AttachableList is brief - usually one frame or non-Main method. There are two common situations in which one-way AttachableLists are used:

  1. Methods wich return AttachableLists usually fill them using AddOneWay. The assumption is that these lists will have a short lifespan.

  2. Short-lifespan AttachableLists created outside of the engine should have elements added to them using one-way methods. Any AttachableList which has a lifespan of one frame or less should usually be populated using AddOneWay.

The reason for two-way relationships is to remove unused IAttachablesarrow-up-right from all AttachableLists which reference them. However, if an AttachableList has a short lifespan, then it will likely fall out of scope before any IAttachablesarrow-up-right that it references are removed. One reason to use one-way relationships for short-lifespan AttachableLists is to reduce the overhead of creating and managing two-way relationships. The Add method is slightly slower than AddOneWay as two-way relationships require two Add calls to internal List<> objects. Another reason is that additional references reduce the efficiency of the garbage collector. A third reason to avoid two-way relationships for short-lifespan AttachableLists is exemplified by the following code. First, consider what occurs when the following code is executed.

TwoWay1.png
TwoWay2.png

TwoWay3.png Now, consider the result of using AddOneWay instead of Add:

TwoWay1.png
OneWay2.png
OneWay3.png

Converting Between Two-Way and One-Way

There are times when an AttachableList should be converted between a two-way and one-way AttachableList. One common scenario is when a one-way AttachableList returned from a method is going to be used for longer than one frame. In this example, all Sprites with the word "collision" in the first SpriteListarrow-up-right are added to the second. Since FindSpritesWithNameContaining returns a one-way SpriteListarrow-up-right, two-way relationships are created for all Spritesarrow-up-right in the the second SpriteListarrow-up-right.

Similarly, all AttachableLists have a MakeOneWay method which makes the relationship with all contained IAttachablesarrow-up-right one-way.

"foreach" allocates memory

Using a foreach statement on an AttachableList will allocate memory. This is a very common gotcha when working with AttachableLists (and PositionedObjectLists). Consider the following to improve the performance of your application:

AttachableList Members

Did this article leave any questions unanswered? Post any question in our forumsarrow-up-right for a rapid response.

Last updated

Was this helpful?