1
\$\begingroup\$

In this script when there is a detection I'm setting the flag detect to true and using in with other scripts. And using the clickForDescription flag and OnGui to display text description for each interactable item if there is a text for it.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class DetectInteractable : MonoBehaviour
{
    public Camera cam;
    public float distanceToSee;
    public string objectHit;
    public bool interactableObject = false;
    public Transform parentToSearch;
    public static bool detected = false;

    private RaycastHit whatObjectHit;
    private bool clickForDescription = false;
    private bool clickForAction = false;
    private int layerMask = 1 << 8;

    private void Start()
    {

    }

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            clickForDescription = true;
            clickForAction = true;
        }

        Debug.DrawRay(cam.transform.position, cam.transform.forward * distanceToSee, Color.magenta);
        if (Physics.Raycast(cam.transform.position, cam.transform.forward, out whatObjectHit, distanceToSee, layerMask))
        {
            detected = true;
            objectHit = whatObjectHit.collider.gameObject.name;
            interactableObject = true;
            print("Hit ! " + whatObjectHit.collider.gameObject.name);
        }
        else
        {
            detected = false;
            clickForDescription = false;
            print("Not Hit !");
        }
    }

    private void OnGUI()
    {
        if (clickForDescription == true)
        {
            ProcessOnGUI(parentToSearch);
        }
    }

    void ProcessOnGUI(Transform parent, int level = 0)
    {
        foreach (Transform child in parent)
        {
            if (child.GetComponent<ItemInformation>() != null)
            {
                ItemInformation iteminformation = child.GetComponent<ItemInformation>();
                if (child.name == objectHit)
                {
                    var centeredStyle = GUI.skin.GetStyle("Label");
                    centeredStyle.alignment = TextAnchor.UpperCenter;
                    GUI.Box(new Rect(
                          Screen.width / 2 - 50 + 20 * level, // <== INDENTATION
                          Screen.height / 2 - 25, 100, 50),
                        iteminformation.description, centeredStyle);
                }
            }

            // Process next deeper level
            ProcessOnGUI(child, level + 1);
        }
    }

    public class ViewableObject : MonoBehaviour
    {
        public string displayText;
        public bool isInteractable;
    }
}

This is the ItemInformation:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ItemInformation : MonoBehaviour
{
    [TextArea]
    public string description;
}

Once object is tagged for Interactable and the ItemInformation is attached to it it will display some description for the item.

Now I want to do something for action. For example if the item is Interactable and a script name ItemAction is attached to it do some action, For example move the object from side to side or rotate the object. The logic should be like the Information. It's just with the Action there might be many options then just displaying text.

I'm not sure how to make some logic and how to work with the ItemAction.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ItemAction : MonoBehaviour
{

}
\$\endgroup\$
6
  • \$\begingroup\$ You want to be able to define different type of actions within the script, in a way you can just choose the action through the editor in the script? \$\endgroup\$
    – Leo
    Commented Mar 8, 2018 at 8:42
  • \$\begingroup\$ @Leo Yes something like that so I created a new script just for the actions. I will update my question with what I just did. \$\endgroup\$
    – Daniel Lip
    Commented Mar 8, 2018 at 8:58
  • \$\begingroup\$ @Leo Updated my question with what I did. But I'm not sure if making the new ItemAction script like a module logic is a good idea and the right way to handle all items that will have action when clicking on them. \$\endgroup\$
    – Daniel Lip
    Commented Mar 8, 2018 at 9:04
  • \$\begingroup\$ It really depends on what you are trying to achieve. Will there be a lot of different actions? Do you plan actions to be performed against items by different agents besides the player? You want to make actions as modular as possible so they don't have to take in consideration anything about the current scene? You want the user t be able to customize the actions that can be done on an item? Details about your game and an idea of how you want the game to feel would help us giving you a better answer :) \$\endgroup\$
    – Leo
    Commented Mar 8, 2018 at 9:08
  • \$\begingroup\$ @Leo There will be interaction with other agents/npcs like conversations. But with items not a lot of actions. Some items will only give text info description on the item some will be for action. I'm not planning to make a lot of items to be interactable. There will be few items in each room/place/area. \$\endgroup\$
    – Daniel Lip
    Commented Mar 8, 2018 at 9:30

