<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Florent Lagrede</title>
    <description>The latest articles on DEV Community by Florent Lagrede (@flagrede).</description>
    <link>https://dev.to/flagrede</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F403330%2F502e6509-2675-4d34-a1aa-76e6b0d7a71b.jpg</url>
      <title>DEV Community: Florent Lagrede</title>
      <link>https://dev.to/flagrede</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/flagrede"/>
    <language>en</language>
    <item>
      <title>How to reproduce Death Stranding UI with react and react-three-fiber</title>
      <dc:creator>Florent Lagrede</dc:creator>
      <pubDate>Thu, 15 Apr 2021 17:15:13 +0000</pubDate>
      <link>https://dev.to/flagrede/how-to-reproduce-death-stranding-ui-with-react-and-react-three-fiber-cif</link>
      <guid>https://dev.to/flagrede/how-to-reproduce-death-stranding-ui-with-react-and-react-three-fiber-cif</guid>
      <description>&lt;p&gt;In this demo, we will try to reproduce one of the main interface of the Death Stranding game.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7dkd3l1beqmy9qxlezv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7dkd3l1beqmy9qxlezv.png" alt="image" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://deathstranding.gameuionweb.com/" rel="noopener noreferrer"&gt;Demo Link&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/Flow11/death-stranding-ui" rel="noopener noreferrer"&gt;Demo repository&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  About the game
&lt;/h1&gt;

&lt;p&gt;Death Stranding is a game produced by Hideo Kojima (especially known for its Metal Gear series games). The game takes place in a post-apocalyptic future where an unknown phenomenon has ravaged most of the world. You play a character, Sam,  responsible for making merchandise deliveries to the scattered remains of the population in a world that became quite dangerous. If Sam looks familiar to you it’s because its model is based on the actor who played Daryl in Walking Dead.&lt;/p&gt;
&lt;h2&gt;
  
  
  About this interface
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi8adocfydxhg0hary5e9.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi8adocfydxhg0hary5e9.jpeg" alt="Alt Text" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On this interface, the player must arrange the merchandise he will carry from point A to point B.&lt;br&gt;
The arrangement done by the player will have a significant consequence on the success of the delivery.&lt;/p&gt;

&lt;p&gt;This interface is really interesting for a number of reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The player is expected to spend some time in this interface so it is really important that it doesn’t break the flow of the game.&lt;/li&gt;
&lt;li&gt;It should also keep the player fully immersed in the universe of the game&lt;/li&gt;
&lt;li&gt;How it uses both a 2D overlay on top of a 3D Scene&lt;/li&gt;
&lt;li&gt;Its aesthetic choices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the sake of this article, I reduced the scope of the interface but I tried to keep the essence of what makes it interesting. Our goal will be to reproduce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The 3D scene to display the merchandises&lt;/li&gt;
&lt;li&gt;The 2D overlay to manage the merchandises&lt;/li&gt;
&lt;li&gt;Keeping some interactions between the 2D overlay and the 3D scene&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the 3D scene, there will be 3 different positions to display the merchandises:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Private locker (the main storage)&lt;/li&gt;
&lt;li&gt;Shared locker (alternative storage)&lt;/li&gt;
&lt;li&gt;Sam cargo (represents merchandises carried by Sam)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Target audience
&lt;/h2&gt;

&lt;p&gt;This article requires some knowledge about threejs and react-three-fiber.&lt;br&gt;
If you have no experience in threejs the best resource on the web to get started is the course made by Bruno Simon: &lt;a href="https://threejs-journey.xyz/" rel="noopener noreferrer"&gt;ThreejsJourney&lt;/a&gt;&lt;br&gt;
If you’re looking for resources on react-three-fiber you can take a look at this &lt;a href="https://github.com/gsimone/awesome-react-three-fiber" rel="noopener noreferrer"&gt;repository&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Format
&lt;/h2&gt;

&lt;p&gt;There are 2 possibilities to consume this article. You can simply read it to get a global understanding of how the demo works or you can try to reproduce the demo to have a deeper understanding.&lt;br&gt;
If you choose the latter, I created a &lt;a href="https://codesandbox.io/s/death-stranding-starter-7v9v9?file=/src/App.js" rel="noopener noreferrer"&gt;starter&lt;/a&gt; project on codesanbox with all the assets to get started more easily. You can also download it if you prefer to work locally.&lt;br&gt;
Feel free to choose what suits you best.&lt;/p&gt;
&lt;h3&gt;
  
  
  Starter
&lt;/h3&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/7v9v9"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Complete demo
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/flagrede" rel="noopener noreferrer"&gt;
        flagrede
      &lt;/a&gt; / &lt;a href="https://github.com/flagrede/death-stranding-ui" rel="noopener noreferrer"&gt;
        death-stranding-ui
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Death Stranding UI made in React
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Death Stranding GameUI demo&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/530644/111818381-6647d400-88df-11eb-8a1c-afa2d4b5c58d.jpeg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F530644%2F111818381-6647d400-88df-11eb-8a1c-afa2d4b5c58d.jpeg" alt="demo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Demo link&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://deathstranding.gameuionweb.com/" rel="nofollow noopener noreferrer"&gt;https://deathstranding.gameuionweb.com/&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Article link:&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://dev.to/flagrede/how-to-reproduce-death-stranding-ui-with-react-and-react-three-fiber-cif" rel="nofollow"&gt;https://dev.to/flagrede/how-to-reproduce-death-stranding-ui-with-react-and-react-three-fiber-cif&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Stack&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;react-three-fiber&lt;/li&gt;
&lt;li&gt;react-three-a11y&lt;/li&gt;
&lt;li&gt;react-spring&lt;/li&gt;
&lt;li&gt;twind&lt;/li&gt;
&lt;li&gt;drei&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Credits&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://sketchfab.com/3d-models/death-stranding-briefcase-a302797e258742f394256c0fbb8396ef" rel="nofollow noopener noreferrer"&gt;Briefcase model&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/flagrede/death-stranding-ui" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;p&gt;The base project is a classic create-react-app. Here’s the list of the additional libraries used in it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/react-spring/react-three-fiber" rel="noopener noreferrer"&gt;react-three-fiber&lt;/a&gt; (for the 3D scene)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.react-spring.io/" rel="noopener noreferrer"&gt;react-spring&lt;/a&gt; (for 2D and 3D animations)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/pmndrs/valtio" rel="noopener noreferrer"&gt;valtio&lt;/a&gt; (state management)&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://twind.dev/" rel="noopener noreferrer"&gt;twind&lt;/a&gt; (styling solution based on Tailwind)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/react-spring/drei" rel="noopener noreferrer"&gt;drei&lt;/a&gt; (react-three-fiber helpers collection)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A note on Twind:&lt;br&gt;
This library is a CSS-in-JS version of TailwindJS. If you're more comfortable with another styling solution don't hesitate to replace it. If you prefer vanilla Tailwind, Twind can be used just like that by using the following &lt;a href="https://twind.dev/handbook/the-shim.html" rel="noopener noreferrer"&gt;shim&lt;/a&gt; (already included in the starter).&lt;/p&gt;
&lt;h2&gt;
  
  
  Interface components
&lt;/h2&gt;

&lt;p&gt;We're going to start building our interface with the 3D part. First, we will create the 3D grid of the private locker. The grid cell delimitations will be done using particles.&lt;br&gt;
Then we will create two smaller grids (for shared locker and sam cargo) without particles. Finally, we need to be able to move the camera between these 3 positions.&lt;/p&gt;
&lt;h3&gt;
  
  
  3D
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Components List
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk0c82m1vy8yvqw2pz2ly.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk0c82m1vy8yvqw2pz2ly.png" alt="3d-archi" width="800" height="504"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Briefcase
&lt;/h4&gt;

&lt;p&gt;This component will be responsible for loading and displaying the model. We will go through the whole process but some parts are already done in the starter.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;download our gltf model from &lt;a href="https://sketchfab.com/3d-models/death-stranding-briefcase-a302797e258742f394256c0fbb8396ef" rel="noopener noreferrer"&gt;sketchfab&lt;/a&gt; (credit goes to luac for the model)&lt;/li&gt;
&lt;li&gt;convert it to a react component using &lt;a href="https://github.com/pmndrs/gltfjsx" rel="noopener noreferrer"&gt;gtltfjsx&lt;/a&gt; locally or the new &lt;a href="https://gltf.pmnd.rs/" rel="noopener noreferrer"&gt;online version&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;convert PNG to JPEG and optimize them&lt;/li&gt;
&lt;li&gt;using &lt;a href="https://github.com/CesiumGS/gltf-pipeline/blob/master/README.md" rel="noopener noreferrer"&gt;draco&lt;/a&gt; to convert our gltf file to GLB and compress it at the same time.&lt;/li&gt;
&lt;li&gt;put the GLB file in our &lt;code&gt;/public&lt;/code&gt; folder&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq604dek6ldwbdu9p0iw6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq604dek6ldwbdu9p0iw6.png" alt="image" width="525" height="378"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;At this point, we should be able to see the model. Now we have to position/rotate/scale the model correctly so it fits the original UI.&lt;/p&gt;

&lt;p&gt;We will also handle a secondary display for the model. It will be useful later on to separate the selected item from the other. For this secondary display, we will try to display it with a translucent blue color and a wireframe on top of it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, we need to duplicate the main material (the first one) of the briefcase into two meshes&lt;/li&gt;
&lt;li&gt;For the translucent blue color we can use a simple shader by using &lt;a href="https://github.com/pmndrs/component-material" rel="noopener noreferrer"&gt;component-material&lt;/a&gt; on the first material
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SelectedMaterial&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;blue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Material&lt;/span&gt;
       &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
       &lt;span class="na"&gt;uniforms&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="na"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;float&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
         &lt;span class="na"&gt;g&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;float&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
         &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;float&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
       &lt;span class="na"&gt;transparent&lt;/span&gt;
     &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Material&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Frag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Body&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`gl_FragColor = vec4(r, g, b, blue);`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Material&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;For the wireframe it’s already built-in threejs, we just have to use the wireframe attribute on the second material&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff8rin3xibteqh7t67khm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff8rin3xibteqh7t67khm.png" alt="selected material" width="257" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To simulate the selected state you can try to use &lt;a href="https://github.com/pmndrs/react-three-a11y" rel="noopener noreferrer"&gt;react-three-a11y&lt;/a&gt;. By wrapping our model with the &lt;code&gt;&amp;lt;A11y&amp;gt;&lt;/code&gt; component we will have access to hover, focus, and pressed state through &lt;code&gt;useA11y()&lt;/code&gt; hook. We can try to display a SelectedMaterial based on the hover state for example.&lt;/p&gt;

&lt;p&gt;Since we will have a 2D overlay on top of the 3D scene we won’t need &lt;code&gt;react-three-a11y&lt;/code&gt; afterward but it’s good to know that you can bring accessibility to your 3D scene quite easily with it.&lt;/p&gt;
&lt;h4&gt;
  
  
  Particles grid
&lt;/h4&gt;

&lt;p&gt;This will be the most complex part of the demo.&lt;br&gt;
To recreate this grid we will need 2 components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Grid component to display the particles&lt;/li&gt;
&lt;li&gt;A GridContainer to compute the positions of the particles and the briefcases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are 2 different kinds of particles which are called &lt;code&gt;smallCross&lt;/code&gt; and &lt;code&gt;bigCross&lt;/code&gt;. In the end, we will have to compute these 2 position arrays plus the one for the briefcases.&lt;/p&gt;
&lt;h5&gt;
  
  
  Grid
&lt;/h5&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn5ivdbof1xs3ciar9elu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn5ivdbof1xs3ciar9elu.png" alt="image" width="465" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, we will start with the Grid component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Grid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;positions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;points&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;pointsMaterial&lt;/span&gt;
     &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
     &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
     &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"#316B74"&lt;/span&gt;
     &lt;span class="na"&gt;alphaMap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;texture&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
     &lt;span class="na"&gt;transparent&lt;/span&gt;
     &lt;span class="na"&gt;depthWrite&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
     &lt;span class="na"&gt;blending&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AdditiveBlending&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
   &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;bufferGeometry&lt;/span&gt; &lt;span class="na"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"geometry"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;bufferAttribute&lt;/span&gt; &lt;span class="na"&gt;attachObject&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attributes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;position&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;positions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;array&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;positions&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;itemSize&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
   &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;bufferGeometry&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;points&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwqudhikzhoob5u1vz72m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwqudhikzhoob5u1vz72m.png" alt="largeCross" width="56" height="56"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Here we’re using an alpha map texture to recreate the “cross” particle effect. We’re also tweaking a few parameters for the colors and the transparency. The particle’s positions and count are given to the &lt;code&gt;bufferAttribute&lt;/code&gt; tag. The positions array needs to have the following format &lt;code&gt;[x1, y1, z1, x2, y2, z2, ...]&lt;/code&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  GridsContainer
