Non-Euclidean
Project description
Non Euclidean worlds is very tricky and is not really possible in the Unity engine but with some tricks up my sleeve we can trick the brain into thinking it's possible.
Cube with many worlds
Here we have a cube with six diffrent sides where each side is viewing a diffrent world. This can be done using the graphiccard dedicated processing and clip out the diffrent part before rendering out the picture. The easiest way to do this is by creating two diffrent shaders. One shader where we read what data to render out and one cutout shader where we write our data to. By placeing the cutout shader on each face of the cube as diffrent objects we can create a cube with nothing inside. If we now pair up each side with a read shader object we can generate this illusion.

Scale over distance
Here is another illusion where we can move this cube and the perspective stays the same. If we later move the player around we can se that the cube has changed in size and the optical illusion has succeeded. This can be done pretty easly with just some math. So if we calculate the new scale by taking the scale the box was when picked up divided by the distance the box was from the camera when picked up and divided this by the distans between the camera to the box we get the result below.

using UnityEngine;
using UnityEngine.InputSystem;
public class ScaleOverDistance : MonoBehaviour
{
[SerializeField] private LayerMask _layerMask;
private bool _hasItem;
private GameObject _item;
private Vector3 _pickupScale;
private float _pickupDistance;
private Camera _camera;
private RaycastHit _hit;
private void Start()
{
_camera = Camera.main;
}
private void Update()
{
if (_hasItem)
{
Physics.Raycast(_camera.transform.position, _camera.transform.forward, out _hit, 100000f, _layerMask);
_item.transform.position = GetNewPosition();
_item.transform.localScale = GetNewScale();
}
}
private Vector3 GetNewScale()
{
var newScale = _pickupScale.x / (_pickupDistance / Vector3.Distance(_camera.transform.position, _item.transform.position));
return new Vector3(newScale, newScale, newScale);
}
private Vector3 GetNewPosition()
{
return new Vector3(_hit.point.x, _hit.point.y + _item.transform.localScale.y / 2, _hit.point.z);
}
public void UpdatePickupButton(InputAction.CallbackContext context)
{
if (context.performed)
{
if (!_hasItem)
PickupItem();
else
DropItem();
}
}
private void PickupItem()
{
Physics.Raycast(_camera.transform.position, _camera.transform.forward, out _hit, 100000f);
if (_hit.collider != null && _hit.collider.CompareTag("MovableItem"))
{
_item = _hit.collider.gameObject;
_pickupDistance = Vector3.Distance(_camera.transform.position, _item.transform.position);
_pickupScale = _item.transform.localScale;
_hasItem = true;
}
}
private void DropItem()
{
_item = null;
_hasItem = false;
}
}
Seamless teleportation
I really wanted to trick myself a bit by doing a seamless teleportation and I think i did it. By walking inside this blue halway and ending up somewhere else without any visuable transition was amazing. This was done by blocking the players view and teleport that player to another identical hallway and preserve the velocity in the direction same direction as the new halway. Then I needed to rotate the player and move the player exactly to that position the player was in the previous hallway.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HallwayParadox : MonoBehaviour
{
[SerializeField] private GameObject player, hallwayA, hallwayB;
void Start()
{
hallwayA.GetComponent().OnEnter += Teleport;
hallwayB.GetComponent().OnEnter += Teleport;
}
private void Teleport()
{
var startTeleport = GetNearestTeleport();
if (startTeleport == 'A')
{
player.transform.parent = hallwayA.transform;
var relativePosition = player.transform.localPosition;
player.transform.parent = hallwayB.transform;
player.transform.localPosition = relativePosition;
player.GetComponent().TurnAround();
player.transform.parent = null;
}
else
{
player.transform.parent = hallwayB.transform;
var relativePosition = player.transform.localPosition;
player.transform.parent = hallwayA.transform;
player.transform.localPosition = relativePosition;
player.transform.parent = null;
}
}
private char GetNearestTeleport()
{
return
Vector3.Distance(player.transform.position, hallwayA.transform.position) <
Vector3.Distance(player.transform.position, hallwayB.transform.position) ? 'A' : 'B';
}
}