How to increase field of view while maintaining range - c#

So I have the following function to generate 3 vector points let creates a triangle:
private Vector3[] GetFieldOfViewPoint() {
float Range = 3f;
float Angle = 45;
Vector3 offset = new Vector3(0, Range, 0);
Quaternion rotation1 = Quaternion.Euler(0, 0, -Angle / 2);
Quaternion rotation2 = Quaternion.Euler(0, 0, Angle / 2);
return new Vector3[3] {
transform.position,
(rotation1 * offset) + transform.position,
(rotation2 * offset) + transform.position
};
}
The issue I am having is that when I increase the angle, the rang decrease which does make some sense to me.
For example, 45 angle:
135 angle:
The issue I need help with is how can I modify the calculation in order to keep the range line (the furtherest straight green line) consistent regardless of the angle (which will be capped at 170 degrees)?
I can get the range to move with the angle but doing random calculations but it is not consistent and just does not work (at least with the random number I have tried). I also thought about hypotenuse formula however I only know 1 sides length (which is the range).
Any help with this would be awesome.

Here:
Script:
using UnityEngine;
public class TrignometryTest : MonoBehaviour
{
public float range;
[Range(5,170)]
public float angle;
Vector3 size = Vector3.one;
Vector3[] GetFOVPoints(float _range, float _angleInDegrees)
{
Vector3 rightPoint, leftPoint;
float halfAngle = _angleInDegrees/2;
rightPoint = new Vector3(_range * Mathf.Tan(halfAngle * Mathf.Deg2Rad), _range, 0);
leftPoint = new Vector3(-_range * Mathf.Tan(halfAngle * Mathf.Deg2Rad), _range, 0);
Vector3[] points = { transform.position, leftPoint, rightPoint};
return points;
}
void OnDrawGizmos()
{
var points = GetFOVPoints(range, angle);
Gizmos.DrawCube(points[0], size);
Gizmos.DrawCube(points[1], size);
Gizmos.DrawCube(points[2],size);
Gizmos.DrawLine(points[0], points[1]);
Gizmos.DrawLine(points[0], points[2]);
Gizmos.DrawLine(points[1], points[2]);
Gizmos.DrawLine(points[0],Vector3.up * range);
}
}
OutPut:
Hope this helps :)

Related

How to scale and rotate a sprite between two points based on distance

