【ハルシオンブログ】あれ?ScriptableObjectの数値が変わってるんだけど?シャローコピーとディープコピーについて
おはようございます。坂内っす。
ゴールデンウィークも終わりましたね。
皆様どのように過ごしたでしょうか・・・・??え?なんでもないです。
Unityにおけるディープコピーについて。
ScriptableObjectでデータの管理をしている時に、そのデータをそのままゲーム中で使い更新すると、元のScriptableObjectのデータも更新されてしまい、困ることありませんか?
こんな感じ。
[EnemyData.cs]
こんな感じでScriptableObjectのクラスを用意。
オブジェクト自体はこんな感じにしてみました。

これでScriptableObjectは完成。
実際にこのデータを使ってみます。
画面はこんな感じ

[Blog20210510.cs]
ボタンを押すとスライムにダメージが入る感じ。
こんな感じにうごくよ。

さて、これでスライムを攻撃するのはできたんですが、ここで問題がおきてます。
元のデータ(ScriptableObject)を見ると、HPが0になってます。

ベースとなるデータの値は変えたくないですよねー
Startの中で「enemy = enemyData.enemyData[0];」これによってデータはコピーしてきたと思ってましたが、ただのシャローコピーのようです。
シャローコピーとディープコピーについてはこことか見るといいかも?
https://programming.pc-note.net/csharp/copy.html
で、ディープコピーをする方法。
いくつかやり方はあると思いますが、一番簡単な方法。
EnemyDataにコピーのコードを追加します。
[EnemyData.cs]
コピーのメソッドを使用するように変更。
[Blog20210510.cs]
これで、ディープコピー完了です。
まぁDeepcopyというメソッドを用意しなくても、コンストラクタでやってもいいんですけどね。
こうすることで、いくらスライムのHPを減らしても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)
« 【ハルシオンブログ】UnityのProject内の検索 | 【ハルシオンブログ】Debug.Logにcontextを指定してみる »
コメント
| h o m e |