[In the second in this series of articles which deconstructs a particularly fun or interesting mechanic in a recent, relevant game, Jeremy Alessi pulls apart Valve's Portal and puts it back together again -- to give us a clearer understanding of the compellingly-executed teleportation mechanics central to that game's astoundingly entertaining gameplay.]
EDITOR'S NOTE: To download the associated demo and code sample for Portal Demystified, please click here.
Welcome back to Games Demystified. This month we'll be examining the chief gameplay mechanic behind last year's amazing Portal! Anyone who's played Portal has heard GLaDOS state, "Speedy thing goes in, speedy thing comes out".
That line sums up the mechanics that distinguished Portal from the rest of the herd this past winter. Sure, the impressive story and rendering also had a part, but it's simply not a game without the mechanics.
Game mechanics are usually abstractions based on real-world physics. In the previous Games Demystified column, we covered gravity as it was applied in Super Mario Galaxy, a force that is mostly unexplainable and yet tremendously fun with the proper application in gameplay.
This go-round we're looking at wormholes, Einstein-Rosen Bridges, or portals, if you will. These phenomena are predicted by Einstein's theory of General Relativity.
Like gravity, Einstein-Rosen Bridges are mostly a mystery. Perhaps someday through imagination and cool video games we'll gain a proper understanding. Until then, we've got a nice pseudo-laboratory in Portal to experiment with these mechanics.
Just playing the game is a mind-trip. Exactly how can we simulate a tunnel or wormhole through the fabric of space-time? How do we do speedy-in, speedy-out, momentum redirection -- or "flinging", as Valve calls it?
Teleport mechanics in video games are nothing new. Puzzles from the original Gauntlet were memorable -- and more than likely, that wasn't the first game to use teleportation as a gameplay mechanic. The difference between Portal and all those that came before it is that Portal's teleportation acts as a frictionless tube between point A and point B.
Physics are still hard at work inside the frictionless tube. Instead of simply repositioning an object from point A to point B, the player enters point A with full velocity and exits point B with the same speed, but moving in a new direction.
In order to break this down we will visit our old friend velocity, which is a vector. A vector is a magnitude coupled with a direction. Velocity is a vector whose magnitude is representative of speed. When the player collides with a portal we can extract the speed component of our velocity and redirect it to form a new velocity vector.
Now what term from the last column do we remember most of all? Those who remembered "surface normal" get a pat on the back along with a cookie. When a portal is applied to a wall, floor, or ramp in the game, the surface normal of the surface is communicated to the portal inhabiting it.
We then take the velocity we entered a portal with, and break it down into components. To do this we multiply by the surface normals of the exit portal.
In the end, Portal uses the traditional teleportation mechanic with a velocity and orientation change. Here's a simple code snippet, again written in Blitz3D, which is an excellent tool for quick prototyping like this.
;====== UPDATE PLAYER PHYSICS ======
player.playerClass = Object.playerClass( localPlayer )
AlignToVector( player\skeleton, 0, 1, 0, 2, 0.05 )
portalCollision = EntityCollided( player\skeleton, PORTALSTATIC )
If ( portalCollision <> 0 And portalOpen() )
EntityType( player\skeleton, STATIC )
For portal.portalClass = Each portalClass
If portal\entrance <> portalCollision Then Exit
PositionEntity( player\skeleton, EntityX( portal\entrance ), EntityY( portal\entrance ), EntityZ( portal\entrance ) )
velocity# = Sqr( player\vX# ^ 2 + player\vY# ^ 2 + player\vZ# ^ 2 )
player\vX# = portal\nX# * velocity# * 1.2
player\vY# = portal\nY# * velocity# * 1.2
player\vZ# = portal\nZ# * velocity# * 1.2
RotateEntity( player\skeleton, VectorPitch( portal\nX#, portal\nY#, portal\nZ# ), EntityYaw( portal\entrance ) + 180 - ( EntityYaw( portalCollision ) - EntityYaw( player\skeleton ) ), 0 )
TranslateEntity( player\skeleton, 10 * portal\nX#, 10 * portal\nY#, 10 * portal\nZ# )
EntityType( player\skeleton, DYNAMIC )
RenderWorld( 1 )
If ( EntityCollided( player\skeleton, STATIC ) )
player\vX# = 0
player\vZ# = 0
If ( CollisionNY( player\skeleton, 1 ) > 0.5 )
player\vY# = 0
MoveEntity( player\skeleton, player\mX#, 0, player\mZ# )
player\jumpTimer = MilliSecs()
player\vX# = player\vX#
player\vY# = player\vY# - 1
player\vZ# = player\vZ#
TranslateEntity( player\skeleton, player\vX#, player\vY#, player\vZ# )
MoveEntity( player\skeleton, player\mX#, 0, player\mZ# )
If player\jumpTimer + 250 > MilliSecs() Then player\vY# = player\vY# + player\mY#
The first part of our updatePlayerPhysics() function is an alignToVector() call which slowly re-orients our player back to the upright position. The parameters ( 0, 1, 0, 2, 0.05) dictate that our character will be aligned full upright along the Y-axis at a speed of 0.05, or very slowly. As the player tumbles through portals, the character's alignment gets constantly re-arranged.
Anyone who's played Portal should realize the developers' extra special attention to detail in this regard, as the player and camera are constantly being adjusted to keep the experience as intuitive as possible. In this demo, the player is slowly rotated back to the upright position, which is something to take notice of while tumbling through the puzzles in Portal.
The next part of the code is where the interesting bits lay. There is a check for portal collision. If it returns true, we will set our player collision type to static and search for the other end of our portal.
Afterwards, we reposition our player at the entrance of the opposing portal (or the exit of the portal we had previously collided with).
Then we calculate our player's total velocity and break it down into components relative to the portal we're coming out of -- by multiplying the player's total velocity by the surface normals of the exit portal.
This is what gives us the "waterslide" effect of entering in one direction and exiting at the same speed but in an entirely new direction.
The rest of the code within the first conditional helps create a smooth transition from one portal to the other. The first part sets the character's orientation with respect to its rotation upon entering the portal.
Afterward, the player is positioned out away from the exit portal an appropriate amount to avoid choppiness or repeated collision, once our player is returned to dynamic collision status.
Finally, a capture, update, and render are completed to avoid any choppiness in the visuals. Portal is especially slick in this regard, as it is virtually seamless when it teleports the player character about.
The final section of the updatePlayerPhysics() function is the flipside of the portal collision conditional. All it does is stop the player upon a static collision or otherwise pull the player down due to gravity.
The most interesting component of this is the collision normal conditional, which only stops the character from falling if they are on flat enough ground. Without that, the player can climb walls.
Portal is a landmark title. It's deceptively simple at a glance, but in truth is the implementation is a highly polished and complex system, which this article only begins to scratch the surface of. As stated above, the story was entertaining and the rendering was nothing short of pure eye candy.
However, without the mechanics Portal would not have been nearly as compelling. The title really conveys the feeling of partaking in the empirical process of groundbreaking science.
Just reading about something like this in a Michael Crichton novel or watching a sci-fi flick with great CG effects would not have had the same impact. Portal's mechanics distinguished it from other games this year, and more importantly, they distinguished games as a whole from the rest of contemporary media.
And now I'm going to grab a piece of cake from my rehearsal dinner and I'm not lying! Until next time... do not step into the... ... ... ... it may cause permanent malfunctions such as death.
[To download the associated demo and code sample for Portal Demystified, please click here.]