Unity 2D 跟随玩家身后的敌人,避开障碍物

问题描述 投票:0回答:1

我在使用此脚本时遇到问题。你能帮助一个新手吗? 我需要敌人追赶玩家并避开障碍物。上面的游戏

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

public class enemy : MonoBehaviour
{
    public float speed = 5f; 
    public float maxSpeed = 10f; 
    public float avoidanceForceMultiplier = 5f; 
    public float raySpacing = 0.5f; 
    public LayerMask obstacleLayerMask;

    private Rigidbody2D rb;
    private Transform Player;

    private void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        Player = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>();
    }

    private void Update()
    {
        // Determine direction of movement
        Vector2 playerDirection = (Player.position - transform.position).normalized;

        // Cast rays to detect obstacles
        RaycastHit2D[] hits = new RaycastHit2D[3];
        Vector2 rayStart = transform.position * playerDirection * rb.velocity.magnitude * Time.deltaTime;

        for (int i = 0; i < 3; i++)
        {
            Vector2 rayDirection = Quaternion.AngleAxis((i - 1) * 30f, Vector3.forward) * playerDirection;
            hits[i] = Physics2D.Raycast(rayStart, rayDirection, raySpacing, obstacleLayerMask);
            Debug.DrawRay(rayStart, rayDirection * raySpacing, Color.red);
        }

        // Calculate avoidance force
        Vector2 avoidanceForce = Vector2.zero;

        foreach (RaycastHit2D hit in hits)
        {
            if (hit.collider != null)
            {
                float distanceToObstacle = Vector2.Distance(transform.position, hit.collider.transform.position);
                float distanceToRay = Vector2.Distance(rayStart, hit.point);
                avoidanceForce += Vector2.Lerp(playerDirection, hit.normal, distanceToRay / distanceToObstacle) * avoidanceForceMultiplier;
            }
        }

        // Apply avoidance force to velocity
        rb.AddForce(avoidanceForce);

        // Normalize velocity to max speed
        if (rb.velocity.magnitude > maxSpeed)
        {
            rb.velocity = rb.velocity.normalized * maxSpeed;
        }

        // Move towards player
        rb.velocity += playerDirection * speed * Time.deltaTime;
    }
}
c# unity-game-engine 2d
1个回答
0
投票

我注意到,你抱怨敌人在舞台上绕圈,这可能只是因为你错误地将敌人命名为“玩家”。这意味着它认为它到达了目的地。我看不出你如何检测和避开障碍物有多少逻辑。 derHugo 关于速度变化的说法是正确的,你应该只改变它一次,例如有一些 if 和 else,并且根据某些条件,每个

Update
仅将其设置为不同的值一次。还有一些变量名称不正确:
raySpacing
实际上不是间距,而是光线长度。在这种情况下,使用
Time.deltaTime
是没有意义的,您正在更新速度,而不是对象的确切位置。

我做了一些更改并添加了一个简单的寻路算法。制作阻挡路径的地形时,将所有连接的对象(敌人没有可以通过的孔)放在一个空的游戏对象下(该算法计算一组中对象的质心)您应该为地形创建新图层,并在编辑器中将其选择为

Obstacle Layer Mask
,我添加了一些可更改的值,例如光线投射的数量(我建议仅使用奇数)以进一步调整移动速度。敌人的速度永远是最大速度。

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