I am working on a project in unity and I have a small circle that shows how much power will be applied to a ball and an arrow that shows the direction.
The circle and arrow are meant to scale up to a max distance; the arrow scales but it is too big (takes up half the screen) and doesn't rotate properly; the circle does not scale at all. I have tried to change the local scale of the arrow and messed around with the various values but I am not sure what to really do. The arrow tends to only face the correct direction when the cursor is in the top left and the arrow is in the bottom right.
The two points, point A and B are two empty objects; point B is attached to the ball and pointA follows the mouse. When the ball is clicked on and the cursor is dragged away pointB moves in the opposite direction; I am trying to get the arrow to face pointB at all times or point at pointB from the opposite side of the ball.
Everything except for the arrow and circle rotating and scaling works. I'm fairly new to code and don't understand Mathf.Log. The arrow rotate and scale code is commented out as I am currently trying to get the circle to work.
If you can point me in the right direction or help with just one of these issues I'd greatly appreciate it.
public class PlayerBallHit : MonoBehaviour
{
private GameObject mousePointA;
private GameObject mousePointB;
private GameObject arrow;
private GameObject circle;
// calc distance
private float currDistance;
public float maxDistance = 3f;
private float spaceLimit;
private float shootPower;
public float shootPowervar;
public Vector2 shootDirection;
void Start()
{
}
void Awake()
{
mousePointA = GameObject.FindGameObjectWithTag("PointA");
mousePointB = GameObject.FindGameObjectWithTag("PointB");
arrow = GameObject.FindGameObjectWithTag("Stick");
circle = GameObject.FindGameObjectWithTag("Circle");
}
private void OnMouseDrag()
{
currDistance = Vector2.Distance(mousePointA.transform.position, transform.position);
if (currDistance <= 3f)
{
spaceLimit = currDistance;
}
else
{
spaceLimit = maxDistance;
}
// Direction of Hit and Circle
StrDirMarkers();
// calc Power & Direction
shootPower = Mathf.Abs(spaceLimit) * shootPowervar;
Vector3 dimxy = mousePointA.transform.position - transform.position;
float difference = dimxy.magnitude;
mousePointB.transform.position = (Vector3)transform.position + ((dimxy / difference) * currDistance * -1);
mousePointB.transform.position = new UnityEngine.Vector3(mousePointB.transform.position.x, mousePointB.transform.position.y);
shootDirection = (Vector2)Vector3.Normalize(mousePointA.transform.position - transform.position);
}
void OnMouseUp()
{
//arrow.GetComponent<SpriteRenderer>().enabled =false;
circle.GetComponent<SpriteRenderer>().enabled = false;
Vector2 push = shootDirection * shootPower *-1;
GetComponent<Rigidbody2D>().AddForce(push, ForceMode2D.Impulse);
}
private void StrDirMarkers()
{
//arrow.GetComponent<SpriteRenderer>().enabled = true;
circle.GetComponent<SpriteRenderer>().enabled = true;
// calc position
/*
if (currDistance <= maxDistance)
{
arrow.transform.position = new Vector2((2f * transform.position.x) - mousePointA.transform.position.x, (2f * transform.position.y) - mousePointA.transform.position.y);
}
else
{
Vector2 dimxy = mousePointA.transform.position - transform.position;
float difference = dimxy.magnitude;
arrow.transform.position = (Vector2)transform.position + ((dimxy / difference) * maxDistance * -1);
arrow.transform.position = new UnityEngine.Vector2(arrow.transform.position.x, arrow.transform.position.y);
}
*/
circle.transform.position = transform.position + new Vector3(0, 0, 0.04f);
Vector3 dir = mousePointA.transform.position - transform.position;
float rot;
if(Vector3.Angle(dir, transform.forward)> 90)
{
rot = Vector3.Angle(dir, transform.right);
}else
{
rot = Vector3.Angle(dir, transform.right) * -1;
}
//arrow.transform.eulerAngles = new Vector3(0, 0, rot);
// scale arrow
float scaleX = Mathf.Log(1 + spaceLimit / 10000f, 2f) * 0.05f;
float scaleY = Mathf.Log(1 + spaceLimit / 10000f, 2f) * 0.05f;
//arrow.transform.localScale = new Vector3(1 + scaleX, 1 + scaleY, 0.001f);
circle.transform.localScale = new Vector3(1 + scaleX, 1 + scaleY, 0.001f);
}
}
try to use this code for scaling and rotate the arrow
Vector3 dir = mousePointA.transform.position - transform.position;
float rot;
if (mousePointA.transform.position.y >= transform.position.y)
{
rot = Vector3.Angle(dir, transform.position) * -1;
}
else
{
rot = Vector3.Angle(dir, transform.position);
}
arrow.transform.eulerAngles = new Vector3(0, 0, rot);
// scale arrow
float scaleValue = Vector3.Distance(mousePointA.transform.position,
transform.position);
arrow.transform.localScale = new Vector3(1 + scaleValue,
arrow.transform.localScale.y, 1);
circle.transform.localScale = new Vector3(1 + scaleValue * 0.05f, 1 + scaleValue *
0.05f, 0.001f);

eulerAngles from Vector3 results in strange rotation