1 Answer 1

1
\$\begingroup\$

I'd create an component with a single empty virtual function which should be called when the item is interacted with. Something like this:

public class ItemAction : MonoBehaviour {
     virtual void ItemUsed() {

     }
}   

Then you can create a new behavior that inherits from this class in order to implement the specific action. For example, if you want an item to make a knock sound when used displaying a bubble with the text "Knock", you could implement:

public class KnockAction : ItemAction {
     public string text;
     override void ItemUsed() {
          DisplayBubbleModule.Show(text);
     }
}

Just set text to "Knock", and you are ready to go.


Both heritage and composition

This might seem like an overkill, creating two classes to just specify such a simple behavior. But it has its advantages:

  • You can create new actions without the need to touch old actions' code.
  • It's easy to mix different actions

Imagine you create another action that changes the sprite of the item. You could use this to make, for example, a drawer being opened. You could create this class:

    public class KnockAction : ItemAction {
        public Sprite NewSprite;
        override void ItemUsed() {
            GetComponent<SpriteRenderer>().sprite = NewSprite; 
        }
    }

You just add the component to the item, and put the desired sprite on the NewSprite field.

Now you want to make a shelf with another behavour, that when used, the sprite changes and shows the shelf knocked down, and a text bubble displaying the text "CRASH". You don't need to create a third action, you can just add both actions we previously created, and setting their fields accordingly.

Note that for this to work, you should get ALL the ItemAction components in an item, not just one.


Extending interactivity

You could also extend you base class, and make the item interactable in more than one way:

public class ItemAction : MonoBehaviour {
     virtual void ItemUsed() {

     }

     virtual void ItemHit(){

     }

     virtual void ItemBurned(){

     }
}   

You could use these new functions, to be called when the item is set on fire, or when it's hit by an attack. Or you could even create a specific class for each of the type of actions to be done on a item, that really depends on how versatile you want your behaviors, and how to keep them all tidy.


Triggering actions

How to trigger these actions really depends on how your system works, but the idea is basically the same in almost all cases:

  • Detect the item you want to be activated
  • Retrieve the ItemAction Components of the item and call the desired

Once know what item to activate, it's as simple as:

foreach( ItemAction ia in gameObjectToActivate.GetComponentsInChildren<ItemAction>() ){ 
    ia.ItemUsed();
}

If you plan to call these actions very quickly and expect to have a lot of actions triggering at the same time, you should probable cache all the item's actions and stored them, in order to call them without the need of relying on the GetComponentsInChildren<> which has some overhead.

Detecting which item to trigger, and how is a whole new issue. You could decide which item to activate and when by:

  • Clicking on them
  • Making you character stand in front of it and pressing a key
  • Adding a new component to you character so specific actions are activated when walking near them
  • Create a new class that creates messages in order to trigger ItemActions in a generic and extensible way, the same way we just did for ItemActions
  • Create listeners that will trigger actions when certain event happen

But pretty much what you are doing in your DetectInteractable class is enough: Get the item and look for the components to call the required function.

\$\endgroup\$
8
  • \$\begingroup\$ Great idea. Could you show me please example of how to use it with my code the DetectInteractable script ? Should I keep using the ItemInformation and the way I'm searching for ItemInformation or should I use for action and text better with your solution ? \$\endgroup\$
    – Daniel Lip
    Commented Mar 8, 2018 at 14:43
  • \$\begingroup\$ You can keep using the current ItemInformation as it is. In the future you might find that the current implementation is not as comfortable to work with any more. Then you should change it to something more versatile. About using the new ItemActions I'll update the answer. \$\endgroup\$
    – Leo
    Commented Mar 8, 2018 at 15:07
  • \$\begingroup\$ I updated the question, I don't know if the example is specific enough. \$\endgroup\$
    – Leo
    Commented Mar 8, 2018 at 15:58
  • \$\begingroup\$ I have a question just to make something clear. I will update my question with what I did. \$\endgroup\$
    – Daniel Lip
    Commented Mar 8, 2018 at 16:38
  • \$\begingroup\$ I have updated my question now with how I did it. Not sure where and how the ItemActions class should be used if at all. \$\endgroup\$
    – Daniel Lip
    Commented Mar 8, 2018 at 16:44

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .