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

Building an interactive object

With these requirements in mind, let's build the framework for an interactive object that can be collected by the player.

Implementing the CustomGameObj script

We will begin with the CustomGameObj class. This class allows us to specify how an interactive object will behave when placed in the inventory, by giving it a unique type that is relevant for our game. Create the script by performing the following steps:

  1. Start from the codebase built in Chapter 1, Introduction to E-Learning and the Three Cs of 3D Games, to create a new subfolder in the assets folder named Chapter 2.
  2. Using the new script wizard, right-click on it and create a new C# script named CustomGameObject.
  3. We will also add a public enumerated type to this class called CustomObjectType. If you recall, an enumeration is just a list of identifiers of the integer type that share a common logical relationship with one another, such as the types of an object! Not only will this make discerning the type of this object easy to read in the code, but it also serves as an interface to describe the classification of this object. We will use this information to determine some custom rules while adding GameObjects to the inventory. To begin, we will start with a few different types of objects, where an object of the Coin type will accumulate in the same slot in the inventory. This holds true for objects of the type Ruby, Diamond, and so on as well. Unique objects will be added in their own slot in InventoryMgr as follows:
    Public enum CustomObjectType
    {
      Invalid = -1,
      Unique = 0,
      Coin = 1,
      Ruby = 2,
      Emerald = 3,
      Diamond = 4
    
    }
  4. A variable of the CustomObject type is added to this class to store the current type from the set discussed in the previous step. We use the public keyword so that a user can directly set the value of this variable inside the Unity Editor on an instance of the object:
    public CustomObjectTypeobjectType CustomObjectType objectType;
  5. A public variable of the string type is added so that Unity users can add some descriptive text to the object while designing them, as shown in the following code; this can be very helpful while debugging or trying to identify the objects inside the editor:
    public string displayName;
  6. Declare a method named validate(), which will be used to assign the unnamed_object string to the displayName field if one has not been assigned in the editor, as shown in the following code:
    public void validate()
    {
      if (displayName == "")
        displayName = "unnamed_object";
    }

Congratulations! We now have a container for the CustomGameObject information that our inventory system will use. To continue, let's create the InteractiveObj script.

Implementing the InteractiveObj script

The InteractiveObj script declares a class that enables simple animation and permits player interactions. Perform the following steps to create the script:

  1. To use the new script wizard, right-click inside the Chapter2 folder of the Project tab and add a C# script named InteractiveObj.
  2. To enable our interactive object to rotate about its own axis at a user specified rate, we need to add two parameters: a rotation axis and a rotation speed, as shown in the following code:
    public Vector3 rotAxis;
    public float rotSpeed;
  3. We will add a private reference to the customGameObject component for this GameObject so that we don't have to look it up at runtime. This can be done with the following line of code:
    private customGameObject gameObjectInfo;
  4. We will also add an ObjectInteraction member variable. This will be the code that specifies what will happen to our gameObject when the player interacts with it. There may be many interactions that an interactive object can implement; we will start our example with OnCloseEnough and will complete this in the OnTriggerEnter method, as shown in the following code:
    public objectInteraction OnCloseEnough;
  5. In the Start() method, we will search for the CustomGameObject component attached to gameObject. If it is found, we will store the reference in the gameObjectInfo private variable. Remember to always check that gameObjectInfo is not null so that debugging the code is a straightforward process, as shown in the following code:
    gameObjectInfo = this.gameObject.GetComponent<customGameObject>();
      if (gameObjectInfo) 
        gameObjectInfo.validate();
  6. In the Update() method, we will apply a simple rotation to the object around the specified rotAxis parameter. We will rotate the object with the speed given in rotSpeed multiplied by Time.deltaTime so that the number of rotations is a function of the elapsed time rather than the frame time, as shown in the following code:
    transform.Rotate(rotAxis, rotSpeed * Time.deltaTime);
  7. The OnTriggerEnter() method will be invoked whenever the collider of this object collides with another collider in the world; incidentally, if we set IsTrigger=false on our gameObject, the OnCollisionEnter() method will be dispatched instead of OnTriggerEnter(). Note, for Unity to dispatch either of these callbacks, we must remember to add a Rigidbody component to the GameObject of InteractiveObj at the design time in the editor.
  8. Note, when Unity dispatches this callback, it passes in another parameter of the collider type. This collider is the collider of the object that entered the trigger volume. Convenient! The signature looks as follows:
    OnTriggerEnter(other collider)
    {
    }
  9. In this method, we check that the other object (the gameObject that has just entered this collider) has a tag equal to Player, as shown in the next line of code. This is how we ensure that our trigger only responds to entities that we specify (we must remember to set the tag on the player gameObject to Player):
    if (other.gameObject.tag == "Player")
  10. If the OnCloseEnough object interaction is not null, we dereference it and invoke the handleInteraction() method. In our example, this method does the work of inserting objects into the inventory as shown in the following code:
    if (OnCloseEnough != null)
    {
        OnCloseEnough.handleInteraction();
    }

