【问题标题】:Can't make my Enemy to randomly move in a range. Unity3D不能让我的敌人在一个范围内随机移动。 Unity3D
【发布时间】:2015-03-01 14:12:55
【问题描述】:

嗨:我正在制作一个 FPS 与一些 3D 生存混合。我有一个代表我的敌人的动画模型。如果我在范围内,我可以让他向我移动。如果他离得足够近,他就会停止移动(和动画)。现在我试图实现一个流浪的行为。

我的想法是这样的:敌人从起始位置开始。他检查玩家是否在范围内,如果为真,则 FollowPlayer(),如果为假,则为 MoveAround(),这意味着他在其实际位置的某个范围内计算一个随机位置,Slerp 到该位置,然后移动到该随机位置。到达随机位置后,他应该等待 X 秒,直到下一个动作。我拥有的 MoveAround() 方法是协程,但我无法使其工作。这是脚本:

using UnityEngine;
using System.Collections;

public class EnemyAI : MonoBehaviour {

public float rotationSpeed;
public float moveSpeed;
public float maxSpeed;
public float minRange;
public float maxRange;
public float moveRange;
Animator anim;
CharacterController controller;

// Use this for initialization
void Awake (){
    anim = GetComponent<Animator> ();
    controller = GetComponent<CharacterController> ();
}
void Start () {
}

// Update is called once per frame
void FixedUpdate () {
    FollowPlayer ();
    StartCoroutine(MoveAround ());
}
void FollowPlayer(){
    Vector3 playerPos = GameObject.Find ("Player").transform.position;
    Vector3 lookDir = playerPos - transform.position;
    Vector3 moveDir = lookDir;// * moveSpeed;
    moveDir *= Time.fixedDeltaTime;

    if((Vector3.Distance(transform.position, playerPos) <= maxRange) && (Vector3.Distance(transform.position, playerPos) > minRange) ){
        Vector3 previous = transform.position;
        transform.rotation = Quaternion.Slerp (transform.rotation, Quaternion.LookRotation (lookDir), rotationSpeed * Time.fixedDeltaTime);
        controller.Move(moveDir);
        float velocity = ((transform.position - previous).magnitude) / Time.fixedDeltaTime;
    //  Debug.Log("velocidad: " + velocity);
        previous = transform.position;
        anim.SetFloat ("speed", velocity);
    }
    if(Vector3.Distance(transform.position, playerPos) < minRange){
        controller.Move(Vector3.zero);
        Debug.LogWarning("hey");
        anim.SetFloat("speed",controller.velocity.magnitude);
    }
}
IEnumerator MoveAround(){
    Vector3 playerPos = GameObject.Find ("Player").transform.position;
    Vector3 randomPos = Random.onUnitSphere * moveRange;
    randomPos = new Vector3 (randomPos.x + transform.position.x, transform.position.y, randomPos.z + transform.position.z);
    Vector3 lookDir = randomPos - transform.position;
    Vector3 moveDir = lookDir;
    moveDir *= Time.fixedDeltaTime;
    Debug.Log ("Player Pos: " + playerPos);
    Debug.Log ("Random Pos: " + randomPos);
    Debug.Log ("Look Dir: " + lookDir);
    Debug.Log ("Move Dir: " + moveDir);

    if(Vector3.Distance(transform.position, playerPos) > maxRange){
        Debug.Log("Moving the enemy");
        Vector3 previous = transform.position;
        transform.rotation = Quaternion.Slerp (transform.rotation, Quaternion.LookRotation (lookDir), rotationSpeed * Time.fixedDeltaTime);
        controller.Move(moveDir);
        float velocity = ((transform.position - previous).magnitude) / Time.fixedDeltaTime;
    //  Debug.Log("velocidad: " + velocity);
        previous = transform.position;
        anim.SetFloat ("speed", velocity);
        Debug.Log("Enemy moved");
        yield return new WaitForSeconds(2f);
        Debug.Log("waiting the first 2 seconds");
    }
    Debug.Log ("arrived at destination");
    yield return new WaitForSeconds(2f);
    Debug.Log ("COroutine final");
}

void OnDrawGizmosSelected(){
    Gizmos.color = Color.green;
    Gizmos.DrawWireSphere (transform.position, maxRange);
    Gizmos.DrawWireSphere (transform.position, minRange);
}
}

注意:地图应该是平坦的,但如果代码适用于具有可变高度的地图,那就太好了。 我用字符控制器移动字符是正确的方法吗?

【问题讨论】:

  • 我可以看看你的调试日志吗?为 playerPos 添加一个
  • 当然:当我点击播放时,敌人设置在 (50,0,50)。然后这是第一个控制台日志: ·Player Pos: (0.6, 0.0, 31.7) ·Random Pos: (50.5, 0.0, 48.0) ·Look Dir: (0.5, 0.0, -2.0) ·Move Dir: (0.0, 0.0 , 0.0)·移动敌人·移动敌人

标签: c# random unity3d artificial-intelligence


【解决方案1】:

您在每一帧都调用MoveAroundFollowPlayer(实际上是每个物理帧,这种情况更频繁)。你实际上想做更多这样的事情:

void Start()
{
    StartCoroutine(Move());
}

IEnumerator Move()
{
    while (true)
    {
        if (nearPlayer)
            yield return StartCoroutine(FollowPlayer());
        else
            yield return StartCoroutine(MoveAround());
    }
}

这样你只有在前一个动作完成后才开始移动。如果您想每帧检查位置,它仍然有效,但您的检查将在MoveAroundFollowPlayer 内进行。因为每一帧都启动协程完全违背了协程的目的。如果你有想要运行每一帧的逻辑(无异常),直接放在UpdateFixedUpdate 中,不要使用协程。

编辑:澄清一下,您的方法最简单的问题是,如果您的协程需要 4 秒才能运行,您将在任何时候运行每个方法的 4/Time.fixedDeltaTime 实例,可能会相互争斗。我认为您不希望 400 个单独的 MoveAround 函数一起运行。 :)

【讨论】:

  • 刚刚尝试过您发布的内容,但我的 FollowPlayer 方法不是协程,所以我应该将其更改为 IEnumerator 方法吗?我还尝试将调用 FollorPlayer 协程的行更改为:仅调用方法本身。
  • 好的,第一点是:你说“敌人从起始位置开始。他检查玩家是否在范围内,如果为真则 FollowPlayer() 如果为假则 MoveAround()”。 “Check/If/Then”逻辑明显重复,但你想要它的频率是多少?我编写的代码假定这两种方法都是协同程序,并且会一直运行到它们决定完成为止。如果FollowPlayer 不需要是协程(如果它真的应该在每一帧都运行),那部分可以更改为FollowPlayer(); yield return null
  • 我的编辑应该说:每一帧,你都在启动一个MoveAround 函数。由于它们是协程并且需要不止一帧来完成它们的动作,所以你有很多 MoveArounds 同时运行!最有可能的是,您实际上只想要一个。这就是我的代码所做的——启动一个,等待它,然后启动另一个。
  • 感谢您的快速答复。我试图让它工作,但似乎不可能......我会坚持你告诉我的,看看我能用它做什么。再次感谢:D
  • 不客气。我建议使用笔和纸——“首先,设置位置。一次。然后,进行检查。如果为真,则发生并立即返回。否则,发生这种情况,并等待/继续直到...”跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-12
  • 1970-01-01
  • 2021-11-05
相关资源
最近更新 更多