I've found this article on Gamasutra on a custom camera projection to make 3D look more 2D and decided to implement it to make some gameplay tests.
I had to make some changes to make it work on URP(on unity 2019.3.6f1) but it "worked" with the following code:
using UnityEngine;
using System.Collections;
using UnityEngine.Rendering;
// This script is meant to be attached to your main camera.
// If you want to use it on more than one camera at a time, it will require
// modifcations due to the Camera.on* delegates in OnEnable()/OnDisable().
[ExecuteInEditMode]
public class CustomProjectionTest : MonoBehaviour {
private void OnEnable(){
RenderPipelineManager.beginFrameRendering += ScenePreCull;
RenderPipelineManager.endFrameRendering += ScenePostRender;
//Camera.onPreCull += ScenePreCull;
}
private void OnDisable(){
RenderPipelineManager.beginFrameRendering -= ScenePreCull;
RenderPipelineManager.endFrameRendering -= ScenePostRender;
GetComponent<Camera>().ResetWorldToCameraMatrix();
}
private void ScenePreCull(ScriptableRenderContext context, Camera[] cam){
// If the camera is the scene view camera, call our OnPreCull() event method for it.
OnPreCull();
}
private void ScenePostRender(ScriptableRenderContext context, Camera[] cam){
// Unity's gizmos don't like it when you change the worldToCameraMatrix.
// The workaround is to reset it after rendering.
foreach(var camm in cam)
camm.ResetWorldToCameraMatrix();
}
private Camera _cam;
private void Start(){
_cam = GetComponent<Camera>();
// Set the camera this script is attached to to use orthographic sorting order.
// Instead of using the euclidean distance from the camera, objects will be sorted based on their depth into the scene.
_cam.transparencySortMode = TransparencySortMode.Orthographic;
}
public Vector4 up = new Vector4(0, 1, 0, 0);
// This is a Unity callback and is the ideal place to set the worldToCameraMatrix.
private void OnPreCull(){
var cam = !Application.isPlaying? Camera.current : _cam;
// First calculate the regular worldToCameraMatrix.
// Start with transform.worldToLocalMatrix.
var m = cam.transform.worldToLocalMatrix;
// Then, since Unity uses OpenGL's view matrix conventions we have to flip the output z-value.
m.SetRow(2, -m.GetRow(2));
// Now for the custom projection.
// Set the world's up vector to always align with the camera's up vector.
// Add a small amount of the original up vector to ensure the matrix will be invertible.
m.SetColumn(2, 1e-3f*m.GetColumn(2) - up);
cam.worldToCameraMatrix = m;
}
public static Matrix4x4 ScreenToWorldMatrix(Camera cam){
// Make a matrix that converts from screen coordinates to clip coordinates.
var rect = cam.pixelRect;
var viewportMatrix = Matrix4x4.Ortho(rect.xMin, rect.xMax, rect.yMin, rect.yMax, -1, 1);
// The camera's view-projection matrix converts from world coordinates to clip coordinates.
var vpMatrix = cam.projectionMatrix*cam.worldToCameraMatrix;
// Setting column 2 (z-axis) to identity makes the matrix ignore the z-axis.
// Instead you get the value on the xy plane.
vpMatrix.SetColumn(2, new Vector4(0, 0, 1, 0));
// Going from right to left:
// convert screen coords to clip coords, then clip coords to world coords.
return vpMatrix.inverse*viewportMatrix;
}
public Vector2 ScreenToWorldPoint(Vector2 point){
return ScreenToWorldMatrix(_cam).MultiplyPoint(point);
}
private void Update(){
if(Input.GetMouseButtonUp(0)){
Debug.Log(ScreenToWorldPoint(Input.mousePosition));
}
}
}
Well, kinda. For some reason, it only works in the scene editor, but not in the game. I just have no clue why
With Scene Editor:
In Game

