You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity 2D敌人AI无法跳上上方平台追击玩家问题求助

Unity 2D敌人AI追击问题:无法跳上玩家所在的前方上方平台

项目设置细节

场景

  • 使用Tilemap
  • 挂载Tilemap Collider 2D
  • 挂载Static Rigidbody 2D
  • 挂载Composite Collider 2D
  • 挂载Platform Effector 2D
  • LayerMask设置为Ground

敌人

  • 标签为Trap
  • 挂载Box Collider 2D
  • 挂载冻结Z轴旋转的Rigidbody 2D

敌人控制脚本

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

public class EnemyController : MonoBehaviour
{
    public float walkSpeed = 4f;
    public float chaseSpeed = 6f;
    public float detectionDistance = 10f;
    public float maxWalkDistance = 10f; 
    public float jumpForce = 10f;
    public LayerMask groundLayer;

    private Rigidbody2D rb;
    private SpriteRenderer spriteRenderer;
    [SerializeField] private Transform player;
    private bool isFacingRight = true;
    private Vector2 initialPosition;
    private bool shouldFlip = false;

    private void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        spriteRenderer = GetComponent<SpriteRenderer>();
        initialPosition = transform.position;
    }

    private void Update()
    {
        float distanceToPlayer = Vector2.Distance(transform.position, player.position);

        if (distanceToPlayer <= detectionDistance)
        {
            // Chase the player
            ChasePlayer();
        }
        else
        {
            // Walk left and right within a set distance
            WalkLeftAndRight();
        }
    }

    private void WalkLeftAndRight()
    {
        // Calculate the movement direction
        Vector2 movement = isFacingRight ? Vector2.right : Vector2.left;

        // Check if the enemy has reached the maximum walking distance
        float currentDistance = Mathf.Abs(transform.position.x - initialPosition.x);

        if (currentDistance >= maxWalkDistance)
        {
            shouldFlip = !shouldFlip; // Toggle the flip flag
            initialPosition = transform.position; // Reset initial position
        }

        // Check for ground detection in front of the enemy
        Vector2 groundCheckPosition = transform.position + (isFacingRight ? Vector3.right : Vector3.left) * 0.2f;
        bool isGroundAhead = Physics2D.Raycast(groundCheckPosition, Vector2.down, 0.2f, groundLayer);

        // Move the enemy
        if (isGroundAhead)
        {
            rb.velocity = new Vector2(movement.x * walkSpeed, rb.velocity.y);
        }
        else
        {
            shouldFlip = !shouldFlip; // Reverse direction if no ground ahead
        }

        // Flip the sprite if needed
        if (shouldFlip)
        {
            FlipSprite();
        }

        // Check for obstacles above the enemy
        Vector2 obstacleCheckPosition = transform.position + Vector3.up * 0.5f; // Adjust the height as needed
        bool hasObstacleAbove = Physics2D.Raycast(obstacleCheckPosition, Vector2.up, 1f, groundLayer);

        // Check for obstacles and jump if necessary
        Vector2 obstacleAheadCheckPosition = transform.position + (isFacingRight ? Vector3.right : Vector3.left) * 0.2f;
        bool hasObstacle = Physics2D.Raycast(obstacleAheadCheckPosition, movement, 0.2f, groundLayer);

        if (hasObstacle && IsGrounded() && !hasObstacleAbove)
        {
            // Jump to overcome the obstacle
            rb.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
        }
    }

    private void ChasePlayer()
    {
        // Calculate the direction to the player
        Vector2 directionToPlayer = (player.position - transform.position).normalized;

        // Move the enemy towards the player
        rb.velocity = new Vector2(directionToPlayer.x * chaseSpeed, rb.velocity.y);

        // Flip the sprite if needed
        if ((directionToPlayer.x > 0 && !isFacingRight) || (directionToPlayer.x < 0 && isFacingRight))
        {
            FlipSprite();
        }

        // Jump if the player is on a higher platform
        if (IsPlayerAbove() && IsGrounded())
        {
            rb.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
        }
    }

    private void FlipSprite()
    {
        isFacingRight = !isFacingRight;
        spriteRenderer.flipX = !isFacingRight;
    }

    private bool IsPlayerAbove()
    {
        // Check if the player is directly above the enemy
        float xOffset = 0.2f; 
        RaycastHit2D hit = Physics2D.Raycast(transform.position + Vector3.up * xOffset, Vector2.up, 1f, groundLayer);
        return hit.collider != null;
    }

    private bool IsGrounded()
    {
        // Check if the enemy is grounded
        float yOffset = 0.1f; 
        RaycastHit2D hit = Physics2D.Raycast(transform.position - Vector3.up * yOffset, Vector2.down, 1f, groundLayer);
        return hit.collider != null;
    }
}

