在运行场景中可以进行对模型操作得回放
using System.Collections.Generic;
using UnityEngine;public class TerrainRecorder : MonoBehaviour
{public Terrain targetTerrain;public bool isRecording = false;public bool isPlayingBack = false;public float recordInterval = 0.1f; // 记录间隔(秒)public List<float[,]> terrainSnapshots = new List<float[,]>();private float timer = 0f;private int currentPlaybackIndex = 0;void Update(){if (isRecording){timer += Time.deltaTime;if (timer >= recordInterval){RecordTerrainState();timer = 0f;}}if (isPlayingBack){PlaybackTerrainState();}}// 记录当前地形状态void RecordTerrainState(){if (targetTerrain == null) return;TerrainData terrainData = targetTerrain.terrainData;int resolution = terrainData.heightmapResolution;float[,] heights = terrainData.GetHeights(0, 0, resolution, resolution);// 深度复制高度图数据float[,] snapshot = new float[resolution, resolution];System.Array.Copy(heights, snapshot, heights.Length);terrainSnapshots.Add(snapshot);Debug.Log($"Recorded terrain state at {Time.time}, total snapshots: {terrainSnapshots.Count}");}// 回放地形状态void PlaybackTerrainState(){if (terrainSnapshots.Count == 0 || targetTerrain == null) return;// 按记录间隔逐步回放timer += Time.deltaTime;if (timer >= recordInterval){if (currentPlaybackIndex < terrainSnapshots.Count){TerrainData terrainData = targetTerrain.terrainData;terrainData.SetHeights(0, 0, terrainSnapshots[currentPlaybackIndex]);currentPlaybackIndex++;}else{// 回放结束isPlayingBack = false;currentPlaybackIndex = 0;Debug.Log("Terrain playback completed");}timer = 0f;}}// 开始录制public void StartRecording(){terrainSnapshots.Clear();isRecording = true;isPlayingBack = false;Debug.Log("Started recording terrain changes");}// 停止录制public void StopRecording(){isRecording = false;Debug.Log($"Stopped recording. Total snapshots: {terrainSnapshots.Count}");}// 开始回放public void StartPlayback(){if (terrainSnapshots.Count == 0){Debug.LogWarning("No terrain recordings to playback");return;}isPlayingBack = true;isRecording = false;currentPlaybackIndex = 0;Debug.Log("Started terrain playback");}// 停止回放public void StopPlayback(){isPlayingBack = false;Debug.Log("Stopped terrain playback");}// 清空记录public void ClearRecordings(){terrainSnapshots.Clear();Debug.Log("Cleared all terrain recordings");}// 获取当前回放进度(0-1)public float GetCurrentProgress(){if (terrainSnapshots.Count == 0) return 0;return (float)currentPlaybackIndex / (terrainSnapshots.Count - 1);}// 获取总录制时长public float GetTotalDuration(){return recordInterval * (terrainSnapshots.Count - 1);}// 设置回放位置(0-1)public void SetPlaybackPosition(float progress){progress = Mathf.Clamp01(progress);int frameIndex = Mathf.RoundToInt(progress * (terrainSnapshots.Count - 1));currentPlaybackIndex = frameIndex;}// 暂停回放public void PausePlayback(){isPlayingBack = false;}// 从指定位置恢复回放public void ResumePlaybackFromPosition(float progress){SetPlaybackPosition1(progress);isPlayingBack = true;}// 设置回放位置(0-1)public void SetPlaybackPosition1(float progress){progress = Mathf.Clamp01(progress);currentPlaybackIndex = Mathf.RoundToInt(progress * (terrainSnapshots.Count - 1));}public void JumpToFrame(int frameIndex){if (terrainSnapshots.Count == 0 || targetTerrain == null){Debug.LogWarning("No recordings available or terrain not assigned");return;}// 确保索引在有效范围内frameIndex = Mathf.Clamp(frameIndex, 0, terrainSnapshots.Count - 1);// 应用指定帧的地形状态targetTerrain.terrainData.SetHeights(0, 0, terrainSnapshots[frameIndex]);// 更新当前播放索引(如果正在回放)if (isPlayingBack){currentPlaybackIndex = frameIndex;}Debug.Log($"Jumped to frame {frameIndex}");}
}
记录回放等功能 还有UI脚本功能
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;[RequireComponent(typeof(Slider))]
public class TerrainRecorderUI : MonoBehaviour, IDragHandler, IEndDragHandler
{public TerrainRecorder terrainRecorder;public Button recordButton;public Button stopButton;public Button playbackButton;public Slider playbackSlider;public Text timeText;private bool wasPlayingBeforeDrag = false;void Start(){// 初始化按钮事件recordButton.onClick.AddListener(() => {terrainRecorder.StartRecording();playbackSlider.value = 0;});stopButton.onClick.AddListener(() => {terrainRecorder.StopRecording();});playbackButton.onClick.AddListener(() => {terrainRecorder.StartPlayback();});// 滑块值变化事件(用于实时跳转)playbackSlider.onValueChanged.AddListener(OnSliderValueChanged);}void Update(){// 自动更新进度条(当没有拖动时)if (terrainRecorder.isPlayingBack && !IsDragging()){float progress = terrainRecorder.GetCurrentProgress();playbackSlider.SetValueWithoutNotify(progress); // 不触发onValueChangedUpdateTimeDisplay(progress);}}// 滑块值变化时的处理void OnSliderValueChanged(float value){if (terrainRecorder == null || terrainRecorder.terrainSnapshots.Count == 0)return;UpdateTimeDisplay(value);// 只有在拖动时才实时跳转if (IsDragging()){int frameIndex = Mathf.RoundToInt(value * (terrainRecorder.terrainSnapshots.Count - 1));terrainRecorder.JumpToFrame(frameIndex);}}// 开始拖动时处理public void OnDrag(PointerEventData eventData){if (terrainRecorder.isPlayingBack){wasPlayingBeforeDrag = true;terrainRecorder.PausePlayback();}}// 结束拖动时处理public void OnEndDrag(PointerEventData eventData){if (wasPlayingBeforeDrag){terrainRecorder.ResumePlaybackFromPosition(playbackSlider.value);wasPlayingBeforeDrag = false;}}// 检查是否正在拖动private bool IsDragging(){return Input.GetMouseButton(0); // 检查鼠标左键是否按住}// 更新时间显示void UpdateTimeDisplay(float progress){if (timeText != null){float totalTime = terrainRecorder.GetTotalDuration();float currentTime = progress * totalTime;timeText.text = $"{currentTime:F1}s / {totalTime:F1}s";}}
}