I'm trying to change the eulerAngle X of a camera rotation while keeping y and z at 0.
However, the following IEnumerator results in strange eulerAngles like 10, -74.653, 0.
I don't understand how the y value can change within the following function:
private IEnumerator LerpCameraNormalRot()
{
float duration = 0.5f;
for (float t = 0f; t < duration; t += Time.deltaTime)
{
float f = Mathf.Lerp(camera.transform.eulerAngles.x, 10, t / duration);
Vector3 vNew = new Vector3(f, 0, 0);
camera.transform.eulerAngles = vNew;
yield return 0;
}
}
Isn't this strange? I never change the Y and Z values!
I only want to change the X rotation (eulerAngle.x) from its current value to 10.
Thank you for the help!
You are setting the eulerAngles but that will result in different localEulerAngles which are indicated by the red box in your image. If the object has any ancestors that are rotated, they will probably not match!
To fix your immediate problem, you could use localEulerAngles instead of eulerAngles:
float f = Mathf.Lerp(camera.transform.localEulerAngles.x, 10, t / duration);
Vector3 vNew = new Vector3(f, 0, 0);
camera.transform.localEulerAngles= vNew;
The problem with this is that it does not account for the ability to go from 359 degrees around to 0 degrees. Better yet would be to use Quaternion.Euler and Quaternion.Slerp to use Quaternions rather than Euler angles:
private IEnumerator LerpCameraNormalRot()
{
float duration = 0.5f;
Quaternion startRotation = camera.transform.localRotation;
Quaternion goalRotation = Quaternion.Euler(10f, 0f, 0f);
for (float t = 0f; t < duration; t += Time.deltaTime)
{
camera.transform.localRotation = Quaternion.Slerp(startRotation, goalRotation, t/duration);
yield return 0;
}
camera.transform.localRotation = goalRotation;
}
Unity uses two representation of rotation, position, eulerAngles, etc
one is in worldspace
the other is in local space
The values not including local in their name are in world space.
If your object has than any parents that are rotated/scaled/translated you won't see the values you set in Unity since the Transform inspector displays the local coordinates.
If you want to set the local coordinates to the exact values instead use localPosition, localRotation or localEulerAngles instead.
For me it looks like you want to rotate the Camera around its local x axis by 10° in 0.5 secons.
So I think you could instead do it like
private IEnumerator LerpCameraNormalRot()
{
float duration = 0.5f;
float initialRotationX = camera.transform.localEulerAngles.x;
float targetRotationX = 10;
for (float t = 0f; t < duration; t += Time.deltaTime)
{
float currentX = Mathf.Lerp(initialRotationX, targetRotationX, t / duration);
camera.transform.localEulerAngles = new Vector3(currentX , 0, 0);
// Which would be the same as using
// camera.transform.localRotation = Quaternion.Euler(new Vector3(currentX, 0, 0));
yield return null;
}
// to be sure you have no overshooting you could set the target rotation fix here as well
camera.transform.localEulerAngles = new Vector3(targetRotation, 0 ,0);
// or
// camera.transform.localRotation = Quaternion.Euler(new Vector3(targetRotation, 0, 0));
}

How to set the distance of the camera from a scaling object?

My camera has to focus on an GameObject that can be scaled over time. How can I compute the camera position so that it is always at the same distance from this GameObject? I've already tried to do something like
camera.position.y += object.scaleFactor / 2;
camera.position.z -= object.scaleFactor / 2;
But the bigger the object becomes, the lesser it works. I'm thinking about using a bounding box, do you think it would work ?
Thanks a lot !
use this:
// compute this when scale is 1.0f
Vector3 originalPosition = camera.position;
Vector3 originalDistance = camera.position - gameObject.position;
// then use:
camera.position = originalPosition + originalDistance * gameObject.scaleFactor;
if this does not work, please describe your situation in more detail and i will edit the answer
for example: if you want that the camera keeps the distance to the object, you will have to use BoundingSphere.radius:
// compute this when scale is 1.0f
Vector3 originalPosition = camera.position;
Vector3 temp = camera.position - gameObject.position;
Vector3 originalDirection = temp.normalized;
float originalDistance = temp.magnitude - boundingSphere.radius;
// use this when object is scaled:
camera.position = originalPosition + originalDirection * (boundingSphere.radius + originalDistance);
public class ObjectRelativeScale : MonoBehaviour
{
public float ObjectScale = 1.0f;
private Vector3 _initialScale;
void Start()
{
_initialScale = transform.localScale;
}
void Update()
{
var cameraMainTransform = Camera.main.transform;
var plane = new Plane(cameraMainTransform.forward, cameraMainTransform.position);
float dist = plane.GetDistanceToPoint(transform.position);
transform.localScale = _initialScale * dist * ObjectScale;
}
}
Basically the way around is better, scale the object depending on the camera position.
Cheers!

Why when instantiating objects they are in the air and not on ground?

