【问题标题】:Enemy AI Movement Decision Making敌人人工智能运动决策
【发布时间】:2017-09-25 22:34:04
【问题描述】:

我正在为 Unity 平台游戏开发敌人 AI 移动系统,该系统将允许敌人不断做出以下三个决定之一:空闲、向右移动或向左移动。我希望敌人能够选择这些决定中的任何一个,即使它刚刚选择的决定与其下一个决定相同(即它可以连续两次选择“向右移动”,或者多次选择想要)。下面的脚本没有错误,但是当我测试游戏时,它会导致我的敌人结结巴巴。有时它会在一瞬间向右移动,然后向左移动,等等。我觉得我的代码的内在逻辑有些正确,但它的实现方式需要一些工作。我很感激你能给我的任何帮助。

顺便说一句,如果我将“MakeMovementDecision”函数放在“Start”函数中,敌人会向左或向右移动 0.07,或者只是静止不动,不再执行其他移动函数。

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

public class AIMovement : MonoBehaviour {

// References the enemy's Rigidbody2D component
private Rigidbody2D enemyRigidbody;

// Sets the enemy's movement speed
[SerializeField]
private int movementSpeed;

// Checks if the enemy is moving (to be used with animations)
private bool isMoving;

// The direction in which the enemy will move
private Vector2 directionToMove;

// The random decision (0, 1 or 2) that represents which movement function the enemy will perform
private int decisionValue;

// The time remaining before the enemy chooses which movement function to perform again
private float timeTilNextDecision;

// The random float that will be used to determine for how long the enemy remains idle
private float idleTime;

// The random float that will be used to determine for how long the enemy moves left or right
private float moveTime;

// Use this for initialization
void Start () {

    // Accesses the enemy's Rigidbody2D component
    enemyRigidbody = GetComponent<Rigidbody2D>();
}

void FixedUpdate () {

    MakeMovementDecision();
}

/// <summary>
/// Generates the decision for which type of movement the enemy will perform
/// </summary>
private void MakeMovementDecision ()
{
    // Chooses a value upon which the movement decision will be based
    decisionValue = Random.Range(0, 3);

    switch (decisionValue)
    {
        // Keeps the enemy standing still
        case 0:
            Idle();
            break;

        // Moves the enemy to the right
        case 1:
            MoveRight();
            break;

        // Moves the enemy to the left
        case 2:
            MoveLeft();
            break;
    }
}

/// <summary>
/// Causes the enemy to stand still with idle animations 
/// </summary>
private void Idle ()
{
    // Sets the idle stance duration
    idleTime = Random.Range(5.0f, 10.0f);

    // Calculates the time until the enemy may decide to change its movement
    timeTilNextDecision = idleTime - Time.deltaTime;

    // Sets the movement bool to false to play the idle animations
    isMoving = false;

    // Stops the enemy's movement
    enemyRigidbody.velocity = Vector2.zero;

    // Checks if the enemy should make a decision on its next movement
    if (timeTilNextDecision < 0)
    {
        MakeMovementDecision();
    }

}

private void MoveRight()
{
    moveTime = Random.Range(2.0f, 5.01f);
    timeTilNextDecision = moveTime - Time.deltaTime;
    isMoving = true;
    directionToMove = Vector2.right;
    transform.Translate(directionToMove * (movementSpeed * Time.deltaTime));

    if (timeTilNextDecision < 0)
    {
        MakeMovementDecision();
    }

}

private void MoveLeft()
{
    moveTime = Random.Range(2.0f, 5.01f);
    timeTilNextDecision = moveTime - Time.deltaTime;
    isMoving = true;
    directionToMove = Vector2.left;
    transform.Translate(directionToMove * (movementSpeed * Time.deltaTime));

    if (timeTilNextDecision < 0)
    {
        MakeMovementDecision();
    }

}

}

【问题讨论】:

  • 为什么在 FixedUpdate() 中调用 MakeMovementDecision 以及决策本身?尝试删除 FixedUpdate() 并将函数 MakeMovementDecision() 放入 Start()...
  • 是的,我已经尝试过了(我编辑了我的原始帖子以包含关于此的注释)。它只是将敌人向左或向右移动 0.07 个单位,或选择“空闲”变体,但随后敌人不再移动。如果不查看检查器中的 x 值,则根本无法感知到敌人正在移动。

标签: c# unity3d artificial-intelligence game-engine


【解决方案1】:

我想通了。我是重写脚本的受害者。这要简单得多,并且完全符合我的要求。关键是timeTilNextMovement 变量是确定下一次移动何时发生的唯一变量。我一直在使用太多变量来在我的其他脚本中完成这个简单的任务。

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

