Hi all, I built a crosshair generator a little while back for use in a game, but never tried things at different resolutions. As a result, the drawn elements looks super tiny at 4K resolutions and huge at low resolutions.
I’ve tried to fix this by using a remapping function, but it doesn’t seem to be working. At first I thought this was due to using Screen.width/height, so I changed everything to Camera.main.pixelWidth/Height, but this hasn’t fixed anything and I’m really not sure where to go from here now.
I’m hoping this is just a bug of some sort as I’m unable to actually test this at higher resolutions than 1920x1080 outside of using a custom fixed resolution (3840x2160) in game view.
The original code which works as intended at 1920x1080 is this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[AddComponentMenu("Arcane/Player/Dynamic Crosshair")]
public class ArcaneDynaCrosshair : MonoBehaviour
{
public static ArcaneDynaCrosshair crosshair;
public bool debugBox = true;
public bool _enabled = true;
public enum Preset { _none, _melee, _pistol, _shotgun, _gatling, _other }
public Preset preset = Preset._none;
public Color color = new Color32(127, 127, 127, 255);
[Header("Slice (individual hairs) Settings")]
[Range(2, 20)]
public int sliceCount = 4;
public int length = 10;
public int width = 2;
[Header("Total Crosshair Settings")]
public bool renderCentrePin = true;
public int centrePinSize = 2;
public bool renderSlices = true;
public int minSpread = 30;
public int maxSpread = 100;
public float spreadPerSecond = 600.0f;
public float rotAngle = 0.0f;
public float rotSpeed = 0.0f;
private Texture2D texture;
private Texture2D debugTex;
private float spread;
void Start()
{
//Base reference resolution - 1920x1080
color = Arcane_GameManager.manager.SetColorFromString(Arcane_GameManager.manager.GetPref("cameraCshrCol"));
//color = Arcane_GameManager.manager.SetColorFromString(Arcane_GameManager.manager.GetPref("cameraCshrTgtCol"));
texture = new Texture2D(1, 1);
debugTex = new Texture2D(1, 1);
//Set internal texture color
for (int y = 0; y < texture.height; y++)
{
for (int x = 0; x < texture.width; x++)
{
texture.SetPixel(x, y, color);
}
}
texture.Apply();
for (int y = 0; y < debugTex.height; y++)
{
for (int x = 0; x < debugTex.width; x++)
{
debugTex.SetPixel(x, y, new Color(0, 255, 0, 255));
}
}
debugTex.Apply();
}
void Update()
{
if (Input.GetKey(Arcane_GameManager.manager.GetCont("contComSFire")) || Input.GetKey(Arcane_GameManager.manager.GetCont("contComSFireAlt")))
{
spread += spreadPerSecond * Time.deltaTime;
}
else
{
spread -= spreadPerSecond * 2 * Time.deltaTime;
}
rotAngle += rotSpeed * Time.deltaTime;
rotAngle = Mathf.Clamp(rotAngle, 0, 360);
if (rotAngle >= 360)
{
rotAngle = 0;
}
}
void OnGUI()
{
for (int y = 0; y < texture.height; y++)
{
for (int x = 0; x < texture.width; x++)
{
texture.SetPixel(x, y, color);
}
}
texture.Apply();
if (_enabled)
{
GUIStyle gStyle = new GUIStyle();
gStyle.normal.background = texture;
spread = Mathf.Clamp(spread, minSpread, maxSpread);
Vector2 pivot = new Vector2(Camera.main.pixelWidth / 2, Camera.main.pixelHeight / 2);
GUIStyle dStyle = new GUIStyle();
dStyle.normal.background = debugTex;
if (renderCentrePin)
{
GUI.Box(new Rect((Camera.main.pixelWidth / 2) - (centrePinSize / 2), (Camera.main.pixelHeight / 2) - (centrePinSize / 2), centrePinSize, centrePinSize), texture, gStyle);
}
if (debugBox)
{
//GUI.Box(new Rect((Camera.main.pixelWidth / 2) - ((minSpread + length) / 2), (Camera.main.pixelHeight / 2) - ((minSpread + length) / 2), (minSpread + length), (minSpread + length)), texture, gStyle);
//Top
GUI.Box(new Rect((Camera.main.pixelWidth / 2) - (50 / 2), (Camera.main.pixelHeight / 2) - (50 / 2), 50, 1), debugTex, dStyle);
//Right
GUI.Box(new Rect((Camera.main.pixelWidth / 2) - (50 / 2), (Camera.main.pixelHeight / 2) - (50 / 2), 1, 50), debugTex, dStyle);
//Bottom
GUI.Box(new Rect((Camera.main.pixelWidth / 2) - (50 / 2), (Camera.main.pixelHeight / 2) + (50 / 2), 50, 1), debugTex, dStyle);
//Left
GUI.Box(new Rect((Camera.main.pixelWidth / 2) + (50 / 2), (Camera.main.pixelHeight / 2) - (50 / 2), 1, 50), debugTex, dStyle);
//TL
GUI.Box(new Rect((Camera.main.pixelWidth / 2) - (50 / 2), (Camera.main.pixelHeight / 2) - (50 / 2), 1, 1), texture, gStyle);
//TR
GUI.Box(new Rect((Camera.main.pixelWidth / 2) + (50 / 2), (Camera.main.pixelHeight / 2) - (50 / 2), 1, 1), texture, gStyle);
//BL
GUI.Box(new Rect((Camera.main.pixelWidth / 2) - (50 / 2), (Camera.main.pixelHeight / 2) + (50 / 2), 1, 1), texture, gStyle);
//BR
GUI.Box(new Rect((Camera.main.pixelWidth / 2) + (50 / 2), (Camera.main.pixelHeight / 2) + (50 / 2), 1, 1), texture, gStyle);
}
switch (preset)
{
//Allow custom settings control over crosshair
case Preset._none:
break;
//Set to centre pin only
case Preset._melee:
renderSlices = false;
renderCentrePin = true;
break;
//Set to traditional "+" crosshair
case Preset._pistol:
sliceCount = 4;
length = 10;
width = 2;
renderCentrePin = true;
renderSlices = true;
minSpread = 30;
rotAngle = 0;
rotSpeed = 0;
break;
//Set to rotated boxed "x" crosshair
case Preset._shotgun:
sliceCount = 4;
length = 2;
width = 14;
renderCentrePin = false;
renderSlices = true;
minSpread = 50;
rotAngle = 45f;
rotSpeed = 0;
break;
//Set to circular "bullets" crosshair
case Preset._gatling:
sliceCount = 8;
length = 2;
width = 6;
renderCentrePin = true;
renderSlices = true;
minSpread = 30;
rotSpeed = 180;
break;
//Set to circular crosshair
case Preset._other:
sliceCount = 20;
length = 2;
width = 6;
renderCentrePin = true;
renderSlices = true;
minSpread = 30;
rotAngle = 0;
rotSpeed = 0;
break;
}
if (renderSlices)
{
GUIUtility.RotateAroundPivot(rotAngle % 360, pivot);
for (int i = 0; i < sliceCount; i++)
{
GUI.Box(new Rect((Camera.main.pixelWidth / 2) - (width / 2), (Camera.main.pixelHeight + spread) / 2, width, length), texture, gStyle);
GUIUtility.RotateAroundPivot(360 / sliceCount, pivot);
}
}
}
}
}
And the new (broken) code is this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[AddComponentMenu("Arcane/Player/Dynamic Crosshair")]
public class ArcaneDynaCrosshair : MonoBehaviour
{
public static ArcaneDynaCrosshair crosshair;
public bool debugBox = true;
public bool _enabled = true;
public enum Preset { _none, _melee, _pistol, _shotgun, _gatling, _other }
public Preset preset = Preset._none;
public Color color = new Color32(127, 127, 127, 255);
[Header("Slice (individual hairs) Settings")]
[Range(2, 20)]
public int sliceCount = 4;
public int length = 10;
public int width = 2;
[Header("Total Crosshair Settings")]
public bool renderCentrePin = true;
public int centrePinSize = 2;
public bool renderSlices = true;
public int minSpread = 30;
public int maxSpread = 100;
public float spreadPerSecond = 600.0f;
float lengthInt = 0;
float widthInt = 0;
float centrePinSizeInt = 0;
float minSpreadInt = 0;
float maxSpreadInt = 0;
float spreadPerSecondInt = 0;
public float rotAngle = 0.0f;
public float rotSpeed = 0.0f;
private Texture2D texture;
private Texture2D debugTex;
private float spread;
void Start()
{
//Base reference resolution - 1920x1080
color = Arcane_GameManager.manager.SetColorFromString(Arcane_GameManager.manager.GetPref("cameraCshrCol"));
//color = Arcane_GameManager.manager.SetColorFromString(Arcane_GameManager.manager.GetPref("cameraCshrTgtCol"));
texture = new Texture2D(1, 1);
debugTex = new Texture2D(1, 1);
//Set internal texture color
for (int y = 0; y < texture.height; y++)
{
for (int x = 0; x < texture.width; x++)
{
texture.SetPixel(x, y, color);
}
}
texture.Apply();
for (int y = 0; y < debugTex.height; y++)
{
for (int x = 0; x < debugTex.width; x++)
{
debugTex.SetPixel(x, y, new Color(0, 255, 0, 255));
}
}
debugTex.Apply();
}
void Update()
{
if (Input.GetKey(Arcane_GameManager.manager.GetCont("contComSFire")) || Input.GetKey(Arcane_GameManager.manager.GetCont("contComSFireAlt")))
{
spread += spreadPerSecond * Time.deltaTime;
}
else
{
spread -= spreadPerSecond * 2 * Time.deltaTime;
}
rotAngle += rotSpeed * Time.deltaTime;
rotAngle = Mathf.Clamp(rotAngle, 0, 360);
if (rotAngle >= 360)
{
rotAngle = 0;
}
}
void OnGUI()
{
lengthInt = Remap(length, 1920, 1080, Camera.main.pixelWidth, Camera.main.pixelHeight);
widthInt = Remap(width, 1920, 1080, Camera.main.pixelWidth, Camera.main.pixelHeight);
centrePinSizeInt = Remap(centrePinSize, 1920, 1080, Camera.main.pixelWidth, Camera.main.pixelHeight);
minSpreadInt = Remap(minSpread, 1920, 1080, Camera.main.pixelWidth, Camera.main.pixelHeight);
maxSpreadInt = Remap(maxSpread, 1920, 1080, Camera.main.pixelWidth, Camera.main.pixelHeight);
spreadPerSecondInt = Remap(spreadPerSecond, 1920, 1080, Camera.main.pixelWidth, Camera.main.pixelHeight);
for (int y = 0; y < texture.height; y++)
{
for (int x = 0; x < texture.width; x++)
{
texture.SetPixel(x, y, color);
}
}
texture.Apply();
if (_enabled)
{
GUIStyle gStyle = new GUIStyle();
gStyle.normal.background = texture;
spread = Mathf.Clamp(spread, minSpread, maxSpread);
Vector2 pivot = new Vector2(Camera.main.pixelWidth / 2, Camera.main.pixelHeight / 2);
GUIStyle dStyle = new GUIStyle();
dStyle.normal.background = debugTex;
if (renderCentrePin)
{
GUI.Box(new Rect((Camera.main.pixelWidth / 2) - (centrePinSizeInt / 2), (Camera.main.pixelHeight / 2) - (centrePinSizeInt / 2), centrePinSizeInt, centrePinSizeInt), texture, gStyle);
}
if (debugBox)
{
//GUI.Box(new Rect((Camera.main.pixelWidth / 2) - ((minSpread + length) / 2), (Camera.main.pixelHeight / 2) - ((minSpread + length) / 2), (minSpread + length), (minSpread + length)), texture, gStyle);
float debugW = Remap(50, 1920, 1080, Camera.main.pixelWidth, Camera.main.pixelHeight);
float debugH = Remap(1, 1920, 1080, Camera.main.pixelWidth, Camera.main.pixelHeight);
//Top
GUI.Box(new Rect((Camera.main.pixelWidth / 2) - (debugW / 2), (Camera.main.pixelHeight / 2) - (debugW / 2), debugW, debugH), debugTex, dStyle);
//Right
GUI.Box(new Rect((Camera.main.pixelWidth / 2) - (debugW / 2), (Camera.main.pixelHeight / 2) - (debugW / 2), debugH, debugW), debugTex, dStyle);
//Bottom
GUI.Box(new Rect((Camera.main.pixelWidth / 2) - (debugW / 2), (Camera.main.pixelHeight / 2) + (debugW / 2), debugW, debugH), debugTex, dStyle);
//Left
GUI.Box(new Rect((Camera.main.pixelWidth / 2) + (debugW / 2), (Camera.main.pixelHeight / 2) - (debugW / 2), debugH, debugW), debugTex, dStyle);
float debugW2 = Remap(1, 1920, 1080, Camera.main.pixelWidth, Camera.main.pixelHeight);
//TL
GUI.Box(new Rect((Camera.main.pixelWidth / 2) - (debugW / 2), (Camera.main.pixelHeight / 2) - (debugW / 2), debugW2, debugW2), texture, gStyle);
//TR
GUI.Box(new Rect((Camera.main.pixelWidth / 2) + (debugW / 2), (Camera.main.pixelHeight / 2) - (debugW / 2), debugW2, debugW2), texture, gStyle);
//BL
GUI.Box(new Rect((Camera.main.pixelWidth / 2) - (debugW / 2), (Camera.main.pixelHeight / 2) + (debugW / 2), debugW2, debugW2), texture, gStyle);
//BR
GUI.Box(new Rect((Camera.main.pixelWidth / 2) + (debugW / 2), (Camera.main.pixelHeight / 2) + (debugW / 2), debugW2, debugW2), texture, gStyle);
}
switch (preset)
{
//Allow custom settings control over crosshair
case Preset._none:
break;
//Set to centre pin only
case Preset._melee:
renderSlices = false;
renderCentrePin = true;
break;
//Set to traditional "+" crosshair
case Preset._pistol:
sliceCount = 4;
length = 10;
width = 2;
renderCentrePin = true;
renderSlices = true;
minSpread = 30;
rotAngle = 0;
rotSpeed = 0;
break;
//Set to rotated boxed "x" crosshair
case Preset._shotgun:
sliceCount = 4;
length = 2;
width = 14;
renderCentrePin = false;
renderSlices = true;
minSpread = 50;
rotAngle = 45f;
rotSpeed = 0;
break;
//Set to circular "bullets" crosshair
case Preset._gatling:
sliceCount = 8;
length = 2;
width = 6;
renderCentrePin = true;
renderSlices = true;
minSpread = 30;
rotSpeed = 180;
break;
//Set to circular crosshair
case Preset._other:
sliceCount = 20;
length = 2;
width = 6;
renderCentrePin = true;
renderSlices = true;
minSpread = 30;
rotAngle = 0;
rotSpeed = 0;
break;
}
if (renderSlices)
{
GUIUtility.RotateAroundPivot(rotAngle % 360, pivot);
for (int i = 0; i < sliceCount; i++)
{
GUI.Box(new Rect((Camera.main.pixelWidth / 2) - (widthInt / 2), (Camera.main.pixelHeight + spread) / 2, widthInt, lengthInt), texture, gStyle);
GUIUtility.RotateAroundPivot(360 / sliceCount, pivot);
}
}
}
}
public static float Remap(float value, float from1, float to1, float from2, float to2)
{
return (value - from1) / (to1 - from1) * (to2 - from2) + from2;
}
}
From my limited skills and understanding, this should work, but it doesn’t. The pixel width and heights are incorrect, and I can’t work out why. Any ideas?