So I have a scriptable object that holds a list of Monsters. The list contains things like "name, stat1, stat2, stat3, etc… It is basically my database of information I can use to pull base data for the monsters in other areas of the game.
Now in another script I have a list that holds monsters that are apart of the players inventory. I can call the database and add the monsters to the players inventory with no issue. My issue starts when I try and change a stat of a monster in the inventory. When I do this it changes the stat of “both” the original monster from the database, and the new monster that’s inside the inventory. I only want the monster in the inventories stat to change.
Im a little rusty with programming, and im sure its something super simple that im missing. Can anyone take a look?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MonsterEncounter : MonoBehaviour
{
[SerializeField] private MonsterDatabase database;
public List<Monster> monsterEncounter = new List<Monster>();
// Start is called before the first frame update
void Start()
{
InitializeEncouunter();
}
public void InitializeEncouunter()
{
Monster newInstanceMonster = new Monster();
newInstanceMonster = (database.Monsters[1]);
monsterEncounter.Add(newInstanceMonster);
//CHANGES BOTH ORIGINAL AND NEW MONSTER STAT
monsterEncounter[0].monsterAttack = 500f;
}
}
In your InitializeEncouunter method you first create a new monster, but then you immediately assign a new value to the newInstanceMonster variable, so newInstanceMonster now refers to some old monster from your database. I’m not sure exactly what it is you are trying to do at row 20 of your code?
Also it would be better to directly use newInstanceMonster instead of monsterEncounter[0] to modify the monsterAttack stat of the monster instance, because if some external systems were to adds monsters to the monsterEncounter list, then monsterEncounter[0] might not refer to the newly created instance.
So your code should look something more like this:
public void InitializeEncouunter()
{
Monster newInstanceMonster = new Monster();
monsterEncounter.Add(newInstanceMonster);
newInstanceMonster.monsterAttack = 500f;
}
On line 19 I created a new monster to just be a empty shell with no information about the monster. On line 20 I was trying to grab all the base information of a monster from the database and apply it to the newInstance of the monster.
Then I was going to write a block of code that randomizes the stats of the new monster within a certain range, but as I was testing I noticed that when I changed the stat of the new monster, the stat of the database monster also changed. Does that make more sense of what im trying to do here?
The code you provided only changes the attack stat. All other stats are blank. I dont want to set everything manually due to there being a lot of properties attached to a monster. The properties are preset through the inspector.
Okay, I see. You can’t copy over the state of a class like that just by using the = operator, that just changes the instance which the variable references. You need to do use the = operator on a lower level to set each stat inside the monster instance separately.
You however mentioned that you don’t want to do that, given how many stats each monster has. Luckily it is also possible to automate this process using serialization. Unity has a class called JsonUtility which can be used to serialize the complete state of an instance, and then override the values of another instance using that serialized data.
public class Monster
{
public float monsterAttack = 100f;
...
// create a new instance of monster
public Monster() { }
// create a new instance of monster with stats copied from another instance
public Monster(Monster prefab)
{
var serialized = JsonUtility.ToJson(prefab);
JsonUtility.FromJsonOverwrite(serialized, this);
}
}
Ok I think I got this working how I wanted. Now im able to copy as many parameter values as I want from a database source, and can change those values without it affecting the source data. I shortened everything out in the scripts so people can better read everything. No idea if this is the best way to do this, but the monster class uses tons of variables, and it works for me. Hope this helps someone having the same issue.
Example Database Script
public List<Monster> Monsters;
[System.Serializable]
public class Monster
{
public float monsterAttack;
//Base parameter values are set in inspector via scriptable object
public Monster(float attack)
{
monsterAttack = attack;
}
//Call this to create a monster with empty values
public Monster()
{
}
//When monster is called its stats belong to itself
public Monster(Monster i)
{
this.monsterAttack = i.monsterAttack;
}
}
Example Encounter Script
[SerializeField] private MonsterDatabase database;
public List<Monster> monsterEncounter = new List<Monster>();
public void InitializeEncouunter()
{
//Create a empty Monster
Monster newInstanceMonster = null;
//Add the empty Monster to the encounter list
monsterEncounter.Add(newInstanceMonster);
//Copy data from database to fill in empty data
monsterEncounter[0] = new Monster (database.Monsters[1]);
//Change stats to whatever you like without overwriting source data
monsterEncounter[0].monsterAttack = 300f;
}