public class EnemyMovement : MonoBehaviour {

private Rigidbody2D enemyRigidbody;

private int movementValue;

private float timeTilNextMovement;

private bool isMoving;

[SerializeField]
private float movementSpeed;

private Vector2 moveRight;
private Vector2 moveLeft;

// Use this for initialization
void Start () {

    enemyRigidbody = GetComponent<Rigidbody2D>();
    moveRight = Vector2.right;
    moveLeft = Vector2.left;

    MakeMovementDecision();
}

// Update is called once per frame
void FixedUpdate () {

    timeTilNextMovement -= Time.fixedDeltaTime;

    switch (movementValue)
    {
        case 0:
            Debug.Log("IDLE"); 
            isMoving = false;
            enemyRigidbody.velocity = Vector2.zero;
            break;
        case 1:
            Debug.Log("RIGHT");
            isMoving = true;
            transform.Translate(moveRight * (movementSpeed * Time.fixedDeltaTime));
            break;
        case 2:
            Debug.Log("LEFT");
            isMoving = true;
            transform.Translate(moveLeft * (movementSpeed * Time.fixedDeltaTime));
            break;
    }

    if (timeTilNextMovement < 0)
    {
        MakeMovementDecision();
    }

}

private void MakeMovementDecision()
{
    movementValue = Random.Range(0, 3);
    timeTilNextMovement = Random.Range(2.0f, 5.0f);
}

}

【讨论】:

    【解决方案2】:

    你需要计算FixedUpdate()里面的时间。您的代码应该看起来像这样;

    void FixedUpdate () {
        // Calculates the time until the enemy may decide to change its movement
        moveTime += Time.deltaTime;
        timeTilNextDecision = idleTime - moveTime;
    
        if (timeTilNextDecision < 0)
        {
            MakeMovementDecision();
        }
    }
    
    /// <summary>
    /// Generates the decision for which type of movement the enemy will perform
    /// </summary>
    private void MakeMovementDecision ()
    {
        // Chooses a value upon which the movement decision will be based
        decisionValue = Random.Range(0, 3);
    
        switch (decisionValue)
        {
            // Keeps the enemy standing still
            case 0:
                Idle();
                break;
    
            // Moves the enemy to the right
            case 1:
                MoveRight();
                break;
    
            // Moves the enemy to the left
            case 2:
                MoveLeft();
                break;
        }
    }
    
    /// <summary>
    /// Causes the enemy to stand still with idle animations 
    /// </summary>
    private void Idle ()
    {
    
        // Sets the idle stance duration
        idleTime = Random.Range(5.0f, 10.0f);
        // Sets the movement bool to false to play the idle animations
        isMoving = false;
    
        // Stops the enemy's movement
        enemyRigidbody.velocity = Vector2.zero;
    
        // Checks if the enemy should make a decision on its next movement
    
    
    }
    
    private void MoveRight()
    {
        moveTime = Random.Range(2.0f, 5.01f);
        isMoving = true;
        directionToMove = Vector2.right;
        transform.Translate(directionToMove * (movementSpeed * Time.deltaTime));
    }
    
    private void MoveLeft()
    {
        moveTime = Random.Range(2.0f, 5.01f);
        isMoving = true;
        directionToMove = Vector2.left;
        transform.Translate(directionToMove * (movementSpeed * Time.deltaTime));
    }
    

    编辑:我发现您以错误的方式计算了经过的时间。 Time.deltaTime 是一个固定参数,它不返回经过的时间。

    【讨论】:

    • 感谢您与我合作,但这个解决方案也导致了我最初提到的将 MakeMovementDecision () 放在 Start () 函数中。
    • @Dalsia 是的,我意识到你也以错误的方式计算了经过的时间,试试编辑的 FixedUpdate() 函数
    • 嗯,似乎又导致了原来的问题(它来回剧烈地断断续续)。我不明白为什么这不起作用,这似乎有道理,而且我忘记了您需要将变量设置为 Time.deltaTime。我尝试了几种不同的安排,但没有成功。
    • 如果你使用FixedUpdate,不应该是Time.fixedDeltaTime吗? docs.unity3d.com/ScriptReference/Time-fixedDeltaTime.html
    • 我认为问题在于timeTilNextDecision 注册为小于 0 的速度比做出第一个移动决定后的重置速度要快。在 switch 语句中包含 Debug.Log 消息后,敌人似乎在 3 个移动选项中进行选择的速度过快,有时每秒记录 10 次不同的移动。
    猜你喜欢
    • 2020-03-01
    • 2011-03-05
    • 1970-01-01
    • 1970-01-01
    • 2011-04-11
    • 2011-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多