Congratulations! We now have a class that implements an interactive object. Let's continue further with an ObjectInteraction script that this class can utilize.

Implementing the ObjectInteraction script

The ObjectInteraction class defines how the interactive object will be manipulated when an interaction occurs between the object and the player. Perform the following steps to implement this:

  1. Two enumerations are required to specify the action and the type of action. The action will be what we do with the item (put in inventory and use) initially, as shown in the following code:
    public enum InteractionAction
    {
      Invalid = -1, 
      PutInInventory = 0, 
      Use = 1, 
    }
  2. The corresponding type specializes this behavior by determining if the object is unique or can be accumulated with other interactive objects of the similar type. A unique interaction specifies that ObjectIneraction will insert this interactive object in a unique slot in InventoryMgr, while an accumulate interaction specifies that ObjectInteraction will insert this item (and increase the quantity) in the first available slot that matches the type set in CustomGameObj, as shown in the following code:
    public enum InteractionType
    { 
      Invalid = -1, 
      Unique = 0, 
      Accumulate = 1, 
    }
  3. We keep the following two public variables to store the two enumerations discussed in the previous step:
    public InteractionAction interaction;
    public InteractionType interactionType;
  4. We also keep a Texture variable to store the icon that will be displayed in the inventory for this GameObject as follows:
    public Texture tex; 
  5. The HandleInteraction() method of this class works on the interactive object that this script is attached to. To begin, we get the InventoryMgr component off the player if it can be found. Don't worry that we haven't created the InventoryMgr yet; we will!
    if (player)
      iMgr = player.GetComponent<InventoryMgr>();
  6. As we extend the number of interaction types that our game supports, this method will grow. For now, if PutIninventory is the type, we will delegate i=InventoryMgr to add this InteractiveObj to its collection as follows:
    if (interaction == InteractionAction.PutInInventory)    
    { 
      if (iMgr) 
        iMgr.Add(this.gameObject.GetComponent<interactiveObj ();
    }

Congratulations! You have implemented an ObjectInteraction class that operates on the InteractiveObj class. Let's continue by implementing the InventoryItem class.

Implementing the InventoryItem script

The InventoryItem class is the base item container that the InventoryMgr collection is built from. It contains a reference to GameObject that has been inserted in the inventory (via the ObjectInteraction class). It also has a copy of the texture to display in the inventory as well as the number of objects that a particular InventoryItem represents, as shown in the following code:

public Texture displayTexture;
public GameObject item;
public int quantity;

Note

Scripts that inherit from monobehavior can be fully manipulated by the Unity3D Editor; they can be attached to GameObjects, have the property values saved, among other things. This class does not inherit from monobehavior; as it is an internal helper class for InventoryMgr. It never has to be attached to GameObject (a programmer or designer would not normally want to attach one of these to a 3D object because it doesn't need a Transform component to do its work). This class only acts as the glue between the interactive objects that have been collected and the UI button that InventoryMgr displays for these objects' custom type. Hence, this class does not derive from any base class. This allows us to declare a list of these objects directly inside InventoryMgr.

To make the class show up in the inspector (in InventoryMgr), we need to add back some of the functionality that would have been included, had we inherited from monobehavior; namely, the serialization of its properties. Hence, we add the following code decoration before the class declaration:

[System.Serializable]
Implementing the InventoryItem script

Implementing the InventoryMgr script

The InventoryMgr class contains the system that manages the InteractiveObj classes that the player collects. It displays inventory items in an inventory panel at the bottom of the screen. It has a method for adding inventory items and displaying inventory at the bottom of the screen. Perform the following steps to implement the InventoryMgr script:

  1. To begin, recall that the class declaration for this system follows the same pattern as the others that were created with the new script wizard. Until this point, however, we haven't included any other namespaces than the default two: UnityEngine and System.Collections. For this class, note that we add using System.Collections.Generic in the code. Doing this gives us access to the List<> datatype in our scripts, which we will need to store the collection of inventory objects, as shown in the following code:
    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    
    public class InventoryMgr : MonoBehaviour {
    
    public List<InventoryItem> inventoryObjects = new List<InventoryItem>();
  2. The InventoryMgr class also has parameters that describe the way in which the constraints on the UI will be displayed, along with a reference to the MissionMgr script, as shown in the following code:
    public int numCells;
    public float height;
    public float width;
    public float yPosition;
    private MissionMgr missionMgr;
  3. In the Start() method, when this class is first created, we will find the object in the scene named Game, and store a reference to the MissionMgr script that is attached to it, as shown in the following code:
    void Start () {
      GameObject go = GameObject.Find ("Game");
      if (go)
        missionMgr = go.GetComponent<MissionMgr>();
    }
  4. The Add() method is used by ObjectInteraction.handleInteraction() to insert an InteractiveObj in the inventory (when it is picked up). Recall that the signature looks as follows:
    public void Add(InteractiveObj iObj)
    {
      ObjectInteraction oi = iObj.OnCloseEnough;
  5. Based on the ObjectInteraction type specified in the interaction, the Add() method will behave in specialized ways, and a switch statement is used to select which specific behavior to use. If the ObjectInteraction type is Unique, then InventoryMgr inserts this InteractiveObj in the first available slot, as shown in the following code:
    switch(oi.interactionType)
    {
       case(ObjectInteraction.interactionType.Unique):
       {
          // slot into first available spot
          Insert(iObj);
       }
       break;
  6. If the ObjectInteraction type is Accumulate, then InventoryMgr will insert this in the first slot that matches the CustomGameObject type on the interactive object. To determine this matching, we first store a reference to the CustomGameObject script on the interactive object that is being inserted. If this object does not have a CustomGameObject script, we assume the type is Invalid, as shown in the following code:
    case(ObjectInteraction.InteractionType.Accumulate):
    {
      bool inserted = false;
    
      // find object of same type, and increase
      CustomGameObject cgo = iObj.gameObject.GetComponent<CustomGameObject>();
    
      CustomGameObject.CustomObjectType ot = CustomGameObject.CustomObjectType.Invalid;
    
      if (cgo != null)
        ot = cgo.objectType;
  7. The InventoryMgr class then loops over all inventory objects in the list. If it finds an object that has a matching CustomGameObject type to the interactive object that is being inserted, it increases the quantity property on that InventoryObj. If a match is not found, then InventoryObj is permitted to be inserted in the list as if it were a unique item, as shown in the following code:
    for (int i = 0; i < inventoryObjects.Count; i++)
    {
    CustomGameObject cgoi = inventoryObjects[i].item.GetComponent
    <CustomGameObject>();
    CustomGameObject.CustomObjectType io = CustomGameObject.CustomObjectType.Invalid;
      if (cgoi != null)
        io = cgoi.objectType;
    
      if (ot == io)
      {
        inventoryObjects[i].quantity++;
        // add token from this object to missionMgr
        // to track, if this obj as a token
        MissionToken mt = iObj.gameObject.GetComponent<MissionToken>();
       
        if (mt != null)
           missionMgr.Add(mt);
    
        iObj.gameObject.SetActive(false);
        inserted = true;
        break;
        }
      }
  8. Note, if the types of the object match any existing object on the list, we do some book keeping. We increase its number as well as copy the texture reference that we will display in the inventory. We will also disable the object (to stop it from rendering and interacting with the world) by setting its active flag to false and then we leave the loop, as shown in the following code. We will declare the MissionToken script later in this chapter:
      if (ot == io) 
      { 
        inventoryObjects[i].quantity++; 
    missionTokenmt = iObj.gameObject.GetComponent<MissionToken>(); iObj.gameObject.SetActive (false);
        inserted = true; 
      }
  9. An important aspect to note here is that we need to check if there is a MissionToken script attached to this InteractiveObj. If there is one, then we will add it to MissionMgr. In this way, we will complete the communication between the two management systems in this chapter. Later, we will see how MissionMgr searches for complete missions and rewards the player using mechanics similar to those discussed earlier:
      if (mt != null) 
        missionMgr.Add(mt); 
  10. The Insert() method of InventoryMgr is used to perform the actual insertion work in the list of inventory objects. It is declared with the following signature:
    void Insert(InteractiveObj iObj){
    }
  11. This method first allocates a new InventoryItem with the new operator. We have to use new instead of Object.Instantiate to create a new instance of this class because this class does not inherit from Object. With a new instance of InventoryItem available for use, we will populate its properties with the data from InteractiveObj, as shown in the following code:
    InventoryItem ii = new InventoryItem();
      ii.item = iObj.gameObject;
      ii.quantity = 1;
  12. Then, we will disable GameObject of InteractiveObj (just in case it is still enabled), and finally add the InventoryItem to the list with a direct call to inventoryObjects.add, as shown in the following code:
      ii.item.SetActive (false);
      inventoryObjects.Add (ii);
  13. Lastly, just in case there is MissionToken attached to this GameObject from some other code path, we will extract the token and add it to MissionMgr for tracking, as shown in the following code:
      MissionToken mt = ii.item.GetComponent<MissionToken>();
      if (mt != null)
        missionMgr.Add(mt);

And this completes the work on the Insert() method.

Implementing the DisplayInventory method

Let's continue our work by developing InventoryMgr as we program the method that will display all of the inventory objects on screen by performing the following steps:

  1. The DisplayInventory() method is declared with the following signature:
    void DisplayInventory() {
    }
  2. This method also walks through the collection, but instead of checking the type of object, it will display a series of GUI buttons on the screen. It will also show displayTexture for the item in each inventory. As the position of the inventory cells are relative to the screen, we need to calculate the button positions based on the screen width and height, as shown in the following code:
    float sw = Screen.width; 
    float sh = Screen.height; 
  3. We will also store a reference to the texture we will display in each cell, as shown in the following code:
    Texture buttonTexture = null; 
  4. Then, for clarity, we will store the number of cells in a local integer to display as shown in the following code:
    int totalCellsToDisplay = inventoryObjects.Count;
  5. We will loop over all the cells and extract the texture and quantity in each InventoryItem in the collection, as shown in the following code:
    for (int i = 0; i<totalCellsToDisplay; i++) 
    {
      InventoryItem ii = InventoryObjects[i]; 
      t = ii.displayTexture; 
      int quantity = ii.quantity; 

    The result of this code is shown as follows:

    Implementing the DisplayInventory method
  6. We will compute the total length of all the cells that we want to display. This is used in the code to render the cells centered in the middle of the screen horizontally. Recall that the width and height hold the individual cell width and height:
    float totalCellLength = sw – (numcells * width); 

    As InventoryMgr loops over all InventoryObjects, we draw a new rectangle for each item to be displayed on the screen. To do this, we need to know the x, y coordinates of the upper-left corner of the rectangle, the height and width of an individual rectangle, and the texture. The y height doesn't vary since the inventory is a horizontal row on screen, and the cell height and width don't vary since the cells are uniform by design. The texture will change, but we can use the cached value. So, we need to focus our attention on the x coordinate for a centered array of varying length.

  7. It turns out that we can use this formula. The totalCellLength parameter is the amount of white space horizontally when all the cells are aligned on one side. If we subtract half of this, we get the starting coordinate that will be positioned half on the right and half on the left equally. Considering that width and height are the dimensions of the individual buttons for display and that i is the loop index for the loop we are discussing, then adding (width*i) ensures that the subsequent x coordinates vary horizontally across the array, as shown in the following code:
    float xcoord = totalCellLength – 0.5f*(totalCellLength) +(width*i); 
  8. The rectangle that corresponds to the shape of the button we want to display is then calculated with the following formula. Note that its position on the screen is a function of i, the loop index, as well as y, the screen width and height, and the button width and height:
    Rect r = new Rect(totalCellLength - 0.5f*(totalCellLength) + (width*i), yPosition*sh, width, height);

    With all of these quantities now calculated, we will display the button with the GUI.button(r, buttonTexture) method, as shown in the following code. We will check for a true return value from this function because this is how we track when the user clicks on a button:

    if (GUI.Button(r, buttonTexture)) 
    { 
      // to do – handle clicks there
    } 
  9. Recall that we need to display the number of items with each button. We do this with the GUI.Label method in a way analogous to the previous code. We will compute a second rectangle for the quantity that mirrors the cell for the button, but we will use half the cell width and height to position the rectangle in the upper-left corner for a nice effect!
  10. We will convert the quantity field of the current InventoryItem class to a string with the built-in function called ToString() that the integer implements, and we will pass this to the GUI.Label method, as shown in the following code:
    Istring s = quantity.ToString()
    GUI.Label(r2, s); 
  11. To display UI textures and elements on the screen, Unity provides a special callback method to place our UI code whenever the UI is refreshed. This method is called OnGui() and has the following signature:
    void OnGUI(){
    }
  12. We invoke our DisplayInventory() method inside the void OnGUI() method that Unity provides because this method draws the InventoryItems list of InventoryMgr to the screen in the form of UI buttons. This callback is where all drawing and GUI-related processing occurs, as shown in the following code:
    void OnGUI() 
    { 
      DisplayInventory();
    }

    We could modify this code slightly to draw the maximum number of cells in the inventory rather than the total number of filled InventoryMgr cells. We must be careful to not dereference past the end of the collection if we have been doing so!

Congratulations! We now have a working InventoryMgr system that can interface with interactive objects and collect them based on their custom type! While we touched briefly on the MissionToken class in this explanation, we need to develop a system for tracking the abstract mission objectives and rewarding the player on achieving success. This requires multiple classes. The result of performing these steps is shown in the following screenshot:

Implementing the DisplayInventory method

Implementing the MissionMgr script

The MissionMgr class tracks the user's progress through abstract objectives. These objectives will be learning objectives that satisfy the game design. The pattern we use to manage missions will be similar to InventoryMgr; however, this system will be in charge of comparing the player's objectives with a master list of missions and with what is required to complete each one. To develop it, let's perform the following steps:

  1. To accomplish this work, MissionMgr will need two lists. One list to hold all of the missions that the player could try and solve and another for the current tokens that the player has acquired (through interacting with interactive objects, for instance). The MissionTokens collection is allocated at runtime and set to empty so that the player always starts having accomplished nothing; we could develop a way later to load and save the mission progress via this system. The missions' list will be populated at runtime in the editor and saved, so we don't want to initialize this at runtime:
    Public List<mission> missions; 
    Public List<missionToken> missionTokens = new List<missionTokens>(); 
  2. The MissionMgr implements three methods that allow it to perform its role and interface with other game systems:
    • Add(missionToken): This method adds a newly acquired MissionToken to the collection. This collection will be queried while trying to determine if a mission has been completed. In Add(), we use a similar methodology as the Add() method for InventoryMgr. In this case, assume that the token is unique and search for a duplicate by iterating over all of the tokens for the current mission m, as shown in the following code:
      bool uniqueToken = true; 
      for (int i = 0; i<missionTokens.Count; i++) 
      { 
        //… 
      }

      If a duplicate is found, namely, a token is found in the collection with the same id field as the add candidate, we abort the operation as shown in the following code:

      if (missionTokens[i].id == mt.id) 
      { 
        // duplicate token found, so abort the insert 
        uniqueToken = false; 
        break; 
      } 

      If a duplicate is not found, we add this token to the list as shown in the following code:

      if (uniqueToken) 
      {  
        missionTokens.add(mt); 
      } 
    • Validate(mission): This method will compare currentToken set to a specific mission. If it has been found to have been satisfied, the system is notified. To do this, we will use a search pattern similar to the one used in the Add() method and start by assuming the mission is complete; only this time we will use a double-nested loop! This is because to validate a mission means to search, individually, for each token in the current mission against all of the tokens the user has collected so far. This is done as shown in the following code:
      bool missionComplete = true; 
      for (intinti = 0; I < m.tokens.Count; i++)
      { 
        bool tokenFound = false; 
        for (int j = 0; j < missionTokens.count ; j++) 
        {
          // if tokens match, tokenFound = true
        }
      }

      By assuming the token will not be found initially, we are required to search for a matching token ID. If we don't find it, it automatically means that the mission cannot be complete, and we don't have to process any more comparisons, as shown in the following code:

      if (tokenFound == true))
      { 
        missionComplete = false;
        break;
      }
    • ValidateAll(): This methods invokes Validate() on all user-defined missions if they are not already complete. If any mission is found to be completed, a reward is instantiated for the player through the InvokeReward() method, as shown in the following code:
      void ValidateAll() {
      
        for (int i = 0; i < missions.Count; i++)
        {
          Mission m = missions[i];
      
          // validate missions…
        }
      }

      We will sequentially search through all user-defined missions that have not already been completed (no need to do this once a mission is done). This enumeration will be defined in the Mission script, as shown in the following code:

      if (m.status != mission.missionStatus.MS_Completed) 
      { 
        bool missionSuccess = Validate(m);

      If the mission has been validated as being complete, the mission implements an InvokeReward() method to reward the user, as shown in the following code:

      if (missionSuccess == true) 
      { 
        m.InvokeReward(); 
      }

Implementing the Mission script

The Mission class is the container for MissionTokens. It implements a state that helps us specialize how a mission should be treated by the game (for instance, we may want to have the player acquire a mission but not start it). This class has a number of state variables for future extension such as activated, visible, and points.

  1. As with the InventoryItem class, the Mission class is a helper class that only the MissionMgr class uses. Hence, it does not inherit from monobehavior. Therefore, the class signature must include the [System.Serializable] tag as before:
    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    
    [System.Serializable]
    public class Mission {
  2. This class also implements an enumeration to describe the state of a particular mission. This is used to encode whether a state is invalid, acquired, in progress, or solved so that MissionMgr can handle the mission appropriately, as shown in the following code:
    public enum missionStatus
    {
      MS_Invalid = -1,
      MS_Acquired = 0,
      MS_InProgress = 1,
      MS_Completed = 2
    };
  3. The public variable status is an instance variable of the status enumerated type in this class. We will use this initially to make sure that once a mission is complete, MissionMgr no longer tries to validate it. This can be done with the following code:
    Public missionStatus status; 
  4. The specific elements that comprise a mission are the mission tokens that the user puts in the tokens' collection. This is a collection of logical pieces that comprises a complete objective in the game. This list will be compared against the players' acquired tokens in MissionMgr, as shown in the following code:
    Public List<missionTokens> tokens; 
  5. The points and reward public variables are used to store the numerical score and the in-game rewards that are given to the user when a mission is completed. Note, GameObject reward could be used as a completion callback, in addition to a reference to a Prefab item for the user to pick up, as shown in the following code:
    public int points;
    public GameObject reward;
  6. The displayName public variable is used by the user in the Unity3D Editor as a place for a helpful string to describe the mission's nature, as shown in the following code:
    public string displayName;

    This class implements one method: the InvokeReward() method. This function will spawn a new gameObject into the world that has been set in the editor. Through this mechanism, the player can be rewarded with points, a new object or objective can appear in the world, or any other outcome can be encapsulated in a Unity Prefab object.

  7. Once a mission has been validated and InvokeReward has been called, the mission itself is disabled and its status is set to Completed, as shown in the following code:
    this.status = missionStatus.MS_Completed; 

Implementing the MissionToken script

The MissionToken class stores the information for an individual mission component. This class acts as a container for this abstract data. We give it an ID, a title that is human readable, and a description. By giving each MissionToken a unique ID, we give the Mission class a powerful way of tracking the mission progress. This class is used in a way by which the user adds instances of this component to various interactive objects that can be manipulated by the player, as shown in the following code:

Public int id; 
Public string title; 
Public string description; 

Implementing the SimpleLifespanScript

The SimpleLifespanScript class is a simple helper class that can be used in conjunction with the Instantiate() method to instantiate a GameObject in the world that will have a specified but finite lifespan. We use it to enable an instance of a Prefab that is live for a set period of time and then destroys itself as a reward for completing a mission. By attaching this to the reward that is displayed when a mission is completed, the prompt is given a set duration on the screen after which it disappears.

Specifically, the seconds parameter is the time for which the object will survive before self destruction, as shown in the following code:

Public float seconds 

In the update method, we count this value by the actual time elapsed in each frame (from Timer.deltaTime). Once this reaches zero, we destroy the object and all the scripts attached to it (including the simpleLifespanScript), as shown in the following code:

seconds -= Time.deltaTime; 
if (seconds <= 0.0f) 
  GameObject.Destroy(this.gameObject); 

Congratulations! We now have a robust set of scripts for MissionMgr, missions, tokens, and rewards. Let's apply what we have built in an example that exercises the mission system, the inventory system, and interactive objects.

主站蜘蛛池模板: 固安县| 莫力| 科技| 武威市| 深圳市| 泾源县| 高邮市| 平原县| 汕尾市| 鄂伦春自治旗| 海兴县| 新巴尔虎右旗| 民权县| 金山区| 乐亭县| 阳高县| 松桃| 泾阳县| 堆龙德庆县| 新源县| 石阡县| 嘉兴市| 咸阳市| 永兴县| 湖州市| 阿勒泰市| 临海市| 安泽县| 临夏市| 宜兴市| 象州县| 北京市| 资兴市| 昂仁县| 响水县| 昌都县| 天祝| 天台县| 安化县| 乃东县| 泽普县|