Good news! To provide faster support and grow the community, we'll be changing the forum to "Read Only" and turn to Discord instead for support.
Join here: https://discord.gg/zjNj5zZ

Passive Crafting - Starting Item Timer via Collection Name

Shipright
Posts: 68
Joined: Tue Jul 12, 2016 7:24 am

Passive Crafting - Starting Item Timer via Collection Name

Postby Shipright » Sat Jun 03, 2017 7:39 pm

Greetings!

So in reference to the timed item script you helped with last year (a timer starts when an item enters a collection, then turns into another item. And by helped I meant wrote for me!), I wanted to leverage this to create a new crafting option.

Passive crafting would take the functionality of a normal object inventory (if I can ever figure that out in the other thread!), add in the timed item functionality, to create a new crafting station. For example, if you have a timed item called "Bread Dough" and put it in the object "Oven"'s collection, that items timer would start and as long as you left it there long enough for the timer to complete it will turn into normal item "Bread."

Here is the code for the timed item type:

Code: Select all

public class UnusableTimedItemType : InventoryItemBase
{
    /// <summary>
    /// When the item is used, play this sound.
    /// </summary>
    public AudioClipInfo audioClipWhenUsed;


    public float expirationTime = 10f;
    public InventoryItemBase afterTimerItem;

    protected ITimerHelper timer;
    private int _timerHandle;

    protected void Awake()
    {
        if (afterTimerItem != null)
        {
            timer = TimerUtility.GetTimer();

            // Start a timer, after expirationTime. After that OnTimerExpired will be called.
            _timerHandle = timer.StartTimer(expirationTime, OnTimerExpired);
        }
    }

    protected void OnDestroy()
    {
        timer.StopTimer(_timerHandle); // When this object is destroyed stop the timer
    }

    // Called when the timer has expired adn it's time to swap out the item.
    protected void OnTimerExpired()
    {
        if (itemCollection != null)
        {
            // We're in a collection, Use the item on timer expiration
            ReplaceItem(this, afterTimerItem);
        }
        else
        {
            // Item is in the world (not in a collection), maybe completely destroy it and re-create it at this spot?
            var inst = UnityEngine.Object.Instantiate<InventoryItemBase>(afterTimerItem);
            inst.currentStackSize = currentStackSize;
            inst.transform.SetParent(transform.parent);
            inst.transform.localPosition = transform.localPosition;
            inst.transform.localRotation = transform.localRotation;
            inst.transform.localScale = transform.localScale;

            // Destroy the original (this object)
            Destroy(gameObject);
        }
    }

    public override void NotifyItemUsed(uint amount, bool alsoNotifyCollection)
    {
        base.NotifyItemUsed(amount, alsoNotifyCollection);
        PlayerManager.instance.currentPlayer.inventoryPlayer.stats.SetAll(stats);
    }


    public void ReplaceItem(InventoryItemBase a, InventoryItemBase replaceWithPrefab)
    {
        var collection = a.itemCollection;
        var index = a.index;
        var currentStackSize = a.currentStackSize;

        var inst = UnityEngine.Object.Instantiate<InventoryItemBase>(replaceWithPrefab);
        inst.currentStackSize = currentStackSize;
        inst.transform.SetParent(a.transform.parent);
        // Clear the slot (this won't fire any events!)
        collection.SetItem(index, null, false);
        // Set the new item
        collection.SetItem(index, inst, true);
    }
}


How can I reference a collections name and prevent the timer from starting unless the collection name matches a designated value?

It seems to me this could be a powerful new feature for InventoryPro. A lot of survival, strategy and RPG games have this type of "cooked" crafting system.

Shipright
Posts: 68
Joined: Tue Jul 12, 2016 7:24 am

Re: Passive Crafting - Starting Item Timer via Collection Name

Postby Shipright » Mon Jun 05, 2017 5:29 pm

So I have been working on this a bit. Here is the whole working script so far:

Code: Select all

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Devdog.General;
using Devdog.InventoryPro;

//summary
//This item type is an unusable item that after a set time will transform into another item.

public class UnusableTimedActivatedItemType : InventoryItemBase
{
    /// <summary>
    /// When the item is used, play this sound.
    /// </summary>
    public AudioClipInfo audioClipWhenUsed;

    public float expirationTime = 10f;
    public InventoryItemBase afterTimerItem;

