Search Unity

Reading Variables from Items with Different Scripts

Discussion in 'Scripting' started by wilhelmscream, May 26, 2015.

  1. wilhelmscream

    wilhelmscream

    Joined:
    Jun 5, 2013
    Posts:
    223
    Think of a shopping cart with several different items in it. Meat, bread, milk, etc.....

    What I'm looking for and *mostly* able to accomplish is reading and adding the price of each item, BUT I'm having difficulty doing so if I don't know, in advance, the number of and name of each item. For example......

    Here's the code for the cart itself. The idea is for the cost variable to be the total cost of each item in the cart.

    Code (JavaScript):
    1. var items : GameObject [];
    2.  
    3. function TotalCost () {    //called from another script - works fine
    4.     for (var oneItem: GameObject in items) {
    5.         cost+= oneItem.GetComponent(Item).itemCost;
    6.     }
    7. }
    8.  
    9. function Update () {
    10.     items = GameObject.FindGameObjectsWithTag("Food");
    11. }
    This works fine as long as all the items have the same script attached. I have two questions here......

    - Is there a simple way to accomplish the same thing (maintaining a running cost total) with each of the items having a different script name (Milk, Bread, Meat, etc).....?

    - How would I go about maintaining a list of items (preferably on a GUI display), wherein each item's name is contained in a string variable?
     
  2. hamsterbytedev

    hamsterbytedev

    Joined:
    Dec 9, 2014
    Posts:
    353
    Try some polymorphism. You can make a class called Item and make the others inherit from it. Then you can make a list of the item class which will accept any of the other scripts that inherit from it. Here's the example from the Unity tutorials:

    https://unity3d.com/learn/tutorials/modules/intermediate/scripting/polymorphism

    Hope this helps you out! Let me know if you need further assistance and best of luck to you in your future endeavours!
     
  3. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Interfaces are also another good solution.
     
    hamsterbytedev likes this.
  4. L-Tyrosine

    L-Tyrosine

    Joined:
    Apr 27, 2011
    Posts:
    305
    You can have multiple scripts in one game object. This seems obvious but too few Unity coders really use the power of component system. For example, you can have your Item class with Price and ItemName. Then, to make any GameObject buyable just attach this script to it, alongside with other specific scripts.

    At shopping cart class, work with a Item[] instead a GameObject[]. In C# (with Linq):

    Code (csharp):
    1.  
    2.  
    3. public class Item: Monobehaviour
    4. {
    5.     public float Price;
    6.     public string ItemName;
    7. }
    8.  
    9. public class ShoppingCart: Monobehaviour
    10. {
    11.     List<Item> Items = new List<Item>();
    12.  
    13.     public void AddItem(GameObject obj)
    14.     {
    15.         // Check if object is "buyable"
    16.         Item item = obj.GetComponent<Item>();      
    17.      
    18.         if (item != null)
    19.             Items.Add(item);
    20.     }
    21.  
    22.     public float TotalCost()
    23.     {
    24.         return Items.Sum(i => i.Price);
    25.     }
    26. }
    27.  
    28.  
     
    Kiwasi and wilhelmscream like this.
  5. wilhelmscream

    wilhelmscream

    Joined:
    Jun 5, 2013
    Posts:
    223
    Multiple scripts hmmmm ..... Ive done that on other things, not sure why the thought of doing so here didn't occur to me. Thanks. :)

    Any thoughts on the second question? Getting each item to register in a GUI list (probably the wrong term)?

    Seems like it should be able to read the item name (a string variable), and populate a visible list, right? Without "slotting" the items (due to not knowing in advance how many items or how much of each).
     
  6. hamsterbytedev

    hamsterbytedev

    Joined:
    Dec 9, 2014
    Posts:
    353
    All good solutions; however there is a caveat. If you intend to release this title on iOS do not use Linq; Linq does not play well with iOS 8 and ARM64. Moreover, component based design is a fine solution, but I tend not to use Monobehaviour unless I need that functionality; in this case you do not. In my opinion this situation is more aptly handled by the use of basic polymorphism or interfaces.

    That being said, I don't think that a component based model of the shopping cart is going to affect performance to a noticeable degree. In the end the choice is yours, any one of the solutions mentioned would suit your needs, and it really comes down to the personal preference of the individual writing the script. I would use basic polymorphism, @BoredMormon would likely favour an interface, and @L-Tyrosine would lean on the component based model. Just use whichever method you are most comfortable with.

    Be aware that your item classes are in essence just a way to store data, i.e. a name and a cost, and you don't need the extra functionality that you would derive from Monobehaviour; however if you do need GameObject instances for each of these items, and you are going to have a Monobehaviour attached to them anyway, I would just add the functionality to that script instead of making new classes to store the data; you are still using polymorphism either way. When you inherit from Monobehaviour you are using polymorphism. If you make a script called Apple that inherits from Item which inherits from Monobehaviour....you get the idea.

    As far as your GUI goes, there are countless solutions to the problem. It really depends how much functionality you need in your GUI. If you just need to display a list you could make a List<string> and populate it with names. Iterate through that list and concatenate the values together with some \n escape characters for new lines. Then, write that new string to a text box. It should be fairly straight forward.

    Cheers!
     
    wilhelmscream and L-Tyrosine like this.
  7. L-Tyrosine

    L-Tyrosine

    Joined:
    Apr 27, 2011
    Posts:
    305
    AFAIK this is not related to IOS but any environment where scripts are compiled AOT (like WebGL), and there are only problems for Linq instructions that use dynamic code generation, like the .ThenBy().

    Also, the component pattern does not exclude the use of class hierarchy, nor interfaces. It is just a matter of answering a question at modeling time (is a or has a). In my code there always a strong mix of both.

    Finally, concerning Monobehaviour usage, I find useful to have serialization and inspector ready for game classes, these things speed up the code development (with no performance penalty).

    But like you said, many things are a matter of taste after all.
     
    hamsterbytedev likes this.
  8. wilhelmscream

    wilhelmscream

    Joined:
    Jun 5, 2013
    Posts:
    223
    It's Windows-only (mainly because I'm using it as a hands-on toll to teach myself - I'm more or less a noob at all of this), so no worries there.

    I don't need a ton of functionality..... at most, I would say each item has a button which can then send a message to the item (i.e., "Remove from cart") when pressed. I've tried arrays and GetComponent in a few different ways without success....
     
  9. hamsterbytedev

    hamsterbytedev

    Joined:
    Dec 9, 2014
    Posts:
    353
    My approach to this would involve making a GUI prefab that had a text component and a button to remove the item from the cart. Then instantiate a prefab and define the properties of it for each of the items in the list. When an item is added you would instantiate a new prefab when it is removed you remove the item from the list and the prefab from the scene.

    I could code it for you, but that's not going to help you learn :)

    If you know how to work with the new GUI and how to make and instantiate prefabs this should be pretty easy. You just have to write some code that integrates the whole lot together. There are other ways to do this, of course, this is just where my mind immediately went. Doing it in this way also gives you a whole mess of other things that you may not need yet, but they would be possible if you did; i.e. mouseover color changes for your list items, etc.

    Good luck! Keep at it!
     
  10. wilhelmscream

    wilhelmscream

    Joined:
    Jun 5, 2013
    Posts:
    223
    Here are the codes I'm currently using:

    Basket -
    Code (JavaScript):
    1. #pragma strict
    2.  
    3. var meatItems : GameObject [];
    4. var itemName : String;
    5. var boxHeight : float = 20;
    6. var meatY : float;
    7.  
    8. function Start () {
    9. }
    10.  
    11. function OnGUI () {
    12.     GUI.BeginGroup (Rect (20, 10, 300, boxHeight));
    13.         GUI.Box (Rect (0, 0, 300, boxHeight), "");
    14.         for (var item : GameObject in meatItems) {
    15.             itemName = GetComponentInChildren(ItemGUITest).itemName;
    16.             GUI.Box (Rect (0, meatY, 300, 20), "");
    17.             GUI.Label (Rect (0, meatY, 300, 20), itemName.ToString ());
    18.         }
    19.     GUI.EndGroup ();
    20. }
    21.  
    22. function NewMeat () {
    23.     meatY +=20;
    24. }
    25.  
    26. function Update () {
    27.     meatItems = GameObject.FindGameObjectsWithTag("Meat");
    28.     boxHeight = 20 + (meatItems.Length*20);
    29. }
    Items -
    Code (JavaScript):
    1. #pragma strict
    2.  
    3. var itemName : String;
    4. var basket : GameObject;
    5.  
    6. function Start () {
    7.     basket = GameObject.FindGameObjectWithTag("Basket");
    8.     SendMessageUpwards ("NewMeat", basket, SendMessageOptions.RequireReceiver);
    9. }
    10.  
    11. function Update () {
    12. }
    So far it's maintaining the size of the display properly. It's giving me all the boxes, labels, etc that I want. However, it's layering all the smaller boxes on top of each other in the position of the last one (instead of each in a unique position), and completely failing (except for the first item in the heirarchy) to even attempt to write the itemName strings in any box anywhere.