前提・実現したいこと
Unityの2Dスクロールゲームで、キャラクターの操作を記録して、その後でそれ同じ動きをするキャラクターを追加したいです。
お互い干渉できるようにしたいので、「時間と場所」を記録させるのではなく、「時間と操作入力」を記録させたいです。
発生している問題・エラーメッセージ
ゲームを始めて動きを記録し、その動きを開始させようとするとエラーが出ます。
Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index
該当のソースコード
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using System.IO; public class Recorder : MonoBehaviour { // 操作キャラクター [SerializeField] private GhostChara ghostChara; // AnimatorController private Animator animator; // 現在記憶しているかどうか private bool isRecord; // 保存するデータの最大数 [SerializeField] private int maxDataNum = 2000; // 記録間隔 [SerializeField] private float recordDuration = 0.005f; // Jumpキー //private string animKey = "Jump"; // 経過時間 private float elapsedTime = 0f; // ゴーストデータ private GhostData ghostData; // 再生中かどうか private bool isPlayBack; // ゴースト用キャラ [SerializeField] private GameObject ghost; [SerializeField] // 保存先フォルダ private string saveDataFolder = "/Projects/Ghost"; // 保存ファイル名 private string saveFileName = "/ghostdata.dat"; void Start() { ghostChara.rbody = ghostChara.GetComponent<Rigidbody2D>(); } // ゴーストデータクラス [Serializable] private class GhostData { // カーソルキーの入力を取得 public List<float> HorizontalLists = new List<float>(); // Update is called once per frame void Update() { var moveHorizontal = Input.GetAxis("Horizontal"); if (isRecord) { elapsedTime += Time.deltaTime; if (elapsedTime >= recordDuration) { if (Input.GetAxis("Horizontal") == 1 || Input.GetAxis("Horizontal") == -1) { ghostData.HorizontalLists.Add(moveHorizontal); Debug.Log("左右記録"); } elapsedTime = 0f; } } } // キャラクターデータの保存 public void StartRecord() { // 保存する時はゴーストの再生を停止 StopAllCoroutines(); StopGhost(); isRecord = true; elapsedTime = 0f; ghostData = new GhostData(); Debug.Log("StartRecord"); } // キャラクターデータの保存の停止 public void StopRecord() { isRecord = false; Debug.Log("StopRecord"); } // ゴーストの再生ボタンを押した時の処理 public void StartGhost() { Debug.Log("StartGhost"); if (ghostData == null) { Debug.Log("ゴーストデータがありません"); } else { isRecord = false; isPlayBack = true; ghost.SetActive(true); StartCoroutine(PlayBack()); } } // ゴーストの停止 public void StopGhost() { Debug.Log("StopGhost"); StopAllCoroutines(); isPlayBack = false; ghost.SetActive(false); } // ゴーストの再生 IEnumerator PlayBack() { var i = 0; while (isPlayBack) { yield return new WaitForSeconds(recordDuration); if (ghostData.HorizontalLists[i] != null) { ghostChara.rbody.velocity = new Vector2(ghostChara.speed * ghostChara.axisH, ghostChara.rbody.velocity.y); Debug.Log("左右移動中"); } i++; } public void Save() { if (ghostData != null) { // GhostDataクラスをJSONデータに書き換え var data = JsonUtility.ToJson(ghostData); // ゲームフォルダにファイルを作成 File.WriteAllText(Application.dataPath + saveDataFolder + saveFileName, data); Debug.Log("ゴーストデータをセーブしました"); } } public void Load() { if (File.Exists(Application.dataPath + saveDataFolder + saveFileName)) { string readAllText = File.ReadAllText(Application.dataPath + saveDataFolder + saveFileName); // ghostDataに読み込んだデータを書き込む if (ghostData == null) { ghostData = new GhostData(); } JsonUtility.FromJsonOverwrite(readAllText, ghostData); Debug.Log("ゴーストデータをロードしました。"); } } void OnApplicationQuit() { Debug.Log("アプリケーション終了"); Save(); } }
試したこと
https://gametukurikata.com/program/ghost
こちらのホームページを参考にして、「時間と操作入力」を記録できるように変更しようとしました。
補足情報(FW/ツールのバージョンなど)
マ〇オみたいなシステムで、左右移動とジャンプのみのシンプルな操作です。
単純に入力されたキーと時刻を保存すればいいのではないのでしょうか?何か制限があるのでしたらほかの方法を考えないといけないと思いますが
コメントありがとうございます、
その入力されたキーと時刻の保存が上手くいかず、質問させていただきました。
コードを追加したので、もしよろしければ確認お願いします。
エラーの個所はどの行ですか?
個人的に自分ならどんな感じで書くだろうか?と考えてみて書いてみた。再生のみ、Json変換やらセーブ&ロードはなし。
FixedUpdateを1フレームとして考えDictionaryを使用した簡易ゴーストの作成。
Cubeを作ってコピペしたコードをアタッチしてみてください。参考になれば。
```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour
{
// フレーム数に対する入力を記録する用
Dictionary<int, InputData> ghostData = new Dictionary<int, InputData>();
// フレーム数
int frameCount = 0;
bool replay = false;
float horizontal = 0;
// 入力はUpdateにて
private void Update()
{
// ghost再生に対応した処理は書いていないので入力は受け付けない
if (replay) return;
horizontal = Input.GetAxis("Horizontal");
// Rキーで再生
if (Input.GetKeyDown(KeyCode.R)) StartCoroutine(Replay());
}
// ghost処理で誤差が発生するためFixedUpdateでの処理とします
private void FixedUpdate()
{
// Updateに同じく
if (replay) return;
frameCount++;
MoveAction(this.gameObject, horizontal);
if (horizontal != 0)
{
// データを追加する
ghostData.Add(frameCount, new InputData() { horizontal = this.horizontal });
}
}
// 移動処理
void MoveAction(GameObject obj, float horizontal)
{
var position = new Vector3(horizontal, 0, 0);
obj.transform.position += position * Time.fixedDeltaTime;
}
// コルーチンを仕様してゴーストを再生
IEnumerator Replay()
{
replay = true;
var obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
int count = 0;
foreach(var action in ghostData)
{
// キーのフレームまでループ
while(action.Key - 1 > count)
{
yield return new WaitForFixedUpdate();
count++;
}
// キーにある行動を行う
MoveAction(obj, action.Value.horizontal);
}
replay = false;
ghostData.Clear();
frameCount = 0;
}
}
// 入力データを記録する用
public struct InputData
{
public float horizontal;
}
```
なるほど、できました!
そういうやり方があるのですね、助かります。
ありがとうございました!
あなたの回答
tips
プレビュー