    protected ITimerHelper timer;
    private int _timerHandle;

    protected void Awake()
    {
        if (afterTimerItem != null)
        {
            timer = TimerUtility.GetTimer();

            // Start a timer, after expirationTime. After that OnTimerExpired will be called.
            _timerHandle = timer.StartTimer(expirationTime, OnTimerExpired);
        }
    }

    protected void OnDestroy()
    {
        timer.StopTimer(_timerHandle); // When this object is destroyed stop the timer
    }

    // Called when the timer has expired adn it's time to swap out the item.
    protected void OnTimerExpired()
    {
        if (itemCollection != null)
        {
            // We're in a collection, Use the item on timer expiration
            ReplaceItem(this, afterTimerItem);
        }
        else
        {
            // Item is in the world (not in a collection), maybe completely destroy it and re-create it at this spot?
            var inst = UnityEngine.Object.Instantiate<InventoryItemBase>(afterTimerItem);
            inst.currentStackSize = currentStackSize;
            inst.transform.SetParent(transform.parent);
            inst.transform.localPosition = transform.localPosition;
            inst.transform.localRotation = transform.localRotation;
            inst.transform.localScale = transform.localScale;

            // Destroy the original (this object)
            Destroy(gameObject);
        }
    }

    public override void NotifyItemUsed(uint amount, bool alsoNotifyCollection)
    {
        base.NotifyItemUsed(amount, alsoNotifyCollection);
        PlayerManager.instance.currentPlayer.inventoryPlayer.stats.SetAll(stats);
    }


    public void ReplaceItem(InventoryItemBase a, InventoryItemBase replaceWithPrefab)
    {
        var collection = a.itemCollection;
        var index = a.index;
        var currentStackSize = a.currentStackSize;

        var inst = UnityEngine.Object.Instantiate<InventoryItemBase>(replaceWithPrefab);
        inst.currentStackSize = currentStackSize;
        inst.transform.SetParent(a.transform.parent);
        // Clear the slot (this won't fire any events!)
        collection.SetItem(index, null, false);
        // Set the new item
        collection.SetItem(index, inst, true);
    }
}



Here is the relevant section I am working with:

public string activatingCollection = "Activating Collection";
public float expirationTime = 10f;
public InventoryItemBase afterTimerItem;

protected ITimerHelper timer;
private int _timerHandle;

protected void Awake()
{
if ((afterTimerItem != null) && (itemCollection.collectionName == activatingCollection))
{
timer = TimerUtility.GetTimer();

// Start a timer, after expirationTime. After that OnTimerExpired will be called.
_timerHandle = timer.StartTimer(expirationTime, OnTimerExpired);
}
}


My changes to an otherwise working script are bolded. So I added activatingCollection so I can enter the name of collections I want to trigger the timer. I then added myCollection from ItemCollectionBase to reference the name of the collection the item as in (as per the comments in that script).

This doesn't work, probably because I am not referencing the specific game object serving as the collection. How would I reference an item's collection's name and compare it to activatingCollection?

Shipright
Posts: 68
Joined: Tue Jul 12, 2016 7:24 am

Re: Passive Crafting - Starting Item Timer via Collection Name

Postby Shipright » Sun Jun 11, 2017 8:12 pm

So thinking about this some more, where exactly does an item reside once you drop it into a slot? Is it then a child of the slot and thus the container and thus the inventory window that the IventoryUI script is attached too?

I ask, because I have attempted several methods pf GetComponenInParent and have had no success. Here is my latest attempt:

Code: Select all

    protected void Awake()
    {
        InventoryUI collectionName = GetComponentInParent<InventoryUI>();

        if ((afterTimerItem != null) && (itemCollection.collectionName == activatingCollection))
//      if (afterTimerItem != null)
        {
            timer = TimerUtility.GetTimer();

            // Start a timer, after expirationTime. After that OnTimerExpired will be called.
            _timerHandle = timer.StartTimer(expirationTime, OnTimerExpired);
        }
    }

nathanj
Posts: 223
Joined: Sun Sep 25, 2016 5:38 am

Re: Passive Crafting - Starting Item Timer via Collection Name

Postby nathanj » Sun Jun 11, 2017 11:08 pm

I'm dealing with a very similar issue here: http://forum.devdog.io/viewtopic.php?f=4&t=3064

