XBox:Framework:Data Objects
From MadoxLabs
|
Edit Downloads:XBox (http://www.madox.ca/mediawiki2/index.php?title=Template:Downloads:XBox&action=edit)
|
Data objects provide a disconnect between the game's state and the UI. This is critical because the UI shouldn't need to know what specifically is going on in the game, it is just a consumer of data values that it uses to create an interface.
Stating the Problem
The UI typically is used to display game values to the user, or it uses those values to determine what interfaces/widgets should be available. When the game changes that value, the UI should be able to get the new value easily so the change can be conveyed to the user. Ideally, the UI should have a reference to the data value so when it changes, there is no work to by the UI to use the new value.
The UI should also be a data consumer, which means that it shouldnt know what the producer of that data value is. If the data value is an enemy's health, the producer is the current enemy. When the current enemy changes, the health value referred to by the UI should now represent the new enemy, in an easy way. We dont want the game to have to update the UI every time it need to hand a new reference to the UI. There needs to be a way to change the producer without affecting the consumers.
Finally, if there is no current enemy, the UI needs to handle that by displaying a 0 or some default value. The lack of a producer shouldnt affect the consumers.
Data Library
At a high level, this is how the problem is solved:
mxData
mxData objects are used to represent a value. An mxData contains the value, so instead of using a regular int i you would use mxData<int> i and use i.Value where ever i was used.
The reason for this is that the data library needs to keep references to all the mxDatas, and you can not keep a reference to things like int or float without resorting to /unsafe mode. So it is less than ideal, but its usable. This worked better in C++, but C++ is all /unsafe mode.
Each mxData is also assigned a publish name. When a consumer asks for a data object by name, the mxData with that name is given out.
mxDataSource
This is a contain for all mxDatas that an object will publish. This container is then handed to the data library for publication. Only published mxData can be given out. Publishing objects with a given name replaces any existing objects published with that name.
The mxDataSource can also unpublish its collection, which should be done for sure in the destructor. Unpublishing msDatas makes the consumers resort to using default values until a new source publishes a replacement.
mxDataSource should be a base class, but we cant use multiple inheritance in C# so it has to be a composite.
mxWrapper
When a consumer wants to access a data object, the data library gives it a mxWrapper. This object contains the mxData so that the consumer can get the values, and the data library can swap out the mxData at anytime, invisibly to the consumer.
When a mxData is unpublished, it is swapped out with a default value. If the consumer requests a non-existant data object, it is also given a default value.
Example Usage
A data publisher runs the following code:
{
mxDataSource source = new mxDataSource();
mxDataLibrary lib = new mxDataLibrary();
// A float value to publish under the name PI
mxFloat value = new mxFloat("PI", 3.14159f);
// A list to publish under the name NAMES
mxDataList<string> list = new mxDataList<string>("NAMES");
list[0] = "Bob";
list[1] = "George";
list[2] = "David";
// publish that stuff
source.Publish(value);
source.Publish(list);
lib.Publish(source);
// POINT A: set a default for when PI isnt available - its not even a float!
lib.SetDefault(new mxString("PI", "You suck"));
// POINT B: modify original
value.Value = 9.8f;
// POINT C: replace it
mxDataSource source2 = new mxDataSource();
mxFloat value2 = new mxFloat("PI", 666f);
source2.Publish(value2);
lib.Publish(source2);
// POINT D: unpublish it
source2.Unpublish();
}
The consumer runs the following code once:
mxDataWrapper wrap = lib.GetData("PI");
float check;
At each point, POINT A,B,C,D, the comsumer runs the following code:
check = (float)wrap.Value(check);
System.Diagnostics.Debug.WriteLine("check: " + check + " " + wrap.Data.ToString());
The output is:
POINT A : check: 3.13159 3.14159 POINT B : check: 9.8 9.8 POINT C : check: 666 666 POINT D : check: 0 You Suck
To consume the list:
mxDataWrapper wrap2 = lib.GetData("NAMES");
for (int i = 0; i < wrap2.Count; ++i)
{
string val = "";
val = (string)wrap2.Value(val);
System.Diagnostics.Debug.WriteLine("name " + i + ": " + val);
}