&lt;/h5&gt;

&lt;p&gt;Let’s continue with the GridsContainer.&lt;br&gt;
We said that we have 3 position arrays to compute but we can do the 3 of them at the same time.&lt;/p&gt;

&lt;p&gt;First question, how many particles do we need for the small cross particles array?&lt;/p&gt;

&lt;p&gt;Let’s say we want&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;20 particles per line&lt;/li&gt;
&lt;li&gt;6 lines&lt;/li&gt;
&lt;li&gt;2 layers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also for one particle weed 3 values (x, y, z).&lt;br&gt;
In the end, we will need an array 720 values (20 * 6 * 2 * 3) to display a grid of 20 columns, 6 lines, and 2 layers.&lt;/p&gt;

&lt;p&gt;This is only for the small cross particles position array, the big cross array has 2 times less coordinate and the briefcases one 4 times less.&lt;/p&gt;

&lt;p&gt;This is because for each cell we want to display:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;4 small cross particles&lt;/li&gt;
&lt;li&gt;2 big cross particles&lt;/li&gt;
&lt;li&gt;1 briefcase&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are probably several ways of doing this. Here’s one method:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;loop over the array with 720 placeholder values&lt;/li&gt;
&lt;li&gt;for each loop, we need to know if we’re computing an x, y, or z coordinate&lt;/li&gt;
&lt;li&gt;for each case, we compute 3 differents coordinates (small cross, big cross, briefcase)&lt;/li&gt;
&lt;li&gt;we push these 3 coordinates in their respective arrays &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the end of the loop, we can filter the coordinates we don’t need for the big cross and briefcases arrays (remember that we have 2 times and 4 times fewer coordinates for these too).&lt;/p&gt;

&lt;p&gt;Don’t hesitate to put every configuration variable (columns, lines, layers, spacing …) for this grid in a tool like &lt;a href="https://github.com/pmndrs/leva" rel="noopener noreferrer"&gt;leva&lt;/a&gt; to make it look like what you want.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ssc8ciqtzoc5bjqi6ut.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ssc8ciqtzoc5bjqi6ut.gif" alt="leva" width="720" height="306"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;In the actual render, we need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;map over an arbitrary number (we will change that later) &lt;/li&gt;
&lt;li&gt;render our Briefcase components with &lt;code&gt;positionsBriefcases&lt;/code&gt; values&lt;/li&gt;
&lt;li&gt;render a Grid components with &lt;code&gt;positionsSmallCross&lt;/code&gt; values&lt;/li&gt;
&lt;li&gt;render a Grid components with &lt;code&gt;positionsBigCross&lt;/code&gt; values&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  External grid
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw67jk2ixrbwutnnq6bf9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw67jk2ixrbwutnnq6bf9.png" alt="image" width="482" height="164"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;This one is simpler than the grid we just build since it doesn’t use any particles. &lt;br&gt;
Here we just want to display briefcases on the same Z value, 3 columns, and any number of lines. In our new ExternalGrid component we will map just the briefcases list and call a util function to get the position.&lt;/p&gt;

&lt;p&gt;Our util function to get the position could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;X_SPACING&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Y_SPACING&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getPositionExternalGrid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;columnWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;columnWidth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;X_SPACING&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;columnWidth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;Y_SPACING&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Floor and fog
&lt;/h4&gt;

&lt;p&gt;To make the scene look right color-wise on the background we have to add a floor and a fog.&lt;/p&gt;

&lt;p&gt;Floor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;   &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Plane&lt;/span&gt; &lt;span class="na"&gt;rotation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;planeBufferGeometry&lt;/span&gt; &lt;span class="na"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"geometry"&lt;/span&gt; &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meshStandardMaterial&lt;/span&gt; &lt;span class="na"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"material"&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"#1D2832"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
   &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Plane&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fog:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;fog&lt;/span&gt; &lt;span class="na"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"fog"&lt;/span&gt; &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#2A3C47&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add these 2 elements to the main canvas.&lt;/p&gt;

&lt;h3&gt;
  
  
  2D
&lt;/h3&gt;

&lt;h4&gt;
  
  
  State and data
&lt;/h4&gt;

&lt;p&gt;Before going into building the HTML UI we need to create our state with the data.&lt;br&gt;
For this demo, I wanted to give a try to &lt;code&gt;valtio&lt;/code&gt; as the state manager.&lt;/p&gt;

&lt;p&gt;We will need to create a state with &lt;code&gt;proxyWithComputed&lt;/code&gt;, because we will have to computed values based on the state.&lt;/p&gt;

&lt;p&gt;In the actual state we have only two values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;allItems (list of all the briefcases)&lt;/li&gt;
&lt;li&gt;selectedItem (index of the selected briefcase inside allItems)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To populate it we need a function to generate data. This function already exists in the starter.&lt;/p&gt;

&lt;p&gt;So our state looks like this for now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;proxyWithComputed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;selectedItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;allItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nf"&gt;generateItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;private&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;generateItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;share&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;generateItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sam&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second parameter takes an object and is used to define the computed values.&lt;br&gt;
Here’s the list of computed values we will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;isPrivateLocker (based on the selectedItem)&lt;/li&gt;
&lt;li&gt;isShareLocker (based on the selectedItem)&lt;/li&gt;
&lt;li&gt;isSamCargo (based on the selectedItem)&lt;/li&gt;
&lt;li&gt;itemsPrivateLocker (filter allItems)&lt;/li&gt;
&lt;li&gt;itemsShareLocker (filter allItems)&lt;/li&gt;
&lt;li&gt;itemsSam (filter allItems)&lt;/li&gt;
&lt;li&gt;allItemsSorted (use filter computed values to sort the array)&lt;/li&gt;
&lt;li&gt;selectedId (ID of the selected item)&lt;/li&gt;
&lt;li&gt;selectedCategory (category of the selected item)&lt;/li&gt;
&lt;li&gt;totalWeight (sum of briefcase weight inside Sam cargo)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Components List
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feby1m0j5npmg3anac8qo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feby1m0j5npmg3anac8qo.png" alt="2d-archi" width="800" height="690"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h4&gt;
  
  
  Inventory
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2myqp40dy3vlaikdse8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2myqp40dy3vlaikdse8.png" alt="inventory" width="603" height="624"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the component that will display our list of briefcases. As we saw on the schema it uses the following child components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MenuTab (pure UI component)&lt;/li&gt;
&lt;li&gt;MenuItems (display a portion of the list, ie: briefcases in PrivateLocker)&lt;/li&gt;
&lt;li&gt;ActionModal (will be discussed just after)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The component should also handle the following events:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keyboard navigation&lt;/li&gt;
&lt;li&gt;mouse events&lt;/li&gt;
&lt;li&gt;update the selected briefcase in the store&lt;/li&gt;
&lt;li&gt;open ActionModal&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Action modal
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmfjyla2ljgjpmg8llb8m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmfjyla2ljgjpmg8llb8m.png" alt="action modal" width="460" height="114"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this modal, we add actions to move the selected briefcase from one category to another.&lt;br&gt;
To do that we just need to update the category of the selected item in the store. Since we’re using computed values to display the lists, everything should update automatically.&lt;/p&gt;

&lt;p&gt;We will also need to handle keyboard navigation in this modal.&lt;/p&gt;
&lt;h4&gt;
  
  
  Item description
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftcgsq858ajuh186osjba.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftcgsq858ajuh186osjba.png" alt="item description" width="495" height="565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the right side part of the UI. We just need to display all the data of the selected item here. &lt;/p&gt;

&lt;p&gt;The only interaction is about the like button. Whenever the user clicks on it we should update the likes count of the selected briefcase. This is simple to do thanks to Valtio, we just update &lt;code&gt;allItems[selectedItem].likes&lt;/code&gt; in the state directly and the likes counts should update in the Inventory.&lt;/p&gt;
&lt;h3&gt;
  
  
  Combining 2D and 3D
&lt;/h3&gt;

&lt;p&gt;We now have a 2D UI and a 3D scene, it would be nice to make them interact with each other. &lt;/p&gt;
&lt;h4&gt;
  
  
  Selected briefcase
&lt;/h4&gt;

&lt;p&gt;Currently, we just highlight the selected item in the UI part. We need to reflect this to the 3D briefcase as well. We already made the selected material, we just need to use it inside the &lt;code&gt;Briefcase&lt;/code&gt; component.&lt;/p&gt;
&lt;h4&gt;
  
  
  Scene transition
&lt;/h4&gt;

&lt;p&gt;From now on, our camera was only looking at the main grid, the private locker. We will create 3 components to move the camera and display them based on the properties isPrivateLocker, isShareLocker, and isSamCargo that we created earlier in the state.&lt;/p&gt;

&lt;p&gt;Here for example the code that look at the main grid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ZoomPrivateLocker&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Vector3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;useFrame&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lerp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.075&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lookAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateProjectionMatrix&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Adding perspective
&lt;/h4&gt;

&lt;p&gt;To give our UI a more realistic look we must make it look like it is slightly rotated from the camera. We can do that with the following CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;perspective&lt;/span&gt; &lt;span class="err"&gt;800px;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.htmlOverlay&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotate3d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;357deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Animations
&lt;/h3&gt;

&lt;p&gt;We’re now going to add some animations to both the UI and the 3D scene.&lt;br&gt;
All animations has been done using &lt;code&gt;react-spring&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  2D
&lt;/h4&gt;
&lt;h5&gt;
  
  
  MenuEffect
&lt;/h5&gt;

&lt;p&gt;This is the animation that happens inside Inventory whenever the selected item changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4becx1twk7fw14pqh03v.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4becx1twk7fw14pqh03v.gif" alt="menu-effect2" width="1000" height="80"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;There are actually 3 parts to this animation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a sliding background going from left to right&lt;/li&gt;
&lt;li&gt;the item background going from 0 to 100% height&lt;/li&gt;
&lt;li&gt;a slight blinking loop for the background-color&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will go through each of them and combine them together with the &lt;code&gt;useChain&lt;/code&gt; hook.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sliding animation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To reproduce this animation we will need custom SVGs (they are already available in the starter). I used the tool &lt;a href="https://yqnn.github.io/svg-path-editor/" rel="noopener noreferrer"&gt;https://yqnn.github.io/svg-path-editor/&lt;/a&gt; to make 3 SVGs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzjnjedblgxkqb588dyna.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzjnjedblgxkqb588dyna.png" alt="image" width="800" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I think we could have an even better effect with more SVGs, feel free to try adding more frames to animation.&lt;br&gt;
To animate these 3 SVGs, we will declare a &lt;code&gt;x&lt;/code&gt; property inside a &lt;code&gt;useSpring&lt;/code&gt; going from 0 to to 2 and in the render we will have this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;         &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;
           &lt;span class="na"&gt;d&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;
             &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
             &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
               &lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
               &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                 &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;M 0 0 l 16 0 l 0 3 l -16 0 l 0 -3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;M 0 0 l 25 0 l -10 3 l -15 0 l 0 -3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;M 0 0 l 16 0 L 16 3 l -5 0 l -11 -3 m 11 3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="p"&gt;],&lt;/span&gt;
             &lt;span class="p"&gt;})&lt;/span&gt;
           &lt;span class="si"&gt;}&lt;/span&gt;
         &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
       &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we just need to animate the opacity and the width and we should have a good sliding animation effect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;background height&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here we’re just expending the item’s background with a default spring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSpring&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;heightRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;glowing color animation&lt;/strong&gt;&lt;br&gt;
To reproduce this part we will make a spring between 2 colors and play with the opacity at the same time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="nx"&gt;bgOpacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSpring&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;bgOpacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#456798&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;bgOpacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#3E5E8D&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bgOpacityRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;easing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;All together&lt;/strong&gt;&lt;br&gt;
Finally, we just have to use these 3 animations with the &lt;code&gt;useChain&lt;/code&gt; hook&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt; &lt;span class="nf"&gt;useChain&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;opacityRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;heightRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bgOpacityRef&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  SideMenuEffect
&lt;/h5&gt;

