Forum rules - please read before posting.

Best way to update Inventory Item Main Graphic?

I want to update an Inventory Item's main graphic at runtime. The item is a Flask, and it may be empty or full of water. I want to show a different icon when it's empty and when it's full. I've been able to change the texture variable by calling:
KickStarter.inventoryManager.GetItem(1).tex = textureFull;

However, I'm using Unity UI and the UI does not update the image. I've tried lots of things, even tried calling UISlot.SetImage and that did not even work.

How can I update an Inventory Item's Main Graphic variable at runtime and also update the Unity UI to reflect the change?
«1

Comments

  • At runtime, the inventory items are copied from the Inventory Manager into a local script - so modifying the manager won't affect the game until you restart.

    Replace "inventoryManager" with "runtimeInventory" and it should work.

    However, it may be easier to split it into two distinct "full" and "empty" items.  You can use the runtimeInventory script's Replace method (or Inventory: Add or remove Action) to swap them around as needed.
  • edited December 2017
    I split it into 2 items because it ended up being much better that way anyways. It's easier to treat the EmptyFlask/FullFlask as completely different interactions with different actionlists.

    However, FYI no matter what I do I cannot change the runtime icon. Even setting GetComponent<Image>.sprite doesn't work, which means AC is overriding this value somewhere and I can't find where.

    Here are some of the code I have tried to change the image, as you can see almost every method possible does not work:

            InvItem item = KickStarter.runtimeInventory.GetItem(1);
            Debug.Log(item.label);  // This is the correct item
            int slot = inventoryBox.GetItemSlot(1);
            UISlot selectedSlot = inventoryBox.uiSlots[slot];
            selectedSlot.GetRectTransform().GetComponent<Image>().sprite = spriteFull;  // Does not work
            selectedSlot.GetRectTransform().GetComponent<UnityEngine.UI.Button>().image.sprite = spriteFull;
            //Does not work
            Debug.Log(selectedSlot.GetRectTransform().gameObject.name);  // Correct Gameobject
            selectedSlot.SetImage(textureFull);   // Does not work
            selectedSlot.sprite = spriteFull;  // Does not work
  • You would need to change the item's tex variable, not manipulate the Unity UI directly.

    However, with UI there is another step involved - apologies for missing it before.  Since inventory textures are converted to sprites in UI mode, they are cached in memory and only reset when necessary.  This can be forced manually, however, with:

    AC.PlayerMenus.ResetInventoryBoxes ();
  • edited January 2018
    Hey Chris/Selzier/anyone else able to help,

    I'm looking to do something similar however I can't use two different Inventory items as an alternative so some further info on this would be helpful. To quickly explain my issue I have a system whereby the player can take a picture at certain moments, when done a screenshot is taken at runtime and used as the photo - this works great however I need to set the Inventory Icon texture as this newly generated picture so that the player can open up their in-game tablet and see all the thumbnails of the photos they took and select one to have a closer look at it.

    I've managed to get the screenshot stuff working and when the player selects the photo and looks at it in greater detail this works great too with the newly generated screenshot showing correctly. My issue however is assigning the Inventory item with this new screenshot for it's main texture so that the player can see a thumbnail preview of the picture when they click on it (should be self explanatory however to clarify each photo taken is an inventory item).

    I've tried using the above KickStarter.runtimeInventory.GetItem(1).tex to assign this new screenshot as the Inventory icon however no luck, I even tried the resetInventoryBoxes tip above too (still class my programming skills as beginner however I do realise that the (1) in the above code is reference to Inventory slot 1, in my case I'm trying to change slot no3 and so I use 3 - just wanted to make sure that this potential error can be ruled out). I keep getting the error regarding object reference is not set to an instance of an object at the above KickStarter.runtimeInventory.GetItem(1).tex line so it seems I'm not actually accessing the Inventory item correctly?

    Link below is to the snippet of code I'm using to take the screenshot - as you can see I take a screenshot which I then convert to a sprite and assign to the necessary place so that the player can see the photo when clicked on. I then afterwards try to assign the screenshot to the Inventory icon but no luck and that's where I get the above issues. I tried commenting out the sprite stuff and just assigning the Inventory icon to ensure that the issue wasn't because of me trying to set the new screenshot as both the full photo sprite and then the Inventory icon as well however still the same issue and I don't believe this is the problem (in my limited experience I could obviously be wrong though). I'd love to know if anyone has achieved this before however I would imagine that 99% of games developed with AC don't require Inventory Icons to be generated at runtime and simply assign the default Icon in the editor.


    Thanks again,
    Mike.
  • Are you using Monodevelop?  AC's public methods a commented, so you should be getting method info when calling AC functions.  The "GetItem" function parameter is the item's ID - not the order as it appears in the current inventory.  Change "3" to whatever you item's ID number is, which is the number to the left of it's name in the Inventory Manager.

    Alternatively, you may wish to look into using Render Textures.  An item's texture can be set to be a render texture, so you could also concievably have a separate camera view a plane with the new texture on it.
  • edited January 2018
    @ChrisIceBox

    Thanks for the quick reply...sorry yes when I said 'Slot 3' I was referring to the ID number of the Inventory item so this was already setup correctly - I should have said ID 3 for clarity.

    I've used RenderTexture elsewhere so I'm aware of this but never thought to use it in this way, I'll definitely consider this for an alternative if required however would this not be quite taxing? I'd need to setup a few different cameras all rendering the different available photo thumbnails. It's great to know that I have that as plan B though as I know for sure that I can implement this.

    Yes I'm using Monodevelop, the code looks fine in Monodevelop and comments are shown advising that the above .tex line is accessing the Inventory items main graphic texture so seems to be accessing the AC method correctly.

    I'm hopefully overestimating the impact that the render texture option will have - the fact you have suggested it seems to indicate that it shouldn't pose a noticeable issue performance wise. I will definitely pursue this option if I get nowhere with the Inventory texture change.

    Thanks,
    Mike.
  • My suggestion was more from the technical perspective - I'm not actually sure of the performance impact of it.  However, RenderTextures shouldn't be necessary here, the texture assigning portion of your code should work.

    Two things:
    1) You don't need to deal with sprites - lines 14 and 15 can be removed
    2) The photo only runs if snapPhoto is 1, but is 0 by default - are you setting it to 1 somewhere else?

    I tested your code without the above and it worked fine for me - both with Unity UI and AC-based Inventory menus.  What are your AC and Unity versions?
  • @ChrisIceBox

    I believe I'm up to date, version 2017.3.0f3 and 1.60.7 respectively. Lines 14 and 15 regarding sprites is required for a different thing, the newly generated texture from the screenshot that the player takes is then converted to a sprite and then applied to the relevant sprite renderer so that when the player selects the photo they have just taken (by clicking on the Inventory Icon) the game can correctly show this new screenshot by showing the GameObject with the sprite renderer that the new screenshot was assigned to.

    The problem is trying to also get the game to assign this new screenshot image to the Inventory Icon main texture. If I try to reference a pre-existing texture in my asset file and try to set the Inventory icon main texture as this asset file texture at runtime then this does work as you stated however for some reason it's not working when trying to assign a screenshot image that is created at runtime despite being able to easily assign that image to a sprite renderer with no issues (as also mentioned previously I tried simply assigning the new screenshot image to the Inventory Icon main texture and commenting out the assigning of it to the sprite renderer to make sure this wasn't causing a conflict however the same problem persisted).

    I do apologise for any confusion as I fully understand now that this doesn't seem to be an issue with the RuntimeInventory code as I previously thought considering that it does work for asset file image textures. Any thoughts though on why it isn't working in this instance whereby the image I'm trying to assign to the Inventory Icon main texture is generated at runtime via a screenshot despite me being able to assign that image to gameobject's sprite renderer component?

    p.s. yes the variable snapPhoto is being set elsewhere.
  • Yes, I'm aware of the core problem.  But I tested your code in full - screenshot-taking included, and it worked for me.

    It could be a platform/Unity issue in that case, since the code is solid so far as I can tell.  If it only works for you with saved asset files, it might be worth saving the texture to disk before assinging it to the inventory - a step necessary anyway if you want the change to be reflected in save-game files.
  • @ChrisIceBox

    Ah sorry, I must apologise - I re-read your reply and you clearly advised that you got it all working correctly however I think i mislead myself when I first read it since you were asking about the sprite lines and for some reason I threw myself off on a tangent thinking that you'd missed that I had been trying to use a newly taken screenshot.

    Really weird then that it works for you and not me? I can't understand why I get the 'object reference is not set to an instance' error and yet it works fine for you - at least that shows me that I can surely rectify this somehow so I'll crack on and I'll update this thread if I manage to crack it.

    By the way saving the image file is something that I was wondering about for the exact reason that you mentioned (saving and loading) however I struggled previously finding out how to do this - hopefully though this may also solve my above issue? Thanks again Chris.
  • @ChrisIceBox

    I've managed to save the newly taken screenshot, I've nearly sussed out how to then assign the newly saved screenshot to the Inventory Icon texture however I'm hitting a slight road block and I'm unsure what's causing the issue.

    I've added some additional code (butchered from examples I found on Google - as all my code usually is) which manages to save the image to a specific location in my assets folder. I was having a problem finding this image via code and so what I did instead was create an image at the save location with the same filename within the editor - then at runtime when the new screenshot is taken and saved rather than saving a new file it actually overwrites the existing image. This way I can assign the image in the editor by dragging and dropping and then the idea is when the new image is taken and overwrites the existing image that this will then update the Inventory icon to show the new image instead.

    The problem I'm having is that the image only gets saved after I exit play mode (and even then it only does so after a few minutes since exiting play mode has elapsed)? I've waited inside play mode for 5 minutes and the asset file remains unchanged (despite the Debug Log confirming that the picture was taken and saved to the required path). Then I exit and 30-60 seconds later a unity pop box appears and the new image is saved and overwritten in the asset folder exactly how I want it to (just obviously incredibly late)!

    Below is a link to the updated code I'm using, any insight is welcome.

  • edited January 2018
    @ChrisIceBox

    CAN IGNORE THIS POST - EXPLANATION IS IN NEXT POST BELOW

    Sorry, another quick post. I quickly tried my original idea (the one that worked for you with the first code that I submitted) however I loaded the demo inventory manager instead and changed the GetItem from 3 to 0 (since there is only 1 item in the demo inventory) and this worked fine and changed the inventory icon's main texture just as did for you when you tried it.

    I use categories in my Inventory but the demo doesn't - that's really one of the only differences between the two, could this be the potential cause? The only other major difference is the number of inventory items, the demo has only 1 however I have over 50 in mine - again would this potentially cause the issue?
  • @ChrisIceBox

    So sorry Chris, please ignore my post above - I've solved it and I feel incredibly stupid and I'm so annoyed that I wasted some of your time with this. You'll hopefully laugh when I explain, apologies again for running you around in circles!

    In my action list I was taking the screenshot (so calling the code that I showed you) and then near the end of the action list I would add the photo to the player's inventory! I just noticed after hovering over the GetItem line in my script that it clearly states players 'CURRENT' inventory! So I made sure that the Action List added the photo to the players inventory before calling the screenshot code and everything now works.

    However in light of this I do feel that you brought up a great point about save/load games and how the screenshots will survive loading a saved game - therefore despite me asking you to ignore the immediate post above can you please still look over the one before that regarding the issue I am having with the saving/loading of the image.

    Once again I can't apologise enough for the unnecessary hassled I've caused!
  • Saving in Application.dataPath might work when testing in the Editor, but won't when people play it on their own machines - you want to use Application.persistentDataPath instead.  That may clear up the delay, as this is where AC stores its save files as well.

    When loading a save game, you'll then need to load in this same texture and re-apply it to the inventory.  Likely you should also set a global Boolean variable too so that you can set whether or not this new texture should be loaded.

    To run code after loading a save, hook into the OnFinishLoading custom event.  This event runs after a succesful load, so this is when to run the code that checks the bool's value, retrieves the file and applies it to the item:

    http://pasteall.org/773831/csharp

    (Where "3" is the ID number of the global boolean, which must be true for the code to run)
  • @ChrisIceBox

    Thanks again for your help, particularly with the latest advice as it is more generic Unity advice rather than AC so I appreciate you helping with that too. I should be able to get this sorted now - just a quick one to clarify though, does the Application.persistentDataPath only work in a executable game build and not within the editor? I keep getting errors when using it whilst playing the game in the editor - I was going to alter the code so that if in the editor then Application.dataPath will be used however if in the proper game build then Application.persistentDataPath however before I delve into that I just wanted to quickly confirm that i understood the issue correctly.

    Thanks again.
  • It's platform dependent but does work in Editor, too - see the link to the Unity documentation.
  • Hi, I know this is an old post but it covers closely what I would like to do. Unfortunately the pasteall screenshots are not there anymore.

    I want the player to take screenshots and then to be able to scroll through these using thumbnails. (Would be a fantastic function in AC!) I've chosen to use Documents (without text) for this.

    When clicking on the Mobile Phone in the lower left, Camera shutter button and an inventory box with Document (textures) appear. See link below.

    The wanted result is that when clicking on the Camera Shutter, a screenshot will be taken, which will be assigned to the first Document item without a texture, and appear last in the inventory box with thumbnails.

    The Camera Shutter button is called through a custom action (Takescreenshot.cs), using the AC Actiontemplate. Screenshot of the short code in link below.

    To take the screenshot, I have copied the code from SaveSystem.cs line 985-1050 and put this before Run() in the custom action script.

    When clicking on the Camera shutter, the game freezes and two error messages appear, see link below.

    In my setup, I want to put this as a Document texture, but with the ickStarter.runtimeInventory.GetItem(1).tex = ScreenShot code it may end up as an Inventory item texture?

    If there is a better way to treat ingame screenshots please let me know. For now I am tearing my hair to get this working...

    Thanks for any help I can get.

    https://imgur.com/a/Il6yI25

  • Welcome to the community, @Magnus. There are a couple of issues to tackle, here.

    Screenshot-taking

    The message that appears is a Unity error, and will display if you attempt to read the contents of the screen before the end of the frame. This is handled in a coroutine, after calling WaitForEndOfFrame. You can find an example of this in the SaveSystem script's PrepareSaveCoroutine function, just before GetScreenshotTexture is called.

    Actions aren't MonoBehaviours, however, so you'll need to move your code to a regular C# script, and not rely on a custom Action for this.

    Place your script's GetScreenshotTexture inside a new C# script, and then also paste in the following:

    public void CreateTexture ()
    {
        StartCoroutine (CreateTextureCo ());
    }
    
    private System.Collections.IEnumerator CreateTextureCo ()
    {
        yield return new WaitForEndOfFrame ();
        Texture2D screenshot = GetScreenshotTexture ();
        Debug.Log ("Took screenshot succesfully.");
    }
    

    To run this code when the ActionList is run, attach your script to a new empty GameObject in the scene, prefab it, and then remove the original from the scene. Then, use AC's Object: Call event Action to call its CreateTexture function. Be sure to have the Action reference the prefab, and not the original script asset.

    If successful, you'll get a new message appear in the Console reporting so.

    Icon updating

    The code you're currently running does indeed affect the Inventory - GetItem returns an item that the Player is currently carrying.

    However, I'd recommend using Inventory items over Documents, as these are better equipped to handle such customisation.

    If you want to display photos in a separate Inventory menu, you can do so by defining a pair of categories in the Inventory Manager's Categories tab (e.g. "Normal" and "Photos"). Move your "regular" items to the Normal category, and then create some placeholder items in the Photos category.

    In your various InventoryBox menu elements, you can then check Limit by category? to limit their display to items in specific categories.

    Since this thread began, AC's been updated to make it much easier to override an item's texture through script - an InvInstance class's Tex property can now be set. I can assist with handling this, but see how you go with the above first, as we'll need to be sure everything else is working beforehand.

  • Superthanks for the rapid reply!!
    I did, I think, what you described under Screenshot-taking although I get the error message Coroutine couldn't be started because the the game object 'Take Screenshot' is inactive!
    https://imgur.com/a/fAtK53N

    Will use Inventory items. I already have the two categories but were not sure what I could do with them.

  • Screenshot-taking

    Ah, quite right. Sorry - the script will need to be in the scene for it to work after all.

    Attach a Constant ID component to the prefab and check Retain in prefab?. Then, place the prefab back in the scene, and use the Object: Send message Action instead. Use this Action to reference the scene object, set the Message to send to Custom, and type in "CreateTexture" to run that function.

    This object will need to be in each scene of your game - you can either drop the prefab in manually, or attach it to a persistent object such as your Player prefab.

    Icon updating

    If you have dummy items set up with a dedicated category in the Inventory Manager, you can attach this code to the end of your CreateTextureCo function:

    int categoryID = 1; // Set this to the photo category's ID number
    InvCollection playerInvCollection = KickStarter.runtimeInventory.PlayerInvCollection;
    foreach (InvItem item in KickStarter.inventoryManager.items)
    {
        if (playerInvCollection.Contains (item.id) || item.binID != categoryID) continue;
        InvInstance newItemInstance = new InvInstance (item);
        newItemInstance.Tex = screenshot;
        playerInvCollection.Add (newItemInstance);
        PlayerMenus.ResetInventoryBoxes ();
        break;
    }
    

    This will go through the Inventory Manager, and find the first item in the category that isn't currently held by the Player. It'll then add it to the Player's Inventory, overriding its texture with the screenshot.

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Welcome to the official forum for Adventure Creator.