This tech tip explores the main feature of P3SampleApp: the rotating cube in the middle of the playfield.
This is a good exercise to see the architecture of a P3 application in action.
With P3SampleApp loaded in Unity, select the Home scene: select Assets > Scenes in the Project view and double-click on the Home scene in the Assets view to the right.
Select MovingTarget in the Hierarchy view. The Inspector view shows the MovingTarget Components.
The MovingTarget is a Cube mesh which needs a Mesh Renderer to display itself.
The Box Collider and Rigidbody Components enable collision detection with the ball.
The Moving Target (Script) is the P3SampleApp specific behavior.
Right-click the little cog to the right of the Moving Target (Script) in the Inspector view and select “Edit Script”.
This will open P3SampleApp\Assets\Scripts\GUI\Home\MovingTarget.cs in the code editor.
This assumes you have configured your External Script Editor. See the tech tip P3 Development Environment Setup.
The MovingTarget script runs on the GUI thread. The GUI thread is a typical Unity application. It handles the audio, the display, and object collision.
By convention, all application code running on the GUI thread is located somewhere under P3SampleApp\Assets\Scripts\GUI.
In the editor, scroll down to look at the OnTriggerEnter() method.
public void OnTriggerEnter(Collider other) { if (disabledCountdown < 0) // if we're not disabled { disabledCountdown = DISABLED_DURATION; // Allow a debounce period // We've been hit! Tell the modes about it. Multimorphic.P3App.Logging.Logger.Log("GUI layer: Target hit by " + other.name + ". Posting TargetHit event to mode layer."); PostGUIEventToModes("Evt_TargetHit", other.name); gameObject.GetComponent<Renderer>().material.color = Color.red; P3SAAudio.Instance.PlaySound("GroupTest"); } } |
Now assume the game is already started and the player has launched the ball. The SDK tracks the ball and moves a virtual ball avatar in Unity. When the ball avatar collides with the moving target, Unity calls OnTriggerEnter(). If the moving target is not disabled by a recent collision, this will send a GUI event to the modes. It will also change the color to red temporarily and play a sound.
The name of the GUI event is Evt_TargetHit. This is not an event defined by the P3 SDK. It is an application specific event. The event argument is the name of the other object that collided with the cube. In P3SampleApp, the only other object that can collide with the MovingTarget is the ball avatar.
Notice how the GUI does not take the initiative. Instead, it notifies the modes which will determine how the application responds to the new situation.
In Unity, select Assets > Scripts > Modes > GameModes in the Project view. Double-click on MovingTargetMode in the Assets view to the right. This will open P3SampleApp\Assets\Scripts\Modes\GameModes\MovingTargetMode.cs in the code editor.
This code runs on the mode thread. By convention, all application code running on the mode thread is located somewhere under P3SampleApp\Assets\Scripts\Modes.
MovingTargetMode is the mode that controls the MovingTarget from the application side. This mode handles the Evt_TargetHit event. First it has to register its interest in this event.
public MovingTargetMode (P3Controller controller, int priority) : base(controller, priority) { AddGUIEventHandler("Evt_TargetHit", TargetHitEventHandler); } |
Now look at the event handler itself.
public void TargetHitEventHandler(string evtName, object evtData) { // Target has been hit. Score some points. ScoreManager.Score (10000); if (hitCount++ > MAX_HITS) hitCount = 0; // Hit by whom? string hitBy = (string)evtData; Multimorphic.P3App.Logging.Logger.Log ("Mode layer: Received TargetHit event from GUI layer. Target hit by " + hitBy); // Tell the UI to move the target to a new position Multimorphic.P3App.Logging.Logger.Log ("Mode layer: Posting MoveTarget event to GUI layer to move to position " + hitCount.ToString()); PostModeEventToGUI("Evt_MoveTarget", hitCount); } |
The active modes that registered an interest in Evt_TargetHit will receive the event. In P3SampleApp, the only mode that registered an interest in Evt_TargetHit is MovingTargetMode. When the Evt_TargetHit event is received, its TargetHitEventHandler is called.
The mode reacts by adding 10000 to the score. After some logging, the mode decides the cube has to move to a new location determined by hitCount. Since moving the cube is a display operation, it sends the Evt_MoveTarget mode event to the GUI.
This code is in the same MovingTarget script we saw earlier.
protected override void CreateEventHandlers() { base.CreateEventHandlers (); AddModeEventHandler("Evt_MoveTarget", MoveTargetEventHandler); … } public void MoveTargetEventHandler(string eventName, object eventData) { // Mode is telling us to move Multimorphic.P3App.Logging.Logger.Log ("GUI layer: Received MoveTarget event from mode layer."); int index = (int) eventData; destination = new Vector3(-7f + index, gameObject.transform.localPosition.y, Random.Range (-2.5f, 2.5f)); } |
The components that registered an interest in Evt_MoveTarget will receive the event. In P3SampleApp, the only P3Aware component that registered an interest in Evt_MoveTarget is MovingTarget. When the Evt_MoveTarget event is received, its MoveTargetEventHandler is called.
This event handler simply modifies the final destination. The actual movement occurs in the Update() method.
public override void Update () { base.Update (); gameObject.GetComponent<Renderer>().material.color = Color.Lerp(gameObject.GetComponent<Renderer>().material.color, Color.blue, Time.deltaTime * 1.5f); gameObject.transform.localPosition = Vector3.Lerp(gameObject.transform.localPosition, destination, Time.deltaTime * SPEED); gameObject.transform.Rotate(new Vector3(1f, 2f, 3f)); if (disabledCountdown > 0) disabledCountdown -= Time.deltaTime; } |
The Update() method gradually changes the color back to blue, and gradually moves the cube to its final destination. It also rotates the cube a constant amount.
The name Lerp stands for Linear Interpolation. It is commonly used to smooth transitions between two values over time.
MovingTarget does not do this, but another common practice is to send a mode to GUI event to the SceneController like HomeSceneController. This centralizes the event handlers in the SceneController making them easy to find. This is a good option when the event affects multiple objects in the scene.
If the event is used in multiple scenes, a good place to implement the event handler is in the shared base class <appcode>SceneController. In P3SampleApp, that’s P3SASceneController.