How to create Ofscreen Enemy indicator in Unity 3D? - unity3d

I am working on creating an Ofscreen Enemy indicator using the tutorial mentioned on below link. However I can get the indicator to rotate to point to the enemy but the indicator does not move from end to end of screen.
http://gamedevelopment.tutsplus.com/tutorials/positioning-on-screen-indicators-to-point-to-off-screen-targets--gamedev-6644
This is the desired outcome:
Until now i have managed to figure out the below Please help.
var screenCenter:Vector3 = new Vector3(0.5, 0.5, 0f);
//Note coordinates are translated
//Make 00 the centre of the screen instead of bottom left
screenpos -= screenCenter;
//find angle from center of screen instead of bototom left
var angle:float = Mathf.Atan2(screenpos.y, screenpos.x);
angle -= 90 * Mathf.Deg2Rad;
var cos:float = Mathf.Cos(angle);
var sin:float = -Mathf.Cos(angle);
screenpos = screenCenter + new Vector3(sin*150, cos*150, 0);
//y=mx + b format
var m:float = cos/sin;
var ScreenBounds:Vector3 = screenCenter;// * 0.9f;
//Check up and down first
if(cos > 0){
screenpos = new Vector3(ScreenBounds.y/m, ScreenBounds.y, 0);
}else{//down
screenpos = new Vector3(-ScreenBounds.y/m, -ScreenBounds.y, 0);
}
//If out of bound then get point on appropriate side
if(screenpos.x > ScreenBounds.x){//Out of bound must be on right
screenpos = new Vector3(ScreenBounds.x, ScreenBounds.y*m, 0);
}else if(screenpos.x < ScreenBounds.x){//Out of bound must be on left
screenpos = new Vector3(-ScreenBounds.x, -ScreenBounds.y*m, 0);
}
//Remove the co ordinate translation
screenpos += screenCenter;
var DistanceIndicatorRectT = DistanceIndicator.GetComponent(RectTransform);
DistanceIndicatorRectT.localPosition = new Vector3(screenpos.x * scrWidth/2, screenpos.y * scrHeight/2, DistanceIndicatorRectT.localPosition.z * screenpos.z);
DistanceIndicator.transform.rotation = Quaternion.Euler(0, 0, angle*Mathf.Rad2Deg);

I did a bit of a different approach than you, what Carlos suggested but without using physics.
If "t" is your target, this way you can get it's position on screen in pixels (if it's off screen it just goes to negative values or values higher that width)
Vector3 targetPosOnScreen = Camera.main.WorldToScreenPoint (t.position);
And this function that return a bool whether the Vector3 (in pixels) is on screen
bool onScreen(Vector2 input){
return !(input.x > Screen.width || input.x < 0 || input.y > Screen.height || input.y < 0);
}
First thing we should do is check if the target is on screen, if it's not then proceed with code.
if (onScreen (targetPosOnScreen)) {
//Some code to destroy indicator or make it invisible
return;
}
Then a simple calculation of angle between center of screen and target.
Vector3 center = new Vector3 (Screen.width / 2f, Screen.height / 2f, 0);
float angle = Mathf.Atan2(targetPosOnScreen.y-center.y, targetPosOnScreen.x-center.x) * Mathf.Rad2Deg;
Next part of code determines where the object is compared to camera based on angle we just calculated.
float coef;
if (Screen.width > Screen.height)
coef = Screen.width / Screen.height;
else
coef = Screen.height / Screen.width;
float degreeRange = 360f / (coef + 1);
if(angle < 0) angle = angle + 360;
int edgeLine;
if(angle < degreeRange / 4f) edgeLine = 0;
else if (angle < 180 - degreeRange / 4f) edgeLine = 1;
else if (angle < 180 + degreeRange / 4f) edgeLine = 2;
else if (angle < 360 - degreeRange / 4f) edgeLine = 3;
else edgeLine = 0;
http://s23.postimg.org/ytpm82ad7/Untitled_1.png
Image represents what value "edgeLine" will have based on target position (red represents camera's view) and black lines division of space.
And then we have this code which sets Transform "t2" (indicator) to correct position and angle.
t2.position = Camera.main.ScreenToWorldPoint(intersect(edgeLine, center, targetPosOnScreen)+new Vector3(0,0,10));
t2.eulerAngles = new Vector3 (0, 0, angle);
Below we have function "intersect" which code is:
Vector3 intersect(int edgeLine, Vector3 line2point1, Vector3 line2point2){
float[] A1 = {-Screen.height, 0, Screen.height, 0};
float[] B1 = {0, -Screen.width, 0, Screen.width};
float[] C1 = {-Screen.width * Screen.height,-Screen.width * Screen.height,0, 0};
float A2 = line2point2.y - line2point1.y;
float B2 = line2point1.x - line2point2.x;
float C2 = A2 * line2point1.x + B2 * line2point1.y;
float det = A1[edgeLine] * B2 - A2 * B1[edgeLine];
return new Vector3 ((B2 * C1[edgeLine] - B1[edgeLine] * C2) / det, (A1[edgeLine] * C2 - A2 * C1[edgeLine]) / det, 0);
}
We send to this function index of which line of camera's view (rectangle) we need to check intersection with, and construct a line between center of screen and target position.
For better explanation of this function look here : https://www.topcoder.com/community/data-science/data-science-tutorials/geometry-concepts-line-intersection-and-its-applications/
I just modified values of A1, B1 and C1, each of them is now array of 4 and each values represents value needed for one line of camera's view (rectangle).
If you want to implement margins just change the pivot of indicator (put the actual sprite renderer as child and move it in local space as you want).
Next thing would be making this work for array of targets and putting all those targets in given array. Hope this helps and don't be too hard on me, it's my first time posting here :)

