【ハルシオンブログ】ScriptableObjectを使う時の注意点。そのまま使うとマスタの値が変わっちゃうよ?
まぁ しませんけど。坂内です。
以前ScriptableObjectの作成方法や、使い方を紹介しました。
【ハルシオンブログ】UnityでScriptableObjectを作成して、使う方法の紹介。なんか使いやすいように使ってください! 】
マスタデータ的な感じで使うことが多いかと思いますが、使う上で気を付けないといけない点があるので、紹介。
こんなデータを作成します。
【Enemy.cs】
public class Enemy : ScriptableObject
{
public List enemys = new List();
[System.Serializable]
public class EnemyData {
public string name;
public int maxHp;
public int hp;
public int atk;
public Sprite image;
}
}

これで、敵のデータ作成完了。
次に画面にボタンを置き、クリックすることで、敵に攻撃をする感じのを作ります。

【Script20190415.cs】
using UnityEngine;
using UnityEngine.UI;
public class Script20190415 : MonoBehaviour
{
[SerializeField]
Enemy enemyMst;
Enemy.EnemyData currentEnemy;
// UI
public Text lblEnemyName;
public Text lblEnemyHP;
public Image imgEnemy;
private void Start() {
MakeNewEnemy();
}
public void OnClickAttack() {
// 攻撃ダメージ(10~30)
int atkDmg = Random.Range(10,31);
currentEnemy.hp -= atkDmg;
if (currentEnemy.hp <= 0) {
MakeNewEnemy();
return;
}
lblEnemyHP.text = currentEnemy.hp + "/" + currentEnemy.maxHp;
}
private void MakeNewEnemy()
{
// 敵のデータをランダムで1体作成
currentEnemy = enemyMst.enemys[Random.Range(0, enemyMst.enemys.Count)];
lblEnemyName.text = currentEnemy.name;
lblEnemyHP.text = currentEnemy.hp + "/" + currentEnemy.maxHp;
imgEnemy.sprite = currentEnemy.image;
}
}
ボタンをクリックしたときにOnClickAttackを呼ぶようにします。
そうすると・・・・

えっ!
なんかおかしい・・・・
調べてみると、こうなっているのが分かります。

えー!マスタの値がかわってるうううううう!!!
これじゃマスタとしての意味なくなってしまいます。
これ、値渡しじゃなくて参照渡しだから起きちゃうんですよね。
なので、ScriptableObjectのオブジェクトを使う場合にはディープコピーにて使ってあげるといいと思います。
なんかいい方法あるのかもしれませんが、今のところこんな感じでやってます。
【Enemy.cs】
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "EnemyData", menuName = "ScriptableObjects/MakeEnemyData")]
public class Enemy : ScriptableObject
{
public List<EnemyData> enemys = new List<EnemyData>();
[System.Serializable]
public class EnemyData {
public string name;
public int maxHp;
public int hp;
public int atk;
public Sprite image;
public void Copy(EnemyData enemyData)
{
this.name = enemyData.name;
this.maxHp = enemyData.maxHp;
this.hp = enemyData.hp;
this.atk = enemyData.atk;
this.image = enemyData.image;
}
}
}
コピーメソッドを付けてあげます。
次にScript20190415.csのMakeNewEnemyメソッドを次のように変えます
Private void MakeNewEnemy()
{
// 敵のデータをランダムで1体作成
currentEnemy = new Enemy.EnemyData();
currentEnemy.Copy(enemyMst.enemys[Random.Range(0, enemyMst.enemys.Count)]);
lblEnemyName.text = currentEnemy.name;
lblEnemyHP.text = currentEnemy.hp + "/" + currentEnemy.maxHp;
imgEnemy.sprite = currentEnemy.image;
}
こんな感じでやれば、ちゃんと動くようになります。

気を付けないと、マスタデータが書き換わってしまいます。
という感じで、今日はScriptableObjectを使う上での注意点でした。
あでゅ~ノシ
« 【ハルシオンブログ】コンソールログのコピペ | 【ハルシオンブログ】ファイルの拡張子だけとか拡張子抜いた文字列が欲しい時 »
コメント
おそらく、MasterData内に現在HPとかのデータが有って、マスターデータと特定の敵のデータが別れていないのがややこしい原因だと思われます。
うちの場合は以下みたいな感じのデータ構造にしてます
public class EnemyMasterData
{
public int id;
public string name;
public int maxHp;
public int atk;
public Sprite image;
}
public class EnemyData
{
public int enemyMasterId;
public int hp;
}
しろくろ #- | URL | 2019/04/15 15:26 [edit]
Re: タイトルなし
しろくろさんへ
ありがとうございます!
たしかに例が悪いですね(笑)
HPは管理しなくて、MAXHPだけ管理した場合に、敵のLVによってMAXHPを増やすとかやった場合とかだとMAXHP触ることになってしまうので、同じ現象が起きてしまいます。
まぁその辺含め変動する値は別途用意するのがいいんですが、今回は例としてのお話でした!
> おそらく、MasterData内に現在HPとかのデータが有って、マスターデータと特定の敵のデータが別れていないのがややこしい原因だと思われます。
>
> うちの場合は以下みたいな感じのデータ構造にしてます
>
> public class EnemyMasterData
> {
> public int id;
> public string name;
> public int maxHp;
> public int atk;
> public Sprite image;
> }
>
> public class EnemyData
> {
> public int enemyMasterId;
> public int hp;
> }
株式会社ハルシオンシステム #- | URL | 2019/04/15 15:37 [edit]
敵のLV等によって変動する値もマスターデータの書き換えをするのはマスターデータとはなんぞや?となっちゃうのでおすすめできないですー。
マスターデータにレベルを渡すと最大HPを返すような関数をマスターデータ側に用意してやって、そちらを使うのがいいかもです。
なんにせよ、マスターデータの値を直接書き換える必要がある設計がNGだと思います。
しろくろ #- | URL | 2019/04/15 17:14 [edit]
Re: タイトルなし
たしかーに
マスタとして使うなら変更されないものだけをScriptableにすべきっすね!
> 敵のLV等によって変動する値もマスターデータの書き換えをするのはマスターデータとはなんぞや?となっちゃうのでおすすめできないですー。
> マスターデータにレベルを渡すと最大HPを返すような関数をマスターデータ側に用意してやって、そちらを使うのがいいかもです。
>
> なんにせよ、マスターデータの値を直接書き換える必要がある設計がNGだと思います。
株式会社ハルシオンシステム #- | URL | 2019/04/15 17:23 [edit]
| h o m e |