Is there anyway to make a portal Effect in the physics engine, like in the Valve game, so things just move on through them? probably not, but just wondering.
thanks
Is there anyway to make a portal Effect in the physics engine, like in the Valve game, so things just move on through them? probably not, but just wondering.
thanks
To create a Portal like visual effect, you unfortunately need Unity Pro because of render textures. To do this, you’d create a camera pointing outwards from Portal B, save it to a render texture and apply it as the texture of Portal A and vise versa.
To allow objects to pass trough the portal, you need to place a trigger collider on both portals. A script would then detect if an object hit the portal and create a second duplicate of the object, to create an illusion of smoothly walking trough the portal.
Then it needs to preserve the velocity of the object, make it point out of the second portal and pass it to the duplicate.
An example:
var velocity = rigidbody.velocity;
velocity = Vector3.Reflect(velocity,currentPortal.forward);
velocity = currentPortal.InverseTransformDirection(velocity);
velocity = otherPortal.TransformDirection(velocity);
rigidbody.velocity = velocity;
In this example I take the original object’s velocity, reflect it to the forward direction of the portal using the Vector3.Reflect function, inverse it, align it to otherPortal’s direction and assign it.
Also make sure that the position is updated correctly. The code for it is very similar to the code that preserves the velocity.
var pos : Vector3 = currentPortal.position - transform.position;
pos = Vector3.Reflect(pos, currentPortal.forward);
pos = otherPortal.TransformDirection(pos);
pos += otherPortal.position;
myDuplicate.position = pos;
It takes the direction between the object and the portal. Reflects it to the second portal and moves it to the second portal.
Simple example
Both examples work with Unity Free and don’t need Unity Pro, because the code handling render textures isn’t included.
Before using the either of these following scripts create tags “PortalA” and “PortalB” - apply them to the portals and assign the script to a rigidbody entity.
public var ignoreTime : float = 1;
function OnTriggerStay(c : Collider) {
if(c.CompareTag("PortalA") || c.CompareTag("PortalB")) {
var otherPortal : Transform = GameObject.FindGameObjectWithTag(c.CompareTag("PortalA")?"PortalB":"PortalA").transform;
transform.localEulerAngles += Quaternion.LookRotation(otherPortal.forward).eulerAngles;
transform.position = otherPortal.TransformDirection(Vector3.Reflect(c.transform.position - transform.position, c.transform.forward)) + otherPortal.position;
rigidbody.velocity = otherPortal.TransformDirection(c.transform.InverseTransformDirection(Vector3.Reflect(rigidbody.velocity,c.transform.forward)));
Physics.IgnoreCollision(collider,otherPortal.collider,true);
yield WaitForSeconds(ignoreTime);
Physics.IgnoreCollision(collider,otherPortal.collider,false);
}
}
Full example [TeleportableEntity.js]
class TeleportableEntity extends MonoBehaviour {
public var myDuplicate : Transform;
public var currentPortal : Transform;
public var otherPortal : Transform;
private var shouldUpdate : boolean = false;
private var initialPosition : Vector3;
function UpdateDuplicate() {
shouldUpdate = true;
while(myDuplicate != null && shouldUpdate) {
if(!shouldUpdate) break;
ForceUpdateDuplicate();
yield;
}
}
function ForceUpdateDuplicate() {
var rot : Vector3 = Quaternion.LookRotation(otherPortal.forward).eulerAngles;
rot += transform.localEulerAngles;
myDuplicate.localEulerAngles = rot;
var pos : Vector3 = currentPortal.position - transform.position;
pos = Vector3.Reflect(pos, currentPortal.forward);
pos = otherPortal.TransformDirection(pos);
pos += otherPortal.position;
myDuplicate.position = pos;
}
function CreateDuplicate () {
myDuplicate = Instantiate(transform, transform.position, transform.rotation) as Transform;
Destroy(myDuplicate.GetComponent(TeleportableEntity));
Destroy(myDuplicate.collider);
UpdateDuplicate();
}
function OnTriggerEnter (c : Collider) {
if(myDuplicate != null || shouldUpdate) return;
if(c.CompareTag("PortalA"))
otherPortal = GameObject.FindGameObjectWithTag("PortalB").transform;
else if(c.CompareTag("PortalB"))
otherPortal = GameObject.FindGameObjectWithTag("PortalA").transform;
else return;
currentPortal = c.transform;
initialPosition = transform.position;
CreateDuplicate();
}
function OnTriggerExit (c : Collider) {
if(currentPortal != null && c == currentPortal.collider) {
shouldUpdate = false;
if((currentPortal.position - transform.position).magnitude < (initialPosition - transform.position).magnitude) {
ForceUpdateDuplicate();
transform.position = myDuplicate.position;
transform.rotation = myDuplicate.rotation;
var velocity = rigidbody.velocity;
velocity = Vector3.Reflect(velocity,currentPortal.forward);
velocity = currentPortal.InverseTransformDirection(velocity);
velocity = otherPortal.TransformDirection(velocity);
rigidbody.velocity = velocity;
}
DestroyDuplicate();
}
}
function DestroyDuplicate () {
Destroy(myDuplicate.gameObject);
myDuplicate = null;
currentPortal = null;
otherPortal = null;
}
}
–David
Do you mean create two portals, have the view as if looking out of the other portal when looking into the one portal and have objects go through the portal and come out the other one?
For the visible effect use two cameras that go to the portals position, for the teleportation effect use a trigger and transform position. To render a cameras view as a texture you'd need Unity pro though, I think.
It is very doable. But it could become a somewhat complicated procedure. If you want to keep it simple, I would try this. Find the magnitude of the velocity as an object enters the portal, then move the object to the portal and set its velocity to the portal's normal times the magnitude we just recorded:
var otherPortal : Transform;
var normal : Vector3; //You can get this various ways, but you will need a surface normal
//transform.forward would probably work.
function OnTriggerEnter (col : Collider) {
var velocity : float = col.rigidbody.velocity.magnitude;
//var velocity : float = col.GetComponent<CharacterController>().velocity.magnitude;
col.transform.position = otherPortal.position;
col.rigidbody.velocity = otherPortal.GetComponent.<ThisScript>().normal * velocity;
//col.GetComponent<CharacterController>().Move(otherPortal.GetComponent.<ThisScript>().normal * velocity * Time.deltatime);
}
This is a really simple script. It takes the velocity an object enters the first portal at and shoots it out the second portal along its normal at the same velocity. The only thing you lose is the possibility of entering the portal at an angle. That can be solved but requires some more math that I can try to replicate if you would like.