利用A* Pathfinding项目在unity中实现自动寻路

A* Pathfinding 项目地址: https://arongranberg.com/astar/

学习视频:Unity 2D AI自动寻路功能 [风农译制]_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili

素材地址:2D Beginner: Tutorial Resources | 资源包 | Unity Asset Store

 

1.生成导航网格

首先制作一个瓦片地图,并且加上一个瓦片地图碰撞器

 

创建一个空物体,挂上pathfinder组件

 

创建一个网格图

 

勾选2d物理系统选项,将网格覆盖瓦片地图,之后选择瓦片地图所在的层。

 

在下方选择扫描,生成导航网格。

 

2.寻路脚本

 

给对象挂上seeker脚本,seeker脚本可以用来生成寻路路径。接着创建一个新脚本(EnemyContorller),用来操控对象自动寻路。

 

编写脚本EnemyContorller(这个脚本还不太完善,只是简单的示例)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Pathfinding;

public class EnemyContorller : MonoBehaviour
{
    public float enemyspeed;//敌人速度
    public Transform target;//设置跟随目标
    public float nextWaypointDistance = 3f;//敌人距离一个路标点多近的时候,需要向下一个点移动
    public Animator animator;

    Path path;//储存路径
    int currentWayPoint = 0;//代表在当前路径上,正在朝哪一个路标点移动
    bool reachedEndOfPath = false;//是否到达最后一个点
    Rigidbody2D rigidbody2d;
    Seeker seeker;

    void Start()
    {
        rigidbody2d = GetComponent<Rigidbody2D>();
        seeker = GetComponent<Seeker>();
        enemyCurrentHealth = enemyMaxHealth;

        InvokeRepeating("UpdatePath", 0, 0.5f);//第二个参数,多久第一次调用它,第三个参数,调用速率
    }

    private void UpdatePath()
    {
        if(seeker.IsDone())//如果没有在计算路径,进行下一次更新
           seeker.StartPath(rigidbody2d.position, target.position, OnPathComplete);
        //生成路径,第三个参数为生成路径完成后调用的函数

    }

    void FixedUpdate()
    {
        if (path == null)
            return;//路径为空,退出
        if (currentWayPoint >= path.vectorPath.Count)
        {
            reachedEndOfPath = true;
            return;
        }
        else
        {
            reachedEndOfPath = false;
        }//检查路径点是否走完

        Vector2 direction = ((Vector2)path.vectorPath[currentWayPoint] - rigidbody2d.position).normalized;
        //设置朝向

        animator.SetFloat("Look X", direction.x);
        animator.SetFloat("Look Y", direction.y);
       
        Vector2 force = direction * enemyspeed * Time.deltaTime;//设置力

        animator.SetFloat("Speed", force.magnitude);//播放行走动画

        rigidbody2d.AddForce(force);//给敌人施加力

        float distance = Vector2.Distance(rigidbody2d.position, path.vectorPath[currentWayPoint]);
        //计算到下一个点的距离

        if (distance < nextWaypointDistance)
        {
            currentWayPoint++;
        }//小于,移动到下一个坐标点
    }

    private void OnPathComplete(Path p)
    {
        if(!p.error)
        {
            path = p;
            currentWayPoint = 0;
        }//若路径无错误,使用这个路径
    }
}

 

3.完善代码

敌人改成了NPC。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Pathfinding;

public class NPCContorller : MonoBehaviour
{
    public float npcSpeed;//NPC速度

    public Transform target;//设置跟随目标
    public float nextWaypointDistance = 3f;//NPC距离一个路标点多近的时候,需要向下一个点移动
    public float followDistance = 3f;//跟随距离

    public Animator animator;

    public int npcMaxHealth;
    private int npcCurrentHealth;
    public int npcHealth { get { return npcCurrentHealth; } }

    Path path = null;//储存路径
    int currentWayPoint = 0;//代表在当前路径上,正在朝哪一个路标点移动
    bool reachedEndOfPath = false;//是否到达最后一个点

    Rigidbody2D rigidbody2d;
    Seeker seeker;

    void Start()
    {
        rigidbody2d = GetComponent<Rigidbody2D>();
        seeker = GetComponent<Seeker>();
        npcCurrentHealth = npcMaxHealth;

        InvokeRepeating("UpdatePath", 0, 0.5f);
    }

    //更新路径
    private void UpdatePath()
    {
        float targetDistance = Vector2.Distance(rigidbody2d.position, target.position);
        //到目标的距离
        if (targetDistance <= followDistance)
        //如果到目标的距离小于或等于跟随距离,停止更新路径
        {
            return;
        }
        else
        {
            if (seeker.IsDone())//如果没有在计算路径,进行下一次更新
                CancelInvoke("NPCStop");//更新路径时,停止调用这个函数,避免动画错误
            seeker.StartPath(rigidbody2d.position, target.position, OnPathComplete);
            //生成路径,第三个参数为生成路径完成后调用的函数    
        }
    }

    void FixedUpdate()
    {
        if (path == null)
            return;//路径为空,退出

        if (currentWayPoint >= path.vectorPath.Count)
        {
            reachedEndOfPath = true;
            Invoke("NPCStop", 0.3f);//0.3秒过后调用函数,使其到达跟随距离
            return;//路径点走完,退出函数
        }
        else
        {
            reachedEndOfPath = false;
        }//检查路径点是否走完

        Vector2 direction = ((Vector2)path.vectorPath[currentWayPoint] - rigidbody2d.position).normalized;
        //设置朝向
        animator.SetFloat("Look X", direction.x);
        animator.SetFloat("Look Y", direction.y);

        Vector2 seepd = direction * npcSpeed * Time.deltaTime;//设置力
        animator.SetFloat("Speed", seepd.magnitude);//播放走路动画
        rigidbody2d.velocity = seepd;//给一个速度

        float distance = Vector2.Distance(rigidbody2d.position, path.vectorPath[currentWayPoint]);
        //计算到下一个点的距离
        if (distance < nextWaypointDistance)
        {
            currentWayPoint++;
        }//小于,移动到下一个坐标点
    }

    //检查路径是否正确
    private void OnPathComplete(Path p)
    {
        if (!p.error)
        {
            path = p;
            currentWayPoint = 0;

            if (path.pathID != 1)
                currentWayPoint++;
            //避免移动时生成新路径后,往新路径的0号路径点移动

        }//若路径无错误,使用这个路径
    }

    //npc生命改变
    public void NPCChangeHealth(int amount)
    {
        npcCurrentHealth = Mathf.Clamp(npcCurrentHealth + amount, 0, npcMaxHealth);
        Debug.Log("NPC:" + npcCurrentHealth + '\\' + npcMaxHealth);
    }

    //靠近目标后停下  
    private void NPCStop()
    {
        rigidbody2d.velocity = Vector2.zero;
        //设置速度为0
        Vector2 direction = ((Vector2)target.position - rigidbody2d.position).normalized;
        //朝向
        animator.SetFloat("Speed", 0);
        animator.SetFloat("Look X", direction.x);
        animator.SetFloat("Look Y", direction.y);
        //朝向动画
    }
}

效果:

posted @ 2020-11-25 17:34  Hao_ran  阅读(1600)  评论(0编辑  收藏  举报