如何从不断变化的对象列表中获取组件?
How do I get a component from a ever changing List of objects?
在 3d 无尽 运行ner 游戏中统一工作时,我遇到了这个问题。我有一个平台列表(segments/roads),当玩家在 z 方向 运行s 时,它位于玩家面前。我已经下载了一个名为 Dreamteck splines 的新资产包。所以每个平台都有一个附加到它的样条组件。放置平台后,玩家会根据样条的模式抓住样条和 运行s。
假设玩家在第一个平台上。当玩家到达第一个平台样条的末端时,将调用 OnEndReached()
事件处理程序,它基本上说明了到达样条的终点时您想发生的事情。所以我想知道如何在到达终点后获得下一条样条曲线。
P = 玩家
如上图所示,这就是我要实现的目标。关于平台如何铺设的简要描述是,一旦玩家走到下一条路,他刚刚经过的那条路就会被禁用,因此下次他可以随机重用玩家面前的路。
代码:轨道管理器脚本。
public 片段[] tilePrefabs;
public 静态段 newSegment;
public static List<Segment> m_Segments;
public static List<Segment> m_PastSegements;
private int m_SafeSegmentLeft;
private int m_PreSegments = -1;
private float startingSegmentDistance = 4f;
private int startingSafeSegments = 2;
private int amtSegmentsOnScreen = 10;
private float segmentRemovalDistace = -40f;
private float m_TotalWorldDistance;
private float m_CurrentSegmentDistance;
void Update ()
{
while (m_Segments.Count < amtSegmentsOnScreen)
{
SpawnNewSegment();
}
m_TotalWorldDistance += scaledSpeed;
m_CurrentSegmentDistance += scaledSpeed;
if (m_CurrentSegmentDistance > m_Segments[0].worldLength)
{
m_CurrentSegmentDistance -= m_Segments[0].worldLength;
m_PastSegements.Add(m_Segments[0]);
m_Segments.RemoveAt(0);
}
Vector3 currentPos;
Quaternion currentRot;
Transform playerTransform = playerMotor.transform;
m_Segments[0].GetPointAtInWorldUnit(m_CurrentSegmentDistance, out currentPos, out currentRot);
bool needRecenter = currentPos.sqrMagnitude > floatingOriginThreshold;
if (needRecenter)
{
int count = m_Segments.Count;
for (int i = 0; i < count; i++)
{
m_Segments[i].transform.position -= currentPos;
}
count = m_PastSegements.Count;
for (int i = 0; i < count; i++)
{
m_PastSegements[i].transform.position -= currentPos;
}
m_Segments[0].GetPointAtInWorldUnit(m_CurrentSegmentDistance, out currentPos, out currentRot);
}
playerTransform.rotation = currentRot;
playerTransform.position = currentPos;
for (int i = 0; i < m_PastSegements.Count; i++)
{
if ((m_PastSegements[i].transform.position - currentPos).z < segmentRemovalDistace)
{
m_PastSegements[i].Cleanup();
m_PastSegements.RemoveAt(i);
i--;
}
}
}
public void SpawnNewSegment()
{
int useSegment = Random.Range(0, tilePrefabs.Length);
if (useSegment == m_PreSegments)
{
useSegment = (useSegment + 1) % tilePrefabs.Length;
}
Segment segmentToUse = tilePrefabs[useSegment];
newSegment = Instantiate(segmentToUse, Vector3.zero, Quaternion.identity);
Vector3 currentExitPoint;
Quaternion currentExitRotation;
if (m_Segments.Count > 0)
m_Segments[m_Segments.Count - 1].GetPointAt(1.0f, out currentExitPoint, out currentExitRotation);
else
{
currentExitPoint = transform.position;
currentExitRotation = transform.rotation;
}
newSegment.transform.rotation = currentExitRotation;
Vector3 entryPoint;
Quaternion entryRotation;
newSegment.GetPointAt(0.0f, out entryPoint, out entryRotation);
Vector3 pos = currentExitPoint + (newSegment.transform.position - entryPoint);
newSegment.transform.position = pos;
newSegment.manager = this;
newSegment.transform.localScale = new Vector3((Random.value > 0.5f ? -1 : 1), 1, 1);
newSegment.objectRoot.localScale = new Vector3(1.0f / newSegment.transform.localScale.x, 1, 1);
if (m_SafeSegmentLeft <= 0)
SpawnObstacle(newSegment);
else
m_SafeSegmentLeft -= 1;
m_Segments.Add(newSegment);
}
播放器脚本
//Current tile segment;
private Segment currentSegment;
//Spline Follower
private SplineFollower follower
//For Dreamteck spline -->
private Segment nextSegment;
void Start()
{
playerCollider = GetComponent<CapsuleCollider>();
anim = GetComponent<Animator>();
follower = GetComponent<SplineFollower>();
moveLane = currentLane;
follower.onEndReached += Follower_onEndReached;
}
private void Follower_onEndReached()
{
currentSegment = nextSegment;
follower.computer = currentSegment.spline;
}
void OnTriggerEnter(Collider col)
{
nextSegment = col.GetComponentInParent<Segment>();
}
段脚本:附加到每条道路/平台
public SplineComputer spline;
public static Segment next;
SplinePoint[] points;
void Start()
{
spline = GetComponentInChildren<SplineComputer>();
spline.space = SplineComputer.Space.Local;
points = spline.GetPoints();
if (points.Length == 0)
return;
}
目前我使用碰撞器,每条路都有一个盒子碰撞器组件。一旦玩家到达平台的尽头,它就会获得下一个样条组件。它可以工作,但有时它无法识别下一个样条线并使用相同的样条线,这会导致玩家 运行 他一次又一次通过的同一平台。
所以我没主意了。所以来到这里寻找解决方案或建议。帮助将不胜感激。
使用 OnTriggerEnter 或 OnTriggerEnter2D(如果您正在处理 2D 碰撞器)应该可以完成这项工作。但是正如您所说,您已经在使用对撞机,我认为这就是您尝试过的方法。
你可以试试:
OnTriggerStay
光线投射到地面物体。在此 link 中是一个二维示例:https://kylewbanks.com/blog/unity-2d-checking-if-a-character-or-object-is-on-the-ground-using-raycasts
您还可以对 3D 对象进行光线投射。
在这种情况下,您将使用它的目的基本上是将 "laser" 射入玩家下方的地面,并根据您告诉它击中的层抓住那里的物体。因此,如果您有一个名为 "Ground" 的层,您的平台是其中的一部分,那么它只能 return 来自该层的对象。
请记住经常进行光线投射,以便更新以反映游戏。
在这种情况下,我会简单地将我可能的段存储在一个列表中,然后当我到达末尾时获取下一个段并将当前的第一个段移动到末尾或您想要将其移动到列表中的任何位置。
也就是
public Segment currentSegment;
public List<Segment> segments;
void OnEndReached()
{
//Put the completed segmetn back in the list ... probably at the end or randomized anywhere but at the start
segments.Insert(segments.Count-1, currentSegment);
//Now get the next segment
currentSegment = segments[0];
segments.RemoveAt(0);
}
在这个模型中,你有一个简单的列表,它代表你的段出现的顺序,你总是将当前段设置为列表中的下一个,例如索引 0,完成后将它们放回列表中...将它们放在末尾,或者如果您想随机化顺序将它们插入除索引 0 之外的任何软件中,例如
segments.Insert(UnityEngine.Random.Range(1, segments.Count), currentSegment);
请注意,我从列表中删除了我所在的部分...该列表仅代表即将到来的顺序,在跑步游戏中我发现它很容易知道,例如这样我就可以根据表现、分数等重新设置、更改细分的属性。
在 3d 无尽 运行ner 游戏中统一工作时,我遇到了这个问题。我有一个平台列表(segments/roads),当玩家在 z 方向 运行s 时,它位于玩家面前。我已经下载了一个名为 Dreamteck splines 的新资产包。所以每个平台都有一个附加到它的样条组件。放置平台后,玩家会根据样条的模式抓住样条和 运行s。
假设玩家在第一个平台上。当玩家到达第一个平台样条的末端时,将调用 OnEndReached()
事件处理程序,它基本上说明了到达样条的终点时您想发生的事情。所以我想知道如何在到达终点后获得下一条样条曲线。
P = 玩家
如上图所示,这就是我要实现的目标。关于平台如何铺设的简要描述是,一旦玩家走到下一条路,他刚刚经过的那条路就会被禁用,因此下次他可以随机重用玩家面前的路。
代码:轨道管理器脚本。 public 片段[] tilePrefabs; public 静态段 newSegment;
public static List<Segment> m_Segments;
public static List<Segment> m_PastSegements;
private int m_SafeSegmentLeft;
private int m_PreSegments = -1;
private float startingSegmentDistance = 4f;
private int startingSafeSegments = 2;
private int amtSegmentsOnScreen = 10;
private float segmentRemovalDistace = -40f;
private float m_TotalWorldDistance;
private float m_CurrentSegmentDistance;
void Update ()
{
while (m_Segments.Count < amtSegmentsOnScreen)
{
SpawnNewSegment();
}
m_TotalWorldDistance += scaledSpeed;
m_CurrentSegmentDistance += scaledSpeed;
if (m_CurrentSegmentDistance > m_Segments[0].worldLength)
{
m_CurrentSegmentDistance -= m_Segments[0].worldLength;
m_PastSegements.Add(m_Segments[0]);
m_Segments.RemoveAt(0);
}
Vector3 currentPos;
Quaternion currentRot;
Transform playerTransform = playerMotor.transform;
m_Segments[0].GetPointAtInWorldUnit(m_CurrentSegmentDistance, out currentPos, out currentRot);
bool needRecenter = currentPos.sqrMagnitude > floatingOriginThreshold;
if (needRecenter)
{
int count = m_Segments.Count;
for (int i = 0; i < count; i++)
{
m_Segments[i].transform.position -= currentPos;
}
count = m_PastSegements.Count;
for (int i = 0; i < count; i++)
{
m_PastSegements[i].transform.position -= currentPos;
}
m_Segments[0].GetPointAtInWorldUnit(m_CurrentSegmentDistance, out currentPos, out currentRot);
}
playerTransform.rotation = currentRot;
playerTransform.position = currentPos;
for (int i = 0; i < m_PastSegements.Count; i++)
{
if ((m_PastSegements[i].transform.position - currentPos).z < segmentRemovalDistace)
{
m_PastSegements[i].Cleanup();
m_PastSegements.RemoveAt(i);
i--;
}
}
}
public void SpawnNewSegment()
{
int useSegment = Random.Range(0, tilePrefabs.Length);
if (useSegment == m_PreSegments)
{
useSegment = (useSegment + 1) % tilePrefabs.Length;
}
Segment segmentToUse = tilePrefabs[useSegment];
newSegment = Instantiate(segmentToUse, Vector3.zero, Quaternion.identity);
Vector3 currentExitPoint;
Quaternion currentExitRotation;
if (m_Segments.Count > 0)
m_Segments[m_Segments.Count - 1].GetPointAt(1.0f, out currentExitPoint, out currentExitRotation);
else
{
currentExitPoint = transform.position;
currentExitRotation = transform.rotation;
}
newSegment.transform.rotation = currentExitRotation;
Vector3 entryPoint;
Quaternion entryRotation;
newSegment.GetPointAt(0.0f, out entryPoint, out entryRotation);
Vector3 pos = currentExitPoint + (newSegment.transform.position - entryPoint);
newSegment.transform.position = pos;
newSegment.manager = this;
newSegment.transform.localScale = new Vector3((Random.value > 0.5f ? -1 : 1), 1, 1);
newSegment.objectRoot.localScale = new Vector3(1.0f / newSegment.transform.localScale.x, 1, 1);
if (m_SafeSegmentLeft <= 0)
SpawnObstacle(newSegment);
else
m_SafeSegmentLeft -= 1;
m_Segments.Add(newSegment);
}
播放器脚本
//Current tile segment;
private Segment currentSegment;
//Spline Follower
private SplineFollower follower
//For Dreamteck spline -->
private Segment nextSegment;
void Start()
{
playerCollider = GetComponent<CapsuleCollider>();
anim = GetComponent<Animator>();
follower = GetComponent<SplineFollower>();
moveLane = currentLane;
follower.onEndReached += Follower_onEndReached;
}
private void Follower_onEndReached()
{
currentSegment = nextSegment;
follower.computer = currentSegment.spline;
}
void OnTriggerEnter(Collider col)
{
nextSegment = col.GetComponentInParent<Segment>();
}
段脚本:附加到每条道路/平台
public SplineComputer spline;
public static Segment next;
SplinePoint[] points;
void Start()
{
spline = GetComponentInChildren<SplineComputer>();
spline.space = SplineComputer.Space.Local;
points = spline.GetPoints();
if (points.Length == 0)
return;
}
目前我使用碰撞器,每条路都有一个盒子碰撞器组件。一旦玩家到达平台的尽头,它就会获得下一个样条组件。它可以工作,但有时它无法识别下一个样条线并使用相同的样条线,这会导致玩家 运行 他一次又一次通过的同一平台。
所以我没主意了。所以来到这里寻找解决方案或建议。帮助将不胜感激。
使用 OnTriggerEnter 或 OnTriggerEnter2D(如果您正在处理 2D 碰撞器)应该可以完成这项工作。但是正如您所说,您已经在使用对撞机,我认为这就是您尝试过的方法。
你可以试试:
OnTriggerStay
光线投射到地面物体。在此 link 中是一个二维示例:https://kylewbanks.com/blog/unity-2d-checking-if-a-character-or-object-is-on-the-ground-using-raycasts
您还可以对 3D 对象进行光线投射。
在这种情况下,您将使用它的目的基本上是将 "laser" 射入玩家下方的地面,并根据您告诉它击中的层抓住那里的物体。因此,如果您有一个名为 "Ground" 的层,您的平台是其中的一部分,那么它只能 return 来自该层的对象。
请记住经常进行光线投射,以便更新以反映游戏。
在这种情况下,我会简单地将我可能的段存储在一个列表中,然后当我到达末尾时获取下一个段并将当前的第一个段移动到末尾或您想要将其移动到列表中的任何位置。
也就是
public Segment currentSegment;
public List<Segment> segments;
void OnEndReached()
{
//Put the completed segmetn back in the list ... probably at the end or randomized anywhere but at the start
segments.Insert(segments.Count-1, currentSegment);
//Now get the next segment
currentSegment = segments[0];
segments.RemoveAt(0);
}
在这个模型中,你有一个简单的列表,它代表你的段出现的顺序,你总是将当前段设置为列表中的下一个,例如索引 0,完成后将它们放回列表中...将它们放在末尾,或者如果您想随机化顺序将它们插入除索引 0 之外的任何软件中,例如
segments.Insert(UnityEngine.Random.Range(1, segments.Count), currentSegment);
请注意,我从列表中删除了我所在的部分...该列表仅代表即将到来的顺序,在跑步游戏中我发现它很容易知道,例如这样我就可以根据表现、分数等重新设置、更改细分的属性。