解决方法简单来说:
-
不允许对角线路径
-
调小寻路 agent 的碰撞体
虽然包含很多优化的 AStar 教程珠玉在前:www.youtube.com/watch?v=-L-…
但是由于他是 3d 的,我一时半会不想改,于是用了一个别人的用于 Tilemap 的 AStar 代码:github.com/wjdeclan/un…
简单 AStar 下我写的一个一直在追逐敌人的 AI
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ChaserAI : EnemyAI
{
/// <summary>
/// 目标对象
/// </summary>
[ReadOnly] public GameObject target;
protected override void Start()
{
base.Start();
type = 0;
target = GameObject.Find("Player");
}
private void FixedUpdate()
{
Vector3 dir = GetTargetDir();
movableIns?.MoveBy(dir);
}
private Vector3 GetTargetDir()
{
if (target != null && movableIns != null & pathFinding != null)
{
Vector3 dir = Vector3.zero;
Vector3 nextPos = Vector3.zero;
// 寻找直线路径
List<AStarNode> sPath = pathFinding.FindStraightPath(transform.position, target.transform.position);
if (sPath != null)
{
// 如果格子数大于 1,那么朝着第一个格子走
if (sPath.Count > 1)
nextPos = pathFinding.WorldPointFromAStarNode(sPath[1]);
// 如果格子数小于等于 1,那么直接朝着目标走
else
nextPos = target.transform.position;
}
// 如果没有直线路径,那么沿着 AStar 路径走
else
{
List<AStarNode> path = pathFinding.FindPath(transform.position, target.transform.position);
if (path != null)
{
// 如果格子数大于 1,那么朝着第二个格子走
if (path.Count > 1)
nextPos = pathFinding.WorldPointFromAStarNode(path[1]);
// 如果格子数小于等于 1,那么直接朝着目标走
else
nextPos = target.transform.position;
}
// 如果找不到 AStar 路径,那么直接朝着目标走
else
nextPos = target.transform.position;
}
dir = (nextPos - transform.position).normalized;
return dir;
}
return Vector3.zero;
}
}
如果要加额外的效果,应用 Steering AI 的想法:
避开主角
private Vector3 AddAwayFromPlayerMove(Vector3 dir)
{
if (target != null)
{
Vector3 dis = target.transform.position - transform.position;
dis = player.transform.position - transform.position;
Vector3 playerDir = dis.normalized;
float force = Mathf.Clamp(5f - dis.magnitude, 0f, 5f) / 5;
return (dir - playerDir * force).normalized;
}
else
return dir;
}
围绕主角
private Vector3 AddIntimidatorMove(Vector3 dir)
{
if (target != null)
{
Vector3 dis = target.transform.position - transform.position;
Vector3 normalDir = Vector3.Cross(dir, new Vector3(0, 0, 1)).normalized;
int reversed = dis.magnitude - (intimidateDis + probeDis * Mathf.Sin(Time.time)) > 0 ? 1 : -1;
return (dir * reversed + normalDir).normalized;
}
else
return dir;
}
然后应用
private void FixedUpdate()
{
Vector3 dir = GetTargetDir();
dir = AddAwayFromPlayerMove(dir);
movableIns.MoveBy(dir);
}
但是简单 AI 始终会有 AI 沿着对角线路径走卡在墙角的问题
类似这样:gamedev.stackexchange.com/questions/7…
所以我再找到一个现成的,改好的 AStar:github.com/Ceeeeed/Pat…
然后再把它改成用于 Tilemap 的
但是改完之后发现让路径平滑的这部分,有点鸡肋,这就跟对角线路径一样蠢
所以由删掉平滑这部分
结果这一删,也就剩地图罚函数和多线程了hhh
那么其实 A* 里面跟我一开始 copy 的没啥区别……
而且 A* 还要改成不允许对角线
把 Grid.cs 里面的 GetNeighbours() 改一下就好了
public List<AStarNode> GetNeighbours(AStarNode node) {
neighbours.Clear();
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
if (x == 0 && y == 0)
continue;
// forbid diagonal to avoid stuck in corner
if (x * y != 0)
continue;
int checkX = node.gridX + x;
int checkY = node.gridY + y;
if (checkX >= 0 && checkX < gridSizeX && checkY >= 0 && checkY < gridSizeY) {
neighbours.Add(grid[checkX,checkY]);
}
}
}
return neighbours;
}
结果我发现,不允许对角线之后还是会有卡在墙上的状况
我一看场景,怪物的半径是 0.5f,然后竖直向上运动,怪物的碰撞球的边缘与墙的碰撞体稍微接触,这都卡住了
于是我瞬间想到调小碰撞体……然后效果就很好……
有点搞笑了,搞那么多代码最后没有调小碰撞体有用hhh