I believe the issue with with the dynamic search because in my code I can manually connect an InventoryUI window to the component if they both belong to the same scene but as soon as I try to make the component dynamically filled it doesn't work. Unless I am completely missing something, which is very possible.

I am pretty sure that the item becomes a child of the inventory (as long as it's not just being referenced (ie. skillbar in some cases, etc).

Nathan

Shipright
Posts: 68
Joined: Tue Jul 12, 2016 7:24 am

Re: Passive Crafting - Starting Item Timer via Collection Name

Postby Shipright » Mon Jun 12, 2017 4:57 pm

So quite on accident I found where the items go:

Image

The blue is where I thought the items would go, as a child of the container in the Inventory window the governing component scripts of the collection are.

The red is where they actual reside when in any collection's slots (I haven't tried skillbars). This poses a problem for me as I can't reference a collection's name by simply recursively referencing up the item's hierarchy until I find it like I was trying.

Shipright
Posts: 68
Joined: Tue Jul 12, 2016 7:24 am

Re: Passive Crafting - Starting Item Timer via Collection Name

Postby Shipright » Fri Jun 30, 2017 9:10 pm

So based on the direction from the other thread where I was advised to got he myCollection.collectionName route instead of looking up the parent/child relationship, I know have the below:

Code: Select all

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Devdog.General;
using Devdog.InventoryPro;

//summary
//This item type is an unusable item that after a set time will transform into another item.

public class UnusableTimedActivatedItemType : InventoryItemBase
{
    /// <summary>
    /// When the item is used, play this sound.
    /// </summary>
    public AudioClipInfo audioClipWhenUsed;

    public string activatingCollection = "Activating Collection";
    public float expirationTime = 10f;
    public InventoryItemBase afterTimerItem;
    public ItemCollectionBase myCollection;

    protected ITimerHelper timer;
    private int _timerHandle;

    protected void Awake()
    {

        if ((afterTimerItem != null) && (myCollection.collectionName == activatingCollection))
        {
            timer = TimerUtility.GetTimer();

            // Start a timer, after expirationTime. After that OnTimerExpired will be called.
            _timerHandle = timer.StartTimer(expirationTime, OnTimerExpired);
        }
    }


This, however, gets me the error:

NullReferenceException: Object reference not set to an instance of an object
UnusableTimedActivatedItemType.OnDestroy () (at Assets/MyInventoryProFiles/Scripts/ItemTypes/UnusableTimedActivatedItemType.cs:42)


I am not sure what object I should be pointing to, I assumed "myCollection" took care of that part. I feel close, but still no cigar!

User avatar
jjahuijbregts
Site Admin
Posts: 2091
Joined: Wed Apr 22, 2015 7:25 pm
Contact:

Re: Passive Crafting - Starting Item Timer via Collection Name

Postby jjahuijbregts » Tue Jul 04, 2017 6:29 am

Shipright wrote:So based on the direction from the other thread where I was advised to got he myCollection.collectionName route instead of looking up the parent/child relationship, I know have the below:

Code: Select all

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Devdog.General;
using Devdog.InventoryPro;

//summary
//This item type is an unusable item that after a set time will transform into another item.

public class UnusableTimedActivatedItemType : InventoryItemBase
{
    /// <summary>
    /// When the item is used, play this sound.
    /// </summary>
    public AudioClipInfo audioClipWhenUsed;

    public string activatingCollection = "Activating Collection";
    public float expirationTime = 10f;
    public InventoryItemBase afterTimerItem;
    public ItemCollectionBase myCollection;

    protected ITimerHelper timer;
    private int _timerHandle;

