Portal Rooms - AKA Cool Non-Euclidean Geometry trick! - C++
Portals (like in the game Portal) are awesome, but expensive for VR. A seamless portal requires you to render the game twice at full resolution (4 times for stereoscopic VR), one time for the players screen, and one time for the texture showing the other side of the portal. HERE is a great video explaining this.
We never have to do the expensive computations of rendering the screen twice. The portal room acts as a buffer zone between both sides of the portal.
While it doesn’t allow you to look out the other side of the portal (which is objectively amazing), The portal room allows you to surprise the player with the fact that they just passed through a wormhole and didn’t know it until it happened.
How I did it:
Two of the exact same rooms(start/end).
Something to block the player’s view of the teleport (in my case it is two doors).
A box collision that when the player overlaps it, TeleportPlayer() is called.
Here is the code to my teleport function:
void APortalRoom::TeleportPlayer(UPARAM(ref)AActor * TargetRoom, UPARAM(ref)AActor * Player) { APlayerController * PlayerController = GetWorld()->GetFirstPlayerController(); AVRCharacter* VRChar = Cast<AVRCharacter>(Player); FVector SavedVelocity = VRChar->GetCharacterMovement()->GetLastUpdateVelocity(); FRotator PlayerRotation = Player->GetActorRotation(); FVector DeltaPosition = Player->GetActorLocation() - GetActorLocation(); //FVector NewDeltaPosition = DeltaPosition.RotateAngleAxis(DeltaRotation, FVector(0,0,1)); // This works but RotateAngleAxis() has a bit of error past the second decimal (this is fine to use, just not perfect though) FVector NewDeltaPosition = FVector(-DeltaPosition.Y, DeltaPosition.X, DeltaPosition.Z); // This only works for a 90 degree delta rotation FVector TargetLocation = TargetRoom->GetActorLocation() + NewDeltaPosition; float TeleportYaw = PlayerRotation.Yaw + DeltaRotation; FRotator TeleportRotation = FRotator(PlayerRotation.Pitch, TeleportYaw, PlayerRotation.Roll); FVector NewVelocity = SavedVelocity.RotateAngleAxis(DeltaRotation, FVector(0, 0, 1.f)); FVector TargetNormal = TeleportRotation.Vector().GetSafeNormal(); float VelocityLength = SavedVelocity.Size(); FVector ResultVector = TargetNormal * VelocityLength; VRChar->GetCharacterMovement()->Velocity.Set(NewVelocity.X, NewVelocity.Y, 0); PlayerController->SetControlRotation(TeleportRotation); Player->SetActorLocationAndRotation(TargetLocation, TeleportRotation); }
DeltaRotation is the difference in the start and end room rotations (in degrees). In my case it is 90.f
The UPARAM(ref) allows blueprints to call this function with parameters. this makes it wayyy easier to implement collision functions like BeginOverlap().
Summary of the code:
Get the player’s current velocity and rotate it by DeltaRotation to get the new velocity
Get the player’s delta position in relation to the start room, and rotate it by DeltaRotation
Get the player’s current yaw and add DeltaRotation to it to get the new rotation
Add the rotated delta position to the end room location to get the new location
NOW WE TELEPORT!
Set the player’s velocity to the rotated velocity
Set the player’s control rotation to the new rotation
Set the player’s location to the new location
Set the player’s rotation to the new rotation