&lt;p&gt;The SideMenu animation will use the same technique we just saw. It will be a spring that goes through 3 SVGs. Again I was a bit lazy on the number of SVG frames, feel free to try with more.&lt;br&gt;
Here are the 3 SVGs I used for the demo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;             &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
               &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;M 0 0 l 16 0 l 0 3 l -16 0 l 0 -3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;M 0 0 l 25 0 l -10 3 l -15 0 l 0 -3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;M 0 0 l 16 0 L 16 3 l -5 0 l -11 -3 m 11 3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  AnimatedOuterBox
&lt;/h5&gt;

&lt;p&gt;Here our OuterBox component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OuterBox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"h-1 w-2 bg-gray-200 absolute top-0 left-0"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"h-1 w-2 bg-gray-200 absolute top-0 right-0"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"h-1 w-2 bg-gray-200 absolute bottom-0 left-0"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"h-1 w-2 bg-gray-200 absolute bottom-0 right-0"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This component is displayed inside ItemDescription one. It shows four little white stripes at the edges of ItemDescription.&lt;/p&gt;

&lt;p&gt;On the animation side, we will have to animate the height property of the component from 0 to 100%.&lt;/p&gt;

&lt;h5&gt;
  
  
  AnimatedBar
&lt;/h5&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuxsmuq7rjm2fl1cgyqg4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuxsmuq7rjm2fl1cgyqg4.png" alt="image" width="453" height="76"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;For the bar that shows an item's durability, we will make an animated bar (like a loader).&lt;br&gt;
We need to animate the &lt;code&gt;width&lt;/code&gt; property based on the damage attribute of the item.&lt;/p&gt;
&lt;h4&gt;
  
  
  3D
&lt;/h4&gt;

&lt;p&gt;For the 3D scene, we will add just one animation that will be triggered whenever a briefcase is changed from one category to another. We will make it seem like the briefcases, those that have changed, are falling from above.&lt;/p&gt;

&lt;p&gt;We can handle this animation in the Briefcase component. Whenever the position of a briefcase will change, we will animate the new value on the Y-axis from the new value plus a delta to the new value.&lt;/p&gt;

&lt;p&gt;Until now the spring animations were triggered whenever a component was mounted. Here we need to animate briefcases that are already mounted.&lt;br&gt;
To trigger a spring that has already been played once we need the second parameter received from the &lt;code&gt;useSpring&lt;/code&gt; hook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;animatedPosition&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSpring&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Be careful to use &lt;code&gt;@react-spring/three&lt;/code&gt; instead of &lt;code&gt;@react-spring/web&lt;/code&gt; here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sounds
&lt;/h3&gt;

&lt;p&gt;For the sounds part we’re going to create a sound manager component using &lt;code&gt;useSound&lt;/code&gt; hook from &lt;a href="https://github.com/joshwcomeau/use-sound" rel="noopener noreferrer"&gt;Joshua Comeau&lt;/a&gt;. After that, we’re going to put our sound functions newly-created into our state so that we can everywhere in the app.&lt;/p&gt;

&lt;p&gt;Here’s the list of sounds we need to handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;like button&lt;/li&gt;
&lt;li&gt;menu change (played whenever the item selected change)&lt;/li&gt;
&lt;li&gt;menu action (played whenever the action modal is opened)&lt;/li&gt;
&lt;li&gt;menu validate (played whenever the action modal is closed)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We’re done with the tutorial, I hope you liked it. If you’re trying to make your own version of the Death Stranding UI, don’t hesitate to share it with me on &lt;a href="https://twitter.com/flagrede" rel="noopener noreferrer"&gt;twitter&lt;/a&gt;. If you're interested in more GameUI on web demos I share updates on the upcoming demos on this &lt;a href="https://www.gameuionweb.com/" rel="noopener noreferrer"&gt;newsletter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>reactthreefiber</category>
    </item>
    <item>
      <title>Making a 2D RPG game with react-three-fiber</title>
      <dc:creator>Florent Lagrede</dc:creator>
      <pubDate>Thu, 22 Oct 2020 06:52:28 +0000</pubDate>
      <link>https://dev.to/flagrede/making-a-2d-rpg-game-with-react-tree-fiber-4af1</link>
      <guid>https://dev.to/flagrede/making-a-2d-rpg-game-with-react-tree-fiber-4af1</guid>
      <description>&lt;p&gt;In this article we're going to take a closer look at an &lt;a href="https://github.com/coldi/r3f-game-demo" rel="noopener noreferrer"&gt;open source&lt;/a&gt; demo published by &lt;a href="https://twitter.com/coldi" rel="noopener noreferrer"&gt;@coldi&lt;/a&gt;. Coldi made a game, called &lt;a href="https://coldigames.itch.io/colmens-quest" rel="noopener noreferrer"&gt;Colmen's Quest&lt;/a&gt; (that you should definitely check out), using react and react-three-fiber. He was kind enough to share the core engine that he made for his game to the community.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frkr8oueuc7cb4ewfovin.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frkr8oueuc7cb4ewfovin.png" alt="Colmen's Quest" width="800" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It might sound odd to use a 3D library like ThreeJS to make a 2D game but it is actually not that uncommon at all. For example Unity, the popular 3D game engine, is also used a lot for 2D games like &lt;a href="https://unity.com/fr/madewith/hollow-knight" rel="noopener noreferrer"&gt;Hollow Knight&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Speaking about Unity, the game architecture that Coldi used is also inspired by Unity and resolve around the concept of &lt;a href="https://blog.eyas.sh/2020/10/unity-for-engineers-pt1-basic-concepts/" rel="noopener noreferrer"&gt;GameObject&lt;/a&gt; components that we will talk about just after.&lt;br&gt;
Adding react-three-fiber to the stack provides a terrific dev experience to make a webgl game with React.&lt;/p&gt;

&lt;p&gt;This project is a really valuable learning material. By exploring it in this article we will learn a lot about game dev techniques, react-three-fiber and also React knowledge in general. We will also try to apply our newly acquired knowledge by tweaking the demo a bit. Let's dive in !&lt;/p&gt;
&lt;h2&gt;
  
  
  The game demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://compassionate-pare-ece3e5.netlify.app/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpum0sxin0cvtteiqqmv0.png" alt="R3F 2D game demo" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://compassionate-pare-ece3e5.netlify.app/" rel="noopener noreferrer"&gt;Demo link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's start by analyzing the elements and features that we have in this demo.&lt;br&gt;
We have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🗺 A map

&lt;ul&gt;
&lt;li&gt;defined with &lt;a href="https://en.wikipedia.org/wiki/Tile-based_video_game" rel="noopener noreferrer"&gt;tilesets&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;🚶‍♂️ A character that can be moved with either a mouse or a keyboard

&lt;ul&gt;
&lt;li&gt;the mouse movement is trickier as it needs to compute the path ahead&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;🧱 A collision system

&lt;ul&gt;
&lt;li&gt;which prevents to walk into walls or objects&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;👉 An interaction system

&lt;ul&gt;
&lt;li&gt;pizza can be picked up and it is possible to interact with computers and coffee machines&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;📽 A scene system

&lt;ul&gt;
&lt;li&gt;to move from one room to another&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can start by cloning the demo here:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/coldi" rel="noopener noreferrer"&gt;
        coldi
      &lt;/a&gt; / &lt;a href="https://github.com/coldi/r3f-game-demo" rel="noopener noreferrer"&gt;
        r3f-game-demo
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A demo on how to do a simple tile-based game with React and react-three-fiber
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;react-three-fiber Game Demo&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/coldi/r3f-game-demo/media/game-demo.gif"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fcoldi%2Fr3f-game-demo%2Fmedia%2Fgame-demo.gif" alt="Game Demo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This repo shows an example implementation of a top-down 2d game made with React and &lt;a href="https://github.com/pmndrs/react-three-fiber" rel="noopener noreferrer"&gt;react-three-fiber&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I used the core functionality to create &lt;a href="https://coldigames.itch.io/colmens-quest" rel="nofollow noopener noreferrer"&gt;Colmen's Quest&lt;/a&gt; and wanted to give you an idea of how a game can be done with React.&lt;/p&gt;
&lt;p&gt;This is by no means the best way to build a game, it's just my way. 😊&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I suggest you use this code as an inspiration and not as a starting point to build your game on top of it. I also do not intend to maintain this code base in any way.&lt;/strong&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Get started&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;You can start the game by &lt;code&gt;yarn &amp;amp;&amp;amp; yarn start&lt;/code&gt;, then &lt;a href="http://localhost:3000/" rel="nofollow noopener noreferrer"&gt;open your Browser&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To get a better understanding of the architecture I used, you may want to read &lt;a href="https://twitter.com/coldi/status/1254446313955119104" rel="nofollow noopener noreferrer"&gt;this thread on Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;👉 Also Florent Lagrede (&lt;a href="https://twitter.com/flagrede" rel="nofollow noopener noreferrer"&gt;@flagrede&lt;/a&gt;) did an &lt;strong&gt;amazing job&lt;/strong&gt; in writing an…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/coldi/r3f-game-demo" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Folders architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fyz43rkbd3k20gfuqebxy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fyz43rkbd3k20gfuqebxy.png" alt="Folder structure" width="175" height="186"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/core"&gt;@core&lt;/a&gt;&lt;/strong&gt;: everything that is reusable and not specific to the current demo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;components&lt;/strong&gt;:  components that hold logics more specific to the current demo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;entities&lt;/strong&gt;: describe elements in the game world (Pizza, Plant, Player...). All these elements are &lt;code&gt;GameObject&lt;/code&gt;. We're going to explain more of this concept just below.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;scenes&lt;/strong&gt;: represents the different rooms in the game. Scenes are an aggregation of &lt;code&gt;GameObject&lt;/code&gt;. In the demo there are two scenes (Office and Other).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Game architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzejl72unn2wfwyf09d0u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzejl72unn2wfwyf09d0u.png" alt="game architecture" width="800" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The component architecture looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Game&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AssetLoader&lt;/span&gt; &lt;span class="na"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;urls&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Loading assets ..."&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SceneManager&lt;/span&gt; &lt;span class="na"&gt;defaultScene&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"office"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Scene&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"office"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;OfficeScene&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Scene&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Scene&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"other"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;OtherScene&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Scene&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;SceneManager&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;AssetLoader&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Game&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're going to explain each of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture - top part
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Game
&lt;/h4&gt;

&lt;p&gt;This component has 4 main features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;register all &lt;code&gt;GameObject&lt;/code&gt; inside the game&lt;/li&gt;
&lt;li&gt;a global state&lt;/li&gt;
&lt;li&gt;render the &lt;code&gt;Canvas&lt;/code&gt; component from &lt;code&gt;react-three-fiber&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;pass a context to all its children with the global state and methods to find/register &lt;code&gt;GameObject&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  AssetLoader
&lt;/h4&gt;

&lt;p&gt;This component will load all image and audio assets of the game with the &lt;code&gt;Image&lt;/code&gt; and &lt;code&gt;Audio&lt;/code&gt; web object. It also displays an html overlay on top of the canvas while the assets are loading.&lt;/p&gt;

&lt;h4&gt;
  
  
  SceneManager
&lt;/h4&gt;

&lt;p&gt;This component holds the state regarding the &lt;code&gt;Scene&lt;/code&gt; currently being displayed. It also exposes a method &lt;code&gt;setScene&lt;/code&gt; through a &lt;code&gt;Context&lt;/code&gt; in order to update the current scene.&lt;/p&gt;

&lt;h4&gt;
  
  
  Scene
&lt;/h4&gt;

&lt;p&gt;This component, besides displaying its children &lt;code&gt;GameObject&lt;/code&gt;, will dispatch the events &lt;code&gt;scene-init&lt;/code&gt; and &lt;code&gt;scene-ready&lt;/code&gt; whenever the current scene changes.&lt;/p&gt;

&lt;p&gt;There is also a level system present in the file that is not being used by the demo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture - Bottom part
&lt;/h3&gt;

&lt;p&gt;Now we are going to look a little deeper, inside the code of the &lt;code&gt;OfficeScene&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;GameObject&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"map"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ambientLight&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TileMap&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mapData&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;resolveMapTile&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;definesMapSize&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;GameObject&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;GameObject&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Collider&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Interactable&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ScenePortal&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"exit"&lt;/span&gt; &lt;span class="na"&gt;enterDirection&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"other/start"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;GameObject&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Player&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;GameObject&lt;/code&gt; component we saw earlier is the most important piece of the architecture. It represents almost every element in the game world. For example for the &lt;code&gt;OfficeScene&lt;/code&gt; just above we have 3 &lt;code&gt;GameObject&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Map&lt;/li&gt;
&lt;li&gt;A Scene changer&lt;/li&gt;
&lt;li&gt;The Player&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;GameObject&lt;/code&gt; holds state information like &lt;code&gt;position&lt;/code&gt;, &lt;code&gt;enabled/disabled&lt;/code&gt; or its &lt;code&gt;layer&lt;/code&gt; in the game (ie: ground, obstacle, item, character ...). They can contain other &lt;code&gt;GameObject&lt;/code&gt; as well.&lt;br&gt;
&lt;code&gt;GameObject&lt;/code&gt; can also contain other components that Coldi called &lt;code&gt;Scripts&lt;/code&gt;. These scripts can hold the logic for interaction, collision or movement for example. Basically game objects are a composition of these reusable &lt;code&gt;Scripts&lt;/code&gt; and other &lt;code&gt;GameObject&lt;/code&gt;. This is a really powerful API because you can describe a game object behaviour component by just dropping components in it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Game Objects
&lt;/h2&gt;

