Impossible Architecture
Hey everyone,
So I've been playing a lot of Davey Wreden and William Pugh's games over the last week or so and love their clever use of "impossible architecture". By this I mean hallways that twist without ever crossing back over themselves, rooms that endlessly repeat, areas that are bigger on the inside etc.
"Bigger on the inside" hallway
Endless Hallway
Now I assume that this is just using well hidden portals of sorts where you have a camera moving relative to the player rendered on a plane and a trigger collider to teleport you to another place. Is there a better way to do this or should I follow that route?
Cheers in advance!
Answer by Bunny83 · Nov 12, 2017 at 01:18 PM
In general the main solution is teleporting as you already mentioned. Visual portals are not necessarily required if you have a clever map design. However in certain situations you just don't have enough room to only use map design as you would have to duplicate large portions of the map or you can't have the transition points too close together.
So your first example video would be hard / almost impossible without visual portals.
The second one (from what we have seen) would be possible just by level design + teleport. Though since it's made with source they might also simply use portals.
Answer by Greenie · Nov 14, 2017 at 04:38 AM
Ok so I have made myself a little game where you can wander around and in one direction there is an endless hallway, in the other is a "bigger on the inside" corridor. I didn't use any portals for the hallway, a simple teleport was far easier. However for the corridor of course a teleport was necessary. Works ok but is quite obvious there is a plane there.
Firstly there's a slight white tint to it. not sure where it's coming from but it looks like you're looking through a window.
Secondly, when facing one direction the camera rotation seems to be a frame out and so you see outside the hallway. I have no idea why this is happening though.
Here is a video demonstrating what I have so far and the issues with it
The lag on the other cameras is the biggest issue, how can I stop that? Induced all my code for reference.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class portal : MonoBehaviour {
public RenderTexture rt;
public GameObject thisPortal;
public GameObject otherPortal;
public Shader cropShader;
public Boolean inPortal;
public float distance;
public float clipping;
private GameObject thisPlane;
private GameObject otherPlane;
private GameObject player;
private Material mat;
// Use this for initialization
void Start () {
thisPortal = this.gameObject;
thisPlane = thisPortal.transform.GetChild(0).gameObject;
otherPlane = otherPortal.transform.GetChild(0).gameObject;
player = Camera.main.transform.parent.gameObject;
rt = new RenderTexture(Screen.width, Screen.height, 16, RenderTextureFormat.ARGB32);
}
// Update is called once per frame
void Update () {
MoveCamera();
Clip();
if (IsOnScreen(thisPlane))
{
//Debug.Log("is on screen");
CreateRenderTexture();
}
else
{
//Debug.Log("is not on screen");
DestroyRenderTexture();
}
if (Input.GetKeyDown("r"))
SceneManager.LoadScene(0);
}
// When you look at the portal, switch on the camera and create the renderTexture
private void CreateRenderTexture()
{
if (rt.IsCreated() == false)
{
//Debug.Log("Render texture doesn't exist... Creating");
rt.Create();
transform.GetChild(1).gameObject.GetComponent<Camera>().targetTexture = rt;
transform.GetChild(1).gameObject.GetComponent<Camera>().enabled = true;
mat = new Material(cropShader);
mat.mainTexture = rt;
thisPlane.GetComponent<MeshRenderer>().material = mat;
}
}
// When you are not looking at the portal, delete the texture and turn off the camera to save resources
private void DestroyRenderTexture()
{
if (rt.IsCreated())
{
//Debug.Log("Render texture exists... Releasing");
rt.Release();
transform.GetChild(1).gameObject.GetComponent<Camera>().enabled = false;
transform.GetChild(1).gameObject.GetComponent<Camera>().targetTexture = null;
thisPlane.GetComponent<MeshRenderer>().material = null;
}
}
// Moves the exit portals camera so it matches the mainCamera's relative position and rotation to the entry portal
private void MoveCamera()
{
var portalPos = thisPlane.transform.position;
var otherPortalPos = otherPlane.transform.position;
var mainCamPos = Camera.main.transform.position;
var playerOffset = mainCamPos - otherPortalPos;
otherPortal.transform.GetChild(1).position = portalPos + playerOffset;
otherPortal.transform.GetChild(1).rotation = Camera.main.transform.rotation;
}
// Checks if any point of an object is on screen
private bool IsOnScreen(GameObject obj)
{
// Defining top left, top right, bottom left, bottom right and middle
Vector3 TL;
Vector3 TR;
Vector3 BL;
Vector3 BR;
Vector3 mid;
// Assigning each of the above a point in world space based off the objects bounding box
BL = Camera.main.WorldToScreenPoint(obj.GetComponent<BoxCollider>().bounds.min);
TR = Camera.main.WorldToScreenPoint(obj.GetComponent<BoxCollider>().bounds.max);
BR = Camera.main.WorldToScreenPoint(new Vector3(obj.GetComponent<BoxCollider>().bounds.max.x, obj.GetComponent<BoxCollider>().bounds.min.y, obj.GetComponent<BoxCollider>().bounds.max.z));
TL = Camera.main.WorldToScreenPoint(new Vector3(obj.GetComponent<BoxCollider>().bounds.min.x, obj.GetComponent<BoxCollider>().bounds.max.y, obj.GetComponent<BoxCollider>().bounds.min.z));
mid = Camera.main.WorldToScreenPoint(obj.GetComponent<BoxCollider>().bounds.center);
// Checking if any of these points are on the screen
if (IsBetween(BL) || IsBetween(TR) || IsBetween(BR) || IsBetween(TL) || IsBetween(mid))
{
return true;
}
// There is a possibility that the player is standing so close to the wall that all the points are outside their cone of vision, so we check if they are within 3 units of the portal
else if (Vector3.Distance(transform.position, Camera.main.transform.position) < 10)
{
return true;
}
else
{
return false;
}
}
// This method just neatens up the IsOnScreen method by doing all the repetative working
private bool IsBetween(Vector3 v3)
{
// Defining the min and max width and height with a 1/3 buffer so you don't catch an untextured plane when you turn too fast
var minWidth = 0 - Screen.width * 0.3;
var minHeight = 0 - Screen.height * 0.3;
var maxWidth = Screen.width + Screen.width * 0.3;
var maxHeight = Screen.height + Screen.height * 0.3;
if (v3.x > minWidth && v3.x < maxWidth && v3.y > minHeight && v3.y < maxHeight && v3.z > 0)
{
return true;
}
else
{
return false;
}
}
private void Clip()
{
var cam = transform.GetChild(1).gameObject;
var dist = Vector3.Distance(transform.position, Camera.main.transform.position);
if(dist >= 9)
{
cam.GetComponent<Camera>().nearClipPlane = dist - 5.8f;
}
else if (dist < 9 && dist > 3)
{
cam.GetComponent<Camera>().nearClipPlane = dist * 0.75f;
}
else
{
cam.GetComponent<Camera>().nearClipPlane = dist * 0.5f;
}
distance = dist;
}
private void OnTriggerEnter(Collider other)
{
Debug.Log("in");
if (inPortal == false)
{
inPortal = true;
otherPortal.GetComponent<portal>().inPortal = true;
player.transform.position = otherPortal.transform.position - (thisPortal.transform.position - player.transform.position);
}
}
private void OnTriggerExit(Collider other)
{
Debug.Log("out");
StopCoroutine("Wait"); // Interrupt in case it's running
StartCoroutine("Wait");
}
IEnumerator Wait()
{
yield return new WaitForSeconds(1);
inPortal = false;
otherPortal.GetComponent<portal>().inPortal = false;
}
}
Your answer
Follow this Question
Related Questions
Creating an Auto-Restart 2 Answers
Architectural help for 2D sprite-layered animations, re: customizable characters 1 Answer
infinity walking track 0 Answers
Rotating a direction vector 0 Answers
How to structure FTL-like events? 0 Answers