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
CodePudding user response:
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 be1.0000001
or0.9999999
.This said anyway it makes no sense to use a
float
for a counter. You rather want to useint clickCounter;
Then you have a bunch of
,
there when calculatingp1
. Seems like this should have been wrapped in ap1 = new Vector3( .... )
However anyway the
p1
you calculate is noTransform
but would rather be aVector3
position. You don't need this as a field! Simply recalculate it later given the twoTransform
sp0
andp2
withinDrawQuadraticBezierCurve
and then you can simply make itvar p1 = (p0.position p2.position) * .05f - Vector3.up;
There is no need for that
LayerMask
andOverlapBox
which result you are not using anywhere at all. You already have a tag to check your hitCollider.gamObject
makes no sense. What you want is access theTransform
of the object you are colliding with which simply is theother.Transform
There is no need for
GetComponent<BCurve>
. This component already is theBCurve
so you could simply useenabled = 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 entireUpdate
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 bothTransform
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);
}
}
CodePudding user response:
derHugo gave an amazing answer, and could not have figured this out otherwise! I added a few things and it's working perfectly. The first thing was to make sure a new LineRender object is being created which a new poker object would automatically reference and have the start and end points be reset:
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;
}
The second was to move both the destruction of the current poker and the instantiation of a new poker to a LateUpdate, because the line wasn't being rendered:
private void LateUpdate()
{
if (destroy == true)
{
Instantiate(newPoker, transform.position, transform.rotation).transform.SetParent(GameObject.Find("MainCamera").transform);
Destroy(this.gameObject);
}
}
Again, thank you for the help derHugo!