&lt;p&gt;We're going to explore more the 3 &lt;code&gt;GameObject&lt;/code&gt; we saw earlier:&lt;/p&gt;
&lt;h3&gt;
  
  
  The map
&lt;/h3&gt;

&lt;p&gt;This component will create the map of the Scene based on an &lt;strong&gt;entities mapping string&lt;/strong&gt;. For example the Office mapping string looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# # # # # # # # # # # # # # # # #
# · W T # T · · W T · W · · · T #
# · · · · · · · · · · · · · · o ·
# o · · # · · · # # # # · · # # #
# # # # # · · · # W o W · · T W #
# C C C # · · · T · · · · · · · #
# o · · · · · · · · · · · · · o #
# # # # # # # # # # # # # # # # #
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the &lt;code&gt;OfficeScene&lt;/code&gt; there is a function called &lt;code&gt;resolveMapTile&lt;/code&gt; which will map each character to a game entity. Entities are &lt;code&gt;GameObject&lt;/code&gt; that match a real element in the game world.&lt;br&gt;
In this case we have the following &lt;strong&gt;entities mapping:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7ruti4fetdm7v63esq0w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7ruti4fetdm7v63esq0w.png" alt="entities" width="160" height="80"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;# : wall&lt;/li&gt;
&lt;li&gt;. : floor&lt;/li&gt;
&lt;li&gt;W : workstation&lt;/li&gt;
&lt;li&gt;C : coffee machine&lt;/li&gt;
&lt;li&gt;T : plant&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The child component &lt;code&gt;TileMap&lt;/code&gt; will then be responsible to return the map base on the &lt;strong&gt;entities mapping string&lt;/strong&gt; and the &lt;code&gt;resolveMapTile&lt;/code&gt; functions.&lt;/p&gt;

&lt;p&gt;The final map is a 2D grid, with each cell holding one or several &lt;code&gt;GameObject&lt;/code&gt; components.&lt;/p&gt;
&lt;h4&gt;
  
  
  Entities - workstation
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fko8mwzuugd95738wkbn2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fko8mwzuugd95738wkbn2.png" alt="workstation" width="94" height="86"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's take a closer look at what an entity looks like. We're going to look at the &lt;code&gt;Workstation&lt;/code&gt; one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Workstation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GameObjectProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;GameObject&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Sprite&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;spriteData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;objects&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"workstation-1"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Collider&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Interactable&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;WorkstationScript&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;GameObject&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see the &lt;code&gt;GameObject&lt;/code&gt; component we were talking about and some child components(&lt;code&gt;Sprite&lt;/code&gt;, &lt;code&gt;Collider&lt;/code&gt;, &lt;code&gt;Interactable&lt;/code&gt; and &lt;code&gt;WorkstationScript&lt;/code&gt;) that define its behaviour.&lt;/p&gt;

&lt;h5&gt;
  
  
  Sprite
&lt;/h5&gt;

&lt;p&gt;The Sprite component is responsible for displaying all graphical elements in the game.&lt;br&gt;
We didn't talk much about &lt;code&gt;react-three-fiber&lt;/code&gt; until now, but it is in this component that most of visual rendering happens.&lt;/p&gt;

&lt;p&gt;In ThreeJS elements are rendered through &lt;code&gt;mesh&lt;/code&gt; objects. A mesh is a combination of a geometry and material.&lt;/p&gt;

&lt;p&gt;In our case for the geometry we're using a simple Plane of 1x1 dimension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PlaneBufferGeometry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And for the material we're just applying the Threejs basic material:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meshBasicMaterial&lt;/span&gt; &lt;span class="na"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"material"&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;materialProps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;texture&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;textureRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"map"&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;textureProps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;meshBasicMaterial&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a plain basic material however we would be just seeing a simple square. Our sprites are actually displayed by giving the &lt;code&gt;&amp;lt;texture&amp;gt;&lt;/code&gt; object, which will apply sprites to the  &lt;code&gt;&amp;lt;meshBasicMaterial&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To sum up, the visual render of this demo is mostly 2D plane with texture applied to them and a camera looking at all of them from the top.&lt;/p&gt;

&lt;h5&gt;
  
  
  The collider
&lt;/h5&gt;

&lt;p&gt;This component is responsible for handling collisions. It has two jobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;store the walkable state (if it is possible to step on it or not) of the &lt;code&gt;GameObject&lt;/code&gt; using it. By default the &lt;code&gt;Collider&lt;/code&gt; is initialized as non walkable.&lt;/li&gt;
&lt;li&gt;listen and trigger events to do some logic whenever there is a collision.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The component also uses the hook &lt;code&gt;useComponentRegistry&lt;/code&gt; to register itself to its &lt;code&gt;GameObject&lt;/code&gt;. This allows other elements in the game (like the player) to know that this game object is an obstacle.&lt;/p&gt;

&lt;p&gt;For now we just have added an obstacle on the map, let's continue with the next component.&lt;/p&gt;

&lt;h5&gt;
  
  
  Interactable
&lt;/h5&gt;

&lt;p&gt;This component is responsible for handling logic when the player interacts with other elements in the game. An interaction occurs when the player has a collision with another &lt;code&gt;GameObject&lt;/code&gt; (this is why the &lt;code&gt;Collider&lt;/code&gt; from earlier was needed).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Interactable&lt;/code&gt; has severals methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;interact: executed by the &lt;code&gt;GameObject&lt;/code&gt; that &lt;strong&gt;initiates&lt;/strong&gt; an interaction&lt;/li&gt;
&lt;li&gt;onInteract: executed by the &lt;code&gt;GameObject&lt;/code&gt; that &lt;strong&gt;receives&lt;/strong&gt; an interaction&lt;/li&gt;
&lt;li&gt;canInteract: is it possible to interact with it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;Interactable&lt;/code&gt; component, as the &lt;code&gt;Collider&lt;/code&gt;, registers itself to its &lt;code&gt;GameObject&lt;/code&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  The WorkstationScript
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;WorkstationScript&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useGameObject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;workState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;useGameObjectEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;InteractionEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;interaction&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;workState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;workState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;workState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;getComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SpriteRef&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sprite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;workstation-2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;getComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SpriteRef&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sprite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;workstation-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;waitForMs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At last we have a script, specific to this entity, to handle some logic.&lt;br&gt;
We can see here that this script is listening to the &lt;code&gt;interaction&lt;/code&gt; event from earlier. Whenever this happens it just swaps the sprite of the computer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffv5pnpdqgitjtgod0jq9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffv5pnpdqgitjtgod0jq9.gif" alt="Workstation interaction" width="143" height="162"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Exercice
&lt;/h4&gt;

&lt;p&gt;We're going to add a monster entity, disguised as a plant. Inside the object sprite sheet asset, we can see that there are two plants that are not used in the demo.&lt;br&gt;
The goal will be to use them to create a new entity called ZombiePlant and place it inside the other Scene.&lt;/p&gt;

&lt;p&gt;When interacting with the entity, the plant should swap from one sprite to the other one.&lt;/p&gt;

&lt;p&gt;We will also have to change both the &lt;strong&gt;entities mapping string&lt;/strong&gt; and the &lt;code&gt;resolveMapTile&lt;/code&gt; function inside the &lt;code&gt;OtherScene&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjxmbcczapvupqrbqm2vi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjxmbcczapvupqrbqm2vi.png" alt="zombie plant" width="477" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Flow11/r3f-game-demo/commit/227c68d573179d0ae089e6718e0bf8f6a729c7a5" rel="noopener noreferrer"&gt;Solution&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The scene changer
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;GameObject&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Collider&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Interactable&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ScenePortal&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"exit"&lt;/span&gt; &lt;span class="na"&gt;enterDirection&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"other/start"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;GameObject&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now let's look at the components that handle the scene change.&lt;br&gt;
This component will be triggered when the player steps on it.&lt;/p&gt;

&lt;p&gt;To create this effect, the scene changer has 3 child components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Collider&lt;/li&gt;
&lt;li&gt;Interactable&lt;/li&gt;
&lt;li&gt;ScenePortal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We are already familiar with some elements like &lt;code&gt;Interactable&lt;/code&gt; and &lt;code&gt;Collider&lt;/code&gt;. This shows us how reusable &lt;code&gt;GameObject&lt;/code&gt; can be with this architecture. Let's look at the ScenePortal.&lt;/p&gt;
&lt;h4&gt;
  
  
  Scene Portal
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fb0rnzifmrymmaroe8cf2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fb0rnzifmrymmaroe8cf2.png" alt="Scene portal" width="400" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This component is responsible for doing the scene change when the player interacts with it.&lt;br&gt;
It has the following props:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;name&lt;/strong&gt;: name of the portal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;target&lt;/strong&gt;: destination where the player should be teleported (scene + portal). This parameter is a string with the following template: &lt;code&gt;sceneName/portalName&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;enterDirection&lt;/strong&gt;: direction that the player should face when entering the new scene;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The component listens to the &lt;code&gt;interaction&lt;/code&gt; event through the hook &lt;code&gt;useInteraction&lt;/code&gt;. When he receives an interaction, it will check if it comes from the player. In that case the &lt;code&gt;port&lt;/code&gt; function is called. It will change the current scene in the global game state. After that the component will wait for the &lt;code&gt;SceneInitEvent&lt;/code&gt; and &lt;code&gt;SceneReadyEvent&lt;/code&gt; to move the player in the right position and direction.&lt;/p&gt;
&lt;h4&gt;
  
  
  Workflow example
&lt;/h4&gt;

&lt;p&gt;Let's try to visualize the whole workflow of the ScenePortal:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flhjvvziaiqdpst3oib0k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flhjvvziaiqdpst3oib0k.png" alt="portal workflow" width="800" height="338"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The Player
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjgusychwds2xtn4l6hwd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjgusychwds2xtn4l6hwd.gif" alt="player" width="107" height="107"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We're now going to explore the biggest &lt;code&gt;GameObject&lt;/code&gt; of the game, the &lt;code&gt;Player&lt;/code&gt; one.&lt;br&gt;
The player &lt;code&gt;GameObject&lt;/code&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;GameObject&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"player"&lt;/span&gt; &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Player"&lt;/span&gt; &lt;span class="na"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"character"&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Moveable&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Interactable&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Collider&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CharacterScript&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Sprite&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;spriteData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;CharacterScript&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CameraFollowScript&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PlayerScript&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;GameObject&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are still familiar with &lt;code&gt;Interactable&lt;/code&gt; and &lt;code&gt;Collider&lt;/code&gt;. &lt;br&gt;
Let's see what the new components are doing.&lt;/p&gt;

&lt;h4&gt;
  
  
  Moveable
&lt;/h4&gt;

&lt;p&gt;This component just exposes an API, it does not listen to any events. It means that there will be another &lt;code&gt;GameObject&lt;/code&gt; that will call the Movable's API to move the &lt;code&gt;GameObject&lt;/code&gt; using it (in our case the Player).&lt;/p&gt;

&lt;p&gt;The most important method is the &lt;code&gt;move&lt;/code&gt; one. It takes a targetPosition as parameter, checks if this position is a collision and if not move the &lt;code&gt;GameObject&lt;/code&gt; to it.&lt;/p&gt;

&lt;p&gt;It also triggers a lot of events that can be used elsewhere. The events sequence look like that:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F50dda949oqeayytzfhrs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F50dda949oqeayytzfhrs.png" alt="move schema" width="800" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also the method &lt;code&gt;move&lt;/code&gt; uses the &lt;a href="https://animejs.com/" rel="noopener noreferrer"&gt;animejs&lt;/a&gt; library to animate the player sprite from one position to another.&lt;/p&gt;

