- Home /
Problem with a direction indicator for an FPS game
Hey Guys,
i am currently working on a direction indicator for a first person game, which shows the player the direction to an object on the UI canvas. The code I got so far works pretty fine, but i got one problem which drives me crazy.
At first some explanations of the current behaviour:
If the object is in front of the player and the player looks beneath the object, the indicator points upwards. If the player looks above the object the indicator points downwards. This is working fine and is exactly what i want.
If the object is behind the player and the player looks beneath the object, the indicator points downwards (Which is fine since the object is behind and below the player).
Now to the problem:
If the object is behind the player and the player looks above the object, the indicator points upwards. This would be fine if the player could do a 360 degree turn around his local X axis, since turning upwards would be the shortest way to get the object into the viewport.
But in an FPS game the player is only able look upwards with a maximum rotation of 90 Degrees. In this case i want the direction indicator to stay at the bottom area of the screen instead to rotate upwards. I tried several things but did not find any satisfying solution for this.
In my scenario the object is slightly below the player (If the object is above the player the same problem occures. In this case its just flipped upside down).
One example for a direction indicator that works correctly if the object is behind the player, is the one in the game called "The Cycle". I tried to achieve a similar result.
This shows the current behaviour (first if the object is in front, then if the object is behind the player):
The code i got so far:
- I am getting the direction vector from the player to the object
- I project this vector onto the local XY plane of the player
- Then i get the angle between the players local up and the projected direction vector
- Then i rotate the UI indicator towards the angle
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class DirectionIndicator : MonoBehaviour
{
[SerializeField] private Transform m_Object;
[SerializeField] private GameObject m_ObjectUIRepresentation;
private Image m_DirectionIndicator;
private Image m_Symbol;
private Vector3 m_DirectionToObject;
private float m_AngleToObject;
void Start()
{
m_DirectionIndicator = m_ObjectUIRepresentation.transform.Find("DirectionIndicator").GetComponent<Image>();
m_Symbol = m_ObjectUIRepresentation.transform.Find("Symbol").GetComponent<Image>();
m_DirectionIndicator.enabled = false;
m_Symbol.enabled = false;
}
// Update is called once per frame
void Update()
{
// Get the direction to the object
m_DirectionToObject = m_Object.position - transform.position;
m_DirectionToObject = Vector3.ProjectOnPlane(m_DirectionToObject, transform.forward);
// Get the angle
m_AngleToObject = Vector3.SignedAngle(transform.up, m_DirectionToObject, transform.forward);
Debug.DrawRay(transform.position, m_DirectionToObject, Color.magenta);
Debug.DrawRay(transform.position, transform.up * 100f, Color.green);
// Check if object is on screen
Vector3 objectScreenPosition = Camera.main.WorldToViewportPoint(m_Object.transform.position);
bool onScreen = objectScreenPosition.x > 0.1f && objectScreenPosition.x < 0.9f && objectScreenPosition.y > 0.1f && objectScreenPosition.y < 0.9f && objectScreenPosition.z > 1f;
if (onScreen)
{
//print("Object is on screen");
m_DirectionIndicator.enabled = false;
m_Symbol.enabled = true;
m_Symbol.rectTransform.position = Camera.main.ViewportToScreenPoint(objectScreenPosition);
}
else
{
//print("Object is not on screen");
m_Symbol.enabled = false;
m_DirectionIndicator.enabled = true;
// Rotate the direction indicator
m_ObjectUIRepresentation.transform.rotation = Quaternion.Euler(0f, 0f, m_AngleToObject);
}
}
}
i am totally out of ideas so every help is very welcome.
Thank you in advance!
Cheers
David
Answer by JPhilipp · Mar 13, 2020 at 09:38 PM
Hi! You can do something like this in a single line of code, and it might even look better and more intuitive, by using an actual 3D cone that's simply pointed at the target:
using UnityEngine;
public class PointAtTarget : MonoBehaviour
{
// Points an object like an arrow at a target.
[SerializeField] Transform target = null;
void Update()
{
transform.LookAt(target);
}
}
Just add an empty ArrowWrapper
gameObject and move it to the front of your avatar eye camera. Then add an Arrow
gameObject inside where you attach the arrow mesh (scale it down to e.g. 0.1 of its original size), and give it above component script PointAtTarget
. Here's a video of it in action; the shotgun is the target.
Note I'm using a special unlit but transparency-supporting shader, and a mesh I made. I've bundled up these assets for you at my server. Good luck!
Answer by ToterKojote · Mar 15, 2020 at 05:34 PM
Many thanks for your answer and also for sharing your resources! Thats a pretty cool alternative way to do it and i will definitely try out your approach.
But i would still love to get a solution for the problem with the UI indicator (I am doing this mostly as a programming practice and it bothers me that i cant figure out a solution for this).