Create a rectangle box collider delimiting the borders of the screen and use Physics2D.Raycast in the direction of the enemy.
The point of collision will tell you where the green arrow needs to be drawn.

In the example above, there is an error with the definition of the angle of visibility of a straight rectangle.
private void SetIndicatorPosition(Indicator obj)
{
var target = obj.Target;
var indicator = obj.PointToTarget;
if (target == null)
{
indicator.SetActive(false);
return;
}
Vector3 targetPosOnScreen = cam.WorldToScreenPoint(target.transform.position);
if (onScreen(targetPosOnScreen))
{
indicator.SetActive(false);
return;
}
indicator.SetActive(true);
Vector3 center = new Vector3(Screen.width / 2f, Screen.height / 2f, 0);
float angle = Mathf.Atan2(targetPosOnScreen.y - center.y, targetPosOnScreen.x - center.x) * Mathf.Rad2Deg;
float scale;
if (Screen.width > Screen.height)
scale = Screen.width / Screen.height;
else
scale = Screen.height / Screen.width;
float degreeRange = 360f / (scale + 1);
float angle2 = Mathf.Atan2(Screen.height - center.y, Screen.width - center.x) * Mathf.Rad2Deg;
if (angle < 0) angle = angle + 360;
int edgeLine;
if (angle < angle2) edgeLine = 0;
else if (angle < 180 - angle2) edgeLine = 1;
else if (angle < 180 + angle2) edgeLine = 2;
else if (angle < 360 - angle2) edgeLine = 3;
else edgeLine = 0;
indicator.transform.position = Camera.main.ScreenToWorldPoint(Intersect(edgeLine, center, targetPosOnScreen));
indicator.transform.eulerAngles = new Vector3(0, 0, angle);
}
Vector3 Intersect(int edgeLine, Vector3 line2point1, Vector3 line2point2)
{
float[] A1 = { -Screen.height, 0, Screen.height, 0 };
float[] B1 = { 0, -Screen.width, 0, Screen.width };
float[] C1 = { -Screen.width * Screen.height, -Screen.width * Screen.height, 0, 0 };
float A2 = line2point2.y - line2point1.y;
float B2 = line2point1.x - line2point2.x;
float C2 = A2 * line2point1.x + B2 * line2point1.y;
float det = A1[edgeLine] * B2 - A2 * B1[edgeLine];
var x = (B2 * C1[edgeLine] - B1[edgeLine] * C2) / det;
var y = (A1[edgeLine] * C2 - A2 * C1[edgeLine]) / det;
return new Vector3(x, y, 0);
}
bool onScreen(Vector2 input)
{
return !(input.x > Screen.width || input.x < 0 || input.y > Screen.height || input.y < 0);
}
public class Indicator
{
public GameObject Target { get; private set; }
public GameObject PointToTarget { get; private set; }
public Indicator(GameObject target, GameObject pointToTarget, ObjectTypeEnum type)
{
Target = target;
PointToTarget = pointToTarget;
var texture = pointToTarget.GetComponentInChildren<UITexture>();
if (texture != null)
{
texture.color = Helper.GetHintColor(type);
}
}
}
You can call in update
foreach (var obj in listIndicator)
{
SetIndicatorPosition(obj);
}

Related

Range bound x-axis movement