&lt;h4&gt;
  
  
  CharacterScript
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;    &lt;span class="nf"&gt;useGameLoop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// apply wobbling animation&lt;/span&gt;
        &lt;span class="nf"&gt;wobble&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// apply breathe animation&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;movementActive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// breathe animation while standing still&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;breathIntensity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;scaleRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;breathIntensity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// no breathe animation while moving&lt;/span&gt;
            &lt;span class="nx"&gt;scaleRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This component is responsible for doing some animation to the Player Sprite. The script handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;flipping the sprite in the current moving direction (use the &lt;code&gt;attempt-move&lt;/code&gt; event we saw earlier)&lt;/li&gt;
&lt;li&gt;apply a &lt;code&gt;wobble&lt;/code&gt; effect while moving

&lt;ul&gt;
&lt;li&gt;this effect is applied inside the &lt;code&gt;useGameLoop&lt;/code&gt; hook. Under the hood this hook uses the &lt;code&gt;useFrame&lt;/code&gt; hook from react-three-fiber. This hook is really useful as it allows us to perform update on each frame&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;add a footstep sprite and sound while moving&lt;/li&gt;

&lt;li&gt;make the animation bounce while moving (use the &lt;code&gt;moving&lt;/code&gt; event we saw earlier)&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To sum up this component perform sprite animation by listening to movement events from the &lt;code&gt;Moveable&lt;/code&gt; component.&lt;/p&gt;

&lt;h4&gt;
  
  
  PlayerScript
&lt;/h4&gt;

&lt;p&gt;Final piece of the &lt;code&gt;Player&lt;/code&gt; entity, the &lt;code&gt;PlayerScript&lt;/code&gt;.&lt;br&gt;
This component handles the logic that the player can do. It will deal with both cursor and keyboard inputs.&lt;/p&gt;

&lt;h5&gt;
  
  
  Keyboard controls
&lt;/h5&gt;

&lt;p&gt;There are 4 hooks &lt;code&gt;useKeyPress&lt;/code&gt; that add the listener to the key given in parameter. These hooks return a boolean whenever the listed keys are pressed. These booleans are then checked inside a &lt;code&gt;useGameLoop&lt;/code&gt;, that we saw previously, and compute the next position consequently. The new position is set in the local state of &lt;code&gt;PlayerScript&lt;/code&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  Cursor controls
&lt;/h5&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgo8guv2ou4vtovg6p1jo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgo8guv2ou4vtovg6p1jo.gif" alt="astar" width="274" height="101"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This part is a bit more tricky. While the keyboard controls could move the player one tile by one tile, the cursor can move it to several tiles. It means that the whole path to the selected position must be computed before moving.&lt;/p&gt;

&lt;p&gt;In order to do that the method use a popular path finding algorithm named &lt;strong&gt;A star (or A*)&lt;/strong&gt;. This algorithm computes the shortest path between two points in a grid by taking collision into consideration.&lt;/p&gt;

&lt;p&gt;As for the keyboard events the new position is updated into the local &lt;code&gt;PlayerScript&lt;/code&gt; state. In addition the path is also displayed visually in this case. In the render method there is &lt;code&gt;PlayerPathOverlay&lt;/code&gt; component which is responsible for doing just that.&lt;/p&gt;

&lt;h5&gt;
  
  
  Moving to the new position
&lt;/h5&gt;

&lt;p&gt;In both cases we saw that the new position is updated in the local state of the component.&lt;br&gt;
There is a useEffect that listens to that change and that will try to move the &lt;code&gt;GameObject&lt;/code&gt;. Remember the &lt;code&gt;Moveable&lt;/code&gt; component from before ? Here we get it and call its &lt;code&gt;move&lt;/code&gt; method on him. If the move is not possible, the method returns &lt;code&gt;false&lt;/code&gt;. In that case we will try to interact with the &lt;code&gt;GameObject&lt;/code&gt; that is in the position that the player couldn't go to.&lt;/p&gt;

&lt;h4&gt;
  
  
  Exercice
&lt;/h4&gt;

&lt;p&gt;This was a big piece but now we should understand how game objects work together, let's try to make a new thing now.&lt;/p&gt;

&lt;p&gt;Remember our &lt;code&gt;ZombiePlant&lt;/code&gt; entity? We're going to add some new features to it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When the player interacts with it: should bounce back from the player (like if the player was attacking it)&lt;/li&gt;
&lt;li&gt;Whenever the interaction occurs: should play a sound effect (we can reuse the eating for example)&lt;/li&gt;
&lt;li&gt;On the third interaction the zombie plant should disappear&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1182gd78xyvjaov7l2x2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1182gd78xyvjaov7l2x2.gif" alt="zombie plant 2" width="422" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Flow11/r3f-game-demo/commit/e05d870c2d533bace1be2a1ec8600e88cd76e01e" rel="noopener noreferrer"&gt;Solution&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;This is it, we've gone through most of the demo !&lt;br&gt;
I hope you learned a lot of things in this demo walkthrough (I did). Thanks again to &lt;a href="https://twitter.com/coldi" rel="noopener noreferrer"&gt;@coldi&lt;/a&gt; for sharing this demo with the community.&lt;br&gt;
Also as he said a lot of things could have been implemented differently. For example the collision system could have been done with a physic engine like &lt;code&gt;react-use-cannon&lt;/code&gt;.&lt;br&gt;
This is still a terrific example of how to make games with &lt;code&gt;react-three-fiber&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Hopefully this gives you some ideas to make a game of your own !&lt;/p&gt;

&lt;p&gt;If you're interested in front-end, react-three-fiber or gamedev, I will publish more content about these topics &lt;a href="https://twitter.com/flagrede" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for reading, happy coding.&lt;/p&gt;

</description>
      <category>react</category>
      <category>gamedev</category>
      <category>r3f</category>
      <category>threejs</category>
    </item>
    <item>
      <title>How to replicate the Zelda BOTW interface with React, Tailwind and Framer-motion: Part 3</title>
      <dc:creator>Florent Lagrede</dc:creator>
      <pubDate>Sun, 19 Jul 2020 09:25:52 +0000</pubDate>
      <link>https://dev.to/flagrede/how-to-replicate-the-zelda-botw-interface-with-react-tailwind-and-framer-motion-part-3-3949</link>
      <guid>https://dev.to/flagrede/how-to-replicate-the-zelda-botw-interface-with-react-tailwind-and-framer-motion-part-3-3949</guid>
      <description>&lt;p&gt;For the final part of the Zelda series we're going to add a lot of little details, more animations and sounds !&lt;/p&gt;

&lt;h2&gt;
  
  
  Checkpoint
&lt;/h2&gt;

&lt;p&gt;After the &lt;a href="https://github.com/Flow11/zelda-botw-starter/tree/part2-checkpoint" rel="noopener noreferrer"&gt;second part&lt;/a&gt; our interface should look like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdm5u2mt6avd3gkx4xhj7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdm5u2mt6avd3gkx4xhj7.png" alt="Part 2 checkpoint" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Items actions
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Creating the modal
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk8hhu5frin746uqzxn0f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk8hhu5frin746uqzxn0f.png" alt="modal wireframe" width="591" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are going to start by creating a &lt;code&gt;Modal&lt;/code&gt; component in order to interact with the item selected. The modal will provide the &lt;code&gt;equip&lt;/code&gt; and &lt;code&gt;drop&lt;/code&gt; actions. For this step, we will display the modal when either a click or the Enter key is pressed on a selected item. The actions will be handled later on, we can close the modal instead when they are selected. We will also handle keyboard navigation inside the modal actions and create a &lt;code&gt;useClickOutside&lt;/code&gt; hook to close the modal when we click elsewhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We also must not forget to reset the focus when the modal is closed to keep the interface navigable with the keyboard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solutions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the first commit below, only the keyboard is handled to open the modal. The onClick handle is done in the second commit.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Flow11/zelda-botw-starter/commit/88019ab674bb039abd2f2b365a5e94e0956b41b8" rel="noopener noreferrer"&gt;Checkpoint commit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Flow11/zelda-botw-starter/commit/ddb347b8ac23ddd534e1572908ca9306c1a44408" rel="noopener noreferrer"&gt;Commit for missing onClick event&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Handling modal actions
&lt;/h2&gt;

&lt;p&gt;We are now going to handle the &lt;code&gt;equip&lt;/code&gt; and &lt;code&gt;drop&lt;/code&gt; actions inside the modal.&lt;br&gt;
For the equip feature we have some requirements. Link can have equip at the same time the following items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 Weapon&lt;/li&gt;
&lt;li&gt;1 Shield&lt;/li&gt;
&lt;li&gt;3 Armors:

&lt;ul&gt;
&lt;li&gt;1 Helm&lt;/li&gt;
&lt;li&gt;1 Armor&lt;/li&gt;
&lt;li&gt;1 Greaves&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpqu8danvl7590r0fxk9l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpqu8danvl7590r0fxk9l.png" alt="Item Equipped" width="88" height="86"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can also add some visual indicators to show that an item is equipped like a radial background in the item cell and a blue bar inside the &lt;code&gt;ItemInformation&lt;/code&gt; component. The blue color should be defined in our Tailwind config file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fd0ujx2wqeg8dggobqmz3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fd0ujx2wqeg8dggobqmz3.png" alt="Item information equipped" width="528" height="208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the data structure of our new state we can use for instance an object with the &lt;code&gt;item.category&lt;/code&gt; string as key and the item data as value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;ItemType&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The drop feature should be simpler, we just want to remove the item selected and add an empty item at the end instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solutions&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Flow11/zelda-botw-starter/commit/b16257cba3a2895cf08d14b965970c9687b3aecb" rel="noopener noreferrer"&gt;Checkpoint commit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Handling items with special bonus
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0q9w0yoplua1vodhvqs1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0q9w0yoplua1vodhvqs1.png" alt="bonus wireframe" width="612" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are now going to handle the bonus for armor's items. To do so we have to:&lt;br&gt;
1) Display the bonus icon of an item inside its cell&lt;br&gt;
2) Compute all the active bonus based on what items are equipped&lt;br&gt;
3) Display the active bonus, with a new component, inside the right column&lt;/p&gt;

&lt;p&gt;For the first step we need to create a &lt;code&gt;BonusIcon&lt;/code&gt; component which takes for example a &lt;code&gt;bonusType&lt;/code&gt; as props and returns the appropriate bonus icon (fire, swimming or climbing). Then we can use this component inside the &lt;code&gt;Item&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;For the second step we need to create a state to store the active bonus. These bonuses will be updated whenever the items equipped change. We can use a &lt;code&gt;useEffect&lt;/code&gt; hook for that. For the data structure of our new bonus state it could look like that (if you are using TS, otherwise define key as usual):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ItemsBonusType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FIRE&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ItemsBonusType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SWIMMING&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ItemsBonusType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLIMBING&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the final step we are going to create two new components &lt;code&gt;BonusBox&lt;/code&gt; and &lt;code&gt;BonusList&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;BonusBox&lt;/code&gt; will display a &lt;code&gt;BonusIcon&lt;/code&gt; and little bars, up to a max of 3, to show how many items which have this bonus type are equipped.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BonusList&lt;/code&gt; will receive the data we've created on step 2 and display a bonus for each of the bonus types.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solutions&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Flow11/zelda-botw-starter/commit/f9d6a30ed0c6901182938066ea6833c47872e77c" rel="noopener noreferrer"&gt;Checkpoint commit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Adding little details
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Sounds
&lt;/h2&gt;

