Object to always appear same size on HUD

I had a hard time deciding the title of this question because it’s a very open-ended question (As I have nothing set in stone yet).

Essentially, what I’m doing is implementing a target “predictor” object that sits a certain amount (Some maths calculation including projectile speed, distance and enemy velocity) ahead of enemy ships (It’s a space dog fighter). Now, this question is two fold:

1.) How would I make this “Predictor” object stay the same size to the camera no matter its distance?

2.) How can I accurately measure where the bullets will fire to? Currently, the camera is above where the bullets spawn so it obviously isn’t the center of the camera. I want to put a HUD item similar to crosshairs on the screen.

I apologize for the vague nature of this question but I can’t really implement a “predictor” until the enemies have simple instructions to move around.

1: Have you used the UI system at all?

If you have a Camera object in script using Camera.Main you can use the WorldToScreenPoint function.

With this you position a UI element: Image, text, whatever you want and it will always appear the same size. I use this for the health bars in my game by having a health bar object be a child of the players/enemies and follow above them.

2: Well for the second question I have some prediction code:

float time = Distance/BulletSpeed;
PredictedTargetPos = TargetPos + (TargetVel * time);

Then for more accuracy you can do this as many times as nessesary:

float TimeToNewTargetPos = Vector3.Distance(PredictedTargetPos, TargetPos)/BulletSpeed;
m_PredictedTargetPos = PredictedTargetPos + (TargetVel * TimeToNewTargetPos);

You can create a world space canvas as the child of the object you wish to display the information of. Then set the desired width and height. I’m going to use 800 x 800 pixels. In order to bring this to proper world size, decrease the scale of the canvas to 0.005 on each axis. This means that your canvas is now 4m in width and height in world space. (800 x 0.005 = 4). You can set any combo of scale and surface size of the canvas to get the results you need.

Now that you have this setup, you need to tweak it with some code. Here’s my example.

using UnityEngine;
using System.Collections;

public class KeepScreenSize : MonoBehaviour {

    private float sizeModifier, cameraDistance;
    private RectTransform canvasTransform;
    private Vector3 tmpLocalScale = new Vector3();
    
    // Use this for initialization
    void Start ()
    {
        canvasTransform = GetComponent<RectTransform>();
        // get distance between camera and 0,0,0. Use this info to calculate size modifier.
        cameraDistance = Vector3.Distance(Camera.main.transform.position, Vector3.zero);
        sizeModifier = cameraDistance / canvasTransform.localScale.x;
	}
	
	// Now that we have the modifier, use varying camera distance to calculate the size of canvas
	void LateUpdate ()
    {
        cameraDistance = Vector3.Distance(Camera.main.transform.position, this.transform.position);
        tmpLocalScale.x = cameraDistance / sizeModifier;
        tmpLocalScale.y = tmpLocalScale.x;
        tmpLocalScale.z = tmpLocalScale.x;

        // Apply new scale
        canvasTransform.localScale = tmpLocalScale;
        // rotate canvas to be perpendicular to camera regardless of camera rotation.
        // Notice we don't use LookAt(camera). This would mirror the canvas.
        canvasTransform.rotation = Camera.main.transform.rotation;
    }
}

Attach this script to the canvas object.
You can use whatever reference you want for getting the sizeModifier (I usually use Vector3.zero), this value is virtual and it doesn’t really matter in the beginning. Setting this modifier in Start() allows you to tweak the scale of the canvas in Editor and just hit run. You don’t need to recalculate anything.
If everything goes well, you should be able to create multiple objects with varying distance from the camera, each with his own modified canvas scale.
Note, the canvas will slightly get “over” increased as you move your objects more and more to the left or right of the camera. It’s due to the new distance, which is a diagonal and larger than the forward distance (cathetus).

Hope this helps.

I’v changed this a bit as the size on the y was not right

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class KeepScreenSize : MonoBehaviour
    {
    
        public float  sizeModifier, sizeModifierY, cameraDistance;
        public RectTransform canvasTransform;
        public Vector3 tmpLocalScale = new Vector3();
    
        void Start()
        {
            canvasTransform = GetComponent<RectTransform>();
            cameraDistance = Vector3.Distance(Camera.main.transform.position, Vector3.zero);
            sizeModifier = cameraDistance / canvasTransform.localScale.x;
            sizeModifierY = cameraDistance / canvasTransform.localScale.y;
        }
    
        void LateUpdate()
        {
            cameraDistance = Vector3.Distance(Camera.main.transform.position, this.transform.position);
            tmpLocalScale.x = cameraDistance / sizeModifier;
            tmpLocalScale.y = cameraDistance / sizeModifierY;
    
            canvasTransform.localScale = tmpLocalScale;
            canvasTransform.rotation = Camera.main.transform.rotation;
        }
    }