当前问题

当玩家跳至敌人前方的上方平台时,敌人无法跳跃上去追击玩家。

问题分析与解决方案

核心问题

  1. IsPlayerAbove()逻辑偏差:当前方法仅检测敌人正上方的地面,无法识别玩家在前方平台的情况。
  2. 追击阶段缺少针对性检测:ChasePlayer方法未判断前方是否存在可跳跃的平台,仅依赖错误的触发条件。

修改方案

1. 新增前方平台检测方法

替换原IsPlayerAbove(),改为检测玩家是否在敌人前方的上方区域:

private bool IsPlayerOnFrontPlatform()
{
    // 判断玩家是否在敌人朝向的前方
    bool playerIsInFront = (player.position.x > transform.position.x && isFacingRight) || 
                           (player.position.x < transform.position.x && !isFacingRight);
    if (!playerIsInFront) return false;

    // 检测前方是否有向上的平台,覆盖玩家所在高度范围
    float horizontalOffset = 0.5f;
    float verticalRange = 2f;
    Vector2 checkStart = transform.position + (isFacingRight ? Vector3.right : Vector3.left) * horizontalOffset;
    RaycastHit2D hit = Physics2D.Raycast(checkStart, Vector2.up, verticalRange, groundLayer);

    // 确认玩家处于该平台的高度区间内
    bool playerInVerticalRange = player.position.y > transform.position.y && 
                                player.position.y <= transform.position.y + verticalRange;

    return hit.collider != null && playerInVerticalRange;
}

2. 更新追击阶段的跳跃逻辑

修改ChasePlayer方法,添加两种触发跳跃的场景:

private void ChasePlayer()
{
    Vector2 directionToPlayer = (player.position - transform.position).normalized;

    // 保留原有移动逻辑
    rb.velocity = new Vector2(directionToPlayer.x * chaseSpeed, rb.velocity.y);

    if ((directionToPlayer.x > 0 && !isFacingRight) || (directionToPlayer.x < 0 && isFacingRight))
    {
        FlipSprite();
    }

    bool shouldJump = false;
    // 场景1:玩家在前方的平台上
    if (IsPlayerOnFrontPlatform() && IsGrounded())
    {
        shouldJump = true;
    }
    // 场景2:前方无地面,但玩家在更高处,需要跳跃到平台
    Vector2 frontGroundCheck = transform.position + (isFacingRight ? Vector3.right : Vector3.left) * 0.3f;
    bool hasGroundInFront = Physics2D.Raycast(frontGroundCheck, Vector2.down, 0.3f, groundLayer);
    if (!hasGroundInFront && player.position.y > transform.position.y && IsGrounded())
    {
        shouldJump = true;
    }

    if (shouldJump)
    {
        rb.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
    }
}

3. 优化地面检测(可选)

使用多点检测提升地面判断的可靠性:

private bool IsGrounded()
{
    float checkRadius = 0.1f;
    // 敌人底部左右两个检测点
    Vector2 leftCheck = new Vector2(transform.position.x - 0.2f, transform.position.y - 0.3f);
    Vector2 rightCheck = new Vector2(transform.position.x + 0.2f, transform.position.y - 0.3f);
    
    return Physics2D.OverlapCircle(leftCheck, checkRadius, groundLayer) != null ||
           Physics2D.OverlapCircle(rightCheck, checkRadius, groundLayer) != null;
}

额外注意事项

  • 调整jumpForce数值:若跳跃高度不足,可适当增大至12f左右。
  • 验证LayerMask:确保groundLayer包含所有平台图层,无遗漏。
  • 检查Platform Effector 2D:确认平台的单向通行属性正确,允许敌人从下方跳跃通过。

内容的提问来源于stack exchange,提问作者Symbiote Scorns

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 18:35:17