{"id":9920,"date":"2017-05-07T13:13:16","date_gmt":"2017-05-07T12:13:16","guid":{"rendered":"http:\/\/www.keris-studio.fr\/blog\/?p=9920"},"modified":"2017-05-07T13:49:57","modified_gmt":"2017-05-07T12:49:57","slug":"9920","status":"publish","type":"post","link":"https:\/\/www.keris-studio.fr\/blog\/?p=9920","title":{"rendered":"Unity 3D \u2013 Architectural Walkthrough with interactive actions"},"content":{"rendered":"<p>Unity 3D \u2013 Architectural Walkthrough with interactive actions.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1165\" height=\"662\" class=\"wp-image-9921\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-141.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-141.png 1165w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-141-300x170.png 300w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-141-768x436.png 768w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-141-1024x582.png 1024w\" sizes=\"auto, (max-width: 1165px) 100vw, 1165px\" \/><\/p>\n<p><!--more--><\/p>\n<p>Start in Sketchup<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1545\" height=\"935\" class=\"wp-image-9922\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-142.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-142.png 1545w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-142-300x182.png 300w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-142-768x465.png 768w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-142-1024x620.png 1024w\" sizes=\"auto, (max-width: 1545px) 100vw, 1545px\" \/><\/p>\n<p>Model house done by brunurb L. (<a href=\"https:\/\/3dwarehouse.sketchup.com\/model\/94b5b4e7f7f4243610582ac1f07d1ac\/Mon-Oncle-house\">https:\/\/3dwarehouse.sketchup.com\/model\/94b5b4e7f7f4243610582ac1f07d1ac\/Mon-Oncle-house<\/a>) from Tati\u2019s \u201cMon Oncle\u201d <a href=\"http:\/\/www.imdb.com\/title\/tt0050706\">http:\/\/www.imdb.com\/title\/tt0050706<\/a>.<\/p>\n<p>Before exporting to Unity, beware of reverse faces.<\/p>\n<p>Click <img loading=\"lazy\" decoding=\"async\" width=\"277\" height=\"46\" class=\"wp-image-9923\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-143.png\" \/> and check the blue faces.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1042\" height=\"555\" class=\"wp-image-9924\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-144.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-144.png 1042w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-144-300x160.png 300w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-144-768x409.png 768w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-144-1024x545.png 1024w\" sizes=\"auto, (max-width: 1042px) 100vw, 1042px\" \/><\/p>\n<p>Right Mouse Click \/ reverse faces<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"1200\" class=\"wp-image-9925\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-145.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-145.png 1920w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-145-300x188.png 300w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-145-768x480.png 768w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-145-1024x640.png 1024w\" sizes=\"auto, (max-width: 1920px) 100vw, 1920px\" \/><\/p>\n<p>The model is now clean.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1442\" height=\"911\" class=\"wp-image-9926\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-146.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-146.png 1442w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-146-300x190.png 300w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-146-768x485.png 768w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-146-1024x647.png 1024w\" sizes=\"auto, (max-width: 1442px) 100vw, 1442px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1392\" height=\"815\" class=\"wp-image-9927\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-147.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-147.png 1392w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-147-300x176.png 300w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-147-768x450.png 768w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-147-1024x600.png 1024w\" sizes=\"auto, (max-width: 1392px) 100vw, 1392px\" \/><\/p>\n<p>Check scales before exporting.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"471\" height=\"298\" class=\"wp-image-9928\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-148.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-148.png 471w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-148-300x190.png 300w\" sizes=\"auto, (max-width: 471px) 100vw, 471px\" \/><\/p>\n<p>Export in FBX with these options<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"329\" height=\"339\" class=\"wp-image-9929\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-149.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-149.png 329w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-149-291x300.png 291w\" sizes=\"auto, (max-width: 329px) 100vw, 329px\" \/><\/p>\n<p>Create a new Unity Project<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1002\" height=\"526\" class=\"wp-image-9930\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-150.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-150.png 1002w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-150-300x157.png 300w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-150-768x403.png 768w\" sizes=\"auto, (max-width: 1002px) 100vw, 1002px\" \/><\/p>\n<p>Drag and drop the FBX file.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"914\" height=\"437\" class=\"wp-image-9931\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-151.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-151.png 914w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-151-300x143.png 300w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-151-768x367.png 768w\" sizes=\"auto, (max-width: 914px) 100vw, 914px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1498\" height=\"466\" class=\"wp-image-9932\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-152.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-152.png 1498w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-152-300x93.png 300w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-152-768x239.png 768w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-152-1024x319.png 1024w\" sizes=\"auto, (max-width: 1498px) 100vw, 1498px\" \/><\/p>\n<p>Prepare the model with checking the scale factor. Generate colliders.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"461\" height=\"554\" class=\"wp-image-9933\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-153.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-153.png 461w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-153-250x300.png 250w\" sizes=\"auto, (max-width: 461px) 100vw, 461px\" \/><\/p>\n<p>Create a FPSController and test the visit<br \/>\n<img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"1200\" class=\"wp-image-9934\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-154.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-154.png 1920w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-154-300x188.png 300w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-154-768x480.png 768w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-154-1024x640.png 1024w\" sizes=\"auto, (max-width: 1920px) 100vw, 1920px\" \/><\/p>\n<p><strong>To interact with the environment, we need to free the cursor and make it independent from the mouse look.<\/strong><\/p>\n<p>The FPSController is structured as followed<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"257\" height=\"39\" class=\"wp-image-9935\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-155.png\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"455\" height=\"239\" class=\"wp-image-9936\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-156.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-156.png 455w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-156-300x158.png 300w\" sizes=\"auto, (max-width: 455px) 100vw, 455px\" \/><\/p>\n<p>And<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"454\" height=\"79\" class=\"wp-image-9937\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-157.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-157.png 454w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-157-300x52.png 300w\" sizes=\"auto, (max-width: 454px) 100vw, 454px\" \/><\/p>\n<p>Uncheck First Person Controler and Audio Source<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"579\" height=\"268\" class=\"wp-image-9938\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-158.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-158.png 579w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-158-300x139.png 300w\" sizes=\"auto, (max-width: 579px) 100vw, 579px\" \/><\/p>\n<p>You can create a new asset from the original one in a specific folder.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"315\" height=\"182\" class=\"wp-image-9939\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-159.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-159.png 315w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-159-300x173.png 300w\" sizes=\"auto, (max-width: 315px) 100vw, 315px\" \/><\/p>\n<p>We will then add a first script to the FPSController<\/p>\n<p>First Script CharacterMotor.JS<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"119\" height=\"123\" class=\"wp-image-9940\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-160.png\" \/><\/p>\n<pre lang=\"languagestring\">#pragma\u00a0strict\r\n#pragma\u00a0implicit\r\n#pragma\u00a0downcast\r\n<em>\/\/\u00a0Does\u00a0this\u00a0script\u00a0currently\u00a0respond\u00a0to\u00a0input?<\/em>\r\nvar\u00a0canControl\u00a0:\u00a0boolean\u00a0=\u00a0true;\r\n\r\nvar\u00a0useFixedUpdate\u00a0:\u00a0boolean\u00a0=\u00a0true;\r\n\r\n<em>\/\/\u00a0For\u00a0the\u00a0next\u00a0variables,\u00a0@System.NonSerialized\u00a0tells\u00a0Unity\u00a0to\u00a0not\u00a0serialize\u00a0the\u00a0variable\u00a0or\u00a0show\u00a0it\u00a0in\u00a0the\u00a0inspector\u00a0view.<\/em>\r\n<em>\/\/\u00a0Very\u00a0handy\u00a0for\u00a0organization!<\/em>\r\n\r\n<em>\/\/\u00a0The\u00a0current\u00a0global\u00a0direction\u00a0we\u00a0want\u00a0the\u00a0character\u00a0to\u00a0move\u00a0in.<\/em>\r\n@System.NonSerialized\r\nvar\u00a0inputMoveDirection\u00a0:\u00a0Vector3\u00a0=\u00a0Vector3.zero;\r\n\r\n<em>\/\/\u00a0Is\u00a0the\u00a0jump\u00a0button\u00a0held\u00a0down?\u00a0We\u00a0use\u00a0this\u00a0interface\u00a0instead\u00a0of\u00a0checking<\/em>\r\n<em>\/\/\u00a0for\u00a0the\u00a0jump\u00a0button\u00a0directly\u00a0so\u00a0this\u00a0script\u00a0can\u00a0also\u00a0be\u00a0used\u00a0by\u00a0AIs.<\/em>\r\n@System.NonSerialized\r\nvar\u00a0inputJump\u00a0:\u00a0boolean\u00a0=\u00a0false;\r\n\r\nclass\u00a0CharacterMotorMovement\u00a0{\r\n<em>\/\/\u00a0The\u00a0maximum\u00a0horizontal\u00a0speed\u00a0when\u00a0moving<\/em>\r\nvar\u00a0maxForwardSpeed\u00a0:\u00a0float\u00a0=\u00a010.0;\r\nvar\u00a0maxSidewaysSpeed\u00a0:\u00a0float\u00a0=\u00a010.0;\r\nvar\u00a0maxBackwardsSpeed\u00a0:\u00a0float\u00a0=\u00a010.0;\r\n\r\n<em>\/\/\u00a0Curve\u00a0for\u00a0multiplying\u00a0speed\u00a0based\u00a0on\u00a0slope\u00a0(negative\u00a0=\u00a0downwards)<\/em>\r\nvar\u00a0slopeSpeedMultiplier\u00a0:\u00a0AnimationCurve\u00a0=\u00a0AnimationCurve(Keyframe(-90,\u00a01),\u00a0Keyframe(0,\u00a01),\u00a0Keyframe(90,\u00a00));\r\n\r\n<em>\/\/\u00a0How\u00a0fast\u00a0does\u00a0the\u00a0character\u00a0change\u00a0speeds?\u00a0\u00a0Higher\u00a0is\u00a0faster.<\/em>\r\nvar\u00a0maxGroundAcceleration\u00a0:\u00a0float\u00a0=\u00a030.0;\r\nvar\u00a0maxAirAcceleration\u00a0:\u00a0float\u00a0=\u00a020.0;\r\n\r\n<em>\/\/\u00a0The\u00a0gravity\u00a0for\u00a0the\u00a0character<\/em>\r\nvar\u00a0gravity\u00a0:\u00a0float\u00a0=\u00a010.0;\r\nvar\u00a0maxFallSpeed\u00a0:\u00a0float\u00a0=\u00a020.0;\r\n\r\n<em>\/\/\u00a0For\u00a0the\u00a0next\u00a0variables,\u00a0@System.NonSerialized\u00a0tells\u00a0Unity\u00a0to\u00a0not\u00a0serialize\u00a0the\u00a0variable\u00a0or\u00a0show\u00a0it\u00a0in\u00a0the\u00a0inspector\u00a0view.<\/em>\r\n<em>\/\/\u00a0Very\u00a0handy\u00a0for\u00a0organization!<\/em>\r\n\r\n<em>\/\/\u00a0The\u00a0last\u00a0collision\u00a0flags\u00a0returned\u00a0from\u00a0controller.Move<\/em>\r\n@System.NonSerialized\r\nvar\u00a0collisionFlags\u00a0:\u00a0CollisionFlags;\r\n\r\n<em>\/\/\u00a0We\u00a0will\u00a0keep\u00a0track\u00a0of\u00a0the\u00a0character's\u00a0current\u00a0velocity,<\/em>\r\n@System.NonSerialized\r\nvar\u00a0velocity\u00a0:\u00a0Vector3;\r\n\r\n<em>\/\/\u00a0This\u00a0keeps\u00a0track\u00a0of\u00a0our\u00a0current\u00a0velocity\u00a0while\u00a0we're\u00a0not\u00a0grounded<\/em>\r\n@System.NonSerialized\r\nvar\u00a0frameVelocity\u00a0:\u00a0Vector3\u00a0=\u00a0Vector3.zero;\r\n\r\n@System.NonSerialized\r\nvar\u00a0hitPoint\u00a0:\u00a0Vector3\u00a0=\u00a0Vector3.zero;\r\n\r\n@System.NonSerialized\r\nvar\u00a0lastHitPoint\u00a0:\u00a0Vector3\u00a0=\u00a0Vector3(Mathf.Infinity,\u00a00,\u00a00);\r\n}\r\n\r\nvar\u00a0movement\u00a0:\u00a0CharacterMotorMovement\u00a0=\u00a0CharacterMotorMovement();\r\n\r\nenum\u00a0MovementTransferOnJump\u00a0{\r\nNone,\u00a0<em>\/\/\u00a0The\u00a0jump\u00a0is\u00a0not\u00a0affected\u00a0by\u00a0velocity\u00a0of\u00a0floor\u00a0at\u00a0all.<\/em>\r\nInitTransfer,\u00a0<em>\/\/\u00a0Jump\u00a0gets\u00a0its\u00a0initial\u00a0velocity\u00a0from\u00a0the\u00a0floor,\u00a0then\u00a0gradualy\u00a0comes\u00a0to\u00a0a\u00a0stop.<\/em>\r\nPermaTransfer,\u00a0<em>\/\/\u00a0Jump\u00a0gets\u00a0its\u00a0initial\u00a0velocity\u00a0from\u00a0the\u00a0floor,\u00a0and\u00a0keeps\u00a0that\u00a0velocity\u00a0until\u00a0landing.<\/em>\r\nPermaLocked\u00a0<em>\/\/\u00a0Jump\u00a0is\u00a0relative\u00a0to\u00a0the\u00a0movement\u00a0of\u00a0the\u00a0last\u00a0touched\u00a0floor\u00a0and\u00a0will\u00a0move\u00a0together\u00a0with\u00a0that\u00a0floor.<\/em>\r\n}\r\n\r\n<em>\/\/\u00a0We\u00a0will\u00a0contain\u00a0all\u00a0the\u00a0jumping\u00a0related\u00a0variables\u00a0in\u00a0one\u00a0helper\u00a0class\u00a0for\u00a0clarity.<\/em>\r\nclass\u00a0CharacterMotorJumping\u00a0{\r\n<em>\/\/\u00a0Can\u00a0the\u00a0character\u00a0jump?<\/em>\r\nvar\u00a0enabled\u00a0:\u00a0boolean\u00a0=\u00a0true;\r\n\r\n<em>\/\/\u00a0How\u00a0high\u00a0do\u00a0we\u00a0jump\u00a0when\u00a0pressing\u00a0jump\u00a0and\u00a0letting\u00a0go\u00a0immediately<\/em>\r\nvar\u00a0baseHeight\u00a0:\u00a0float\u00a0=\u00a01.0;\r\n\r\n<em>\/\/\u00a0We\u00a0add\u00a0extraHeight\u00a0units\u00a0(meters)\u00a0on\u00a0top\u00a0when\u00a0holding\u00a0the\u00a0button\u00a0down\u00a0longer\u00a0while\u00a0jumping<\/em>\r\nvar\u00a0extraHeight\u00a0:\u00a0float\u00a0=\u00a04.1;\r\n\r\n<em>\/\/\u00a0How\u00a0much\u00a0does\u00a0the\u00a0character\u00a0jump\u00a0out\u00a0perpendicular\u00a0to\u00a0the\u00a0surface\u00a0on\u00a0walkable\u00a0surfaces?<\/em>\r\n<em>\/\/\u00a00\u00a0means\u00a0a\u00a0fully\u00a0vertical\u00a0jump\u00a0and\u00a01\u00a0means\u00a0fully\u00a0perpendicular.<\/em>\r\nvar\u00a0perpAmount\u00a0:\u00a0float\u00a0=\u00a00.0;\r\n\r\n<em>\/\/\u00a0How\u00a0much\u00a0does\u00a0the\u00a0character\u00a0jump\u00a0out\u00a0perpendicular\u00a0to\u00a0the\u00a0surface\u00a0on\u00a0too\u00a0steep\u00a0surfaces?<\/em>\r\n<em>\/\/\u00a00\u00a0means\u00a0a\u00a0fully\u00a0vertical\u00a0jump\u00a0and\u00a01\u00a0means\u00a0fully\u00a0perpendicular.<\/em>\r\nvar\u00a0steepPerpAmount\u00a0:\u00a0float\u00a0=\u00a00.5;\r\n\r\n<em>\/\/\u00a0For\u00a0the\u00a0next\u00a0variables,\u00a0@System.NonSerialized\u00a0tells\u00a0Unity\u00a0to\u00a0not\u00a0serialize\u00a0the\u00a0variable\u00a0or\u00a0show\u00a0it\u00a0in\u00a0the\u00a0inspector\u00a0view.<\/em>\r\n<em>\/\/\u00a0Very\u00a0handy\u00a0for\u00a0organization!<\/em>\r\n\r\n<em>\/\/\u00a0Are\u00a0we\u00a0jumping?\u00a0(Initiated\u00a0with\u00a0jump\u00a0button\u00a0and\u00a0not\u00a0grounded\u00a0yet)<\/em>\r\n<em>\/\/\u00a0To\u00a0see\u00a0if\u00a0we\u00a0are\u00a0just\u00a0in\u00a0the\u00a0air\u00a0(initiated\u00a0by\u00a0jumping\u00a0OR\u00a0falling)\u00a0see\u00a0the\u00a0grounded\u00a0variable.<\/em>\r\n@System.NonSerialized\r\nvar\u00a0jumping\u00a0:\u00a0boolean\u00a0=\u00a0false;\r\n\r\n@System.NonSerialized\r\nvar\u00a0holdingJumpButton\u00a0:\u00a0boolean\u00a0=\u00a0false;\r\n\r\n<em>\/\/\u00a0the\u00a0time\u00a0we\u00a0jumped\u00a0at\u00a0(Used\u00a0to\u00a0determine\u00a0for\u00a0how\u00a0long\u00a0to\u00a0apply\u00a0extra\u00a0jump\u00a0power\u00a0after\u00a0jumping.)<\/em>\r\n@System.NonSerialized\r\nvar\u00a0lastStartTime\u00a0:\u00a0float\u00a0=\u00a00.0;\r\n\r\n@System.NonSerialized\r\nvar\u00a0lastButtonDownTime\u00a0:\u00a0float\u00a0=\u00a0-100;\r\n\r\n@System.NonSerialized\r\nvar\u00a0jumpDir\u00a0:\u00a0Vector3\u00a0=\u00a0Vector3.up;\r\n}\r\n\r\nvar\u00a0jumping\u00a0:\u00a0CharacterMotorJumping\u00a0=\u00a0CharacterMotorJumping();\r\n\r\nclass\u00a0CharacterMotorMovingPlatform\u00a0{\r\nvar\u00a0enabled\u00a0:\u00a0boolean\u00a0=\u00a0true;\r\n\r\nvar\u00a0movementTransfer\u00a0:\u00a0MovementTransferOnJump\u00a0=\u00a0MovementTransferOnJump.PermaTransfer;\r\n\r\n@System.NonSerialized\r\nvar\u00a0hitPlatform\u00a0:\u00a0Transform;\r\n\r\n@System.NonSerialized\r\nvar\u00a0activePlatform\u00a0:\u00a0Transform;\r\n\r\n@System.NonSerialized\r\nvar\u00a0activeLocalPoint\u00a0:\u00a0Vector3;\r\n\r\n@System.NonSerialized\r\nvar\u00a0activeGlobalPoint\u00a0:\u00a0Vector3;\r\n\r\n@System.NonSerialized\r\nvar\u00a0activeLocalRotation\u00a0:\u00a0Quaternion;\r\n\r\n@System.NonSerialized\r\nvar\u00a0activeGlobalRotation\u00a0:\u00a0Quaternion;\r\n\r\n@System.NonSerialized\r\nvar\u00a0lastMatrix\u00a0:\u00a0Matrix4x4;\r\n\r\n@System.NonSerialized\r\nvar\u00a0platformVelocity\u00a0:\u00a0Vector3;\r\n\r\n@System.NonSerialized\r\nvar\u00a0newPlatform\u00a0:\u00a0boolean;\r\n}\r\n\r\nvar\u00a0movingPlatform\u00a0:\u00a0CharacterMotorMovingPlatform\u00a0=\u00a0CharacterMotorMovingPlatform();\r\n\r\nclass\u00a0CharacterMotorSliding\u00a0{\r\n<em>\/\/\u00a0Does\u00a0the\u00a0character\u00a0slide\u00a0on\u00a0too\u00a0steep\u00a0surfaces?<\/em>\r\nvar\u00a0enabled\u00a0:\u00a0boolean\u00a0=\u00a0true;\r\n\r\n<em>\/\/\u00a0How\u00a0fast\u00a0does\u00a0the\u00a0character\u00a0slide\u00a0on\u00a0steep\u00a0surfaces?<\/em>\r\nvar\u00a0slidingSpeed\u00a0:\u00a0float\u00a0=\u00a015;\r\n\r\n<em>\/\/\u00a0How\u00a0much\u00a0can\u00a0the\u00a0player\u00a0control\u00a0the\u00a0sliding\u00a0direction?<\/em>\r\n<em>\/\/\u00a0If\u00a0the\u00a0value\u00a0is\u00a00.5\u00a0the\u00a0player\u00a0can\u00a0slide\u00a0sideways\u00a0with\u00a0half\u00a0the\u00a0speed\u00a0of\u00a0the\u00a0downwards\u00a0sliding\u00a0speed.<\/em>\r\nvar\u00a0sidewaysControl\u00a0:\u00a0float\u00a0=\u00a01.0;\r\n\r\n<em>\/\/\u00a0How\u00a0much\u00a0can\u00a0the\u00a0player\u00a0influence\u00a0the\u00a0sliding\u00a0speed?<\/em>\r\n<em>\/\/\u00a0If\u00a0the\u00a0value\u00a0is\u00a00.5\u00a0the\u00a0player\u00a0can\u00a0speed\u00a0the\u00a0sliding\u00a0up\u00a0to\u00a0150%\u00a0or\u00a0slow\u00a0it\u00a0down\u00a0to\u00a050%.<\/em>\r\nvar\u00a0speedControl\u00a0:\u00a0float\u00a0=\u00a00.4;\r\n}\r\n\r\nvar\u00a0sliding\u00a0:\u00a0CharacterMotorSliding\u00a0=\u00a0CharacterMotorSliding();\r\n\r\n@System.NonSerialized\r\nvar\u00a0grounded\u00a0:\u00a0boolean\u00a0=\u00a0true;\r\n\r\n@System.NonSerialized\r\nvar\u00a0groundNormal\u00a0:\u00a0Vector3\u00a0=\u00a0Vector3.zero;\r\n\r\nprivate\u00a0var\u00a0lastGroundNormal\u00a0:\u00a0Vector3\u00a0=\u00a0Vector3.zero;\r\n\r\nprivate\u00a0var\u00a0tr\u00a0:\u00a0Transform;\r\n\r\nprivate\u00a0var\u00a0controller\u00a0:\u00a0CharacterController;\r\n\r\nfunction\u00a0Awake\u00a0()\u00a0{\r\ncontroller\u00a0=\u00a0GetComponent\u00a0(CharacterController);\r\ntr\u00a0=\u00a0transform;\r\n}\r\n\r\nprivate\u00a0function\u00a0UpdateFunction\u00a0()\u00a0{\r\n<em>\/\/\u00a0We\u00a0copy\u00a0the\u00a0actual\u00a0velocity\u00a0into\u00a0a\u00a0temporary\u00a0variable\u00a0that\u00a0we\u00a0can\u00a0manipulate.<\/em>\r\nvar\u00a0velocity\u00a0:\u00a0Vector3\u00a0=\u00a0movement.velocity;\r\n\r\n<em>\/\/\u00a0Update\u00a0velocity\u00a0based\u00a0on\u00a0input<\/em>\r\nvelocity\u00a0=\u00a0ApplyInputVelocityChange(velocity);\r\n\r\n<em>\/\/\u00a0Apply\u00a0gravity\u00a0and\u00a0jumping\u00a0force<\/em>\r\nvelocity\u00a0=\u00a0ApplyGravityAndJumping\u00a0(velocity);\r\n\r\n<em>\/\/\u00a0Moving\u00a0platform\u00a0support<\/em>\r\nvar\u00a0moveDistance\u00a0:\u00a0Vector3\u00a0=\u00a0Vector3.zero;\r\nif\u00a0(MoveWithPlatform())\u00a0{\r\nvar\u00a0newGlobalPoint\u00a0:\u00a0Vector3\u00a0=\u00a0movingPlatform.activePlatform.TransformPoint(movingPlatform.activeLocalPoint);\r\nmoveDistance\u00a0=\u00a0(newGlobalPoint\u00a0-\u00a0movingPlatform.activeGlobalPoint);\r\nif\u00a0(moveDistance\u00a0!=\u00a0Vector3.zero)\r\ncontroller.Move(moveDistance);\r\n\r\n<em>\/\/\u00a0Support\u00a0moving\u00a0platform\u00a0rotation\u00a0as\u00a0well:<\/em>\r\nvar\u00a0newGlobalRotation\u00a0:\u00a0Quaternion\u00a0=\u00a0movingPlatform.activePlatform.rotation\u00a0*\u00a0movingPlatform.activeLocalRotation;\r\nvar\u00a0rotationDiff\u00a0:\u00a0Quaternion\u00a0=\u00a0newGlobalRotation\u00a0*\u00a0Quaternion.Inverse(movingPlatform.activeGlobalRotation);\r\n\r\nvar\u00a0yRotation\u00a0=\u00a0rotationDiff.eulerAngles.y;\r\nif\u00a0(yRotation\u00a0!=\u00a00)\u00a0{\r\n<em>\/\/\u00a0Prevent\u00a0rotation\u00a0of\u00a0the\u00a0local\u00a0up\u00a0vector<\/em>\r\ntr.Rotate(0,\u00a0yRotation,\u00a00);\r\n}\r\n}\r\n\r\n<em>\/\/\u00a0Save\u00a0lastPosition\u00a0for\u00a0velocity\u00a0calculation.<\/em>\r\nvar\u00a0lastPosition\u00a0:\u00a0Vector3\u00a0=\u00a0tr.position;\r\n\r\n<em>\/\/\u00a0We\u00a0always\u00a0want\u00a0the\u00a0movement\u00a0to\u00a0be\u00a0framerate\u00a0independent.\u00a0\u00a0Multiplying\u00a0by\u00a0Time.deltaTime\u00a0does\u00a0this.<\/em>\r\nvar\u00a0currentMovementOffset\u00a0:\u00a0Vector3\u00a0=\u00a0velocity\u00a0*\u00a0Time.deltaTime;\r\n\r\n<em>\/\/\u00a0Find\u00a0out\u00a0how\u00a0much\u00a0we\u00a0need\u00a0to\u00a0push\u00a0towards\u00a0the\u00a0ground\u00a0to\u00a0avoid\u00a0loosing\u00a0grouning<\/em>\r\n<em>\/\/\u00a0when\u00a0walking\u00a0down\u00a0a\u00a0step\u00a0or\u00a0over\u00a0a\u00a0sharp\u00a0change\u00a0in\u00a0slope.<\/em>\r\nvar\u00a0pushDownOffset\u00a0:\u00a0float\u00a0=\u00a0Mathf.Max(controller.stepOffset,\u00a0Vector3(currentMovementOffset.x,\u00a00,\u00a0currentMovementOffset.z).magnitude);\r\nif\u00a0(grounded)\r\ncurrentMovementOffset\u00a0-=\u00a0pushDownOffset\u00a0*\u00a0Vector3.up;\r\n\r\n<em>\/\/\u00a0Reset\u00a0variables\u00a0that\u00a0will\u00a0be\u00a0set\u00a0by\u00a0collision\u00a0function<\/em>\r\nmovingPlatform.hitPlatform\u00a0=\u00a0null;\r\ngroundNormal\u00a0=\u00a0Vector3.zero;\r\n\r\n<em>\/\/\u00a0Move\u00a0our\u00a0character!<\/em>\r\nmovement.collisionFlags\u00a0=\u00a0controller.Move\u00a0(currentMovementOffset);\r\n\r\nmovement.lastHitPoint\u00a0=\u00a0movement.hitPoint;\r\nlastGroundNormal\u00a0=\u00a0groundNormal;\r\n\r\nif\u00a0(movingPlatform.enabled\u00a0&amp;&amp;\u00a0movingPlatform.activePlatform\u00a0!=\u00a0movingPlatform.hitPlatform)\u00a0{\r\nif\u00a0(movingPlatform.hitPlatform\u00a0!=\u00a0null)\u00a0{\r\nmovingPlatform.activePlatform\u00a0=\u00a0movingPlatform.hitPlatform;\r\nmovingPlatform.lastMatrix\u00a0=\u00a0movingPlatform.hitPlatform.localToWorldMatrix;\r\nmovingPlatform.newPlatform\u00a0=\u00a0true;\r\n}\r\n}\r\n\r\n<em>\/\/\u00a0Calculate\u00a0the\u00a0velocity\u00a0based\u00a0on\u00a0the\u00a0current\u00a0and\u00a0previous\u00a0position.\u00a0\u00a0<\/em>\r\n<em>\/\/\u00a0This\u00a0means\u00a0our\u00a0velocity\u00a0will\u00a0only\u00a0be\u00a0the\u00a0amount\u00a0the\u00a0character\u00a0actually\u00a0moved\u00a0as\u00a0a\u00a0result\u00a0of\u00a0collisions.<\/em>\r\nvar\u00a0oldHVelocity\u00a0:\u00a0Vector3\u00a0=\u00a0new\u00a0Vector3(velocity.x,\u00a00,\u00a0velocity.z);\r\nmovement.velocity\u00a0=\u00a0(tr.position\u00a0-\u00a0lastPosition)\u00a0\/\u00a0Time.deltaTime;\r\nvar\u00a0newHVelocity\u00a0:\u00a0Vector3\u00a0=\u00a0new\u00a0Vector3(movement.velocity.x,\u00a00,\u00a0movement.velocity.z);\r\n\r\n<em>\/\/\u00a0The\u00a0CharacterController\u00a0can\u00a0be\u00a0moved\u00a0in\u00a0unwanted\u00a0directions\u00a0when\u00a0colliding\u00a0with\u00a0things.<\/em>\r\n<em>\/\/\u00a0We\u00a0want\u00a0to\u00a0prevent\u00a0this\u00a0from\u00a0influencing\u00a0the\u00a0recorded\u00a0velocity.<\/em>\r\nif\u00a0(oldHVelocity\u00a0==\u00a0Vector3.zero)\u00a0{\r\nmovement.velocity\u00a0=\u00a0new\u00a0Vector3(0,\u00a0movement.velocity.y,\u00a00);\r\n}\r\nelse\u00a0{\r\nvar\u00a0projectedNewVelocity\u00a0:\u00a0float\u00a0=\u00a0Vector3.Dot(newHVelocity,\u00a0oldHVelocity)\u00a0\/\u00a0oldHVelocity.sqrMagnitude;\r\nmovement.velocity\u00a0=\u00a0oldHVelocity\u00a0*\u00a0Mathf.Clamp01(projectedNewVelocity)\u00a0+\u00a0movement.velocity.y\u00a0*\u00a0Vector3.up;\r\n}\r\n\r\nif\u00a0(movement.velocity.y\u00a0&lt;\u00a0velocity.y\u00a0-\u00a00.001)\u00a0{\r\nif\u00a0(movement.velocity.y\u00a0&lt;\u00a00)\u00a0{\r\n<em>\/\/\u00a0Something\u00a0is\u00a0forcing\u00a0the\u00a0CharacterController\u00a0down\u00a0faster\u00a0than\u00a0it\u00a0should.<\/em>\r\n<em>\/\/\u00a0Ignore\u00a0this<\/em>\r\nmovement.velocity.y\u00a0=\u00a0velocity.y;\r\n}\r\nelse\u00a0{\r\n<em>\/\/\u00a0The\u00a0upwards\u00a0movement\u00a0of\u00a0the\u00a0CharacterController\u00a0has\u00a0been\u00a0blocked.<\/em>\r\n<em>\/\/\u00a0This\u00a0is\u00a0treated\u00a0like\u00a0a\u00a0ceiling\u00a0collision\u00a0-\u00a0stop\u00a0further\u00a0jumping\u00a0here.<\/em>\r\njumping.holdingJumpButton\u00a0=\u00a0false;\r\n}\r\n}\r\n\r\n<em>\/\/\u00a0We\u00a0were\u00a0grounded\u00a0but\u00a0just\u00a0loosed\u00a0grounding<\/em>\r\nif\u00a0(grounded\u00a0&amp;&amp;\u00a0!IsGroundedTest())\u00a0{\r\ngrounded\u00a0=\u00a0false;\r\n\r\n<em>\/\/\u00a0Apply\u00a0inertia\u00a0from\u00a0platform<\/em>\r\nif\u00a0(movingPlatform.enabled\u00a0&amp;&amp;\r\n(movingPlatform.movementTransfer\u00a0==\u00a0MovementTransferOnJump.InitTransfer\u00a0||\r\nmovingPlatform.movementTransfer\u00a0==\u00a0MovementTransferOnJump.PermaTransfer)\r\n)\u00a0{\r\nmovement.frameVelocity\u00a0=\u00a0movingPlatform.platformVelocity;\r\nmovement.velocity\u00a0+=\u00a0movingPlatform.platformVelocity;\r\n}\r\n\r\nSendMessage(\"OnFall\",\u00a0SendMessageOptions.DontRequireReceiver);\r\n<em>\/\/\u00a0We\u00a0pushed\u00a0the\u00a0character\u00a0down\u00a0to\u00a0ensure\u00a0it\u00a0would\u00a0stay\u00a0on\u00a0the\u00a0ground\u00a0if\u00a0there\u00a0was\u00a0any.<\/em>\r\n<em>\/\/\u00a0But\u00a0there\u00a0wasn't\u00a0so\u00a0now\u00a0we\u00a0cancel\u00a0the\u00a0downwards\u00a0offset\u00a0to\u00a0make\u00a0the\u00a0fall\u00a0smoother.<\/em>\r\ntr.position\u00a0+=\u00a0pushDownOffset\u00a0*\u00a0Vector3.up;\r\n}\r\n<em>\/\/\u00a0We\u00a0were\u00a0not\u00a0grounded\u00a0but\u00a0just\u00a0landed\u00a0on\u00a0something<\/em>\r\nelse\u00a0if\u00a0(!grounded\u00a0&amp;&amp;\u00a0IsGroundedTest())\u00a0{\r\ngrounded\u00a0=\u00a0true;\r\njumping.jumping\u00a0=\u00a0false;\r\nSubtractNewPlatformVelocity();\r\n\r\nSendMessage(\"OnLand\",\u00a0SendMessageOptions.DontRequireReceiver);\r\n}\r\n\r\n<em>\/\/\u00a0Moving\u00a0platforms\u00a0support<\/em>\r\nif\u00a0(MoveWithPlatform())\u00a0{\r\n<em>\/\/\u00a0Use\u00a0the\u00a0center\u00a0of\u00a0the\u00a0lower\u00a0half\u00a0sphere\u00a0of\u00a0the\u00a0capsule\u00a0as\u00a0reference\u00a0point.<\/em>\r\n<em>\/\/\u00a0This\u00a0works\u00a0best\u00a0when\u00a0the\u00a0character\u00a0is\u00a0standing\u00a0on\u00a0moving\u00a0tilting\u00a0platforms.\u00a0<\/em>\r\nmovingPlatform.activeGlobalPoint\u00a0=\u00a0tr.position\u00a0+\u00a0Vector3.up\u00a0*\u00a0(controller.center.y\u00a0-\u00a0controller.height*0.5\u00a0+\u00a0controller.radius);\r\nmovingPlatform.activeLocalPoint\u00a0=\u00a0movingPlatform.activePlatform.InverseTransformPoint(movingPlatform.activeGlobalPoint);\r\n\r\n<em>\/\/\u00a0Support\u00a0moving\u00a0platform\u00a0rotation\u00a0as\u00a0well:<\/em>\r\nmovingPlatform.activeGlobalRotation\u00a0=\u00a0tr.rotation;\r\nmovingPlatform.activeLocalRotation\u00a0=\u00a0Quaternion.Inverse(movingPlatform.activePlatform.rotation)\u00a0*\u00a0movingPlatform.activeGlobalRotation;\r\n}\r\n}\r\n\r\nfunction\u00a0FixedUpdate\u00a0()\u00a0{\r\nif\u00a0(movingPlatform.enabled)\u00a0{\r\nif\u00a0(movingPlatform.activePlatform\u00a0!=\u00a0null)\u00a0{\r\nif\u00a0(!movingPlatform.newPlatform)\u00a0{\r\nvar\u00a0lastVelocity\u00a0:\u00a0Vector3\u00a0=\u00a0movingPlatform.platformVelocity;\r\n\r\nmovingPlatform.platformVelocity\u00a0=\u00a0(\r\nmovingPlatform.activePlatform.localToWorldMatrix.MultiplyPoint3x4(movingPlatform.activeLocalPoint)\r\n-\u00a0movingPlatform.lastMatrix.MultiplyPoint3x4(movingPlatform.activeLocalPoint)\r\n)\u00a0\/\u00a0Time.deltaTime;\r\n}\r\nmovingPlatform.lastMatrix\u00a0=\u00a0movingPlatform.activePlatform.localToWorldMatrix;\r\nmovingPlatform.newPlatform\u00a0=\u00a0false;\r\n}\r\nelse\u00a0{\r\nmovingPlatform.platformVelocity\u00a0=\u00a0Vector3.zero;\r\n}\r\n}\r\n\r\nif\u00a0(useFixedUpdate)\r\nUpdateFunction();\r\n}\r\n\r\nfunction\u00a0Update\u00a0()\u00a0{\r\nif\u00a0(!useFixedUpdate)\r\nUpdateFunction();\r\n}\r\n\r\nprivate\u00a0function\u00a0ApplyInputVelocityChange\u00a0(velocity\u00a0:\u00a0Vector3)\u00a0{\r\nif\u00a0(!canControl)\r\ninputMoveDirection\u00a0=\u00a0Vector3.zero;\r\n\r\n<em>\/\/\u00a0Find\u00a0desired\u00a0velocity<\/em>\r\nvar\u00a0desiredVelocity\u00a0:\u00a0Vector3;\r\nif\u00a0(grounded\u00a0&amp;&amp;\u00a0TooSteep())\u00a0{\r\n<em>\/\/\u00a0The\u00a0direction\u00a0we're\u00a0sliding\u00a0in<\/em>\r\ndesiredVelocity\u00a0=\u00a0Vector3(groundNormal.x,\u00a00,\u00a0groundNormal.z).normalized;\r\n<em>\/\/\u00a0Find\u00a0the\u00a0input\u00a0movement\u00a0direction\u00a0projected\u00a0onto\u00a0the\u00a0sliding\u00a0direction<\/em>\r\nvar\u00a0projectedMoveDir\u00a0=\u00a0Vector3.Project(inputMoveDirection,\u00a0desiredVelocity);\r\n<em>\/\/\u00a0Add\u00a0the\u00a0sliding\u00a0direction,\u00a0the\u00a0spped\u00a0control,\u00a0and\u00a0the\u00a0sideways\u00a0control\u00a0vectors<\/em>\r\ndesiredVelocity\u00a0=\u00a0desiredVelocity\u00a0+\u00a0projectedMoveDir\u00a0*\u00a0sliding.speedControl\u00a0+\u00a0(inputMoveDirection\u00a0-\u00a0projectedMoveDir)\u00a0*\u00a0sliding.sidewaysControl;\r\n<em>\/\/\u00a0Multiply\u00a0with\u00a0the\u00a0sliding\u00a0speed<\/em>\r\ndesiredVelocity\u00a0*=\u00a0sliding.slidingSpeed;\r\n}\r\nelse\r\ndesiredVelocity\u00a0=\u00a0GetDesiredHorizontalVelocity();\r\n\r\nif\u00a0(movingPlatform.enabled\u00a0&amp;&amp;\u00a0movingPlatform.movementTransfer\u00a0==\u00a0MovementTransferOnJump.PermaTransfer)\u00a0{\r\ndesiredVelocity\u00a0+=\u00a0movement.frameVelocity;\r\ndesiredVelocity.y\u00a0=\u00a00;\r\n}\r\n\r\nif\u00a0(grounded)\r\ndesiredVelocity\u00a0=\u00a0AdjustGroundVelocityToNormal(desiredVelocity,\u00a0groundNormal);\r\nelse\r\nvelocity.y\u00a0=\u00a00;\r\n\r\n<em>\/\/\u00a0Enforce\u00a0max\u00a0velocity\u00a0change<\/em>\r\nvar\u00a0maxVelocityChange\u00a0:\u00a0float\u00a0=\u00a0GetMaxAcceleration(grounded)\u00a0*\u00a0Time.deltaTime;\r\nvar\u00a0velocityChangeVector\u00a0:\u00a0Vector3\u00a0=\u00a0(desiredVelocity\u00a0-\u00a0velocity);\r\nif\u00a0(velocityChangeVector.sqrMagnitude\u00a0&gt;\u00a0maxVelocityChange\u00a0*\u00a0maxVelocityChange)\u00a0{\r\nvelocityChangeVector\u00a0=\u00a0velocityChangeVector.normalized\u00a0*\u00a0maxVelocityChange;\r\n}\r\n<em>\/\/\u00a0If\u00a0we're\u00a0in\u00a0the\u00a0air\u00a0and\u00a0don't\u00a0have\u00a0control,\u00a0don't\u00a0apply\u00a0any\u00a0velocity\u00a0change\u00a0at\u00a0all.<\/em>\r\n<em>\/\/\u00a0If\u00a0we're\u00a0on\u00a0the\u00a0ground\u00a0and\u00a0don't\u00a0have\u00a0control\u00a0we\u00a0do\u00a0apply\u00a0it\u00a0-\u00a0it\u00a0will\u00a0correspond\u00a0to\u00a0friction.<\/em>\r\nif\u00a0(grounded\u00a0||\u00a0canControl)\r\nvelocity\u00a0+=\u00a0velocityChangeVector;\r\n\r\nif\u00a0(grounded)\u00a0{\r\n<em>\/\/\u00a0When\u00a0going\u00a0uphill,\u00a0the\u00a0CharacterController\u00a0will\u00a0automatically\u00a0move\u00a0up\u00a0by\u00a0the\u00a0needed\u00a0amount.<\/em>\r\n<em>\/\/\u00a0Not\u00a0moving\u00a0it\u00a0upwards\u00a0manually\u00a0prevent\u00a0risk\u00a0of\u00a0lifting\u00a0off\u00a0from\u00a0the\u00a0ground.<\/em>\r\n<em>\/\/\u00a0When\u00a0going\u00a0downhill,\u00a0DO\u00a0move\u00a0down\u00a0manually,\u00a0as\u00a0gravity\u00a0is\u00a0not\u00a0enough\u00a0on\u00a0steep\u00a0hills.<\/em>\r\nvelocity.y\u00a0=\u00a0Mathf.Min(velocity.y,\u00a00);\r\n}\r\n\r\nreturn\u00a0velocity;\r\n}\r\n\r\nprivate\u00a0function\u00a0ApplyGravityAndJumping\u00a0(velocity\u00a0:\u00a0Vector3)\u00a0{\r\n\r\nif\u00a0(!inputJump\u00a0||\u00a0!canControl)\u00a0{\r\njumping.holdingJumpButton\u00a0=\u00a0false;\r\njumping.lastButtonDownTime\u00a0=\u00a0-100;\r\n}\r\n\r\nif\u00a0(inputJump\u00a0&amp;&amp;\u00a0jumping.lastButtonDownTime\u00a0&lt;\u00a00\u00a0&amp;&amp;\u00a0canControl)\r\njumping.lastButtonDownTime\u00a0=\u00a0Time.time;\r\n\r\nif\u00a0(grounded)\r\nvelocity.y\u00a0=\u00a0Mathf.Min(0,\u00a0velocity.y)\u00a0-\u00a0movement.gravity\u00a0*\u00a0Time.deltaTime;\r\nelse\u00a0{\r\nvelocity.y\u00a0=\u00a0movement.velocity.y\u00a0-\u00a0movement.gravity\u00a0*\u00a0Time.deltaTime;\r\n\r\n<em>\/\/\u00a0When\u00a0jumping\u00a0up\u00a0we\u00a0don't\u00a0apply\u00a0gravity\u00a0for\u00a0some\u00a0time\u00a0when\u00a0the\u00a0user\u00a0is\u00a0holding\u00a0the\u00a0jump\u00a0button.<\/em>\r\n<em>\/\/\u00a0This\u00a0gives\u00a0more\u00a0control\u00a0over\u00a0jump\u00a0height\u00a0by\u00a0pressing\u00a0the\u00a0button\u00a0longer.<\/em>\r\nif\u00a0(jumping.jumping\u00a0&amp;&amp;\u00a0jumping.holdingJumpButton)\u00a0{\r\n<em>\/\/\u00a0Calculate\u00a0the\u00a0duration\u00a0that\u00a0the\u00a0extra\u00a0jump\u00a0force\u00a0should\u00a0have\u00a0effect.<\/em>\r\n<em>\/\/\u00a0If\u00a0we're\u00a0still\u00a0less\u00a0than\u00a0that\u00a0duration\u00a0after\u00a0the\u00a0jumping\u00a0time,\u00a0apply\u00a0the\u00a0force.<\/em>\r\nif\u00a0(Time.time\u00a0&lt;\u00a0jumping.lastStartTime\u00a0+\u00a0jumping.extraHeight\u00a0\/\u00a0CalculateJumpVerticalSpeed(jumping.baseHeight))\u00a0{\r\n<em>\/\/\u00a0Negate\u00a0the\u00a0gravity\u00a0we\u00a0just\u00a0applied,\u00a0except\u00a0we\u00a0push\u00a0in\u00a0jumpDir\u00a0rather\u00a0than\u00a0jump\u00a0upwards.<\/em>\r\nvelocity\u00a0+=\u00a0jumping.jumpDir\u00a0*\u00a0movement.gravity\u00a0*\u00a0Time.deltaTime;\r\n}\r\n}\r\n\r\n<em>\/\/\u00a0Make\u00a0sure\u00a0we\u00a0don't\u00a0fall\u00a0any\u00a0faster\u00a0than\u00a0maxFallSpeed.\u00a0This\u00a0gives\u00a0our\u00a0character\u00a0a\u00a0terminal\u00a0velocity.<\/em>\r\nvelocity.y\u00a0=\u00a0Mathf.Max\u00a0(velocity.y,\u00a0-movement.maxFallSpeed);\r\n}\r\n\r\nif\u00a0(grounded)\u00a0{\r\n<em>\/\/\u00a0Jump\u00a0only\u00a0if\u00a0the\u00a0jump\u00a0button\u00a0was\u00a0pressed\u00a0down\u00a0in\u00a0the\u00a0last\u00a00.2\u00a0seconds.<\/em>\r\n<em>\/\/\u00a0We\u00a0use\u00a0this\u00a0check\u00a0instead\u00a0of\u00a0checking\u00a0if\u00a0it's\u00a0pressed\u00a0down\u00a0right\u00a0now<\/em>\r\n<em>\/\/\u00a0because\u00a0players\u00a0will\u00a0often\u00a0try\u00a0to\u00a0jump\u00a0in\u00a0the\u00a0exact\u00a0moment\u00a0when\u00a0hitting\u00a0the\u00a0ground\u00a0after\u00a0a\u00a0jump<\/em>\r\n<em>\/\/\u00a0and\u00a0if\u00a0they\u00a0hit\u00a0the\u00a0button\u00a0a\u00a0fraction\u00a0of\u00a0a\u00a0second\u00a0too\u00a0soon\u00a0and\u00a0no\u00a0new\u00a0jump\u00a0happens\u00a0as\u00a0a\u00a0consequence,<\/em>\r\n<em>\/\/\u00a0it's\u00a0confusing\u00a0and\u00a0it\u00a0feels\u00a0like\u00a0the\u00a0game\u00a0is\u00a0buggy.<\/em>\r\nif\u00a0(jumping.enabled\u00a0&amp;&amp;\u00a0canControl\u00a0&amp;&amp;\u00a0(Time.time\u00a0-\u00a0jumping.lastButtonDownTime\u00a0&lt;\u00a00.2))\u00a0{\r\ngrounded\u00a0=\u00a0false;\r\njumping.jumping\u00a0=\u00a0true;\r\njumping.lastStartTime\u00a0=\u00a0Time.time;\r\njumping.lastButtonDownTime\u00a0=\u00a0-100;\r\njumping.holdingJumpButton\u00a0=\u00a0true;\r\n\r\n<em>\/\/\u00a0Calculate\u00a0the\u00a0jumping\u00a0direction<\/em>\r\nif\u00a0(TooSteep())\r\njumping.jumpDir\u00a0=\u00a0Vector3.Slerp(Vector3.up,\u00a0groundNormal,\u00a0jumping.steepPerpAmount);\r\nelse\r\njumping.jumpDir\u00a0=\u00a0Vector3.Slerp(Vector3.up,\u00a0groundNormal,\u00a0jumping.perpAmount);\r\n\r\n<em>\/\/\u00a0Apply\u00a0the\u00a0jumping\u00a0force\u00a0to\u00a0the\u00a0velocity.\u00a0Cancel\u00a0any\u00a0vertical\u00a0velocity\u00a0first.<\/em>\r\nvelocity.y\u00a0=\u00a00;\r\nvelocity\u00a0+=\u00a0jumping.jumpDir\u00a0*\u00a0CalculateJumpVerticalSpeed\u00a0(jumping.baseHeight);\r\n\r\n<em>\/\/\u00a0Apply\u00a0inertia\u00a0from\u00a0platform<\/em>\r\nif\u00a0(movingPlatform.enabled\u00a0&amp;&amp;\r\n(movingPlatform.movementTransfer\u00a0==\u00a0MovementTransferOnJump.InitTransfer\u00a0||\r\nmovingPlatform.movementTransfer\u00a0==\u00a0MovementTransferOnJump.PermaTransfer)\r\n)\u00a0{\r\nmovement.frameVelocity\u00a0=\u00a0movingPlatform.platformVelocity;\r\nvelocity\u00a0+=\u00a0movingPlatform.platformVelocity;\r\n}\r\n\r\nSendMessage(\"OnJump\",\u00a0SendMessageOptions.DontRequireReceiver);\r\n}\r\nelse\u00a0{\r\njumping.holdingJumpButton\u00a0=\u00a0false;\r\n}\r\n}\r\n\r\nreturn\u00a0velocity;\r\n}\r\n\r\nfunction\u00a0OnControllerColliderHit\u00a0(hit\u00a0:\u00a0ControllerColliderHit)\u00a0{\r\nif\u00a0(hit.normal.y\u00a0&gt;\u00a00\u00a0&amp;&amp;\u00a0hit.normal.y\u00a0&gt;\u00a0groundNormal.y\u00a0&amp;&amp;\u00a0hit.moveDirection.y\u00a0&lt;\u00a00)\u00a0{\r\nif\u00a0((hit.point\u00a0-\u00a0movement.lastHitPoint).sqrMagnitude\u00a0&gt;\u00a00.001\u00a0||\u00a0lastGroundNormal\u00a0==\u00a0Vector3.zero)\r\ngroundNormal\u00a0=\u00a0hit.normal;\r\nelse\r\ngroundNormal\u00a0=\u00a0lastGroundNormal;\r\n\r\nmovingPlatform.hitPlatform\u00a0=\u00a0hit.collider.transform;\r\nmovement.hitPoint\u00a0=\u00a0hit.point;\r\nmovement.frameVelocity\u00a0=\u00a0Vector3.zero;\r\n}\r\n}\r\n\r\nprivate\u00a0function\u00a0SubtractNewPlatformVelocity\u00a0()\u00a0{\r\n<em>\/\/\u00a0When\u00a0landing,\u00a0subtract\u00a0the\u00a0velocity\u00a0of\u00a0the\u00a0new\u00a0ground\u00a0from\u00a0the\u00a0character's\u00a0velocity<\/em>\r\n<em>\/\/\u00a0since\u00a0movement\u00a0in\u00a0ground\u00a0is\u00a0relative\u00a0to\u00a0the\u00a0movement\u00a0of\u00a0the\u00a0ground.<\/em>\r\nif\u00a0(movingPlatform.enabled\u00a0&amp;&amp;\r\n(movingPlatform.movementTransfer\u00a0==\u00a0MovementTransferOnJump.InitTransfer\u00a0||\r\nmovingPlatform.movementTransfer\u00a0==\u00a0MovementTransferOnJump.PermaTransfer)\r\n)\u00a0{\r\n<em>\/\/\u00a0If\u00a0we\u00a0landed\u00a0on\u00a0a\u00a0new\u00a0platform,\u00a0we\u00a0have\u00a0to\u00a0wait\u00a0for\u00a0two\u00a0FixedUpdates<\/em>\r\n<em>\/\/\u00a0before\u00a0we\u00a0know\u00a0the\u00a0velocity\u00a0of\u00a0the\u00a0platform\u00a0under\u00a0the\u00a0character<\/em>\r\nif\u00a0(movingPlatform.newPlatform)\u00a0{\r\nvar\u00a0platform\u00a0:\u00a0Transform\u00a0=\u00a0movingPlatform.activePlatform;\r\nyield\u00a0WaitForFixedUpdate();\r\nyield\u00a0WaitForFixedUpdate();\r\nif\u00a0(grounded\u00a0&amp;&amp;\u00a0platform\u00a0==\u00a0movingPlatform.activePlatform)\r\nyield\u00a01;\r\n}\r\nmovement.velocity\u00a0-=\u00a0movingPlatform.platformVelocity;\r\n}\r\n}\r\n\r\nprivate\u00a0function\u00a0MoveWithPlatform\u00a0()\u00a0:\u00a0boolean\u00a0{\r\nreturn\u00a0(\r\nmovingPlatform.enabled\r\n&amp;&amp;\u00a0(grounded\u00a0||\u00a0movingPlatform.movementTransfer\u00a0==\u00a0MovementTransferOnJump.PermaLocked)\r\n&amp;&amp;\u00a0movingPlatform.activePlatform\u00a0!=\u00a0null\r\n);\r\n}\r\n\r\nprivate\u00a0function\u00a0GetDesiredHorizontalVelocity\u00a0()\u00a0{\r\n<em>\/\/\u00a0Find\u00a0desired\u00a0velocity<\/em>\r\nvar\u00a0desiredLocalDirection\u00a0:\u00a0Vector3\u00a0=\u00a0tr.InverseTransformDirection(inputMoveDirection);\r\nvar\u00a0maxSpeed\u00a0:\u00a0float\u00a0=\u00a0MaxSpeedInDirection(desiredLocalDirection);\r\nif\u00a0(grounded)\u00a0{\r\n<em>\/\/\u00a0Modify\u00a0max\u00a0speed\u00a0on\u00a0slopes\u00a0based\u00a0on\u00a0slope\u00a0speed\u00a0multiplier\u00a0curve<\/em>\r\nvar\u00a0movementSlopeAngle\u00a0=\u00a0Mathf.Asin(movement.velocity.normalized.y)\u00a0\u00a0*\u00a0Mathf.Rad2Deg;\r\nmaxSpeed\u00a0*=\u00a0movement.slopeSpeedMultiplier.Evaluate(movementSlopeAngle);\r\n}\r\nreturn\u00a0tr.TransformDirection(desiredLocalDirection\u00a0*\u00a0maxSpeed);\r\n}\r\n\r\nprivate\u00a0function\u00a0AdjustGroundVelocityToNormal\u00a0(hVelocity\u00a0:\u00a0Vector3,\u00a0groundNormal\u00a0:\u00a0Vector3)\u00a0:\u00a0Vector3\u00a0{\r\nvar\u00a0sideways\u00a0:\u00a0Vector3\u00a0=\u00a0Vector3.Cross(Vector3.up,\u00a0hVelocity);\r\nreturn\u00a0Vector3.Cross(sideways,\u00a0groundNormal).normalized\u00a0*\u00a0hVelocity.magnitude;\r\n}\r\n\r\nprivate\u00a0function\u00a0IsGroundedTest\u00a0()\u00a0{\r\nreturn\u00a0(groundNormal.y\u00a0&gt;\u00a00.01);\r\n}\r\n\r\nfunction\u00a0GetMaxAcceleration\u00a0(grounded\u00a0:\u00a0boolean)\u00a0:\u00a0float\u00a0{\r\n<em>\/\/\u00a0Maximum\u00a0acceleration\u00a0on\u00a0ground\u00a0and\u00a0in\u00a0air<\/em>\r\nif\u00a0(grounded)\r\nreturn\u00a0movement.maxGroundAcceleration;\r\nelse\r\nreturn\u00a0movement.maxAirAcceleration;\r\n}\r\n\r\nfunction\u00a0CalculateJumpVerticalSpeed\u00a0(targetJumpHeight\u00a0:\u00a0float)\u00a0{\r\n<em>\/\/\u00a0From\u00a0the\u00a0jump\u00a0height\u00a0and\u00a0gravity\u00a0we\u00a0deduce\u00a0the\u00a0upwards\u00a0speed\u00a0<\/em>\r\n<em>\/\/\u00a0for\u00a0the\u00a0character\u00a0to\u00a0reach\u00a0at\u00a0the\u00a0apex.<\/em>\r\nreturn\u00a0Mathf.Sqrt\u00a0(2\u00a0*\u00a0targetJumpHeight\u00a0*\u00a0movement.gravity);\r\n}\r\n\r\nfunction\u00a0IsJumping\u00a0()\u00a0{\r\nreturn\u00a0jumping.jumping;\r\n}\r\n\r\nfunction\u00a0IsSliding\u00a0()\u00a0{\r\nreturn\u00a0(grounded\u00a0&amp;&amp;\u00a0sliding.enabled\u00a0&amp;&amp;\u00a0TooSteep());\r\n}\r\n\r\nfunction\u00a0IsTouchingCeiling\u00a0()\u00a0{\r\nreturn\u00a0(movement.collisionFlags\u00a0&amp;\u00a0CollisionFlags.CollidedAbove)\u00a0!=\u00a00;\r\n}\r\n\r\nfunction\u00a0IsGrounded\u00a0()\u00a0{\r\nreturn\u00a0grounded;\r\n}\r\n\r\nfunction\u00a0TooSteep\u00a0()\u00a0{\r\nreturn\u00a0(groundNormal.y\u00a0&lt;=\u00a0Mathf.Cos(controller.slopeLimit\u00a0*\u00a0Mathf.Deg2Rad));\r\n}\r\n\r\nfunction\u00a0GetDirection\u00a0()\u00a0{\r\nreturn\u00a0inputMoveDirection;\r\n}\r\n\r\nfunction\u00a0SetControllable\u00a0(controllable\u00a0:\u00a0boolean)\u00a0{\r\ncanControl\u00a0=\u00a0controllable;\r\n}\r\n\r\n<em>\/\/\u00a0Project\u00a0a\u00a0direction\u00a0onto\u00a0elliptical\u00a0quater\u00a0segments\u00a0based\u00a0on\u00a0forward,\u00a0sideways,\u00a0and\u00a0backwards\u00a0speed.<\/em>\r\n<em>\/\/\u00a0The\u00a0function\u00a0returns\u00a0the\u00a0length\u00a0of\u00a0the\u00a0resulting\u00a0vector.<\/em>\r\nfunction\u00a0MaxSpeedInDirection\u00a0(desiredMovementDirection\u00a0:\u00a0Vector3)\u00a0:\u00a0float\u00a0{\r\nif\u00a0(desiredMovementDirection\u00a0==\u00a0Vector3.zero)\r\nreturn\u00a00;\r\nelse\u00a0{\r\nvar\u00a0zAxisEllipseMultiplier\u00a0:\u00a0float\u00a0=\u00a0(desiredMovementDirection.z\u00a0&gt;\u00a00\u00a0?\u00a0movement.maxForwardSpeed\u00a0:\u00a0movement.maxBackwardsSpeed)\u00a0\/\u00a0movement.maxSidewaysSpeed;\r\nvar\u00a0temp\u00a0:\u00a0Vector3\u00a0=\u00a0new\u00a0Vector3(desiredMovementDirection.x,\u00a00,\u00a0desiredMovementDirection.z\u00a0\/\u00a0zAxisEllipseMultiplier).normalized;\r\nvar\u00a0length\u00a0:\u00a0float\u00a0=\u00a0new\u00a0Vector3(temp.x,\u00a00,\u00a0temp.z\u00a0*\u00a0zAxisEllipseMultiplier).magnitude\u00a0*\u00a0movement.maxSidewaysSpeed;\r\nreturn\u00a0length;\r\n}\r\n}\r\n\r\nfunction\u00a0SetVelocity\u00a0(velocity\u00a0:\u00a0Vector3)\u00a0{\r\ngrounded\u00a0=\u00a0false;\r\nmovement.velocity\u00a0=\u00a0velocity;\r\nmovement.frameVelocity\u00a0=\u00a0Vector3.zero;\r\nSendMessage(\"OnExternalVelocity\");\r\n}\r\n\r\n<em>\/\/\u00a0Require\u00a0a\u00a0character\u00a0controller\u00a0to\u00a0be\u00a0attached\u00a0to\u00a0the\u00a0same\u00a0game\u00a0object<\/em>\r\n@script\u00a0RequireComponent\u00a0(CharacterController)\r\n@script\u00a0AddComponentMenu\u00a0(\"Character\/Character\u00a0Motor\")\r\n<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"126\" height=\"120\" class=\"wp-image-9941\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-161.png\" \/><\/p>\n<p>Second script The FPSInputController<\/p>\n<pre lang=\"languagestring\">private\u00a0var\u00a0motor\u00a0:\u00a0CharacterMotor;\r\n\r\n<em>\/\/\u00a0Use\u00a0this\u00a0for\u00a0initialization<\/em>\r\nfunction\u00a0Awake\u00a0()\u00a0{\r\nmotor\u00a0=\u00a0GetComponent(CharacterMotor);\r\n}\r\n\r\n<em>\/\/\u00a0Update\u00a0is\u00a0called\u00a0once\u00a0per\u00a0frame<\/em>\r\nfunction\u00a0Update\u00a0()\u00a0{\r\n<em>\/\/\u00a0Get\u00a0the\u00a0input\u00a0vector\u00a0from\u00a0kayboard\u00a0or\u00a0analog\u00a0stick<\/em>\r\nvar\u00a0directionVector\u00a0=\u00a0new\u00a0Vector3(Input.GetAxis(\"Horizontal\"),\u00a00,\u00a0Input.GetAxis(\"Vertical\"));\r\n\r\nif\u00a0(directionVector\u00a0!=\u00a0Vector3.zero)\u00a0{\r\n<em>\/\/\u00a0Get\u00a0the\u00a0length\u00a0of\u00a0the\u00a0directon\u00a0vector\u00a0and\u00a0then\u00a0normalize\u00a0it<\/em>\r\n<em>\/\/\u00a0Dividing\u00a0by\u00a0the\u00a0length\u00a0is\u00a0cheaper\u00a0than\u00a0normalizing\u00a0when\u00a0we\u00a0already\u00a0have\u00a0the\u00a0length\u00a0anyway<\/em>\r\nvar\u00a0directionLength\u00a0=\u00a0directionVector.magnitude;\r\ndirectionVector\u00a0=\u00a0directionVector\u00a0\/\u00a0directionLength;\r\n\r\n<em>\/\/\u00a0Make\u00a0sure\u00a0the\u00a0length\u00a0is\u00a0no\u00a0bigger\u00a0than\u00a01<\/em>\r\ndirectionLength\u00a0=\u00a0Mathf.Min(1,\u00a0directionLength);\r\n\r\n<em>\/\/\u00a0Make\u00a0the\u00a0input\u00a0vector\u00a0more\u00a0sensitive\u00a0towards\u00a0the\u00a0extremes\u00a0and\u00a0less\u00a0sensitive\u00a0in\u00a0the\u00a0middle<\/em>\r\n<em>\/\/\u00a0This\u00a0makes\u00a0it\u00a0easier\u00a0to\u00a0control\u00a0slow\u00a0speeds\u00a0when\u00a0using\u00a0analog\u00a0sticks<\/em>\r\ndirectionLength\u00a0=\u00a0directionLength\u00a0*\u00a0directionLength;\r\n\r\n<em>\/\/\u00a0Multiply\u00a0the\u00a0normalized\u00a0direction\u00a0vector\u00a0by\u00a0the\u00a0modified\u00a0length<\/em>\r\ndirectionVector\u00a0=\u00a0directionVector\u00a0*\u00a0directionLength;\r\n}\r\n\r\n<em>\/\/\u00a0Apply\u00a0the\u00a0direction\u00a0to\u00a0the\u00a0CharacterMotor<\/em>\r\nmotor.inputMoveDirection\u00a0=\u00a0transform.rotation\u00a0*\u00a0directionVector;\r\nmotor.inputJump\u00a0=\u00a0Input.GetButton(\"Jump\");\r\n}\r\n\r\n<em>\/\/\u00a0Require\u00a0a\u00a0character\u00a0controller\u00a0to\u00a0be\u00a0attached\u00a0to\u00a0the\u00a0same\u00a0game\u00a0object<\/em>\r\n@script\u00a0RequireComponent\u00a0(CharacterMotor)\r\n@script\u00a0AddComponentMenu\u00a0(\"Character\/FPS\u00a0Input\u00a0Controller\")\r\n<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"217\" height=\"124\" class=\"wp-image-9942\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-162.png\" \/><\/p>\n<p>The third one is a modification of the MouseLook. In the original MouseLook, it\u2019s difficult to aim and click precisely a specific object or a menu. With this modification, the player presses on MiddleMouseButton (for instance) to look.<\/p>\n<p>Comparison between the standard Mouse Look and the modified one.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1102\" height=\"731\" class=\"wp-image-9943\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-14.gif\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1102\" height=\"731\" class=\"wp-image-9944\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-15.gif\" \/><\/p>\n<p>The mofified MouseLook called MouseLookClick is as followed:<\/p>\n<pre lang=\"languagestring\">using\u00a0UnityEngine;\r\nusing\u00a0System.Collections;\r\n\r\n<em>\/\/\/\u00a0MouseLook\u00a0rotates\u00a0the\u00a0transform\u00a0based\u00a0on\u00a0the\u00a0mouse\u00a0delta.<\/em>\r\n<em>\/\/\/\u00a0Minimum\u00a0and\u00a0Maximum\u00a0values\u00a0can\u00a0be\u00a0used\u00a0to\u00a0constrain\u00a0the\u00a0possible\u00a0rotation<\/em>\r\n\r\n<em>\/\/\/\u00a0To\u00a0make\u00a0an\u00a0FPS\u00a0style\u00a0character:<\/em>\r\n<em>\/\/\/\u00a0-\u00a0Create\u00a0a\u00a0capsule.<\/em>\r\n<em>\/\/\/\u00a0-\u00a0Add\u00a0the\u00a0MouseLook\u00a0script\u00a0to\u00a0the\u00a0capsule.<\/em>\r\n<em>\/\/\/\u00a0\u00a0\u00a0-&gt;\u00a0Set\u00a0the\u00a0mouse\u00a0look\u00a0to\u00a0use\u00a0LookX.\u00a0(You\u00a0want\u00a0to\u00a0only\u00a0turn\u00a0character\u00a0but\u00a0not\u00a0tilt\u00a0it)<\/em>\r\n<em>\/\/\/\u00a0-\u00a0Add\u00a0FPSInputController\u00a0script\u00a0to\u00a0the\u00a0capsule<\/em>\r\n<em>\/\/\/\u00a0\u00a0\u00a0-&gt;\u00a0A\u00a0CharacterMotor\u00a0and\u00a0a\u00a0CharacterController\u00a0component\u00a0will\u00a0be\u00a0automatically\u00a0added.<\/em>\r\n\r\n<em>\/\/\/\u00a0-\u00a0Create\u00a0a\u00a0camera.\u00a0Make\u00a0the\u00a0camera\u00a0a\u00a0child\u00a0of\u00a0the\u00a0capsule.\u00a0Reset\u00a0it's\u00a0transform.<\/em>\r\n<em>\/\/\/\u00a0-\u00a0Add\u00a0a\u00a0MouseLook\u00a0script\u00a0to\u00a0the\u00a0camera.<\/em>\r\n<em>\/\/\/\u00a0\u00a0\u00a0-&gt;\u00a0Set\u00a0the\u00a0mouse\u00a0look\u00a0to\u00a0use\u00a0LookY.\u00a0(You\u00a0want\u00a0the\u00a0camera\u00a0to\u00a0tilt\u00a0up\u00a0and\u00a0down\u00a0like\u00a0a\u00a0head.\u00a0The\u00a0character\u00a0already\u00a0turns.)<\/em>\r\n[AddComponentMenu(\"Camera-Control\/Mouse\u00a0Look\")]\r\npublic\u00a0class\u00a0MouseLookClick\u00a0:\u00a0MonoBehaviour\u00a0{\r\n\r\npublic\u00a0enum\u00a0RotationAxes\u00a0{\u00a0MouseXAndY\u00a0=\u00a00,\u00a0MouseX\u00a0=\u00a01,\u00a0MouseY\u00a0=\u00a02\u00a0}\r\npublic\u00a0RotationAxes\u00a0axes\u00a0=\u00a0RotationAxes.MouseXAndY;\r\npublic\u00a0float\u00a0sensitivityX\u00a0=\u00a015F;\r\npublic\u00a0float\u00a0sensitivityY\u00a0=\u00a015F;\r\n\r\npublic\u00a0float\u00a0minimumX\u00a0=\u00a0-360F;\r\npublic\u00a0float\u00a0maximumX\u00a0=\u00a0360F;\r\n\r\npublic\u00a0float\u00a0minimumY\u00a0=\u00a0-60F;\r\npublic\u00a0float\u00a0maximumY\u00a0=\u00a060F;\r\n\r\nfloat\u00a0rotationY\u00a0=\u00a00F;\r\n\r\nvoid\u00a0Update\u00a0()\r\n{\r\nif\u00a0(axes\u00a0==\u00a0RotationAxes.MouseXAndY)\r\n{\r\nif\u00a0(Input.GetMouseButton\u00a0(2))\r\n{\r\nfloat\u00a0rotationX\u00a0=\u00a0transform.localEulerAngles.y\u00a0+\u00a0Input.GetAxis\u00a0(\"Mouse\u00a0X\")\u00a0*\u00a0sensitivityX;\r\n\r\nrotationY\u00a0+=\u00a0Input.GetAxis\u00a0(\"Mouse\u00a0Y\")\u00a0*\u00a0sensitivityY;\r\nrotationY\u00a0=\u00a0Mathf.Clamp\u00a0(rotationY,\u00a0minimumY,\u00a0maximumY);\r\n\r\ntransform.localEulerAngles\u00a0=\u00a0new\u00a0Vector3\u00a0(-rotationY,\u00a0rotationX,\u00a00);\r\n}\r\n}\r\nelse\u00a0if\u00a0(axes\u00a0==\u00a0RotationAxes.MouseX)\r\n{\r\nif\u00a0(Input.GetMouseButton\u00a0(2))\r\n{\r\ntransform.Rotate\u00a0(0,\u00a0Input.GetAxis\u00a0(\"Mouse\u00a0X\")\u00a0*\u00a0sensitivityX,\u00a00);\r\n}\r\n}\r\nelse\r\n{\r\nif\u00a0(Input.GetMouseButton\u00a0(2))\r\n{\r\nrotationY\u00a0+=\u00a0Input.GetAxis(\"Mouse\u00a0Y\")\u00a0*\u00a0sensitivityY;\r\nrotationY\u00a0=\u00a0Mathf.Clamp\u00a0(rotationY,\u00a0minimumY,\u00a0maximumY);\r\n\r\ntransform.localEulerAngles\u00a0=\u00a0new\u00a0Vector3(-rotationY,\u00a0transform.localEulerAngles.y,\u00a00);\r\n}\r\n}\r\n}\r\n\r\nvoid\u00a0Start\u00a0()\r\n{\r\n<em>\/\/\u00a0Make\u00a0the\u00a0rigid\u00a0body\u00a0not\u00a0change\u00a0rotation<\/em>\r\nif\u00a0(GetComponent&lt;Rigidbody&gt;())\r\nGetComponent&lt;Rigidbody&gt;().freezeRotation\u00a0=\u00a0true;\r\n}\r\n}\r\n<\/pre>\n<p>To choose the Mouse Button:<\/p>\n<pre lang=\"languagestring\">if\u00a0(Input.GetMouseButton\u00a0(2))\r\n<\/pre>\n<p><strong>0 is Left Mouse Button, 1 is Right Mouse Button and 2 is Middle Mouse Button.<\/strong><\/p>\n<p>Drag and drop the scripts to the FPSController<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1269\" height=\"473\" class=\"wp-image-9945\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-163.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-163.png 1269w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-163-300x112.png 300w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-163-768x286.png 768w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-163-1024x382.png 1024w\" sizes=\"auto, (max-width: 1269px) 100vw, 1269px\" \/><\/p>\n<p>Choose Mouse X for Axes<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"590\" height=\"177\" class=\"wp-image-9946\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-164.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-164.png 590w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-164-300x90.png 300w\" sizes=\"auto, (max-width: 590px) 100vw, 590px\" \/><\/p>\n<p>Drag and drop MouseLookClick to the FirstPersonCharacter<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1257\" height=\"558\" class=\"wp-image-9947\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-165.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-165.png 1257w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-165-300x133.png 300w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-165-768x341.png 768w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-165-1024x455.png 1024w\" sizes=\"auto, (max-width: 1257px) 100vw, 1257px\" \/><\/p>\n<p>Choose Mouse Y for Axes<\/p>\n<p>Test.<\/p>\n<p><strong>Now, we will change the wall aspect with menu<\/strong><\/p>\n<p>Create 4 Materials<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"450\" height=\"139\" class=\"wp-image-9948\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-166.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-166.png 450w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-166-300x93.png 300w\" sizes=\"auto, (max-width: 450px) 100vw, 450px\" \/><\/p>\n<p>Copy and convert your texture into Sprites<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"443\" height=\"1025\" class=\"wp-image-9949\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-167.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-167.png 443w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-167-130x300.png 130w\" sizes=\"auto, (max-width: 443px) 100vw, 443px\" \/><\/p>\n<p>Add a Canvas and a first Button (WHITE is the button\u2019s name)<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"293\" height=\"49\" class=\"wp-image-9950\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-168.png\" \/><\/p>\n<p>Name the button, set a good position, then put one the sprite.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"444\" height=\"619\" class=\"wp-image-9951\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-169.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-169.png 444w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-169-215x300.png 215w\" sizes=\"auto, (max-width: 444px) 100vw, 444px\" \/><\/p>\n<p>Set the button\u2019s label<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"435\" height=\"104\" class=\"wp-image-9952\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-170.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-170.png 435w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-170-300x72.png 300w\" sizes=\"auto, (max-width: 435px) 100vw, 435px\" \/><\/p>\n<p>Let\u2019s add a new script<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"97\" height=\"114\" class=\"wp-image-9953\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-171.png\" \/><\/p>\n<pre lang=\"languagestring\">var\u00a0material\u00a0:\u00a0Material;\r\n\r\nvar\u00a0object\u00a0:\u00a0GameObject;\r\n\r\nfunction\u00a0OnMouseDown(){\r\n\r\nGetComponent.&lt;Renderer&gt;().material\u00a0=\u00a0material;\r\n\r\n}\r\n<\/pre>\n<p>Apply the script to an object, a group a walls in our example.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1322\" height=\"508\" class=\"wp-image-9954\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-172.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-172.png 1322w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-172-300x115.png 300w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-172-768x295.png 768w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-172-1024x393.png 1024w\" sizes=\"auto, (max-width: 1322px) 100vw, 1322px\" \/><\/p>\n<p>It is not necessary to fill material and object items.<\/p>\n<p>Go back to the button Script.<\/p>\n<p>Add the walls as objects, select MeshRenderer \/ Material material and select the material<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"1200\" class=\"wp-image-9955\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-173.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-173.png 1920w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-173-300x188.png 300w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-173-768x480.png 768w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-173-1024x640.png 1024w\" sizes=\"auto, (max-width: 1920px) 100vw, 1920px\" \/><\/p>\n<p>Final result.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"445\" height=\"306\" class=\"wp-image-9956\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-174.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-174.png 445w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-174-300x206.png 300w\" sizes=\"auto, (max-width: 445px) 100vw, 445px\" \/><\/p>\n<p>Repeat for each button<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"442\" height=\"710\" class=\"wp-image-9957\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-175.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-175.png 442w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-175-187x300.png 187w\" sizes=\"auto, (max-width: 442px) 100vw, 442px\" \/><\/p>\n<p>Result<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1143\" height=\"632\" class=\"wp-image-9958\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-176.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-176.png 1143w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-176-300x166.png 300w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-176-768x425.png 768w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-176-1024x566.png 1024w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-176-672x372.png 672w\" sizes=\"auto, (max-width: 1143px) 100vw, 1143px\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1186\" height=\"715\" class=\"wp-image-9959\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-16.gif\" \/><\/p>\n<p>Now, we will decide that the menu appears only when stepping close to the fa\u00e7ade.<\/p>\n<p>Create a box.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"922\" height=\"578\" class=\"wp-image-9960\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-177.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-177.png 922w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-177-300x188.png 300w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-177-768x481.png 768w\" sizes=\"auto, (max-width: 922px) 100vw, 922px\" \/><\/p>\n<p>The box is not rendered and is trigger<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1182\" height=\"692\" class=\"wp-image-9961\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-178.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-178.png 1182w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-178-300x176.png 300w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-178-768x450.png 768w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-178-1024x599.png 1024w\" sizes=\"auto, (max-width: 1182px) 100vw, 1182px\" \/><\/p>\n<p>Create a java Script Load-Menu<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"121\" height=\"125\" class=\"wp-image-9962\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-179.png\" \/><\/p>\n<pre lang=\"languagestring\">var\u00a0Menu\u00a0:\u00a0Canvas;\r\n\r\nfunction\u00a0\u00a0OnTriggerEnter\u00a0()\u00a0{\r\nMenu.enabled\u00a0=\u00a0true;\r\n}\r\n\r\nfunction\u00a0OnTriggerExit\u00a0()\u00a0{\r\nMenu.enabled\u00a0=\u00a0false;\r\n}\r\n<\/pre>\n<p>Set Canvas Off<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"446\" height=\"579\" class=\"wp-image-9963\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-180.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-180.png 446w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-180-231x300.png 231w\" sizes=\"auto, (max-width: 446px) 100vw, 446px\" \/><\/p>\n<p>Drag and drop the script to the trigger cube<\/p>\n<p>Add Canvas in the var Zone<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"439\" height=\"472\" class=\"wp-image-9964\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-181.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-181.png 439w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-181-279x300.png 279w\" sizes=\"auto, (max-width: 439px) 100vw, 439px\" \/><\/p>\n<p>Test<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1186\" height=\"715\" class=\"wp-image-9965\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-17.gif\" \/><\/p>\n<p>Last step, create a menu to load or mask objects<\/p>\n<p>Create a Java Script (not necessary in fact)<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"122\" height=\"112\" class=\"wp-image-9966\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-182.png\" \/><\/p>\n<pre lang=\"languagestring\">var\u00a0Tree\u00a0:\u00a0GameObject;\r\n\r\nfunction\u00a0\u00a0Start\u00a0()\u00a0{\r\nTree.enabled\u00a0=\u00a0true;\r\n}\r\n<\/pre>\n<p>Create a new Canvas and a Toggle button, I named it Trees-On-Off<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"186\" height=\"72\" class=\"wp-image-9967\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-183.png\" \/><\/p>\n<p>Add objects in the options as followed<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"431\" height=\"176\" class=\"wp-image-9968\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-184.png\" srcset=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-184.png 431w, https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-184-300x123.png 300w\" sizes=\"auto, (max-width: 431px) 100vw, 431px\" \/><\/p>\n<p>Test<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1186\" height=\"715\" class=\"wp-image-9969\" src=\"https:\/\/www.keris-studio.fr\/blog\/wp-content\/word-image-18.gif\" \/><\/p>\n<pre lang=\"languagestring\"><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Unity 3D \u2013 Architectural Walkthrough with interactive actions.<\/p>\n","protected":false},"author":2,"featured_media":9978,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6,190,195,8,26,174,205],"tags":[301,302,42,306,303,91,47,80],"class_list":["post-9920","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-3d","category-architecture-2","category-creation","category-methodologie","category-production","category-simulations","category-unity-2","tag-3d","tag-3dsmax","tag-animation","tag-linkedin","tag-methodologie","tag-modelisation","tag-simulation","tag-tutorial"],"_links":{"self":[{"href":"https:\/\/www.keris-studio.fr\/blog\/index.php?rest_route=\/wp\/v2\/posts\/9920","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.keris-studio.fr\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.keris-studio.fr\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.keris-studio.fr\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.keris-studio.fr\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=9920"}],"version-history":[{"count":8,"href":"https:\/\/www.keris-studio.fr\/blog\/index.php?rest_route=\/wp\/v2\/posts\/9920\/revisions"}],"predecessor-version":[{"id":9977,"href":"https:\/\/www.keris-studio.fr\/blog\/index.php?rest_route=\/wp\/v2\/posts\/9920\/revisions\/9977"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.keris-studio.fr\/blog\/index.php?rest_route=\/wp\/v2\/media\/9978"}],"wp:attachment":[{"href":"https:\/\/www.keris-studio.fr\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=9920"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.keris-studio.fr\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=9920"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.keris-studio.fr\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=9920"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}