    protected void Awake()
    {

        if ((afterTimerItem != null) && (myCollection.collectionName == activatingCollection))
        {
            timer = TimerUtility.GetTimer();

            // Start a timer, after expirationTime. After that OnTimerExpired will be called.
            _timerHandle = timer.StartTimer(expirationTime, OnTimerExpired);
        }
    }


This, however, gets me the error:

NullReferenceException: Object reference not set to an instance of an object
UnusableTimedActivatedItemType.OnDestroy () (at Assets/MyInventoryProFiles/Scripts/ItemTypes/UnusableTimedActivatedItemType.cs:42)


I am not sure what object I should be pointing to, I assumed "myCollection" took care of that part. I feel close, but still no cigar!


Well, that's hard to say, it errors out on UnusableTimedActivatedItemType line 42, which isn't in your post above. Might it be something in the Use method? Can you post the entire stack trace + custom item type?

Shipright
Posts: 68
Joined: Tue Jul 12, 2016 7:24 am

Re: Passive Crafting - Starting Item Timer via Collection Name

Postby Shipright » Tue Jul 04, 2017 5:44 pm

Hey jjahuijbregts, its totally my fault but this discussion is bleeding over into two threads to avoid confusion I am going to post the last two posts by you and I from there here so we can consolidate the troubleshooting.

This is where you suggested switching from a myCollection solution to a myItem solution.

jjahuijbregts wrote:
Shipright wrote:I get how to reference the field, by question is how to get to the specific instance of the collection a particular item is in.

You say myCollection.collectionName, but without referring to a particular parent how do I know I am referencing the collectionName for the right collection?

I was trying to use GetComponentInParent but that doesn't work. Does "myCollection" have some functionality where if used inside an items script it knows to go directly to that particular items collection?

For instance I tried doing this:

Code: Select all

        InventoryUI collectionName = GetComponentInParent<InventoryUI>();

        if ((afterTimerItem != null) && (itemCollection.collectionName == activatingCollection))

        {
            timer = TimerUtility.GetTimer();

            // Start a timer, after expirationTime. After that OnTimerExpired will be called.
            _timerHandle = timer.StartTimer(expirationTime, OnTimerExpired);


How would I implement what you are telling me in this scenario? To be clear since I cam calling the window from prefab its possible to have multiple game objects with the exact same collectionName. If the player wants to build ten ovens, then baking bread in any one of them should work because the item is looking for the collectionName "Oven" to start the item timer. That's why I started by working my way through the parent, because there can possibly be multiple of the same script on different game objects at the same time, I need to make sure I reference to the one that holds the collection the item is actually in.



Hm.. not sure if I'm following along correctly; But you could just use myItem.itemCollection, this references to the item collection the item is currently in. This will be null if the item is not in a collection.

Optionally, you could also give each "Oven" a unique suffixed number.


And this is my latest attempt utilizing myItem and running into basically the same error (marked in the code this time):

So I went that route and got a similar error

NullReferenceException: Object reference not set to an instance of an object
UnusableTimedActivatedItemType.Awake () (at Assets/MyInventoryProFiles/Scripts/ItemTypes/UnusableTimedActivatedItemType.cs:28)


The code as it stands:

Code: Select all

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Devdog.General;
using Devdog.InventoryPro;

//summary
//This item type is an unusable item that after a set time will transform into another item if its in the the right collection.

public class UnusableTimedActivatedItemType : InventoryItemBase
{
    /// <summary>
    /// When the item is used, play this sound.
    /// </summary>
    public AudioClipInfo audioClipWhenUsed;

    public string activatingCollection;
    public float expirationTime = 10f;
    public InventoryItemBase afterTimerItem;
    public InventoryItemBase myItem;

    protected ITimerHelper timer;
    private int _timerHandle;

    protected void Awake()

   

    {
        var collection1 = activatingCollection;
        GameObject thisItem = this.gameObject;
       
        //  Failed Attempt    myItem = thisItem.GetComponent<InventoryItemBase>();
        //  Failed Attempt    myItem.itemCollection.collectionName;
        //  Failed Attempt    var collection2 = thisItem.GetComponents<InventoryItemBase>(myItem.itemCollection.collectionName);

        myItem = GetComponent<InventoryItemBase>();
        var collection2 = myItem.itemCollection;

 
        //      Check if the collection the item is in equals the items activivating colletion, and if so start the timer.
        if (afterTimerItem != null && collection1 != null && collection2.collectionName != null && collection1 == collection2.collectionName) <-----------------------------ERROR!!!!!!!!!!!!!!!!!!!!!!
        {
            timer = TimerUtility.GetTimer();

            // Start a timer, after expirationTime. After that OnTimerExpired will be called.
            _timerHandle = timer.StartTimer(expirationTime, OnTimerExpired);
        }

        Debug.Log("activating collection is " + collection1);
        Debug.Log("Item being compared is " + thisItem);
        Debug.Log("Collection item is in " + collection2);

    }

    protected void OnDestroy()
    {
        timer.StopTimer(_timerHandle); // When this object is destroyed stop the timer
    }

    // Called when the timer has expired adn it's time to swap out the item.
    protected void OnTimerExpired()
    {
        if (itemCollection != null)
        {
            // We're in a collection, Use the item on timer expiration
            ReplaceItem(this, afterTimerItem);
        }
        else
        {
            // Item is in the world (not in a collection), maybe completely destroy it and re-create it at this spot?
            var inst = UnityEngine.Object.Instantiate<InventoryItemBase>(afterTimerItem);
            inst.currentStackSize = currentStackSize;
            inst.transform.SetParent(transform.parent);
            inst.transform.localPosition = transform.localPosition;
            inst.transform.localRotation = transform.localRotation;
            inst.transform.localScale = transform.localScale;

            // Destroy the original (this object)
            Destroy(gameObject);
        }
    }

    public override void NotifyItemUsed(uint amount, bool alsoNotifyCollection)
    {
        base.NotifyItemUsed(amount, alsoNotifyCollection);
        PlayerManager.instance.currentPlayer.inventoryPlayer.stats.SetAll(stats);
    }


    public void ReplaceItem(InventoryItemBase a, InventoryItemBase replaceWithPrefab)
    {
        var collection = a.itemCollection;
        var index = a.index;
        var currentStackSize = a.currentStackSize;

        var inst = UnityEngine.Object.Instantiate<InventoryItemBase>(replaceWithPrefab);
        inst.currentStackSize = currentStackSize;
        inst.transform.SetParent(a.transform.parent);
        // Clear the slot (this won't fire any events!)
        collection.SetItem(index, null, false);
        // Set the new item
        collection.SetItem(index, inst, true);
    }
}




I suppose in this case I would want the script to refer to itself (the item it is on)?

EDIT: Alright, a day of working on this in 85 degrees (AC is out, and I live in the desert...) and I think I need to just step this back.

When you say use myItem.ItemCollection I assume I implement this as such:

1.)Set a public variable for this Item Type were I can set the collection name I want to trigger the timer as so: "public string activatingCollection;

2.) add a variable at the top of the script as so: "public InventoryItemBase myItem;

3.) Inside the Awake() method set a local variable that gives me the dame of the collection the item is in (or null if its not) as so "var residingCollection = myItem.itemcollection.collectionName;