I am working in unity/C# and trying to make a function that keeps an character in certain x range (-3 to 3). Below isthe code I got to work. Is there a way to simplify it?
//function creation to limit movement in the x axis
float rangeBoundX(int upperBound, int lowerBound, Vector3 i, float horMoveSpe = 0)
{
//will change velocity to keep the x value in the desired range. - velocity to mvoe away from the upper bound and positive velocity goes away from the lowerBound.
if (i.x > upperBound)
{
horMoveSpe = -1;
}
else if (i.x < lowerBound)
{
horMoveSpe = 1;
}
return horMoveSpe;
}
'private void FixedUpdate()'
{
Vector3 enemyforwardMove = transform.forward * enemySpeed * Time.fixedDeltaTime;
Vector3 horizontalMove = transform.position;
magn = rangeBoundX(3, -3, horizontalMove, magn);
horizontalMove = transform.right * magn * freq;
enemyRB.MovePosition(enemyRB.position + enemyforwardMove + horizontalMove);
}
Yes you can use Math.Clamp to bound any value Like this.
void update(){
float xPos = Mathf.Clamp(transform.position.x, -3, 3);
transform.position = new Vector3(xPos, transform.position.y, transform.position.z);
}
using this code you can bound the position of object horizontaly.
Yes, there is a way to simplify it. You can use Mathf.Clamp method https://docs.unity3d.com/ScriptReference/Mathf.Clamp.html
example usage: float xPos = Mathf.Clamp(xValue, xMin, xMax);

Move the camera so that all objects are in the camera's field of view

select objects
Camera is moved but top cut
Move the camera so that all objects are in the camera's field of view
I want to make the objects that are not the selected ones invisible and enlarge them as much as I can while making all visible objects visible
Vector3 averagePos=Vector3.zero;
for (int i = 0; i < set[num].Count; i++)
{
objectManager.SetActive(set[num][i], true);
averagePos += objectManager.ReturnPos(set[num][i]);
}
averagePos /= set[num].Count;
if (move_On)
{
angle += Time.deltaTime;
if (angle > 0.01)
angle = 0.009f;
// transform.position = Vector3.SmoothDamp(transform.position, destPos, ref velocity, smoothTime);
Vector3 ve3=Vector3.Lerp(transform.position, destPos, angle);
Debug.Log(Vector3.Distance(ve3, destPos));
if (Vector3.Distance(ve3,destPos)>1)
transform.position = Vector3.Lerp(transform.position, destPos, angle);
}
centerOfPos = new Vector3((maxPosX + minPosX) / 2, (maxPosY + minPosY) / 2, 0);
Camera.main.transform.LookAt(centerOfPos);
float hoizontalCamZ = (maxPosY + 1 - minPosY) / Mathf.Cos(Camera.main.fieldOfView / 2 * Mathf.Deg2Rad);
float verticalCamZ = (maxPosX + 1 - minPosX) / Mathf.Cos
(Mathf.Atan(Mathf.Tan(Camera.main.fieldOfView / 2 * Mathf.Deg2Rad)));
if (hoizontalCamZ < verticalCamZ)
camZ = verticalCamZ;
else
camZ = hoizontalCamZ;
Vector3 newCamPos = new Vector3(0, 0, -camZ);

Get volume of an object in Unity3D

I'm writing a script for an Object in Unity3D.
I want to get the volume of my object.
rigidBody = GetComponent<Rigidbody>();
I'm looking in the documentation at the attributes contained in Rigidbody but I don't see anything I can use.
I tried using bounds but I found that rotation of an object changed those values even without size changing:
int getSize(Vector3 bounds)
{
float size = bounds[0] * bounds[1] * bounds[2] * 1000;
Debug.Log("size value = " + (int)size);
return (int)size;
}
What properties can I use to calculate the volume of an object?
The math is explained here.
In C# for convencience:
float SignedVolumeOfTriangle(Vector3 p1, Vector3 p2, Vector3 p3)
{
float v321 = p3.x * p2.y * p1.z;
float v231 = p2.x * p3.y * p1.z;
float v312 = p3.x * p1.y * p2.z;
float v132 = p1.x * p3.y * p2.z;
float v213 = p2.x * p1.y * p3.z;
float v123 = p1.x * p2.y * p3.z;
return (1.0f / 6.0f) * (-v321 + v231 + v312 - v132 - v213 + v123);
}
float VolumeOfMesh(Mesh mesh)
{
float volume = 0;
Vector3[] vertices = mesh.vertices;
int[] triangles = mesh.triangles;
for (int i = 0; i < mesh.triangles.Length; i += 3)
{
Vector3 p1 = vertices[triangles[i + 0]];
Vector3 p2 = vertices[triangles[i + 1]];
Vector3 p3 = vertices[triangles[i + 2]];
volume += SignedVolumeOfTriangle(p1, p2, p3);
}
return Mathf.Abs(volume);
}
Mesh mesh = GetComponent<MeshFilter>().sharedMesh;
Debug.Log(VolumeOfMesh(mesh));

Rotate a position according to forward

