mirror of
https://gitee.com/jisol/jisol-game/
synced 2025-09-27 02:36:14 +00:00
提交寻路
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Game.Plugins.JNGame.Sync.Frame.AstarPath.RVO;
|
||||
using Plugins.JNGame.Sync.Frame.AStar;
|
||||
using Script.battle;
|
||||
using UnityEngine;
|
||||
|
||||
public class GRVO02WorldMode : GBaseMode<Object>
|
||||
{
|
||||
|
||||
//初始化3D避障
|
||||
[HideInInspector]
|
||||
public JNRVOSimulator Simulator = new(MovementPlane.XZ);
|
||||
|
||||
//初始化寻路
|
||||
[HideInInspector]
|
||||
public JNAStarPath AStar = new();
|
||||
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a85dc713c3eb290448e178323fed525c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,227 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Game.Plugins.App.Sync;
|
||||
using Game.Plugins.JNGame.Sync.Frame.AStar.Util;
|
||||
using Game.Plugins.JNGame.Sync.Frame.AstarPath.AI;
|
||||
using Game.Plugins.JNGame.Sync.Frame.AstarPath.RVO;
|
||||
using Plugins.JNGame.Sync.Frame.AStar;
|
||||
using Plugins.JNGame.Sync.Frame.game.Time;
|
||||
using Plugins.JNGame.Util;
|
||||
using UnityEngine;
|
||||
|
||||
public class GRVO02WorldPlayerController : JNGSyncFrameDefault
|
||||
{
|
||||
//模式
|
||||
public GRVO02WorldMode mode;
|
||||
|
||||
//添加寻路AI
|
||||
private JNAISeeker ai;
|
||||
//添加RVO避障 控制器 (里面默认使用避障)
|
||||
private JNRVOController rvo;
|
||||
|
||||
//目标位置
|
||||
private Vector3 target;
|
||||
|
||||
//是否正在查询位置
|
||||
private bool canSearchAgain = true;
|
||||
|
||||
//当前路径
|
||||
private JNPath path = null;
|
||||
|
||||
// 向量路径
|
||||
List<Vector3> vectorPath;
|
||||
// waypoint进度
|
||||
private int wp = 0;
|
||||
|
||||
//移动距离
|
||||
public float moveNextDist = 1;
|
||||
|
||||
//下一次寻路 (主要目的是为了在寻路过程中引入一定的随机性和变化,特别是在动态环境或者存在移动障碍物的场景中。这样的设计可以帮助代理(例如游戏中的角色)更加智能和灵活地适应环境的变化 )
|
||||
//如果不重新计算路径,代理可能会遇到障碍物而停止移动,或者沿着一条不再是最佳(甚至不再可通行)的路径移动
|
||||
public float nextRepath = 1;
|
||||
|
||||
//平滑停止
|
||||
public float slowdownDistance = 1;
|
||||
|
||||
//最大速度
|
||||
public float maxSpeed = 10;
|
||||
|
||||
//碰撞层级
|
||||
public LayerMask groundMask;
|
||||
|
||||
public override void OnSyncLoad()
|
||||
{
|
||||
base.OnSyncLoad();
|
||||
//初始化寻路AI
|
||||
ai = new JNAISeeker(mode.AStar);
|
||||
//初始化避障控制器
|
||||
rvo = new JNRVOController(mode.Simulator,gameObject);
|
||||
}
|
||||
|
||||
//设置移动位置
|
||||
public void SetTarget (Vector3 target) {
|
||||
this.target = target;
|
||||
RecalculatePath();
|
||||
}
|
||||
|
||||
/// 重新计算到达目标点的路径
|
||||
public void RecalculatePath() {
|
||||
//不允许再次搜索路径
|
||||
canSearchAgain = false;
|
||||
//下一次寻路时间
|
||||
nextRepath = GetSync().Time.time + 2;
|
||||
// 启动路径搜索,传入当前位置、目标位置和路径搜索完成的回调函数
|
||||
ai.StartPath(transform.position, target, OnPathComplete);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 路径搜索完成时的回调函数
|
||||
/// <param name="_p">搜索得到的路径</param>
|
||||
public void OnPathComplete(JNPath _p) {
|
||||
// 将传入的路径尝试转换为ABPath类型
|
||||
JNABPath p = _p as JNABPath;
|
||||
|
||||
// 允许再次搜索路径
|
||||
canSearchAgain = true;
|
||||
|
||||
// 如果之前存在路径对象,则释放对它的引用
|
||||
if (path != null) path.Release(this);
|
||||
|
||||
// 更新当前路径对象为搜索得到的新路径
|
||||
path = p;
|
||||
|
||||
// 声明对该路径的所有权
|
||||
p.Claim(this);
|
||||
|
||||
// 如果路径搜索出错(比如没有找到可行路径)
|
||||
if (p.error) {
|
||||
//重置数据
|
||||
// 重置waypoint进度
|
||||
wp = 0;
|
||||
// 清空向量路径
|
||||
vectorPath = null;
|
||||
// 提前返回,不再继续执行后续代码
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取路径的起始点
|
||||
Vector3 p1 = p.originalStartPoint;
|
||||
// 获取代理当前的位置
|
||||
Vector3 p2 = transform.position;
|
||||
// 将起始点的y坐标设置为当前位置的y坐标,确保路径在同一水平面上
|
||||
p1.y = p2.y;
|
||||
// 计算当前位置与起始点之间的距离
|
||||
float d = (p2 - p1).magnitude;
|
||||
// 重置waypoint进度
|
||||
wp = 0;
|
||||
|
||||
// 更新向量路径为搜索得到的新路径的向量表示
|
||||
vectorPath = p.vectorPath;
|
||||
Vector3 waypoint; // 用于存储当前waypoint的位置
|
||||
|
||||
// 如果定义了每次移动的最大距离(moveNextDist > 0)
|
||||
if (moveNextDist > 0) {
|
||||
// 遍历从起始点到当前位置的距离,每次增加moveNextDist的60%
|
||||
for (float t = 0; t <= d; t += moveNextDist * 0.6f) {
|
||||
// 递减waypoint索引,因为我们是从路径的末尾开始向前遍历
|
||||
wp--;
|
||||
// 计算当前位置的向量表示
|
||||
Vector3 pos = p1 + (p2 - p1) * t;
|
||||
|
||||
// 循环遍历向量路径,直到找到一个waypoint,使得代理从当前位置移动到该waypoint的距离
|
||||
// 大于或等于moveNextDist,或者已经到达向量路径的最后一个waypoint
|
||||
do {
|
||||
// 递增waypoint索引
|
||||
wp++;
|
||||
// 获取向量路径中当前waypoint的位置
|
||||
waypoint = vectorPath[wp];
|
||||
// 循环条件:代理到当前waypoint的距离小于moveNextDist,并且还没有到达向量路径的最后一个waypoint
|
||||
} while (rvo.To2D(pos - waypoint).sqrMagnitude < moveNextDist * moveNextDist && wp != vectorPath.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void OnSyncUpdate(int dt, JNFrameInfo frame, Object input)
|
||||
{
|
||||
base.OnSyncUpdate(dt, frame, input);
|
||||
|
||||
// 检查是否需要重新计算路径
|
||||
if (GetSync().Time.time >= nextRepath && canSearchAgain)
|
||||
{
|
||||
RecalculatePath();
|
||||
}
|
||||
|
||||
Vector3 pos = transform.position;
|
||||
|
||||
// 如果存在路径且路径不为空
|
||||
if (vectorPath != null && vectorPath.Count != 0)
|
||||
{
|
||||
// 遍历路径直到找到下一个要到达的点
|
||||
while ((rvo.To2D(pos - vectorPath[wp]).sqrMagnitude < moveNextDist * moveNextDist && wp != vectorPath.Count - 1) || wp == 0)
|
||||
{
|
||||
wp++;
|
||||
}
|
||||
|
||||
// 计算路径段上的点
|
||||
var p1 = vectorPath[wp - 1];
|
||||
var p2 = vectorPath[wp];
|
||||
var t = VectorMath.LineCircleIntersectionFactor(rvo.To2D(transform.position), rvo.To2D(p1), rvo.To2D(p2), moveNextDist);
|
||||
t = Mathf.Clamp01(t);
|
||||
Vector3 waypoint = Vector3.Lerp(p1, p2, t);
|
||||
|
||||
// 计算到路径终点的剩余距离
|
||||
float remainingDistance = rvo.To2D(waypoint - pos).magnitude + rvo.To2D(waypoint - p2).magnitude;
|
||||
for (int i = wp; i < vectorPath.Count - 1; i++)
|
||||
{
|
||||
remainingDistance += rvo.To2D(vectorPath[i + 1] - vectorPath[i]).magnitude;
|
||||
}
|
||||
|
||||
// 设置目标点和期望速度
|
||||
var rvoTarget = (waypoint - pos).normalized * remainingDistance + pos;
|
||||
var desiredSpeed = Mathf.Clamp01(remainingDistance / slowdownDistance) * maxSpeed;
|
||||
|
||||
// // 绘制一条从当前位置到路径点的红色线段用于调试
|
||||
// Debug.DrawLine(transform.position, waypoint, Color.red);
|
||||
|
||||
// 设置RVO控制器的目标点和速度
|
||||
rvo.SetTarget(rvoTarget, desiredSpeed, maxSpeed);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果没有路径,则静止不动
|
||||
rvo.SetTarget(pos, maxSpeed, maxSpeed);
|
||||
}
|
||||
|
||||
// 从RVO控制器获取处理过的移动增量并移动角色
|
||||
var movementDelta = rvo.CalculateMovementDelta(Time.deltaTime);
|
||||
pos += movementDelta;
|
||||
|
||||
// 如果移动增量足够大,则旋转角色以面向移动方向
|
||||
if (Time.deltaTime > 0 && movementDelta.magnitude / Time.deltaTime > 0.01f)
|
||||
{
|
||||
var rot = transform.rotation;
|
||||
var targetRot = Quaternion.LookRotation(movementDelta, rvo.To3D(Vector2.zero, 1));
|
||||
const float RotationSpeed = 5;
|
||||
if (rvo.movementPlane == MovementPlane.XY)
|
||||
{
|
||||
targetRot = targetRot * Quaternion.Euler(-90, 180, 0);
|
||||
}
|
||||
transform.rotation = Quaternion.Slerp(rot, targetRot, Time.deltaTime * RotationSpeed);
|
||||
}
|
||||
|
||||
// 如果角色在XZ平面上移动,确保它保持在地面上
|
||||
if (rvo.movementPlane == MovementPlane.XZ)
|
||||
{
|
||||
RaycastHit hit;
|
||||
if (Physics.Raycast(pos + Vector3.up, Vector3.down, out hit, 2, groundMask))
|
||||
{
|
||||
pos.y = hit.point.y;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新角色的位置
|
||||
transform.position = pos;
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a83c78eaa47b3244c9974793e439d257
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user