Search Unity

Flexible system of interaction of the player's items

Discussion in 'Scripting' started by Gorki12, Aug 7, 2022.

  1. Gorki12

    Gorki12

    Joined:
    Jul 12, 2015
    Posts:
    73
    I am creating a simple puzzle game in which the player will have various objects and, using them, must solve various logical puzzles. However, I am having trouble designing a reasonably flexible system for the interaction of an item the player chooses with another object, for example: the player chooses a torch and can: set the other torch on fire brightening up a section of the map, or he can use it on the player's character leading to a loss.

    I create abstract class Item inheritance from ScriptableObject:

    Code (CSharp):
    1. public abstract class Item : ScriptableObject
    2. {
    3.     public GameObject ItemPrefab;
    4.     public Sprite ItemIcon;
    5.     public string ItemName;
    6.  
    7.     public abstract void UseItem();
    8. }
    9.  
    and I created second class UsableItem inheritance from Item class:
    Code (CSharp):
    1. [CreateAssetMenu(fileName = "ScriptableObjects", menuName = "ScriptableObjects/UsableItem", order = 1)]
    2. public class UsableItem : Item
    3. {
    4.     public ItemInteractionAction ItemInteractionAction;
    5.    
    6.     public override void UseItem()
    7.     {
    8.         // Switch for all interaction action?
    9.     }
    10. }
    11.  
    and enum ItemInteractionAction
    Code (CSharp):
    1. public enum ItemInteractionAction
    2. {
    3.     Fire,
    4.     Water
    5. }
    6.  
    and my initial idea was to create a switch in the UsableItem class in the UseItem() method for each enum element but I decided that this was not a very good idea. Additionally, I would have to create a lot of if for different objects that would behave differently after interacting with the selected item.
     
  2. R1PFake

    R1PFake

    Joined:
    Aug 7, 2015
    Posts:
    542
    There are many different ways to solve this, but I will start with one solution.

    You could change your ItemInteractionAction to a abstract ScriptableObject (like your Item) and add a abstract method like "InvokeAction" or something like that. Then instead of enum values you could implement different classes per ItemInteractionAction (FireInteractionAction, WaterInteractionAction, etc.)

    Then you create a asset of the specific ItemInteractionAction implementation and you can drag & drop the action into the UseableItem field.

    Then your UseItem method can call a abstract method of the given ItemInteractionAction and doesn't have to care what the actual implementation does.

    From there you can expand the system depending on your needs, for example the Item could have a List of ItemInteractionAction instead of just one or you could make a fancy editor script which allows you to add the actions directly in the item inspector etc, but that's all optional bonus.
     
    mopthrow likes this.
  3. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,925
    There's a lot of questions about defining item behaviour lately... Someone should make a tutorial for this.

    What you're wanting is potentially some way to change behaviour, or to potentially socket behaviour.

    Like above you could have an abstract base class that defines an abstract "UseItem" method, and make derived types from that. If your games doesn't have very many items this would be the most straight forward approach.

    So basically:
    Code (CSharp):
    1. public abstract class Item : ScriptableObject
    2. {
    3.     public abstract void UseItem();
    4. }
    5.  
    6. public class ItemTorch : Item
    7. {
    8.     public override void UseItem()
    9.     {
    10.         //Torch things
    11.     }
    12. }
    You could also change the
    UseItem()
    method to take in a parameter, say, the game object using it, so you can gleam whatever components off it you want.

    If you have a good number of items with various effects, you'll probably want to socket behaviour. This is where you want to make the item effects themselves scriptable objects as well, with an abstract base type, and your items have a list of these effects. Like so:
    Code (CSharp):
    1. public class Item : ScriptableObject
    2. {
    3.     [SerializeField]
    4.     private List<ItemEffectBase> itemEffects = new List<ItemEffectBase>();
    5.  
    6.     //We can tell if the item is usable with this property
    7.     public bool Usable => itemEffects.Count > 0;
    8.  
    9.     public void UseItem()
    10.     {
    11.         foreach (ItemEffectBase effect in itemEffects)
    12.         {
    13.             effect.UseEffect();
    14.         }
    15.     }
    16. }
    17.  
    18. public abstract class ItemEffectBase : ScriptableObject
    19. {
    20.     public abstract void UseEffect();
    21. }
    This way you only need the one item class, and you can easily mix and match item effects with a list of objects. Of course, if you have lots of effects, then this becomes cumbersome.

    The most flexible way IMO is to use the SerializeReference attribute, which lets you serialise a polymorphic data even in collections. With this you could make your effects just plain classes and have them serialised directly into the item. However this needs custom inspector work to be usable, of which there are both paid and free options
     
    eses, Gorki12 and mopthrow like this.
  4. Gorki12

    Gorki12

    Joined:
    Jul 12, 2015
    Posts:
    73
    Thank you spiney199 for explain :)