HowTo/Upgrade GameObject.GetId()
The following is an example of how to update a script which uses the obsolete script function GameObject.GetId(). It is intended to be read by content creators already familiar with TrainzScript, but perhaps unfamiliar with the more modern concepts invovled with Asynchronous Route Streaming.
It is recommended readers first familiarise themselves with topics covered in the following pages:
Contents |
Overview
The script function GameObject.GetId() returns an integer ID which identifies an object within the script context. This ID is unique and constant for the life of the object only. When the specific object reference is released the ID becomes usable by other objects, and if the game is reloaded the world item represented by that GameObject (e.g. a Signal) will have some other ID.
This makes this ID of limited usefulness, and with the introduction of Route streaming they cannot be trusted and are considered obsolete. The modern replacement function is
public native GameObjectID GameObject.GetGameObjectID(void);
Getting an Object ID
The following example script shows a simple class which gets and stores objects integer ID.
class Example { int m_gameObjectID = -1; public void SetObject(GameObject obj) { // Save the objects ID. if (obj) m_gameObjectID = obj.GetId(); else m_gameObjectID = -1; } };
This class has a single member variable used to store the ID of an object, and a function which sets the object reference. To upgrade the script to support modern versions of Trainz, the integer ID must be replaced with a GameObjectID, and the GetId() call replaced with GetGameObjectID().
class Example { GameObjectID m_gameObjectID = null; public void SetObject(GameObject obj) { // Save the objects ID. if (obj) m_gameObjectID = obj.GetGameObjectID(); else m_gameObjectID = null; } };
Getting an Object from an ID
The above example is a demonstration only, and serves no useful purpose. To give it some purpose, let's consider a class which sets a junction direction. In older versions of Trainz this was quite simple, as the object was more or less guaranteed to remain loaded forever.
class Example { int m_junctionID = -1; public void SetJunction(Junction junc) { // Save the junctions ID. if (junc) m_junctionID = junc.GetId(); else m_junctionID = -1; } public bool SetJunctionDirection(SecurityToken token, int direction) { if (m_junctionID < 0) return false; Junction junc = cast<Junction>(Router.GetGameObject(m_junctionID)); if (!junc) return false; return junc.SetDirection(token, direction); } };
Now we have a potentially useful helper class. However, the approach will not work in TRS19 and later, because Route streaming means the object may be unloaded, and it's integer ID can change. As such, we must instead use the GameObjectID, as follows:
class Example { GameObjectID m_junctionID = null; public void SetJunction(Junction junc) { // Save the junctions ID. if (junc) m_junctionID = junc.GetGameObjectID(); else m_junctionID = null; } public bool SetJunctionDirection(SecurityToken token, int direction) { if (!m_junctionID) return false; Junction junc = cast<Junction>(Router.GetGameObject(m_junctionID)); if (!junc) return false; return junc.SetDirection(token, direction); } };
This approach is a direct replacement to the original functions, but if Route streaming is enabled and the junction has been unloaded, then a call to SetJunctionDirection() will fail. In many use cases this may be appropriate, and if the caller checks the return result from SetJunctionDirection() then they can reattempt the call later. However, if it's critical that the junction is updated immediately, then script can instead request that the junction is loaded, as follows:
public thread void SetJunctionDirection(SecurityToken token, int direction) { if (!m_junctionID) return; Junction junc = cast<Junction>(World.SynchronouslyLoadGameObjectByID(m_junctionID)); if (!junc) return; junc.SetDirection(token, direction); }
There are several important things to note about this new function.
- The function will now force the tile/section which contains the junction object to be loaded into memory. If this junction set is gameplay critical, the forcing the tile to load is likely acceptable, but it does come with a performance cost and script should avoid doing this over multiple items.
- The function is now declared as a thread. Script threads allow an object to use function calls like wait() and Sleep() and in this case, to perform a synchronous object load. This simplifies the update somewhat, but to use the SynchronouslyLoadGameObjectByID() helper function the calling function must be running on a thread, so that it can wait for the asynchronous query to complete.
- The function no longer has a return code. This is necessary because the function is now asynchronous, meaning that the caller will continue execution after calling SetJunctionDirection(), before SetJunctionDirection() has a chance to run or complete. If it is necessary for the caller to be notified of the success or failure of this call, then this is best achieved with message posts.
It is worth noting that an object cannot have too many thread functions running on it at once, so too many calls to SetJunctionDirection() will result in a script exception with the error code ER_ThreadError.