How to determine thickness vs width? (Using Raycasting) - unity3d

Visual aids
Thickness vs width: here
Please view the short gif.
Thickness here is different from width as there are multiple walls as there are outer and inner cylinders. Thickness is the measurement of the distance between the outer/inner wall of any side of the cylinder where as thickness is the distance from one end to the other encompassing the hollow space between.
Quick synopsis on the gifs provided
-On every click the origin point (blue) and destination point (orange) orbs are created to denote where the user clicks and the interpreted end point used to calculate the distance (displayed on the GUI).
The origin defines where the user clicks on the surface of an objects collider and the destination defines the point, perpendicular with the world Y axis of the origin, where a second ray cast towards the first ray, hits the other side of the collider.
Current:
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
//obtain the vector where the ray hit the collider.
hitPoint = hit.point; //origin point
//offset the ray, keeping it along the XZ plane of the hit
Vector3 offsetDirection = -1 * hit.normal;
//offset a long way, minimum thickness of the object
ray.origin = hit.point + offsetDirection * 100;
//point the ray back at the first hit point
ray.direction = (hit.point - ray.origin).normalized;
//raycast all, because there might be other objects in the way
RaycastHit[] hits = Physics.RaycastAll(ray);
foreach (RaycastHit h in hits)
{
if (h.collider == hit.collider)
{
hitBack = h.point; //destination point
}
}
}
Currently, width is the functionality in place. I want to calculate thickness without having to go inside of an object (as seen in the gif).
Amazing reference
http://answers.unity3d.com/questions/386698/detecting-how-many-times-a-raycast-collides-with-a.html
This guy basically had the same question as me and has a solution that could possibly work. I'm not sure how Linecasting works vs Raycasting.