&lt;p&gt;To add sounds to our interface, we are going to use the handy &lt;a href="https://github.com/joshwcomeau/use-sound" rel="noopener noreferrer"&gt;use-sound&lt;/a&gt; hook from &lt;a href="http://joshwcomeau.com/" rel="noopener noreferrer"&gt;Joshua Comeau&lt;/a&gt;.&lt;br&gt;
The sounds has been added in the &lt;a href="https://github.com/Flow11/zelda-botw-starter/commit/b4b49c34624ec87376dbac2172bdbaa7c510812f" rel="noopener noreferrer"&gt;repository&lt;/a&gt;. There is one for selection (arrow keys, click) and another one for actions (equip, drop). The sounds should be playable from anywhere inside the app so we should create a new context for that. We might need to create a wrapper for our main &lt;code&gt;App&lt;/code&gt; component for the new sound context.&lt;br&gt;
Then we should use the sounds wherever it feels necessary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solutions&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Flow11/zelda-botw-starter/commit/ddb347b8ac23ddd534e1572908ca9306c1a44408" rel="noopener noreferrer"&gt;Checkpoint commit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Endurance gauge
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnxwb0kd1ye2zvl2rxzda.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnxwb0kd1ye2zvl2rxzda.png" alt="Endurance gauge" width="215" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is going to be a small step but details matter. We are going to display an endurance gauge with css only.&lt;br&gt;
We are going to need two &lt;code&gt;divs&lt;/code&gt;, one for the outer circle and one for the inner circle. To create the circle effect we just just have to apply a &lt;code&gt;border-radius: 50%&lt;/code&gt; to them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It might be easier to create a custom css file for this rather than add all missing values to Tailwind, since we're not going to need them elsewhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solutions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Flow11/zelda-botw-starter/commit/b9ac0472ba2c0f3b5d5ceea54726ad6f43114938" rel="noopener noreferrer"&gt;Checkpoint commit&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Full durability star
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuqzhbunb646n6caf43kj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuqzhbunb646n6caf43kj.png" alt="Durability star" width="94" height="92"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the final touch, we are going to add a sparkle animation for items with full durability (items with the &lt;code&gt;isNew&lt;/code&gt; property in the dataset).&lt;br&gt;
Like we've done before, we need to convert the sparkle SVG from the asset folder with &lt;a href="https://react-svgr.com/playground/?typescript=true" rel="noopener noreferrer"&gt;SVGR&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After that, we need to create an animation, still with &lt;a href="https://www.framer.com/api/motion/animation/" rel="noopener noreferrer"&gt;framer-motion&lt;/a&gt;, to rotate and scale the sparkle at the same time and infinitely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ressources&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Joshua Comeau has written a &lt;a href="https://joshwcomeau.com/react/animated-sparkles-in-react/" rel="noopener noreferrer"&gt;great article&lt;/a&gt; on the subject.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solutions&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Flow11/zelda-botw-starter/commit/ed4beedeca5f1295814ca898f4a4234301d00d1f" rel="noopener noreferrer"&gt;Checkpoint commit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Wrapping up
&lt;/h1&gt;

&lt;p&gt;Wow, we're finally done with the ZeldaUI, congrats on completing it!&lt;br&gt;
I hope that you learned new things and that you had fun at the same time.&lt;br&gt;
If you have any ideas or feedback, let me know in the comments. I will gladly try to address them.&lt;/p&gt;

&lt;p&gt;See you then!&lt;/p&gt;

</description>
      <category>react</category>
      <category>tailwind</category>
      <category>framermotion</category>
    </item>
    <item>
      <title>How to replicate the Zelda BOTW interface with React, Tailwind and Framer-motion: Part 2</title>
      <dc:creator>Florent Lagrede</dc:creator>
      <pubDate>Sun, 05 Jul 2020 07:54:20 +0000</pubDate>
      <link>https://dev.to/flagrede/how-to-replicate-the-zelda-botw-interface-with-react-tailwind-and-framer-motion-part-2-3nd4</link>
      <guid>https://dev.to/flagrede/how-to-replicate-the-zelda-botw-interface-with-react-tailwind-and-framer-motion-part-2-3nd4</guid>
      <description>&lt;h1&gt;
  
  
  Introduction and notes on first part
&lt;/h1&gt;

&lt;p&gt;Welcome back for the second part of the &lt;strong&gt;Zelda series&lt;/strong&gt;.&lt;br&gt;
I hope that you manage to complete the first part without much trouble. If not or if you've missed the first part I created a &lt;a href="https://github.com/Flow11/zelda-botw-starter/tree/part1-checkpoint" rel="noopener noreferrer"&gt;checkpoint&lt;/a&gt; branch with all the steps completed.&lt;br&gt;
Also there were some errors in the solutions provided in part 1 that should all be fixed now.&lt;/p&gt;

&lt;p&gt;After the first part, our interface should looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvhx147hl0hgd19zogfke.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvhx147hl0hgd19zogfke.png" alt="Checkpoint part1" width="559" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this second part we'll focus first on the &lt;strong&gt;right column&lt;/strong&gt; of our layout and then we're going to handle &lt;strong&gt;items pagination&lt;/strong&gt; and ways to navigate between them.&lt;/p&gt;

&lt;p&gt;For providing solutions I decided to stop using &lt;em&gt;gist files&lt;/em&gt;, they were hard to maintain and even for reading it was not convenient either to jump between files for the same solution.&lt;br&gt;
Instead I created a checkpoint commit at each step of the second part, it should make reading easier hopefully.&lt;/p&gt;
&lt;h1&gt;
  
  
  Displaying additional items information
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Right column layout
&lt;/h2&gt;

&lt;p&gt;We're going to start by creating the base blocks of the right column.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbsqw7o3zxj32gvqhkcll.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbsqw7o3zxj32gvqhkcll.png" alt="Layout" width="800" height="1033"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We need an &lt;strong&gt;ItemInformation&lt;/strong&gt; component that should be displayed at the bottom of the column. For now the component should be an empty box, but its width should match the &lt;strong&gt;ItemGrid&lt;/strong&gt; width when we are in column display.&lt;/p&gt;

&lt;p&gt;Once the &lt;strong&gt;ItemInformation&lt;/strong&gt; component is set, we'll need to position the &lt;em&gt;Link&lt;/em&gt; image below and hide it for breakpoints below XL. We can use an &lt;em&gt;absolute&lt;/em&gt; here for the positioning.&lt;/p&gt;
&lt;h3&gt;
  
  
  Notes
&lt;/h3&gt;

&lt;p&gt;The color for the &lt;strong&gt;ItemInformation&lt;/strong&gt; background is missing  in the starter tailwind configuration file. We can add it now:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bgBlackTransparent: "rgba(0, 0, 0, 0.5)",&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Flow11/zelda-botw-starter/commit/b99de506b9572e66570f0bfe04f1c3f9cdef7963" rel="noopener noreferrer"&gt;Checkpoint commit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Items information
&lt;/h2&gt;

&lt;p&gt;Now we're going to display more Item information. This step is mostly passing down props and implementing the design of the ItemInformation component.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3334aa50qa0kadec7q51.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3334aa50qa0kadec7q51.png" alt="Item information" width="648" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We want to display the &lt;em&gt;name&lt;/em&gt;, &lt;em&gt;value&lt;/em&gt;, &lt;em&gt;category&lt;/em&gt; and &lt;em&gt;description&lt;/em&gt; of the item. The category will be displayed as an Icon. In the first part we've displayed only one category among the 3 available in the dataset but we'll handle all 3 cases here. We need to create a &lt;strong&gt;CategoryIcon&lt;/strong&gt; component which displays the appropriate icon based on a &lt;em&gt;type&lt;/em&gt; props for example.&lt;/p&gt;

&lt;p&gt;But first we have to convert our SVGs (&lt;em&gt;Armor&lt;/em&gt;, &lt;em&gt;Shield&lt;/em&gt;, &lt;em&gt;Sword&lt;/em&gt;) inside &lt;em&gt;src/assets/items&lt;/em&gt; into React components. We will use &lt;a href="https://react-svgr.com/playground/" rel="noopener noreferrer"&gt;SVGR&lt;/a&gt; for that.&lt;/p&gt;

&lt;p&gt;On this step we will also add the calamity font since this is the first time we will display text. It is available in the font folder under &lt;em&gt;assets&lt;/em&gt;. We have to move it to &lt;em&gt;create-react-app&lt;/em&gt; public folder and add &lt;em&gt;font-face&lt;/em&gt; declarations in a separate &lt;em&gt;css&lt;/em&gt; file.&lt;/p&gt;
&lt;h3&gt;
  
  
  Notes
&lt;/h3&gt;

&lt;p&gt;We can reuse the &lt;em&gt;Armor&lt;/em&gt; icon for &lt;em&gt;helm&lt;/em&gt; and &lt;em&gt;greave&lt;/em&gt; category.&lt;/p&gt;
&lt;h3&gt;
  
  
  Ressources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/fr/docs/Web/CSS/@font-face" rel="noopener noreferrer"&gt;https://developer.mozilla.org/fr/docs/Web/CSS/@font-face&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Flow11/zelda-botw-starter/commit/39a8eacbe109368fcbb159c4c00aa247290fbf18" rel="noopener noreferrer"&gt;Checkpoint commit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Adding a component to animate text
&lt;/h2&gt;

&lt;p&gt;Now we are going to animate the item description by adding a typewriter effect animation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqexun5t910tzjtred1z7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqexun5t910tzjtred1z7.png" alt="Windups" width="487" height="161"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To achieve this effect we are going to use the &lt;a href="https://windups.gwil.co/" rel="noopener noreferrer"&gt;windups&lt;/a&gt; library from &lt;a href="https://twitter.com/sgwil" rel="noopener noreferrer"&gt;@sgwil&lt;/a&gt;. The library is really well written, has a nice API and great documentation. We can even trigger effects, like sounds, during the animation. For this demo we are just going to use the base functionality of &lt;em&gt;windups&lt;/em&gt; with the &lt;em&gt;useWindupString&lt;/em&gt; hook provided for React.&lt;br&gt;
This hook renders a lot of time so we should avoid using it at a high level in our app structure otherwise it would trigger a lot of unneeded render on children components and seriously lower the performance of our interface.&lt;br&gt;
Instead we will create a &lt;strong&gt;TypeWriter&lt;/strong&gt; component that we will use inside &lt;strong&gt;ItemInformation&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Notes
&lt;/h3&gt;

&lt;p&gt;Have a look at the &lt;a href="https://windups.gwil.co/quick" rel="noopener noreferrer"&gt;interactive guides&lt;/a&gt; to get a feeling of what you can do with &lt;em&gt;windups&lt;/em&gt; or even the &lt;a href="https://github.com/sgwilym/windups" rel="noopener noreferrer"&gt;sources&lt;/a&gt; if you have some time they are really interesting.&lt;/p&gt;
&lt;h3&gt;
  
  
  Ressources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://windups.gwil.co/api#use-windup-string" rel="noopener noreferrer"&gt;useWindupString doc&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Flow11/zelda-botw-starter/commit/c3bc905c270f3cde92035661084f5cf0bc804969" rel="noopener noreferrer"&gt;Checkpoint commit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  Adding pagination
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Handling pagination logic
&lt;/h2&gt;

&lt;p&gt;In the first part we create a &lt;em&gt;getItems&lt;/em&gt; function to retrieve one category of items and fill the rest of the grid cells with an empty item object.&lt;/p&gt;

&lt;p&gt;Now we have to tweak this function a bit to return all the categories of items by page.&lt;/p&gt;

&lt;p&gt;The function should have the following &lt;strong&gt;ItemsPage&lt;/strong&gt; return type&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;ItemsMainCategoriesType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;WEAPONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;weapons&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;SHIELDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shields&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ARMORS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;armors&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ItemsPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ItemType&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;mainCategory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ItemsMainCategoriesType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that is done we need to create two states using &lt;em&gt;useState&lt;/em&gt; react hook inside our &lt;strong&gt;App&lt;/strong&gt; component:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One that should be initialized with the result of our &lt;em&gt;getItems&lt;/em&gt; function&lt;/li&gt;
&lt;li&gt;Another one to store the current page, that we initialize to 0 for now&lt;/li&gt;
&lt;li&gt;Then we need to create our new &lt;em&gt;items&lt;/em&gt; variable based on the active page&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the end of this step we won't be able to navigate between pages yet but we'll have the base logic ready.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Flow11/zelda-botw-starter/commit/f0b60bfa47e99b365518a0479ef4b691433174bf" rel="noopener noreferrer"&gt;Checkpoint commit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating a component for page navigation
&lt;/h2&gt;

&lt;p&gt;For the next part we're going to create the component to move between the groups of items, we can call it &lt;strong&gt;CategoriesMenu&lt;/strong&gt;. We'll also need a component for each item inside that menu, we can call it &lt;strong&gt;CategoriesMenuItem&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Faddzdxwrssxybbgtwhbm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Faddzdxwrssxybbgtwhbm.png" alt="Categories menu" width="629" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;CategoriesMenuItem&lt;/strong&gt; will have to display one of the 3 SVG react components (Weapon, Shield, Armor) and call the &lt;em&gt;setPage&lt;/em&gt; from earlier on click. In the image, the first icon is selected and has different style applied.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Flow11/zelda-botw-starter/commit/054043d51181f0f45ac59f65be21d4100920aabd" rel="noopener noreferrer"&gt;Checkpoint commit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Adding animated arrow to navigate
&lt;/h2&gt;