The Circle part put the objects in the air.
How can i make that they will be on the ground ?
They are standing in the air. When using the square formation they are on ground but with the circle they are in the air.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SquadFormation : MonoBehaviour
{
enum Formation
{
Square, Circle
}
public Transform squadMemeber;
public int columns = 4;
public int space = 10;
public int numObjects = 20;
// Use this for initialization
void Start()
{
ChangeFormation();
}
// Update is called once per frame
void Update()
{
}
private void ChangeFormation()
{
Formation formation = Formation.Square;
switch (formation)
{
/*case Formation.Square:
for (int i = 0; i < 23; i++)
{
Transform go = Instantiate(squadMemeber);
Vector3 pos = FormationSquare(i);
go.position = new Vector3(transform.position.x + pos.x, 0, transform.position.y + pos.y);
go.Rotate(new Vector3(0, -90, 0));
}
break;*/
case Formation.Circle:
Vector3 center = transform.position;
for (int i = 0; i < numObjects; i++)
{
Vector3 pos = RandomCircle(center, 5.0f);
var rot = Quaternion.LookRotation(pos - center);
Instantiate(squadMemeber, pos, rot);
}
break;
}
}
Vector2 FormationSquare(int index) // call this func for all your objects
{
float posX = (index % columns) * space;
float posY = (index / columns) * space;
return new Vector2(posX, posY);
}
Vector3 RandomCircle(Vector3 center, float radius)
{
float ang = Random.value * 360;
Vector3 pos;
pos.x = center.x + radius * Mathf.Sin(ang * Mathf.Deg2Rad);
pos.z = center.z + radius * Mathf.Cos(ang * Mathf.Deg2Rad);
pos.y = center.y;
return pos;
}
}
They should be instantiating on the ground(Terrain).
Need to position them on the ground.
Update:
This is what i tried now.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SquadFormation : MonoBehaviour
{
enum Formation
{
Square, Circle
}
public Transform squadMemeber;
public int columns = 4;
public int space = 10;
public int numObjects = 20;
public float yOffset = 1;
// Use this for initialization
void Start()
{
ChangeFormation();
}
// Update is called once per frame
void Update()
{
}
private void ChangeFormation()
{
Formation formation = Formation.Circle;
switch (formation)
{
/*case Formation.Square:
for (int i = 0; i < 23; i++)
{
Transform go = Instantiate(squadMemeber);
Vector3 pos = FormationSquare(i);
go.position = new Vector3(transform.position.x + pos.x, 0, transform.position.y + pos.y);
go.Rotate(new Vector3(0, -90, 0));
}
break;*/
case Formation.Circle:
Vector3 center = transform.position;
for (int i = 0; i < numObjects; i++)
{
Vector3 pos = RandomCircle(center, 5.0f);
var rot = Quaternion.LookRotation(pos - center);
pos.y = Terrain.activeTerrain.SampleHeight(pos);
pos.y = pos.y + yOffset;
Instantiate(squadMemeber, pos, rot);
}
break;
}
}
Vector2 FormationSquare(int index) // call this func for all your objects
{
float posX = (index % columns) * space;
float posY = (index / columns) * space;
return new Vector2(posX, posY);
}
Vector3 RandomCircle(Vector3 center, float radius)
{
float ang = Random.value * 360;
Vector3 pos;
pos.x = center.x + radius * Mathf.Sin(ang * Mathf.Deg2Rad);
pos.z = center.z + radius * Mathf.Cos(ang * Mathf.Deg2Rad);
pos.y = center.y;
return pos;
}
}
I added a offset yOffset and this two lines inside the for loop:
pos.y = Terrain.activeTerrain.SampleHeight(pos);
pos.y = pos.y + yOffset;
Now they are on the ground but lie down on the back/stomach and not standing like they were in the air.
Why when instantiating objects they are in the air and not on ground?
You need to find a way to calculate the height of the terrain then use that as your y-axis value.
After you get your pos, modify the y-axis with the Terrain.activeTerrain.SampleHeight function.
Vector3 pos = RandomCircle(center, 5.0f);
pos.y = Terrain.activeTerrain.SampleHeight(pos);
Maybe add an offset depending on the type of the GameObject(The yOffset should be a float)
pos.y = pos.y + yOffset;
Now, go ahead and instantiate with that new pos:
var rot = Quaternion.LookRotation(pos - center);
Instantiate(squadMemeber, pos, rot);
Note:
Depending on the size of your character,you must and have to keep changing the value of the yOffset until you get the position you want. This position should work well for the-same character. If you want to do this to the GameObjects with different size, you also have to modify the yOffset until you are satisfied with it.
Not an actual answer, but some advice:
I see some basic problem here with your code being kind of confused anyway. There are some things that should be similar that aren't. I think the baseline here is that you need some basic structure.
First things first, you should start with identifying the basic parts you need here.
There are three basic things to resolve:
XY-position
Z-position
rotation
Last one is the issue with your characters lying on the ground. See the rotation by -90 degrees for the square? I assume that this one does this.
But a more pressing matter here, here is a question for you: Aside from the XY-position, what should be the difference between squares and circles? Answer: none. You treat both cases far more different than they should be. The code should look somewhat like this:
Position xyPosition;
switch (formation)
case Circle: xyPosition = randomCirclePosition(someargument)
case Square: xyPosition = randomSquarePosition(someargument)
Position position = rotateUpright(xyPosition)
position = adjustZ(position)
See how the difference is applied only at one part?
The thing that jumps to my eyes immediately are those signatures: Vector2 FormationSquare and Vector3 RandomCircle. In particular, Vector2 and Vector3. Those are two entirely different approaches. Why? This makes your code unnecessarily heterogeneous, makes it harder to read. Does not allow for the clear structure I wrote above. Why did you derive that much? Why didn't you copy the old code of the square and then adjusted it as needed?
Do I assume correct when I say that you copied the square code from the internet? And that you have troubles understanding it for some reason? Maybe because you are not familiar with the math behind it? If so, my "answer" is this: Take the square code and do not continue until you understand any single line it it. What it does, why it is needed, what happens without it. Perfectly fine if you simply go and outcomment lines, then look what happens in the engine. Or ask somebody. But it is most important that you understand every single line in the end.
You also might want to put the code on CodeReview when it is working. I see some issues here and there in style. Like the mysterious 23 that comes out of nothing.
Sorry if this sounded a little headstrong or the like, or if I misjudge the situation here.

