Unity运用NavMeshAgent和LineRenderer实现物体运动轨迹回放功能

        最近公司有个项目需要做个Demo,功能是在3D场景中实时更新物体的GPS位置让物体在3d场景中移动,然后可以回访轨迹。GPS坐标转换为Unity坐标网上有比较多的博客,这里就不说了,主要分享一下物体运动和轨迹绘制以及回放的实现方法。

        效果看视频:

Unity轨迹回放功能录屏

        逻辑是,通过NavMeshAgent组件和Unity自带的路网烘培让小球动起来,然后鼠标按下的时候记录一下小球所在的位置,用于之后的轨迹的绘制,然后通过LineRenderer在场景中画出轨迹,UI的Slider来控制物体是在哪个点,代码如下:

PlayerMove挂载在NavMeshAgent的物体上,鼠标点击路网让他移动,按下空格键回放轨迹

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.UI;
/// <summary>
/// 物体移动
/// </summary>
public class PlayerMove : MonoBehaviour
{
    /// <summary>
    /// ID
    /// </summary>
    public string ID
    {
        get;
        set;
    }
    /// <summary>
    /// 回放时得到下个点的速度
    /// </summary>
    public float getPointSpeed;
    /// <summary>
    /// 回放时移动速度
    /// </summary>
    public float replayMoveSpeed;
    /// <summary>
    /// 存放所有行走的路径点
    /// </summary>
    private List<Vector3> roadPoints = new List<Vector3>();
    /// <summary>
    /// 寻路组件
    /// </summary>
    private NavMeshAgent meshAgent;
    /// <summary>
    /// 轨迹回放控制器
    /// </summary>
    ReplayController replayController;
    // Start is called before the first frame update
    void Start()
    {
        meshAgent = GetComponent<NavMeshAgent>();
        replayController = GameObject.Find("ReplayController").GetComponent<ReplayController>();
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0)&&!replayController.isPlaying)//按下鼠标左键
        {

            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit[] raycastHits = Physics.RaycastAll(ray);
            foreach (RaycastHit hit in raycastHits)
            {

                if (hit.transform.CompareTag("Road"))
                {
                    roadPoints.Add(transform.position);//添加到路径点列表,以便回放
                    meshAgent.SetDestination(hit.point);
                    break;
                }
            }
        }
        if (Input.GetKeyDown(KeyCode.Space))
        {
            roadPoints.Add(transform.position);//添加到路径点列表,以便回放
            replayController.OnInit(roadPoints, meshAgent);
            replayController.ReplayRuningData();
        }
    }

 
}

MySlider类,继承于Slider类,主要是记录一下鼠标是否在拖动Slider

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class MySlider : Slider, IBeginDragHandler, IEndDragHandler
{
    /// <summary>
    /// 是否在拖动滑块
    /// </summary>
    public bool IsDraging
    {
        get;
        private set;
    }
    public void OnBeginDrag(PointerEventData eventData)
    {
        IsDraging = true;
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        IsDraging = false;
    }
    public override void OnPointerDown(PointerEventData eventData)
    {
        base.OnPointerDown(eventData);
       IsDraging = true;
    }

    public override void OnPointerUp(PointerEventData eventData)
    {
        base.OnPointerUp(eventData);
        IsDraging = false;
    }
   

}

ReplayController类,主要包括存放物体移动的点的列表,和两个协程(一个让物体回放轨迹,一个更新Slider)。之后项目中,将鼠标点击时存入roadPoints 的点换成GPS转换为Unity坐标的点即可。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.UI;
/// <summary>
/// 轨迹回放控制器
/// </summary>
public class ReplayController : MonoBehaviour
{
    /// <summary>
    /// 存放所有行走的路径点
    /// </summary>
    private List<Vector3> roadPoints = new List<Vector3>();
    /// <summary>
    /// 寻路组件
    /// </summary>
    private NavMeshAgent meshAgent;
    /// <summary>
    /// 绘制轨迹组件
    /// </summary>
    private LineRenderer lineRenderer;
    /// <summary>
    /// 进度条
    /// </summary>
    private MySlider progressSlider;
    /// <summary>
    /// UI面板
    /// </summary>
    private Canvas canvas;
    /// <summary>
    /// 当前进度
    /// </summary>
    private float process;
    /// <summary>
    /// 当前速度
    /// </summary>
    float speed;
    int index;
    /// <summary>
    /// 当前会放到第几个点
    /// </summary>
    int Index
    {
        get
        {
            return index;
        }
        set
        {
           // if (value>0)
            {
                List<Vector3> posList = new List<Vector3>();
                posList = roadPoints.GetRange(0,Mathf.Clamp(value + 1,0, roadPoints.Count-1));
                lineRenderer.positionCount = posList.Count;
                lineRenderer.SetPositions(posList.ToArray()); 
            }
            index = value;

        }
    }
    /// <summary>
    /// 是否在回放
    /// </summary>
    public bool isPlaying;
    private void Start()
    {
        progressSlider = transform.Find("Canvas/ProgressSlider").GetComponent<MySlider>();
        canvas = transform.Find("Canvas").GetComponent<Canvas>();
        canvas.gameObject.SetActive(false);
        lineRenderer = GetComponent<LineRenderer>();
    }
    /// <summary>
    /// s初始化
    /// </summary>
    public void OnInit(List<Vector3> roadPoints, NavMeshAgent meshAgent)
    {
        this.roadPoints = roadPoints;
        this.meshAgent = meshAgent;
        canvas.gameObject.SetActive(true);
        Index = 0;
    }

