I'm pretty new and I can't seem to get this to work. Players have a long pole and if they poke certain objects it starts a string-like connection to the next one they poke. These objects are tagged as "PokableObjects", and to poke players will click. I'm going to have hundreds of different pokable objects, and I want the script on the pole to work for all of them.
I think I'm misunderstanding how to reference only the objects being poked. I want the points of a Bezier Curve script, which are public Transforms, to adapt and become whatever "PokableObject" the player clicks.
This is my script:
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);
}
}
}
All help is much appreciated.
Thanks
First of all never compare float
values using ==
! Due to floating point (im)precision this might fail even though logically you think it would match.
E.g.
0.2f * 5f / 10f == 1.0f
is not necessarily true
because it might actually be 1.0000001
or 0.9999999
.
This said anyway it makes no sense to use a float
for a counter. You rather want to use
int clickCounter;
Then you have a bunch of ,
there when calculating p1
. Seems like this should have been wrapped in a p1 = new Vector3( .... )
However anyway the p1
you calculate is no Transform
but would rather be a Vector3
position. You don't need this as a field! Simply recalculate it later given the two Transform
s p0
and p2
within DrawQuadraticBezierCurve
and then you can simply make it
var p1 = (p0.position + p2.position) * .05f - Vector3.up;
There is no need for that LayerMask
and OverlapBox
which result you are not using anywhere at all. You already have a tag to check your hit
Collider.gamObject
makes no sense. What you want is access the Transform
of the object you are colliding with which simply is the other.Transform
There is no need for GetComponent<BCurve>
. This component already is the BCurve
so you could simply use
enabled = false;
However, as soon as you get the second point you are disabling the component so Update
will never be called again.
Either you want to draw the line only once with the positions in that moment so simply call DrawQuadraticBezierCurve
right away and remove your entire Update
method.
Or if your objects might continue moving and you want to continuously update the line then keep Update
but keep this component enabled.
In both cases you should only call DrawQuadraticBezierCurve
once you already have both Transform
s.
So all together it could look like this
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);
}
}