Unity3D 运动问题(基于网格的寻路)
Unity3D movement issue (grid-based pathfinding)
我目前正在尝试将我的角色移动到我的鼠标目标。自 2 周以来,我做了很多研究,我觉得接近我的目标,但我仍在挣扎。在按下播放之前我收到这个警告:
Assets\Scripts\Worldmaps\Movement.cs(11,12): warning CS0649: Field 'Movement.path' is never assigned to, and will always have its default value null
当我按下播放键时,点击任何节点后出现此错误:
NullReferenceException: Object reference not set to an instance of an object
Movement.Update () (at Assets/Scripts/Worldmaps/Movement.cs:24)
代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Movement : MonoBehaviour
{
private const float speed = 10f;
private int targetIndex;
Vector3[] path;
private Pathfinding pathfinding;
void Awake()
{
pathfinding = GetComponent<Pathfinding>();
}
private void Update()
{
if(Input.GetMouseButtonDown(0))
{
Vector3 target = GetMouseWorldPosition();
pathfinding.StartFindPath(transform.position, target);
targetIndex = 0;
Vector3 finalPath = path[0];
while(true)
{
if(transform.position == finalPath)
{
targetIndex++;
if(targetIndex >= path.Length)
{
break;
}
finalPath = path[targetIndex];
}
transform.position = Vector3.MoveTowards(transform.position, finalPath, speed * Time.deltaTime);
}
Debug.Log(target);
}
}
public static Vector3 GetMouseWorldPosition()
{
Vector3 clickPosition = new Vector3();
clickPosition.y = 0;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if(Physics.Raycast(ray, out hit))
{
clickPosition = hit.point;
}
return clickPosition;
}
}
寻路脚本没有任何问题,因为当我在带有 Update() FindPath(character.position, target)
的寻路脚本中使用 public Transform character;
时,它可以很好地找到并更新角色和目标之间的路径。
即使我是初学者并且缺乏技能,我也知道为其他人编写代码可能很烦人,所以也欢迎提出诚实的建议!作为初学者,寻路可能不是最容易开始的事情,但这是我真正想继续的项目的基础!
以防万一,如需Pathfinding代码参考(Sebastian Lague教程):
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Pathfinding : MonoBehaviour
{
AGrid grid;
void Awake()
{
grid = GetComponent<AGrid>();
}
public void StartFindPath(Vector3 startPos, Vector3 targetPos)
{
StartCoroutine(FindPath(startPos, targetPos));
}
IEnumerator FindPath(Vector3 startPos, Vector3 targetPos)
{
Node startNode = grid.NodeFromWorldPoint(startPos);
Node targetNode = grid.NodeFromWorldPoint(targetPos);
Heap<Node> openSet = new Heap<Node>(grid.MaxSize);
HashSet<Node> closedSet = new HashSet<Node>();
openSet.Add(startNode);
while(openSet.Count > 0)
{
Node currentNode = openSet.RemoveFirst();
closedSet.Add(currentNode);
if(currentNode == targetNode)
{
RetracePath(startNode, targetNode);
break;
}
foreach(Node neighbour in grid.GetNeighbours(currentNode))
{
if(!neighbour.walkable || closedSet.Contains(neighbour))
{
continue;
}
int newMovementCostToNeighbour = currentNode.gCost + GetDistance(currentNode, neighbour);
if(newMovementCostToNeighbour < neighbour.gCost || !openSet.Contains(neighbour))
{
neighbour.gCost = newMovementCostToNeighbour;
neighbour.hCost = GetDistance(neighbour, targetNode);
neighbour.parent = currentNode;
if(!openSet.Contains(neighbour))
{
openSet.Add(neighbour);
}
else
{
openSet.UpdateItem(neighbour);
}
}
}
}
yield return null;
}
void RetracePath(Node startNode, Node endNode)
{
List<Node> path = new List<Node>();
Node currentNode = endNode;
while(currentNode != startNode)
{
path.Add(currentNode);
currentNode = currentNode.parent;
}
path.Add(startNode);
path.Reverse();
}
int GetDistance(Node nodeA, Node nodeB)
{
int dstX = Mathf.Abs(nodeA.gridX - nodeB.gridX);
int dstZ = Mathf.Abs(nodeA.gridZ - nodeB.gridZ);
if(dstX > dstZ)
{
return 14 * dstZ + 10 * (dstX - dstZ);
}
return 14 * dstX + 10 * (dstZ - dstX);
}
}
提前致谢!
花枪:)
您必须在使用前实例化 Vector3[] 路径数组。所以在 Awake() 中,只需将其实例化为:
path = new Vector3[<write the length of the array here>];
举个例子:
path = new Vector3[5];
如果不确定数组的长度或数组中对象的数量经常变化,最好使用列表而不是数组。
我目前正在尝试将我的角色移动到我的鼠标目标。自 2 周以来,我做了很多研究,我觉得接近我的目标,但我仍在挣扎。在按下播放之前我收到这个警告:
Assets\Scripts\Worldmaps\Movement.cs(11,12): warning CS0649: Field 'Movement.path' is never assigned to, and will always have its default value null
当我按下播放键时,点击任何节点后出现此错误:
NullReferenceException: Object reference not set to an instance of an object
Movement.Update () (at Assets/Scripts/Worldmaps/Movement.cs:24)
代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Movement : MonoBehaviour
{
private const float speed = 10f;
private int targetIndex;
Vector3[] path;
private Pathfinding pathfinding;
void Awake()
{
pathfinding = GetComponent<Pathfinding>();
}
private void Update()
{
if(Input.GetMouseButtonDown(0))
{
Vector3 target = GetMouseWorldPosition();
pathfinding.StartFindPath(transform.position, target);
targetIndex = 0;
Vector3 finalPath = path[0];
while(true)
{
if(transform.position == finalPath)
{
targetIndex++;
if(targetIndex >= path.Length)
{
break;
}
finalPath = path[targetIndex];
}
transform.position = Vector3.MoveTowards(transform.position, finalPath, speed * Time.deltaTime);
}
Debug.Log(target);
}
}
public static Vector3 GetMouseWorldPosition()
{
Vector3 clickPosition = new Vector3();
clickPosition.y = 0;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if(Physics.Raycast(ray, out hit))
{
clickPosition = hit.point;
}
return clickPosition;
}
}
寻路脚本没有任何问题,因为当我在带有 Update() FindPath(character.position, target)
的寻路脚本中使用 public Transform character;
时,它可以很好地找到并更新角色和目标之间的路径。
即使我是初学者并且缺乏技能,我也知道为其他人编写代码可能很烦人,所以也欢迎提出诚实的建议!作为初学者,寻路可能不是最容易开始的事情,但这是我真正想继续的项目的基础!
以防万一,如需Pathfinding代码参考(Sebastian Lague教程):
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Pathfinding : MonoBehaviour
{
AGrid grid;
void Awake()
{
grid = GetComponent<AGrid>();
}
public void StartFindPath(Vector3 startPos, Vector3 targetPos)
{
StartCoroutine(FindPath(startPos, targetPos));
}
IEnumerator FindPath(Vector3 startPos, Vector3 targetPos)
{
Node startNode = grid.NodeFromWorldPoint(startPos);
Node targetNode = grid.NodeFromWorldPoint(targetPos);
Heap<Node> openSet = new Heap<Node>(grid.MaxSize);
HashSet<Node> closedSet = new HashSet<Node>();
openSet.Add(startNode);
while(openSet.Count > 0)
{
Node currentNode = openSet.RemoveFirst();
closedSet.Add(currentNode);
if(currentNode == targetNode)
{
RetracePath(startNode, targetNode);
break;
}
foreach(Node neighbour in grid.GetNeighbours(currentNode))
{
if(!neighbour.walkable || closedSet.Contains(neighbour))
{
continue;
}
int newMovementCostToNeighbour = currentNode.gCost + GetDistance(currentNode, neighbour);
if(newMovementCostToNeighbour < neighbour.gCost || !openSet.Contains(neighbour))
{
neighbour.gCost = newMovementCostToNeighbour;
neighbour.hCost = GetDistance(neighbour, targetNode);
neighbour.parent = currentNode;
if(!openSet.Contains(neighbour))
{
openSet.Add(neighbour);
}
else
{
openSet.UpdateItem(neighbour);
}
}
}
}
yield return null;
}
void RetracePath(Node startNode, Node endNode)
{
List<Node> path = new List<Node>();
Node currentNode = endNode;
while(currentNode != startNode)
{
path.Add(currentNode);
currentNode = currentNode.parent;
}
path.Add(startNode);
path.Reverse();
}
int GetDistance(Node nodeA, Node nodeB)
{
int dstX = Mathf.Abs(nodeA.gridX - nodeB.gridX);
int dstZ = Mathf.Abs(nodeA.gridZ - nodeB.gridZ);
if(dstX > dstZ)
{
return 14 * dstZ + 10 * (dstX - dstZ);
}
return 14 * dstX + 10 * (dstZ - dstX);
}
}
提前致谢!
花枪:)
您必须在使用前实例化 Vector3[] 路径数组。所以在 Awake() 中,只需将其实例化为:
path = new Vector3[<write the length of the array here>];
举个例子:
path = new Vector3[5];
如果不确定数组的长度或数组中对象的数量经常变化,最好使用列表而不是数组。