更改 public 基于碰撞的变换。根据 public 变换更改线渲染器点
Change public Transforms based on collision. Change line renderer points based on public transforms
我是新手,我似乎无法让它工作。玩家有一根长杆,如果他们戳某些物体,它就会开始与他们戳的下一个物体建立类似绳索的连接。这些对象被标记为“PokableObjects”,戳玩家会点击。我将拥有数百个不同的 pokeable 对象,我希望杆子上的脚本适用于所有这些对象。
我想我误解了如何只引用被戳的对象。我想要贝塞尔曲线脚本的点,即 public 转换,以适应并成为玩家点击的任何“PokableObject”。
这是我的脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BCurve : MonoBehaviour
{
//curved line renderer stuff
private LineRenderer lineRenderer;
public Transform p0;
public Transform p1;
public Transform p2;
//Object name detection stuff
bool m_Started;
public LayerMask m_LayerMask;
//Respawn Stuff
private float clickCounter;
public GameObject newPoker;
void Start()
{
lineRenderer = GetComponent<LineRenderer>();
m_Started = true;
clickCounter = 0;
}
private void OnTriggerStay(Collider other)
{
if (other.tag == "PokableObject")
{
Collider[] hitColliders = Physics.OverlapBox(gameObject.transform.position, transform.localScale / 2, Quaternion.identity, m_LayerMask);
if (Input.GetMouseButtonDown(0) && (clickCounter == 0))
{
p0 = Collider.gameObject.position;
clickCounter++;
}
else
{
p2 = Collider.gameObject.position;
//find midpoint between p0 & p2 then lower it's Y coordinate by 1
p1 = ((p0.position.x + p2.position.x) * .05f, ((p0.position.y + p2.position.y) * .05f) - 1), (p0.position.z + p2.position.z) * .05f;
//disable current object and spawn a new one so players can repeat
Instantiate(newPoker, transform.position, Quaternion.Euler(0, 0, 0));
GetComponent<BCurve>().enabled = false;
}
}
}
void Update()
{
DrawQuadraticBezierCurve(p0.position, p1.position, p2.position);
}
void DrawQuadraticBezierCurve(Vector3 p0, Vector3 p1, Vector3 p2)
{
lineRenderer.positionCount = 200;
float t = 0f;
Vector3 B = new Vector3(0, 0, 0);
for (int i = 0; i < lineRenderer.positionCount; i++)
{
B = (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
lineRenderer.SetPosition(i, B);
t += (1 / (float)lineRenderer.positionCount);
}
}
}
非常感谢所有帮助。
谢谢
首先从不使用==
比较float
值!由于浮点 (im) 精度,这可能会失败,即使在逻辑上你认为它会匹配。
例如
0.2f * 5f / 10f == 1.0f
不一定 true
因为它实际上可能是 1.0000001
或 0.9999999
.
无论如何,使用 float
作为计数器是没有意义的。你宁愿使用
int clickCounter;
那你在计算p1
的时候就有一堆,
了。好像这应该被包裹在 p1 = new Vector3( .... )
但是无论如何,您计算出的 p1
不是 Transform
而是 Vector3
位置。你不需要这个作为一个领域!给定 DrawQuadraticBezierCurve
中的两个 Transform
s p0
和 p2
之后再简单地重新计算它,然后你就可以简单地使它成为
var p1 = (p0.position + p2.position) * .05f - Vector3.up;
不需要 LayerMask
和 OverlapBox
,结果您根本没有在任何地方使用。您已经有一个标签来检查您的命中
Collider.gamObject
没有意义。你想要的是访问你正在与之碰撞的对象的 Transform
,它只是 other.Transform
不需要GetComponent<BCurve>
。这个组件已经是 BCurve
所以你可以 简单地使用
enabled = false;
但是,一旦您获得第二个点,您就会禁用该组件,因此 Update
将永远不会被再次调用。
要么你只想用那一刻的位置画线一次,那么只需立即调用 DrawQuadraticBezierCurve
并删除你的整个 Update
方法。
或者,如果您的对象可能会继续移动并且您希望不断更新线条,则保持 Update
但保持此组件处于启用状态。
在这两种情况下,您应该只在已经拥有两个 Transform
时才调用 DrawQuadraticBezierCurve
。
所以总的来说它看起来像这样
public class BCurve : MonoBehaviour
{
//curved line renderer stuff
[SerializeField] private LineRenderer lineRenderer;
public Transform start;
public Transform end;
//Respawn Stuff
public GameObject newPoker;
// I would make this adjustable via the Inspctor
[SerializeField] private int positionCount = 200;
private void Start()
{
if(!lineRenderer) lineRenderer = GetComponent<LineRenderer>();
}
private void OnTriggerStay(Collider other)
{
if(start && end)
{
return;
}
// in general rather use CompareTag instead of ==
// the second silently fails in case of typos and is slightly slower
if (other.CompareTag("PokableObject"))
{
// Then you ALWAYS want to check fr the mouse click, not only
// if the counter is 0
if(Input.GetMouseButtonDown(0))
{
// you rather want to check here
if (!start)
{
start = other.transform;
}
// I would add a check though to not enable to click on the same object twice
else if (other.transform != start)
{
end = other.transform;
// If you anyway want the line to be drawn only ONCE with the current positions
// then directly call this here and delete Update completely
//DrawQuadraticBezierCurve(start.position, end.position);
//enabled = false;
// or even
//Destroy(this);
Instantiate(newPoker, transform.position, Quaternion.Euler(0, 0, 0));
}
}
}
}
private void Update()
{
if(start && end)
{
DrawQuadraticBezierCurve(start.position, end.position);
}
}
private void DrawQuadraticBezierCurve(Vector3 p0, Vector3 p2)
{
lineRenderer.positionCount = positionCount;
var inversePositionCount = 1f / positionCount;
var t = 0f;
var p1 = (p0.position + p2.position) * .05f - Vector3.up;
var positions = new Vector3[positionCount];
for (int i = 0; i < lineRenderer.positionCount; i++)
{
var B = (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
positions[i] = B;
t += inversePositionCount;
}
// Note: It is WAY cheaper to call this and set all positions at once
lineRenderer.SetPositions(positions);
}
}
derHugo 给出了一个惊人的答案,否则无法想出这个!我添加了一些东西,它工作得很好。第一件事是确保创建一个新的 LineRender 对象,新的扑克对象将自动引用该对象并重置起点和终点:
private void Start()
{
if (!lineRenderer) lineRenderer = GetComponent<LineRenderer>();
lineRenderer = Instantiate(lineObject, transform.position, Quaternion.Euler(0, 0, 0)).GetComponent<LineRenderer>();
start = null;
end = null;
destroy = false;
}
第二个是将当前扑克牌的销毁和新扑克牌的实例化都移至 LateUpdate,因为该行未被渲染:
private void LateUpdate()
{
if (destroy == true)
{
Instantiate(newPoker, transform.position, transform.rotation).transform.SetParent(GameObject.Find("MainCamera").transform);
Destroy(this.gameObject);
}
}
再次感谢derHugo的帮助!
我是新手,我似乎无法让它工作。玩家有一根长杆,如果他们戳某些物体,它就会开始与他们戳的下一个物体建立类似绳索的连接。这些对象被标记为“PokableObjects”,戳玩家会点击。我将拥有数百个不同的 pokeable 对象,我希望杆子上的脚本适用于所有这些对象。
我想我误解了如何只引用被戳的对象。我想要贝塞尔曲线脚本的点,即 public 转换,以适应并成为玩家点击的任何“PokableObject”。
这是我的脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BCurve : MonoBehaviour
{
//curved line renderer stuff
private LineRenderer lineRenderer;
public Transform p0;
public Transform p1;
public Transform p2;
//Object name detection stuff
bool m_Started;
public LayerMask m_LayerMask;
//Respawn Stuff
private float clickCounter;
public GameObject newPoker;
void Start()
{
lineRenderer = GetComponent<LineRenderer>();
m_Started = true;
clickCounter = 0;
}
private void OnTriggerStay(Collider other)
{
if (other.tag == "PokableObject")
{
Collider[] hitColliders = Physics.OverlapBox(gameObject.transform.position, transform.localScale / 2, Quaternion.identity, m_LayerMask);
if (Input.GetMouseButtonDown(0) && (clickCounter == 0))
{
p0 = Collider.gameObject.position;
clickCounter++;
}
else
{
p2 = Collider.gameObject.position;
//find midpoint between p0 & p2 then lower it's Y coordinate by 1
p1 = ((p0.position.x + p2.position.x) * .05f, ((p0.position.y + p2.position.y) * .05f) - 1), (p0.position.z + p2.position.z) * .05f;
//disable current object and spawn a new one so players can repeat
Instantiate(newPoker, transform.position, Quaternion.Euler(0, 0, 0));
GetComponent<BCurve>().enabled = false;
}
}
}
void Update()
{
DrawQuadraticBezierCurve(p0.position, p1.position, p2.position);
}
void DrawQuadraticBezierCurve(Vector3 p0, Vector3 p1, Vector3 p2)
{
lineRenderer.positionCount = 200;
float t = 0f;
Vector3 B = new Vector3(0, 0, 0);
for (int i = 0; i < lineRenderer.positionCount; i++)
{
B = (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
lineRenderer.SetPosition(i, B);
t += (1 / (float)lineRenderer.positionCount);
}
}
}
非常感谢所有帮助。
谢谢
首先从不使用
==
比较float
值!由于浮点 (im) 精度,这可能会失败,即使在逻辑上你认为它会匹配。例如
0.2f * 5f / 10f == 1.0f
不一定
true
因为它实际上可能是1.0000001
或0.9999999
.无论如何,使用
float
作为计数器是没有意义的。你宁愿使用int clickCounter;
那你在计算
p1
的时候就有一堆,
了。好像这应该被包裹在p1 = new Vector3( .... )
但是无论如何,您计算出的
p1
不是Transform
而是Vector3
位置。你不需要这个作为一个领域!给定DrawQuadraticBezierCurve
中的两个Transform
sp0
和p2
之后再简单地重新计算它,然后你就可以简单地使它成为var p1 = (p0.position + p2.position) * .05f - Vector3.up;
不需要
LayerMask
和OverlapBox
,结果您根本没有在任何地方使用。您已经有一个标签来检查您的命中Collider.gamObject
没有意义。你想要的是访问你正在与之碰撞的对象的Transform
,它只是other.Transform
不需要
GetComponent<BCurve>
。这个组件已经是BCurve
所以你可以 简单地使用enabled = false;
但是,一旦您获得第二个点,您就会禁用该组件,因此
Update
将永远不会被再次调用。要么你只想用那一刻的位置画线一次,那么只需立即调用
DrawQuadraticBezierCurve
并删除你的整个Update
方法。或者,如果您的对象可能会继续移动并且您希望不断更新线条,则保持
Update
但保持此组件处于启用状态。在这两种情况下,您应该只在已经拥有两个
Transform
时才调用DrawQuadraticBezierCurve
。
所以总的来说它看起来像这样
public class BCurve : MonoBehaviour
{
//curved line renderer stuff
[SerializeField] private LineRenderer lineRenderer;
public Transform start;
public Transform end;
//Respawn Stuff
public GameObject newPoker;
// I would make this adjustable via the Inspctor
[SerializeField] private int positionCount = 200;
private void Start()
{
if(!lineRenderer) lineRenderer = GetComponent<LineRenderer>();
}
private void OnTriggerStay(Collider other)
{
if(start && end)
{
return;
}
// in general rather use CompareTag instead of ==
// the second silently fails in case of typos and is slightly slower
if (other.CompareTag("PokableObject"))
{
// Then you ALWAYS want to check fr the mouse click, not only
// if the counter is 0
if(Input.GetMouseButtonDown(0))
{
// you rather want to check here
if (!start)
{
start = other.transform;
}
// I would add a check though to not enable to click on the same object twice
else if (other.transform != start)
{
end = other.transform;
// If you anyway want the line to be drawn only ONCE with the current positions
// then directly call this here and delete Update completely
//DrawQuadraticBezierCurve(start.position, end.position);
//enabled = false;
// or even
//Destroy(this);
Instantiate(newPoker, transform.position, Quaternion.Euler(0, 0, 0));
}
}
}
}
private void Update()
{
if(start && end)
{
DrawQuadraticBezierCurve(start.position, end.position);
}
}
private void DrawQuadraticBezierCurve(Vector3 p0, Vector3 p2)
{
lineRenderer.positionCount = positionCount;
var inversePositionCount = 1f / positionCount;
var t = 0f;
var p1 = (p0.position + p2.position) * .05f - Vector3.up;
var positions = new Vector3[positionCount];
for (int i = 0; i < lineRenderer.positionCount; i++)
{
var B = (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
positions[i] = B;
t += inversePositionCount;
}
// Note: It is WAY cheaper to call this and set all positions at once
lineRenderer.SetPositions(positions);
}
}
derHugo 给出了一个惊人的答案,否则无法想出这个!我添加了一些东西,它工作得很好。第一件事是确保创建一个新的 LineRender 对象,新的扑克对象将自动引用该对象并重置起点和终点:
private void Start()
{
if (!lineRenderer) lineRenderer = GetComponent<LineRenderer>();
lineRenderer = Instantiate(lineObject, transform.position, Quaternion.Euler(0, 0, 0)).GetComponent<LineRenderer>();
start = null;
end = null;
destroy = false;
}
第二个是将当前扑克牌的销毁和新扑克牌的实例化都移至 LateUpdate,因为该行未被渲染:
private void LateUpdate()
{
if (destroy == true)
{
Instantiate(newPoker, transform.position, transform.rotation).transform.SetParent(GameObject.Find("MainCamera").transform);
Destroy(this.gameObject);
}
}
再次感谢derHugo的帮助!