首页 > 解决方案 > 这个基于网格的 npc 移动脚本有什么错误?

问题描述

我一直在尝试在基于网格的游戏中为 NPC 制作脚本(正方形为 1 个单位宽)。NPC 应该从 1 个方格 (例如 (1, 0)) 移动到一个相邻方格 ((0, 0), (1, -1) (1, 1), (2, 0)),然后他应该在选择另一个方向之前保持空闲一段时间。显然,它已经有一个脚本可以检测相邻方块中的碰撞对象。

问题是有时它会移动到一个不是整数的位置(例如 (0, 0,768)),但它会在几步之后自行纠正。有人可以在需要的地方更正代码吗?

PS,令牌是一个游戏对象,它为创建它的 NPC 保留正方形,以阻止任何其他实体移动到这些坐标

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

public class NPCMoveToPointScript : MonoBehaviour 
{
    public float speed = 2.0f; //movement speed
    public Vector3 pos;
    List<int> directions;   //available directions

    //where he's facing
    public bool up;
    public bool down;
    public bool left;
    public bool right;

    public int dir;    //value to use for animator
    public float idleTime= 0.5f;

    public bool idle; //false if a movement button is pressed
    public bool canMove = true;
    public GameObject token;    //to avoid collisions between entities while moving towards a tile
    CollisionScript cs;

    void Start()
    {
        pos = transform.position;
        down = true;
        cs = GetComponent<CollisionScript>();
    }

    void Update()   //move stickman in the direction of the keys and maintain the direction he's facing with animations
    {
        dir = ChooseDirection();

        if (transform.position == pos) //go idle
        {
            StartCoroutine(StayIdle(idleTime));
        }

        //go in the chosen direction
        if (!idle && dir == 0 && transform.position == pos)
        {
            AllFalse();
            down = true;
            pos += Vector3.down;
        }

        else if (!idle && (dir ==1) && (transform.position == pos))
        {
            AllFalse();
            left = true;
            pos += Vector3.left;
        }

        else if (!idle && (dir == 2) && (transform.position == pos))
        {
            AllFalse();
            up = true;
            pos += Vector3.up;
        }

        else if (!idle && dir == 3 && transform.position == pos)
        {
            AllFalse();
            right = true;
            pos += Vector3.right;
        }

        //if you just started moving towards a different tile, spawn a Token in that tile to avoid other npcs moving towards it
        if (((transform.position.x - (int)transform.position.x == 0) && (transform.position.y - (int)transform.position.y == 0)) && pos != transform.position)
        {
            Instantiate(token, pos, Quaternion.identity);
        }
        transform.position = Vector3.MoveTowards(transform.position, pos, Time.deltaTime * speed);
    }

    //avoid bug by deactivating all direction booleans before changing one
    void AllFalse()
    {
        up = false;
        down = false;
        left = false;
        right = false;
        idle = false;
    }

    //checks how many directions are available and then chooses one of them randomly
    int ChooseDirection()
    {
        directions = new List<int>();
        //check the directions
        if (!cs.hitDown)
        {
            directions.Add(0);
        }
        if (!cs.hitLeft)
        {
            directions.Add(1);
        }
        if (!cs.hitUp)
        {
            directions.Add(2);
        }
        if (!cs.hitRight)
        {
            directions.Add(3);
        }

        //turn directions into an array and pick a random direction
        var array = directions.ToArray();
        directions.Clear();
        return array[Random.Range(0, (array.Length))];
    }

    //literaly what the name says (for some seconds) lol
    public IEnumerator StayIdle(float idleTime)
    {
        yield return new WaitForSeconds(idleTime);
        idle = true;
        yield return new WaitForSeconds(4*idleTime);
        idle = false;
    }
}

标签: c#unity3d

解决方案


我找到了一个完美的解决方案(感谢评论中的各种建议)。如果其他人需要一个工作的休闲网格移动脚本,这里是

            public class NPCMovementNew : MonoBehaviour {

public float speed = 2.0f; //movement speed
public float idleTime = 1.0f;   //how much time does he spend idling
public int dir; //where he's facing

public bool idle; //false if a movement button is pressed
public bool canMove = true; //to disable casual movement

public GameObject token;    //to avoid collisions between entities while moving towards a tile
Vector3 pos;
List<int> directions;   //available directions
CollisionScript cs;

// Use this for initialization
void Start () {
    cs = GetComponent<CollisionScript>();
    pos = transform.position;
    dir = 0;
}

// Update is called once per frame
void Update () {
    if(canMove && !idle)    //check if he can move
    {
        if (transform.position != pos)  //if he's not at the center of a square, he's not idling
        {
            idle = false;
            transform.position = Vector3.MoveTowards(transform.position, pos, Time.deltaTime * speed);   //move to that position
        }
        else   //here he decides to move
        {
            StartCoroutine(IdleThenMove(idleTime));
        }
    }
}

//function that goes idle for a certain time, then moves to an adiacent square
IEnumerator IdleThenMove(float t)
{
    idle = true;
    yield return new WaitForSeconds(t);

    //here it randomly chooses a direction and translates it to a position vector
    dir = ChooseDirection();
    switch (dir)
    {
        case 0:
            pos += Vector3.down;
            break;
        case 1:
            pos += Vector3.left;
            break;
        case 2:
            pos += Vector3.up;
            break;
        case 3:
            pos += Vector3.right;
            break;
        default:
            pos = transform.position;
            break;
    }
    idle = false;
    Instantiate(token, pos, Quaternion.identity);   //spawn token to occupy square
    transform.position= Vector3.MoveTowards(transform.position, pos, Time.deltaTime * speed);   //move to that position
}

//checks how many directions are available and then chooses one of them randomly
int ChooseDirection()
{
    directions = new List<int>();
    //check the directions
    if (!cs.hitDown)
    {
        directions.Add(0);
    }
    if (!cs.hitLeft)
    {
        directions.Add(1);
    }
    if (!cs.hitUp)
    {
        directions.Add(2);
    }
    if (!cs.hitRight)
    {
        directions.Add(3);
    }

    //turn directions into an array and pick a random direction
    var array = directions.ToArray();
    directions.Clear();
    return array[Random.Range(0, (array.Length))];
}
}

推荐阅读