{"id":199,"date":"2015-09-12T00:04:39","date_gmt":"2015-09-11T14:04:39","guid":{"rendered":"https:\/\/ocias.com\/blog\/?p=199"},"modified":"2015-09-16T20:40:13","modified_gmt":"2015-09-16T10:40:13","slug":"how-to-set-up-a-fixed-camera-system-in-unity","status":"publish","type":"post","link":"https:\/\/ocias.com\/blog\/how-to-set-up-a-fixed-camera-system-in-unity\/","title":{"rendered":"How to Set Up a Fixed Camera System in Unity"},"content":{"rendered":"<p>Last month\u00a0I released my latest short, &#8220;<a href=\"https:\/\/ocias.com\/blog\/foggy-shore-new-game-release\/\" target=\"_blank\">Foggy Shore<\/a>&#8220;, built in Unity. It\u00a0used a fixed camera system &#8211; whereby the camera is fixed in place until the player leaves the frame, at which point the camera jumps to a new appropriate shot.<!--more--><\/p>\n<p>Cinematic games and storytelling are a great passion of mine, and a good bit of this design has been fuelled by a chat with my friend <a href=\"http:\/\/www.richardlemarchand.com\" target=\"_blank\">Richard Lemarchand<\/a>\u00a0earlier this year\u00a0at\u00a0GDC2015. It&#8217;s time\u00a0to share some of the design and tech behind what I built for Foggy Shore.<\/p>\n<p>Most games these days use a single unlimited tracking shot, and game engines are generally designed to facilitate what most games use, so it can be a bit difficult to figure out how to make something that goes against the grain. This guide will quickly get you\u00a0up and running with a more cinematic camera system.<\/p>\n<h2>Why Use a Fixed Camera?<\/h2>\n<p>When your camera is locked to a given perspective, the player gets to experience exploring the entire screen space with their eyes. In most modern games &#8211; where the player character is locked to one part of the screen &#8211; the player&#8217;s focus remains solely on the centre of the screen, skirting out to the peripheral to discover new enemies or items. This subconsciously reduces the environment design and allows for no visual composition without a large conscious effort on the part of the player.<\/p>\n<p>Not only do tracking cameras send a lot of the art budget to waste by this subconscious direction of focus, but using a fixed camera can reduce the strain and demands of building a 3D game by ensuring parts of a scene will never be viewed. This allows for greatly simplified UV mapping and prop distribution, as will as removing the burden of creating distance-terrain details.<\/p>\n<p>However, more than anything else, fixed cameras greatly increase your potential audience by almost entirely mitigating motion sickness. It is estimated that up to a third of people suffer from some degree of motion sickness while playing 3D games, and this is a total barrier to entry for many of those people. If you&#8217;re interested in the relationship between motion sickness and 3D games, as well as how to mitigate its effects while still using dynamic cameras, <a href=\"http:\/\/www.gdcvault.com\/play\/1020460\/50-Camera\" target=\"_blank\">John Nesky&#8217;s talks<\/a> on the development of the camera in Journey are very much worth checking out.<\/p>\n<p>As for why <strong>not<\/strong> to use a fixed camera? It&#8217;s unusual, does not necessarily allow the player to look at everything they want to, and can weaken a sense of digital proprioception.<\/p>\n<h2>History of Fixed Camera\u00a0In Games<\/h2>\n<p>Fixed position cameras used to be really popular around the Playstation 1\u00a0era when we didn&#8217;t have the processing power to deliver full 3D environments, and it was sort of our only option for higher fidelity graphics: a pre-rendered image background with 3D characters moving on top of it. The games that come to most peoples&#8217; minds are Resident Evil and Final Fantasy.<\/p>\n<p><a href=\"https:\/\/ocias.com\/blog\/wp-content\/uploads\/2015\/09\/ff8re3.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-220 size-large\" src=\"https:\/\/ocias.com\/blog\/wp-content\/uploads\/2015\/09\/ff8re3-1024x384.jpg\" alt=\"Fixed camera in Final Fantasy VIII and Resident Evil 3\" width=\"660\" height=\"248\" srcset=\"https:\/\/ocias.com\/blog\/wp-content\/uploads\/2015\/09\/ff8re3-1024x384.jpg 1024w, https:\/\/ocias.com\/blog\/wp-content\/uploads\/2015\/09\/ff8re3-300x113.jpg 300w, https:\/\/ocias.com\/blog\/wp-content\/uploads\/2015\/09\/ff8re3.jpg 1200w\" sizes=\"auto, (max-width: 660px) 100vw, 660px\" \/><\/a><\/p>\n<p>As game consoles got more powerful, and the hunger for games-as-simulations increased, full 3D environments became the standard. With all the time spent on creating full-3D environments, there was a great desire to show them off, so control of the camera was mostly deferred to the player, and tracking cameras have been the standard ever since.<\/p>\n<p>Due to the rising pressure on indie game developers to move into the 3D space, and with the artistic flexibility camera editing provides, I believe fixed camera design will\u00a0make a comeback.<\/p>\n<h2>Part 1: Basic\u00a0Setup<\/h2>\n<p>The most obvious way to build this might appear to be to set up a camera for every shot, and then swap the &#8220;main&#8221; camera to be the one that currently focuses on the player. The problem with this is that all active cameras render the scene even if they&#8217;re not visible, so you would have to carefully make sure no more than one camera is ever active. Additionally, it&#8217;s easy to accidentally omit an image effect, or have an inconsistent field of view. Basically, it&#8217;s likely to run into some human error.<\/p>\n<p>Instead, the best solution I found is to use a single camera, and use some stored information to jump it around and update the attributes\u00a0I want.<\/p>\n<p><a href=\"https:\/\/ocias.com\/blog\/wp-content\/uploads\/2015\/09\/Screen-Shot-2015-09-11-at-11.27.34-pm.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-221\" src=\"https:\/\/ocias.com\/blog\/wp-content\/uploads\/2015\/09\/Screen-Shot-2015-09-11-at-11.27.34-pm-1024x488.jpg\" alt=\"Multiple shots and trigger volumes\" width=\"660\" height=\"315\" srcset=\"https:\/\/ocias.com\/blog\/wp-content\/uploads\/2015\/09\/Screen-Shot-2015-09-11-at-11.27.34-pm-1024x488.jpg 1024w, https:\/\/ocias.com\/blog\/wp-content\/uploads\/2015\/09\/Screen-Shot-2015-09-11-at-11.27.34-pm-300x143.jpg 300w, https:\/\/ocias.com\/blog\/wp-content\/uploads\/2015\/09\/Screen-Shot-2015-09-11-at-11.27.34-pm.jpg 1912w\" sizes=\"auto, (max-width: 660px) 100vw, 660px\" \/><\/a><\/p>\n<p>This\u00a0fixed camera system requires two main components:<\/p>\n<ol>\n<li><strong>Shots<\/strong> &#8211; data detailing\u00a0the camera, including position, rotation and any other attributes you want to specify\u00a0such as field-of-view.<\/li>\n<li><strong>Shot\u00a0Zones<\/strong> &#8211; trigger volumes\u00a0that set the camera to a particular shot when the player is within them.<\/li>\n<\/ol>\n<p>Let&#8217;s set these two up:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">using UnityEngine;\r\nusing System.Collections;\r\n\r\npublic class Shot : MonoBehaviour {\r\n    public void CutToShot () {\r\n        Camera.main.transform.localPosition = transform.position;\r\n        Camera.main.transform.localRotation = transform.rotation;\r\n    }\r\n}<\/pre>\n<p>Shot just has one function that jumps the camera to the same position and rotation as the object the script is attached to. We use the local transforms so that we can wrap the camera in other objects later in case we want to add effects like camera shakes or similar.<\/p>\n<p>To quickly position a Shot object, move your viewport to a desired angle, select your Shot object and press Shift-Cmd\/Ctrl-F.<\/p>\n<p>Next we have the ShotZone we&#8217;ll attach to our triggers:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">using UnityEngine;\r\nusing System.Collections;\r\n\r\npublic class ShotZone : MonoBehaviour {\r\n    public Shot targetShot;\r\n    void OnTriggerEnter (Collider c) {\r\n        if (c.CompareTag(\"Player\")) {\r\n            targetShot.CutToShot();\r\n        }\r\n    }\r\n}<\/pre>\n<p>To set up a ShotZone, you need to attach it to an object which has a collider marked as a trigger, and also assign\u00a0your player the &#8220;player&#8221; tag (you can always expand this with a fancy layer mask later if necessary). This tag check prevents the camera switching shots whenever other characters enter a ShotZone.<\/p>\n<p>Finally, attach the appropriate Shot into the targetShot property in the inspector.<\/p>\n<p>That&#8217;s it! You have a basic fixed camera system working!<\/p>\n<h2>Part 2: Quality of Life Additions<\/h2>\n<p>Being able to preview your shots immediately is critical in laying out a scene. The best way to do this is set up the game camera to snap to a shot when it&#8217;s selected outside of edit mode. Just add this to your Shot script:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-highlight=\"10-14\">using UnityEngine;\r\nusing System.Collections;\r\n\r\npublic class Shot : MonoBehaviour {\r\n    public void CutToShot () {\r\n        Camera.main.transform.localPosition = transform.position;\r\n        Camera.main.transform.localRotation = transform.rotation;\r\n    }\r\n\r\n    void OnDrawGizmosSelected () {\r\n        if (!Application.isPlaying) {\r\n            CutToShot();\r\n        }\r\n    }\r\n}<\/pre>\n<p>To make your shots easier to select, give them an icon using the coloured cubes beside their names in the inspector.<\/p>\n<p>Next, manually shifting your shots by their axis is very finicky, it&#8217;s much faster to get good results if we can manipulate their focal points and positions separately. Let&#8217;s add a focal point to our Shot script (and visualise it on our object too):<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-highlight=\"5,8,19-22\">using UnityEngine;\r\nusing System.Collections;\r\n\r\npublic class Shot : MonoBehaviour {\r\n    public Vector3 focalPoint;\r\n\r\n    public void CutToShot () {\r\n        transform.LookAt(focalPoint);\r\n        Camera.main.transform.localPosition = transform.position;\r\n        Camera.main.transform.localRotation = transform.rotation;\r\n    }\r\n\r\n    void OnDrawGizmosSelected () {\r\n        if (!Application.isPlaying) {\r\n            CutToShot();\r\n        }\r\n    }\r\n\r\n    void OnDrawGizmos () {\r\n        Gizmos.color = Color.green;\r\n        Gizmos.DrawLine (transform.position, focalPoint);\r\n    }\r\n}<\/pre>\n<p>This gives much nicer results, but it would be even better if we could have a handle to move our focal point around with, just like we can move our shot position. To achieve this, we need to add an Editor script. So add a folder called &#8220;Editor&#8221; if you don&#8217;t already have one, and add a ShotEditor script to it:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">using UnityEngine;\r\nusing UnityEditor;\r\n\r\n[CustomEditor(typeof (Shot))]\r\npublic class ShotEditor : Editor {\r\n    Shot shot;\r\n\r\n    void OnEnable() {\r\n        shot = target as Shot;\r\n    }\r\n\r\n    void OnSceneGUI ()  {\r\n        Undo.RecordObject(shot, \"Target Move\");\r\n        shot.focalPoint = Handles.PositionHandle (\r\n            shot.focalPoint,\r\n            Quaternion.identity);\r\n        if (GUI.changed) {\r\n            EditorUtility.SetDirty(target);\r\n        }\r\n    }\r\n}<\/pre>\n<p>If you&#8217;ve implemented all of this, you should have shot objects that look something like this in your editor now:<\/p>\n<h2><a href=\"https:\/\/ocias.com\/blog\/wp-content\/uploads\/2015\/09\/Screen-Shot-2015-09-11-at-8.15.04-pm.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-216\" src=\"https:\/\/ocias.com\/blog\/wp-content\/uploads\/2015\/09\/Screen-Shot-2015-09-11-at-8.15.04-pm-1024x492.jpg\" alt=\"A shot with two position handles.\" width=\"660\" height=\"317\" srcset=\"https:\/\/ocias.com\/blog\/wp-content\/uploads\/2015\/09\/Screen-Shot-2015-09-11-at-8.15.04-pm-1024x492.jpg 1024w, https:\/\/ocias.com\/blog\/wp-content\/uploads\/2015\/09\/Screen-Shot-2015-09-11-at-8.15.04-pm-300x144.jpg 300w, https:\/\/ocias.com\/blog\/wp-content\/uploads\/2015\/09\/Screen-Shot-2015-09-11-at-8.15.04-pm.jpg 1908w\" sizes=\"auto, (max-width: 660px) 100vw, 660px\" \/><\/a>Part 3: Setting Up The Trigger Volumes<\/h2>\n<p>While developing Foggy Shore I found setting up the ShotZone areas, aka the trigger volumes, to be one of the tricker aspects of using a fixed camera system.<\/p>\n<p>First of all, the trigger volumes are almost never going to be perfect rectangular prisms and having to go to a separate 3D app to create your volumes defeats the purpose of working with a nice game engine like Unity. Unfortunately <a href=\"https:\/\/www.youtube.com\/watch?v=6ubu76gEvM8\" target=\"_blank\">modern level design tools are\u00a0in a pretty sorry state<\/a>, but the best solution I found was to use <a href=\"https:\/\/www.assetstore.unity3d.com\/en\/#!\/content\/11919\" target=\"_blank\">Procore Prototype<\/a>.\u00a0It&#8217;s not perfect, but it&#8217;s free and much faster than importing\/exporting meshes.<\/p>\n<p>One\u00a0thing you have to bear in mind with a fixed camera system is that it&#8217;s easy for the player to end up off-screen if your trigger volumes aren&#8217;t designed carefully with your shots in mind. I struggled a lot with this before finding that the best process was to set up the Shot <em>last<\/em>. Start by defining your trigger volumes, deciding the size of the space the player should walk within, how long it should take to cross a shot. Then create a Shot, moving it back until it visually encompasses the entire ShotZone. Afterwards you can make tweaks to both the objects once\u00a0you have something that mostly works.\u00a0This might sound counter-intuitive to the idea of cinematography, but I found it gave some pretty organic and interesting results none-the-less.<\/p>\n<h2>Part 4: Mixing Audio and Shot Cuts<\/h2>\n<p>One reason cuts and fixed shots are so common and effective in cinema but so rare in games is the presence of pre-designed audio.<\/p>\n<p>Cuts are a jump in space, and somehow the viewer has to be able to process continuity. In film, audio often\u00a0cuts at a\u00a0different time\u00a0from\u00a0video. This disparity is critical in allowing viewers to &#8220;bridge&#8221; the gap between two images and maintain immersion. In games this can be quite difficult to do, as we don&#8217;t really know what the player will do in the future, so we cannot cut audio ahead of time.<\/p>\n<p>So the best option we have is to cut audio <em>after<\/em> video. I achieved this by separating the Audio Listener component onto another GameObject and updating its position a second after the camera moves:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-highlight=\"6,7,13,16-18\">using UnityEngine;\r\nusing System.Collections;\r\n\r\npublic class Shot : MonoBehaviour {\r\n    public Vector3 focalPoint;\r\n    public GameObject audioListener\r\n    public float audioCutDelay = 1.0f;\r\n\r\n    public void CutToShot () {\r\n        transform.LookAt(focalPoint);\r\n        Camera.main.transform.localPosition = transform.position;\r\n        Camera.main.transform.localRotation = transform.rotation;\r\n        Invoke (\"MoveAudioListener\", audioCutDelay);\r\n    }\r\n\r\n    public void MoveAudioListener () {\r\n        audioListener.transform.position = transform.position;\r\n    }\r\n\r\n    void OnDrawGizmosSelected () {\r\n        if (!Application.isPlaying) {\r\n            CutToShot();\r\n        }\r\n    }\r\n\r\n    void OnDrawGizmos () {\r\n        Gizmos.color = Color.green;\r\n        Gizmos.DrawLine (transform.position, focalPoint);\r\n    }\r\n}<\/pre>\n<h2>Tips &amp; Pitfalls<\/h2>\n<ul>\n<li>As motion sickness is avoided, you have a lot more flexibility with your camera&#8217;s field-of-view than most games. Don&#8217;t be afraid to take advantage of it!<\/li>\n<li>Film tends to use 55mm lenses, equivalent to about 36\u00b0 on Unity cameras. Many games these days go for around\u00a0around 55\u00b0 (close to a 35mm camera). I like to use a Panavision-like FOV of 28\u00b0.<\/li>\n<li>Adding a catch-all camera system on top of a fixed-point system &#8211; even if it&#8217;s a regular tracking camera &#8211; is a really good idea. You don&#8217;t want to have to wait until your shots are implemented before being able to test new level geometry.<\/li>\n<li>You may want to separate out shot focal points onto separate transforms so you can translate them en-masse if your level design shifts significantly.<\/li>\n<\/ul>\n<h2>Good Luck!<\/h2>\n<p>Thanks for reading and I hope this will\u00a0enable you to create a fantastic cinematic game!<\/p>\n<p>If you run into any problems, or have any questions, feel free to send me an email or leave a comment here and I&#8217;ll help you as best I can.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Last month\u00a0I released my latest short, &#8220;Foggy Shore&#8220;, built in Unity. It\u00a0used a fixed camera system &#8211; whereby the camera is fixed in place until the player leaves the frame, at which point the camera jumps to a new appropriate shot.<\/p>\n","protected":false},"author":1,"featured_media":223,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[1],"tags":[9,3],"class_list":["post-199","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorised","tag-tutorial","tag-unity3d"],"_links":{"self":[{"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/posts\/199","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/comments?post=199"}],"version-history":[{"count":10,"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/posts\/199\/revisions"}],"predecessor-version":[{"id":231,"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/posts\/199\/revisions\/231"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/media\/223"}],"wp:attachment":[{"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/media?parent=199"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/categories?post=199"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ocias.com\/blog\/wp-json\/wp\/v2\/tags?post=199"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}