4.) In the if statement that would start the timer, I can then compare "residingCollection" and "activating collection" and if they are the same start the timer.

Am I missing something here?

Shipright
Posts: 68
Joined: Tue Jul 12, 2016 7:24 am

Re: Passive Crafting - Starting Item Timer via Collection Name

Postby Shipright » Fri Jul 07, 2017 7:01 pm

Hey all, has anyone else returned an item's current collection name for other purposes? If so how did you do it? No matter how I try and implement "myItem" or "myCollection" I get a null reference. I think the problem is that that collectionName for whatever collection the item is in doesn't actually reside on the item game object but I honestly don't know.

I am just repeating myself at this point and am at a loss. Has anyone invented this wheel already?

User avatar
jjahuijbregts
Site Admin
Posts: 2091
Joined: Wed Apr 22, 2015 7:25 pm
Contact:

Re: Passive Crafting - Starting Item Timer via Collection Name

Postby jjahuijbregts » Mon Jul 10, 2017 5:23 am

Shipright wrote:Hey all, has anyone else returned an item's current collection name for other purposes? If so how did you do it? No matter how I try and implement "myItem" or "myCollection" I get a null reference. I think the problem is that that collectionName for whatever collection the item is in doesn't actually reside on the item game object but I honestly don't know.

I am just repeating myself at this point and am at a loss. Has anyone invented this wheel already?


The gameObject reference is never supposed to be null, as the gameObject is the container that keeps all of the components. Nevertheless, it does seem to throw it on the this.gameObject line; But that makes very little sense, as it would have to throw an exception on this.... :?:

Is the object destroyed maybe? It could explain the .gameObject error if it's throwing the exception inside Unity's internal lib..

Could you zip the file and upload it as an attachment? The forum adds a lot of empty line breaks, and I'm wondering if I'm not looking at the wrong line of code here.


Return to “Programming”

Who is online

Users browsing this forum: No registered users and 3 guests