#So rebuilding the dictionary in Start
1 messages · Page 1 of 1 (latest)
Remove static from everything, and make your ItemDatabase inherit MonoBehavior
Then drag it in the scene
Only classes inheriting UnityEngine.Object can have serialized values
so either a MonoBehavior or a ScriptableObject
and Dictionary can't be serialized anyway
(which is incredibly annoying and I can't believe it still can't be done)
so yes you'd have to build it, or use SerializedDictionary which is an extension someone suggested me. Havent tried yet tho
Well, my intention was to create this ItemDatabase as a static class that could be accessed from anywhere easily. I just would like a dictionary to look up items from a string.
I guess I'm going about doing this the wrong way.
You cannot serialize a static instance, it must inherit UnityEngine.Object
So start by deciding if you want your database to live in the hierarchy or the project
I want it to live in the project.
Then change it to public class ItemDatabase : ScriptableObject
add [CreateMenuItem(menuName = "ItemDatabase")] above it
Then you will get a new editor option to create an ItemDatabase instance
(that lives in your project, and can have serialized state)
Right, I'm familiar with the basics of scriptable objects. Making a scriptable object that is static like that will work? and then just Itemdatabase.itemDictionary will work how I'm expecting it to?
No, it cannot be static, that was my mistake
Sorry I'm not the greatest at all of this, I really appreciate the help.
You will need to write some code to fetch an instance of your scriptable object. The legacy approach is using Resources.Load, the newer one is to use Addressables
You can start with Resources.Load for prototyping, it's easier to set up
This will return the serialized instance of your ItemDatabase asset
Which you can then read at runtime
Well, I already have a GameManager object in my hierarchy, I could probably just drop a reference to the SO in that and call GameManager.ItemDatabase for a simple method couldn't i?
That's fine too
As long as your GameManager is an actual game object and not also static
It's singleton. That isn't an issue is it? It's a pattern I followed from a video about having a singleton GameManager that instantiates prefabs of other non singleton managers as children and has public references to them.
That's fine
I recommend you read this https://docs.unity3d.com/Manual/script-Serialization.html
I will do. As you can probably tell there are some gaps in my understanding of serialization in Unity, among other things.
It's all good, that's why people are here to help 👍
some people like to hate on Singletons. But for these use cases they are awesome
Serialization especially is a tricky topic in unity
The reason people hate on them is that singletons are usually a sign of bad design
Yeah, I've seen a lot of people talk about singletons, but I watched a youtube video (https://www.youtube.com/watch?v=tcatvGLvCDc&ab_channel=ThousandAnt) linked in an article about this method and it seemed really awesome. It's very modular as well, if you set it up right, where you can just instantiate the systems you need to work with.
We take a look at how Sam set up his Master Singleton in his game Zarvot, and many common problems and considerations taken, as well as the advantages and disadvantages of his “Master Singleton” structure.
Singletons are very useful and can be very dangerous. Hopefully this video sheds some light on how Sam did it for his game.
If you’d like t...
Anyway, I appreciate the help from both of you. Thanks again!
The main issue with them is that it ties a lot of game logic to a single class
Also what the video describes is not actually the singleton pattern
😅
Well, it uses a singleton. But its purpose is just to allow other gameobjects it holds references to to reference each other.
Yes, but that pattern is called the service locator pattern, not singleton
The actual services in this case are singletons
Ah ok. I didn't recall the name of the pattern.
I do recall that name from the article I read that mentioned it, though. It seems like a good pattern for something like an overall game manager to me, though.
As some food for thought, pertaining to your ItemDatabase example, it is more modular to just give a serialized reference of the ItemDatabase instance of whatever object needs it, instead of storing it in a singleton instance
That enables you to pass separate ItemDatabase to different instance of objects, which may even make so you don't need a "global" item database
You could instead pass a direct Item instead, if a specific item is needed by an instance, or a DropTableList, which contains items, but holds different information, etc
Oh, here's a question. The reason I had built the ItemDatabase the way I did is because it needs to be modifiable at runtime. Making it a scriptable object causes no problems with this?
So you want to initialize it with some values and then modify that at runtime?
And the general idea was so that "Inventories" on characters could just hold an ItemID that they could look up the item using the item database. This so I only need to save the ID and not information about the item in each inventory.
oh, don't use ScriptableObject if you want to edit the data
when you build the game, it won't save anything
even tho it does in editor
Right, that's what I was thinking.
I don't see why you need to modify the data on the ItemDatabase for this use case. Just access the database and find the item from the ID, and store the ID on whatever owns the item
e.g. a player
The main idea behind my system was that it works like this: In the editor I create a scriptable object version of the editor and add it to the ItemDatabase, the ItemDatabase then builds an "Item" from the scriptable object that is actually used by the game with a unique item ID. I then pass it an ItemID when I need to know something about an item.
So it's like a global item database of all the items in the game.
Those items contains some information about them though right? For example, how much the item costs
Correct. But the inventory of a character would only need to know the ItemID.
I still don't see why you need to change any data on the ItemDatabase
It just holds references to all items
Because I want the player to be able to create items during gameplay.
You mean a concrete instance of an item, for example, so that the item can take damage
Well, I mean I want to add the item to the item database so that all I need to store in their inventory is its ID.
I guess it seemed like it would be better for when I needed to save the inventories if they've been modified. Just saving a array of IDs vs all of the data on the item.
I mean I assume the item would store the item ID
Well, if the inventory contains the actual item, then there's not much of a need for it to have an ID.
Sure, for saving 🙂
Saving is an entirely different beast, and there is no easy answer to it, it heavily depends on the project
I'll write you a small code sample as an example of what I would do to achieve something like you mentioned. To go back to what you asked earlier, you can store runtime data on a scriptable object, but it won't be serialized
(and I would recommend not making it serialized, and I would also recommend not storing runtime data on a scriptable object at all)
Well, I have Easy Save 3, which based on my testing works ok for saving things like how I've structured my inventory system. I just wanted to create a database that would let me get a reference to my items when I need to so I could just replace them with their ID in the inventory and look them up when needed.
Could I just save the runtime changes to a scriptable object with Easy Save 3 and load those changes when needed?
No, as the scriptable object is not something that is saved at runtime
You will need to save your save game to a file
The main use of scriptable objects is authoring time data
Well, that's what I mean. Easy Save 3 can write a dictionary out to a file and load it back from that file. Could I just save and load runtime changes to the dictionary on the scriptable object with that?
Sure, but I wouldn't recommend it
I would instead store this runtime data on something that lives in your scene, e.g. a game object, or ideally, on the actor that owns the item
Or I guess at this point I understand what I need to do. I think I'll just create an ItemDatabaseManager object with a script that holds a DefaultItemDatabase scriptable object it will copy data from when a new game is started. Then it will have a bool itemDatabaseHasBeenModifies and when a game is loaded it if that bool is true it will load the dictionary in from a file.
Just to illustrate the use case for scriptable objects, this is roughly of how I would implement something like this:
public class ItemSettings : ScriptableObject
{
public int Cost;
public Item Create()
{
return new Item(); //set stuff here from settings
}
}
//This is the thing getting saved
public class Item
{
}
Your item database would hold all ItemSettings, and let you create new items, and Item holds instance data (e.g. damage taken) for an instance of an item
public class WeaponItemSO : EquippableItemSO
{
public WeaponType weaponType;
public float attackPower;
private void Awake()
{
itemType = ItemType.Weapon;
}
public override void BuildItem()
{
item = new WeaponItemData
{
name = name,
itemID = itemID,
useable = useable,
itemType = itemType,
itemMaterial = itemMaterial,
weaponType = weaponType,
attackPower = attackPower,
useResource = useResource,
onUseResourceDecrease = onUseResourceDecrease,
destroyOnResourceDepleted = destroyOnResourceDepleted,
replaceOnResourceDepleted = replaceOnResourceDepleted,
replacementItemID = replacementItemID
};
}
}```
I'm doing something very similar already in using scriptable objects to create items.
yep, that looks good, but I wouldn't store the instances of WeaponItemData in the ItemDatabase
Well, my idea was that everything about the item should be in the database, because I really only need to know that information if a character equips an item or if i need to show it to the player.
That was my thought process at least.
So I figured inventoryslots could just contain the ItemID and the amount of the item in the slot. Then if a character equips an item or something I can use the ItemID to see how their stats need to change, for example.
I understand the thought process, there is just no advantage to doing it this way that I can see. If you want to save your game, you need to save your actors anyways since they have the item id.
Instead of iterating over IDs of items to calculate stats, you could just directly iterate over the items
But then when I save an inventory, I would have all the extra data like the stats of the item being saved for every inventory, right?
No, because all immutable data is stored in ItemSettings, not in Item. Whether or not that data is immutable depends on your game
For example, if Damage is immutable, you would read it as myItem.ItemSettings.Damage instead of myItem.Damage
All instances of Item share the same reference to the ItemSettings object that created it
When serializing your game state to a save file, all Item instances store the id of the ItemSettings that created it, so they can find the scriptable object that contains those immutable values at runtime
But if an item is created at runtime, it wouldn't have an associated scriptableobject, so all of the data for items has to be stored in the database.
Why does it not have an associated scriptable object?
You are creating the item from the scriptable object at runtime
public class ItemSettings : ScriptableObject
{
public Item Create()
{
return new Item() { ItemSettings = this };
}
}
public class Item
{
public ItemSettings Settings;
}
Then you could for example have an enemy that drops an item on death:
public class Enemy : MonoBehavior
{
public ItemSettings ItemToDropOnDeath;
public void OnDeath()
{
ItemSettings.Create().Spawn(transform.position);
}
}
I'm sorry I'm having a little trouble articulating what I'm not understanding, I guess.
What I was trying to say is if "Damage" for example is stored on the scriptable object then if I wanted a new item at runtime with a different Damage value, I would use the scriptable object to make an Item and then change its damage value?
Why are you deciding the damage at runtime, what value do you want to set here?
Well, to have a specific example, imagine the item in question is a sword. I want the player to be able to make a sword with stats they have picked while playing the game.
Is there any authoring time data that decides this damage value?
In this example I would assume it depends on the stats chosen, not on the item at all
Well, there will be formulas to determine ranged of inputs eventually but at the moment I just wanted to input numbers through UI.
ranges*
I was aiming for the time being to make it capable of relatively arbitrary item generation. Like create a sword with any damage value I want, for example.
This is entirely up to you, and is extremely game specific.
For example, you could have a base damage value, and then add a random multiplier to generate "procedural" weapons:
public class ItemSettings : ScriptableObject
{
public float BaseDamage;
public Item Create()
{
return new Item() { ItemSettings = this, Damage = BaseDamage * Random.Range(0.5f, 1.5f) };
}
}
public class Item
{
public ItemSettings Settings;
public float Damage;
}
Or you could make the damage not be an authoring time value at all:
public class ItemSettings : ScriptableObject
{
public Item Create(float damage)
{
return new Item() { ItemSettings = this, Damage = damage };
}
}
public class Item
{
public ItemSettings Settings;
public float Damage;
}
The scriptable object holds all the shared and authoring time data required to create the concrete instance
If you don't have any authoring time data required to create your item, then you don't need a scriptable object for it
At then end of the day they are just an authoring time data storage mechanism, nothing more
Right, my plan was to use them to create all of the items that would be in the game when it is new, but allow the player to modify them in various ways while playing and for modified items to be able to be given to NPCs and such. Pretty standard stuff and this seems like a really obvious use case for scriptable objects.
Well, modify instances of items, i mean, not the base version of the item.
But how are you creating all the items that will be in the game at the start of the game, that is the definition of authoring data
I'm creating them from the scriptable objects.
All of them at once?
What do you mean?
create all of the items that would be in the game when it is new
I would assume you create the exact amount of items you need at the time
For example, if there are 2 entities with 2 weapons each, there would be 4 Item instances
Even if you have 500 different ItemSettings instances
For example, if you spawn an enemy
public class Enemy : MonoBehavior
{
public ItemSettings WeaponToEquipOnSpawning;
public Item EquippedWeapon;
void Start()
{
EquippedWeapon = WeaponToEquipOnSpawning.Create();
}
}
Coming back to your question earlier, you can modify the created instance of Item, but you shouldn't modify the shared instance ItemSettings
so you could do EquippedWeapon.Damage = 500;
The easiest analogy I can give is that ItemSettings represent the uniquely authored weapons in your game, so you could have 500 different weapons. Then you can create an infinite number of instances of these unique designs at runtime, modifying whatever data you need.
What parts of your data are on ItemSettings and what are on Item is not something I can tell you, that's part of your game design
Right, I think this is more or less what I'm trying to do. I think based on everything you've told me I'll be able to get the behavior I want.
At the end of the day I'm just telling you how I would do this, you are free to implement it however you want. One thing I would recommend to you though is that you do not modify a scriptable object's data at runtime, it's just not what they are for
Right, that was my understanding. They are intended for authoring data in the editor only.
I'm off to bed then
Have a nice night. Thanks once again for all of the help!
No worries, I hope I didn't confuse you too much, these aren't easy concepts to explain, especially if you are relatively new to unity