&lt;p&gt;We are now going to provide a second way to navigate between the pages with arrows at the edge of the items grid component.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9fla22n6g0io1r5jqr4d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9fla22n6g0io1r5jqr4d.png" alt="Navigation arrows" width="739" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This time instead of calling &lt;em&gt;setPage&lt;/em&gt; directly, we'll create a &lt;em&gt;navigateToDirection&lt;/em&gt; function which should either take &lt;em&gt;1&lt;/em&gt; or &lt;em&gt;-1&lt;/em&gt; to represent the direction and then call &lt;em&gt;setPage&lt;/em&gt; with the appropriate value.&lt;/p&gt;

&lt;p&gt;These arrows should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;call &lt;em&gt;navigateToDirection&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;be animated on the &lt;em&gt;scale&lt;/em&gt; property to create a zoom in/out effect with framer motion&lt;/li&gt;
&lt;li&gt;be disabled if reaching either max/min page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll also have to update our &lt;em&gt;useState&lt;/em&gt; inside &lt;strong&gt;App&lt;/strong&gt; to store both the page and the direction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Notes
&lt;/h3&gt;

&lt;p&gt;The arrow image is available under the asset folder under the name &lt;em&gt;arrow-no-curve.png&lt;/em&gt;. We may have to move it to the public folder to use a static url directly.&lt;/p&gt;

&lt;p&gt;We only have one arrow as an image, so we need to rotate the image to create the left arrow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Flow11/zelda-botw-starter/commit/c0a148ce30be26b0ab2c545cb6fe437d262152d9" rel="noopener noreferrer"&gt;Checkpoint commit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Handling keyboard to navigate between page
&lt;/h2&gt;

&lt;p&gt;We are now going to provide a third way to navigate between the pages ! &lt;strong&gt;Triforce requirement&lt;/strong&gt;, we have no choice. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fp952m4w4j02diytiaykv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fp952m4w4j02diytiaykv.png" alt="Keyboard navigation" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this step we have to change the &lt;em&gt;handleKeyPressed&lt;/em&gt; and &lt;em&gt;goLeft&lt;/em&gt; / &lt;em&gt;goRight&lt;/em&gt; functions we wrote in part 1.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;handleKeyPressed&lt;/em&gt;: we need to call &lt;em&gt;navigateToDirection&lt;/em&gt; inside &lt;strong&gt;ArrowLeft&lt;/strong&gt; and &lt;strong&gt;ArrowRight&lt;/strong&gt; event when we are at the edges of the &lt;strong&gt;ItemsGrid&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;goLeft&lt;/em&gt; / &lt;em&gt;goRight&lt;/em&gt;: we loop over horizontal cells when we reach the edges of the ItemGrid.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Notes
&lt;/h3&gt;

&lt;p&gt;We can optionally refactor the &lt;em&gt;if/else&lt;/em&gt; inside the &lt;em&gt;handleKeyPressed&lt;/em&gt; function by a &lt;em&gt;switch block&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Flow11/zelda-botw-starter/commit/d55e06c03f86477e431c84dc644b53442ad349b4" rel="noopener noreferrer"&gt;Checkpoint commit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Adding animation on page change
&lt;/h2&gt;

&lt;p&gt;For the last part we are going to add an animation to the page transition.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fv4u30c7jc5bx8tmgimim.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fv4u30c7jc5bx8tmgimim.png" alt="Page animation" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The animation will be added to the &lt;strong&gt;ItemsGrid&lt;/strong&gt; component. We want to slide the ItemsGrid from the right or from the left depending on the &lt;em&gt;direction&lt;/em&gt; that we stored in our &lt;em&gt;useState&lt;/em&gt;.&lt;br&gt;
To create this animation we are going to use the &lt;em&gt;variant&lt;/em&gt; api from &lt;em&gt;framer-motion&lt;/em&gt;, it allows us to define the different states of our animation in an object. In this object we will have two states:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;enter&lt;/em&gt;: this is the starting position, here we will slide the ItemsGrid to either &lt;em&gt;-100&lt;/em&gt; or &lt;em&gt;100&lt;/em&gt; on the &lt;em&gt;x&lt;/em&gt; axis and apply an opacity to 0.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;center&lt;/em&gt;: here we will move the &lt;strong&gt;ItemsGrid&lt;/strong&gt; back to the center and reveal the &lt;strong&gt;ItemsGrid&lt;/strong&gt; at the same time with &lt;em&gt;opacity&lt;/em&gt; to 1.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To sum up we are going to give our &lt;em&gt;motion.div&lt;/em&gt; a &lt;em&gt;variant&lt;/em&gt; object with two entries &lt;em&gt;enter&lt;/em&gt; and &lt;em&gt;center&lt;/em&gt;. Each entry will define values for &lt;em&gt;x&lt;/em&gt; and &lt;em&gt;opacity&lt;/em&gt; properties. It should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;variants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;enter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll also need to define the transition we want for these properties, we can use the values below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;      &lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
        &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tween&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the end our &lt;em&gt;motion.div&lt;/em&gt; component inside &lt;strong&gt;ItemsGrid&lt;/strong&gt; should have the following props:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;motion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
    &lt;span class="na"&gt;custom&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;direction&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;variants&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"enter"&lt;/span&gt;
    &lt;span class="na"&gt;animate&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"center"&lt;/span&gt;
    &lt;span class="na"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{...}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since there is no direction involved when we change pages using the &lt;strong&gt;CategoriesMenu&lt;/strong&gt; we should probably just animate the opacity in this case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ressources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.framer.com/api/motion/animation/#variants" rel="noopener noreferrer"&gt;Variant API&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Flow11/zelda-botw-starter/commit/993426d6a11b54c32d18e99c46c4e1daf1b53173" rel="noopener noreferrer"&gt;Checkpoint commit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  What's next
&lt;/h1&gt;

&lt;p&gt;Congratulations for completing part 2 we've made a lot of progress on completing our demo.&lt;br&gt;
In part 3 we will add the final touch to bring this interface to life !&lt;br&gt;
Again feel free to share any feedback and I hope you had fun.&lt;/p&gt;

</description>
      <category>react</category>
      <category>tailwind</category>
      <category>framermotion</category>
    </item>
    <item>
      <title>How to replicate the Zelda BOTW interface with React, Tailwind and Framer-motion: Part 1</title>
      <dc:creator>Florent Lagrede</dc:creator>
      <pubDate>Sat, 20 Jun 2020 14:22:40 +0000</pubDate>
      <link>https://dev.to/flagrede/how-to-replicate-the-zelda-botw-interface-with-react-tailwind-and-framer-motion-part-1-298g</link>
      <guid>https://dev.to/flagrede/how-to-replicate-the-zelda-botw-interface-with-react-tailwind-and-framer-motion-part-1-298g</guid>
      <description>&lt;p&gt;In this article series we'll learn how to replicate the menu inventory interface of Zelda: Breath Of The Wild on the web !&lt;/p&gt;

&lt;p&gt;Click on the image below to check the final demo: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://60ab6a8f04ae260008b8c395--youthful-swirles-08caa6.netlify.app/zelda-botw/inventory" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F56q5bsao1c8ix58ahmlg.png" alt=" " width="800" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Articles structure
&lt;/h2&gt;

&lt;p&gt;This will be a series of 3 articles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PART1: Setup the Item Grid + Adding navigation + Selection Animation&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;PART2: Adding pagination + page transition animation + display additional items data&lt;/li&gt;
&lt;li&gt;PART3: Handling actions with a modal + Handling items with bonus + new item animation + add sound&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Target audience:
&lt;/h2&gt;

&lt;p&gt;Intermediate Front-end developer.&lt;br&gt;
If you are more experienced you might not learn much (at least for part 1) but it might still be an interesting exercise to do.&lt;/p&gt;
&lt;h1&gt;
  
  
  Why doing that ?
&lt;/h1&gt;

&lt;p&gt;I find GameUI very inspiring, they share different problems than web interfaces and also similar ones. They are usually designed to not get in the way of a player's experience of a game (Zelda BOTW is a perfect example of that). Web is not much different in that regard. When we design a website we're actually building an experience, the interface is just the way to interact with the experience.&lt;br&gt;
Also GameUI are often filled with a lot of little details, these details are not always meant to be noticed at first glance yet they amount for a significant part of an enjoyable interface.&lt;/p&gt;
&lt;h1&gt;
  
  
  About this interface
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9pu88lnaiourdgdz9riw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9pu88lnaiourdgdz9riw.jpg" alt="Zelda inventory interface" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;screenshot from &lt;a href=""&gt;interfaceingame.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The interface is basically a two &lt;strong&gt;layout&lt;/strong&gt; column.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first column contains a grid of items grouped by categories.&lt;/li&gt;
&lt;li&gt;The second column is used to display information about the item selected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are a lot of &lt;strong&gt;animations&lt;/strong&gt; on the interface that all serve a purpose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;arrows moving in and out the edge of the selected items&lt;/li&gt;
&lt;li&gt;arrows at the horizontal edge of the grid to indicate that you can navigate between categories&lt;/li&gt;
&lt;li&gt;when you change the selected item, the item description is typed progressively to catch your eye on the fact that these data have changed&lt;/li&gt;
&lt;li&gt;items that are new (max durability) have a little shiny star. This animation is really subtle compare to the other, because it concerns a detail about the item&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first 3 animations, which are the most visible, act as  &lt;strong&gt;signifiers&lt;/strong&gt; (This a UX term that comes from Donald Norman, the author of &lt;strong&gt;The design of everyday thing&lt;/strong&gt;), and helps the player understand the actions he can do on the interface.&lt;/p&gt;
&lt;h1&gt;
  
  
  About the format
&lt;/h1&gt;

&lt;p&gt;At first I thought of just providing some code samples with explanations but I don't think it would have provided much value.&lt;br&gt;
You might have come to realize that we, as developper, learn best by actively doing something. That's why this series will actively encourage you to code your own version of the Zelda BOTW interface.&lt;br&gt;
I'll give you a goal to reach at each step and provide leads for realizing it.&lt;br&gt;
If you're stuck or if you just want to keep reading, the solutions will be provided at each step in separate gists. &lt;br&gt;
I have  setup a based repository with all the ressources to get you started immediately: &lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/flagrede" rel="noopener noreferrer"&gt;
        flagrede
      &lt;/a&gt; / &lt;a href="https://github.com/flagrede/zelda-botw-starter" rel="noopener noreferrer"&gt;
        zelda-botw-starter
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Template for reproducing the Zelda BOTW inventory interface
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Zelda BOTW starter&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/530644/84588444-c3ca6400-ae27-11ea-827d-444ea4f9f4f2.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F530644%2F84588444-c3ca6400-ae27-11ea-827d-444ea4f9f4f2.png" alt="Zelda BOTW demo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Complete demo&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://www.gameuionweb.com/zelda-botw" rel="nofollow noopener noreferrer"&gt;https://www.gameuionweb.com/zelda-botw&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Articles links:&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/flagrede/how-to-replicate-the-zelda-botw-interface-with-react-tailwind-and-framer-motion-part-1-298g" rel="nofollow"&gt;Part 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/flagrede/how-to-replicate-the-zelda-botw-interface-with-react-tailwind-and-framer-motion-part-2-3nd4" rel="nofollow"&gt;Part 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/flagrede/how-to-replicate-the-zelda-botw-interface-with-react-tailwind-and-framer-motion-part-3-3949" rel="nofollow"&gt;Part 3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Stack&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;Tailwind&lt;/li&gt;
&lt;li&gt;Framer-motion (beta)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Useful links for Tailwind&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nerdcave.com/tailwind-cheat-sheet" rel="nofollow noopener noreferrer"&gt;Cheatsheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss" rel="nofollow noopener noreferrer"&gt;VS Auto completion plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://plugins.jetbrains.com/plugin/12074-tailwindcss" rel="nofollow noopener noreferrer"&gt;Intellij Auto completion plugin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Credits&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Items Data: &lt;a href="https://zelda.gamepedia.com/Main_Page" rel="nofollow noopener noreferrer"&gt;Zelda Gamepedia&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Font: &lt;a href="https://www.reddit.com/user/75thTrombone/" rel="nofollow noopener noreferrer"&gt;Reddit user /75thTrombone&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/flagrede/zelda-botw-starter" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;The repository is set up with Typescript but no obligation here, you can use either ts or js as you prefer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prior requirements and notes about technologies used
&lt;/h2&gt;

&lt;p&gt;This articles series is based on the following stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ReactJS and leverage some libraries of its ecosystem

&lt;ul&gt;
&lt;li&gt;Some prior knowledge is expected but nothing advanced &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;TailwindCSS

&lt;ul&gt;
&lt;li&gt;No prior knowledge expected, we'll introduce it briefly if you are not familiar with it.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Framer-motion:

