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

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.

主站蜘蛛池模板: 凌源市| 洛浦县| 朝阳区| 宜川县| 天气| 阜平县| 虎林市| 红桥区| 贵定县| 金门县| 江川县| 右玉县| 翁源县| 盐源县| 昌邑市| 丽水市| 门源| 寻乌县| 洪湖市| 石首市| 西安市| 巴彦县| 岢岚县| 扎赉特旗| 禄丰县| 房产| 东乡| 沅江市| 遂平县| 灵丘县| 新营市| 松滋市| 温宿县| 海安县| 增城市| 安达市| 美姑县| 奉贤区| 剑河县| 靖宇县| 定安县|