Keep:
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
//obtain the vector where the ray hit the collider.
hitPoint = hit.point; //origin point
//offset the ray, keeping it along the XZ plane of the hit
Vector3 offsetDirection = -1 * hit.normal;
//offset a long way, minimum thickness of the object
ray.origin = hit.point + offsetDirection * 100;
//point the ray back at the first hit point
ray.direction = (hit.point - ray.origin).normalized;
Replace:
//raycast all, because there might be other objects in the way
RaycastHit[] hits = Physics.RaycastAll(ray);
foreach (RaycastHit h in hits)
{
if (h.collider == hit.collider)
{
hitBack = h.point; //destination point
}
}
With (credits to MirrorMirror's insightful post, and #ryemoss for his instrumental advice and assistance):
int counter = 0;
bool calculating = false; //set this to true on click
Vector3 Point, PreviousPoint, Goal, Direction;
Point = ray.origin;
Goal = hit.point;
Direction = ray.direction;
PreviousPoint = Vector3.zero;
while (calculating == true)
{
counter++;
RaycastHit hit2;
if (Physics.Linecast(Point, Goal, out hit2))
{
if(counter > 100)
{
hitBack = hitPoint;
counter = 0;
calculating = false;
break;
}
PreviousPoint = hit2.point;
Point = hit2.point + (Direction / 10000f);
}
else
{
if (PreviousPoint == Vector3.zero)
hitBack = hitPoint;
else
hitBack = PreviousPoint;
calculating = false;
counter = 0;
}
}
Linecast vs Raycast
With a raycast you set the start point, the direction, and the distance to check in that direction, with a linecast you simply set start and end points and it checks between those 2 points.
So, if you know the end destination specifically, use linecast, if you want to check in a specific direction but have no specific end point, use raycast.
Solution
First, use the initial raycast to obtain the first point, hit.point. Then, set the ray.origin to a point in world space outside the collider (the collider of the object we first collided with to obtain hit.point), and set the ray.direction to face the ray back at the first point, hit.point.
Finally, use a while loop to create a new linecast, at ray.origins new position (updated each time through the while loop until a linecast reaches hit.point), each time a collision with the object occurs until a linecast reaches hit.point. Once hit.point has been reached, it means every surface of the object was hit and on each hit, a new line was created until a line reached the first initial point, hit.point. To calculate thickness, take the distance between the first hit, hit.point, and the hit previous to the reverse linecast hitting hit.point, PreviousPoint.
UPDATE
1-Revise the code to properly handle 1-sided objects (ex: Planes).
2-Added counter to prevent special cases in which calculation not possible.
3-Improve readability.

Related

Unity rotation calculation

//RAYCAST
RaycastHit hit = new RaycastHit();
if(Physics.Raycast(impact.position, bloom, out hit, 1000f, canBeShot))
{
GameObject newBulletHole = Instantiate(bulletHolePrefab, hit.point + hit.normal * 0.001f, Quaternion.identity) as GameObject;
newBulletHole.transform.LookAt(hit.point + hit.normal);
Destroy(newBulletHole, 5f);
}
//Bullet
bulletSpawnPoint = GameObject.Find("BulletSpawn").transform;
var bullet = Instantiate(bulletPrefab, bulletSpawnPoint.position, **bulletSpawnPoint.rotation**);
bullet.GetComponent<Rigidbody>().velocity = bulletSpawnPoint.forward * loadout[currentIndex].bulletSpeed;
I need to get the perfect "bulletSpawnPoint.rotation" depending of my bullet hole created by the raycast hit. Thanks
enter image description here
So you want to spawn the bullet and then use Rigitbody to move it to the newBulletHole gameobject.
Easy way would be to store position of the bullet hole and then pass it into LookAt method of the bullet Transform before adding velocity.
A bit more advanced way (and this is what you are asking in your question, afaik): calculate the direction vector (bulletSpawnPoint.transform.position - newBulletHole.transform.position) and then get Quaternion (i.e. rotation) with LookRotation

Dragging an object using perspective camera

I'm trying to write a function so when I hold the mouse down I can drag the game object and then I latch it into target.
I'm using a perspective,vertical camera with physical Camera checked and with focal length 35. Also I don't know if this is important but I am dragging the object in the Y and Z axis.
The code I'm using drags the objects too close to the camera. How can I fix this?
private void OnMouseDrag()
{
if (IsLatched)
{
print($"is latched:{IsLatched}");
return;
}
float distance = -Camera.main.transform.position.z + this.transform.position.z;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Vector3 rayPoint = ray.GetPoint(distance);
this.transform.position = rayPoint;
print($"{name} transform.position:{transform.position}");
this.gameObject.GetComponent<Rigidbody>().isKinematic = true;
isHeld = true;
}
You are calculating the distance by subtracting the z coordinates, then taking a point along the click-ray with that distance. That will not be a point on the same z coordinate. If you want to keep one component constant, I would rather intersect the ray with an XY plane.
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
float Zplane = this.transform.position.z; // example. use any Z from anywhere here.
// find distance along ray.
float distance = (Zplane-ray.origin.z)/ray.direction.z ;
// that is our point
Vector3 point = ray.origin + ray.direction*distance;
// Z will be equal to Zplane, unless considering rounding errors.
// but can remove that error anyway.
point.z = Zplane;
this.transform.position = point;
Could this help? Would work similar with any other plane.

Find point at distance along a vector

Given a known direction and range I'm trying to calculate the 3D position along the vector at that range from the starting point.
To do this I'm basically following the Unity manual and the solution offered in various other questions about this topic:
Take your direction, normalise it and multiply by the required
distance.
This isn't working for me but I can't figure out what's wrong. Here's my code that should draw a line from the starting point towards the mouse cursor position, ending at the given distance from the starting point:
IEnumerator DrawLineToMouse(float range)
{
RayCastHit hit;
Vector3 endPos;
float rangeSquared = Mathf.Sqrt(range);
while (Input.GetButton("Fire1"))
{
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, 2000))
{
endPos = (hit.point - startPos).normalized * rangeSquared; // startPos is the starting point of the line to be drawn
Debug.DrawLine(startPos, hit.point, Color.green, Time.deltaTime);
Debug.DrawLine(startPos, endPos, Color.red, Time.deltaTime);
}
yield return null;
}
}
For some reason the direction seems off. In this picture the green Debug.Draw line is direct from the starting point to the mouse position, the red line is to the calculated vector:
I've tried both perspective and orthographic cameras, and I've tried varying the starting point, the problem is the same. I don't know what to try regarding the code because everything I've read suggests it should work.
What could be the problem?
Currently your endPos is at the vector distance and direction but starting from 0,0,0
Instead it should rather be
endPos = startPos + (hit.point - startPos).normalized * rangeSquared;
or for better understanding
var distance = rangeSquared;
var direction = (hit.point - startPos).normalized;
endPos = startPos + direction * distance;

Translated grapple physics from Processing to Unity to get different results