Is there a way to transport RectTransform into camera ViewPort in unity3d?

Is there a way to transport RectTransform into camera ViewPort in unity3d ?
I used to try do it several times but it has no result.
I want to make viewport of camera exactly in bounds of proper recttransform.
There is my code:
public static Rect RectTransformToCameraViewport(RectTransform rectTransform)
{
float leftDownCornerX = (rectTransform.anchoredPosition.x - rectTransform.sizeDelta.x / 2);
float leftDownCornerY = (rectTransform.anchoredPosition.y - rectTransform.sizeDelta.y / 2);
Vector3 leftCorner = new Vector3(leftDownCornerX, leftDownCornerY, 0);
Vector3 viewPortLeftCorner = new Vector3(leftCorner.x / Screen.width, leftCorner.y / Screen.height, 0);
float viewportWidth = Mathf.Abs(rectTransform.sizeDelta.x / Screen.width);
float viewportHeight = Mathf.Abs(rectTransform.sizeDelta.y / Screen.height);
return new Rect(0.5f + viewPortLeftCorner.x, 0.5f + viewPortLeftCorner.y, viewportWidth, viewportHeight);
}
But as I said before it does not work.
Edit 1:
It works, but it is not work on two diff machines where I work. Maybe there is something with one of those rects.
Maybe you already solved this problem ?
I had this issue in one if my previous projects where the camera needed to move to world space UI canvases. Here is my implementation:
private static void CalculateCameraTransform(Canvas canvas, float fov, out Vector3 pos, out Quaternion rot)
{
var rectTransform = canvas.GetComponent<RectTransform> ();
var corners = new Vector3[4]; rectTransform.GetWorldCorners(corners);
var normal = GetNormalPlane(corners[0], corners[1], corners[2]);
var distance = Mathf.Cos(fov * 0.5f * Mathf.Deg2Rad) * Vector3.Distance(corners[0], corners[2]) * 0.5f;
pos = canvas.transform.position + normal * distance;
rot = Quaternion.LookRotation(-normal);
}
private static Vector3 GetNormalPlane(Vector3 a, Vector3 b, Vector3 c)
{
var dir = Vector3.Cross(b - a, c - a);
var norm = Vector3.Normalize(dir);
return norm;
}
}

Categories

Resources