Hi, I have developed an idea for a project within the Unity Engine that involves simulating Non-Euclidean geometry within a 2D space using immersive portals. I would like to be able to slice an object when it enters the portal as shown in the image below.
I am not particularly concerned with rendering views through portals, and the portals will be viewed from the side.
I am not entirely sure how to begin going about a project like this, and would appreciate some guidance.
If you can leave a reply, I will be most grateful.
You described what you don’t want to know about (rendering) but you didn’t describe specifically what you do want to know about. It’s an open question and hard to answer.
The simple version is to duplicate the object as it goes through the portal, particularly its visual components, the Sprite Renderer.
From your example, now you have two ladders. Then use a Sprite Mask to and update the SpriteRenderer with the proper mask settings. As the original ladder enters the portal it gets masked out and the second ladder exits the other portal, also exiting a mask.
You may need some extra fx to cover up a hard edge from the mask.
The very first step is to make a rough proof of concept in the editor where you manually move your objects around, coming in and out of the mask.
Yes, this ^ ^ ^ Make the ladder, clone it, make two rectangular sprite masks appropriate to each portal, and get it to at least manually look right. This should take like five minutes TOPS. Grab a blank piece of paper and a pen, scribble out some sprites, photograph them with your phone, and cut them out in Unity.
Once you have that up, save it. That’s your reference. Everything else is just sloggy legwork related to handling the ladder as a dynamic object, cloning it to the other portal, making game design choices to keep the portals from overlapping, etc.
GO!
Thank you all for the replies! I have completed the project. Well, the parts I wanted to complete.
I mainly wanted to see if I could simulate a sort of TARDIS effect in Unity and figured the portal thing would of been a good analogy. The only thing I didn’t really know how to handle was object collision when and object entered the portal.
I didn’t want to use meshes, and using duplicate objects would result in visible discrepancies, so I needed to determine how to mask a 2D collider.
I spent some time researching how I could mask a collider and found Angus Johnson’s clipper library.
I downloaded the library and wrote the following code that allows for masking of the PolygonCollider2D component.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Clipper2Lib;
public class TheProperPolygonClipper : MonoBehaviour
{
[Header("Assigning")]
public PolygonCollider2D subjCol; //The cookie dough
public PolygonCollider2D clipCol; //The cookie cutter
public PolygonCollider2D outputCol; //The cut cookie dough
public enum MaskInteractionOps {VisibleInsideMask, VisibleOutsideMask }
public MaskInteractionOps MaskInteraction;
[Header("Debugging")]
public List<Path> debugSubjPoints = new List<Path>();
public List<Path> debugClipPoints = new List<Path>();
public List<Path> debugClippedPoints = new List<Path>();
float scale = 1000000;
// Start is called before the first frame update
void Update()
{
//Get tho polygon collider points and place them into lists of vector2's, floats, and scaled integers.
List<Path> subjPoints = PolygonColliderPointsToPointData(subjCol);
List<Path> clipPoints = PolygonColliderPointsToPointData(clipCol);
debugSubjPoints = subjPoints;
debugClipPoints = clipPoints;
//Turn the points into paths64 values
Paths64 subj = new Paths64();
Paths64 clip = new Paths64();
foreach (Path P in subjPoints)
{
subj.Add(Clipper.MakePath(P.scaledPoints.ToArray()));
}
foreach (Path P in clipPoints)
{
clip.Add(Clipper.MakePath(P.scaledPoints.ToArray()));
}
//Performs a clip
Paths64 solution = new Paths64();
if (MaskInteraction == MaskInteractionOps.VisibleOutsideMask)
{
solution = Clipper.Difference(subj, clip, FillRule.NonZero);
}
else
{
solution = Clipper.Intersect(subj, clip, FillRule.NonZero);
}
//Puts the cut points onto another collider
List<List<Vector2>> pointsAfterClip = Paths64ToListListVector2(solution);
AddPointsToPolygonCollider(pointsAfterClip, outputCol);
}
private List<Vector2> m_Path = new List<Vector2>();
List<Path> PolygonColliderPointsToPointData(PolygonCollider2D PC)
{
List<Path> ListToReturn = new List<Path>();
for (int i = 0; i < PC.pathCount; i++)
{
m_Path.Clear();
PC.GetPath(i, m_Path);
Path currentPath = new Path();
foreach (var point in m_Path)
{
var worldPoint = PC.transform.localToWorldMatrix.MultiplyPoint(point);
currentPath.PointsAsVector2.Add(worldPoint);
}
ListToReturn.Add(currentPath);
}
for (int i = 0; i < ListToReturn.Count; i++)
{
foreach(Vector2 V in ListToReturn[i].PointsAsVector2)
{
ListToReturn[i].pointsAsFloats.Add(V.x);
ListToReturn[i].pointsAsFloats.Add(V.y);
}
}
for (int i = 0; i < ListToReturn.Count; i++)
{
foreach(float f in ListToReturn[i].pointsAsFloats)
{
ListToReturn[i].scaledPoints.Add((int)(f * scale));
}
}
return ListToReturn;
}
//A class containing the points as vector2's, the points as a list of floats, and a list of scaled ints.
[System.Serializable]
public class Path
{
public List<Vector2> PointsAsVector2 = new List<Vector2>();
public List<float> pointsAsFloats = new List<float>();
public List<int> scaledPoints = new List<int>();
}
List<List<Vector2>> Paths64ToListListVector2(Paths64 p)
{
List<List<Vector2>> listToReturn = new List<List<Vector2>>();
for(int i = 0; i < p.Count; i++)
{
List<Vector2> pathList = new List<Vector2>();
Path64 path = p[i];
foreach(Point64 point in path)
{
pathList.Add(new Vector2(point.X / scale, point.Y / scale));
}
listToReturn.Add(pathList);
}
return listToReturn;
}
void AddPointsToPolygonCollider(List<List<Vector2>> points, PolygonCollider2D PC)
{
PC.pathCount = 0;
for (int i = 0; i < points.Count; i++)
{
List<Vector2> worldPath = points[i];
Vector2[] localPath = new Vector2[worldPath.Count];
for (int j = 0; j < worldPath.Count; j++)
{
localPath[j] = transform.InverseTransformPoint(worldPath[j]);
}
PC.SetPath(i, localPath);
}
}
}
From there, it’s just a matter of assigning a few collision layers, and I can simulate the TARDIS effect rather well.
Thank you all for the responses nontheless.