&lt;ul&gt;
&lt;li&gt;This is the main library used for animation, no prior knowledge expected&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tailwind quick introduction
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;You can skip this part if you're already familiar with Tailwind&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1lln79fv8kemgaxn8gl2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1lln79fv8kemgaxn8gl2.png" alt="Tailwind logo" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tailwind is a CSS utility framework, it provides a set of css classes with low responsability like  &lt;code&gt;w-full&lt;/code&gt; for &lt;code&gt;width: 100%&lt;/code&gt; or &lt;code&gt;flex&lt;/code&gt; for &lt;code&gt;display: flex&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;These classes are generated based on a JS configuration file. In this file you define design tokens like color, spacing, ... and then tailwind will generate all the css classes based on the design constraints you defined.&lt;/p&gt;

&lt;p&gt;You can find the structure of this file here: &lt;a href="https://github.com/tailwindcss/tailwindcss/blob/master/stubs/defaultConfig.stub.js" rel="noopener noreferrer"&gt;Default configuration&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I choose Tailwind for the article series because it allows me to prepare the design configuration upfront so you can focus on the most interesting part.&lt;br&gt;
Also I will be able to put tailwind classes needed directly on schemas provided in the article to make implementation even easier.&lt;/p&gt;

&lt;p&gt;If this is your first time with tailwind you could use this cheat sheet to help you find class at the beginning: &lt;a href="https://nerdcave.com/tailwind-cheat-sheet" rel="noopener noreferrer"&gt;https://nerdcave.com/tailwind-cheat-sheet&lt;/a&gt;&lt;br&gt;
I also strongly recommend installing a plugin to get autocompletion on tailwind classes (see starter readme for the links).&lt;/p&gt;
&lt;h1&gt;
  
  
  Starting with the layout
&lt;/h1&gt;

&lt;p&gt;We're going to start by creating the base layout of our interface.&lt;br&gt;
What we want here is a responsive layout with the following constraints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extra large screen (XL): a two columns layout column, with each column taking 1/2 of the screen.&lt;/li&gt;
&lt;li&gt;below extra large screen (&amp;lt;XL): there should only be one column taking full width.&lt;/li&gt;
&lt;li&gt;The layout should be contained inside a centered responsive container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once your layout is done you can add the &lt;code&gt;bg-zelda-darkGreen&lt;/code&gt; class to your main div and we're good for that part.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9qkfvi2yjanzurczy8hd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9qkfvi2yjanzurczy8hd.png" alt="Layout" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ressources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tailwindcss.com/docs/responsive-design/#app" rel="noopener noreferrer"&gt;https://tailwindcss.com/docs/responsive-design/#app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailwindcss.com/docs/container/#app" rel="noopener noreferrer"&gt;https://tailwindcss.com/docs/container/#app&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/flagrede/e440ac5c56f21ef9b9db37d1c780ad20" rel="noopener noreferrer"&gt;https://gist.github.com/flagrede/e440ac5c56f21ef9b9db37d1c780ad20&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Adding the items grid
&lt;/h1&gt;

&lt;p&gt;We'll now create our &lt;strong&gt;ItemsGrid&lt;/strong&gt; and &lt;strong&gt;Item&lt;/strong&gt; components.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffu14ijns4ddr0mkmjdo6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffu14ijns4ddr0mkmjdo6.png" alt="Alt Text" width="518" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We want to create a 5 columns Grid, with css-grid, which reduces to 3 columns on mobile, that displays an Item component in each cell.&lt;br&gt;
To create the Item component we will need to take a look at the data available in &lt;code&gt;src/data/items.js&lt;/code&gt;. An item data looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tree Branch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;weapon&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/zelda-botw/items/weapons/BotW_Tree_Branch_Icon.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;isNew&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Wooden branches such as this are pretty common, but it's surprisingly well-balanced. It doesn't do much damage but can serve as a weapon in a pinch.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For now our component will just have to display its icon and value and also his name for the image title.&lt;br&gt;
Our item should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1lv2ezoui0ezoqnobhqn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1lv2ezoui0ezoqnobhqn.png" alt="Alt Text" width="91" height="94"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are 3 main categories of items in the dataset &lt;code&gt;weapons&lt;/code&gt;, &lt;code&gt;shields&lt;/code&gt; and &lt;code&gt;armors&lt;/code&gt;. We need to display only one category for now so we can start by taking the &lt;code&gt;weapons&lt;/code&gt; items for example. We can create a &lt;code&gt;getItems&lt;/code&gt; function to retrieve that group and fill the rest of the array, with an empty item object, to reach the number of cells wanted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ressources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tailwindcss.com/docs/grid-template-columns/" rel="noopener noreferrer"&gt;https://tailwindcss.com/docs/grid-template-columns/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Item: &lt;a href="https://gist.github.com/flagrede/dc21859cc3020454cd1dc3c6f5afabdb" rel="noopener noreferrer"&gt;https://gist.github.com/flagrede/dc21859cc3020454cd1dc3c6f5afabdb&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ItemsGrid: &lt;a href="https://gist.github.com/flagrede/f752e3e2de8cbb6a7edf1609dc42f9b7" rel="noopener noreferrer"&gt;https://gist.github.com/flagrede/f752e3e2de8cbb6a7edf1609dc42f9b7&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;getItems: &lt;a href="https://gist.github.com/flagrede/4471044563611d572707c96546d64635" rel="noopener noreferrer"&gt;https://gist.github.com/flagrede/4471044563611d572707c96546d64635&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  Items selections
&lt;/h1&gt;

&lt;p&gt;Now we want to be able to select an item by clicking on it by applying the following classes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0h43eezxfz3h2bs6dczr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0h43eezxfz3h2bs6dczr.png" alt="Item selected" width="411" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our app structure should look like this at this point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ItemsGrid&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will need to:&lt;br&gt;
    - store the index position of the selected item using &lt;code&gt;React.useSate&lt;/code&gt; in &lt;code&gt;App&lt;/code&gt; component&lt;br&gt;
    - create a context with &lt;code&gt;React.createContext&lt;/code&gt; to store the &lt;code&gt;selectedItem&lt;/code&gt; position and the &lt;code&gt;setSelectedItem&lt;/code&gt; from the previous &lt;code&gt;useState&lt;/code&gt;&lt;br&gt;
    - Retrieve the context value inside the &lt;code&gt;Item&lt;/code&gt; component using &lt;code&gt;useContext&lt;/code&gt; hook.&lt;br&gt;
    - Activate the selected classes from above using &lt;code&gt;classnames&lt;/code&gt; utils and the value from the context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ressources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://reactjs.org/docs/hooks-reference.html#usecontext" rel="noopener noreferrer"&gt;https://reactjs.org/docs/hooks-reference.html#usecontext&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jedwatson.github.io/classnames/" rel="noopener noreferrer"&gt;https://jedwatson.github.io/classnames/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/flagrede/d0627032cb459166dd454110337a1ab1" rel="noopener noreferrer"&gt;https://gist.github.com/flagrede/d0627032cb459166dd454110337a1ab1&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Keyboard navigation
&lt;/h1&gt;

&lt;p&gt;For this part we will have to convert the index position of an item to a 5x4 matrix position and another one to revert it.&lt;br&gt;
The grid position should be like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flxxbidvq925yh2malzwy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flxxbidvq925yh2malzwy.png" alt="Grid position" width="800" height="616"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So we will need a method that convert for instance &lt;code&gt;6&lt;/code&gt; to &lt;code&gt;{x:1 ,y:1}&lt;/code&gt;&lt;br&gt;
And another method that convert &lt;code&gt;{x:1 ,y:1}&lt;/code&gt; to &lt;code&gt;6&lt;/code&gt;&lt;br&gt;
The first method signature should be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;(index: number) =&amp;gt; {x:number, y:number}&lt;/code&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second method signature should be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;({x:number, y:number}) =&amp;gt; number&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will then need four methods to compute the new position base on the direction &lt;code&gt;goUp, goRight, goLeft, goBottom&lt;/code&gt; &lt;br&gt;
I strongly recommend doing tests for these methods, but no obligation here.&lt;/p&gt;

&lt;p&gt;Test example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should convert 6 to x:1, y:1 position&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Given&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// When&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;convertIndexToPosition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Then&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally a &lt;code&gt;handleKeyPressed&lt;/code&gt; function inside the &lt;code&gt;App&lt;/code&gt; component to handle the behaviour to execute based on the key pressed.&lt;/p&gt;

&lt;p&gt;Note: the keyboard navigation is expected to work only for the 5 columns grid (since we have 3 columns for the mobile layout).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ressources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://reactjs.org/docs/events.html#keyboard-events" rel="noopener noreferrer"&gt;https://reactjs.org/docs/events.html#keyboard-events&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keyboardNavigation: &lt;a href="https://gist.github.com/flagrede/dbce8e6be44e39f37a9f8e459a39f28c" rel="noopener noreferrer"&gt;https://gist.github.com/flagrede/dbce8e6be44e39f37a9f8e459a39f28c&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;App: &lt;a href="https://gist.github.com/flagrede/58c2bd6dfceda4861be0da90cf099c50" rel="noopener noreferrer"&gt;https://gist.github.com/flagrede/58c2bd6dfceda4861be0da90cf099c50&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Tests:
&lt;a href="https://gist.github.com/flagrede/72d64e3db9634d5b4cc10f37a9f220ee" rel="noopener noreferrer"&gt;https://gist.github.com/flagrede/72d64e3db9634d5b4cc10f37a9f220ee&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Animation
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3lfm8zif1hmvdqw5p2bz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3lfm8zif1hmvdqw5p2bz.png" alt="Item selection animation schema" width="368" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To close this first part we'll add some animation with framer-motion.&lt;br&gt;
We will now add triangles at the edge of the selected item and animate them.&lt;br&gt;
The triangles will be done using css. The css classes to create the triangles are already available, you have &lt;code&gt;zelda-botw-triangle-up&lt;/code&gt; and &lt;code&gt;zelda-botw-triangle-bottom&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We will need to create two components:&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;Triangle&lt;/code&gt; component with the following props:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animateParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;rotate&lt;/code&gt; should be something like &lt;code&gt;"-10deg"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;x&lt;/code&gt; should be something like &lt;code&gt;[8, 2, 4]&lt;/code&gt;, it will animate the triangle on the x axis through the values provided in the array. The values given here are random, I let you find the right ones.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;y&lt;/code&gt; same a &lt;code&gt;x&lt;/code&gt; but for the &lt;code&gt;y&lt;/code&gt; axis&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;className&lt;/code&gt; will be used to apply a list of css classes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A &lt;code&gt;TrianglesBox&lt;/code&gt; component that has no props but display 4 &lt;code&gt;Triangle&lt;/code&gt; components.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To position the triangles we can apply a position &lt;code&gt;absolute&lt;/code&gt; on them and position &lt;code&gt;relative&lt;/code&gt; on parent &lt;code&gt;Item&lt;/code&gt;. To animate the &lt;code&gt;triangle&lt;/code&gt; component we will have to use the &lt;code&gt;motion&lt;/code&gt; api from framer-motion
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;motion&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;framer-motion&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nc"&gt;DummyComponent &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;animateParams&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;motion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;
    &lt;span class="nx"&gt;animate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;animateParams&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will also need to apply a transition parameter to &lt;code&gt;motion.div&lt;/code&gt; to obtain the desired effect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ressources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.framer.com/api/motion/animation/#transitions" rel="noopener noreferrer"&gt;https://www.framer.com/api/motion/animation/#transitions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Triangle component:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/flagrede/8143023838936fb4b7cf81eae1c52d54" rel="noopener noreferrer"&gt;https://gist.github.com/flagrede/8143023838936fb4b7cf81eae1c52d54&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TrianglesBox component:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/flagrede/9587f518c4cf7f8ca991c63a8144b75b" rel="noopener noreferrer"&gt;https://gist.github.com/flagrede/9587f518c4cf7f8ca991c63a8144b75b&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  What's next
&lt;/h1&gt;

&lt;p&gt;Congratulations for completing the first part, I hope you enjoyed it !&lt;br&gt;
On part 2 we will implement the pagination between the items categories, the page transition animation and display additional data about the items.&lt;br&gt;
If you encountered any issues or if you have feedback let me know here or on &lt;a href="https://twitter.com/flagrede" rel="noopener noreferrer"&gt;twitter&lt;/a&gt;, I will gladly try to address them !&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;All the schemas were done using Excalidraw. Thanks to the team for providing such a great tool for free.&lt;/p&gt;

</description>
      <category>react</category>
      <category>tailwind</category>
      <category>framermotion</category>
    </item>
  </channel>
</rss>
