1"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[243],{24033:(e,t,n)=>{n.d(t,{A:()=>r});const r=n.p+"assets/images/part-1-unity-hub-new-project-3bb443b5155cd778a4411e36d9b50a0d.jpg"},31246:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>u,frontMatter:()=>o,metadata:()=>r,toc:()=>l});const r=JSON.parse('{"id":"intro/tutorials/unity-tutorial/part-1","title":"1 - Setup","description":"Unity Tutorial Hero Image","source":"@site/versioned_docs/version-1.12.0/00100-intro/00300-tutorials/00300-unity-tutorial/00200-part-1.md","sourceDirName":"00100-intro/00300-tutorials/00300-unity-tutorial","slug":"/tutorials/unity/part-1","permalink":"/docs/1.12.0/tutorials/unity/part-1","draft":false,"unlisted":false,"editUrl":"https://github.com/clockworklabs/SpacetimeDB/edit/master/docs/versioned_docs/version-1.12.0/00100-intro/00300-tutorials/00300-unity-tutorial/00200-part-1.md","tags":[],"version":"1.12.0","sidebarPosition":200,"frontMatter":{"title":"1 - Setup","slug":"/tutorials/unity/part-1"},"sidebar":"sidebar","previous":{"title":"Unity Tutorial","permalink":"/docs/1.12.0/tutorials/unity"},"next":{"title":"2 - Connecting to SpacetimeDB","permalink":"/docs/1.12.0/tutorials/unity/part-2"}}');var a=n(86106),i=n(56225);n(51471),n(43244);const o={title:"1 - Setup",slug:"/tutorials/unity/part-1"},s=void 0,c={},l=[{value:"Setting up the Tutorial Unity Project",id:"setting-up-the-tutorial-unity-project",level:2},{value:"Step 1: Create a Blank Unity Project",id:"step-1-create-a-blank-unity-project",level:3},{value:"Import the SpacetimeDB Unity SDK",id:"import-the-spacetimedb-unity-sdk",level:3},{value:"Create the GameManager Script",id:"create-the-gamemanager-script",level:3},{value:"Add the GameManager to the Scene",id:"add-the-gamemanager-to-the-scene",level:3},{value:"Add the SpacetimeDB Network Manager",id:"add-the-spacetimedb-network-manager",level:3},{value:"Create the Server Module",id:"create-the-server-module",level:3}];function d(e){const t={a:"a",admonition:"admonition",blockquote:"blockquote",code:"code",h2:"h2",h3:"h3",img:"img",li:"li",ol:"ol",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,i.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(t.p,{children:(0,a.jsx)(t.img,{alt:"Unity Tutorial Hero Image",src:n(59153).A+"",width:"3346",height:"1842"})}),"\n",(0,a.jsxs)(t.p,{children:["Need help with the tutorial? ",(0,a.jsx)(t.a,{href:"https://discord.gg/spacetimedb",children:"Join our Discord server"}),"!"]}),"\n",(0,a.jsxs)(t.blockquote,{children:["\n",(0,a.jsx)(t.p,{children:"A completed version of the game we'll create in this tutorial is available at:"}),"\n",(0,a.jsx)(t.p,{children:(0,a.jsx)(t.a,{href:"https://github.com/clockworklabs/SpacetimeDB/tree/master/demo/Blackholio",children:"https://github.com/clockworklabs/SpacetimeDB/tree/master/demo/Blackholio"})}),"\n"]}),"\n",(0,a.jsx)(t.h2,{id:"setting-up-the-tutorial-unity-project",children:"Setting up the Tutorial Unity Project"}),"\n",(0,a.jsx)(t.p,{children:"In this section, we will guide you through the process of setting up a Unity Project that will serve as the starting point for our tutorial. By the end of this section, you will have a basic Unity project and be ready to implement the server functionality."}),"\n",(0,a.jsx)(t.h3,{id:"step-1-create-a-blank-unity-project",children:"Step 1: Create a Blank Unity Project"}),"\n",(0,a.jsxs)(t.p,{children:["SpacetimeDB supports Unity version ",(0,a.jsx)(t.code,{children:"2022.3.32f1"})," or later. See ",(0,a.jsx)(t.a,{href:".",children:"the overview"})," for more information on specific supported versions."]}),"\n",(0,a.jsxs)(t.p,{children:['Open Unity and create a new project by selecting "New" from the Unity Hub or going to ',(0,a.jsx)(t.strong,{children:"File -> New Project"}),"."]}),"\n",(0,a.jsx)(t.p,{children:(0,a.jsx)(t.img,{alt:"Unity Hub New Project",src:n(24033).A+"",width:"1024",height:"600"})}),"\n",(0,a.jsx)(t.admonition,{type:"warning",children:(0,a.jsx)(t.p,{children:(0,a.jsxs)(t.strong,{children:["Make sure to choose the ",(0,a.jsx)(t.code,{children:"Universal 2D"})," template for your new project."]})})}),"\n",(0,a.jsxs)(t.p,{children:["For ",(0,a.jsx)(t.code,{children:"Project Name"})," use ",(0,a.jsx)(t.code,{children:"blackholio"}),". For ",(0,a.jsx)(t.code,{children:"Project Location"})," select a directory that you can navigate to via the CLI because we will need to do so in part 2."]}),"\n",(0,a.jsx)(t.p,{children:(0,a.jsx)(t.img,{alt:"Universal 2D Template",src:n(73288).A+"",width:"2392",height:"1324"})}),"\n",(0,a.jsx)(t.p,{children:'Click "Create" to generate the blank project.'}),"\n",(0,a.jsx)(t.h3,{id:"import-the-spacetimedb-unity-sdk",children:"Import the SpacetimeDB Unity SDK"}),"\n",(0,a.jsx)(t.p,{children:'Add the SpacetimeDB Unity Package using the Package Manager. Open the Package Manager window by clicking on Window -> Package Manager. Click on the + button in the top left corner of the window and select "Add package from git URL". Enter the following URL and click Add.'}),"\n",(0,a.jsx)(a.Fragment,{children:(0,a.jsx)(t.pre,{className:"shiki spacetime-dark",style:{backgroundColor:"var(--clockworklabs-code-background-color)",color:"#F8F8F2"},tabIndex:"0",children:(0,a.jsx)(t.code,{children:(0,a.jsx)(t.span,{className:"line",children:(0,a.jsx)(t.span,{style:{color:"#50FA7B"},children:"https://github.com/clockworklabs/com.clockworklabs.spacetimedbsdk.git"})})})})}),"\n",(0,a.jsx)(t.p,{children:"The SpacetimeDB Unity SDK provides helpful tools for integrating SpacetimeDB into Unity, including a network manager which will synchronize your Unity client's state with your SpacetimeDB database in accordance with your subscription queries."}),"\n",(0,a.jsx)(t.h3,{id:"create-the-gamemanager-script",children:"Create the GameManager Script"}),"\n",(0,a.jsxs)(t.ol,{children:["\n",(0,a.jsxs)(t.li,{children:["In the Unity ",(0,a.jsx)(t.strong,{children:"Project"})," window, go to the folder where you want to keep your scripts (e.g., ",(0,a.jsx)(t.code,{children:"Scripts"})," folder)."]}),"\n",(0,a.jsxs)(t.li,{children:[(0,a.jsx)(t.strong,{children:"Right-click"})," in the folder, then select ",(0,a.jsx)(t.code,{children:"Create > C# Script"})," or in Unity 6 ",(0,a.jsx)(t.code,{children:"MonoBehavior Script"}),"."]}),"\n",(0,a.jsxs)(t.li,{children:["Name the script ",(0,a.jsx)(t.code,{children:"GameManager"}),"."]}),"\n"]}),"\n",(0,a.jsxs)(t.p,{children:["The ",(0,a.jsx)(t.code,{children:"GameManager"})," script will be where we will put the high level initialization and coordination logic for our game."]}),"\n",(0,a.jsx)(t.h3,{id:"add-the-gamemanager-to-the-scene",children:"Add the GameManager to the Scene"}),"\n",(0,a.jsxs)(t.ol,{children:["\n",(0,a.jsxs)(t.li,{children:["\n",(0,a.jsxs)(t.p,{children:[(0,a.jsx)(t.strong,{children:"Create an Empty GameObject"}),":"]}),"\n",(0,a.jsxs)(t.ul,{children:["\n",(0,a.jsxs)(t.li,{children:["Go to the top menu and select ",(0,a.jsx)(t.strong,{children:"GameObject > Create Empty"}),"."]}),"\n",(0,a.jsxs)(t.li,{children:["Alternatively, right-click in the ",(0,a.jsx)(t.strong,{children:"Hierarchy"})," window and select ",(0,a.jsx)(t.strong,{children:"Create Empty"}),"."]}),"\n"]}),"\n"]}),"\n",(0,a.jsxs)(t.li,{children:["\n",(0,a.jsxs)(t.p,{children:[(0,a.jsx)(t.strong,{children:"Rename the GameObject"}),":"]}),"\n",(0,a.jsxs)(t.ul,{children:["\n",(0,a.jsxs)(t.li,{children:["In the ",(0,a.jsx)(t.strong,{children:"Inspector"}),", click on the GameObject\u2019s name at the top and rename it to ",(0,a.jsx)(t.code,{children:"GameManager"}),"."]}),"\n"]}),"\n"]}),"\n",(0,a.jsxs)(t.li,{children:["\n",(0,a.jsxs)(t.p,{children:[(0,a.jsx)(t.strong,{children:"Attach the GameManager Script"}),":"]}),"\n",(0,a.jsxs)(t.ul,{children:["\n",(0,a.jsxs)(t.li,{children:["Drag and drop the ",(0,a.jsx)(t.code,{children:"GameManager"})," script from the ",(0,a.jsx)(t.strong,{children:"Project"})," window onto the ",(0,a.jsx)(t.code,{children:"GameManager"})," GameObject in the ",(0,a.jsx)(t.strong,{children:"Hierarchy"})," window."]}),"\n",(0,a.jsxs)(t.li,{children:["Alternatively, in the ",(0,a.jsx)(t.strong,{children:"Inspector"}),", click ",(0,a.jsx)(t.strong,{children:"Add Component"}),", search for ",(0,a.jsx)(t.code,{children:"GameManager"}),", and select it."]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,a.jsx)(t.h3,{id:"add-the-spacetimedb-network-manager",children:"Add the SpacetimeDB Network Manager"}),"\n",(0,a.jsxs)(t.p,{children:["The ",(0,a.jsx)(t.code,{children:"SpacetimeDBNetworkManager"})," is a simple script which hooks into the Unity ",(0,a.jsx)(t.code,{children:"Update"})," loop in order to drive the sending and processing of messages between your client and SpacetimeDB. You don't have to interact with this script, but it must be present on a single GameObject which is in the scene in order for it to facilitate the processing of messages."]}),"\n",(0,a.jsxs)(t.p,{children:["When you build a new connection to SpacetimeDB, that connection will be added to and managed by the ",(0,a.jsx)(t.code,{children:"SpacetimeDBNetworkManager"})," automatically."]}),"\n",(0,a.jsxs)(t.p,{children:["Click on the ",(0,a.jsx)(t.code,{children:"GameManager"})," object in the scene and click ",(0,a.jsx)(t.strong,{children:"Add Component"}),". Search for and select the ",(0,a.jsx)(t.code,{children:"SpacetimeDBNetworkManager"})," to add it to your ",(0,a.jsx)(t.code,{children:"GameManager"})," object."]}),"\n",(0,a.jsx)(t.p,{children:"Our Unity project is all set up! If you press play, it will show a blank screen, but it should start the game without any errors. Now we're ready to get started on our SpacetimeDB server module, so we have something to connect to!"}),"\n",(0,a.jsx)(t.h3,{id:"create-the-server-module",children:"Create the Server Module"}),"\n",(0,a.jsxs)(t.p,{children:["We've now got the very basics set up. In ",(0,a.jsx)(t.a,{href:"part-2",children:"part 2"})," you'll learn the basics of how to create a SpacetimeDB server module and how to connect to it from your client."]})]})}function u(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,a.jsx)(t,{...e,children:(0,a.jsx)(d,{...e})}):d(e)}},43244:(e,t,n)=>{n.d(t,{A:()=>
1o});n(7378);var r=n(23372);const a={tabItem:"tabItem_w6aI"};var i=n(86106);function o({children:e,hidden:t,className:n}){return(0,i.jsx)("div",{role:"tabpanel",className:(0,r.A)(a.tabItem,n),hidden:t,children:e})}},51471:(e,t,n)=>{n.d(t,{A:()=>k});var r=n(7378),a=n(23372),i=n(23418),o=n(46585),s=n(30505),c=n(99202),l=n(81922),d=n(46923),u=n(73126);function h(e){return r.Children.toArray(e).filter(e=>"\n"!==e).map(e=>{if(!e||(0,r.isValidElement)(e)&&function(e){const{props:t}=e;return!!t&&"object"==typeof t&&"value"in t}(e))return e;throw new Error(`Docusaurus error: Bad <Tabs> child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.`)})?.filter(Boolean)??[]}function p(e){const{values:t,children:n}=e;return(0,r.useMemo)(()=>{const e=t??function(e){return h(e).map(({props:{value:e,label:t,attributes:n,default:r}})=>({value:e,label:t,attributes:n,default:r}))}(n);return function(e){const t=(0,d.XI)(e,(e,t)=>e.value===t.value);if(t.length>0)throw new Error(`Docusaurus error: Duplicate values "${t.map(e=>e.value).join(", ")}" found in <Tabs>. Every value needs to be unique.`)}(e),e},[t,n])}function m({value:e,tabValues:t}){return t.some(t=>t.value===e)}function g({queryString:e=!1,groupId:t}){const n=(0,s.W6)(),a=function({queryString:e=!1,groupId:t}){if("string"==typeof e)return e;if(!1===e)return null;if(!0===e&&!t)throw new Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return t??null}({queryString:e,groupId:t});return[(0,l.aZ)(a),(0,r.useCallback)(e=>{if(!a)return;const t=new URLSearchParams(n.location.search);t.set(a,e),n.replace({...n.location,search:t.toString()})},[a,n])]}function j(e){const{defaultValue:t,queryString:n=!1,groupId:a}=e,i=p(e),[o,s]=(0,r.useState)(()=>function({defaultValue:e,tabValues:t}){if(0===t.length)throw new Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(e){if(!m({value:e,tabValues:t}))throw new Error(`Docusaurus error: The <Tabs> has a defaultValue "${e}" but none of its children has the corresponding value. Available values are: ${t.map(e=>e.value).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return e}const n=t.find(e=>e.default)??t[0];if(!n)throw new Error("Unexpected error: 0 tabValues");return n.value}({defaultValue:t,tabValues:i})),[l,d]=g({queryString:n,groupId:a}),[h,j]=function({groupId:e}){const t=function(e){return e?`docusaurus.tab.${e}`:null}(e),[n,a]=(0,u.Dv)(t);return[n,(0,r.useCallback)(e=>{t&&a.set(e)},[t,a])]}({groupId:a}),b=(()=>{const e=l??h;return m({value:e,tabValues:i})?e:null})();(0,c.A)(()=>{b&&s(b)},[b]);return{selectedValue:o,selectValue:(0,r.useCallback)(e=>{if(!m({value:e,tabValues:i}))throw new Error(`Can't select invalid tab value=${e}`);s(e),d(e),j(e)},[d,j,i]),tabValues:i}}var b=n(52538);const x={tabList:"tabList_duiv",tabItem:"tabItem_a1H9"};var f=n(86106);function y({className:e,block:t,selectedValue:n,selectValue:r,tabValues:i}){const s=[],{blockElementScrollPositionUntilNextRender:c}=(0,o.a_)(),l=e=>{const t=e.currentTarget,a=s.indexOf(t),o=i[a].value;o!==n&&(c(t),r(o))},d=e=>{let t=null;switch(e.key){case"Enter":l(e);break;case"ArrowRight":{const n=s.indexOf(e.currentTarget)+1;t=s[n]??s[0];break}case"ArrowLeft":{const n=s.indexOf(e.currentTarget)-1;t=s[n]??s[s.length-1];break}}t?.focus()};return(0,f.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,a.A)("tabs",{"tabs--block":t},e),children:i.map(({value:e,label:t,attributes:r})=>(0,f.jsx)("li",{role:"tab",tabIndex:n===e?0:-1,"aria-selected":n===e,ref:e=>{s.push(e)},onKeyDown:d,onClick:l,...r,className:(0,a.A)("tabs__item",x.tabItem,r?.className,{"tabs__item--active":n===e}),children:t??e},e))})}function v({lazy:e,children:t,selectedValue:n}){const i=(Array.isArray(t)?t:[t]).filter(Boolean);if(e){const e=i.find(e=>e.props.value===n);return e?(0,r.cloneElement)(e,{className:(0,a.A)("margin-top--md",e.props.className)}):null}return(0,f.jsx)("div",{className:"margin-top--md",children:i.map((e,t)=>(0,r.cloneElement)(e,{key:t,hidden:e.props.value!==n}))})}function w(e){const t=j(e);return(0,f.jsxs)("div",{className:(0,a.A)(i.G.tabs.container,"tabs-container",x.tabList),
1children:[(0,f.jsx)(y,{...t,...e}),(0,f.jsx)(v,{...t,...e})]})}function k(e){const t=(0,b.A)();return(0,f.jsx)(w,{...e,children:h(e.children)},String(t))}},56225:(e,t,n)=>{n.d(t,{R:()=>o,x:()=>s});var r=n(7378);const a={},i=r.createContext(a);function o(e){const t=r.useContext(i);return r.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function s(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),r.createElement(i.Provider,{value:t},e.children)}},59153:(e,t,n)=>{n.d(t,{A:()=>r});const r=n.p+"assets/images/part-1-hero-image-4e5662263e32decd272d4e0ae7d50165.png"},73288:(e,t,n)=>{n.d(t,{A:()=>r});const r=n.p+"assets/images/part-1-universal-2d-template-60d4ccc89468d8ec603335c41b4a1917.png"}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.