在 Unity 中沿光线投射实例化预定义数量的对象
Instantiate predefined number of object along a raycast in Unity
我有一个基于 2 个点每帧渲染的光线投射,这 2 个点每帧改变位置。
我需要的是一个系统,不需要一个方向,也不需要一些对象,而是取2个点,然后根据需要实例化或销毁尽可能多的对象,从一侧获取实例化的对象到另一个减 spaceBetweenPoints
。如果你愿意,你可以把它想象成一个 Angry Birds Style slingshot HUD,除了没有重力,并且基于光线投射。
我的脚本:
public int numOfPoints; // The number of points that are generated (This would need to chnage based one the distance in the end)
public float spaceBetweenPoints; // The spacing between the generated points
private GameObject[] predictionPoints; // The prefab to be gernerated
private Vector2 firstPathStart; // The starting point for the raycast (Changes each frame)
private Vector2 firstPathEnd; // The ending point for the raycast (Changes each frame)
void start()
{
predictionPoints = new GameObject[numOfPoints];
for (int i = 0; i < numOfPoints; i++)
{
predictionPoints[i] = Instantiate(predictionPointPrefab, firePoint.position,
Quaternion.identity);
}
}
void Update
{
Debug.DrawLine(firstPathStart, firstPathEnd, UnityEngine.Color.black);
DrawPredictionDisplay();
}
void DrawPredictionDisplay()
{
for (int i = 0; i < numOfPoints; i++)
{
predictionPoints[i].transform.position = predictionPointPosition(i * spaceBetweenPoints);
}
}
Vector2 predictionPointPosition(float time)
{
Vector2 position = (Vector2)firstPathStart + direction.normalized * 10f * time;
return position;
}
当前系统只是简单地获取起始位置和方向,然后根据时间在该方向上移动预设数量的对象。这种做法也会导致问题,因为它是无止境的,而不是一直到光线投射结束:(请原谅我的绘画技巧)
Blue line = raycast
Black dots = instantiated prefab
Orange dot = raycast orange
Green dot = end of raycast
备注:
direction
是我在编辑器中设置的动力,我需要它来整理我目前正在做的事情,但是当 运行 基于积分时,它应该不是必需的。
希望我没听错。
首先,计算 A 到 B 的直线,即 B 减去 A。
要获得所需对象的数量,请将线大小除以对象的间距。您还可以添加预测点对象的直径以避免重叠。
然后为了得到每个对象的位置,写(几乎)相同的for循环。
这是我想出来的,没有测试,如果有帮助请告诉我!
public class CustomLineRenderer : MonoBehaviour
{
public float SpaceBetweenPoints;
public GameObject PredictionPointPrefab;
// remove transforms if you need to
public Transform start;
public Transform end;
private List<GameObject> _predictionPoints;
// these are your raycast start & end point, make them public or whatever
private Vector2 _firstPathStart;
private Vector2 _firstPathEnd;
private void Awake()
{
_firstPathStart = start.position;
_firstPathEnd = end.position;
_predictionPoints = new List<GameObject>();
}
private void Update()
{
_firstPathStart = start.position;
_firstPathEnd = end.position;
// using any small value to clamp everything and avoid division by zero
if (SpaceBetweenPoints < 0.001f) SpaceBetweenPoints = 0.001f;
var line = _firstPathEnd - _firstPathStart;
var objectsNumber = Mathf.FloorToInt(line.magnitude / SpaceBetweenPoints);
var direction = line.normalized;
// Update the collection so that the line isn't too short
for (var i = _predictionPoints.Count; i <= objectsNumber; ++i)
_predictionPoints.Add(Instantiate(PredictionPointPrefab));
for (var i = 0; i < objectsNumber; ++i)
{
_predictionPoints[i].SetActive(true);
_predictionPoints[i].transform.position = _firstPathStart + direction * (SpaceBetweenPoints * i);
}
// You could destroy objects, but it's better to add them to the pool since you'll use them quite often
for (var i = objectsNumber; i < _predictionPoints.Count; ++i)
_predictionPoints[i].SetActive(false);
}
}
如果你问我,我会说如果你知道一点数学技巧就很容易了。我并不是说我非常擅长数学,但是一旦你掌握了它,下次就很容易实现。在这里,如果我试图解释一切,我将无法解释清楚。看下面的代码,我把整段代码都注释掉了,方便大家理解。
基本上我使用了一种名为 Vector2.Lerp()
线性插值的方法,这意味着该方法将 return 根据第三个参数 t
的值 point1, and point2
之间的值来自 0 to 1
.
public class TestScript : MonoBehaviour
{
public Transform StartPoint;
public Transform EndPoint;
public float spaceBetweenPoints;
[Space]
public Vector2 startPosition;
public Vector2 endPosition;
[Space]
public List<Vector3> points;
private float distance;
private void Update()
{
startPosition = StartPoint.position; //Setting Starting point and Ending point.
endPosition = EndPoint.position;
//Finding the distance between point
distance = Vector2.Distance(startPosition, endPosition);
//Generating the points
GeneratePointsObjects();
Debug.DrawLine(StartPoint.position, EndPoint.position, Color.black);
}
private void OnDrawGizmos()
{
//Drawing the Dummy Gizmo Sphere to see the points
Gizmos.color = Color.black;
foreach (Vector3 p in points)
{
Gizmos.DrawSphere(p, spaceBetweenPoints / 2);
}
}
private void OnValidate()
{
//Validating that the space between two objects is not 0 because that would be Raise an exception "Devide by Zero"
if (spaceBetweenPoints <= 0)
{
spaceBetweenPoints = 0.01f;
}
}
private void GeneratePointsObjects()
{
//Vlearing the list so that we don't iterate over old points
points.Clear();
float numbersOfPoints = distance / spaceBetweenPoints; //Finding numbers of objects to be spawned by dividing "distance" by "spaceBetweenPoints"
float increnment = 1 / numbersOfPoints; //finding the increment for Lerp this will always be between 0 to 1, because Lerp() takes value between 0 to 1;
for (int i = 1; i < numbersOfPoints; i ++)
{
Vector3 v = Vector2.Lerp(startPosition, endPosition, increnment * i); //Find next position using Vector2.Lerp()
points.Add(v);//Add the newlly found position in List so that we can spwan the Object at that position.
}
}
}
更新: 添加,如何在位置上设置预制件
我只是简单地销毁了旧对象并实例化了新对象。但请记住,在您的游戏中频繁实例化和销毁对象会耗尽玩家机器上的内存。 Os 我建议您使用对象池。作为参考,我将在教程中添加 link。
private void Update()
{
startPosition = StartPoint.position; //Setting Starting point and Ending point.
endPosition = EndPoint.position;
//Finding the distance between point
distance = Vector2.Distance(startPosition, endPosition);
//Generating the points
GeneratePointsObjects();
//Update: Generating points/dots on all to location;
InstenciatePrefabsOnPositions();
Debug.DrawLine(StartPoint.position, EndPoint.position, Color.black);
}
private void InstenciatePrefabsOnPositions()
{
//Remove all old prefabs/objects/points
for (int i = 0; i < pointParent.childCount; i++)
{
Destroy(pointParent.GetChild(i).gameObject);
}
//Instantiate new Object on the positions calculated in GeneratePointsObjects()
foreach (Vector3 v in points)
{
Transform t = Instantiate(pointPrefab);
t.SetParent(pointParent);
t.localScale = Vector3.one;
t.position = v;
t.gameObject.SetActive(true);
}
}
希望这对您有所帮助,请参阅下面的 link 以获得更多参考
我有一个基于 2 个点每帧渲染的光线投射,这 2 个点每帧改变位置。
我需要的是一个系统,不需要一个方向,也不需要一些对象,而是取2个点,然后根据需要实例化或销毁尽可能多的对象,从一侧获取实例化的对象到另一个减 spaceBetweenPoints
。如果你愿意,你可以把它想象成一个 Angry Birds Style slingshot HUD,除了没有重力,并且基于光线投射。
我的脚本:
public int numOfPoints; // The number of points that are generated (This would need to chnage based one the distance in the end)
public float spaceBetweenPoints; // The spacing between the generated points
private GameObject[] predictionPoints; // The prefab to be gernerated
private Vector2 firstPathStart; // The starting point for the raycast (Changes each frame)
private Vector2 firstPathEnd; // The ending point for the raycast (Changes each frame)
void start()
{
predictionPoints = new GameObject[numOfPoints];
for (int i = 0; i < numOfPoints; i++)
{
predictionPoints[i] = Instantiate(predictionPointPrefab, firePoint.position,
Quaternion.identity);
}
}
void Update
{
Debug.DrawLine(firstPathStart, firstPathEnd, UnityEngine.Color.black);
DrawPredictionDisplay();
}
void DrawPredictionDisplay()
{
for (int i = 0; i < numOfPoints; i++)
{
predictionPoints[i].transform.position = predictionPointPosition(i * spaceBetweenPoints);
}
}
Vector2 predictionPointPosition(float time)
{
Vector2 position = (Vector2)firstPathStart + direction.normalized * 10f * time;
return position;
}
当前系统只是简单地获取起始位置和方向,然后根据时间在该方向上移动预设数量的对象。这种做法也会导致问题,因为它是无止境的,而不是一直到光线投射结束:(请原谅我的绘画技巧)
Blue line = raycast
Black dots = instantiated prefab
Orange dot = raycast orange
Green dot = end of raycast
备注:
direction
是我在编辑器中设置的动力,我需要它来整理我目前正在做的事情,但是当 运行 基于积分时,它应该不是必需的。
希望我没听错。
首先,计算 A 到 B 的直线,即 B 减去 A。 要获得所需对象的数量,请将线大小除以对象的间距。您还可以添加预测点对象的直径以避免重叠。 然后为了得到每个对象的位置,写(几乎)相同的for循环。
这是我想出来的,没有测试,如果有帮助请告诉我!
public class CustomLineRenderer : MonoBehaviour
{
public float SpaceBetweenPoints;
public GameObject PredictionPointPrefab;
// remove transforms if you need to
public Transform start;
public Transform end;
private List<GameObject> _predictionPoints;
// these are your raycast start & end point, make them public or whatever
private Vector2 _firstPathStart;
private Vector2 _firstPathEnd;
private void Awake()
{
_firstPathStart = start.position;
_firstPathEnd = end.position;
_predictionPoints = new List<GameObject>();
}
private void Update()
{
_firstPathStart = start.position;
_firstPathEnd = end.position;
// using any small value to clamp everything and avoid division by zero
if (SpaceBetweenPoints < 0.001f) SpaceBetweenPoints = 0.001f;
var line = _firstPathEnd - _firstPathStart;
var objectsNumber = Mathf.FloorToInt(line.magnitude / SpaceBetweenPoints);
var direction = line.normalized;
// Update the collection so that the line isn't too short
for (var i = _predictionPoints.Count; i <= objectsNumber; ++i)
_predictionPoints.Add(Instantiate(PredictionPointPrefab));
for (var i = 0; i < objectsNumber; ++i)
{
_predictionPoints[i].SetActive(true);
_predictionPoints[i].transform.position = _firstPathStart + direction * (SpaceBetweenPoints * i);
}
// You could destroy objects, but it's better to add them to the pool since you'll use them quite often
for (var i = objectsNumber; i < _predictionPoints.Count; ++i)
_predictionPoints[i].SetActive(false);
}
}
如果你问我,我会说如果你知道一点数学技巧就很容易了。我并不是说我非常擅长数学,但是一旦你掌握了它,下次就很容易实现。在这里,如果我试图解释一切,我将无法解释清楚。看下面的代码,我把整段代码都注释掉了,方便大家理解。
基本上我使用了一种名为 Vector2.Lerp()
线性插值的方法,这意味着该方法将 return 根据第三个参数 t
的值 point1, and point2
之间的值来自 0 to 1
.
public class TestScript : MonoBehaviour
{
public Transform StartPoint;
public Transform EndPoint;
public float spaceBetweenPoints;
[Space]
public Vector2 startPosition;
public Vector2 endPosition;
[Space]
public List<Vector3> points;
private float distance;
private void Update()
{
startPosition = StartPoint.position; //Setting Starting point and Ending point.
endPosition = EndPoint.position;
//Finding the distance between point
distance = Vector2.Distance(startPosition, endPosition);
//Generating the points
GeneratePointsObjects();
Debug.DrawLine(StartPoint.position, EndPoint.position, Color.black);
}
private void OnDrawGizmos()
{
//Drawing the Dummy Gizmo Sphere to see the points
Gizmos.color = Color.black;
foreach (Vector3 p in points)
{
Gizmos.DrawSphere(p, spaceBetweenPoints / 2);
}
}
private void OnValidate()
{
//Validating that the space between two objects is not 0 because that would be Raise an exception "Devide by Zero"
if (spaceBetweenPoints <= 0)
{
spaceBetweenPoints = 0.01f;
}
}
private void GeneratePointsObjects()
{
//Vlearing the list so that we don't iterate over old points
points.Clear();
float numbersOfPoints = distance / spaceBetweenPoints; //Finding numbers of objects to be spawned by dividing "distance" by "spaceBetweenPoints"
float increnment = 1 / numbersOfPoints; //finding the increment for Lerp this will always be between 0 to 1, because Lerp() takes value between 0 to 1;
for (int i = 1; i < numbersOfPoints; i ++)
{
Vector3 v = Vector2.Lerp(startPosition, endPosition, increnment * i); //Find next position using Vector2.Lerp()
points.Add(v);//Add the newlly found position in List so that we can spwan the Object at that position.
}
}
}
更新: 添加,如何在位置上设置预制件
我只是简单地销毁了旧对象并实例化了新对象。但请记住,在您的游戏中频繁实例化和销毁对象会耗尽玩家机器上的内存。 Os 我建议您使用对象池。作为参考,我将在教程中添加 link。
private void Update()
{
startPosition = StartPoint.position; //Setting Starting point and Ending point.
endPosition = EndPoint.position;
//Finding the distance between point
distance = Vector2.Distance(startPosition, endPosition);
//Generating the points
GeneratePointsObjects();
//Update: Generating points/dots on all to location;
InstenciatePrefabsOnPositions();
Debug.DrawLine(StartPoint.position, EndPoint.position, Color.black);
}
private void InstenciatePrefabsOnPositions()
{
//Remove all old prefabs/objects/points
for (int i = 0; i < pointParent.childCount; i++)
{
Destroy(pointParent.GetChild(i).gameObject);
}
//Instantiate new Object on the positions calculated in GeneratePointsObjects()
foreach (Vector3 v in points)
{
Transform t = Instantiate(pointPrefab);
t.SetParent(pointParent);
t.localScale = Vector3.one;
t.position = v;
t.gameObject.SetActive(true);
}
}
希望这对您有所帮助,请参阅下面的 link 以获得更多参考