官术网_书友最值得收藏!

Message registration

The following code contains a pair of simple classes that register with the Messaging System, each requesting to have one of their methods called whenever certain types of messages have been broadcast from anywhere in our codebase:


public class EnemyManagerWithMessagesComponent : MonoBehaviour {
private List<GameObject> _enemies = new List<GameObject>();
[SerializeField] private GameObject _enemyPrefab;

void Start() {
MessagingSystem.Instance.AttachListener(typeof(CreateEnemyMessage),
this.HandleCreateEnemy);
}

bool HandleCreateEnemy(Message msg) {
CreateEnemyMessage castMsg = msg as CreateEnemyMessage;
string[] names = { "Tom", "Dick", "Harry" };
GameObject enemy = GameObject.Instantiate(_enemyPrefab,
5.0f * Random.insideUnitSphere,
Quaternion.identity);
string enemyName = names[Random.Range(0, names.Length)];
enemy.gameObject.name = enemyName;
_enemies.Add(enemy);
MessagingSystem.Instance.QueueMessage(new EnemyCreatedMessage(enemy,
enemyName));
return true;
}
}

public class EnemyCreatedListenerComponent : MonoBehaviour {
void Start () {
MessagingSystem.Instance.AttachListener(typeof(EnemyCreatedMessage),
HandleEnemyCreated);
}

bool HandleEnemyCreated(Message msg) {
EnemyCreatedMessage castMsg = msg as EnemyCreatedMessage;
Debug.Log(string.Format("A new enemy was created! {0}",
castMsg.enemyName));
return true;
}
}

During initialization, the EnemyManagerWithMessagesComponent class registers to receive messages of the type CreateEnemyMessage, and will process the message through it's HandleCreateEnemy() delegate. During this method, it can typecast the message into the appropriate derived message type and resolves the message in its own unique way. Other classes can register for the same message and resolve it differently through its own custom delegate method (assuming that an earlier listener didn't return true from its own delegate).

We know what type of message will be provided by the msg argument of the HandleCreateEnemy() method, because we defined it during registration through the AttachListener() call. Due to this, we can be certain that our typecasting is safe, and we can save time by not having to do a null reference check, although, technically, there is nothing stopping us using the same delegate to handle multiple message types. In these cases, though, we will need to implement a way to determine which message object is being passed and treat it accordingly. However, the best approach is to define a unique method for each message type in order to keep things appropriately decoupled. There really is little benefit in trying to use one monolithic method to handle all message types.

Note how the HandleEnemyCreated() method definition matches the function signature of MessageHandlerDelegate (that is, it has the same return type and argument list), and that it is being referenced in the AttachListener() call. This is how we tell the Messaging System what method to call when the given message type is broadcast, and how delegates ensure type-safety. If the function signature had a different return value or a different list of arguments, then it would be an invalid delegate for the AttachListener() method, and we would get compiler errors. Also, note that HandleEnemyCreated() is also a private method, and yet our MessagingSystem class is able to call it. This is a useful feature of delegates in that we can allow only systems we give permission to call this message handler. Exposing the method publicly might lead to some confusion in our code’s API, and developers may think that they’re meant to call the method directly, which is not its intended use.

The beautiful part is that we're free to give the delegate method whatever name we want. The most sensible approach is to name the method after the message which it handles. This makes it clear to anyone reading our code what the method is used for and what message object type must be sent in order to call it. This makes future parsing and debugging of our code much more straight-forward since we can follow the chain of events by the matching names of the messages and their handler delegates.

During the HandleCreateEnemy() method, we also queue another event, which broadcasts an EnemyCreatedMessage instead. The second class, EnemyCreatedListenerComponent registers to receive these messages, and then prints out a message containing that information. This is how we would implement a way for subsystems to notify other subsystems of changes. In a real application, we might register a UI system to listen for these types of messages, and update a counter on the screen to show how many enemies are now active. In this case, the enemy management and UI systems are appropriately decoupled such that neither needs to know any specific information about how the other operates in order to do their assigned tasks.

If we now add the EnemyManagerWithMessagesComponent, EnemyCreatorComponent and EnemyCreatedListenerComponent to our Scene, and press the Space Bar several times, we should see log messages appear in the Console window, informing us of a successful test:

Note that a MessagingSystem Singleton object will be created during Scene initialization, when either the EnemyManagerWithMessagesComponent object's or EnemyCreatedListenerComponent object's Start() methods are called (whichever happens first), since that is when they register their delegates with the Messaging System, which accesses the Instance property, and hence creates the necessary GameObject containing the Singleton Component. No additional effort is required on our part to create the MessagingSystem object.

主站蜘蛛池模板: 无棣县| 赣榆县| 曲靖市| 历史| 潼南县| 康乐县| 崇左市| 塔河县| 博乐市| 寿光市| 普格县| 鄢陵县| 遂昌县| 铜鼓县| 宜黄县| 金坛市| 万载县| 友谊县| 于都县| 望江县| 平果县| 石首市| 昌江| 准格尔旗| 乌什县| 德令哈市| 沙湾县| 开化县| 临颍县| 洞口县| 奇台县| 黑山县| 赫章县| 襄汾县| 谷城县| 延津县| 威信县| 稻城县| 平阳县| 苍梧县| 云安县|