FC2ブログ
    05 «1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.» 07

    ハルシオンシステムの気ままBlog

    株式会社ハルシオンシステムのメンバーが送る、UnityやらJavaやらの技術的話題から、自社開発のアプリの宣伝とかとかのブログです。ほんと気ままにいきたいと思います。更新日は毎週 月 木でっす!

     

    【ハルシオンブログ】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を使う上での注意点でした。

    あでゅ~ノシ

    Category: 開発日記(Unity)

    Tag: Unity  ゲーム  ゲーム開発  C#  ScriptableObject 
    tb 0 : cm 4   

    コメント

    おそらく、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]

    コメントの投稿

    Secret

    トラックバック

    トラックバックURL
    →http://halcyonsystemblog.jp/tb.php/593-9b586d11
    この記事にトラックバックする(FC2ブログユーザー)