- Home /
Portal Physics Effect.
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
Answer by DavidDebnar · Mar 28, 2013 at 01:46 PM
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
If i didn't have unity pro but at least want the portal to act like a portal, the magic is these lines right?
var velocity = rigidbody.velocity;
velocity = Vector3.Reflect(velocity,currentPortal.forward);
velocity = currentPortal.InverseTransformDirection(velocity);
velocity = otherPortal.TransformDirection(velocity);
rigidbody.velocity = velocity;
Actually that code didn't do anything at all... I could have just as easily did trigger.gameObject.transform.position = otherPortal.transform.position;
Using your code, if i drop an object into the portal and the other portal is flat on the floor, the ball wont jump out into the air... it just keeps falling down.
Oh wait a $$anonymous$$ute, i didn't realize i have to put the exit portal upside down locally, so when the y axis is pointing down the object flies out into the opposite direction. It appears to work if i just drop the ball in, im going to try some angles.
Yea angles work, heres the simple version for people with INDI$$anonymous$$
//Teleport.js
#pragma strict
var otherPortal : GameObject;
function OnTriggerEnter(trigger : Collider)
{
if(trigger.gameObject.tag == "Player")
{
trigger.transform.position = otherPortal.transform.position;
var velocity : Vector3 = trigger.rigidbody.velocity;
velocity = Vector3.Reflect(velocity,trigger.transform.forward);
velocity = transform.InverseTransformDirection(velocity);
velocity = otherPortal.transform.TransformDirection(velocity);
trigger.rigidbody.velocity = velocity;
}
no, actually, you need the whole thing for it to work properly. I didn't include the code that handles the render textures, so my code is indie too ;)
Answer by Joshua · Apr 26, 2011 at 12:34 AM
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.
Yeah, you need unity pro. If you want to make a simple effect though, you should be fine.
Answer by taoa · May 05, 2011 at 12:15 PM
Try this for the closest 'visual effect' of portal you can do in Unity.
The above link is broken, here it is : http://answers.unity3d.com/questions/39509/portal-2-how-to-make-a-portal-in-unity-3d.html
Answer by Peter G · Apr 26, 2011 at 01:15 AM
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.
Agreed, achieving an instant teleport isn't very hard. Here's a real challenge, though. :)
How would you teleport fluidly, i.e. teleport only the part of character model that has entered the portal? (Enabling you to "stick your head through", and the head appears at the other portal, but the body remains)
Through blood sweat and $$anonymous$$rs. It would take some major procedural mesh generation or you could instantiate a new object at the portal and delete the old one when it is entirely through the hole.
If a "portal" rotates and the other not, rotate the object. How would the scrip?, To turn Portal and the other not, without affecting the position of the object, between portals.