tl;dr Moving my game from Processing to Unity. Code responsible for grappling by manually changing the player's velocity doesn't work even though it's basically copy/pasted.
Hi, I've been working on a project of mine over the summer on Processing, and last week I decided to translate it over to Unity.
What I'm having a problem with is the grapple/rope physics. It's supposed to essentially keep the player inside a circle (made by the endpoint of the rope and the length of the rope). When the player falls outside of this circle, the player's position is moved back to the edge of the circle and the player's velocity is set to tangent of the circle.
Decreasing the length of the rope while swinging is supposed to speed you up. (See Floating Point)
On Processing, it works perfectly just as described above, but when I basically copy/pasted the code into unity it loses momentum too quickly (always ends up stopping at the same angle on the other side the player started on). Here is the code for both (run on each physics frame):
(I've also made some images to describe the motion that both versions produce)
Processing
Code
(warning: bad and redundant)
physics update:
exists = (endPoint != null);
if(lgth<=0) lgth = 1;
if(exists) {
currentLength = phs.position.dist(endPoint);
if(currentLength > lgth) {
float angle = getAngle(endPoint, phs.position);
phs.addPosition(abs(currentLength - lgth), angle);
float angleBetween = getAngle(phs.position, endPoint);
PVector relativeVelocity = new PVector(phs.velocity.x + phs.position.x, phs.velocity.y + phs.position.y);
float displacement = angleBetween - 90;
Line l1 = lineFromTwoPoints(relativeVelocity, endPoint);
Line l2 = lineFromAngle(phs.position, displacement);
PVector pointToLerpTo = intersection(l1, l2);
if(pointToLerpTo!=null) {
phs.velocity.x = pointToLerpTo.x-phs.position.x;
phs.velocity.y = pointToLerpTo.y-phs.position.y;
}
else phs.velocity.mult(0);
}
}
when the player shortens the rope, speed increases:
if(exists) {
float newLgth = lgth-d;
float distance = getDistance(phs.position, endPoint);
if(distance > newLgth) {
float ratio = (distance-newLgth)/lgth;
phs.velocity.setMag(phs.velocity.mag()*(1+ratio));
}
lgth = newLgth;
}
Motion from Processing (good)
Player starts by moving downwards at left edge of rope circle. Doesn't lose speed and continues going around multiple times until gravity slows it down.
Unity
Code
both code blocks from above are handled in the same place here, under FixedUpdate() (problematic part seems to be the velocity section)
distance = Vector2.Distance(transform.position, endpoint);
if(connected && distance > length) {
//lerp position -> endpoint// keep gameObject within length of the rope
float posLerpAmount = (distance - length) / distance;
transform.position = Vector2.Lerp(transform.position, endpoint, posLerpAmount);
//'lerp' velocity -> endpoint// keep the velocity locked to the tangent of the circle around the endpoint
Vector2 relativeVelocity = GetComponent<Rigidbody2D>().velocity + (Vector2)transform.position;
Line l1 = Geometry.LineFromTwoPoints(relativeVelocity, endpoint);
Line l2 = Geometry.LineFromAngle(transform.position, Geometry.GetAngle(endpoint, transform.position) - 90);
if(!Geometry.AreParallel(l1, l2)) {
Vector2 pointToLerpTo = Geometry.Intersection(l1, l2) - (Vector2)transform.position;
GetComponent<Rigidbody2D>().velocity = pointToLerpTo;
}
else GetComponent<Rigidbody2D>().velocity = new Vector2(0, 0);
//increases the magnitude of the velocity based on how far the rope moved the object's position
float ratio = (distance - length) / length;
GetComponent<Rigidbody2D>().velocity *= 1 + ratio;
distance = length;
}
Motion from Unity (bad)
Player starts by moving downward at left edge of rope circle. Gains a little bit of speed from gravity, then will always stop 45 degrees on the other side where it started (regardless of starting speed), then slowly fall back down to the bottom of the circle.
If anyone needs me to explain the Geometry class (lines, intersections) then I can, but I think it's mostly self-explanatory. Otherwise, I think I explained this the best I could. Thanks in advance for any help.
(also, StackOverflow isn't letting me add the Unity2d tag so I guess I gotta settle for Unity3d)
I found out that Rigidbody2D.velocity.magnitude is not how far the object moves every physics update. This is what was causing the issue, because the Processing code was based off the velocity being added directly to the position every update.
To fix this, what I did was do the same geometry, but scale the velocity to the % of how much of the velocity was actually 'used' (it usually travels 2% of the actual velocity vector).
Here is the final code in Unity: (this time I'm showing the fill FixedUpdate(), with the irrelevant parts removed)
float lastMagnitude;
Vector2 lastPosition;
void FixedUpdate() {
float velocityMoved = Vector2.Distance(lastPosition, transform.position) / lastMagnitude;
Debug.Log(velocityMoved * 100 + "%"); //this is usually 2%
bool shortenedRope = false;
if(Input.GetButton("Shorten Rope")) {
shortenedRope = true;
length -= ropeShortenLength;
}
distance = Vector2.Distance(transform.position, endpoint);
if(connected && distance > length) {
//lerp position -> endpoint// keep gameObject within length of the rope
float posLerpAmount = (distance - length) / distance;
transform.position = Vector2.Lerp(transform.position, endpoint, posLerpAmount);
//'lerp' velocity -> endpoint// keep the velocity locked to the tangent of the circle around the endpoint
Vector2 adjustedVelocity = rigidbody.velocity * velocityMoved;
Vector2 relativeVelocity = adjustedVelocity + (Vector2)transform.position;
Line l1 = Geometry.LineFromTwoPoints(relativeVelocity, endpoint);
Line l2 = Geometry.LineFromAngle(transform.position, Geometry.GetAngle(endpoint, transform.position) - 90);
if(!Geometry.AreParallel(l1, l2)) {
Vector2 pointToLerpTo = Geometry.Intersection(l1, l2) - (Vector2)transform.position;
rigidbody.velocity = pointToLerpTo;
rigidbody.velocity /= velocityMoved;
}
else rigidbody.velocity = new Vector2(0, 0);
//'give back' the energy it lost from moving it's position
if(shortenedRope) {
float ratio = (distance - length) / length;
rigidbody.velocity *= 1 + ratio;
}
distance = length;
}
lastPosition = transform.position;
lastMagnitude = rigidbody.velocity.magnitude;
}
EDIT: Recently learned that it is better to use Time.deltaFixedTime instead of the variable I made velocityMoved, since Time.deltaFixedTime is already calculated.

How to Calculate Target Destination

I'm having a bit of trouble figuring this one out. What I'm trying to achieve is a sort of tackling motion. The player lunges at the target from a distance.
The diagram shows the set up. The blue diamond is the player and the red thing is the target. The purple box is the renderer bounds of the targets SkinnedMeshRenderer. I'm using renderer bounds because some target's mesh are much larger than other. Currently, the player is shooting to the orange star...which is unrealistic. I want him to, no matter what way the target is facing, always target the closest point of the target relative to his position...in the diagram's case that would be the brown star. Here's the code I've been using...
public IEnumerator Blitz()
{
rigidbody.velocity = Vector3.zero; //ZERO OUT THE RIGIDBODY VELOCITY TO GET READY FOR THE BLITZ
SkinnedMeshRenderer image = target.GetComponentInChildren<SkinnedMeshRenderer>();
Vector3 position = image.renderer.bounds.center + image.renderer.bounds.extents;
position.y = target.transform.position.y;
while(Vector3.Distance(transform.position, position) > 0.5f)
{
transform.position = Vector3.Lerp(transform.position, position, Time.deltaTime * 10);
yield return null;
}
Results(); //IRRELEVANT TO THIS PROBLEM. THIS CALCULATES DAMAGE.
Blitz.Stop(); //THE PARTICLE EFFECT ASSOCIATED WITH THE BLITZ.
GetComponent<Animator>().SetBool(moveName, false); //TRANSITIONS OUT OF THE BLITZ ANIMATION
GetComponent<Input>().NotAttacking(); //LET'S THE INPUT SCRIPT KNOW THE PLAYER CAN HAVE CONTROL BACK.
}
//Get the derection to tarvel in and normalize it to length of 1
Vector3 Direction = (Target - transform.position).normalized
With Direction, you can do many things. For example, you can multiply the direction by your speed and add that to your position.
transform.position += Direction * MoveSpeed;
If you want to get to the closest point, you can use the handy Collider.ClosestPointOnBounds method.
Target = TargetObject.GetComponent<Collider>().ClosestPointOnBounds(transform.position)
and plug the Target into the code used to get the direction.
Alternatively, you can use Vector3.Lerp without getting a direction since it's just interpolating.
transform.position = Vector3.Lerp(Target,transform.position,time.DeltaTime);
For stopping at the target point, you can use the arrival behavior.
//Declare the distance to start slowing down
float ClosingDistance = Speed * 2;
//Get the distance to the target
float Distance = (Target - transform.position).magnitude;
//Check if the player needs to slow down
if (Distance < ClosingDistance)
{
//If you're closer than the ClosingDistance, move slower
transform.position += Direction * (MoveSpeed * Distance / ClosingDistance);
}
else{
//If not, move at normal speed
transform.position += Directino * MoveSpeed;
}