c# - 这个基于网格的 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;
}
}
解决方案
我找到了一个完美的解决方案(感谢评论中的各种建议)。如果其他人需要一个工作的休闲网格移动脚本,这里是
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))];
}
}
推荐阅读
- c++ - ReSharper 使 C++ 20 中的协程失效
- javascript - 条件为真时显示元素有效
- python - 在python中将字符串浮点数转换为int
- excel - 组合框中的重叠选项
- java - 删除所有新行并使用 n 个字符制作单行
- google-sheets-formula - 带有超链接值的 VLookup - GoogleSheets
- python - python中的easyocr和可执行文件
- android - PublishSubject 调用 observable,它只是在后台后忽略调用(有时)
- java - 如何忽略此警告?
- python - 如何在 ubuntu20 和 ros neotic 上使用和安装 arbotix_gui?