{"id":50,"date":"2015-06-02T11:57:04","date_gmt":"2015-06-02T10:57:04","guid":{"rendered":"http:\/\/jenshendar.com\/?p=50"},"modified":"2015-06-02T11:57:04","modified_gmt":"2015-06-02T10:57:04","slug":"inventing-an-inventory-in-unity3d-part-1","status":"publish","type":"post","link":"https:\/\/www.jenshendar.com\/index.php\/2015\/06\/02\/inventing-an-inventory-in-unity3d-part-1\/","title":{"rendered":"Inventing an inventory in Unity3D (Part 1)"},"content":{"rendered":"<p><em>Disclaimer: I do not claim this is the best (or even a good way) to design and implement an inventory system. There is a slight (great) chance that I throughout the authoring of this post decide that &#8216;this is stupid&#8217; and just scrap the system all-together and start over. If so, you&#8217;ll get your money(time) back in the form of virtual cupcakes.<\/em><\/p>\n<p>In this post I will try to explain the inventory system for my current lowpoly game project. A short explanation of the gameplay I&#8217;m ultimately going for is somewhat of an adventure\/mystery game. This is because I \u00a0want to keep the project on a difficulty level I can handle, therefor I\u00a0don&#8217;t want to have a combat system and as few advanced models (characters) as possible (This is the first project where I make the models mostly myself).<\/p>\n<p>Right now you can walk around, open a pneumatic house, pick up rocks and look out across the ocean. Epic gameplay!<\/p>\n<p><a href=\"https:\/\/files.jenshendar.com\/lowpoly\/0.08\/0.08.html\">Here<\/a> is a demo of the current working version.<br \/>\n(Because of limitations in Chrome, you have to open it in Firefox)<\/p>\n<p>You move around with WASD and look around with the mouse. The pointer is locked in the middle of the screen and invisible.<\/p>\n<p>If you &#8216;look&#8217; at something you get a small text describing what it is you are looking at (eg. &#8220;Tree&#8221;). This is done with a code similar to this (I removed some other code not needed for the inventory system):<\/p>\n<p>[code lang=&#8221;js&#8221;]<br \/>\nfunction Update () {<br \/>\n    var ray = Camera.main.ViewportPointToRay (Vector3(0.5f,0.5f,0.5f));<br \/>\n    var hit : RaycastHit;<\/p>\n<p>    if (Physics.Raycast (ray, hit, 2)){<br \/>\n       Debug.DrawLine (ray.origin, hit.point);<br \/>\n       rayHitCollider = hit.transform.GetComponent(Collider);<br \/>\n       switch (hit.transform.tag) {<br \/>\n              case &quot;Untagged&quot;:<br \/>\n                     information_text.GetComponent(UI.Text).text = &quot;&quot;;<br \/>\n                     action_text.GetComponent(UI.Text).text = &quot;&quot;;<br \/>\n                     action = &quot;none&quot;;<br \/>\n              break;<br \/>\n              case &quot;Item&quot;:<br \/>\n                     information_text.GetComponent(UI.Text).text = hit.transform.name;<br \/>\n                     if (hit.transform.GetComponent(Collider).GetComponent(Item)) {<br \/>\n                            rayHitCollider = hit.transform.GetComponent(Collider);<br \/>\n                            if (rayHitCollider.GetComponent(Item).ableToPickUp) {<br \/>\n                                   action_text.GetComponent(UI.Text).text = &quot;Pick up (E)&quot;;<br \/>\n                                   action = &quot;pickUpItem&quot;;<br \/>\n                            } else {<br \/>\n                                   action_text.GetComponent(UI.Text).text = &quot;&quot;;<br \/>\n                            }<\/p>\n<p>                     }<br \/>\n              break;<br \/>\n              default:<br \/>\n                     information_text.GetComponent(UI.Text).text = hit.transform.name;<br \/>\n                     action_text.GetComponent(UI.Text).text = &quot;&quot;;<br \/>\n                     action = &quot;none&quot;;<br \/>\n              break;<br \/>\n       }<br \/>\n    } else {<br \/>\n       information_text.GetComponent(UI.Text).text = &quot;&quot;;<br \/>\n       action_text.GetComponent(UI.Text).text = &quot;&quot;;<br \/>\n       action = &quot;none&quot;;<br \/>\n    }<\/p>\n<p>    if (Input.GetKeyDown (&quot;e&quot;)) {<br \/>\n       doAction();<br \/>\n    }<br \/>\n    if (Input.GetKeyDown(&quot;f&quot;)) {<br \/>\n       doItemAction(&quot;throw&quot;);<br \/>\n    }<br \/>\n}<br \/>\n[\/code]<\/p>\n<p>The scripts shoots a raycast towards where the player is looking, locked to the center of the screen. If the hit of the raycast has a tag which is not &#8220;Untagged&#8221;, the text underneath the cursor changes to the name of the object and if the object type (decided by tag) has an option (eg. door-&gt;open) there is an actiontext below the cursor as well and a variable called &#8220;action&#8221; is set. If the object is &#8220;Untagged&#8221; or the raycast doesn&#8217;t hit anything the text is nulled.<\/p>\n<p>If you aim your mouse at something you can interact with and press &#8220;E&#8221; a function called &#8220;doAction()&#8221; is called. It looks like this:<\/p>\n<p>[code lang=&#8221;js&#8221;]<br \/>\nfunction doAction() {<br \/>\n\tswitch (action) {<br \/>\n\t\tcase &quot;openDoor&quot;:<br \/>\n\t\t\tif (rayHitCollider.GetComponent(Door).doorOpen) {<br \/>\n\t\t\t\trayHitCollider.GetComponent(Door).closeDoor();<br \/>\n\t\t\t} else {<br \/>\n\t\t\t\trayHitCollider.GetComponent(Door).openDoor();<br \/>\n\t\t\t}<br \/>\n\t\t\tbreak;<br \/>\n\t\tcase &quot;pickUpItem&quot;:<br \/>\n\t\t\ttransform.GetComponent(CharacterInventory).addItem(rayHitCollider.gameObject);<br \/>\n\t\t\trayHitCollider.gameObject.SetActive(false);<br \/>\n\t\t\trayHitCollider.gameObject.tag = &quot;Untagged&quot;;<br \/>\n\t\t\trayHitCollider.gameObject.transform.parent = equipItemObject.transform;<br \/>\n\t\t\trayHitCollider.gameObject.transform.localPosition = Vector3(0,0,0);<br \/>\n\t\t\trayHitCollider.gameObject.transform.localRotation = Quaternion(0f,0f,0f,0f);<br \/>\n\t\t\tbreak;<br \/>\n\t\tdefault:<br \/>\n\t\t\tbreak;<\/p>\n<p>\t}<\/p>\n<p>}<br \/>\n[\/code]<\/p>\n<p>I&#8217;m going to focus on the inventory-action. If you are able to pick up the item, determined in the first script (a bool in the &#8220;Item&#8221; script attached to all items), the &#8220;pickUpItem&#8221;-switch case is called and a few things happen.<\/p>\n<ul>\n<li>A function in the inventory script is called.<\/li>\n<li>The item is set to disabled.<\/li>\n<li>The item is &#8220;Untagged&#8221;.<\/li>\n<li>The items parent is set to an object called &#8220;equipItemObject&#8221;, which is basically just the position for the item in the players hand.<\/li>\n<li>The position and rotation of the item is set to &#8220;forward&#8221;.<\/li>\n<\/ul>\n<p>So now the item is no longer on the ground but instead resides disabled as a child to a game object on the player. This is how my inventory looks in the Unity editor:<\/p>\n<p><a href=\"http:\/\/174.138.106.112\/wp-content\/uploads\/2015\/06\/Screen-Shot-2015-06-02-at-12.44.34.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-60\" src=\"http:\/\/174.138.106.112\/wp-content\/uploads\/2015\/06\/Screen-Shot-2015-06-02-at-12.44.34.png\" alt=\"Screen Shot 2015-06-02 at 12.44.34\" width=\"266\" height=\"276\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>It&#8217;s an empty gameObject with 8 &#8216;slots&#8217;. When the game starts they\u00a0an array with 8 empty objects (named &#8220;Empty&#8221; and with the empty thumbnail) is created and iterated through, changing the 1-8 to empty objects.<\/p>\n<p>The function called in the inventory\u00a0script on the other hand looks like this:<\/p>\n<p>[code lang=&#8221;js&#8221;]<br \/>\nfunction addItem(item : UnityEngine.GameObject) {<br \/>\n\tvar i=0;<br \/>\n\tfor (var child: UnityEngine.Transform in inventoryUI.transform) {<br \/>\n\t\tif (child.name == &quot;Empty&quot;) {<br \/>\n\t\t\tinventoryList[i] = item;<br \/>\n\t\t\tbreak;<br \/>\n\t\t}<br \/>\n\t\ti++;<br \/>\n\t}<\/p>\n<p>\ti=0;<br \/>\n\tfor (var child: UnityEngine.Transform in inventoryUI.transform) {<br \/>\n\t\tif (i &lt; inventoryList.Count) {<br \/>\n\t\t\tvar tempItem : UnityEngine.GameObject = inventoryList[i];<br \/>\n\t\t\tchild.name = tempItem.GetComponent(Item).itemNameInInventory;<br \/>\n\t\t\tchild.GetComponent(UI.Image).sprite = tempItem.GetComponent(Item).itemImageInInventory;<br \/>\n\t\t}<br \/>\n\t\ti++;<br \/>\n\t}<br \/>\n}<br \/>\n[\/code]<\/p>\n<p>It takes the item picked up from the ground as a parameter and tries to place it in an empty slot. Then it &#8216;resets&#8217; all the items and images according to the new array.<\/p>\n<p>(Pre pick-up)<a href=\"http:\/\/174.138.106.112\/wp-content\/uploads\/2015\/06\/Screen-Shot-2015-06-02-at-12.44.48-3.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignleft size-large wp-image-59\" src=\"http:\/\/174.138.106.112\/wp-content\/uploads\/2015\/06\/Screen-Shot-2015-06-02-at-12.44.48-3-1024x640.png\" alt=\"Screen Shot 2015-06-02 at 12.44.48 (3)\" width=\"1000\" height=\"625\" srcset=\"https:\/\/www.jenshendar.com\/wp-content\/uploads\/2015\/06\/Screen-Shot-2015-06-02-at-12.44.48-3-1024x640.png 1024w, https:\/\/www.jenshendar.com\/wp-content\/uploads\/2015\/06\/Screen-Shot-2015-06-02-at-12.44.48-3-300x188.png 300w, https:\/\/www.jenshendar.com\/wp-content\/uploads\/2015\/06\/Screen-Shot-2015-06-02-at-12.44.48-3-768x480.png 768w, https:\/\/www.jenshendar.com\/wp-content\/uploads\/2015\/06\/Screen-Shot-2015-06-02-at-12.44.48-3-1536x960.png 1536w, https:\/\/www.jenshendar.com\/wp-content\/uploads\/2015\/06\/Screen-Shot-2015-06-02-at-12.44.48-3.png 1680w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>(Post pick-up)<a href=\"http:\/\/174.138.106.112\/wp-content\/uploads\/2015\/06\/Screen-Shot-2015-06-02-at-13.52.52-3.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignleft size-large wp-image-63\" src=\"http:\/\/174.138.106.112\/wp-content\/uploads\/2015\/06\/Screen-Shot-2015-06-02-at-13.52.52-3-1024x640.png\" alt=\"Screen Shot 2015-06-02 at 13.52.52 (3)\" width=\"1000\" height=\"625\" srcset=\"https:\/\/www.jenshendar.com\/wp-content\/uploads\/2015\/06\/Screen-Shot-2015-06-02-at-13.52.52-3-1024x640.png 1024w, https:\/\/www.jenshendar.com\/wp-content\/uploads\/2015\/06\/Screen-Shot-2015-06-02-at-13.52.52-3-300x188.png 300w, https:\/\/www.jenshendar.com\/wp-content\/uploads\/2015\/06\/Screen-Shot-2015-06-02-at-13.52.52-3-768x480.png 768w, https:\/\/www.jenshendar.com\/wp-content\/uploads\/2015\/06\/Screen-Shot-2015-06-02-at-13.52.52-3-1536x960.png 1536w, https:\/\/www.jenshendar.com\/wp-content\/uploads\/2015\/06\/Screen-Shot-2015-06-02-at-13.52.52-3.png 1680w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/a><\/p>\n<p>A rock. In my inventory. Ready for throwing.<\/p>\n<p>There we have it for the first part of the explaining of my inventory system. I&#8217;ll try to get the next\u00a0one, where I&#8217;ll go over actions you can take with the item in hand and how to place it back on the ground, later this week.<\/p>\n<p>Thanks for reading and comment with any questions or if you have any tips on how I could improve, both this code and in general.<\/p>\n<p>Have a good one!<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p><em>Edit: I noted a bug in the code that removes the item from the inventory when thrown. If you try the demo and throw the rock away, then pick it up, the &#8220;Action-text&#8221; will be nulled. It&#8217;s fixed now, but I won&#8217;t upload a new version of the demo just yet.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Disclaimer: I do not claim this is the best (or even a good way) to design and implement an inventory system. There is a slight (great) chance that I throughout the authoring of this post decide that &#8216;this is stupid&#8217; and just scrap the system all-together and start over. If so, you&#8217;ll get your money(time).<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3,1],"tags":[25,32,43],"class_list":["post-50","post","type-post","status-publish","format-standard","hentry","category-game-development","category-uncategorized","tag-inventory","tag-lowpolyworld","tag-ui"],"_links":{"self":[{"href":"https:\/\/www.jenshendar.com\/index.php\/wp-json\/wp\/v2\/posts\/50","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.jenshendar.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.jenshendar.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.jenshendar.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.jenshendar.com\/index.php\/wp-json\/wp\/v2\/comments?post=50"}],"version-history":[{"count":0,"href":"https:\/\/www.jenshendar.com\/index.php\/wp-json\/wp\/v2\/posts\/50\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.jenshendar.com\/index.php\/wp-json\/wp\/v2\/media?parent=50"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.jenshendar.com\/index.php\/wp-json\/wp\/v2\/categories?post=50"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.jenshendar.com\/index.php\/wp-json\/wp\/v2\/tags?post=50"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}