[{"data":1,"prerenderedAt":2564},["ShallowReactive",2],{"tutorial-2020-01-25d-game-in-unity-part-4":3},{"tutorial":4,"series":581,"author":2555},{"id":5,"title":6,"author":7,"body":8,"category":562,"cover":563,"date":566,"description":567,"extension":568,"meta":569,"navigation":233,"path":570,"seo":571,"series":572,"stem":573,"tags":574,"youtube":577,"youtubeRange":578,"__hash__":579,"slug":580},"posts\u002Fposts\u002F2020\u002F01\u002F25d-game-in-unity-part-4.md","Gate\u002FDoor Hinge","nir",{"type":9,"value":10,"toc":556},"minimark",[11,15,20,27,30,33,38,44,47,51,57,66,85,89,92,107,113,116,119,122,125,128,134,140,146,155,161,164,173,185,188,191,194,198,201,523,531,534,537,541,544,549,552],[12,13,14],"p",{},"In part four of 2.5D Game in Unity tutorial, we will add hinges for creating doors and gates in our 2.5D world. While at first, it may seem straightforward to implement, it’s quite tricky to implement in our 45 degrees rotated world.",[16,17,19],"article-giphy",{"gif":18},"dZ3HGKnnvw6buM0nWU","\n2.5D hinge issues.\n",[12,21,22,23,26],{},"Right of the bat, let’s look into why creating such a door or gate is problematic in our world.",[24,25],"br",{},"\nWe have in front of us a rotated sprite, here is the catch; let’s mimic a gate opening by rotating it on the Y-axis. When looking from the side, we can notice that when rotating the gate in world space detaches it from the fence. When we rotate the gate in its local space, we have a different issue – it collides into the ground.",[12,28,29],{},"To tackle the issues we encountered, we will first create the scene while keeping the physics objects separated from the visualization. After that, we will create a shader based on an idea that we discussed in the previous part. And eventually, we will create a script to connect the physics and the shader.",[12,31,32],{},"In this tutorial, I’m using the Universal Render Pipeline with Shader Graph. I’m working with Unity 2019.3, but this should work with any version that has support for Shader Graph.",[34,35,37],"h2",{"id":36},"texture-configuration","Texture Configuration",[12,39,40,41,43],{},"This part is super simple, but crucial for our gate to work.",[24,42],{},"\nWhenever we import a sprite texture that is supposed to rotate on a hinge, we must remember to set its pivot to one of its bottom corners. I went ahead and imported sprites of a fence and a gate.",[45,46],"article-assets",{},[34,48,50],{"id":49},"scene-building","Scene Building",[12,52,53,54,56],{},"Let’s create a new scene. I want to create a composition of a two-part gate with a fence from each side. One thing vital before we go on is to recall from the first video of this tutorial that although we rotate our assets, we position the colliders perpendicular to the ground.",[24,55],{},"\nLet’s create a fence object, it should have a Rigidbody component marked as kinematic, and a box collider. Inside it, a child object rotated by 45 degrees on the X-axis and a sprite renderer with the fence sprite. Make sure the collider matches the size of the fence sprite.",[12,58,59,60,62,63,65],{},"Now, create a new object for the gate with two child objects – name one Renderer and the other Physics.",[24,61],{},"\nAdd a sprite renderer component to the Renderer object, rotate it by 45 degrees on the X-axis, and set the gate sprite.",[24,64],{},"\nTo the Physics object, we need to add three components, a Rigidbody, a box collider configured to match the sprite dimensions, and a Hinge Joint with the following configuration:",[67,68,69,73,76,79,82],"ul",{},[70,71,72],"li",{},"The connected body should be the adjacent fence’s Rigidbody, set the anchor to the edge between the fence and the gate.",[70,74,75],{},"The axis should be 1 for Y and 0 for the rest.",[70,77,78],{},"Last, mark the Use Limits checkbox and configure it to have an appropriate opening angle.",[70,80,81],{},"Duplicate the fence and the gate. Make the appropriate adjustments like connecting the correct fence to the Hinge and so on.",[70,83,84],{},"Ensure the angle limits are correct for both sides.",[34,86,88],{"id":87},"shader-graph","Shader Graph",[12,90,91],{},"This shader contains a concept we have discussed and implemented in the previous part – if you haven’t watched it yet, I highly recommend you watch it now and then come back here.",[12,93,94,95,97,98,102,103,106],{},"Create a new Unlit Shader Graph and call it 2.5D Hinge. In the Blackboard, create two new properties.",[24,96],{},"\nA 2D texture named Texture with its reference named ",[99,100,101],"code",{},"_MainTex",", and a Vector1 called Angle and its reference named ",[99,104,105],{},"_Angle",". Make sure both are marked as Exposed.",[108,109,112],"article-image",{"src":110,"alt":111},"\u002Fposts\u002F2020\u002F01-17\u002Fshader-hinge.jpg","2.5D Game in Unity – Skewed Hinge Shader Graph","\nSkewed hinge final Shader Graph.\n",[12,114,115],{},"Configure the master node by clicking the cog on it. Set it up as a two-sided shader.",[12,117,118],{},"Next, drag the texture property from the Blackboard to the canvas. Create a Sample Texture 2D node and connect the texture property into it. Connect the RGBA and Alpha output slots from the Sampler to the master node’s Color and Alpha input slots.",[12,120,121],{},"Like the previous video, we need a foundation that manipulates the vertex position in world space and then converts it into object space for rendering.",[12,123,124],{},"Create a Transform node from World to Object space and connect it to the Vertex Position input slot of the master node.",[12,126,127],{},"Create a Combine node and connect its output into the Transform node’s input. We will later connect different components into it, but for now, let’s connect the world space position.",[12,129,130,131,133],{},"Create a Position node, and set it to World space, connect it into a new Split node so we would have access to each of the position components.",[24,132],{},"\nConnect each of the RGB output slots to the matching input slots of the Combine node.",[12,135,136,137,139],{},"What we want to do now is to modify the X and Z position of the vertices. We have an angle property that tells us how much is the gate pushed, and the more it pushed, we also want to push and rotate the gate.",[24,138],{},"\nThe catch is that we don’t want to modify the vertices that are “connected” to the hinge – only the vertices that are far from it.",[12,141,142],{},[143,144,145],"em",{},"Before we actually modify the X and Z vertices, we will create the toolset used for modification.",[12,147,148,149,151,152,154],{},"First, we calculate the object’s width in world space. Create two Transform nodes from Object to World space.",[24,150],{},"\nThe first one should be in position 1 for the X. The second one should be at the origin 0.",[24,153],{},"\nWe add Subtract node and connect the Transform nodes into it. Finally, we connect to it a new Split node.",[12,156,157,158,160],{},"Next up, some simple trigonometry. Drag the Angle property from the Blackboard and connect it to a new Degrees to Radians node.",[24,159],{},"\nNow, we add Cosine and Sine nodes and connect the radians into them.",[12,162,163],{},"Last, we create a new Position node, and set it to Object space, connect it to a new Split node.",[12,165,166,167,169,170,172],{},"We can now modify the X position.",[24,168],{},"\nCreate a Multiply node, connect into it the R output from the Width’s Split node, and the Cosine of the angle. Create another Multiply node and connect the previous multiplay node and the R output from the Object Space Split node. Now, we connect the Multiply node to a new Add node and the output of the origin vertex position in World Space.",[24,171],{},"\nWe have a modified X position, and we can connect it to the R input slot of the Combine node.",[12,174,175,176,178,179,181,182,184],{},"On to modifying the Z position.",[24,177],{},"\nLike we did for the X position, create a Multiply node, connect into it the R output from the Width’s Split node, and the Sine of the angle. Create another Multiply node and connect the previous multiplay node and the R output from the Object Space Split node.",[24,180],{},"\nNow, connect the Multiply node to a new Add node and the output slot B of the World Space.",[24,183],{},"\nWe now also have a modified Z position, and we can connect it to the B input slot of the Combine node.",[12,186,187],{},"That’s it for the shader. Save the Shader Graph asset. Create a new Material by right-clicking on the shader in the Project window and selecting create > material and name it Gate. Assign it to the sprite renderers of the gates.",[12,189,190],{},"Let’s move on to creating a script that will connect the Hinge and the shader.",[192,193],"article-affiliate-group",{},[34,195,197],{"id":196},"script","Script",[12,199,200],{},"Create a new MonoBehaviour script and call it HingeSync.",[202,203,209],"pre",{"className":204,"code":205,"filename":206,"language":207,"meta":208,"style":208},"language-csharp shiki shiki-themes github-light","using UnityEngine;\n\npublic class HingeSync : MonoBehaviour\n{\n  #region Connstants\n\n  private static readonly int ANGLE = Shader.PropertyToID(\"_Angle\");\n\n  #endregion\n\n\n  #region Inspector\n\n  [SerializeField]\n  private HingeJoint hinge = null;\n\n  [SerializeField]\n  private SpriteRenderer spriteRenderer = null;\n\n  #endregion\n\n\n  #region MonoBehaviour\n\n  private void Update ()\n  {\n    float angle = -hinge.angle;\n    spriteRenderer.material.SetFloat(ANGLE, angle);\n  }\n\n  #endregion\n}\n","HingeSync.cs","csharp","",[99,210,211,228,235,253,259,272,277,313,318,326,331,336,346,351,363,382,387,396,413,418,425,430,435,445,450,464,470,487,499,505,510,517],{"__ignoreMap":208},[212,213,216,220,224],"span",{"class":214,"line":215},"line",1,[212,217,219],{"class":218},"sD7c4","using",[212,221,223],{"class":222},"s7eDp"," UnityEngine",[212,225,227],{"class":226},"sgsFI",";\n",[212,229,231],{"class":214,"line":230},2,[212,232,234],{"emptyLinePlaceholder":233},true,"\n",[212,236,238,241,244,247,250],{"class":214,"line":237},3,[212,239,240],{"class":218},"public",[212,242,243],{"class":218}," class",[212,245,246],{"class":222}," HingeSync",[212,248,249],{"class":226}," : ",[212,251,252],{"class":222},"MonoBehaviour\n",[212,254,256],{"class":214,"line":255},4,[212,257,258],{"class":226},"{\n",[212,260,262,265,268],{"class":214,"line":261},5,[212,263,264],{"class":226},"  #",[212,266,267],{"class":218},"region",[212,269,271],{"class":270},"sYBdl"," Connstants\n",[212,273,275],{"class":214,"line":274},6,[212,276,234],{"emptyLinePlaceholder":233},[212,278,280,283,286,289,292,295,298,301,304,307,310],{"class":214,"line":279},7,[212,281,282],{"class":218},"  private",[212,284,285],{"class":218}," static",[212,287,288],{"class":218}," readonly",[212,290,291],{"class":218}," int",[212,293,294],{"class":222}," ANGLE",[212,296,297],{"class":218}," =",[212,299,300],{"class":226}," Shader.",[212,302,303],{"class":222},"PropertyToID",[212,305,306],{"class":226},"(",[212,308,309],{"class":270},"\"_Angle\"",[212,311,312],{"class":226},");\n",[212,314,316],{"class":214,"line":315},8,[212,317,234],{"emptyLinePlaceholder":233},[212,319,321,323],{"class":214,"line":320},9,[212,322,264],{"class":226},[212,324,325],{"class":218},"endregion\n",[212,327,329],{"class":214,"line":328},10,[212,330,234],{"emptyLinePlaceholder":233},[212,332,334],{"class":214,"line":333},11,[212,335,234],{"emptyLinePlaceholder":233},[212,337,339,341,343],{"class":214,"line":338},12,[212,340,264],{"class":226},[212,342,267],{"class":218},[212,344,345],{"class":270}," Inspector\n",[212,347,349],{"class":214,"line":348},13,[212,350,234],{"emptyLinePlaceholder":233},[212,352,354,357,360],{"class":214,"line":353},14,[212,355,356],{"class":226},"  [",[212,358,359],{"class":222},"SerializeField",[212,361,362],{"class":226},"]\n",[212,364,366,368,371,374,376,380],{"class":214,"line":365},15,[212,367,282],{"class":218},[212,369,370],{"class":222}," HingeJoint",[212,372,373],{"class":222}," hinge",[212,375,297],{"class":218},[212,377,379],{"class":378},"sYu0t"," null",[212,381,227],{"class":226},[212,383,385],{"class":214,"line":384},16,[212,386,234],{"emptyLinePlaceholder":233},[212,388,390,392,394],{"class":214,"line":389},17,[212,391,356],{"class":226},[212,393,359],{"class":222},[212,395,362],{"class":226},[212,397,399,401,404,407,409,411],{"class":214,"line":398},18,[212,400,282],{"class":218},[212,402,403],{"class":222}," SpriteRenderer",[212,405,406],{"class":222}," spriteRenderer",[212,408,297],{"class":218},[212,410,379],{"class":378},[212,412,227],{"class":226},[212,414,416],{"class":214,"line":415},19,[212,417,234],{"emptyLinePlaceholder":233},[212,419,421,423],{"class":214,"line":420},20,[212,422,264],{"class":226},[212,424,325],{"class":218},[212,426,428],{"class":214,"line":427},21,[212,429,234],{"emptyLinePlaceholder":233},[212,431,433],{"class":214,"line":432},22,[212,434,234],{"emptyLinePlaceholder":233},[212,436,438,440,442],{"class":214,"line":437},23,[212,439,264],{"class":226},[212,441,267],{"class":218},[212,443,444],{"class":270}," MonoBehaviour\n",[212,446,448],{"class":214,"line":447},24,[212,449,234],{"emptyLinePlaceholder":233},[212,451,453,455,458,461],{"class":214,"line":452},25,[212,454,282],{"class":218},[212,456,457],{"class":218}," void",[212,459,460],{"class":222}," Update",[212,462,463],{"class":226}," ()\n",[212,465,467],{"class":214,"line":466},26,[212,468,469],{"class":226},"  {\n",[212,471,473,476,479,481,484],{"class":214,"line":472},27,[212,474,475],{"class":218},"    float",[212,477,478],{"class":222}," angle",[212,480,297],{"class":218},[212,482,483],{"class":218}," -",[212,485,486],{"class":226},"hinge.angle;\n",[212,488,490,493,496],{"class":214,"line":489},28,[212,491,492],{"class":226},"    spriteRenderer.material.",[212,494,495],{"class":222},"SetFloat",[212,497,498],{"class":226},"(ANGLE, angle);\n",[212,500,502],{"class":214,"line":501},29,[212,503,504],{"class":226},"  }\n",[212,506,508],{"class":214,"line":507},30,[212,509,234],{"emptyLinePlaceholder":233},[212,511,513,515],{"class":214,"line":512},31,[212,514,264],{"class":226},[212,516,325],{"class":218},[212,518,520],{"class":214,"line":519},32,[212,521,522],{"class":226},"}\n",[67,524,525,528],{},[70,526,527],{},"We are creating a static property ID since it yields better performance.",[70,529,530],{},"Be sure you are accessing the material and not the sharedMaterial as we want a different value for each instance.",[12,532,533],{},"To sum up, there is not much to this script; all it does is updating the shader to match the hinge rotation of every frame.",[12,535,536],{},"Add the script we created to each of the gates, don’t forget to drag into it the HingeJoint and the SpriteRenderer.",[16,538,540],{"gif":539},"W4XNXeQLQTuG7UUb3D","\nFunctioning 2.5D sheered gate.\n",[12,542,543],{},"We now have a working gate with dynamic skewing to match our 2.5D game style!",[545,546],"article-download",{"href":547,"title":548},"https:\u002F\u002Fgithub.com\u002Fnotslot\u002Ftutorial-25d-game-unity","Download Unity Project",[12,550,551],{},"In the next part, we will configure Cinemachine for our 2.5D style with interactive multi-cam switching.",[553,554,555],"style",{},"html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":208,"searchDepth":237,"depth":237,"links":557},[558,559,560,561],{"id":36,"depth":230,"text":37},{"id":49,"depth":230,"text":50},{"id":87,"depth":230,"text":88},{"id":196,"depth":230,"text":197},"tutorial",{"480w":564,"960w":565},"\u002Fposts\u002F2020\u002F01-17\u002F25d-game-unity-part-4-480w.jpg","\u002Fposts\u002F2020\u002F01-17\u002F25d-game-unity-part-4-960w.jpg","2020-01-17","Create doors and gates in our 2.5D world with physics and Shader Graph.","md",{},"\u002Fposts\u002F2020\u002F01\u002F25d-game-in-unity-part-4",{"title":6,"description":567},"2019-25d-game-unity","posts\u002F2020\u002F01\u002F25d-game-in-unity-part-4",[575,99,576],"unity","shader","mFl5HH-bnAM",null,"Y-nejRxCGkiJFuX7ss9RM64g5HngaExIacAX_M_xkh4","25d-game-in-unity-part-4",{"id":582,"title":583,"description":584,"extension":585,"hideParts":586,"meta":587,"shortTitle":578,"stem":588,"__hash__":589,"posts":590},"tutorials\u002Ftutorials\u002F2019-25d-game-unity.yaml","2.5D Games in Unity","Techinques, tips, and Shaders for creating 2.5D Unity games.","yaml",false,{},"tutorials\u002F2019-25d-game-unity","KxCeiBMoAOrkwhVcgjFDa_E0PVejsVB8l1Y3v2jT5VE",[591,786,1156,1338,1715],{"id":592,"title":593,"author":7,"body":594,"category":562,"cover":773,"date":776,"description":777,"extension":568,"meta":778,"navigation":233,"path":779,"seo":780,"series":572,"stem":781,"tags":782,"youtube":783,"youtubeRange":578,"__hash__":784,"slug":785},"posts\u002Fposts\u002F2019\u002F11\u002F25d-game-in-unity-part-1.md","Basic Scene",{"type":9,"value":595,"toc":765},[596,599,602,607,610,612,616,619,644,648,654,658,678,689,693,707,711,714,717,722,724,728,731,734,737,741,757,760,762],[12,597,598],{},"2.5D games come in many flavors. A popular approach is to use a 3D environment and assets but limit the player movement to only two axes. Another method would be to use 2D assets while exploiting the third dimension only for its parallax effect.",[12,600,601],{},"For our game Nugget & Penny, we chose a different approach, we use 2D assets to describe the world while retaining all three axes of movement. We saw this style in a few games and immediately fell in love!",[108,603,606],{"src":604,"alt":605},"\u002Fposts\u002F2019\u002F11-27\u002F25d-game-unity-references.jpg","2.5D Games in Unity – Reference Games","\nMineko's Night Market, The Wild at Heart, Knights & Bikes.\n",[12,608,609],{},"During the development of Nugget & Penny, we encountered a few challenges while implementing the game’s art style. The following are tips we have learned while working with this style and a few solutions to its pitfalls.",[45,611],{},[34,613,615],{"id":614},"the-recipe","The Recipe",[12,617,618],{},"There are three ingredients required to achieve the desired style:",[620,621,622,633,636],"ol",{},[70,623,624,625,628,629,632],{},"Cards – the most important component, these are your 2D art & animations. Layed out with an ",[99,626,627],{},"X","-axis rotation of ",[99,630,631],{},"45°",".",[70,634,635],{},"Ground – while you may use a flat plane, we like creating a 3D mesh. No need for complex shapes – the majority of the ground details will come from the texture painting.",[70,637,638,639,628,641,643],{},"Camera – use a static ",[99,640,627],{},[99,642,631],{},". This way, the Cards will appear perpendicular to the player.",[34,645,647],{"id":646},"the-sweet-spot","The Sweet Spot",[12,649,650,651,653],{},"I mentioned above that we use a ",[99,652,631],{}," rotation for the Cards and the Camera. You can choose a different angle, take a look at the effect different angles have over the same scene:",[16,655,657],{"gif":656},"KBVnklcabwX6BKvhje","\nComparison of rotation degrees.\n",[12,659,660,661,663,664,666,667,670,671,674,675,677],{},"So, why have we chosen ",[99,662,631],{}," over the other degrees?",[24,665],{},"\nThe resulting scene with ",[99,668,669],{},"60°"," appeared flat, so it was an easy rejection. In contrast, the ",[99,672,673],{},"30°"," scene felt spot-on, although there are a couple of drawbacks compared to the ",[99,676,631],{}," scene:",[67,679,680,683],{},[70,681,682],{},"The field of view was too large, requiring bigger worlds and details, even for locations the players will never arrive at.",[70,684,685,686,688],{},"Mathematically, ",[99,687,631],{}," is more approachable for a few computations that we will discuss later on.",[34,690,692],{"id":691},"rotations","Rotations",[12,694,695,696,699,700,703,704,706],{},"Do not rotate the camera beyond its initial rotation or other axes. You may translate it in any axis, and using Cinemahine yields fantastic results with this 2.5D style.\nWhen it comes to Cards rotation, we do sometimes rotate the ",[99,697,698],{},"Y"," and ",[99,701,702],{},"Z"," axes, but never change to ",[99,705,627],{},"-axis as it breaks the illusion.",[34,708,710],{"id":709},"colliders","Colliders",[12,712,713],{},"Developing our demo game, Adventure Machine, we attached angled colliders to the different game components. It resulted in sub-optimal results. Therefore, in the newer game, we are positioning the collider orthogonal to the ground.",[12,715,716],{},"Another difference from the demo is creating colliders for groups of objects instead of each object individually. This prevents players from getting stuck between objects. Creating a tool for quickly drawing custom collider shapes around objects made this approach fast and easy – I will write about it in the future.",[108,718,721],{"src":719,"alt":720},"\u002Fposts\u002F2019\u002F11-27\u002F25d-game-unity-colliders.jpg","Colliders in a Unity 2.5D Game","\nNote the characters orthogonal colliders, and the collider walls surrounding the objects groups.\n",[192,723],{},[34,725,727],{"id":726},"the-missing-d","The Missing ½D",[12,729,730],{},"Sometimes, describing the world with 2D Cards is just not enough. When we encounter such situations, mostly when players feel the skill mechanics are 'cheating', we don't hesitate to use 3D.",[12,732,733],{},"To blend the 3D meshes nicely with the 2D Cards, Danielle paints its texture with the same brushes and technique as the 2D art. The texture is then applied to an unlit material.",[12,735,736],{},"Another trick we use for a natural blend is to skew the mesh with a custom shader – we will expand on it in the second part of this article.",[34,738,740],{"id":739},"the-gravity-dilemma","The Gravity Dilemma",[12,742,743,744,746,747,749,750,753,754,756],{},"For an object to go up or down, and feel natural, we need to keep the ",[99,745,702],{}," offset correlated to the ",[99,748,698],{}," offset amount. For example, If we raise an object ",[99,751,752],{},"1 unit"," above the ground, we should also push it ",[99,755,752],{}," away from the camera.",[12,758,759],{},"This is a rule we only conform to sparingly, and on a case-to-case basis. Our general guideline would be to break this rule when we use physics, so we aren't often required to change the object’s gravity behavior.",[545,761],{"href":547,"title":548},[12,763,764],{},"In the next parts we will look into three shaders I created to solve issues that popped up during development.",{"title":208,"searchDepth":237,"depth":237,"links":766},[767,768,769,770,771,772],{"id":614,"depth":230,"text":615},{"id":646,"depth":230,"text":647},{"id":691,"depth":230,"text":692},{"id":709,"depth":230,"text":710},{"id":726,"depth":230,"text":727},{"id":739,"depth":230,"text":740},{"480w":774,"960w":775},"\u002Fposts\u002F2019\u002F11-27\u002F25d-game-unity-480w.jpg","\u002Fposts\u002F2019\u002F11-27\u002F25d-game-unity-960w.jpg","2019-11-27","Guide and tips for creating 2.5D games with unity based on 2D sprites in a 3D environment.",{},"\u002Fposts\u002F2019\u002F11\u002F25d-game-in-unity-part-1",{"title":593,"description":777},"posts\u002F2019\u002F11\u002F25d-game-in-unity-part-1",[575],"wYulZiBKg-I","vpfgLS84IIhOJXWV-Pik9v9Ohj2jtRRn_oSTqIomIhU","25d-game-in-unity-part-1",{"id":787,"title":788,"author":7,"body":789,"category":562,"cover":1143,"date":1146,"description":1147,"extension":568,"meta":1148,"navigation":233,"path":1149,"seo":1150,"series":572,"stem":1151,"tags":1152,"youtube":1153,"youtubeRange":578,"__hash__":1154,"slug":1155},"posts\u002Fposts\u002F2019\u002F12\u002F25d-game-in-unity-part-2.md","Depth Shader",{"type":9,"value":790,"toc":1141},[791,794,810,814,823,826,828,845,862,884,891,898,967,978,984,1013,1044,1061,1084,1086,1111,1125,1132,1135,1138],[12,792,793],{},"In this part of the 2.5D Game in Unity series, we will write a shader to resolve the depth issues that appeared in the scene we created.",[12,795,796,797,799,800,802,803,805,806,809],{},"As we observed in the previous part of the tutorial, moving the camera results in rendering order glitches.",[24,798],{},"\nThis issue originates from the way Unity renders sprites:",[24,801],{},"\nOn each frame, Unity determines which sprite to draw in front based on its distance from the camera.",[24,804],{},"\nAlthough the Sprite Renderer has options for ",[143,807,808],{},"Sort Point"," – both do not solve the issue.",[16,811,813],{"gif":812},"keUEo3DkIuDRRyG9y7","\nBefore and after fixing sprites z-fighting.\n",[12,815,816,817,819,820,822],{},"To solve this, we will create a shader for rendering our sprites. The main difference is that it treats sprites as “full” 3D objects and sorts their rendering by depth.",[24,818],{},"\nOur solution is using the Universal Render Pipeline, note that a similar approach can be applied to the default render pipeline.",[24,821],{},"\nA pleasant outcome for our rendering approach is that it also unlocks the possibility to create effects such as Depth of Field, Fog, and more.",[12,824,825],{},"Our custom sprite shader will be a modified version of the Unlit shader from the Universal Render Pipeline. Also, we need to change a few files that the shader depends on.",[45,827],{},[12,829,830,831,632,838,840,841,844],{},"Let’s begin by going to the URP ",[832,833,837],"a",{"href":834,"rel":835},"https:\u002F\u002Fgithub.com\u002FUnity-Technologies\u002FGraphics",[836],"nofollow","repository on GitHub",[24,839],{},"\nChoose the appropriate branch of the Universal Render Pipeline. I’m working with Unity version ",[143,842,843],{},"2019.3",", so I’ll select the matching branch.",[12,846,847,848,632,851,853,854,856,857,861],{},"Go to the URP directory; its full name is ",[99,849,850],{},"com.unity.render-pipelines.universal",[24,852],{},"\nWe need to copy 1 shader file and 4 supporting files to a new Unity project.",[24,855],{},"\nFrom the ",[858,859,860],"strong",{},"Shaders"," directory, copy the following files:",[67,863,864,869,874,879],{},[70,865,866],{},[99,867,868],{},"Unlit.shader",[70,870,871],{},[99,872,873],{},"DepthOnlyPass.hlsl",[70,875,876],{},[99,877,878],{},"UnlitInput.hlsl",[70,880,881],{},[99,882,883],{},"UnlitMetaPass.hlsl",[12,885,886,887,890],{},"Now, go back and select the ",[858,888,889],{},"ShaderLibrary"," directory. Copy this file:",[67,892,893],{},[70,894,895],{},[99,896,897],{},"SurfaceInput.hlsl",[12,899,900,901,903,904,907,908,910,911,914,915,632,918,920,921,924,925,927,928,903,931,934,935,927,937,903,940,943,944,946,947,903,950,952,953,956,957,632,960,962,963,966],{},"Rename the file ",[99,902,868],{}," to ",[99,905,906],{},"UnlitSprite.shader"," and open it.",[24,909],{},"\nOn the first line, add the word ",[99,912,913],{},"Sprite"," after ",[99,916,917],{},"Unlit",[24,919],{},"\nComment out or remove line ",[858,922,923],{},"22"," to prevent any collision.",[24,926],{},"\nChange line ",[858,929,930],{},"32",[99,932,933],{},"ZWrite On",". The shader will now write to the depth buffer and prevent the glitches.",[24,936],{},[858,938,939],{},"33",[99,941,942],{},"Cull Off"," so we could flip sprites.",[24,945],{},"\nFor the shader to support with the Unity Sprite Renderer, we need to change the property name ",[99,948,949],{},"_BaseMap",[99,951,101],{}," on lines ",[858,954,955],{},"5, 82",", and ",[858,958,959],{},"94",[24,961],{},"\nLet’s also replace line ",[858,964,965],{},"97",", I found this line to yield unstable results. Replace it so it will hide the transparent pixels:",[202,968,972],{"className":969,"code":970,"language":971,"meta":208,"style":208},"language-glsl shiki shiki-themes github-light","clip(alpha - _Cutoff);\n","glsl",[99,973,974],{"__ignoreMap":208},[212,975,976],{"class":214,"line":215},[212,977,970],{},[12,979,980,983],{},[858,981,982],{},"The shader won’t compile yet",", we first need to modify the supporting files we have duplicated.",[12,985,986,987,989,990,903,992,994,995,699,998,632,1001,1003,1004,1006,1007,903,1010,632],{},"Let’s begin with the file ",[99,988,873],{},". As in the previous file, change ",[99,991,949],{},[99,993,101],{},". It appears in lines ",[858,996,997],{},"27",[858,999,1000],{},"36",[24,1002],{},"\nOn line ",[858,1005,1000],{}," also change ",[99,1008,1009],{},"sampler_BaseMap",[99,1011,1012],{},"sampler_MainTex",[12,1014,1015,1016,1018,1019,1021,1022,1025,1026,903,1029,632,1032,1021,1034,1037,1038,1041,1042,632],{},"Moving on to the file ",[99,1017,878],{},":",[24,1020],{},"\nAt line ",[858,1023,1024],{},"7",", change ",[99,1027,1028],{},"_BaseMap_ST",[99,1030,1031],{},"_MainTex_ST",[24,1033],{},[858,1035,1036],{},"4",", change the ",[99,1039,1040],{},"#include"," statement to remove the full path and only include ",[99,1043,897],{},[12,1045,1046,1047,1049,1050,1053,1054,1057,1058,632],{},"Next, in the file ",[99,1048,897],{}," at line ",[858,1051,1052],{},"8",", change both appearances of ",[99,1055,1056],{},"BaseMap"," into ",[99,1059,1060],{},"MainTex",[12,1062,1063,1064,632,1066,1068,1069,1071,1072,1075,1076,699,1079,1081,1082,632],{},"Now, for the last file, open ",[99,1065,883],{},[24,1067],{},"\nYou already know what to change, ",[99,1070,1056],{}," appears ",[858,1073,1074],{},"three"," times on lines ",[858,1077,1078],{},"29",[858,1080,1000],{}," – change it to ",[99,1083,1060],{},[192,1085],{},[12,1087,1088,1089,1091,1092,1094,1095,632,1098,1100,1101,1104,1105,632,1108,1110],{},"Finally, go back to file ",[99,1090,906],{},". We need to change the #include statements to use our modified files.",[24,1093],{},"\nWe have #include statements on lines ",[858,1096,1097],{},"133, 134, 153, 154",[24,1099],{},"\nGo to the end of the file, on line ",[858,1102,1103],{},"159",", change the FallBack shader to ",[99,1106,1107],{},"Hidden\u002FInternalErrorShader",[24,1109],{},"\nThe last thing, comment out the line below the previous line – so we won’t use the SRP material editor.",[12,1112,1113,1114,1117,1118,1121,1122,1124],{},"We finished modifying the shader. Let’s create a Material for it. Right-click on the shader ",[99,1115,1116],{},"UnlitSprite"," and select ",[99,1119,1120],{},"Create > Material",", name the created Material Unlit Sprite.",[24,1123],{},"\nWe can now use the material on all of our Sprite Renderers.",[12,1126,1127,1128,1131],{},"Before committing to this solution, it essential to recognize a drawback it has. To draw the sprites as opaque objects, we use the ",[143,1129,1130],{},"Alpha Clipping"," method. This hides the transparent pixels based on their alpha value. It’s recommended that your art assets have near sharp edges to prevent artifacts.",[545,1133,1134],{"href":547,"title":548},"\nShaders updated to support Unity 2020 & URP 10+\n",[12,1136,1137],{},"In the next parts, we will create two more shaders using Shader Graph. The first shader will allow us to combine 3D into our game harmonically. The second shader will enable opening doors and gates without them getting stuck in the ground.",[553,1139,1140],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":208,"searchDepth":237,"depth":237,"links":1142},[],{"480w":1144,"960w":1145},"\u002Fposts\u002F2019\u002F12-14\u002F25d-game-unity-part-2-480w.jpg","\u002Fposts\u002F2019\u002F12-14\u002F25d-game-unity-part-2-960w.jpg","2019-12-14","Write a shader to resolve the depth issues of the 2.5D billboard style.",{},"\u002Fposts\u002F2019\u002F12\u002F25d-game-in-unity-part-2",{"title":788,"description":1147},"posts\u002F2019\u002F12\u002F25d-game-in-unity-part-2",[575,576],"e1j82BEnHhg","nmfvMZ1vqyL9jDlNxt5gwM8Zpg3WsSxaYv7gC1JLMK4","25d-game-in-unity-part-2",{"id":1157,"title":1158,"author":7,"body":1159,"category":562,"cover":1325,"date":1328,"description":1329,"extension":568,"meta":1330,"navigation":233,"path":1331,"seo":1332,"series":572,"stem":1333,"tags":1334,"youtube":1335,"youtubeRange":578,"__hash__":1336,"slug":1337},"posts\u002Fposts\u002F2019\u002F12\u002F25d-game-in-unity-part-3.md","Skew Shader",{"type":9,"value":1160,"toc":1321},[1161,1164,1167,1171,1183,1185,1189,1195,1199,1211,1220,1223,1228,1240,1243,1246,1249,1251,1255,1258,1261,1266,1276,1283,1287,1305,1308,1316,1318],[12,1162,1163],{},"In this third part, we will make a shader with Shader Graph that will allow us to blend 3D objects into our 2.5D environment in a harmonious way.",[12,1165,1166],{},"Let’s have a look at the final result of this shader.",[16,1168,1170],{"gif":1169},"SUd9IiOxIuuyH3KHYW","\nBeafore and after applying 3D skewing.\n",[12,1172,1173,1174,1176,1177,1179,1180,1182],{},"On the left, we have a regular object, and on the right, an object with our shader.",[24,1175],{},"\nDo you notice how the unmodified object sticks out, while the modified object blends in?",[24,1178],{},"\nTo better understand the difference, let’s look at how the shader effect primitive objects – the right object of each primitive is the modified one.",[24,1181],{},"\nIt may look a bit weird when we isolate a 3D object, but as a whole, when it surrounded by 2D sprites, it feels much more unified and ties the whole game together.",[45,1184],{},[34,1186,1188],{"id":1187},"base-shader","Base Shader",[12,1190,1191,1192,1194],{},"This quick-fix is useful if you have a static game object that doesn’t need rotation. Even if you need a more advanced movement, this part explains the approach we are taking and is the base for a more advanced shader we will create later in this article.",[24,1193],{},"\nWhat we are trying to achieve is skewing the 3D models in the same angle as the 2D sprites.",[16,1196,1198],{"gif":1197},"RGSfSsui1KNzptjVAX","\n3D skewing process.\n",[12,1200,1201,1202,1204,1205,1207,1208,1210],{},"From a side view, it looks like this. The left line represents a 2D sprite, and the right box represents a 3D cube. What the shader does is skewing the Cube to match the 45 degrees rotation of the 2D sprite.",[24,1203],{},"\nThe way the shader works is by translating the ",[99,1206,702],{}," position of each vertex with the same amount of its ",[99,1209,698],{}," position.",[12,1212,1213,1216,1217,632],{},[143,1214,1215],{},"OK, let’s start working!"," In the Project Window, add a new Shader by selecting the Unlit Graph. Name it ",[143,1218,1219],{},"Simple 3D Skew",[12,1221,1222],{},"Create the following graph:",[108,1224,1227],{"src":1225,"alt":1226},"\u002Fposts\u002F2019\u002F12-25\u002F3d-skew-basic.jpg","2.5D Game in Unity – Basic Skew Shader","\n3D skewing base Shader Graph.\n",[12,1229,1230,1231,1233,1234,1236,1237,1239],{},"Create a new material by right-clicking the shader we have created and select ",[99,1232,1120],{},". Name it ",[143,1235,1219],{},", and add a texture to its MainTex property.",[24,1238],{},"\nAssign the material to an object.",[12,1241,1242],{},"Our objects are now skewed as intended – matching the rotation of the sprites.",[12,1244,1245],{},"When we select an object, notice how the outline is still rendering without the shaders skew. This is because the shader only affects the rendering, and not, let’s say physics colliders. Recall from the first part that this is actually good for us as we treat physics colliders as they are full 3D objects without rotating them.",[12,1247,1248],{},"The shader now works, but we have an issue. Take a look at what happens if we rotate an object. The skewing is always applied to the object relative to its origin transformation. But don’t worry, we are going to handle it now...",[192,1250],{},[34,1252,1254],{"id":1253},"advanced-shader","Advanced Shader",[12,1256,1257],{},"We can now take our shader to the next level by allowing transforming the objects. We can do it by applying the skewing effect in the World Space instead of the Object Space.",[12,1259,1260],{},"In the Project Window, add a new Shader by selecting the Unlit Graph. Name it “Advanced 3D Skew” and create the following graph:",[108,1262,1265],{"src":1263,"alt":1264},"\u002Fposts\u002F2019\u002F12-25\u002F3d-skew-advanced.jpg","2.5D Game in Unity – Advanced Skew Shader","\n3D skewing advanced Shader Graph.\n",[12,1267,1268,1269,1271,1272,1275],{},"Create a material by right-clicking the shader and selecting ",[99,1270,1120],{},"; we will name it ",[143,1273,1274],{},"Advanced 3D Skew",". Set the texture property with a texture of your choice. We can now assign the material to the object in the scene instead of the basic shader material.",[12,1277,1278,1279,1282],{},"Notice the ",[143,1280,1281],{},"Z Skew"," nodes group in the bottom right, without when we move to objects upwards – instead of just pushing the object up, it also pushes it back.",[16,1284,1286],{"gif":1285},"jqC7HWiKlWYLuzT2Lu","\nBreakdown of variable height support in the advanced shader.\n",[12,1288,1289,1290,1292,1293,1295,1296,1298,1299,1301,1302,1304],{},"The issue happens because we add the ",[99,1291,698],{}," position of a vertex in the World Space – if the object is high in the scene, it will push the ",[99,1294,702],{}," position beyond our intentions.",[24,1297],{},"\nWhat we do to solve it is to take the ",[99,1300,698],{}," position of the object’s origin point, and reduce it from our skew. This will ensure we only skew ",[99,1303,702],{}," relative to our object position.",[1306,1307],"hr",{},[12,1309,1310,1311,1117,1313,632],{},"You may encounter a glitch while transforming an object or moving the camera; this happens because of the render pipeline’s batching. While we can go to the render pipelines setting and turn off batching, it is better to modify shader’s materials – select the material ",[143,1312,1274],{},[99,1314,1315],{},"Enable GPU Instancing",[545,1317],{"href":547,"title":548},[12,1319,1320],{},"In the next parts, we will make a shader to add neat gates or doors for our 2.5D game. We will also configure Cinemachine for our 2.5D style with interactive multi-cam switching.",{"title":208,"searchDepth":237,"depth":237,"links":1322},[1323,1324],{"id":1187,"depth":230,"text":1188},{"id":1253,"depth":230,"text":1254},{"480w":1326,"960w":1327},"\u002Fposts\u002F2019\u002F12-25\u002F25d-game-unity-part-3-480w.jpg","\u002Fposts\u002F2019\u002F12-25\u002F25d-game-unity-part-3-960w.jpg","2019-12-25","Crate a shader with Shader Graph that will allow us to blend 3D objects into our 2.5D environment in a harmonious way.",{},"\u002Fposts\u002F2019\u002F12\u002F25d-game-in-unity-part-3",{"title":1158,"description":1329},"posts\u002F2019\u002F12\u002F25d-game-in-unity-part-3",[575,576],"aZfIJUmYKf4","6_bNNSMLZVQkeEZmx702MfvBr449355JlXq4EE9BUGo","25d-game-in-unity-part-3",{"id":5,"title":6,"author":7,"body":1339,"category":562,"cover":1711,"date":566,"description":567,"extension":568,"meta":1712,"navigation":233,"path":570,"seo":1713,"series":572,"stem":573,"tags":1714,"youtube":577,"youtubeRange":578,"__hash__":579,"slug":580},{"type":9,"value":1340,"toc":1705},[1341,1343,1345,1349,1351,1353,1355,1359,1361,1363,1367,1373,1385,1387,1389,1397,1399,1401,1403,1405,1407,1409,1413,1417,1421,1427,1431,1433,1439,1447,1449,1451,1453,1455,1457,1685,1691,1693,1695,1697,1699,1701,1703],[12,1342,14],{},[16,1344,19],{"gif":18},[12,1346,22,1347,26],{},[24,1348],{},[12,1350,29],{},[12,1352,32],{},[34,1354,37],{"id":36},[12,1356,40,1357,43],{},[24,1358],{},[45,1360],{},[34,1362,50],{"id":49},[12,1364,53,1365,56],{},[24,1366],{},[12,1368,59,1369,62,1371,65],{},[24,1370],{},[24,1372],{},[67,1374,1375,1377,1379,1381,1383],{},[70,1376,72],{},[70,1378,75],{},[70,1380,78],{},[70,1382,81],{},[70,1384,84],{},[34,1386,88],{"id":87},[12,1388,91],{},[12,1390,94,1391,97,1393,102,1395,106],{},[24,1392],{},[99,1394,101],{},[99,1396,105],{},[108,1398,112],{"src":110,"alt":111},[12,1400,115],{},[12,1402,118],{},[12,1404,121],{},[12,1406,124],{},[12,1408,127],{},[12,1410,130,1411,133],{},[24,1412],{},[12,1414,136,1415,139],{},[24,1416],{},[12,1418,1419],{},[143,1420,145],{},[12,1422,148,1423,151,1425,154],{},[24,1424],{},[24,1426],{},[12,1428,157,1429,160],{},[24,1430],{},[12,1432,163],{},[12,1434,166,1435,169,1437,172],{},[24,1436],{},[24,1438],{},[12,1440,175,1441,178,1443,181,1445,184],{},[24,1442],{},[24,1444],{},[24,1446],{},[12,1448,187],{},[12,1450,190],{},[192,1452],{},[34,1454,197],{"id":196},[12,1456,200],{},[202,1458,1459],{"className":204,"code":205,"filename":206,"language":207,"meta":208,"style":208},[99,1460,1461,1469,1473,1485,1489,1497,1501,1525,1529,1535,1539,1543,1551,1555,1563,1577,1581,1589,1603,1607,1613,1617,1621,1629,1633,1643,1647,1659,1667,1671,1675,1681],{"__ignoreMap":208},[212,1462,1463,1465,1467],{"class":214,"line":215},[212,1464,219],{"class":218},[212,1466,223],{"class":222},[212,1468,227],{"class":226},[212,1470,1471],{"class":214,"line":230},[212,1472,234],{"emptyLinePlaceholder":233},[212,1474,1475,1477,1479,1481,1483],{"class":214,"line":237},[212,1476,240],{"class":218},[212,1478,243],{"class":218},[212,1480,246],{"class":222},[212,1482,249],{"class":226},[212,1484,252],{"class":222},[212,1486,1487],{"class":214,"line":255},[212,1488,258],{"class":226},[212,1490,1491,1493,1495],{"class":214,"line":261},[212,1492,264],{"class":226},[212,1494,267],{"class":218},[212,1496,271],{"class":270},[212,1498,1499],{"class":214,"line":274},[212,1500,234],{"emptyLinePlaceholder":233},[212,1502,1503,1505,1507,1509,1511,1513,1515,1517,1519,1521,1523],{"class":214,"line":279},[212,1504,282],{"class":218},[212,1506,285],{"class":218},[212,1508,288],{"class":218},[212,1510,291],{"class":218},[212,1512,294],{"class":222},[212,1514,297],{"class":218},[212,1516,300],{"class":226},[212,1518,303],{"class":222},[212,1520,306],{"class":226},[212,1522,309],{"class":270},[212,1524,312],{"class":226},[212,1526,1527],{"class":214,"line":315},[212,1528,234],{"emptyLinePlaceholder":233},[212,1530,1531,1533],{"class":214,"line":320},[212,1532,264],{"class":226},[212,1534,325],{"class":218},[212,1536,1537],{"class":214,"line":328},[212,1538,234],{"emptyLinePlaceholder":233},[212,1540,1541],{"class":214,"line":333},[212,1542,234],{"emptyLinePlaceholder":233},[212,1544,1545,1547,1549],{"class":214,"line":338},[212,1546,264],{"class":226},[212,1548,267],{"class":218},[212,1550,345],{"class":270},[212,1552,1553],{"class":214,"line":348},[212,1554,234],{"emptyLinePlaceholder":233},[212,1556,1557,1559,1561],{"class":214,"line":353},[212,1558,356],{"class":226},[212,1560,359],{"class":222},[212,1562,362],{"class":226},[212,1564,1565,1567,1569,1571,1573,1575],{"class":214,"line":365},[212,1566,282],{"class":218},[212,1568,370],{"class":222},[212,1570,373],{"class":222},[212,1572,297],{"class":218},[212,1574,379],{"class":378},[212,1576,227],{"class":226},[212,1578,1579],{"class":214,"line":384},[212,1580,234],{"emptyLinePlaceholder":233},[212,1582,1583,1585,1587],{"class":214,"line":389},[212,1584,356],{"class":226},[212,1586,359],{"class":222},[212,1588,362],{"class":226},[212,1590,1591,1593,1595,1597,1599,1601],{"class":214,"line":398},[212,1592,282],{"class":218},[212,1594,403],{"class":222},[212,1596,406],{"class":222},[212,1598,297],{"class":218},[212,1600,379],{"class":378},[212,1602,227],{"class":226},[212,1604,1605],{"class":214,"line":415},[212,1606,234],{"emptyLinePlaceholder":233},[212,1608,1609,1611],{"class":214,"line":420},[212,1610,264],{"class":226},[212,1612,325],{"class":218},[212,1614,1615],{"class":214,"line":427},[212,1616,234],{"emptyLinePlaceholder":233},[212,1618,1619],{"class":214,"line":432},[212,1620,234],{"emptyLinePlaceholder":233},[212,1622,1623,1625,1627],{"class":214,"line":437},[212,1624,264],{"class":226},[212,1626,267],{"class":218},[212,1628,444],{"class":270},[212,1630,1631],{"class":214,"line":447},[212,1632,234],{"emptyLinePlaceholder":233},[212,1634,1635,1637,1639,1641],{"class":214,"line":452},[212,1636,282],{"class":218},[212,1638,457],{"class":218},[212,1640,460],{"class":222},[212,1642,463],{"class":226},[212,1644,1645],{"class":214,"line":466},[212,1646,469],{"class":226},[212,1648,1649,1651,1653,1655,1657],{"class":214,"line":472},[212,1650,475],{"class":218},[212,1652,478],{"class":222},[212,1654,297],{"class":218},[212,1656,483],{"class":218},[212,1658,486],{"class":226},[212,1660,1661,1663,1665],{"class":214,"line":489},[212,1662,492],{"class":226},[212,1664,495],{"class":222},[212,1666,498],{"class":226},[212,1668,1669],{"class":214,"line":501},[212,1670,504],{"class":226},[212,1672,1673],{"class":214,"line":507},[212,1674,234],{"emptyLinePlaceholder":233},[212,1676,1677,1679],{"class":214,"line":512},[212,1678,264],{"class":226},[212,1680,325],{"class":218},[212,1682,1683],{"class":214,"line":519},[212,1684,522],{"class":226},[67,1686,1687,1689],{},[70,1688,527],{},[70,1690,530],{},[12,1692,533],{},[12,1694,536],{},[16,1696,540],{"gif":539},[12,1698,543],{},[545,1700],{"href":547,"title":548},[12,1702,551],{},[553,1704,555],{},{"title":208,"searchDepth":237,"depth":237,"links":1706},[1707,1708,1709,1710],{"id":36,"depth":230,"text":37},{"id":49,"depth":230,"text":50},{"id":87,"depth":230,"text":88},{"id":196,"depth":230,"text":197},{"480w":564,"960w":565},{},{"title":6,"description":567},[575,99,576],{"id":1716,"title":1717,"author":7,"body":1718,"category":562,"cover":2542,"date":2545,"description":2546,"extension":568,"meta":2547,"navigation":233,"path":2548,"seo":2549,"series":572,"stem":2550,"tags":2551,"youtube":2552,"youtubeRange":578,"__hash__":2553,"slug":2554},"posts\u002Fposts\u002F2020\u002F03\u002F25d-game-in-unity-part-5.md","Cinemachine Control Zones",{"type":9,"value":1719,"toc":2534},[1720,1726,1730,1747,1750,1763,1766,1770,1831,1834,1866,1869,1880,1882,1886,1895,2238,2249,2252,2256,2281,2284,2309,2315,2319,2335,2338,2375,2378,2384,2411,2414,2416,2420,2427,2450,2453,2474,2477,2504,2511,2516,2519,2523,2530,2532],[12,1721,1722,1723,1725],{},"In this part, we will combine various camera design methods using Cinemachine.",[24,1724],{},"\nFirst, we will create a basic camera the follows our character. Next, we will create control zones that change camera properties like zoom when the character is inside it. After that, we will add a track and a dolly camera. And last, we will configure the camera to follow two or more characters.",[34,1727,1729],{"id":1728},"cinemachine-package","Cinemachine Package",[12,1731,1732,1733,1736,1737,632,1740,1742,1743,1746],{},"Let’s start by downloading the ",[858,1734,1735],{},"Cinemachine"," package – it’s an ",[143,1738,1739],{},"excellent extension for Unity that provides us with outstanding AAA like camera controls",[24,1741],{},"\nIn the menu bar, go to ",[99,1744,1745],{},"Window > Package Manager",". Wait a few moments for the list to update, find Cinemachine, and click the install button.",[12,1748,1749],{},"After the installation, we have a new menu with predefined Cinemachine objects.",[67,1751,1752],{},[70,1753,1754,1755,1758,1759,1762],{},"Select the ",[143,1756,1757],{},"Main Camera"," object, and add the ",[143,1760,1761],{},"CinemachineBrain"," component to it.",[12,1764,1765],{},"With Cinemachine, we only use one Camera, and instead of modifying its state directly, we use multiple virtual cameras and transition between them.",[34,1767,1769],{"id":1768},"virtual-camera","Virtual Camera",[67,1771,1772,1778,1784,1791,1800,1808,1816],{},[70,1773,1774,1775,632],{},"Go to the Cinemachine menu, and select ",[143,1776,1777],{},"Create Virtual Camera",[70,1779,1780,1781,632],{},"Name the new object ",[99,1782,1783],{},"vcam Main",[70,1785,1786,1787,1790],{},"Drag the Character from the Hierarchy Window to the ",[143,1788,1789],{},"Follow"," field – this will set the camera to follow our character.",[70,1792,1793,1794,903,1797,632],{},"Set ",[143,1795,1796],{},"Body",[858,1798,1799],{},"Framing Transposer",[70,1801,1793,1802,903,1805,632],{},[143,1803,1804],{},"Aim",[858,1806,1807],{},"Do Nothing",[70,1809,1793,1810,903,1813,632],{},[143,1811,1812],{},"distance",[858,1814,1815],{},"13",[70,1817,1818,1819,1822,1823,1826,1827,1830],{},"Make sure the virtual camera’s ",[143,1820,1821],{},"rotation"," is set to ",[99,1824,1825],{},"45"," degrees in the X-axis and ",[99,1828,1829],{},"0"," for the rest.",[12,1832,1833],{},"We can now play the game and have the camera follow after a character. But we can leverage Cinemachine to have a smoother feeling movement:",[67,1835,1836,1847,1856],{},[70,1837,1838,1839,699,1841,903,1844,632],{},"When the camera zooms in and out, we want it to have a bit of more damping, so increase the ",[143,1840,698],{},[143,1842,1843],{},"Z damping",[858,1845,1846],{},"2",[70,1848,1849,1850,903,1853,632],{},"Since we generally want the character in the center of the screen, we will set the ",[143,1851,1852],{},"soft zone",[858,1854,1855],{},"0.4",[70,1857,1858,1859,903,1862,1865],{},"We can also set the ",[143,1860,1861],{},"dead zone",[858,1863,1864],{},"0.2",", so the camera won’t move if the characters only move by a bit.",[12,1867,1868],{},"Let’s run the game to see the changes we made in action.",[12,1870,1871,1872,1875,1876,1879],{},"There is one thing we need to do before we start coding the script. Go to the ",[143,1873,1874],{},"Character"," object and set its tag to ",[858,1877,1878],{},"Player"," – we will use it in the script soon.",[45,1881],{},[34,1883,1885],{"id":1884},"camzone-script","CamZone Script",[12,1887,1888,1889,1891,1892,632],{},"Next, let’s create our camera control zones. These are zones that while the character is inside them, transition to another virtual camera that better suites the zone’s environment or gameplay purpose.",[24,1890],{},"\nCreate a new MonoBehaviour script and call it ",[99,1893,1894],{},"CamZone",[202,1896,1899],{"className":204,"code":1897,"filename":1898,"language":207,"meta":208,"style":208},"using Cinemachine;\nusing UnityEngine;\n\n[RequireComponent(typeof(Collider))]\npublic class CamZone : MonoBehaviour\n{\n  #region Inspector\n\n  [SerializeField]\n  private CinemachineVirtualCamera virtualCamera = null;\n\n  #endregion\n\n\n  #region MonoBehaviour\n\n  private void Start ()\n  {\n    virtualCamera.enabled = false;\n  }\n\n  private void OnTriggerEnter (Collider other)\n  {\n    if ( other.CompareTag(\"Player\") )\n      virtualCamera.enabled = true;\n  }\n\n  private void OnTriggerExit (Collider other)\n  {\n    if ( other.CompareTag(\"Player\") )\n      virtualCamera.enabled = false;\n  }\n\n  private void OnValidate ()\n  {\n    GetComponent\u003CCollider>().isTrigger = true;\n  }\n\n  #endregion\n}\n","CamZone.cs",[99,1900,1901,1910,1918,1922,1943,1956,1960,1968,1972,1980,1996,2000,2006,2010,2014,2022,2026,2037,2041,2054,2058,2062,2082,2086,2105,2117,2121,2125,2142,2146,2160,2170,2174,2179,2191,2196,2216,2221,2226,2233],{"__ignoreMap":208},[212,1902,1903,1905,1908],{"class":214,"line":215},[212,1904,219],{"class":218},[212,1906,1907],{"class":222}," Cinemachine",[212,1909,227],{"class":226},[212,1911,1912,1914,1916],{"class":214,"line":230},[212,1913,219],{"class":218},[212,1915,223],{"class":222},[212,1917,227],{"class":226},[212,1919,1920],{"class":214,"line":237},[212,1921,234],{"emptyLinePlaceholder":233},[212,1923,1924,1927,1930,1932,1935,1937,1940],{"class":214,"line":255},[212,1925,1926],{"class":226},"[",[212,1928,1929],{"class":222},"RequireComponent",[212,1931,306],{"class":226},[212,1933,1934],{"class":218},"typeof",[212,1936,306],{"class":226},[212,1938,1939],{"class":222},"Collider",[212,1941,1942],{"class":226},"))]\n",[212,1944,1945,1947,1949,1952,1954],{"class":214,"line":261},[212,1946,240],{"class":218},[212,1948,243],{"class":218},[212,1950,1951],{"class":222}," CamZone",[212,1953,249],{"class":226},[212,1955,252],{"class":222},[212,1957,1958],{"class":214,"line":274},[212,1959,258],{"class":226},[212,1961,1962,1964,1966],{"class":214,"line":279},[212,1963,264],{"class":226},[212,1965,267],{"class":218},[212,1967,345],{"class":270},[212,1969,1970],{"class":214,"line":315},[212,1971,234],{"emptyLinePlaceholder":233},[212,1973,1974,1976,1978],{"class":214,"line":320},[212,1975,356],{"class":226},[212,1977,359],{"class":222},[212,1979,362],{"class":226},[212,1981,1982,1984,1987,1990,1992,1994],{"class":214,"line":328},[212,1983,282],{"class":218},[212,1985,1986],{"class":222}," CinemachineVirtualCamera",[212,1988,1989],{"class":222}," virtualCamera",[212,1991,297],{"class":218},[212,1993,379],{"class":378},[212,1995,227],{"class":226},[212,1997,1998],{"class":214,"line":333},[212,1999,234],{"emptyLinePlaceholder":233},[212,2001,2002,2004],{"class":214,"line":338},[212,2003,264],{"class":226},[212,2005,325],{"class":218},[212,2007,2008],{"class":214,"line":348},[212,2009,234],{"emptyLinePlaceholder":233},[212,2011,2012],{"class":214,"line":353},[212,2013,234],{"emptyLinePlaceholder":233},[212,2015,2016,2018,2020],{"class":214,"line":365},[212,2017,264],{"class":226},[212,2019,267],{"class":218},[212,2021,444],{"class":270},[212,2023,2024],{"class":214,"line":384},[212,2025,234],{"emptyLinePlaceholder":233},[212,2027,2028,2030,2032,2035],{"class":214,"line":389},[212,2029,282],{"class":218},[212,2031,457],{"class":218},[212,2033,2034],{"class":222}," Start",[212,2036,463],{"class":226},[212,2038,2039],{"class":214,"line":398},[212,2040,469],{"class":226},[212,2042,2043,2046,2049,2052],{"class":214,"line":415},[212,2044,2045],{"class":226},"    virtualCamera.enabled ",[212,2047,2048],{"class":218},"=",[212,2050,2051],{"class":378}," false",[212,2053,227],{"class":226},[212,2055,2056],{"class":214,"line":420},[212,2057,504],{"class":226},[212,2059,2060],{"class":214,"line":427},[212,2061,234],{"emptyLinePlaceholder":233},[212,2063,2064,2066,2068,2071,2074,2076,2079],{"class":214,"line":432},[212,2065,282],{"class":218},[212,2067,457],{"class":218},[212,2069,2070],{"class":222}," OnTriggerEnter",[212,2072,2073],{"class":226}," (",[212,2075,1939],{"class":222},[212,2077,2078],{"class":222}," other",[212,2080,2081],{"class":226},")\n",[212,2083,2084],{"class":214,"line":437},[212,2085,469],{"class":226},[212,2087,2088,2091,2094,2097,2099,2102],{"class":214,"line":447},[212,2089,2090],{"class":218},"    if",[212,2092,2093],{"class":226}," ( other.",[212,2095,2096],{"class":222},"CompareTag",[212,2098,306],{"class":226},[212,2100,2101],{"class":270},"\"Player\"",[212,2103,2104],{"class":226},") )\n",[212,2106,2107,2110,2112,2115],{"class":214,"line":452},[212,2108,2109],{"class":226},"      virtualCamera.enabled ",[212,2111,2048],{"class":218},[212,2113,2114],{"class":378}," true",[212,2116,227],{"class":226},[212,2118,2119],{"class":214,"line":466},[212,2120,504],{"class":226},[212,2122,2123],{"class":214,"line":472},[212,2124,234],{"emptyLinePlaceholder":233},[212,2126,2127,2129,2131,2134,2136,2138,2140],{"class":214,"line":489},[212,2128,282],{"class":218},[212,2130,457],{"class":218},[212,2132,2133],{"class":222}," OnTriggerExit",[212,2135,2073],{"class":226},[212,2137,1939],{"class":222},[212,2139,2078],{"class":222},[212,2141,2081],{"class":226},[212,2143,2144],{"class":214,"line":501},[212,2145,469],{"class":226},[212,2147,2148,2150,2152,2154,2156,2158],{"class":214,"line":507},[212,2149,2090],{"class":218},[212,2151,2093],{"class":226},[212,2153,2096],{"class":222},[212,2155,306],{"class":226},[212,2157,2101],{"class":270},[212,2159,2104],{"class":226},[212,2161,2162,2164,2166,2168],{"class":214,"line":512},[212,2163,2109],{"class":226},[212,2165,2048],{"class":218},[212,2167,2051],{"class":378},[212,2169,227],{"class":226},[212,2171,2172],{"class":214,"line":519},[212,2173,504],{"class":226},[212,2175,2177],{"class":214,"line":2176},33,[212,2178,234],{"emptyLinePlaceholder":233},[212,2180,2182,2184,2186,2189],{"class":214,"line":2181},34,[212,2183,282],{"class":218},[212,2185,457],{"class":218},[212,2187,2188],{"class":222}," OnValidate",[212,2190,463],{"class":226},[212,2192,2194],{"class":214,"line":2193},35,[212,2195,469],{"class":226},[212,2197,2199,2202,2205,2207,2210,2212,2214],{"class":214,"line":2198},36,[212,2200,2201],{"class":222},"    GetComponent",[212,2203,2204],{"class":226},"\u003C",[212,2206,1939],{"class":222},[212,2208,2209],{"class":226},">().isTrigger ",[212,2211,2048],{"class":218},[212,2213,2114],{"class":378},[212,2215,227],{"class":226},[212,2217,2219],{"class":214,"line":2218},37,[212,2220,504],{"class":226},[212,2222,2224],{"class":214,"line":2223},38,[212,2225,234],{"emptyLinePlaceholder":233},[212,2227,2229,2231],{"class":214,"line":2228},39,[212,2230,264],{"class":226},[212,2232,325],{"class":218},[212,2234,2236],{"class":214,"line":2235},40,[212,2237,522],{"class":226},[67,2239,2240,2243,2246],{},[70,2241,2242],{},"We want disable the virtual camera until its needed.",[70,2244,2245],{},"Only when the colliding object has the tag Player we enable the camera. We disable it whrn the player gets out.",[70,2247,2248],{},"We validate the script has access to a collider and ensure it is a trigger.",[12,2250,2251],{},"The script is ready, we can now create Camera Control Zones with it.",[34,2253,2255],{"id":2254},"control-zones","Control Zones",[67,2257,2258,2264,2267,2272],{},[70,2259,2260,2261,632],{},"Create an object, name it ",[99,2262,2263],{},"CamZone Gate",[70,2265,2266],{},"Add a collider of your choice, mark it as a trigger.",[70,2268,2269,2270,1762],{},"Add the ",[143,2271,1894],{},[70,2273,2274,2275,2277,2278,632],{},"Duplicate the ",[143,2276,1783],{}," object, and name it ",[99,2279,2280],{},"vcam Gate",[12,2282,2283],{},"The gate camera will activate as the character goes near the gate, we still want it to track the character, but it should zoom in.",[67,2285,2286,2296],{},[70,2287,2288,2289,2291,2292,903,2294,632],{},"Change the virtual camera ",[143,2290,1812],{}," from ",[858,2293,1815],{},[858,2295,1052],{},[70,2297,1754,2298,2301,2302,2304,2305,2308],{},[143,2299,2300],{},"Gate Zone"," object, and drag ",[143,2303,2280],{}," into its ",[143,2306,2307],{},"camera"," field.",[12,2310,2311,2312,2314],{},"We can now run the game and see how the camera transitions between the virtual cameras as we approach the gate and move away from it.",[24,2313],{},"\nNotice that we still have a single camera, it’s just the active virtual camera that changes – we can see in the scene view how it works behind the scenes.",[16,2316,2318],{"gif":2317},"jpbtcHssm7nzutEIgP","\nCinemachine control zone.\n",[12,2320,2321,2322,2324,2325,2327,2328,903,2331,2334],{},"One thing we can change before we move on is the transition duration between the virtual cameras. Select the ",[143,2323,1757],{}," GameObject, and in the Inspector, go to the ",[143,2326,1761],{}," component. Change the default ",[143,2329,2330],{},"blend",[858,2332,2333],{},"1.2"," seconds.",[12,2336,2337],{},"Let’s add a zoom out zone:",[67,2339,2340,2347,2357,2365],{},[70,2341,2342,2343,699,2345,632],{},"Duplicate the objects ",[143,2344,2263],{},[143,2346,2280],{},[70,2348,2349,2350,2353,2354,632],{},"Name the zone object ",[99,2351,2352],{},"CamZone Final"," and the virtual camera object ",[99,2355,2356],{},"vcam Final",[70,2358,2359,2360,2362,2363,632],{},"Move the zone to its proper position and change its ",[143,2361,2307],{}," property to ",[143,2364,2356],{},[70,2366,2367,2368,2291,2370,903,2372,632],{},"Set the zoom out, change the virtual camera’s ",[143,2369,1812],{},[858,2371,1052],{},[858,2373,2374],{},"16",[12,2376,2377],{},"We can run the game and see that when we reach the end, the camera zooms out.",[12,2379,2380,2381,2383],{},"Now, let’s do something else.",[24,2382],{},"\nInstead of changing the camera’s distance, let’s set a zone where the camera is static and doesn’t follow the character.",[67,2385,2386,2394,2402],{},[70,2387,2388,2389,699,2391,2393],{},"Once again, duplicate the ",[143,2390,2263],{},[143,2392,2280],{}," objects.",[70,2395,2349,2396,2353,2399,632],{},[99,2397,2398],{},"CamZone Start",[99,2400,2401],{},"vcam Start",[70,2403,2404,2405,2291,2407,1057,2409,632],{},"In the virtual camera, change the ",[143,2406,1796],{},[858,2408,1799],{},[858,2410,1807],{},[12,2412,2413],{},"Position the camera as you wish and run the game. Now, when we start the game, we will have a static camera that only starts following the character once we exit the CamZone we created.",[192,2415],{},[34,2417,2419],{"id":2418},"dolly","Dolly",[12,2421,2422,2423,2426],{},"To create a dolly cam, first, duplicate one of the cam zones, and name it ",[99,2424,2425],{},"CamZone Dolly",". Adjust the trigger to contain the whole dolly zone.",[67,2428,2429,2434,2444],{},[70,2430,1774,2431,632],{},[143,2432,2433],{},"Create Dolly Camera with Track",[70,2435,2436,2437,2440,2441,632],{},"Name the camera object ",[99,2438,2439],{},"vcam Dolly",", and the track should be called ",[99,2442,2443],{},"track Dolly",[70,2445,2446,2447,2449],{},"Drag the new virtual camera to the ",[143,2448,1894],{}," we have created.",[12,2451,2452],{},"Now let’s configure the camera:",[67,2454,2455,2460,2468],{},[70,2456,1793,2457,2459],{},[143,2458,1789],{}," to follow our character.",[70,2461,2462,2463,1822,2465,632],{},"Make sure the ",[143,2464,1796],{},[858,2466,2467],{},"Tracked Dolly",[70,2469,2470,2471,632],{},"Add some ",[143,2472,2473],{},"damping",[12,2475,2476],{},"Open the Auto Dolly settings:",[67,2478,2479,2486,2495],{},[70,2480,2481,2482,2485],{},"Tick the ",[143,2483,2484],{},"Enabled"," checkbox",[70,2487,1793,2488,2491,2492],{},[143,2489,2490],{},"Position"," Offset to ",[858,2493,2494],{},"5",[70,2496,1793,2497,2500,2501],{},[143,2498,2499],{},"Search"," Resolution to ",[858,2502,2503],{},"10",[12,2505,2506,2507,1822,2509,632],{},"Last for the camera, make sure ",[143,2508,1804],{},[858,2510,1807],{},[108,2512,2515],{"src":2513,"alt":2514},"\u002Fposts\u002F2020\u002F03-10\u002Fcinemachine-dolly-track.jpg","2.5D Game in Unity – Cinemachine dolly track","\nCinemachine dolly track.\n",[12,2517,2518],{},"Go to the track object, and set the path you desire, you can add as many waypoints you need.\nRun the game, we now have a dolly following our character. The advantage we have with a dolly is that we can control some near-camera objects for a parallax effect that works great with this style.",[34,2520,2522],{"id":2521},"multiplayer","Multiplayer",[12,2524,2525,2526,2529],{},"Tracking multiple players instead of one is not much different then what we have done until this point. It requires a target group to follow and a distance range instead of a static distance.\nTo add a target group, go to the Cinemachine menu, and select ",[143,2527,2528],{},"Create Target Group Camera",".\nIn the targets, drag all the characters and set an appropriate radius depending on the size of the characters.\nThe virtual camera is already set with the target group as the Follow Target.\nA difference to note is that we no longer have a static distance, we instead define a distance range for the camera.",[545,2531],{"href":547,"title":548},[553,2533,555],{},{"title":208,"searchDepth":237,"depth":237,"links":2535},[2536,2537,2538,2539,2540,2541],{"id":1728,"depth":230,"text":1729},{"id":1768,"depth":230,"text":1769},{"id":1884,"depth":230,"text":1885},{"id":2254,"depth":230,"text":2255},{"id":2418,"depth":230,"text":2419},{"id":2521,"depth":230,"text":2522},{"480w":2543,"960w":2544},"\u002Fposts\u002F2020\u002F03-10\u002F25d-game-unity-part-5-480w.jpg","\u002Fposts\u002F2020\u002F03-10\u002F25d-game-unity-part-5-960w.jpg","2020-03-10","Create rich camera behaviors with Cinemachine and camera control zones.",{},"\u002Fposts\u002F2020\u002F03\u002F25d-game-in-unity-part-5",{"title":1717,"description":2546},"posts\u002F2020\u002F03\u002F25d-game-in-unity-part-5",[575,99],"WKZ-FLo7ACM","IHJAs63KpI-YEyeK_EFRvLHA4Ogx4X0Rj73dP6CDTXk","25d-game-in-unity-part-5",{"id":2556,"authorPage":233,"extension":585,"meta":2557,"name":2558,"social":2559,"stem":2562,"__hash__":2563},"team\u002Fteam\u002Fnir.yaml",{},"Nir Lahad",{"twitter":2560,"linkedin":2561},"Nirlah","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fnir-lahad-b994675b","team\u002Fnir","XR6GHUplaX0xSjFx9OIVhSL2dd4CVyDFtBi3TRUSr5c",1790297819561]