public class enemy : MonoBehaviour
{
public float speed = 5f;
public float maxSpeed = 10f;
public float avoidanceForceMultiplier = 5f;
public float raySpacing = 2f;
public LayerMask obstacleLayerMask;
public float allowed_distance = 1f;
public int amount_of_raycasts = 7;

private Rigidbody2D rb;
private Transform Player;
private Vector2 shortest_vector_to_target;
private Vector2 closest_point_to_target;

private void Start()
{
    rb = GetComponent<Rigidbody2D>();
    Player = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>();
}

private void Update()
{
    // Determine direction of movement
    Vector2 playerDirection = (Player.position - transform.position).normalized;

    bool ray_hit = false;

    // Cast rays to detect obstacles
    RaycastHit2D[] hits = new RaycastHit2D[amount_of_raycasts];
    Vector2 rayStart = transform.position;

    for (int i = 0; i < amount_of_raycasts; i++)
    {
        Vector2 rayDirection = Quaternion.AngleAxis((i - ((amount_of_raycasts - amount_of_raycasts % 2) / 2)) * 30f, Vector3.forward) * playerDirection;
        hits[i] = Physics2D.Raycast(rayStart, rayDirection, raySpacing, obstacleLayerMask);
        Debug.DrawRay(rayStart, rayDirection.normalized * raySpacing, Color.red);
    }

    // Calculate avoidance force
    Vector2 avoidanceForce = Vector2.zero;
    float lowest_distance = 0f;

    foreach (RaycastHit2D hit in hits)
    {
        if(hit.collider != null)
        {
            closest_point_to_target = hit.collider.ClosestPoint(transform.position);
            if (!ray_hit)
            {
                lowest_distance = Vector2.Distance(closest_point_to_target, new Vector2(transform.position.x, transform.position.y));
                shortest_vector_to_target = closest_point_to_target - new Vector2(transform.position.x, transform.position.y);
            }
            else if(lowest_distance > Vector2.Distance(closest_point_to_target, new Vector2(transform.position.x, transform.position.y)))
            {
                lowest_distance = Vector2.Distance(closest_point_to_target, new Vector2(transform.position.x, transform.position.y));
                shortest_vector_to_target = closest_point_to_target - new Vector2(transform.position.x, transform.position.y);
            }
            
            Vector2 direction_to_objects = GetCenterOfParentInWorldSpace(hit.collider.gameObject.transform.parent) - new Vector2(transform.position.x, transform.position.y);
            Debug.DrawRay(rayStart, direction_to_objects, Color.green);
            float right_or_left = direction_to_objects.x * playerDirection.y - direction_to_objects.y * playerDirection.x;
            Debug.Log(right_or_left);
            if (right_or_left > 0)
            {
                // left
                avoidanceForce = new Vector2(-shortest_vector_to_target.y, shortest_vector_to_target.x) * avoidanceForceMultiplier;
            }
            else if (right_or_left < 0)
            {
                // right
                avoidanceForce = new Vector2(shortest_vector_to_target.y, -shortest_vector_to_target.x) * avoidanceForceMultiplier;
            }
            else avoidanceForce = new Vector2(-shortest_vector_to_target.y, shortest_vector_to_target.x) * avoidanceForceMultiplier;

            Debug.DrawRay(rayStart, shortest_vector_to_target, Color.yellow);
            avoidanceForce = avoidanceForce.normalized * maxSpeed;
            ray_hit = true;
        }
    }

    float distance_from_object = Mathf.Sqrt(shortest_vector_to_target.x * shortest_vector_to_target.x + shortest_vector_to_target.y * shortest_vector_to_target.y);

    if (ray_hit)
    {
        if (distance_from_object < allowed_distance)
        {
            rb.velocity = (avoidanceForce - shortest_vector_to_target).normalized * maxSpeed;
        }
        else if (distance_from_object > (raySpacing - raySpacing/5))
        {
            rb.velocity = (shortest_vector_to_target).normalized * maxSpeed;
        }
        else
        {
            rb.velocity = (avoidanceForce + playerDirection).normalized * maxSpeed;
        }
    }
    else
    {
        rb.velocity = (playerDirection).normalized * maxSpeed;
    }

}

Vector2 GetCenterOfParentInWorldSpace(Transform parent)
{
    if (parent.childCount == 0)
    {
        return parent.position;
    }

    Vector3 totalPosition = Vector3.zero;
    foreach (Transform child in parent)
    {
        totalPosition += parent.TransformPoint(child.localPosition);
    }

    return totalPosition / parent.childCount;
}
}

如果您对算法、功能或其他任何问题还有其他疑问,请随时提问。

© www.soinside.com 2019 - 2024. All rights reserved.