I am currently making a climbing detection system that follows the player around and detects the closest point the player can climb. I have a game object that casts rays according to the players position. The rays move with the player but do not rotate around. How can I make them rotate with the player ?
This is the code that draws the rays
void Update()
{
//Debug.DrawRay();
raySpace = surfaceWidth / definition;
raySpaceDividedByTwo = raySpace / 2;
surfaceWidthDividedByTwo = surfaceWidth / 2;
for (int i = 0; i < definition; i++)
{
for (int y = 0; y < definition; y++)
{
raycastCoordinates = new Vector3(
(this.transform.position.x + (i * raySpace) + raySpaceDividedByTwo) - surfaceWidthDividedByTwo,
this.transform.position.y,
(this.transform.position.z + (y * raySpace) + raySpaceDividedByTwo) - surfaceWidthDividedByTwo);
//offset = Quaternion.AngleAxis(Input.GetAxis("Mouse X") * turnSpeed, Vector3.up) * offset;
//raycastCoordinates = Quaternion.AngleAxis(this.transform.rotation.y, Vector3.up) * raycastCoordinates;
raycastUpHitsList.Add(Physics.RaycastAll(raycastCoordinates, Vector3.up, detectionHeight, LayerMask.GetMask("Water")));
Debug.DrawRay(raycastCoordinates, Vector3.up * detectionHeight, Color.green, 0.1f);
}
}
}
This should work:
void Update()
{
//Debug.DrawRay();
raySpace = surfaceWidth / definition;
raySpaceDividedByTwo = raySpace / 2;
surfaceWidthDividedByTwo = surfaceWidth / 2;
for (int i = 0; i < definition; i++)
{
for (int y = 0; y < definition; y++)
{
raycastCoordinates = new Vector3(
(this.transform.position.x + (i * raySpace) + raySpaceDividedByTwo) - surfaceWidthDividedByTwo,
this.transform.position.y,
(this.transform.position.z + (y * raySpace) + raySpaceDividedByTwo) - surfaceWidthDividedByTwo) * transform.forward;//if this doesn't work, try transform.right instead
//offset = Quaternion.AngleAxis(Input.GetAxis("Mouse X") * turnSpeed, Vector3.up) * offset;
//raycastCoordinates = Quaternion.AngleAxis(this.transform.rotation.y, Vector3.up) * raycastCoordinates;
raycastUpHitsList.Add(Physics.RaycastAll(raycastCoordinates, Vector3.up, detectionHeight, LayerMask.GetMask("Water")));
Debug.DrawRay(raycastCoordinates, Vector3.up * detectionHeight, Color.green, 0.1f);
}
}
}

OpenGL Projection matrix won't allow displaying anything

I'm trying to get some basic OpenGL-ES with Shaders to run on the iPhone, based on some examples.
For some reason my projection matrix refuses to result in something on the screen. It feels like a clipping plane is set very near but that contradicts with the values I supply. If I render the same scene with an Orthogonal projection matrix I see my object just no perspective obviously.
Here's the code that generates the projection matrix:
esPerspective(&proj, 45.f, 768.0/1024.0, 1.f, 10000.f);
void esPerspective(ESMatrix *result, float fovy, float aspect, float nearZ, float farZ)
{
float frustumW, frustumH;
frustumH = tanf( fovy / 360.0f * PI ) * nearZ;
frustumW = frustumH * aspect;
esFrustum( result, -frustumW, frustumW, -frustumH, frustumH, nearZ, farZ );
}
void esFrustum(ESMatrix *result, float left, float right, float bottom, float top, float nearZ, float farZ)
{
float deltaX = right - left;
float deltaY = top - bottom;
float deltaZ = farZ - nearZ;
ESMatrix frust;
if ( (nearZ <= 0.0f) || (farZ <= 0.0f) ||
(deltaX <= 0.0f) || (deltaY <= 0.0f) || (deltaZ <= 0.0f) )
return;
frust.m[0][0] = 2.0f * nearZ / deltaX;
frust.m[0][1] = frust.m[0][2] = frust.m[0][3] = 0.0f;
frust.m[1][1] = 2.0f * nearZ / deltaY;
frust.m[1][0] = frust.m[1][2] = frust.m[1][3] = 0.0f;
frust.m[2][0] = (right + left) / deltaX;
frust.m[2][1] = (top + bottom) / deltaY;
frust.m[2][2] = -(nearZ + farZ) / deltaZ;
frust.m[2][3] = -1.0f;
frust.m[3][2] = -2.0f * nearZ * farZ / deltaZ;
frust.m[3][0] = frust.m[3][1] = frust.m[3][3] = 0.0f;
esMatrixMultiply(result, &frust, result);
}
My projection matrix comes out as:
[3.21, 0, 0, 0]
[0, 2.41, 0, 0]
[0, 0, -1, -1]
[0, 0, -2, 0]
Even if I manually set the [3][3] cell to 1 I still don't see anything.
Any ideas?
Swap your rows and columns over (ie transpose).
Well your projection matrix is transposed either way. You have a row major projection matrix ...