FC2ブログ
    06 «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.31.» 08

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

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

     

    【ハルシオンブログ】あれ?ScriptableObjectの数値が変わってるんだけど?シャローコピーとディープコピーについて 

    おはようございます。坂内っす。
    ゴールデンウィークも終わりましたね。
    皆様どのように過ごしたでしょうか・・・・??え?なんでもないです。

    Unityにおけるディープコピーについて。

    ScriptableObjectでデータの管理をしている時に、そのデータをそのままゲーム中で使い更新すると、元のScriptableObjectのデータも更新されてしまい、困ることありませんか?

    こんな感じ。

    [EnemyData.cs]

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    [CreateAssetMenu(menuName = "SObject/Create EnemyData")]
    public class EnemyData : ScriptableObject {

    public List<EnemyParam> enemyData = new List<EnemyParam>();

    [System.Serializable]
    public class EnemyParam {
    public string enemyName;
    public int maxHp;
    public int hp;
    public int atk;
    public int def;
    public int exp;
    public int gold;
    }
    }


    こんな感じでScriptableObjectのクラスを用意。

    オブジェクト自体はこんな感じにしてみました。


    これでScriptableObjectは完成。

    実際にこのデータを使ってみます。

    画面はこんな感じ



    [Blog20210510.cs]

    using UnityEngine;
    using UnityEngine.UI;

    public class Blog20210510 : MonoBehaviour
    {
    [SerializeField] EnemyData enemyData;
    [SerializeField] Button btnAttack;
    [SerializeField] Text lblMessage;
    [SerializeField] Slider sldEnemyHP;
    [SerializeField] Text lblEnemyHP;

    EnemyData.EnemyParam enemy;
    int attackPower = 100;

    private void Start() {
    // とりあえず敵はスライム
    enemy = enemyData.enemyData[0];
    sldEnemyHP.maxValue = enemy.maxHp;
    sldEnemyHP.value = enemy.hp;
    lblEnemyHP.text = $"{enemy.hp} / {enemy.maxHp}";
    btnAttack.onClick.AddListener(AttackToEnemy);
    }

    void AttackToEnemy() {
    int damage = attackPower - enemy.def;
    // ダメージのブレ
    damage = Mathf.RoundToInt(Random.Range(damage * 0.8f, damage * 1.2f));
    lblMessage.text += enemy.enemyName + "に " + damage + "のダメージを与えた\r\n";
    enemy.hp -= damage;
    if (enemy.hp < 0) {
    enemy.hp = 0;
    }
    lblEnemyHP.text = $"{enemy.hp} / {enemy.maxHp}";
    }

    }



    ボタンを押すとスライムにダメージが入る感じ。

    こんな感じにうごくよ。



    さて、これでスライムを攻撃するのはできたんですが、ここで問題がおきてます。

    元のデータ(ScriptableObject)を見ると、HPが0になってます。



    ベースとなるデータの値は変えたくないですよねー

    Startの中で「enemy = enemyData.enemyData[0];」これによってデータはコピーしてきたと思ってましたが、ただのシャローコピーのようです。
    シャローコピーとディープコピーについてはこことか見るといいかも?
    https://programming.pc-note.net/csharp/copy.html


    で、ディープコピーをする方法。
    いくつかやり方はあると思いますが、一番簡単な方法。

    EnemyDataにコピーのコードを追加します。
    [EnemyData.cs]

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    [CreateAssetMenu(menuName = "MyScriptable/Create EnemyData")]
    public class EnemyData : ScriptableObject {

    public List enemyData = new List();

    [System.Serializable]
    public class EnemyParam {
    public string enemyName;
    public int maxHp;
    public int hp;
    public int atk;
    public int def;
    public int exp;
    public int gold;

    public void Deepcopy(EnemyParam source) {
    this.enemyName = source.enemyName;
    this.maxHp = source.maxHp;
    this.hp = source.hp;
    this.atk = source.atk;
    this.def = source.def;
    this.exp = source.exp;
    this.gold = source.gold;
    }

    }
    }


    コピーのメソッドを使用するように変更。
    [Blog20210510.cs]

    using UnityEngine;
    using UnityEngine.UI;

    public class Blog20210510 : MonoBehaviour
    {
    [SerializeField] EnemyData enemyData;
    [SerializeField] Button btnAttack;
    [SerializeField] Text lblMessage;
    [SerializeField] Slider sldEnemyHP;
    [SerializeField] Text lblEnemyHP;

    EnemyData.EnemyParam enemy;
    int attackPower = 100;

    private void Start() {
    // とりあえず敵はスライム
    enemy = new EnemyData.EnemyParam();
    enemy.Deepcopy(enemyData.enemyData[0]);

    sldEnemyHP.maxValue = enemy.maxHp;
    sldEnemyHP.value = enemy.hp;
    lblEnemyHP.text = $"{enemy.hp} / {enemy.maxHp}";
    btnAttack.onClick.AddListener(AttackToEnemy);
    }





    これで、ディープコピー完了です。
    まぁDeepcopyというメソッドを用意しなくても、コンストラクタでやってもいいんですけどね。

    こうすることで、いくらスライムのHPを減らしてもScriptableObjectのベースのデータには反映されません。

    ということで、ディープコピーとシャローコピーの話でした。

    以上。

    あでゅ~ノシ

    Category: 開発日記(Unity)

    tb 0 : cm 0   

    コメント

    コメントの投稿

    Secret

    トラックバック

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