    /// <summary>
    /// 回放
    /// </summary>
    public void ReplayRuningData()
    {
        isPlaying = true;
        StartCoroutine("IReplayRuningData");
        StartCoroutine("IOnDragingProcessSlider");
    }
    /// <summary>
    /// 回放协程
    /// </summary>
    /// <returns></returns>
    IEnumerator IReplayRuningData()
    {
        meshAgent.enabled = false;
        meshAgent.transform.position = roadPoints[Index];
        meshAgent.enabled = true;
        //lineRenderer.positionCount = roadPoints.Count;
        //lineRenderer.SetPositions(roadPoints.ToArray());
        while (true)
        {
            yield return new WaitUntil(() => progressSlider.IsDraging == false);//没有拖动滑块的时候才更新
            yield return 0;
            if (!meshAgent.hasPath)
            {
                if (Index >= roadPoints.Count)
                {
                    lineRenderer.positionCount = 0;
                    progressSlider.value = 0;
                    canvas.gameObject.SetActive(false);
                    Index = 0;
                    isPlaying = false;
                    break;
                }
                meshAgent.SetDestination(roadPoints[Index]);
                float distance = Vector3.Distance(meshAgent.transform.position, roadPoints[Mathf.Clamp(Index + 1, 0, roadPoints.Count - 1)]);
                float time = distance / meshAgent.speed;
                process = (float)Index / (roadPoints.Count - 1);
                if (time != 0)
                {
                    speed = process / time;
                }
                else
                {
                    speed = 0;
                }
                yield return IUpDateProgressSlider();
                Index++;
            }
        }
    }
    /// <summary>
    /// 更新进度条
    /// </summary>
    /// <param name="process"></param>
    /// <returns></returns>
    IEnumerator IUpDateProgressSlider()
    {
        while (progressSlider.value < process&& !progressSlider.IsDraging)
        {
            yield return new WaitForEndOfFrame();
            progressSlider.value += speed * Time.deltaTime;
        }
    }

    /// <summary>
    /// 鼠标拖动滑块
    /// </summary>
   IEnumerator IOnDragingProcessSlider()
    {
        while (true)
        {
            yield return new WaitUntil(() => progressSlider.IsDraging == true);//没有拖动滑块的时候才更新
            float value = progressSlider.value;
            Index = Mathf.Clamp(Mathf.RoundToInt(roadPoints.Count * value)-1, 0, roadPoints.Count - 1);
            meshAgent.enabled = false;
            meshAgent.transform.position = roadPoints[Index];
            meshAgent.enabled = true; 
        }
    }
}

Demo的Unity包下载地址链接:https://pan.baidu.com/s/1q_1wnp7aqm_nuAjnjJYygQ 
提取码:45ta 
Demo开发用的Unity2017.3.1。

有可以改进的地方欢迎大家提出来

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Unity游戏操作回放是一种记录和重放游戏操作的技术。通过使用Unity引擎提供的功能,可以将游戏中的玩家操作记录下来,并在以后的时间点上进行回放回放游戏操作具有许多应用,其中包括游戏开发和游戏教学。在游戏开发中,回放功能可以帮助开发者调试游戏,查找潜在的bug和问题。通过重新播放游戏操作,开发者可以重现特定的情境并找到问题的根源,从而更好地优化游戏体验。 此外,回放功能还可以用于游戏教学。教师可以记录自己的游戏操作并与学生分享,让学生在观看教师的游戏过程中学习游戏技巧和策略。这种教学方式能够更加生动地呈现游戏内容,并提供互动的学习体验。 实现Unity游戏操作回放的一种方法是使用Unity的序列化功能。开发者可以将玩家的操作转化为序列化的数据,并保存到文件中。在回放时,可以重新加载该文件,并按照序列化的数据重现游戏操作。通过在回放过程中控制游戏对象的状态和行为,可以实现对游戏操作的准确回放。 总的来说,Unity游戏操作回放是一种非常有用的技术,可以在游戏开发和游戏教学中发挥重要作用。通过记录和重现游戏操作,开发者和教师可以更好地理解和传递游戏内容,提升游戏的质量和教学的效果。 ### 回答2: Unity游戏操作回放是指记录并重播游戏中的操作过程,使玩家能够重新观看自己或其他玩家在游戏中的游戏操作。在Unity游戏引擎中,可以通过记录玩家的输入、游戏物体状态和事件等信息来实现游戏操作回放功能实现游戏操作回放的基本原理是将玩家的输入数据和游戏状态以一定的方式进行记录和存储。在游戏进行回放时,通过读取存储的数据,将游戏状态逐帧还原,并根据记录的输入数据重新执行操作,从而还原玩家的操作过程。 Unity提供了一系列的API和工具来实现游戏操作回放功能。具体而言,可以使用Unity的Input类和相关方法来记录玩家的输入数据,例如键盘按键、鼠标点击等。可以使用Unity的组件系统来记录游戏物体的状态信息,例如位置、旋转、缩放等。可以使用Unity的事件机制来记录游戏中的事件,例如碰撞、触发等。 在回放时,可以使用Unity的Time类和相关方法来控制游戏的时间流逝,从而实现逐帧还原游戏状态和操作。通过重新执行记录的输入数据和事件,可以还原玩家在游戏中的操作过程。同时,可以利用Unity的渲染和动画系统将回放的操作过程以视觉的形式展现给玩家。 总之,Unity游戏操作回放是一种记录和还原玩家在游戏中操作的技术,通过记录输入数据、游戏状态和事件等信息,实现对游戏操作过程的回放。它可以为玩家提供学习、观赏和分享的机会,同时也是游戏开发中重要的功能之一。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值