?目录
一、具体参考的文章如下:
二、我想要实现的功能:
三、实现方法:
1、ReplayController.cs
关键代码:
Index类
下面附上这个阶段ReplayController.cs的代码:
2、HappyBoyController.cs
需要添加的变量:
?需要添加一个函数addReplayPoint()函数:
其他部分的改动:
下面附上这个阶段HappyBoyController.cs的代码:
四、遇到并解决的BUG:(遮挡问题)
一、具体参考的文章如下:
Unity物体运动时画出轨迹 wUnity运用NavMeshAgent和LineRenderer实现物体运动轨迹回放功能Unity3D实现回放的功能(记录操作数)关于Unity的回放功能
二、我想要实现的功能:
小人按照关卡设计走完相应的路程之后,触发回放功能,回放小人走过的轨迹
实现效果:
开心!
三、实现方法:
?我们需要改的类有:HappyBoyController.cs,并添加一个新的ReplayController.cs类(创建空物体line并为其赋予lineRendershu属性)
1、ReplayController.cs
关键代码:
posList = roadPoints.GetRange(0, Mathf.Clamp(value + 1, 0, roadPoints.Count - 1));
?参考解释说明:
Redis Getrange 命令
Unity中限制轴向移动范围Mathf.Clamp:限制 value的值在min,max之间,如果value大于max,则返回max,如果value小于min,则返回min,否则返回value;
根据以上解释,可以明白,这一句代码的意思是:把指定范围的字符串赋值给posList数组,而赋值的范围是如果这个value+1(value+1是因为value是从0开始的)在0到roadPoints.Count - 1(-1是因为Count是数组里一共有几个数据,而数组编号是从0开始的,所以数组最后一个的编号会比数组中的元素总数少一)之间,就返回value+1
Index类
int Index
{
get
{
return index;
}
set
{
if (value < roadPoints.Count)
{
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());
happyBoy.transform.position = roadPoints[value];
}
index = value;
print(index);
}
}
?定义一个Index类,默认为internal(同一个程序集的任何代码都可以访问该类型或成员),当调用数据这个类的数据的时候,会调用get函数,并输出这个int类型的index,但是当要改变index的值的时候,会调用set函数,这个时候把划线并且把人物位置进行移动
类说明参见:
?C# 类访问权限
C#中get和set
c#中的value
下面附上这个阶段ReplayController.cs的代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ReplayController : MonoBehaviour
{
/// <summary>
/// 存放所有行走的路径点
/// </summary>
public List<Vector3> roadPoints;
/// <summary>
/// 绘制轨迹组件
/// </summary>
private LineRenderer lineRenderer;
/// <summary>
/// 存放要回放的人物
/// </summary>
public GameObject happyBoy;
int index;
/// <summary>
/// 当前会放到第几个点
/// </summary>
int Index
{
get
{
return index;
}
set
{
print("开始Set函数:");
if (value < roadPoints.Count)
{
List<Vector3> posList = new List<Vector3>();
//下面这一句是写轨迹追踪的关键:
posList = roadPoints.GetRange(0, Mathf.Clamp(value + 1, 0, roadPoints.Count - 1));
//posList = roadPoints;
lineRenderer.positionCount = posList.Count;
lineRenderer.SetPositions(posList.ToArray());
//happyBoy.transform.position = roadPoints[index];
happyBoy.transform.position = roadPoints[value];
}
index = value;
print(index);
}
}
/// <summary>
/// 是否在回放
/// </summary>
public bool isPlaying;
// Start is called before the first frame update
void Start()
{
lineRenderer = GetComponent<LineRenderer>();
}
/// <summary>
/// s初始化
/// </summary>
public void OnInit(List<Vector3> roadPoints)
{
this.roadPoints = roadPoints;
Index = 0;
}
/// <summary>
/// 回放
/// </summary>
public void ReplayRuningData()
{
isPlaying = true;
StartCoroutine("IReplayRuningData");
}
// Update is called once per frame
void Update()
{
}
/// <summary>
/// 回放协程
/// </summary>
/// <returns></returns>
IEnumerator IReplayRuningData()
{
///<summary>
/// 这个if语句是为了让回放了一遍之后再点空格不要再往里面添加其他的点了,
/// 因为之前如果没有这个if语句会出一个BUG,
/// 就是可能会把line的初位置在第二次调用的时候连进去,
/// 但是我暂时么有明白是为什么
/// </summary>
if (Index < roadPoints.Count)
{
//happyBoy.transform.position = roadPoints[Index];
//meshAgent.enabled = true;
lineRenderer.positionCount = roadPoints.Count;
print(happyBoy.transform.position);
print("进入回放阶段");
//lineRenderer.SetPositions(roadPoints.ToArray());
while (true)
{
yield return 0;
if (Index >= roadPoints.Count)
{
print("Index:");
print(index);
print("回放到最后一个点了:");
//这句话是为了让人物定格在最后一个点,不要返回去
happyBoy.transform.position = roadPoints[roadPoints.Count - 1];
///下面这两句,如果是想多次回放可以用,
///但是我目前的程序中只想调用一次这个
///所以下面两句暂时不要用,用了的话会在画完线之后线消失且人物回到原点
//lineRenderer.positionCount = 0;
//Index = 0;
isPlaying = false;
print("Index:");
print(index);
break;
}
print("调用Index里的Set函数:");
Index++;
}
}
print("该回放的点已经回放完毕,不要再点了");
}
}
2、HappyBoyController.cs
需要添加的变量:
/// </summary>
/// 回放时移动速度
public float replayMoveSpeed;
/// <summary>
/// 存放所有行走的路径点
/// </summary>
//public List<Vector3> roadPoints;
/// <summary>
/// 轨迹回放控制器
/// </summary>
public ReplayController replayController;
/// <summary>
/// 判断是不是正在记录需要回访的数据点,默认是true,
/// 当记录完毕,也就是需要调用回放函数的时候,将其改为false
/// </summary>
public bool recordingreplayData=true;
/// <summary>
/// 索引端点
/// </summary>
private int i = 0;
/// <summary>
/// 用来控制时间,判断记录NPC位置的时候多长时间记录一个
/// </summary>
private float time = 0;
/// <summary>
/// RunNext用来记录前一个时间点的位置,最开始是(0,0,0)
/// </summary>
public Vector3 RunStart;
/// <summary>
/// RunNext用来记录当前位置,最开始是NPC的起始位置
/// </summary>
public Vector3 RunNext;
/// <summary>
/// 存储运动物体位置,也就是当RunNext和RunNext不一样的时候,说明物体在运动,记录下当前位置RunNext
/// </summary>
public Vector3 position;
?需要添加一个函数addReplayPoint()函数:
/// <summary>
/// ____________________________________________________________________________________
/// 将运动点添加到点集里
/// </summary>
void addReplayPoint()
{
time += Time.deltaTime;
RunNext = this.transform.position;
if (recordingreplayData == true)
{
if (RunStart != RunNext && time > 0.2)
{
time = 0;
i++;
//lineRender.positionCount = i;//设置顶点数
//lineRender.SetPosition(i - 1, run.transform.position);
position = new Vector3(this.transform.position.x, this.transform.position.y, this.transform.position.z + 3.0f);
//lineRender.SetPosition(i - 1, position);
replayController.roadPoints.Add(position);
//roadPoints.Add(position);
}
RunStart = RunNext;
}
}
?当recordingreplayData =true,时间间隔0.2s,并且RunStart != RunNext(也就是NPC移动)的时候,将当前的位置记录在replayController类中的roadPoints数组里
注意:这里把赋给line的位置加了3(this.transform.position.z + 3.0f)
position = new Vector3(this.transform.position.x, this.transform.position.y, this.transform.position.z + 3.0f);
?因为如果不这样做的话,我们画的线会把人物挡住。
其他部分的改动:
?在之前的每个运动函数运动行为完成之后,添加一句调用addReplayPoint()的语句:
addReplayPoint();
?在Update里添加一个控制开始回放的代码:(目前先定为点击空格键的时候开始进行轨迹回放)
if (Input.GetKeyDown(KeyCode.Space))
{
//roadPoints.Add(transform.position);//添加到路径点列表,以便回放
//replayController.OnInit(roadPoints);
replayController.ReplayRuningData();
recordingreplayData = false;
}
?也就是:调用?replayController类中的回放函数,并且将recordingreplayData 置为false,也就是说这之后就不再将物体的移动记录进回放点集里了。
下面附上这个阶段HappyBoyController.cs的代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityStandardAssets.CrossPlatformInput;
using DG.Tweening;
public class HappyBoyController : MonoBehaviour
{
/// <summary>
/// 确定移动方向时使用
/// </summary>
public float walkSpeed;
private float moveInputWalk;
public float jumpMaxHight;
public float jumpA;
//这个speed实际上是跳跃的速度
public float jumpSpeed;
public float jumpForce;
/// <summary>
/// 检测角色是否站在地面上
/// </summary>
private bool isGrounded;
public Transform groundCheck;
public float checkRadius;
public LayerMask whatIsGround;
public float jumpTime;
//private float jumpTimeCounter;
//private bool isJumping;
//private bool doubleJump;
private Animator anim;
private Rigidbody2D rb;
/// <summary>
/// __________________________________________________________________________________________________________________________________________________________
/// 下面是控制重播时需要的变量
/// </summary>
/// 回放时移动速度
public float replayMoveSpeed;
/// <summary>
/// 存放所有行走的路径点
/// </summary>
//public List<Vector3> roadPoints;
/// <summary>
/// 轨迹回放控制器
/// </summary>
public ReplayController replayController;
public bool recordingreplayData=true;
/// <summary>
/// 索引端点
/// </summary>
private int i = 0;
private float time = 0;
public Vector3 RunStart;
public Vector3 RunNext;
/// <summary>
/// 存储运动物体位置
/// </summary>
public Vector3 position;
/// ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
/// </summary>
// Start is called before the first frame update
void Start()
{
anim = GetComponent<Animator>();
rb = GetComponent<Rigidbody2D>();
//replayController = GameObject.Find("ReplayController").GetComponent<ReplayController>();
}
/// <summary>
/// 判断人物移动方向从而确定人物是否转向
/// </summary>
void Flip()
{
bool playerHasXAxisSpeed = Mathf.Abs(rb.velocity.x) > Mathf.Epsilon;
if (playerHasXAxisSpeed)
{
if (rb.velocity.x>0.1f)
{
transform.localRotation = Quaternion.Euler(0, 0, 0);
}
if (rb.velocity.x < -0.1f)
{
transform.localRotation = Quaternion.Euler(0, 180, 0);
}
}
//________________________________________________________________________
addReplayPoint();
//________________________________________________________________________
}
void walk()
{
moveInputWalk = CrossPlatformInputManager.GetAxis("Horizontal");
//if (Mathf.Abs(moveInputWalk)<0.01f) return;
Vector3 movement = new Vector3(moveInputWalk*walkSpeed, rb.velocity.y, 0);
rb.velocity = movement;
if (movement.x != 0)
{
anim.SetBool("isWalking", true);
}
else
{
anim.SetBool("isWalking", false);
}
//________________________________________________________________________
addReplayPoint();
//________________________________________________________________________
}
void jump()
{
isGrounded = Physics2D.OverlapCircle(groundCheck.position, checkRadius, whatIsGround);
if (CrossPlatformInputManager.GetButton("Jump") && isGrounded)
{
//Vector2 jumpVel = new Vector2(0.0f,jumpSpeed);
//Vector2 jumpVel = new Vector2(0.0f, jumpA);
//添加加速度
//rb.velocity = Vector2.up * jumpVel;
rb.AddForce(Vector2.up * jumpForce,ForceMode2D.Impulse);
//rb.velocity = Vector2.up * jumpVel;
//transform.DOMoveY(transform.position.y + jumpMaxHight, jumpTime);
//________________________________________________________________________
addReplayPoint();
//________________________________________________________________________
anim.SetTrigger("isJumping");
}
}
/// <summary>
/// ____________________________________________________________________________________
/// 添加运动点到点集中
/// </summary>
void addReplayPoint()
{
time += Time.deltaTime;
RunNext = this.transform.position;
if (recordingreplayData == true)
{
if (RunStart != RunNext && time > 0.2)
{
time = 0;
i++;
//lineRender.positionCount = i;//设置顶点数
//lineRender.SetPosition(i - 1, run.transform.position);
position = new Vector3(this.transform.position.x, this.transform.position.y, this.transform.position.z + 3.0f);
//lineRender.SetPosition(i - 1, position);
replayController.roadPoints.Add(position);
//roadPoints.Add(position);
}
RunStart = RunNext;
}
}
private void FixedUpdate()
{
//jump();
}
// Update is called once per frame
void Update()
{
Flip();
walk();
jump();
if (Input.GetKeyDown(KeyCode.Space))
{
//roadPoints.Add(transform.position);//添加到路径点列表,以便回放
//replayController.OnInit(roadPoints);
replayController.ReplayRuningData();
recordingreplayData = false;
}
}
}
四、遇到并解决的BUG:(遮挡问题)
于是我改了line的层级
但是这样做了之后还是没有用,不知道是为什么,我点开三位模式看见这个line的三维位置是位于这个“书山”后面,而line的位置是根据NPC的位置来的于是窝就把NPC的Z轴位置改成了-5,这样就解决了遮挡的问题。
|