3D Asset Store Propsplanet upgrades from Unity to PlayCanvas | PlayCanvas Blog
-
+
diff --git a/404.html b/404.html
index ed7525d38..0d2a24a06 100644
--- a/404.html
+++ b/404.html
@@ -5,7 +5,7 @@
Page Not Found | PlayCanvas Blog
-
+
diff --git a/a-faster-supersplat-with-pwa-support/index.html b/a-faster-supersplat-with-pwa-support/index.html
index 4dd76834c..dda351545 100644
--- a/a-faster-supersplat-with-pwa-support/index.html
+++ b/a-faster-supersplat-with-pwa-support/index.html
@@ -5,7 +5,7 @@
A Faster SuperSplat with PWA Support | PlayCanvas Blog
-
+
diff --git a/a-graph-rendering-library-for-the-web-pcui-graph/index.html b/a-graph-rendering-library-for-the-web-pcui-graph/index.html
index aec9180d1..810551c45 100644
--- a/a-graph-rendering-library-for-the-web-pcui-graph/index.html
+++ b/a-graph-rendering-library-for-the-web-pcui-graph/index.html
@@ -5,7 +5,7 @@
A Graph Rendering Library for the Web: PCUI Graph | PlayCanvas Blog
-
+
diff --git a/a-multiplayer-3rd-person-shooter-in-html5/index.html b/a-multiplayer-3rd-person-shooter-in-html5/index.html
index 0c5f292fb..538ad3ac3 100644
--- a/a-multiplayer-3rd-person-shooter-in-html5/index.html
+++ b/a-multiplayer-3rd-person-shooter-in-html5/index.html
@@ -5,7 +5,7 @@
Making a multiplayer 3rd-person shooter in HTML5 | PlayCanvas Blog
-
+
diff --git a/a-new-ui-for-the-playcanvas-editor/index.html b/a-new-ui-for-the-playcanvas-editor/index.html
index d8bde20c2..dd56eb3cb 100644
--- a/a-new-ui-for-the-playcanvas-editor/index.html
+++ b/a-new-ui-for-the-playcanvas-editor/index.html
@@ -5,7 +5,7 @@
A New UI for the PlayCanvas Editor | PlayCanvas Blog
-
+
diff --git a/a-wild-designer-appears/index.html b/a-wild-designer-appears/index.html
index ee8574b8c..09572544b 100644
--- a/a-wild-designer-appears/index.html
+++ b/a-wild-designer-appears/index.html
@@ -5,7 +5,7 @@
A wild designer appears! | PlayCanvas Blog
-
+
diff --git a/a-wild-programmer-appears/index.html b/a-wild-programmer-appears/index.html
index 49ec793a1..b55c4d798 100644
--- a/a-wild-programmer-appears/index.html
+++ b/a-wild-programmer-appears/index.html
@@ -5,7 +5,7 @@
A wild programmer appears! | PlayCanvas Blog
-
+
diff --git a/anim-layer-masks-and-blending/index.html b/anim-layer-masks-and-blending/index.html
index e05c65c3d..ee9e8e11c 100644
--- a/anim-layer-masks-and-blending/index.html
+++ b/anim-layer-masks-and-blending/index.html
@@ -5,7 +5,7 @@
Anim Layer Masks and Blending | PlayCanvas Blog
-
+
diff --git a/announcing-playcanvas-answers/index.html b/announcing-playcanvas-answers/index.html
index 4a2b257fa..3f9f6838d 100644
--- a/announcing-playcanvas-answers/index.html
+++ b/announcing-playcanvas-answers/index.html
@@ -5,7 +5,7 @@
PlayCanvas Community Part 1 - Announcing PlayCanvas Answers | PlayCanvas Blog
-
+
diff --git a/announcing-the-new-playcanvas-asset-store/index.html b/announcing-the-new-playcanvas-asset-store/index.html
index 959a6e14c..097d540e1 100644
--- a/announcing-the-new-playcanvas-asset-store/index.html
+++ b/announcing-the-new-playcanvas-asset-store/index.html
@@ -5,7 +5,7 @@
Announcing the New PlayCanvas Asset Store | PlayCanvas Blog
-
+
diff --git a/apple-embraces-webgl/index.html b/apple-embraces-webgl/index.html
index 6bae671c4..b99c64970 100644
--- a/apple-embraces-webgl/index.html
+++ b/apple-embraces-webgl/index.html
@@ -5,7 +5,7 @@
Apple Embraces WebGL | PlayCanvas Blog
-
+
diff --git a/archive/index.html b/archive/index.html
index b16405bf8..7dd716430 100644
--- a/archive/index.html
+++ b/archive/index.html
@@ -5,7 +5,7 @@
Archive | PlayCanvas Blog
-
+
diff --git a/arm-and-playcanvas-open-source-seemore-webgl-demo/index.html b/arm-and-playcanvas-open-source-seemore-webgl-demo/index.html
index 735a5c9c9..ac5e8528c 100644
--- a/arm-and-playcanvas-open-source-seemore-webgl-demo/index.html
+++ b/arm-and-playcanvas-open-source-seemore-webgl-demo/index.html
@@ -5,7 +5,7 @@
Arm and PlayCanvas Open Source Seemore WebGL Demo | PlayCanvas Blog
-
+
diff --git a/assets/js/09a165e5.e0522093.js b/assets/js/09a165e5.e0522093.js
new file mode 100644
index 000000000..1a242bb35
--- /dev/null
+++ b/assets/js/09a165e5.e0522093.js
@@ -0,0 +1 @@
+"use strict";(self.webpackChunk_playcanvas_blog=self.webpackChunk_playcanvas_blog||[]).push([[43845],{32038:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>r,default:()=>h,frontMatter:()=>o,metadata:()=>l,toc:()=>i});var a=n(74848),s=n(28453);const o={authors:"nathan",slug:"playhack-december-collecting-presents",title:"PLAYHACK December - Collecting Presents"},r=void 0,l={permalink:"/playhack-december-collecting-presents",editUrl:"https://github.com/playcanvas/blog/tree/main/blog/2014-12-22-playhack-december-collecting-presents.md",source:"@site/blog/2014-12-22-playhack-december-collecting-presents.md",title:"PLAYHACK December - Collecting Presents",description:"**PLAYHACK** is our fun monthly game building session. Throughout the month I\u2019ll be posting tips and tricks to help you get a game made by the end of the month.\xa0Don\u2019t forget, these are just examples. You can make **any** game you like. Read more about this month\u2019s PLAYHACK.",date:"2014-12-22T00:00:00.000Z",tags:[],readingTime:5.955,hasTruncateMarker:!0,authors:[{name:"Nathan Patel",title:"Technical Blogger",page:{permalink:"/authors/nathan"},key:"nathan"}],frontMatter:{authors:"nathan",slug:"playhack-december-collecting-presents",title:"PLAYHACK December - Collecting Presents"},unlisted:!1,prevItem:{title:"PlayCanvas in 2014: A Year In Review",permalink:"/playcanvas-in-2014-a-year-in-review"},nextItem:{title:"PLAYHACK December - Creating Presents",permalink:"/playhack-december-creating-presents"}},c={authorsImageUrls:[void 0]},i=[];function m(e){const t={a:"a",em:"em",img:"img",p:"p",strong:"strong",...(0,s.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(t.p,{children:(0,a.jsxs)(t.em,{children:[(0,a.jsx)(t.strong,{children:"PLAYHACK"})," is our fun monthly game building session. Throughout the month I\u2019ll be posting tips and tricks to help you get a game made by the end of the month.\xa0Don\u2019t forget, these are just examples. You can make ",(0,a.jsx)(t.strong,{children:"any"})," game you like. ",(0,a.jsx)(t.a,{href:"https://blog.playcanvas.com/playhack-december-jolly-santa/",children:"Read more"})," about this month\u2019s PLAYHACK."]})}),"\n",(0,a.jsx)(t.p,{children:(0,a.jsx)(t.img,{alt:"PLAYHACK Logo",src:n(6694).A+"",width:"603",height:"218"})})]})}function h(e={}){const{wrapper:t}={...(0,s.R)(),...e.components};return t?(0,a.jsx)(t,{...e,children:(0,a.jsx)(m,{...e})}):m(e)}},6694:(e,t,n)=>{n.d(t,{A:()=>a});const a=n.p+"assets/images/playhack-logo-xmas-451ccd5e2d37685bc0f90e7e5a066b00.jpg"},28453:(e,t,n)=>{n.d(t,{R:()=>r,x:()=>l});var a=n(96540);const s={},o=a.createContext(s);function r(e){const t=a.useContext(o);return a.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function l(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),a.createElement(o.Provider,{value:t},e.children)}}}]);
\ No newline at end of file
diff --git a/assets/js/09a165e5.ee011a7e.js b/assets/js/09a165e5.ee011a7e.js
deleted file mode 100644
index f4bfabe7e..000000000
--- a/assets/js/09a165e5.ee011a7e.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(self.webpackChunk_playcanvas_blog=self.webpackChunk_playcanvas_blog||[]).push([[43845],{32038:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>h,frontMatter:()=>o,metadata:()=>c,toc:()=>i});var a=n(74848),s=n(28453);const o={authors:"nathan",slug:"playhack-december-collecting-presents",title:"PLAYHACK December - Collecting Presents"},r=void 0,c={permalink:"/playhack-december-collecting-presents",editUrl:"https://github.com/playcanvas/blog/tree/main/blog/2014-12-22-playhack-december-collecting-presents.md",source:"@site/blog/2014-12-22-playhack-december-collecting-presents.md",title:"PLAYHACK December - Collecting Presents",description:"**PLAYHACK** is our fun monthly game building session. Throughout the month I\u2019ll be posting tips and tricks to help you get a game made by the end of the month.\xa0Don\u2019t forget, these are just examples. You can make **any** game you like. Read more about this month\u2019s PLAYHACK.",date:"2014-12-22T00:00:00.000Z",tags:[],readingTime:5.955,hasTruncateMarker:!0,authors:[{name:"Nathan",page:{permalink:"/authors/nathan"},key:"nathan"}],frontMatter:{authors:"nathan",slug:"playhack-december-collecting-presents",title:"PLAYHACK December - Collecting Presents"},unlisted:!1,prevItem:{title:"PlayCanvas in 2014: A Year In Review",permalink:"/playcanvas-in-2014-a-year-in-review"},nextItem:{title:"PLAYHACK December - Creating Presents",permalink:"/playhack-december-creating-presents"}},l={authorsImageUrls:[void 0]},i=[];function m(e){const t={a:"a",em:"em",img:"img",p:"p",strong:"strong",...(0,s.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(t.p,{children:(0,a.jsxs)(t.em,{children:[(0,a.jsx)(t.strong,{children:"PLAYHACK"})," is our fun monthly game building session. Throughout the month I\u2019ll be posting tips and tricks to help you get a game made by the end of the month.\xa0Don\u2019t forget, these are just examples. You can make ",(0,a.jsx)(t.strong,{children:"any"})," game you like. ",(0,a.jsx)(t.a,{href:"https://blog.playcanvas.com/playhack-december-jolly-santa/",children:"Read more"})," about this month\u2019s PLAYHACK."]})}),"\n",(0,a.jsx)(t.p,{children:(0,a.jsx)(t.img,{alt:"PLAYHACK Logo",src:n(6694).A+"",width:"603",height:"218"})})]})}function h(e={}){const{wrapper:t}={...(0,s.R)(),...e.components};return t?(0,a.jsx)(t,{...e,children:(0,a.jsx)(m,{...e})}):m(e)}},6694:(e,t,n)=>{n.d(t,{A:()=>a});const a=n.p+"assets/images/playhack-logo-xmas-451ccd5e2d37685bc0f90e7e5a066b00.jpg"},28453:(e,t,n)=>{n.d(t,{R:()=>r,x:()=>c});var a=n(96540);const s={},o=a.createContext(s);function r(e){const t=a.useContext(o);return a.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function c(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),a.createElement(o.Provider,{value:t},e.children)}}}]);
\ No newline at end of file
diff --git a/assets/js/253ba14e.375899b2.js b/assets/js/253ba14e.375899b2.js
new file mode 100644
index 000000000..3027cb15a
--- /dev/null
+++ b/assets/js/253ba14e.375899b2.js
@@ -0,0 +1 @@
+"use strict";(self.webpackChunk_playcanvas_blog=self.webpackChunk_playcanvas_blog||[]).push([[1168],{8e3:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>d,frontMatter:()=>s,metadata:()=>r,toc:()=>h});var a=n(74848),o=n(28453);const s={authors:"nathan",slug:"playhack-december-player-character",title:"PLAYHACK December - Player Character"},i=void 0,r={permalink:"/playhack-december-player-character",editUrl:"https://github.com/playcanvas/blog/tree/main/blog/2014-12-08-playhack-december-player-character.md",source:"@site/blog/2014-12-08-playhack-december-player-character.md",title:"PLAYHACK December - Player Character",description:"**PLAYHACK** is our fun monthly game building session. Throughout the month I'll be posting tips and tricks to help you get a game made by the end of the month.\xa0Don\u2019t forget, these are just examples. You can make **any** game you like. Read more about this month's PLAYHACK.",date:"2014-12-08T00:00:00.000Z",tags:[],readingTime:8.095,hasTruncateMarker:!0,authors:[{name:"Nathan Patel",title:"Technical Blogger",page:{permalink:"/authors/nathan"},key:"nathan"}],frontMatter:{authors:"nathan",slug:"playhack-december-player-character",title:"PLAYHACK December - Player Character"},unlisted:!1,prevItem:{title:"PLAYCANVAS in LUDUM DARE 31",permalink:"/playcanvas-in-ludum-dare-31"},nextItem:{title:"PlayCanvas Update 5/12/14",permalink:"/playcanvas-update-51214"}},l={authorsImageUrls:[void 0]},h=[{value:"Camera",id:"camera",level:2},{value:"Player",id:"player",level:2},{value:"Player Controller",id:"player-controller",level:2},{value:"Keyboard Controls",id:"keyboard-controls",level:2},{value:"Mouse Controls",id:"mouse-controls",level:2}];function c(e){const t={a:"a",code:"code",em:"em",h2:"h2",img:"img",p:"p",pre:"pre",strong:"strong",...(0,o.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(t.p,{children:(0,a.jsxs)(t.em,{children:[(0,a.jsx)(t.strong,{children:"PLAYHACK"})," is our fun monthly game building session. Throughout the month I'll be posting tips and tricks to help you get a game made by the end of the month.\xa0Don\u2019t forget, these are just examples. You can make ",(0,a.jsx)(t.strong,{children:"any"})," game you like. ",(0,a.jsx)(t.a,{href:"https://blog.playcanvas.com/playhack-december-jolly-santa/",children:"Read more"})," about this month's PLAYHACK."]})}),"\n",(0,a.jsx)(t.p,{children:(0,a.jsx)(t.img,{alt:"PLAYHACK Logo",src:n(6694).A+"",width:"603",height:"218"})}),"\n",(0,a.jsx)("div",{className:"iframe-container",children:(0,a.jsx)("iframe",{loading:"lazy",src:"https://playcanv.as/b/WbIFTpdn/",title:"360 lookaround camera",webkitallowfullscreen:"true",mozallowfullscreen:"true",allow:"autoplay",allowfullscreen:"true",allowvr:"",scrolling:"no",frameborder:"0"})}),"\n",(0,a.jsx)(t.p,{children:(0,a.jsx)(t.em,{children:"Use\xa0W and S to move the sleigh. Press SPACE to switch to mouse control."})}),"\n",(0,a.jsx)(t.p,{children:"In the coming weeks, I'll be taking you through how to make a complete game in PlayCanvas - from very beginning to very end, with only basic programming knowledge required!"}),"\n",(0,a.jsx)(t.p,{children:"We're going to be making a side-scrolling endless runner game, where Santa must pick up presents and avoid various things to deliver presents on time! However, the techniques we'll be using will be applicable to many kinds of games."}),"\n",(0,a.jsx)(t.p,{children:(0,a.jsx)(t.img,{alt:"Santa",src:n(39914).A+"",width:"1113",height:"727"})}),"\n",(0,a.jsxs)(t.p,{children:["To follow along with this tutorial, fork the ",(0,a.jsx)(t.a,{href:"https://playcanvas.com/project/333365/overview/playhack-dec-14",children:"PLAYHACK December project"}),"\xa0to get started. To see what we\u2019ll be making this time, ",(0,a.jsx)(t.a,{href:"https://playcanvas.com/project/333872/overview/playhack_tut1",children:"look here"}),". Or just check out the embedded app\xa0above."]}),"\n",(0,a.jsx)(t.p,{children:"So you've forked the repository and got it open in the editor. We're going to need to make a few changes to start making our game."}),"\n",(0,a.jsx)(t.h2,{id:"camera",children:"Camera"}),"\n",(0,a.jsx)(t.p,{children:'Firstly, the camera. There are two camera types in PlayCanvas - Perspective and Orthographic. Perspective is what you want for most 3D games \u2013 it\u2019s how things look in life.\nHowever, our game is pretty much 2D - which means an Orthographic projection would be best for us. To make the switch, select the Camera in the Pack Explorer, then in the Attribute Editor on the right, change Projection from "Perspective" to "Orthographic."'}),"\n",(0,a.jsx)(t.p,{children:"If you test now by pressing the Launch button, you'll see that Santa is tiny in the middle of the screen! That's not what we want. We can fix this by reducing the Ortho Height attribute of the camera. Reduce it from 100 down to about 10, and now check the game. Santa's now a much better size!"}),"\n",(0,a.jsx)(t.h2,{id:"player",children:"Player"}),"\n",(0,a.jsx)(t.p,{children:(0,a.jsx)(t.a,{target:"_blank","data-noBrokenLinkCheck":!0,href:n(87562).A+"",children:(0,a.jsx)(t.img,{alt:"Santa",src:n(54080).A+"",width:"600",height:"330"})})}),"\n",(0,a.jsx)(t.p,{children:"Now, we want Santa to start on the left of the screen, looking right. To make things easier for us, we'll align our camera along the Z-axis so that moving things around on screen becomes easier for us later."}),"\n",(0,a.jsx)(t.p,{children:"To do that, set the camera position to 0, 0, 10, and the rotation to 0, 0, 0. Santa should now be looking right at the camera in the middle of the screen. You can use the rotate and translate tools in the editor to move him to the right place, or you can select Santa_sleigh from the Pack Explorer and set his position to -17, 0, 0 and his rotation to 0, 90, 0. Great! Take a look at the game again. Santa's now in the right place, facing the right way. Lovely."}),"\n",(0,a.jsx)(t.p,{children:"Let's get Santa moving. We're going to create two ways for Santa to move - by mouse and by keyboard. Let's create a new script, called Santa_Controller, and attach it to the Santa_sleigh entity we have."}),"\n",(0,a.jsx)(t.p,{children:'To do this, right click on Santa_sleigh in the Pack Explorer, and select Add Component > Script. Now we can attach scripts to Santa. To attach a new script to Santa, simply type the name of the new script (in our case, "Santa_Controller") in the URL box of the Script Attribute we just added to Santa.'}),"\n",(0,a.jsx)(t.h2,{id:"player-controller",children:"Player Controller"}),"\n",(0,a.jsx)(t.p,{children:"Now we've created the new script and attached it to Santa, let's edit it and make Santa do something! Click on the blue Santa_Controller.js link that should have appeared below the URL box to get editing!"}),"\n",(0,a.jsx)(t.p,{children:"You'll be greeted by a new script that should look like this:"}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-javascript",children:"pc.script.create('Santa_Controller', function (context) {\n // Creates a new Santa_Controller instance\n var Santa_Controller = function (entity) {\n this.entity = entity;\n };\n\n Santa_Controller.prototype = {\n // Called once after all resources are loaded\n initialize: function () {\n },\n\n // Called every frame, dt is time in seconds since last update\n update: function (dt) {\n }\n };\n\n return Santa_Controller;\n});\n"})}),"\n",(0,a.jsx)(t.p,{children:"Let\u2019s think about what we need to do to make Santa move up and down. Well, we\u2019ll need the speed we want him to move \u2013 so let\u2019s put that as an attribute so we can change it easily."}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-javascript",children:'pc.script.attribute("speed", "number", 10);\npc.script.create(\'Santa_Controller\', function (context) {\n'})}),"\n",(0,a.jsx)(t.p,{children:"Now, let\u2019s make 2 functions \u2013 one that moves Santa up, and one that moves Santa down."}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-javascript",children:" // Called every frame, dt is time in seconds since last update\n update: function (dt) {\n },\n\n moveUp: function(dt) {\n this.entity.translate(0, this.speed * dt, 0);\n },\n\n moveDown: function(dt) {\n this.entity.translate(0, -this.speed * dt, 0);\n }\n};\n"})}),"\n",(0,a.jsxs)(t.p,{children:["The ",(0,a.jsx)(t.code,{children:"translate"})," function is one that all entities have, and simply moves the entity by a specified amount. In this case, we want Santa to move up or down by the speed, multiplied by the amount of time passed in the current frame. We do this so that Santa will move the same speed however fast our game is running!"]}),"\n",(0,a.jsx)(t.h2,{id:"keyboard-controls",children:"Keyboard Controls"}),"\n",(0,a.jsxs)(t.p,{children:["Now, let\u2019s look at keyboard controls. To do anything with the keyboard in PlayCanvas, we need to use ",(0,a.jsx)(t.code,{children:"context.keyboard"}),". ",(0,a.jsx)(t.code,{children:"context"})," is available in all scripts, and allows us access to all the data our game has to offer \u2013 in this case, we need the keyboard, which deals with keyboard input."]}),"\n",(0,a.jsxs)(t.p,{children:["We can use ",(0,a.jsx)(t.code,{children:"context.keyboard.isPressed"})," to check if specific keys are pressed \u2013 if W or S are pressed, we\u2019ll move Santa up or down accordingly. We\u2019ll need to check every frame, so put the following code in the update function:"]}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-javascript",children:"if (context.keyboard.isPressed(pc.input.KEY_W)) {\n this.moveUp(dt);\n}\nif (context.keyboard.isPressed(pc.input.KEY_S)) {\n this.moveDown(dt);\n}\n"})}),"\n",(0,a.jsxs)(t.p,{children:["Before we test the game, however, there\u2019s something we need to do. The editor needs to be told when we add an attribute to a script, so back in the editor, and go ",(0,a.jsx)(t.em,{children:"Entity > Refresh Script Attributes"}),". A box should come up underneath our Santa_Controller.js script allowing us to change the speed of Santa."]}),"\n",(0,a.jsx)(t.p,{children:"Now test the game \u2013 we should be able to move Santa up and down now using the W and S keys! Change the speed in the editor until you find something that feels fun."}),"\n",(0,a.jsx)(t.h2,{id:"mouse-controls",children:"Mouse Controls"}),"\n",(0,a.jsx)(t.p,{children:"That\u2019s all good, however, we want the option of using the mouse or the keyboard! Let\u2019s add another attribute in our script that will allow us to change between mouse and keyboard when testing:"}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-javascript",children:'pc.script.attribute("keyboard", "boolean", true);\n'})}),"\n",(0,a.jsxs)(t.p,{children:["If you do ",(0,a.jsx)(t.em,{children:"Entity > Refresh Script Attributes"})," again, you\u2019ll see another attribute that we can change, and this time it\u2019s a check box. That\u2019s all a Boolean is \u2013 a true or a false value. Either off or on. If keyboard is ticked we\u2019ll use the keyboard input, and if it\u2019s not we\u2019ll use the mouse."]}),"\n",(0,a.jsxs)(t.p,{children:["To do things with the mouse in PlayCanvas, we have to use \u2013 you guessed it \u2013 ",(0,a.jsx)(t.code,{children:"context.mouse"}),"! Mouse input is going to be slightly more work than keyboard, but we\u2019ll be okay. We need to get the position of the mouse on screen, compare it to Santa\u2019s position, and move Santa towards the mouse."]}),"\n",(0,a.jsx)(t.p,{children:"So, we\u2019ll need a variable to store the mouse position, and we\u2019ll also need some way of getting the mouse position. For that, we\u2019ll need a listener."}),"\n",(0,a.jsx)(t.p,{children:"A listener is simply a function that we define, that gets called by a specific event. In this example, the event we want to \u201clisten\u201d for is the mouse moving."}),"\n",(0,a.jsx)(t.p,{children:"So, let\u2019s set this up:"}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-javascript",children:"// Called once after all resources are loaded\ninitialize: function () {\n //Used to store the mouse position\n this.pos = new pc.Vec3();\n context.mouse.on(pc.input.EVENT_MOUSEMOVE, this.onMouseMove, this);\n},\n"})}),"\n",(0,a.jsxs)(t.p,{children:["We\u2019ve got a variable called ",(0,a.jsx)(t.code,{children:"pos"})," that we\u2019ll store the current mouse position in. The next line is what creates our listener. We\u2019re saying when there\u2019s a mouse move event, we want a function called ",(0,a.jsx)(t.code,{children:"onMouseMove"})," to execute. Let\u2019s write that function now:"]}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-javascript",children:"moveDown: function(dt) {\n this.entity.translate(0, -this.speed * dt, 0);\n},\n\nonMouseMove: function () {\n // Use the camera component's screenToWorld function to convert the\n // position of the mouse into a position in 3D space\n var depth = 10;\n var cameraEntity = context.root.findByName('Camera');\n cameraEntity.camera.screenToWorld(event.x, event.y, depth, this.pos);\n}\n"})}),"\n",(0,a.jsxs)(t.p,{children:["In the ",(0,a.jsx)(t.code,{children:"onMouseMove"})," function, which gets called every time the mouse moves, we have to do some work to get the position we need. Because the mouse is only on a 2D screen, we have to look at use the camera\u2019s ",(0,a.jsx)(t.code,{children:"screenToWorld"})," function to get the position we want."]}),"\n",(0,a.jsxs)(t.p,{children:["Have a look at this ",(0,a.jsx)(t.a,{href:"https://developer.playcanvas.com/tutorials/mouse-input/",children:"mouse tutorial"})," if you want to know more about how this works!"]}),"\n",(0,a.jsxs)(t.p,{children:["Now we\u2019ve got the position of the mouse in ",(0,a.jsx)(t.code,{children:"this.pos"})," \u2013 we can use that to move Santa! Let\u2019s update our ",(0,a.jsx)(t.code,{children:"update"})," function:"]}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-javascript",children:"update: function (dt) {\n if(this.keyboard) {\n if(context.keyboard.isPressed(pc.input.KEY_W)) {\n this.moveUp(dt);\n }\n if(context.keyboard.isPressed(pc.input.KEY_S)) {\n this.moveDown(dt);\n }\n } else {\n if(this.pos.y > this.entity.getPosition().y) {\n this.moveUp(dt);\n }\n if(this.pos.y < this.entity.getPosition().y) {\n this.moveDown(dt);\n }\n }\n},\n"})}),"\n",(0,a.jsx)(t.p,{children:"First things first - we now check to see if we\u2019re using keyboard or mouse. If we\u2019re using the keyboard, we do exactly what we did before \u2013 otherwise, we can use the mouse for input."}),"\n",(0,a.jsxs)(t.p,{children:["We have the mouse\u2019s position stored in ",(0,a.jsx)(t.code,{children:"this.pos"})," \u2013 now all we have to do is compare it to Santa\u2019s position. If the mouse is above Santa, we move him up, and if it\u2019s below, we move him down."]}),"\n",(0,a.jsx)(t.p,{children:"Test the game and we should be able to use either the keyboard or the mouse to move Santa, depending on whether or not you\u2019ve ticked the keyboard box!"}),"\n",(0,a.jsx)(t.p,{children:"That\u2019s all for this time \u2013 next time we\u2019ll look at adding presents for Santa to collect!"})]})}function d(e={}){const{wrapper:t}={...(0,o.R)(),...e.components};return t?(0,a.jsx)(t,{...e,children:(0,a.jsx)(c,{...e})}):c(e)}},87562:(e,t,n)=>{n.d(t,{A:()=>a});const a=n.p+"assets/files/editor-santa-67a77497d4a37e4e9528cf58743d54da.jpg"},54080:(e,t,n)=>{n.d(t,{A:()=>a});const a=n.p+"assets/images/editor-santa-67a77497d4a37e4e9528cf58743d54da.jpg"},6694:(e,t,n)=>{n.d(t,{A:()=>a});const a=n.p+"assets/images/playhack-logo-xmas-451ccd5e2d37685bc0f90e7e5a066b00.jpg"},39914:(e,t,n)=>{n.d(t,{A:()=>a});const a=n.p+"assets/images/playhack-santa-44751e4b1ac1ee058e934d48dd0453f3.jpg"},28453:(e,t,n)=>{n.d(t,{R:()=>i,x:()=>r});var a=n(96540);const o={},s=a.createContext(o);function i(e){const t=a.useContext(s);return a.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function r(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:i(e.components),a.createElement(s.Provider,{value:t},e.children)}}}]);
\ No newline at end of file
diff --git a/assets/js/253ba14e.ce9e3123.js b/assets/js/253ba14e.ce9e3123.js
deleted file mode 100644
index 35e0a24fe..000000000
--- a/assets/js/253ba14e.ce9e3123.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(self.webpackChunk_playcanvas_blog=self.webpackChunk_playcanvas_blog||[]).push([[1168],{8e3:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>d,frontMatter:()=>s,metadata:()=>r,toc:()=>h});var a=n(74848),o=n(28453);const s={authors:"nathan",slug:"playhack-december-player-character",title:"PLAYHACK December - Player Character"},i=void 0,r={permalink:"/playhack-december-player-character",editUrl:"https://github.com/playcanvas/blog/tree/main/blog/2014-12-08-playhack-december-player-character.md",source:"@site/blog/2014-12-08-playhack-december-player-character.md",title:"PLAYHACK December - Player Character",description:"**PLAYHACK** is our fun monthly game building session. Throughout the month I'll be posting tips and tricks to help you get a game made by the end of the month.\xa0Don\u2019t forget, these are just examples. You can make **any** game you like. Read more about this month's PLAYHACK.",date:"2014-12-08T00:00:00.000Z",tags:[],readingTime:8.095,hasTruncateMarker:!0,authors:[{name:"Nathan",page:{permalink:"/authors/nathan"},key:"nathan"}],frontMatter:{authors:"nathan",slug:"playhack-december-player-character",title:"PLAYHACK December - Player Character"},unlisted:!1,prevItem:{title:"PLAYCANVAS in LUDUM DARE 31",permalink:"/playcanvas-in-ludum-dare-31"},nextItem:{title:"PlayCanvas Update 5/12/14",permalink:"/playcanvas-update-51214"}},l={authorsImageUrls:[void 0]},h=[{value:"Camera",id:"camera",level:2},{value:"Player",id:"player",level:2},{value:"Player Controller",id:"player-controller",level:2},{value:"Keyboard Controls",id:"keyboard-controls",level:2},{value:"Mouse Controls",id:"mouse-controls",level:2}];function c(e){const t={a:"a",code:"code",em:"em",h2:"h2",img:"img",p:"p",pre:"pre",strong:"strong",...(0,o.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(t.p,{children:(0,a.jsxs)(t.em,{children:[(0,a.jsx)(t.strong,{children:"PLAYHACK"})," is our fun monthly game building session. Throughout the month I'll be posting tips and tricks to help you get a game made by the end of the month.\xa0Don\u2019t forget, these are just examples. You can make ",(0,a.jsx)(t.strong,{children:"any"})," game you like. ",(0,a.jsx)(t.a,{href:"https://blog.playcanvas.com/playhack-december-jolly-santa/",children:"Read more"})," about this month's PLAYHACK."]})}),"\n",(0,a.jsx)(t.p,{children:(0,a.jsx)(t.img,{alt:"PLAYHACK Logo",src:n(6694).A+"",width:"603",height:"218"})}),"\n",(0,a.jsx)("div",{className:"iframe-container",children:(0,a.jsx)("iframe",{loading:"lazy",src:"https://playcanv.as/b/WbIFTpdn/",title:"360 lookaround camera",webkitallowfullscreen:"true",mozallowfullscreen:"true",allow:"autoplay",allowfullscreen:"true",allowvr:"",scrolling:"no",frameborder:"0"})}),"\n",(0,a.jsx)(t.p,{children:(0,a.jsx)(t.em,{children:"Use\xa0W and S to move the sleigh. Press SPACE to switch to mouse control."})}),"\n",(0,a.jsx)(t.p,{children:"In the coming weeks, I'll be taking you through how to make a complete game in PlayCanvas - from very beginning to very end, with only basic programming knowledge required!"}),"\n",(0,a.jsx)(t.p,{children:"We're going to be making a side-scrolling endless runner game, where Santa must pick up presents and avoid various things to deliver presents on time! However, the techniques we'll be using will be applicable to many kinds of games."}),"\n",(0,a.jsx)(t.p,{children:(0,a.jsx)(t.img,{alt:"Santa",src:n(39914).A+"",width:"1113",height:"727"})}),"\n",(0,a.jsxs)(t.p,{children:["To follow along with this tutorial, fork the ",(0,a.jsx)(t.a,{href:"https://playcanvas.com/project/333365/overview/playhack-dec-14",children:"PLAYHACK December project"}),"\xa0to get started. To see what we\u2019ll be making this time, ",(0,a.jsx)(t.a,{href:"https://playcanvas.com/project/333872/overview/playhack_tut1",children:"look here"}),". Or just check out the embedded app\xa0above."]}),"\n",(0,a.jsx)(t.p,{children:"So you've forked the repository and got it open in the editor. We're going to need to make a few changes to start making our game."}),"\n",(0,a.jsx)(t.h2,{id:"camera",children:"Camera"}),"\n",(0,a.jsx)(t.p,{children:'Firstly, the camera. There are two camera types in PlayCanvas - Perspective and Orthographic. Perspective is what you want for most 3D games \u2013 it\u2019s how things look in life.\nHowever, our game is pretty much 2D - which means an Orthographic projection would be best for us. To make the switch, select the Camera in the Pack Explorer, then in the Attribute Editor on the right, change Projection from "Perspective" to "Orthographic."'}),"\n",(0,a.jsx)(t.p,{children:"If you test now by pressing the Launch button, you'll see that Santa is tiny in the middle of the screen! That's not what we want. We can fix this by reducing the Ortho Height attribute of the camera. Reduce it from 100 down to about 10, and now check the game. Santa's now a much better size!"}),"\n",(0,a.jsx)(t.h2,{id:"player",children:"Player"}),"\n",(0,a.jsx)(t.p,{children:(0,a.jsx)(t.a,{target:"_blank","data-noBrokenLinkCheck":!0,href:n(87562).A+"",children:(0,a.jsx)(t.img,{alt:"Santa",src:n(54080).A+"",width:"600",height:"330"})})}),"\n",(0,a.jsx)(t.p,{children:"Now, we want Santa to start on the left of the screen, looking right. To make things easier for us, we'll align our camera along the Z-axis so that moving things around on screen becomes easier for us later."}),"\n",(0,a.jsx)(t.p,{children:"To do that, set the camera position to 0, 0, 10, and the rotation to 0, 0, 0. Santa should now be looking right at the camera in the middle of the screen. You can use the rotate and translate tools in the editor to move him to the right place, or you can select Santa_sleigh from the Pack Explorer and set his position to -17, 0, 0 and his rotation to 0, 90, 0. Great! Take a look at the game again. Santa's now in the right place, facing the right way. Lovely."}),"\n",(0,a.jsx)(t.p,{children:"Let's get Santa moving. We're going to create two ways for Santa to move - by mouse and by keyboard. Let's create a new script, called Santa_Controller, and attach it to the Santa_sleigh entity we have."}),"\n",(0,a.jsx)(t.p,{children:'To do this, right click on Santa_sleigh in the Pack Explorer, and select Add Component > Script. Now we can attach scripts to Santa. To attach a new script to Santa, simply type the name of the new script (in our case, "Santa_Controller") in the URL box of the Script Attribute we just added to Santa.'}),"\n",(0,a.jsx)(t.h2,{id:"player-controller",children:"Player Controller"}),"\n",(0,a.jsx)(t.p,{children:"Now we've created the new script and attached it to Santa, let's edit it and make Santa do something! Click on the blue Santa_Controller.js link that should have appeared below the URL box to get editing!"}),"\n",(0,a.jsx)(t.p,{children:"You'll be greeted by a new script that should look like this:"}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-javascript",children:"pc.script.create('Santa_Controller', function (context) {\n // Creates a new Santa_Controller instance\n var Santa_Controller = function (entity) {\n this.entity = entity;\n };\n\n Santa_Controller.prototype = {\n // Called once after all resources are loaded\n initialize: function () {\n },\n\n // Called every frame, dt is time in seconds since last update\n update: function (dt) {\n }\n };\n\n return Santa_Controller;\n});\n"})}),"\n",(0,a.jsx)(t.p,{children:"Let\u2019s think about what we need to do to make Santa move up and down. Well, we\u2019ll need the speed we want him to move \u2013 so let\u2019s put that as an attribute so we can change it easily."}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-javascript",children:'pc.script.attribute("speed", "number", 10);\npc.script.create(\'Santa_Controller\', function (context) {\n'})}),"\n",(0,a.jsx)(t.p,{children:"Now, let\u2019s make 2 functions \u2013 one that moves Santa up, and one that moves Santa down."}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-javascript",children:" // Called every frame, dt is time in seconds since last update\n update: function (dt) {\n },\n\n moveUp: function(dt) {\n this.entity.translate(0, this.speed * dt, 0);\n },\n\n moveDown: function(dt) {\n this.entity.translate(0, -this.speed * dt, 0);\n }\n};\n"})}),"\n",(0,a.jsxs)(t.p,{children:["The ",(0,a.jsx)(t.code,{children:"translate"})," function is one that all entities have, and simply moves the entity by a specified amount. In this case, we want Santa to move up or down by the speed, multiplied by the amount of time passed in the current frame. We do this so that Santa will move the same speed however fast our game is running!"]}),"\n",(0,a.jsx)(t.h2,{id:"keyboard-controls",children:"Keyboard Controls"}),"\n",(0,a.jsxs)(t.p,{children:["Now, let\u2019s look at keyboard controls. To do anything with the keyboard in PlayCanvas, we need to use ",(0,a.jsx)(t.code,{children:"context.keyboard"}),". ",(0,a.jsx)(t.code,{children:"context"})," is available in all scripts, and allows us access to all the data our game has to offer \u2013 in this case, we need the keyboard, which deals with keyboard input."]}),"\n",(0,a.jsxs)(t.p,{children:["We can use ",(0,a.jsx)(t.code,{children:"context.keyboard.isPressed"})," to check if specific keys are pressed \u2013 if W or S are pressed, we\u2019ll move Santa up or down accordingly. We\u2019ll need to check every frame, so put the following code in the update function:"]}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-javascript",children:"if (context.keyboard.isPressed(pc.input.KEY_W)) {\n this.moveUp(dt);\n}\nif (context.keyboard.isPressed(pc.input.KEY_S)) {\n this.moveDown(dt);\n}\n"})}),"\n",(0,a.jsxs)(t.p,{children:["Before we test the game, however, there\u2019s something we need to do. The editor needs to be told when we add an attribute to a script, so back in the editor, and go ",(0,a.jsx)(t.em,{children:"Entity > Refresh Script Attributes"}),". A box should come up underneath our Santa_Controller.js script allowing us to change the speed of Santa."]}),"\n",(0,a.jsx)(t.p,{children:"Now test the game \u2013 we should be able to move Santa up and down now using the W and S keys! Change the speed in the editor until you find something that feels fun."}),"\n",(0,a.jsx)(t.h2,{id:"mouse-controls",children:"Mouse Controls"}),"\n",(0,a.jsx)(t.p,{children:"That\u2019s all good, however, we want the option of using the mouse or the keyboard! Let\u2019s add another attribute in our script that will allow us to change between mouse and keyboard when testing:"}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-javascript",children:'pc.script.attribute("keyboard", "boolean", true);\n'})}),"\n",(0,a.jsxs)(t.p,{children:["If you do ",(0,a.jsx)(t.em,{children:"Entity > Refresh Script Attributes"})," again, you\u2019ll see another attribute that we can change, and this time it\u2019s a check box. That\u2019s all a Boolean is \u2013 a true or a false value. Either off or on. If keyboard is ticked we\u2019ll use the keyboard input, and if it\u2019s not we\u2019ll use the mouse."]}),"\n",(0,a.jsxs)(t.p,{children:["To do things with the mouse in PlayCanvas, we have to use \u2013 you guessed it \u2013 ",(0,a.jsx)(t.code,{children:"context.mouse"}),"! Mouse input is going to be slightly more work than keyboard, but we\u2019ll be okay. We need to get the position of the mouse on screen, compare it to Santa\u2019s position, and move Santa towards the mouse."]}),"\n",(0,a.jsx)(t.p,{children:"So, we\u2019ll need a variable to store the mouse position, and we\u2019ll also need some way of getting the mouse position. For that, we\u2019ll need a listener."}),"\n",(0,a.jsx)(t.p,{children:"A listener is simply a function that we define, that gets called by a specific event. In this example, the event we want to \u201clisten\u201d for is the mouse moving."}),"\n",(0,a.jsx)(t.p,{children:"So, let\u2019s set this up:"}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-javascript",children:"// Called once after all resources are loaded\ninitialize: function () {\n //Used to store the mouse position\n this.pos = new pc.Vec3();\n context.mouse.on(pc.input.EVENT_MOUSEMOVE, this.onMouseMove, this);\n},\n"})}),"\n",(0,a.jsxs)(t.p,{children:["We\u2019ve got a variable called ",(0,a.jsx)(t.code,{children:"pos"})," that we\u2019ll store the current mouse position in. The next line is what creates our listener. We\u2019re saying when there\u2019s a mouse move event, we want a function called ",(0,a.jsx)(t.code,{children:"onMouseMove"})," to execute. Let\u2019s write that function now:"]}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-javascript",children:"moveDown: function(dt) {\n this.entity.translate(0, -this.speed * dt, 0);\n},\n\nonMouseMove: function () {\n // Use the camera component's screenToWorld function to convert the\n // position of the mouse into a position in 3D space\n var depth = 10;\n var cameraEntity = context.root.findByName('Camera');\n cameraEntity.camera.screenToWorld(event.x, event.y, depth, this.pos);\n}\n"})}),"\n",(0,a.jsxs)(t.p,{children:["In the ",(0,a.jsx)(t.code,{children:"onMouseMove"})," function, which gets called every time the mouse moves, we have to do some work to get the position we need. Because the mouse is only on a 2D screen, we have to look at use the camera\u2019s ",(0,a.jsx)(t.code,{children:"screenToWorld"})," function to get the position we want."]}),"\n",(0,a.jsxs)(t.p,{children:["Have a look at this ",(0,a.jsx)(t.a,{href:"https://developer.playcanvas.com/tutorials/mouse-input/",children:"mouse tutorial"})," if you want to know more about how this works!"]}),"\n",(0,a.jsxs)(t.p,{children:["Now we\u2019ve got the position of the mouse in ",(0,a.jsx)(t.code,{children:"this.pos"})," \u2013 we can use that to move Santa! Let\u2019s update our ",(0,a.jsx)(t.code,{children:"update"})," function:"]}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-javascript",children:"update: function (dt) {\n if(this.keyboard) {\n if(context.keyboard.isPressed(pc.input.KEY_W)) {\n this.moveUp(dt);\n }\n if(context.keyboard.isPressed(pc.input.KEY_S)) {\n this.moveDown(dt);\n }\n } else {\n if(this.pos.y > this.entity.getPosition().y) {\n this.moveUp(dt);\n }\n if(this.pos.y < this.entity.getPosition().y) {\n this.moveDown(dt);\n }\n }\n},\n"})}),"\n",(0,a.jsx)(t.p,{children:"First things first - we now check to see if we\u2019re using keyboard or mouse. If we\u2019re using the keyboard, we do exactly what we did before \u2013 otherwise, we can use the mouse for input."}),"\n",(0,a.jsxs)(t.p,{children:["We have the mouse\u2019s position stored in ",(0,a.jsx)(t.code,{children:"this.pos"})," \u2013 now all we have to do is compare it to Santa\u2019s position. If the mouse is above Santa, we move him up, and if it\u2019s below, we move him down."]}),"\n",(0,a.jsx)(t.p,{children:"Test the game and we should be able to use either the keyboard or the mouse to move Santa, depending on whether or not you\u2019ve ticked the keyboard box!"}),"\n",(0,a.jsx)(t.p,{children:"That\u2019s all for this time \u2013 next time we\u2019ll look at adding presents for Santa to collect!"})]})}function d(e={}){const{wrapper:t}={...(0,o.R)(),...e.components};return t?(0,a.jsx)(t,{...e,children:(0,a.jsx)(c,{...e})}):c(e)}},87562:(e,t,n)=>{n.d(t,{A:()=>a});const a=n.p+"assets/files/editor-santa-67a77497d4a37e4e9528cf58743d54da.jpg"},54080:(e,t,n)=>{n.d(t,{A:()=>a});const a=n.p+"assets/images/editor-santa-67a77497d4a37e4e9528cf58743d54da.jpg"},6694:(e,t,n)=>{n.d(t,{A:()=>a});const a=n.p+"assets/images/playhack-logo-xmas-451ccd5e2d37685bc0f90e7e5a066b00.jpg"},39914:(e,t,n)=>{n.d(t,{A:()=>a});const a=n.p+"assets/images/playhack-santa-44751e4b1ac1ee058e934d48dd0453f3.jpg"},28453:(e,t,n)=>{n.d(t,{R:()=>i,x:()=>r});var a=n(96540);const o={},s=a.createContext(o);function i(e){const t=a.useContext(s);return a.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function r(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:i(e.components),a.createElement(s.Provider,{value:t},e.children)}}}]);
\ No newline at end of file
diff --git a/assets/js/3f9ae9f6.2916bee0.js b/assets/js/3f9ae9f6.56d0711d.js
similarity index 57%
rename from assets/js/3f9ae9f6.2916bee0.js
rename to assets/js/3f9ae9f6.56d0711d.js
index 42844bcaf..8c29e1874 100644
--- a/assets/js/3f9ae9f6.2916bee0.js
+++ b/assets/js/3f9ae9f6.56d0711d.js
@@ -1 +1 @@
-"use strict";(self.webpackChunk_playcanvas_blog=self.webpackChunk_playcanvas_blog||[]).push([[43032],{18322:i=>{i.exports=JSON.parse('{"authors":[{"name":"Adam Razzak","title":"Intern","page":{"permalink":"/authors/adam"},"socials":{"linkedin":"https://www.linkedin.com/in/adam-razzak/"},"imageURL":"https://media.licdn.com/dms/image/v2/C4D03AQGHCOiKWo7s_A/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1622574917949?e=1730332800&v=beta&t=5uT_foApGCHnZNHhvyQUvZS1521v2FB5ub571XxO_6s","key":"adam","count":6},{"name":"Christy O\'Connor","title":"Community Manager","page":{"permalink":"/authors/christy"},"socials":{"x":"https://x.com/christy_oconnor","linkedin":"https://www.linkedin.com/in/christy-o-connor-76aa2239/"},"imageURL":"https://media.licdn.com/dms/image/v2/C4D03AQFn6GYclP_OEg/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1613509676962?e=1730332800&v=beta&t=0sYsgQcijA_JtsZ2TtzsWXtjQfF7zvN5pW3N0EeHBpc","key":"christy","count":21},{"name":"Dave Evans","title":"CTO","page":{"permalink":"/authors/dave"},"socials":{"x":"https://x.com/daredevildave","linkedin":"https://www.linkedin.com/in/davewevans/","github":"https://github.com/daredevildave"},"imageURL":"https://media.licdn.com/dms/image/v2/D5603AQENzddgxVV7dA/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1701843222003?e=1730332800&v=beta&t=j98lgSCe6uWB9QD2FU0NVEuZKivY6M4JjkVBUpc9Q6E","key":"dave","count":97},{"name":"Donovan Hutchence","title":"Staff Software Engineer","page":{"permalink":"/authors/donovan"},"socials":{"x":"https://x.com/slimbuck7","linkedin":"https://www.linkedin.com/in/dhutchence/","github":"https://github.com/slimbuck"},"imageURL":"https://media.licdn.com/dms/image/v2/C4D03AQE9f98jnVdkOQ/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1587112296905?e=1730332800&v=beta&t=gSIRVC1dWfjFPeSHQRt4YeEygFoD0e4s8xbHu7woGoY","key":"donovan","count":1},{"name":"Elliott Thompson","title":"Software Engineer","page":{"permalink":"/authors/elliott"},"socials":{"x":"https://x.com/elliott_thomps","linkedin":"https://www.linkedin.com/in/ellt92/","github":"https://github.com/ellthompson"},"imageURL":"https://media.licdn.com/dms/image/v2/D4E03AQFo37-S1i9r9Q/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1720644467185?e=1730332800&v=beta&t=TOGCSTz8FUhh_bJCD7FBZ5jIjsHuLqx-kr56DLOyclw","key":"elliott","count":4},{"name":"Mark Lundin","title":"Software Engineer","page":{"permalink":"/authors/mark"},"socials":{"x":"https://x.com/mark_lundin","linkedin":"https://www.linkedin.com/in/marklundin2/","github":"https://github.com/marklundin"},"imageURL":"https://media.licdn.com/dms/image/v2/C5603AQF-25U0u8HhVA/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1632995375227?e=1730332800&v=beta&t=mEnvFh7BINcBDtz7_F0_Sv_V9FKtzZUdCAKWyTZ-Qco","key":"mark","count":1},{"name":"Martin Valigursky","title":"Software Engineer","page":{"permalink":"/authors/martin"},"socials":{"x":"https://x.com/ValigurskyM","linkedin":"https://www.linkedin.com/in/martin-valigursky/","github":"https://github.com/mvaligursky"},"imageURL":"https://media.licdn.com/dms/image/v2/C4D03AQE4bZqBMHaTuw/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1589921553525?e=1730332800&v=beta&t=RekQph-b_JA1byxs2u6gCIGCWESMP7q5w3vW7VHEooI","key":"martin","count":1},{"name":"Nathan","page":{"permalink":"/authors/nathan"},"key":"nathan","count":3},{"name":"Paulo Oliveira","title":"Associate Partner Support Engineer","page":{"permalink":"/authors/paulo"},"socials":{"linkedin":"https://www.linkedin.com/in/paulo-oliveira-ninitoph/"},"imageURL":"https://media.licdn.com/dms/image/v2/C4D03AQGBCxiefbbUlw/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1654615914498?e=1730332800&v=beta&t=430DvI8uuos5obx7BhhHBqhT5TnNTv3q91eOOg6diII","key":"paulo","count":3},{"name":"Ray Tran","title":"Software Engineer","page":{"permalink":"/authors/ray"},"socials":{"x":"https://x.com/RayT_uk","linkedin":"https://www.linkedin.com/in/raytranuk/","github":"https://github.com/raytranuk"},"imageURL":"https://media.licdn.com/dms/image/v2/C4E03AQEAuMhkp3w-HA/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1517669131492?e=1730332800&v=beta&t=t1rW484XEHk8WHRH1qunKXtdiUekN_bm9CVuK6izXjU","key":"ray","count":1},{"name":"Steven Yau","title":"Partner Relations Manager","page":{"permalink":"/authors/steven"},"socials":{"x":"https://x.com/yaustar","linkedin":"https://www.linkedin.com/in/stevenyau/","github":"https://github.com/yaustar"},"imageURL":"https://media.licdn.com/dms/image/v2/D4E03AQE3XHwaxq_kNw/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1688384458829?e=1730332800&v=beta&t=9FiqE0-B2UZTHVhkbbT8xgdBF62LkcApkcP9HOvuOUM","key":"steven","count":25},{"name":"Will Eastcott","title":"CEO","description":"Will Eastcott is an entrepreneur and veteran technologist of the games industry with experience at EA, Sony, and Activision. He has been credited in many AAA game franchises such as GTA, Call of Duty and Max Payne. He is best known for co-founding PlayCanvas, the web graphics creation platform. As CEO, he has championed the company\'s mission to make graphical web app development more accessible and collaborative through open-source technologies and cloud-based tools.","page":{"permalink":"/authors/will"},"socials":{"x":"https://x.com/willeastcott","linkedin":"https://www.linkedin.com/in/willeastcott/","github":"https://github.com/willeastcott"},"imageURL":"https://media.licdn.com/dms/image/v2/D4D03AQF9YXIeHZW9kg/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1688995668268?e=1730332800&v=beta&t=UmAwlJ0nPBmnMczQ4wknx1jSDJ2anqurm2b15iLm3pM","key":"will","count":72}]}')}}]);
\ No newline at end of file
+"use strict";(self.webpackChunk_playcanvas_blog=self.webpackChunk_playcanvas_blog||[]).push([[43032],{18322:i=>{i.exports=JSON.parse('{"authors":[{"name":"Adam Razzak","title":"Intern","page":{"permalink":"/authors/adam"},"socials":{"linkedin":"https://www.linkedin.com/in/adam-razzak/"},"imageURL":"https://media.licdn.com/dms/image/v2/C4D03AQGHCOiKWo7s_A/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1622574917949?e=1730332800&v=beta&t=5uT_foApGCHnZNHhvyQUvZS1521v2FB5ub571XxO_6s","key":"adam","count":6},{"name":"Christy O\'Connor","title":"Community Manager","page":{"permalink":"/authors/christy"},"socials":{"x":"https://x.com/christy_oconnor","linkedin":"https://www.linkedin.com/in/christy-o-connor-76aa2239/"},"imageURL":"https://media.licdn.com/dms/image/v2/C4D03AQFn6GYclP_OEg/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1613509676962?e=1730332800&v=beta&t=0sYsgQcijA_JtsZ2TtzsWXtjQfF7zvN5pW3N0EeHBpc","key":"christy","count":21},{"name":"Dave Evans","title":"CTO","page":{"permalink":"/authors/dave"},"socials":{"x":"https://x.com/daredevildave","linkedin":"https://www.linkedin.com/in/davewevans/","github":"https://github.com/daredevildave"},"imageURL":"https://media.licdn.com/dms/image/v2/D5603AQENzddgxVV7dA/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1701843222003?e=1730332800&v=beta&t=j98lgSCe6uWB9QD2FU0NVEuZKivY6M4JjkVBUpc9Q6E","key":"dave","count":97},{"name":"Donovan Hutchence","title":"Staff Software Engineer","page":{"permalink":"/authors/donovan"},"socials":{"x":"https://x.com/slimbuck7","linkedin":"https://www.linkedin.com/in/dhutchence/","github":"https://github.com/slimbuck"},"imageURL":"https://media.licdn.com/dms/image/v2/C4D03AQE9f98jnVdkOQ/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1587112296905?e=1730332800&v=beta&t=gSIRVC1dWfjFPeSHQRt4YeEygFoD0e4s8xbHu7woGoY","key":"donovan","count":1},{"name":"Elliott Thompson","title":"Software Engineer","page":{"permalink":"/authors/elliott"},"socials":{"x":"https://x.com/elliott_thomps","linkedin":"https://www.linkedin.com/in/ellt92/","github":"https://github.com/ellthompson"},"imageURL":"https://media.licdn.com/dms/image/v2/D4E03AQFo37-S1i9r9Q/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1720644467185?e=1730332800&v=beta&t=TOGCSTz8FUhh_bJCD7FBZ5jIjsHuLqx-kr56DLOyclw","key":"elliott","count":4},{"name":"Mark Lundin","title":"Software Engineer","page":{"permalink":"/authors/mark"},"socials":{"x":"https://x.com/mark_lundin","linkedin":"https://www.linkedin.com/in/marklundin2/","github":"https://github.com/marklundin"},"imageURL":"https://media.licdn.com/dms/image/v2/C5603AQF-25U0u8HhVA/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1632995375227?e=1730332800&v=beta&t=mEnvFh7BINcBDtz7_F0_Sv_V9FKtzZUdCAKWyTZ-Qco","key":"mark","count":1},{"name":"Martin Valigursky","title":"Software Engineer","page":{"permalink":"/authors/martin"},"socials":{"x":"https://x.com/ValigurskyM","linkedin":"https://www.linkedin.com/in/martin-valigursky/","github":"https://github.com/mvaligursky"},"imageURL":"https://media.licdn.com/dms/image/v2/C4D03AQE4bZqBMHaTuw/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1589921553525?e=1730332800&v=beta&t=RekQph-b_JA1byxs2u6gCIGCWESMP7q5w3vW7VHEooI","key":"martin","count":1},{"name":"Nathan Patel","title":"Technical Blogger","page":{"permalink":"/authors/nathan"},"key":"nathan","count":3},{"name":"Paulo Oliveira","title":"Associate Partner Support Engineer","page":{"permalink":"/authors/paulo"},"socials":{"linkedin":"https://www.linkedin.com/in/paulo-oliveira-ninitoph/"},"imageURL":"https://media.licdn.com/dms/image/v2/C4D03AQGBCxiefbbUlw/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1654615914498?e=1730332800&v=beta&t=430DvI8uuos5obx7BhhHBqhT5TnNTv3q91eOOg6diII","key":"paulo","count":3},{"name":"Ray Tran","title":"Software Engineer","page":{"permalink":"/authors/ray"},"socials":{"x":"https://x.com/RayT_uk","linkedin":"https://www.linkedin.com/in/raytranuk/","github":"https://github.com/raytranuk"},"imageURL":"https://media.licdn.com/dms/image/v2/C4E03AQEAuMhkp3w-HA/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1517669131492?e=1730332800&v=beta&t=t1rW484XEHk8WHRH1qunKXtdiUekN_bm9CVuK6izXjU","key":"ray","count":1},{"name":"Steven Yau","title":"Partner Relations Manager","page":{"permalink":"/authors/steven"},"socials":{"x":"https://x.com/yaustar","linkedin":"https://www.linkedin.com/in/stevenyau/","github":"https://github.com/yaustar"},"imageURL":"https://media.licdn.com/dms/image/v2/D4E03AQE3XHwaxq_kNw/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1688384458829?e=1730332800&v=beta&t=9FiqE0-B2UZTHVhkbbT8xgdBF62LkcApkcP9HOvuOUM","key":"steven","count":25},{"name":"Will Eastcott","title":"CEO","description":"Will Eastcott is an entrepreneur and veteran technologist of the games industry with experience at EA, Sony, and Activision. He has been credited in many AAA game franchises such as GTA, Call of Duty and Max Payne. He is best known for co-founding PlayCanvas, the web graphics creation platform. As CEO, he has championed the company\'s mission to make graphical web app development more accessible and collaborative through open-source technologies and cloud-based tools.","page":{"permalink":"/authors/will"},"socials":{"x":"https://x.com/willeastcott","linkedin":"https://www.linkedin.com/in/willeastcott/","github":"https://github.com/willeastcott"},"imageURL":"https://media.licdn.com/dms/image/v2/D4D03AQF9YXIeHZW9kg/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1688995668268?e=1730332800&v=beta&t=UmAwlJ0nPBmnMczQ4wknx1jSDJ2anqurm2b15iLm3pM","key":"will","count":72}]}')}}]);
\ No newline at end of file
diff --git a/assets/js/441ad90d.0c6fb981.js b/assets/js/441ad90d.0c6fb981.js
new file mode 100644
index 000000000..a1b1c3e05
--- /dev/null
+++ b/assets/js/441ad90d.0c6fb981.js
@@ -0,0 +1 @@
+"use strict";(self.webpackChunk_playcanvas_blog=self.webpackChunk_playcanvas_blog||[]).push([[22836],{93086:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>h,contentTitle:()=>r,default:()=>d,frontMatter:()=>i,metadata:()=>o,toc:()=>c});var s=n(74848),a=n(28453);const i={authors:"nathan",slug:"playhack-december-creating-presents",title:"PLAYHACK December - Creating Presents"},r=void 0,o={permalink:"/playhack-december-creating-presents",editUrl:"https://github.com/playcanvas/blog/tree/main/blog/2014-12-12-playhack-december-creating-presents.md",source:"@site/blog/2014-12-12-playhack-december-creating-presents.md",title:"PLAYHACK December - Creating Presents",description:"**PLAYHACK** is our fun monthly game building session. Throughout the month I\u2019ll be posting tips and tricks to help you get a game made by the end of the month.\xa0Don\u2019t forget, these are just examples. You can make **any** game you like. Read more about this month\u2019s PLAYHACK.",date:"2014-12-12T00:00:00.000Z",tags:[],readingTime:6.54,hasTruncateMarker:!0,authors:[{name:"Nathan Patel",title:"Technical Blogger",page:{permalink:"/authors/nathan"},key:"nathan"}],frontMatter:{authors:"nathan",slug:"playhack-december-creating-presents",title:"PLAYHACK December - Creating Presents"},unlisted:!1,prevItem:{title:"PLAYHACK December - Collecting Presents",permalink:"/playhack-december-collecting-presents"},nextItem:{title:"Physically Based Rendering comes to WebGL",permalink:"/physically-based-rendering-comes-to-webgl"}},h={authorsImageUrls:[void 0]},c=[{value:"Moving the Presents",id:"moving-the-presents",level:3},{value:"Creating Many Presents",id:"creating-many-presents",level:3},{value:"Housekeeping",id:"housekeeping",level:3}];function l(e){const t={a:"a",code:"code",em:"em",h3:"h3",img:"img",p:"p",pre:"pre",strong:"strong",...(0,a.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(t.p,{children:(0,s.jsxs)(t.em,{children:[(0,s.jsx)(t.strong,{children:"PLAYHACK"})," is our fun monthly game building session. Throughout the month I\u2019ll be posting tips and tricks to help you get a game made by the end of the month.\xa0Don\u2019t forget, these are just examples. You can make ",(0,s.jsx)(t.strong,{children:"any"})," game you like. ",(0,s.jsx)(t.a,{href:"https://blog.playcanvas.com/playhack-december-jolly-santa/",children:"Read more"})," about this month\u2019s PLAYHACK."]})}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.img,{alt:"PLAYHACK Logo",src:n(6694).A+"",width:"603",height:"218"})}),"\n",(0,s.jsxs)(t.p,{children:["We'll be following on from ",(0,s.jsx)(t.a,{href:"https://blog.playcanvas.com/playhack-december-player-character/",children:"last time's tutorial"})," this time, so make sure you've followed that one before attempting this!"]}),"\n",(0,s.jsx)("div",{className:"iframe-container",children:(0,s.jsx)("iframe",{loading:"lazy",src:"https://playcanv.as/b/eyPboW2T/",title:"360 lookaround camera",webkitallowfullscreen:"true",mozallowfullscreen:"true",allow:"autoplay",allowfullscreen:"true",allowvr:"",scrolling:"no",frameborder:"0"})}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.em,{children:"Use W and S to move the sleigh."})}),"\n",(0,s.jsx)(t.p,{children:"In this tutorial, we're going to program the gifts\xa0that will come in from the right for Santa to collect."}),"\n",(0,s.jsx)(t.h3,{id:"moving-the-presents",children:"Moving the Presents"}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.a,{target:"_blank","data-noBrokenLinkCheck":!0,href:n(74624).A+"",children:(0,s.jsx)(t.img,{alt:"Santa and the Present",src:n(53934).A+"",width:"862",height:"469"})})}),"\n",(0,s.jsx)(t.p,{children:"To start with, we'll need to create a script that will make the presents move across the screen towards Santa. We're also going to make the presents rotate, just because it looks nice. To do that, we need to attach a new script to the Gift entity, just like we did last time with Santa. Call it something like 'gift_controller'."}),"\n",(0,s.jsx)(t.p,{children:"Now, let's think about what we need - just like with Santa's script, we're going to need the speed we want the presents to move, and we'll also need the speed we want the presents to rotate. So let's make two attributes for that:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-javascript",children:'pc.script.attribute("speed", "number", 10);\npc.script.attribute("rotspeed", "number", 90);\n'})}),"\n",(0,s.jsxs)(t.p,{children:["Put them at the top of the script like before (remember to do ",(0,s.jsx)(t.em,{children:"Entity > Refresh Script Attributes"})," when you come to test!)."]}),"\n",(0,s.jsx)(t.p,{children:"Now we need to move the gift every frame. This is again, very similar to the Santa movement code:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-javascript",children:"// Called every frame, dt is time in seconds since last update\nupdate: function (dt) {\n this.entity.translate(0, 0, -this.speed*dt);\n this.entity.rotate(0, this.rotspeed*dt, 0);\n}\n"})}),"\n",(0,s.jsxs)(t.p,{children:["To move the present, we use the ",(0,s.jsx)(t.code,{children:"entity.translate"})," function, as before. To rotate, we use the ",(0,s.jsx)(t.code,{children:"entity.rotate"})," function, which as its name suggests, rotates the present by a certain amount."]}),"\n",(0,s.jsxs)(t.p,{children:["If you launch the game (remembering to do ",(0,s.jsx)(t.em,{children:"Entity > Refresh Script Attributes"}),"!) then the present in the middle of the screen should hopefully move offscreen whilst rotating. Success!"]}),"\n",(0,s.jsx)(t.h3,{id:"creating-many-presents",children:"Creating Many Presents"}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.a,{target:"_blank","data-noBrokenLinkCheck":!0,href:n(97974).A+"",children:(0,s.jsx)(t.img,{alt:"Many Presents",src:n(8).A+"",width:"1440",height:"784"})})}),"\n",(0,s.jsx)(t.p,{children:"Now, we'll need to create a script that will create presents every so often off the right hand edge of the screen. This might seem odd - up until now we've only created scripts that do things to entities already on screen. To do this, we're going to attach a script to the root entity of the game - which is the entity called \"Santa\" in the pack explorer. Add a script to the root entity called 'gift_creator'."}),"\n",(0,s.jsxs)(t.p,{children:["So, let's think about how to go about this. We've got quite a few things to think about. Firstly, how do we add new presents? We can achieve this by cloning the gift that's already on screen. To do this, we need a reference to that present. In the ",(0,s.jsx)(t.code,{children:"initialize"})," method, add the following code:"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-javascript",children:'this.gift = context.root.findByName("Gift");\n'})}),"\n",(0,s.jsxs)(t.p,{children:["Now we have a variable, ",(0,s.jsx)(t.code,{children:"this.gift"}),", which contains the gift that is on screen. We'll get on to how to clone it in a second."]}),"\n",(0,s.jsx)(t.p,{children:"What else do we have to think about? Two main things - where we want to add the new presents, and how often we want to add them. We're going to have an element of randomness to where and when we add the presents - we want the presents to come in from the right at different heights, and we also don't want the presents to come in like clockwork - we want to keep the player on his toes and have them spawn with slightly different amounts of time between them."}),"\n",(0,s.jsx)(t.p,{children:"We'll add some new attributes so we can easily test and change these things:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-javascript",children:'pc.script.attribute("avgPresentTime", "number", 0.75);\npc.script.attribute("presentRange", "number", 0.5);\npc.script.attribute("maxY", "number", 8);\npc.script.attribute("offscreenZ", "number", 20);\n'})}),"\n",(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.code,{children:"avgPresentTime"})," is the average amount of time between each present coming spawning,\xa0and ",(0,s.jsx)(t.code,{children:"presentRange"})," is how many seconds around the average time presents could come in. ",(0,s.jsx)(t.code,{children:"maxY"})," is the highest position a present could come in at, and ",(0,s.jsx)(t.code,{children:"offscreenZ"})," is the z position we are going to create our new presents at."]}),"\n",(0,s.jsx)(t.p,{children:"So, now we've got all these attributes, what do we need to do? We need some sort of timer than allows us to check whether or not to add a new present yet. Let's add some variables so we can do that:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-javascript",children:'initialize: function () {\n this.gift = context.root.findByName("Gift");\n this.presentTimer = 0;\n this.presentTimeToGet = 0;\n},\n'})}),"\n",(0,s.jsxs)(t.p,{children:["The first line we added earlier - but we've now got two new variables, ",(0,s.jsx)(t.code,{children:"presentTimer"})," and ",(0,s.jsx)(t.code,{children:"presentTimeToGet"}),". We will use ",(0,s.jsx)(t.code,{children:"presentTimer"})," to count how long it's been since we last added a present, and ",(0,s.jsx)(t.code,{children:"presentTimeToGet"})," will be how long we need to wait until adding the next present."]}),"\n",(0,s.jsxs)(t.p,{children:["So, we now know that every frame, we need to increment ",(0,s.jsx)(t.code,{children:"this.presentTimer"})," by the amount of time passed since the last frame - which is helpfully passed into the ",(0,s.jsx)(t.code,{children:"update"})," function as the variable ",(0,s.jsx)(t.code,{children:"dt"}),"."]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-javascript",children:"update: function (dt) {\n this.presentTimer += dt;\n if(this.presentTimer >= this.presentTimeToGet) {\n //Create a new gift\n }\n}\n"})}),"\n",(0,s.jsx)(t.p,{children:"So, now we know when we're adding a new gift. To add a new gift to the game, we can use the following code:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-javascript",children:"var newGift = this.gift.clone();\nnewGift.enabled = true;\ncontext.root.addChild(newGift);\n"})}),"\n",(0,s.jsxs)(t.p,{children:["This clones ",(0,s.jsx)(t.code,{children:"this.gift"}),", and stores the new gift in the variable ",(0,s.jsx)(t.code,{children:"newGift"}),". We then have to do two things to actually get the gift in game - we have to enable it, and then add it to the entity hierarchy."]}),"\n",(0,s.jsx)(t.p,{children:"Now we have a new gift in the game - however, it's at the same position as the first gift! That's not good. We want it to be offscreen at a random height to start with! Let's fix that."}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-javascript",children:"// Set its position correctly\nvar rand = Math.random() * 2 - 1;\nvar newY = rand * maxY;\nnewGift.setPosition(0, newY, this.offscreenZ);\n"})}),"\n",(0,s.jsxs)(t.p,{children:["There's a few new things going on here! First, let's look at the randomness. ",(0,s.jsx)(t.code,{children:"Math.random()"})," is a built in JavaScript function that returns a random number from 0 to 1. We, however, want a random number from ",(0,s.jsx)(t.code,{children:"maxY"})," to negative ",(0,s.jsx)(t.code,{children:"maxY"}),". To achieve this, we multiply our random number by 2 (giving us a number between 0 and 2), then take away 1 (between -1 and 1), then multiply it by maxY (between ",(0,s.jsx)(t.code,{children:"-maxY"})," and ",(0,s.jsx)(t.code,{children:"maxY"}),"). Once we have the random ",(0,s.jsx)(t.code,{children:"newY"})," position, we can set the position of the ",(0,s.jsx)(t.code,{children:"newGift"}),"."]}),"\n",(0,s.jsxs)(t.p,{children:["Now, we're very almost done. However, we've now added a present, so we need to reset ",(0,s.jsx)(t.code,{children:"presentTimer"})," to 0, as it's been 0 seconds since we added a present! We also want to change ",(0,s.jsx)(t.code,{children:"presentTimeToGet"}),", as we want to wait a slightly different length of time before adding another present."]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-javascript",children:"// And reset the timer\nthis.presentTimer = 0;\nthis.presentTimeToGet = this.avgPresentTime + this.presentRange * (Math.random() - 0.5);\n"})}),"\n",(0,s.jsxs)(t.p,{children:["We use ",(0,s.jsx)(t.code,{children:"Math.random()"})," again here to get the time before we want to add another present. This gives us a number that will be no more than half of ",(0,s.jsx)(t.code,{children:"presentRange"})," away from ",(0,s.jsx)(t.code,{children:"avgPresentTime"}),"."]}),"\n",(0,s.jsxs)(t.p,{children:["That should be it! Test your code (REMEMBER ",(0,s.jsx)(t.em,{children:"Entity Menu > Refresh Script Attributes"}),"), and presents should start coming in from the right. Perfect!"]}),"\n",(0,s.jsx)(t.h3,{id:"housekeeping",children:"Housekeeping"}),"\n",(0,s.jsx)(t.p,{children:"One last thing - we've left the sack floating in the middle of the screen, and we don't really want the first present to start in the middle of the screen either.\xa0To fix this, simply select the gift/sack, and uncheck the 'Enabled' attribute. That's it! The initial sack and gift will no longer\xa0show up in the editor or when you test the game."}),"\n",(0,s.jsxs)(t.p,{children:["Next time, we'll be looking at ",(0,s.jsx)(t.a,{href:"https://developer.playcanvas.com/tutorials/collision-and-triggers/",children:"Trigger Volumes"}),", which will allow us to have Santa actually collect the presents when he touches them."]})]})}function d(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,s.jsx)(t,{...e,children:(0,s.jsx)(l,{...e})}):l(e)}},97974:(e,t,n)=>{n.d(t,{A:()=>s});const s=n.p+"assets/files/Screen-Shot-2014-11-24-at-15.58.42-0fa83dd1712c5252016697245a7dc84d.png"},74624:(e,t,n)=>{n.d(t,{A:()=>s});const s=n.p+"assets/files/editor-santa-and-present-582b7c8b13122c206cfa31a24978f695.png"},8:(e,t,n)=>{n.d(t,{A:()=>s});const s=n.p+"assets/images/Screen-Shot-2014-11-24-at-15.58.42-0fa83dd1712c5252016697245a7dc84d.png"},53934:(e,t,n)=>{n.d(t,{A:()=>s});const s=n.p+"assets/images/editor-santa-and-present-582b7c8b13122c206cfa31a24978f695.png"},6694:(e,t,n)=>{n.d(t,{A:()=>s});const s=n.p+"assets/images/playhack-logo-xmas-451ccd5e2d37685bc0f90e7e5a066b00.jpg"},28453:(e,t,n)=>{n.d(t,{R:()=>r,x:()=>o});var s=n(96540);const a={},i=s.createContext(a);function r(e){const t=s.useContext(i);return s.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function o(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:r(e.components),s.createElement(i.Provider,{value:t},e.children)}}}]);
\ No newline at end of file
diff --git a/assets/js/441ad90d.e66c0dda.js b/assets/js/441ad90d.e66c0dda.js
deleted file mode 100644
index b6435a9b7..000000000
--- a/assets/js/441ad90d.e66c0dda.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(self.webpackChunk_playcanvas_blog=self.webpackChunk_playcanvas_blog||[]).push([[22836],{93086:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>h,contentTitle:()=>r,default:()=>d,frontMatter:()=>i,metadata:()=>o,toc:()=>c});var s=n(74848),a=n(28453);const i={authors:"nathan",slug:"playhack-december-creating-presents",title:"PLAYHACK December - Creating Presents"},r=void 0,o={permalink:"/playhack-december-creating-presents",editUrl:"https://github.com/playcanvas/blog/tree/main/blog/2014-12-12-playhack-december-creating-presents.md",source:"@site/blog/2014-12-12-playhack-december-creating-presents.md",title:"PLAYHACK December - Creating Presents",description:"**PLAYHACK** is our fun monthly game building session. Throughout the month I\u2019ll be posting tips and tricks to help you get a game made by the end of the month.\xa0Don\u2019t forget, these are just examples. You can make **any** game you like. Read more about this month\u2019s PLAYHACK.",date:"2014-12-12T00:00:00.000Z",tags:[],readingTime:6.54,hasTruncateMarker:!0,authors:[{name:"Nathan",page:{permalink:"/authors/nathan"},key:"nathan"}],frontMatter:{authors:"nathan",slug:"playhack-december-creating-presents",title:"PLAYHACK December - Creating Presents"},unlisted:!1,prevItem:{title:"PLAYHACK December - Collecting Presents",permalink:"/playhack-december-collecting-presents"},nextItem:{title:"Physically Based Rendering comes to WebGL",permalink:"/physically-based-rendering-comes-to-webgl"}},h={authorsImageUrls:[void 0]},c=[{value:"Moving the Presents",id:"moving-the-presents",level:3},{value:"Creating Many Presents",id:"creating-many-presents",level:3},{value:"Housekeeping",id:"housekeeping",level:3}];function l(e){const t={a:"a",code:"code",em:"em",h3:"h3",img:"img",p:"p",pre:"pre",strong:"strong",...(0,a.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(t.p,{children:(0,s.jsxs)(t.em,{children:[(0,s.jsx)(t.strong,{children:"PLAYHACK"})," is our fun monthly game building session. Throughout the month I\u2019ll be posting tips and tricks to help you get a game made by the end of the month.\xa0Don\u2019t forget, these are just examples. You can make ",(0,s.jsx)(t.strong,{children:"any"})," game you like. ",(0,s.jsx)(t.a,{href:"https://blog.playcanvas.com/playhack-december-jolly-santa/",children:"Read more"})," about this month\u2019s PLAYHACK."]})}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.img,{alt:"PLAYHACK Logo",src:n(6694).A+"",width:"603",height:"218"})}),"\n",(0,s.jsxs)(t.p,{children:["We'll be following on from ",(0,s.jsx)(t.a,{href:"https://blog.playcanvas.com/playhack-december-player-character/",children:"last time's tutorial"})," this time, so make sure you've followed that one before attempting this!"]}),"\n",(0,s.jsx)("div",{className:"iframe-container",children:(0,s.jsx)("iframe",{loading:"lazy",src:"https://playcanv.as/b/eyPboW2T/",title:"360 lookaround camera",webkitallowfullscreen:"true",mozallowfullscreen:"true",allow:"autoplay",allowfullscreen:"true",allowvr:"",scrolling:"no",frameborder:"0"})}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.em,{children:"Use W and S to move the sleigh."})}),"\n",(0,s.jsx)(t.p,{children:"In this tutorial, we're going to program the gifts\xa0that will come in from the right for Santa to collect."}),"\n",(0,s.jsx)(t.h3,{id:"moving-the-presents",children:"Moving the Presents"}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.a,{target:"_blank","data-noBrokenLinkCheck":!0,href:n(74624).A+"",children:(0,s.jsx)(t.img,{alt:"Santa and the Present",src:n(53934).A+"",width:"862",height:"469"})})}),"\n",(0,s.jsx)(t.p,{children:"To start with, we'll need to create a script that will make the presents move across the screen towards Santa. We're also going to make the presents rotate, just because it looks nice. To do that, we need to attach a new script to the Gift entity, just like we did last time with Santa. Call it something like 'gift_controller'."}),"\n",(0,s.jsx)(t.p,{children:"Now, let's think about what we need - just like with Santa's script, we're going to need the speed we want the presents to move, and we'll also need the speed we want the presents to rotate. So let's make two attributes for that:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-javascript",children:'pc.script.attribute("speed", "number", 10);\npc.script.attribute("rotspeed", "number", 90);\n'})}),"\n",(0,s.jsxs)(t.p,{children:["Put them at the top of the script like before (remember to do ",(0,s.jsx)(t.em,{children:"Entity > Refresh Script Attributes"})," when you come to test!)."]}),"\n",(0,s.jsx)(t.p,{children:"Now we need to move the gift every frame. This is again, very similar to the Santa movement code:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-javascript",children:"// Called every frame, dt is time in seconds since last update\nupdate: function (dt) {\n this.entity.translate(0, 0, -this.speed*dt);\n this.entity.rotate(0, this.rotspeed*dt, 0);\n}\n"})}),"\n",(0,s.jsxs)(t.p,{children:["To move the present, we use the ",(0,s.jsx)(t.code,{children:"entity.translate"})," function, as before. To rotate, we use the ",(0,s.jsx)(t.code,{children:"entity.rotate"})," function, which as its name suggests, rotates the present by a certain amount."]}),"\n",(0,s.jsxs)(t.p,{children:["If you launch the game (remembering to do ",(0,s.jsx)(t.em,{children:"Entity > Refresh Script Attributes"}),"!) then the present in the middle of the screen should hopefully move offscreen whilst rotating. Success!"]}),"\n",(0,s.jsx)(t.h3,{id:"creating-many-presents",children:"Creating Many Presents"}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.a,{target:"_blank","data-noBrokenLinkCheck":!0,href:n(97974).A+"",children:(0,s.jsx)(t.img,{alt:"Many Presents",src:n(8).A+"",width:"1440",height:"784"})})}),"\n",(0,s.jsx)(t.p,{children:"Now, we'll need to create a script that will create presents every so often off the right hand edge of the screen. This might seem odd - up until now we've only created scripts that do things to entities already on screen. To do this, we're going to attach a script to the root entity of the game - which is the entity called \"Santa\" in the pack explorer. Add a script to the root entity called 'gift_creator'."}),"\n",(0,s.jsxs)(t.p,{children:["So, let's think about how to go about this. We've got quite a few things to think about. Firstly, how do we add new presents? We can achieve this by cloning the gift that's already on screen. To do this, we need a reference to that present. In the ",(0,s.jsx)(t.code,{children:"initialize"})," method, add the following code:"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-javascript",children:'this.gift = context.root.findByName("Gift");\n'})}),"\n",(0,s.jsxs)(t.p,{children:["Now we have a variable, ",(0,s.jsx)(t.code,{children:"this.gift"}),", which contains the gift that is on screen. We'll get on to how to clone it in a second."]}),"\n",(0,s.jsx)(t.p,{children:"What else do we have to think about? Two main things - where we want to add the new presents, and how often we want to add them. We're going to have an element of randomness to where and when we add the presents - we want the presents to come in from the right at different heights, and we also don't want the presents to come in like clockwork - we want to keep the player on his toes and have them spawn with slightly different amounts of time between them."}),"\n",(0,s.jsx)(t.p,{children:"We'll add some new attributes so we can easily test and change these things:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-javascript",children:'pc.script.attribute("avgPresentTime", "number", 0.75);\npc.script.attribute("presentRange", "number", 0.5);\npc.script.attribute("maxY", "number", 8);\npc.script.attribute("offscreenZ", "number", 20);\n'})}),"\n",(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.code,{children:"avgPresentTime"})," is the average amount of time between each present coming spawning,\xa0and ",(0,s.jsx)(t.code,{children:"presentRange"})," is how many seconds around the average time presents could come in. ",(0,s.jsx)(t.code,{children:"maxY"})," is the highest position a present could come in at, and ",(0,s.jsx)(t.code,{children:"offscreenZ"})," is the z position we are going to create our new presents at."]}),"\n",(0,s.jsx)(t.p,{children:"So, now we've got all these attributes, what do we need to do? We need some sort of timer than allows us to check whether or not to add a new present yet. Let's add some variables so we can do that:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-javascript",children:'initialize: function () {\n this.gift = context.root.findByName("Gift");\n this.presentTimer = 0;\n this.presentTimeToGet = 0;\n},\n'})}),"\n",(0,s.jsxs)(t.p,{children:["The first line we added earlier - but we've now got two new variables, ",(0,s.jsx)(t.code,{children:"presentTimer"})," and ",(0,s.jsx)(t.code,{children:"presentTimeToGet"}),". We will use ",(0,s.jsx)(t.code,{children:"presentTimer"})," to count how long it's been since we last added a present, and ",(0,s.jsx)(t.code,{children:"presentTimeToGet"})," will be how long we need to wait until adding the next present."]}),"\n",(0,s.jsxs)(t.p,{children:["So, we now know that every frame, we need to increment ",(0,s.jsx)(t.code,{children:"this.presentTimer"})," by the amount of time passed since the last frame - which is helpfully passed into the ",(0,s.jsx)(t.code,{children:"update"})," function as the variable ",(0,s.jsx)(t.code,{children:"dt"}),"."]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-javascript",children:"update: function (dt) {\n this.presentTimer += dt;\n if(this.presentTimer >= this.presentTimeToGet) {\n //Create a new gift\n }\n}\n"})}),"\n",(0,s.jsx)(t.p,{children:"So, now we know when we're adding a new gift. To add a new gift to the game, we can use the following code:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-javascript",children:"var newGift = this.gift.clone();\nnewGift.enabled = true;\ncontext.root.addChild(newGift);\n"})}),"\n",(0,s.jsxs)(t.p,{children:["This clones ",(0,s.jsx)(t.code,{children:"this.gift"}),", and stores the new gift in the variable ",(0,s.jsx)(t.code,{children:"newGift"}),". We then have to do two things to actually get the gift in game - we have to enable it, and then add it to the entity hierarchy."]}),"\n",(0,s.jsx)(t.p,{children:"Now we have a new gift in the game - however, it's at the same position as the first gift! That's not good. We want it to be offscreen at a random height to start with! Let's fix that."}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-javascript",children:"// Set its position correctly\nvar rand = Math.random() * 2 - 1;\nvar newY = rand * maxY;\nnewGift.setPosition(0, newY, this.offscreenZ);\n"})}),"\n",(0,s.jsxs)(t.p,{children:["There's a few new things going on here! First, let's look at the randomness. ",(0,s.jsx)(t.code,{children:"Math.random()"})," is a built in JavaScript function that returns a random number from 0 to 1. We, however, want a random number from ",(0,s.jsx)(t.code,{children:"maxY"})," to negative ",(0,s.jsx)(t.code,{children:"maxY"}),". To achieve this, we multiply our random number by 2 (giving us a number between 0 and 2), then take away 1 (between -1 and 1), then multiply it by maxY (between ",(0,s.jsx)(t.code,{children:"-maxY"})," and ",(0,s.jsx)(t.code,{children:"maxY"}),"). Once we have the random ",(0,s.jsx)(t.code,{children:"newY"})," position, we can set the position of the ",(0,s.jsx)(t.code,{children:"newGift"}),"."]}),"\n",(0,s.jsxs)(t.p,{children:["Now, we're very almost done. However, we've now added a present, so we need to reset ",(0,s.jsx)(t.code,{children:"presentTimer"})," to 0, as it's been 0 seconds since we added a present! We also want to change ",(0,s.jsx)(t.code,{children:"presentTimeToGet"}),", as we want to wait a slightly different length of time before adding another present."]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-javascript",children:"// And reset the timer\nthis.presentTimer = 0;\nthis.presentTimeToGet = this.avgPresentTime + this.presentRange * (Math.random() - 0.5);\n"})}),"\n",(0,s.jsxs)(t.p,{children:["We use ",(0,s.jsx)(t.code,{children:"Math.random()"})," again here to get the time before we want to add another present. This gives us a number that will be no more than half of ",(0,s.jsx)(t.code,{children:"presentRange"})," away from ",(0,s.jsx)(t.code,{children:"avgPresentTime"}),"."]}),"\n",(0,s.jsxs)(t.p,{children:["That should be it! Test your code (REMEMBER ",(0,s.jsx)(t.em,{children:"Entity Menu > Refresh Script Attributes"}),"), and presents should start coming in from the right. Perfect!"]}),"\n",(0,s.jsx)(t.h3,{id:"housekeeping",children:"Housekeeping"}),"\n",(0,s.jsx)(t.p,{children:"One last thing - we've left the sack floating in the middle of the screen, and we don't really want the first present to start in the middle of the screen either.\xa0To fix this, simply select the gift/sack, and uncheck the 'Enabled' attribute. That's it! The initial sack and gift will no longer\xa0show up in the editor or when you test the game."}),"\n",(0,s.jsxs)(t.p,{children:["Next time, we'll be looking at ",(0,s.jsx)(t.a,{href:"https://developer.playcanvas.com/tutorials/collision-and-triggers/",children:"Trigger Volumes"}),", which will allow us to have Santa actually collect the presents when he touches them."]})]})}function d(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,s.jsx)(t,{...e,children:(0,s.jsx)(l,{...e})}):l(e)}},97974:(e,t,n)=>{n.d(t,{A:()=>s});const s=n.p+"assets/files/Screen-Shot-2014-11-24-at-15.58.42-0fa83dd1712c5252016697245a7dc84d.png"},74624:(e,t,n)=>{n.d(t,{A:()=>s});const s=n.p+"assets/files/editor-santa-and-present-582b7c8b13122c206cfa31a24978f695.png"},8:(e,t,n)=>{n.d(t,{A:()=>s});const s=n.p+"assets/images/Screen-Shot-2014-11-24-at-15.58.42-0fa83dd1712c5252016697245a7dc84d.png"},53934:(e,t,n)=>{n.d(t,{A:()=>s});const s=n.p+"assets/images/editor-santa-and-present-582b7c8b13122c206cfa31a24978f695.png"},6694:(e,t,n)=>{n.d(t,{A:()=>s});const s=n.p+"assets/images/playhack-logo-xmas-451ccd5e2d37685bc0f90e7e5a066b00.jpg"},28453:(e,t,n)=>{n.d(t,{R:()=>r,x:()=>o});var s=n(96540);const a={},i=s.createContext(a);function r(e){const t=s.useContext(i);return s.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function o(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:r(e.components),s.createElement(i.Provider,{value:t},e.children)}}}]);
\ No newline at end of file
diff --git a/assets/js/46a87d83.143c879b.js b/assets/js/46a87d83.143c879b.js
deleted file mode 100644
index 94d2d4fed..000000000
--- a/assets/js/46a87d83.143c879b.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(self.webpackChunk_playcanvas_blog=self.webpackChunk_playcanvas_blog||[]).push([[27156],{28082:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>d,frontMatter:()=>s,metadata:()=>r,toc:()=>h});var o=n(74848),i=n(28453);const s={authors:"nathan",slug:"playhack-december-collecting-presents",title:"PLAYHACK December - Collecting Presents"},a=void 0,r={permalink:"/playhack-december-collecting-presents",editUrl:"https://github.com/playcanvas/blog/tree/main/blog/2014-12-22-playhack-december-collecting-presents.md",source:"@site/blog/2014-12-22-playhack-december-collecting-presents.md",title:"PLAYHACK December - Collecting Presents",description:"**PLAYHACK** is our fun monthly game building session. Throughout the month I\u2019ll be posting tips and tricks to help you get a game made by the end of the month.\xa0Don\u2019t forget, these are just examples. You can make **any** game you like. Read more about this month\u2019s PLAYHACK.",date:"2014-12-22T00:00:00.000Z",tags:[],readingTime:5.955,hasTruncateMarker:!0,authors:[{name:"Nathan",page:{permalink:"/authors/nathan"},key:"nathan"}],frontMatter:{authors:"nathan",slug:"playhack-december-collecting-presents",title:"PLAYHACK December - Collecting Presents"},unlisted:!1,prevItem:{title:"PlayCanvas in 2014: A Year In Review",permalink:"/playcanvas-in-2014-a-year-in-review"},nextItem:{title:"PLAYHACK December - Creating Presents",permalink:"/playhack-december-creating-presents"}},l={authorsImageUrls:[void 0]},h=[{value:"Adding an Offscreen Trigger Volume",id:"adding-an-offscreen-trigger-volume",level:2},{value:"Setting up the present",id:"setting-up-the-present",level:2},{value:"Santa Trigger Volume",id:"santa-trigger-volume",level:2}];function c(e){const t={a:"a",code:"code",em:"em",h2:"h2",img:"img",p:"p",pre:"pre",strong:"strong",...(0,i.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(t.p,{children:(0,o.jsxs)(t.em,{children:[(0,o.jsx)(t.strong,{children:"PLAYHACK"})," is our fun monthly game building session. Throughout the month I\u2019ll be posting tips and tricks to help you get a game made by the end of the month.\xa0Don\u2019t forget, these are just examples. You can make ",(0,o.jsx)(t.strong,{children:"any"})," game you like. ",(0,o.jsx)(t.a,{href:"https://blog.playcanvas.com/playhack-december-jolly-santa/",children:"Read more"})," about this month\u2019s PLAYHACK."]})}),"\n",(0,o.jsx)(t.p,{children:(0,o.jsx)(t.img,{alt:"PLAYHACK Logo",src:n(6694).A+"",width:"603",height:"218"})}),"\n",(0,o.jsxs)(t.p,{children:["We'll be following on from ",(0,o.jsx)(t.a,{href:"https://blog.playcanvas.com/playhack-december-creating-presents/",children:"last time's tutorial"})," this time, so make sure you've followed that one before attempting this! As always, you can check out all the up to date code ",(0,o.jsx)(t.a,{href:"https://playcanvas.com/project/333523/overview/playhack_december",children:"in this project"}),"."]}),"\n",(0,o.jsx)("div",{className:"iframe-container",children:(0,o.jsx)("iframe",{loading:"lazy",src:"https://playcanv.as/b/MjlE6fOY/",title:"360 lookaround camera",webkitallowfullscreen:"true",mozallowfullscreen:"true",allow:"autoplay",allowfullscreen:"true",allowvr:"",scrolling:"no",frameborder:"0"})}),"\n",(0,o.jsx)(t.p,{children:(0,o.jsx)(t.em,{children:"Use W and S to move the sleigh."})}),"\n",(0,o.jsxs)(t.p,{children:["In this tutorial, we're going to do two things - delete the presents as they go off-screen, and find out when the presents collide with Santa. How are we going to do all this? The answer: ",(0,o.jsx)(t.strong,{children:"Trigger Volumes"}),"!"]}),"\n",(0,o.jsx)(t.h2,{id:"adding-an-offscreen-trigger-volume",children:"Adding an Offscreen Trigger Volume"}),"\n",(0,o.jsx)(t.p,{children:"At the moment, when presents go offscreen, they just keep going on and on forever and ever. We don't need the presents anymore after they've gone offscreen, so it doesn't hurt (and may even help) us if we delete them. So, we need a way to test when a present has left the screen. We can do that with a Trigger Volume placed just offscreen."}),"\n",(0,o.jsx)(t.p,{children:(0,o.jsx)(t.a,{target:"_blank","data-noBrokenLinkCheck":!0,href:n(10487).A+"",children:(0,o.jsx)(t.img,{alt:"DEC-PLAYHACK-TUT3-2",src:n(48501).A+"",width:"1462",height:"828"})})}),"\n",(0,o.jsxs)(t.p,{children:["You may be thinking - what is a trigger volume?! A trigger volume is simply an ",(0,o.jsx)(t.strong,{children:"entity with a collision component"})," added to it (but without a rigidbody - we'll get to that later). They allow us to test when rigidbodies enter and leave them - which is perfect for testing when a present goes off-screen."]}),"\n",(0,o.jsx)(t.p,{children:'So, to add the off-screen trigger volume, simply right click on\xa0"Santa" in the Pack Explorer and add a new Entity. I called mine "offscreen". Then, add a collision component to your new offscreen entity - a small blue cube should appear in the editor. Now, all we need to do is move it off-screen, and resize it so it catches all the presents. You can move it with the translate tool, and to make it big enough, you can edit the "half-extents" of the collision component until it covers the whole of the edge of the camera.'}),"\n",(0,o.jsx)(t.p,{children:(0,o.jsx)(t.a,{target:"_blank","data-noBrokenLinkCheck":!0,href:n(36846).A+"",children:(0,o.jsx)(t.img,{alt:"DEC-PLAYHACK-TUT3-3",src:n(65612).A+"",width:"263",height:"287"})})}),"\n",(0,o.jsx)(t.p,{children:'Now, let\'s write\xa0some code to delete the presents that come off-screen. Add a script component to the offscreen entity, and add a new script - I called the script "offscreen.js" (creative name, I know).'}),"\n",(0,o.jsx)(t.p,{children:"Whenever a rigidbody enters the trigger volume, the collision component will fire an event that we can listen to. Or, in simple terms, we can tell the collision component to call a specific function any time it comes into contact with a rigidbody. Put the following code in the initialize function of your new script:"}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-javascript",children:"this.entity.collision.on('triggerenter', this.onTriggerEnter, this);\n"})}),"\n",(0,o.jsxs)(t.p,{children:['This tells our collision component that when the "',(0,o.jsx)(t.strong,{children:"triggerenter"}),'" event happens (i.e. something enters the trigger volume), call the function "',(0,o.jsx)(t.strong,{children:"onTriggerEnter"}),'", in the scope of ',(0,o.jsx)(t.strong,{children:"this"})," (so we have access to all our variables and functions).\xa0Let's write that function now:"]}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-javascript",children:"onTriggerEnter: function (other) {\n other.destroy();\n}\n"})}),"\n",(0,o.jsx)(t.p,{children:"Pretty simple huh? The onTriggerEnter function gets called with one argument, which is the entity it collided with. We simply destroy that entity, and it's gone!"}),"\n",(0,o.jsx)(t.h2,{id:"setting-up-the-present",children:"Setting up the present"}),"\n",(0,o.jsx)(t.p,{children:(0,o.jsx)(t.a,{target:"_blank","data-noBrokenLinkCheck":!0,href:n(21756).A+"",children:(0,o.jsx)(t.img,{alt:"DEC-PLAYHACK-TUT3-1",src:n(46398).A+"",width:"1464",height:"830"})})}),"\n",(0,o.jsxs)(t.p,{children:["If we run the game now though, the presents still won't get deleted even if they go off-screen!\xa0That's because trigger volumes only check for collisions with rigidbodies, so we'll need to add some more components to the present. We need to add a ",(0,o.jsx)(t.strong,{children:"collision"})," component and a\xa0",(0,o.jsx)(t.strong,{children:"kinematic\xa0rigidbody"})," component. Together, these will allow us to check when the present passes through our trigger volumes."]}),"\n",(0,o.jsxs)(t.p,{children:['So, add those components to the\xa0Gift. Make sure you change the "type" attribute of the rigidbody component to ',(0,o.jsx)(t.strong,{children:"Kinematic"})," - we don't want it to be Static, because then we couldn't move the presents, and we don't need it to be ",(0,o.jsx)(t.strong,{children:"Dynamic"})," because then our present would get affected by gravity and physics and we don't need that.\xa0",(0,o.jsx)(t.strong,{children:"Kinematic"})," allows us to move our present in code, but not have it affected by physics. You can fiddle around with the half extents of the collision box until it fits around the present too."]}),"\n",(0,o.jsx)(t.p,{children:"You'll also notice the collision box isn't centred on the present model. To fix this, you can removed the model component from Gift and add a model entity child instead, which you can then translate without affecting the position of the collision box."}),"\n",(0,o.jsx)(t.p,{children:"One last thing - it's very important that you uncheck the Enabled attribute of the Gift. If the original gift goes off-screen and\xa0collides with the off-screen Trigger Volume, it will get deleted, and we won't be able to make any copies of it. We can make sure that never happens by disabling\xa0the present - now the original present won't do anything, but it will still be there for us to clone and make more presents."}),"\n",(0,o.jsx)(t.p,{children:"If you test the game now, the presents should be getting deleted after they go off-screen! You can test that it's actually working by moving the\xa0off-screen Trigger Volume on-screen a bit - the presents should then disappear just before they go off-screen."}),"\n",(0,o.jsx)(t.h2,{id:"santa-trigger-volume",children:"Santa Trigger Volume"}),"\n",(0,o.jsx)(t.p,{children:"But the present going offscreen isn't the only thing we want to check for - we also need to check when it collides with Santa! Luckily, this is very similar to the offscreen trigger volume."}),"\n",(0,o.jsxs)(t.p,{children:["Like before, we need to add a collision component - so right click on Santa and add one. There's a clever thing we can do here: instead of messing around with the half-extends of the collision box, trying to get it to match Santa, we can simply tell the collision component to match the size and shape of the Santa Model. Change the type attribute of the collision component to ",(0,o.jsx)(t.strong,{children:"Mesh"}),", and select the Santa_sleigh model for the ",(0,o.jsx)(t.strong,{children:"Asset"}),". Now, the collision will only check for exactly the right size of Santa."]}),"\n",(0,o.jsx)(t.p,{children:"Wondering why we can't use a Mesh\xa0for the collision component of Gift? Checking for collisions between two\xa0Meshes is a lot more computationally expensive compared to comparing collisions with simple geometry. Therefore, checking for a collision between two Meshes isn't allowed, and since our present is basically a cube, we're using a cube for the collision volume of the present."}),"\n",(0,o.jsx)(t.p,{children:"Now we just need to add a small amount of code to Santa so we know when presents collide with him."}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-javascript",children:"this.entity.collision.on('triggerenter', this.onTriggerEnter, this);\n"})}),"\n",(0,o.jsx)(t.p,{children:"Put that line in the santa_controller.js script - it does exactly the same thing as before. We're going to write a simple onTriggerEnter function for now, and actually write the functionality we want next time."}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-javascript",children:'onTriggerEnter:function(other) {\n if (other.name === "Gift") {\n other.destroy();\n // we\'ll be adding score incrementing and other stuff here...\n }\n}\n'})}),"\n",(0,o.jsx)(t.p,{children:"All we're doing\xa0here at the moment is the same as the offscreen trigger volume - destroying the present when it comes into contact with Santa."}),"\n",(0,o.jsx)(t.p,{children:"And that's it for this time! Next time we'll be looking at adding a GUI to the game so you can see how many presents you've collected!"})]})}function d(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,o.jsx)(t,{...e,children:(0,o.jsx)(c,{...e})}):c(e)}},21756:(e,t,n)=>{n.d(t,{A:()=>o});const o=n.p+"assets/files/DEC-PLAYHACK-TUT3-1-0205bd8512dfff21ec30a0bbeafa50ff.png"},10487:(e,t,n)=>{n.d(t,{A:()=>o});const o=n.p+"assets/files/DEC-PLAYHACK-TUT3-2-d72dd6816c989b680675579e67aa5bcc.png"},36846:(e,t,n)=>{n.d(t,{A:()=>o});const o=n.p+"assets/files/DEC-PLAYHACK-TUT3-3-88b48cfdbdb5bf96024ff3a36b8a8b70.png"},46398:(e,t,n)=>{n.d(t,{A:()=>o});const o=n.p+"assets/images/DEC-PLAYHACK-TUT3-1-0205bd8512dfff21ec30a0bbeafa50ff.png"},48501:(e,t,n)=>{n.d(t,{A:()=>o});const o=n.p+"assets/images/DEC-PLAYHACK-TUT3-2-d72dd6816c989b680675579e67aa5bcc.png"},65612:(e,t,n)=>{n.d(t,{A:()=>o});const o="data:image/png;base64,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"},6694:(e,t,n)=>{n.d(t,{A:()=>o});const o=n.p+"assets/images/playhack-logo-xmas-451ccd5e2d37685bc0f90e7e5a066b00.jpg"},28453:(e,t,n)=>{n.d(t,{R:()=>a,x:()=>r});var o=n(96540);const i={},s=o.createContext(i);function a(e){const t=o.useContext(s);return o.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function r(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),o.createElement(s.Provider,{value:t},e.children)}}}]);
\ No newline at end of file
diff --git a/assets/js/46a87d83.3eb196d3.js b/assets/js/46a87d83.3eb196d3.js
new file mode 100644
index 000000000..3b2ba796d
--- /dev/null
+++ b/assets/js/46a87d83.3eb196d3.js
@@ -0,0 +1 @@
+"use strict";(self.webpackChunk_playcanvas_blog=self.webpackChunk_playcanvas_blog||[]).push([[27156],{28082:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>d,frontMatter:()=>s,metadata:()=>r,toc:()=>h});var o=n(74848),i=n(28453);const s={authors:"nathan",slug:"playhack-december-collecting-presents",title:"PLAYHACK December - Collecting Presents"},a=void 0,r={permalink:"/playhack-december-collecting-presents",editUrl:"https://github.com/playcanvas/blog/tree/main/blog/2014-12-22-playhack-december-collecting-presents.md",source:"@site/blog/2014-12-22-playhack-december-collecting-presents.md",title:"PLAYHACK December - Collecting Presents",description:"**PLAYHACK** is our fun monthly game building session. Throughout the month I\u2019ll be posting tips and tricks to help you get a game made by the end of the month.\xa0Don\u2019t forget, these are just examples. You can make **any** game you like. Read more about this month\u2019s PLAYHACK.",date:"2014-12-22T00:00:00.000Z",tags:[],readingTime:5.955,hasTruncateMarker:!0,authors:[{name:"Nathan Patel",title:"Technical Blogger",page:{permalink:"/authors/nathan"},key:"nathan"}],frontMatter:{authors:"nathan",slug:"playhack-december-collecting-presents",title:"PLAYHACK December - Collecting Presents"},unlisted:!1,prevItem:{title:"PlayCanvas in 2014: A Year In Review",permalink:"/playcanvas-in-2014-a-year-in-review"},nextItem:{title:"PLAYHACK December - Creating Presents",permalink:"/playhack-december-creating-presents"}},l={authorsImageUrls:[void 0]},h=[{value:"Adding an Offscreen Trigger Volume",id:"adding-an-offscreen-trigger-volume",level:2},{value:"Setting up the present",id:"setting-up-the-present",level:2},{value:"Santa Trigger Volume",id:"santa-trigger-volume",level:2}];function c(e){const t={a:"a",code:"code",em:"em",h2:"h2",img:"img",p:"p",pre:"pre",strong:"strong",...(0,i.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(t.p,{children:(0,o.jsxs)(t.em,{children:[(0,o.jsx)(t.strong,{children:"PLAYHACK"})," is our fun monthly game building session. Throughout the month I\u2019ll be posting tips and tricks to help you get a game made by the end of the month.\xa0Don\u2019t forget, these are just examples. You can make ",(0,o.jsx)(t.strong,{children:"any"})," game you like. ",(0,o.jsx)(t.a,{href:"https://blog.playcanvas.com/playhack-december-jolly-santa/",children:"Read more"})," about this month\u2019s PLAYHACK."]})}),"\n",(0,o.jsx)(t.p,{children:(0,o.jsx)(t.img,{alt:"PLAYHACK Logo",src:n(6694).A+"",width:"603",height:"218"})}),"\n",(0,o.jsxs)(t.p,{children:["We'll be following on from ",(0,o.jsx)(t.a,{href:"https://blog.playcanvas.com/playhack-december-creating-presents/",children:"last time's tutorial"})," this time, so make sure you've followed that one before attempting this! As always, you can check out all the up to date code ",(0,o.jsx)(t.a,{href:"https://playcanvas.com/project/333523/overview/playhack_december",children:"in this project"}),"."]}),"\n",(0,o.jsx)("div",{className:"iframe-container",children:(0,o.jsx)("iframe",{loading:"lazy",src:"https://playcanv.as/b/MjlE6fOY/",title:"360 lookaround camera",webkitallowfullscreen:"true",mozallowfullscreen:"true",allow:"autoplay",allowfullscreen:"true",allowvr:"",scrolling:"no",frameborder:"0"})}),"\n",(0,o.jsx)(t.p,{children:(0,o.jsx)(t.em,{children:"Use W and S to move the sleigh."})}),"\n",(0,o.jsxs)(t.p,{children:["In this tutorial, we're going to do two things - delete the presents as they go off-screen, and find out when the presents collide with Santa. How are we going to do all this? The answer: ",(0,o.jsx)(t.strong,{children:"Trigger Volumes"}),"!"]}),"\n",(0,o.jsx)(t.h2,{id:"adding-an-offscreen-trigger-volume",children:"Adding an Offscreen Trigger Volume"}),"\n",(0,o.jsx)(t.p,{children:"At the moment, when presents go offscreen, they just keep going on and on forever and ever. We don't need the presents anymore after they've gone offscreen, so it doesn't hurt (and may even help) us if we delete them. So, we need a way to test when a present has left the screen. We can do that with a Trigger Volume placed just offscreen."}),"\n",(0,o.jsx)(t.p,{children:(0,o.jsx)(t.a,{target:"_blank","data-noBrokenLinkCheck":!0,href:n(10487).A+"",children:(0,o.jsx)(t.img,{alt:"DEC-PLAYHACK-TUT3-2",src:n(48501).A+"",width:"1462",height:"828"})})}),"\n",(0,o.jsxs)(t.p,{children:["You may be thinking - what is a trigger volume?! A trigger volume is simply an ",(0,o.jsx)(t.strong,{children:"entity with a collision component"})," added to it (but without a rigidbody - we'll get to that later). They allow us to test when rigidbodies enter and leave them - which is perfect for testing when a present goes off-screen."]}),"\n",(0,o.jsx)(t.p,{children:'So, to add the off-screen trigger volume, simply right click on\xa0"Santa" in the Pack Explorer and add a new Entity. I called mine "offscreen". Then, add a collision component to your new offscreen entity - a small blue cube should appear in the editor. Now, all we need to do is move it off-screen, and resize it so it catches all the presents. You can move it with the translate tool, and to make it big enough, you can edit the "half-extents" of the collision component until it covers the whole of the edge of the camera.'}),"\n",(0,o.jsx)(t.p,{children:(0,o.jsx)(t.a,{target:"_blank","data-noBrokenLinkCheck":!0,href:n(36846).A+"",children:(0,o.jsx)(t.img,{alt:"DEC-PLAYHACK-TUT3-3",src:n(65612).A+"",width:"263",height:"287"})})}),"\n",(0,o.jsx)(t.p,{children:'Now, let\'s write\xa0some code to delete the presents that come off-screen. Add a script component to the offscreen entity, and add a new script - I called the script "offscreen.js" (creative name, I know).'}),"\n",(0,o.jsx)(t.p,{children:"Whenever a rigidbody enters the trigger volume, the collision component will fire an event that we can listen to. Or, in simple terms, we can tell the collision component to call a specific function any time it comes into contact with a rigidbody. Put the following code in the initialize function of your new script:"}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-javascript",children:"this.entity.collision.on('triggerenter', this.onTriggerEnter, this);\n"})}),"\n",(0,o.jsxs)(t.p,{children:['This tells our collision component that when the "',(0,o.jsx)(t.strong,{children:"triggerenter"}),'" event happens (i.e. something enters the trigger volume), call the function "',(0,o.jsx)(t.strong,{children:"onTriggerEnter"}),'", in the scope of ',(0,o.jsx)(t.strong,{children:"this"})," (so we have access to all our variables and functions).\xa0Let's write that function now:"]}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-javascript",children:"onTriggerEnter: function (other) {\n other.destroy();\n}\n"})}),"\n",(0,o.jsx)(t.p,{children:"Pretty simple huh? The onTriggerEnter function gets called with one argument, which is the entity it collided with. We simply destroy that entity, and it's gone!"}),"\n",(0,o.jsx)(t.h2,{id:"setting-up-the-present",children:"Setting up the present"}),"\n",(0,o.jsx)(t.p,{children:(0,o.jsx)(t.a,{target:"_blank","data-noBrokenLinkCheck":!0,href:n(21756).A+"",children:(0,o.jsx)(t.img,{alt:"DEC-PLAYHACK-TUT3-1",src:n(46398).A+"",width:"1464",height:"830"})})}),"\n",(0,o.jsxs)(t.p,{children:["If we run the game now though, the presents still won't get deleted even if they go off-screen!\xa0That's because trigger volumes only check for collisions with rigidbodies, so we'll need to add some more components to the present. We need to add a ",(0,o.jsx)(t.strong,{children:"collision"})," component and a\xa0",(0,o.jsx)(t.strong,{children:"kinematic\xa0rigidbody"})," component. Together, these will allow us to check when the present passes through our trigger volumes."]}),"\n",(0,o.jsxs)(t.p,{children:['So, add those components to the\xa0Gift. Make sure you change the "type" attribute of the rigidbody component to ',(0,o.jsx)(t.strong,{children:"Kinematic"})," - we don't want it to be Static, because then we couldn't move the presents, and we don't need it to be ",(0,o.jsx)(t.strong,{children:"Dynamic"})," because then our present would get affected by gravity and physics and we don't need that.\xa0",(0,o.jsx)(t.strong,{children:"Kinematic"})," allows us to move our present in code, but not have it affected by physics. You can fiddle around with the half extents of the collision box until it fits around the present too."]}),"\n",(0,o.jsx)(t.p,{children:"You'll also notice the collision box isn't centred on the present model. To fix this, you can removed the model component from Gift and add a model entity child instead, which you can then translate without affecting the position of the collision box."}),"\n",(0,o.jsx)(t.p,{children:"One last thing - it's very important that you uncheck the Enabled attribute of the Gift. If the original gift goes off-screen and\xa0collides with the off-screen Trigger Volume, it will get deleted, and we won't be able to make any copies of it. We can make sure that never happens by disabling\xa0the present - now the original present won't do anything, but it will still be there for us to clone and make more presents."}),"\n",(0,o.jsx)(t.p,{children:"If you test the game now, the presents should be getting deleted after they go off-screen! You can test that it's actually working by moving the\xa0off-screen Trigger Volume on-screen a bit - the presents should then disappear just before they go off-screen."}),"\n",(0,o.jsx)(t.h2,{id:"santa-trigger-volume",children:"Santa Trigger Volume"}),"\n",(0,o.jsx)(t.p,{children:"But the present going offscreen isn't the only thing we want to check for - we also need to check when it collides with Santa! Luckily, this is very similar to the offscreen trigger volume."}),"\n",(0,o.jsxs)(t.p,{children:["Like before, we need to add a collision component - so right click on Santa and add one. There's a clever thing we can do here: instead of messing around with the half-extends of the collision box, trying to get it to match Santa, we can simply tell the collision component to match the size and shape of the Santa Model. Change the type attribute of the collision component to ",(0,o.jsx)(t.strong,{children:"Mesh"}),", and select the Santa_sleigh model for the ",(0,o.jsx)(t.strong,{children:"Asset"}),". Now, the collision will only check for exactly the right size of Santa."]}),"\n",(0,o.jsx)(t.p,{children:"Wondering why we can't use a Mesh\xa0for the collision component of Gift? Checking for collisions between two\xa0Meshes is a lot more computationally expensive compared to comparing collisions with simple geometry. Therefore, checking for a collision between two Meshes isn't allowed, and since our present is basically a cube, we're using a cube for the collision volume of the present."}),"\n",(0,o.jsx)(t.p,{children:"Now we just need to add a small amount of code to Santa so we know when presents collide with him."}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-javascript",children:"this.entity.collision.on('triggerenter', this.onTriggerEnter, this);\n"})}),"\n",(0,o.jsx)(t.p,{children:"Put that line in the santa_controller.js script - it does exactly the same thing as before. We're going to write a simple onTriggerEnter function for now, and actually write the functionality we want next time."}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-javascript",children:'onTriggerEnter:function(other) {\n if (other.name === "Gift") {\n other.destroy();\n // we\'ll be adding score incrementing and other stuff here...\n }\n}\n'})}),"\n",(0,o.jsx)(t.p,{children:"All we're doing\xa0here at the moment is the same as the offscreen trigger volume - destroying the present when it comes into contact with Santa."}),"\n",(0,o.jsx)(t.p,{children:"And that's it for this time! Next time we'll be looking at adding a GUI to the game so you can see how many presents you've collected!"})]})}function d(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,o.jsx)(t,{...e,children:(0,o.jsx)(c,{...e})}):c(e)}},21756:(e,t,n)=>{n.d(t,{A:()=>o});const o=n.p+"assets/files/DEC-PLAYHACK-TUT3-1-0205bd8512dfff21ec30a0bbeafa50ff.png"},10487:(e,t,n)=>{n.d(t,{A:()=>o});const o=n.p+"assets/files/DEC-PLAYHACK-TUT3-2-d72dd6816c989b680675579e67aa5bcc.png"},36846:(e,t,n)=>{n.d(t,{A:()=>o});const o=n.p+"assets/files/DEC-PLAYHACK-TUT3-3-88b48cfdbdb5bf96024ff3a36b8a8b70.png"},46398:(e,t,n)=>{n.d(t,{A:()=>o});const o=n.p+"assets/images/DEC-PLAYHACK-TUT3-1-0205bd8512dfff21ec30a0bbeafa50ff.png"},48501:(e,t,n)=>{n.d(t,{A:()=>o});const o=n.p+"assets/images/DEC-PLAYHACK-TUT3-2-d72dd6816c989b680675579e67aa5bcc.png"},65612:(e,t,n)=>{n.d(t,{A:()=>o});const o="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQcAAAEfCAIAAADDTJQGAAAdD0lEQVR42u2d73MT17nH+wdkmAlcANeVmQDYGNnBGIEtwEAsEgxUJFEMIhe7NbSGiVNwmjpA2+AmUlKLEM1tlWKFKvLVmNFVI6yKuqJGFCkz2klGyWQy07xh8oLptG/bt+3b3ufsD+msdleWf+vH95tgr84eHa3Xz2fP8xx5v/rWf0EQpNa3zGYzzgIE6VBxAoIgRTIVFgiCFIEKCAIVEAQqIAhUQBCogCBQAUGgAoIWqkOHDnm93mQyefPmzeeff37lqBibyqYnzi/9D0yvMzWG3ztUDAlBEB4/fvzNN9/Q16+//vrFF1+cPxXnJ9LZ7INb/dzjXKDz27owsI1b9rrGtiIvbjSI3nHkpX4KvU70F6s2uZXXLXFMqGbkcrkIBportmzZcvXqVdoeHx9va2ubHxWv3U6n73yc+pP3u9t25oL4tyelQOe3C2TetIraS6XCYBBtt1WcuKfIVJiV1y1xTKhmdPz48dOnTxMSVqv1k08+ISrOnj27ffv2eVHBoPio//sfpVLjToYFd8WeCnLbY+K0cDdyN80e8HOF8MfwdIb1eRBx9apyHfGCPqYaxNI7MnFXakhP3eifBZ5eV+SB1Dct8HPF+YIxlW7yIagOFfFSM9q5c+eBAwceixoaGiJCWltb50OFBMWGuguT6dT4SWl+CKZSH9ilq/WZQH77F1ECwHPy6c0NG1Zf/p08RYhUeF5q3dTQdPbWw4cfnW/5Ze6qrsR5/0e5QczvxB/cvtrb1rDBtP/NaGbqne6dBhkUBf/xG3ezD4Kv7qpf8x2z60428jOJCvF1uYOkMbP3bp2lbpvs3j9kp9493C4dlXSo0qFAtaHh4eEvv/xycHBwViSKUKFA0dhGWzIWRhmUlCytbWhu48qJgsaPf77hrTsaKoK5QaiLqnb48KWN29uNMqiff5y9O9a8ur5xh5RBjfJUTPBjsm6mbbTdRSBFrm4khnJHBdWSTp065Xa7d+3aNSsSxlQQCnyQSlgUp6Jgu6CReKAppSgV8Xfq9GsHTQYljbfRXFBt61BBY0rdGBVEDx3DrNUOVI2iapvSp0uXLrW3t8+TipHbApsp5Ph8mTKgD06KASf87+BmhYrctiEVEy9Th2f6f3VXuOd7YdVQSEhPfL/F/Ez/TyfuCTIVyiAsKbp/65Kjy2J55vyNqYmxYnXFyzfvCb9/8zh17f8p4VtIhWrMD35gpX6vTTzI3vMdBRU1K3qb4vLly3v37p3/XCFCsT4XPCyJ+miAIu7mvYxSyHLbhlSk/iSlQ5G3v7txnanZMXYnJVXTE3fSKb+jrvGH3IC9PwnEHsr5070PhzZtbTFeme169TeJjDTUVJKyM44K/sAsvW+H7yvV9ltH61ebpAwKVNSkaDWWkKDKe74ZlGXH1vWq2NmxdcOGrZTGtzU3rH9y1aonN7WotqVVUX5lVtrYUFe/hrqsrtvSIs5b7ebNVObS482Nm+vEXvyA7U83siqYHq3ZYNq2g/+BGjXZ1Q7piTTUpu9wK7Nib37Mli3iITy5pl46Bv5QIWhuK7MQBCogCAIVEAQqIAhUQBCogCBQAUGgAoJABQStKBUQBMGTHIJABQSVJiJCRYUZgmpboAKCQAUEgQoIAhUQBCogCFRAEKiAIFABQaCiutXc3GwymdauXWv0HirteuONN5xOJ85VzVAxEEinAwOlNJb43EoTIUHWQ6+//vqbBpqYmHj06NFXX3117NgxhGPlUEHRqXJ/zUbdoKJUrVu37syZMydPnnTo6f3333+kKJlMIhwri4o5gAAqCs7vC6LI9/cFtcjzNIfEp59+SibyCMcKp4KFbHg8nBQnj+T4oIU19vkSgmJ4GfexNtYtGojK3ULDdnW4W/o80WQyHo0n0snwqF1pkUZIpgWFCnYIlUkInU+73R4Ohz/77LPe3l67ouvXr+eQ+OKLL6hnXV0dwrHyqcimo54Bq9ls98SFhM/B77U6fYksa2PdkqERJ3WzDoXSQniEp4JaEl4RFLNlNCzEPTbWJ+Fjo1psI+HqoOL27dtS9D98+PDEiRNUP3g8Hh4Jm832xBNPNDU1IRwrt64QCeHTG5s3ocFG3q/KggblB0rjCAPBIu+0eOLZ6O2wIISGqyyDmpyczDEQi8X4WuLzzz8/ePAgrVOtWbMGsVgdGVQuZN1RqQOlP6F4UpCzKA0VAwVU0NMKyviPo1lt90o/v4cPH+ZJyIl+YPpgz23bttFXLIhXLRV07RcSgZE+R4/VaK5IjvcZzBXKQOnAYJVR0S2Kr62lxIlgoKypq6uL9oKKqqWCvlHhwEoI50ggIchUUJONQt864GXlh5N7roVVDmE3KzrEp4S8P3JHBSHqcVi4ISq+rjio6L333sslTvv379+6deu+ffukXaCi0usKkQDdDMrpiUpdk7TupMwVAi0mSY3SKpNmDUpKuNLhUYLBbB+V17XS0VA0XRVU7Ofk9/tpliAYNm/e3NnZmWsHFZVGBbQArV+/fvfu3VZOVFs/9dRTfOOePXuoG84VqKgV0eecNzQ0kOVWh4Ho8zypQ0tLC84VqKgVUbJEYNBUYPTXgbSLOtDnF+JcgYoaEq0y9fT0HDMQ7aIOOEugAoJABQSBCggCFRAEKiAIVEAQqIAW7eRCleLLDyqWjwoTVPYCFaACAhWgAgIVoAICFaACWhEqpLuOFDOOKrlztGyo6L4ymRJvs4pcU21DZU+FkKb/Q8MWULHYVJwLplLBc62abaj8qUgHRseTWemu6xwVWk80ZkYQD8fTknPa0IBXumE1HRqxsSdqPdEq+NbTRaLiWiQ/MfDbUCVQMcCsB8iYwKIzV+Q80ZidAZl8OCxmO1lEMXs05kswGhVEaw+tJ1ptUdHkGItICVJmZvJKN7U4/SnlXvhU5Hf5bb+zqd8by/W9doSe64rMyHunfefEp6aCY/7pDEu3xL0zsUhsOiWPrGlh/Sd9k9IYM77+JsCwGFSQV4EY+069DEpucuftnLhOLAMj6zOtJ5q7ljKopouTmUzEZW81NXWc889kp71247nC4ZvJTF7saDI1Hbnon/R+70IwlZ3x88kVA2omeNHeKmVe02Pd0qtcmczEXB3aFtY/FXE5qX+3K5aZ9h4BDYtChdkyzDwxRxULQK0nmiEVjACtJ1pNUUFQpPz9CiKuWFYKTGMqpr39Xa2552aCF1S/SXbt9zuVvTFXEzdyJKRpucb1N3WMTSNVWzQqzGJilAyHxcc6nmhFqdB6otVUtU0hnwtL7pFBXUEZUDA2k2EJU8zb/7bquYVU0NPUikQ0LSoqUMAsLhVicZCfGAo90YpSofVE66ulukJ1vWdX62JzRT7vOsLSnd/dKXmuMGgxgYqlpMJsI3s/KZC1nmhFqdDzRKshKuZUVzi9k36xZhCpiP3yZ5MZbV2hRDkbOTV5TSwxWu0X/cGxlzUtDlCB97bLcw2KX1e60l1sZbbjon9aXpKSuuquQeWiXNybkfZOXjnSpNMCKkAF/uIDAhWgAgIVoAICFaACAhWgAgIV1UsFBDcDSKVOqEIEKiBo9kkeVEAQqIAgUAFBoAJrUBDWoMqYivJZki+rN0/K6nhABajA8YAKUAEqQAWoABWgAlSAihWhQnLUlJ08EqFRe6lBwVw9xFtT+Xtcc42gAlRUOBVSHFud7nBasRCcg2rVg9PoF8+5o+V80UrowN23ypw/JLsCVaP/QreqsUgUkqftTGo6FpueyQ1VJlTo/pizHbDBKVU3i225psWiQn7AbALJmkD2y8wKij2m2TLoi+fa3A4ZBn6uibrzhOiNwPaGx8NJySVhfNCSf9UK5KqkyyH7LZGNWQkdch4HreSCJnuE8I3kkUDeaqpGoyjsYIZUko+aXXRkKyMqdH/MOR1w/pSyrVlvUV84FWSRliVbJ9FfM+ohGxuLbSggW9D2jSfJC8dmMVscI4GwbyAHAD9XKNu6I0jhH/UMkD2O3RMXRP6qmoqOa5EMWS7PtQOLF81vu0gIFEYh+RnM+BzS9oWgxlenXDKo/I85hwPmz9gyUGGxDXrjQpYZxDJvp8Cg3IHZY7L4ZVQkfIM91sLESY8K3RFUPW1kyVbZFcjsv/jSJwqV6HKZLTDFJO/NILuGNpUShapgKSlyVoSK/I85hwNWnTEug8r7qix6tZ2Me4esUs3MXbuVR7LHpmJSXpQK/RFUFQg1VjcV85wouqk1E7nWobr0iw7OY85WU9VQwf+YJR+wwSltdY7FMurLS8ciZlCyWPpDjsr5i7qS60iXfoeY/fxslrlCZ4TqpSJ/1cqV1vOaKJr6qXyY8WvrzaYj7DNhuO7Us1Ko0J6cgh+z1AMuckrpysGXI07/4lOhWxUM+MKBEeaYKVIR97ymhLgY9F57aXWFHhXVWFfMZ6Igs/8YuQIapQKcl63Us0LrCp0fs6QDLnZKmQl13v6a9Vx8KgrWm6QVJBv5zcrJltiUD3HbSEjaER3l1qC0I9QQFeyqprjpc035NVptB6d3OjMTvKB+ji8WcfWTpz8lCbRbLiw0PStrDUr7Yxoc8Gxn7NrkNLdAlwdG7In3tsuNCvZLTgUvaBySc79jvQ6F5uMsi+i44I3J1pnc6r7Ss7T3K/wXusvp/Qq9H9PggGc7Y05X7uTMRFyOJtXJBxV4bxvvbePvoEAFqAAVoAJUgApQASpABagAFaCiaqiA4GYAqQSnSjhqQlD1TPKgAoJABQSBCggCFViDgrAGVcZU4P0BvF8BgQpQAYEKUAGBClABgQpQMavHBzP5UKzLtCruEVh7vpqgorqpkCKYfAdC6Ww6NLRgKmpyrlhBE8vyd9Rc2ZOzECrMknegZJmm8cNUO2f2+RKCYtksm0PVnK9m+RgIlLubwUqfnIVQYXV6ooIQddtK8a1RZHX6JLOn2vPVLB+zmXJ3vlnpk7OwuiId9vSxK/fsfpja1Kn2fDXLx5isAlzSVvTkzH+uYNd1xaZ/dj9M2V1TzqKMqKhuX01QUf1UMF8z2kwGKM+f1Q+TdUgERvoc5MVcfK6oYl9NUFELVIhcJOl6PtilWw9wzpkUy+Tab2VlBTkKCizIa89XE3VFddcV+au1yAUFfJeOHybvnEmFubSZjAaiUpDXnK8m1qCqdw0KWvz3K5bbxLL8HTVX9uSACry3jeMBFaACVIAKUAEqQAWoABWgAlSAClBR0VRAcDOAVIJTJRw1Iah6JnlQAUGgAoJABQSBCqxBQViDKmMq8P4A3q+AQAWogEAFqIBABahYmGrPFg1U1A4VZNcRTcrWN0Iy7hkAFaCixqkQDQcSPmZZRtYEw95owA0q5ktFq907veyGGkZRuFIHU27HMx8qmIdA3GMpMoGkE+PkPFtooclTIXZOxqPxRFqxLKg4a4KFU0FGFqLKgooVPJhyO575zhXkb+kd7uux5huHQ8wZasiqEwtaC03ybFYsbyyjYSHusdUkFcpvv1zmipU6mHI7nnnXFeGE4k4TFS01Ve5mRqkTZ4WWn2yYbWb1e/SDiqqnIjcJ9Awykyfm1K92wlTYMbDQZB9WoRKoABVVQ4VSDlBQa+eKIhaaqrmi1tegQEVVUDEwHo96BsWawuJg7n/M9VIsNlR1hY6FJm+bmQ67ndIq1kgg5O1DXQEqKpoK27CPEqPc6lJg2K6/BqW10CxYgxJkr/9Rh8UMKkBFFWVQ0MJXZplSfmd5rISuwMGU2/GACry3jeMBFaACVIAKUAEqQAWoABWgAlSAClBR6VRAcDOAVIJTJRw1Iah6JnlQAUGgAoJABQQtlAoIgnSoOAFBtSpDKiwQVKsCFRAEKiAIVEAQqIAgUAFBetq+ffv69euLr71SB+oGKqBa0YYNG/r7+39SVNSBuoEKqOJ16NAhr9ebTCZv3rz5/PPPF8mCTthPHD12lHTs2DHlnyLxsf2EXTfUF4+KsalseuI8vwFBS4CEIAiPHz/+5ptv6OvXX3/94osvGlHRw0RQiN96erhv8sbRnqPLQMUte11jm2pDDw9gA81XLpeLYKC5YsuWLVevXqXt8fHxtrY23ch+7rkj9N8rrwz985//vH//vrj9yr///e+pqRhtP3eE/j237FRQo3nTKmmjQEbtEFRUx48fP336NCFhtVo/+eQTouLs2bNGFfOzh+m/Z589fPiNN974z3/+8/777//973/PZDKNjY27d+8Rdz27QCp6XZEHsvff1I2XxYeSodWDiKu3+FzB9x17mZsr9AYR7k3dE1/nwa1XuxAGUKF27tx54MCBx6KGhoaIkNbWVt3Ittm6u23d0pdgMEhg/O1vfyOEaG5hu8R9C6Li+I272QfBV7sa161eterJNbveiWfv3Tq7q37NJrv3D9mpdw+361PBNq5EhHu/ce6qb2h9yfXb66c2u+9I7UdoTO0gwh89L7Vu2ma7fjd7x9W+A1EAaTQ8PPzll18ODg4aISFFNhUhh56h/+nfoV/7fk1U/PWvf7XZbGIrNT9D/xZEBYXr3bHm1fWNO9q5h6ZtlAV1UXBHrm503TGi4r9v3stmZiKB/xkb6T/U1rxV6fmu0SBrG5rbLK9OpNN++0YzYgAq1KlTp9xu965du4yQkCL74IEDB+nfgYMjIyOEBFUjRMXnn3++f/9+1s52HVgoFfF36nIxyj9kAR0dXfWLqBEVbZaunh9eHL0R/H0qK/zflVyu9W6RQSyW80TFb0+i+oC0ovim9OnSpUvt7e1FqKDo39+1/9y5c//4xz8SiQRNLP3f+96//vWvcDjctb+LRPsXnEHd/+AHVs3DZ16beJC95ztahIp3b9/91avH97Xt7n7/9zQhrHuLy6CMBgEVkLHobYrLly/v3bu3+Fyxb9++vey/vZ0dnU1NTU8/vYO2Ozo7qNqm0oKeTh0WWm2/Hb6f5art3MMHkbeO1q82GWdQP7xxVzH0v/ubwWYu9I0HARVQUVFYExJUeRehwtppldWZ+yJvdMoPOhe+MtvesqV+zZOs2t6waftO7mH9lhY2k+XWWzUbbds3iV1Xrd7Q0LyDX5k1HoSe1VgHKqD56dvf/vaOtrYOps78107lUUcnfd+xYwd1WyAVEFQxopVXivjifx1YX19P3UAFVCui9aWTJ0+eLqre3l7qBiogaHaBCggCFRAEKiAIVEAQqIAgUAFBK0YFBMF9uZAKeLVDtWzTDyogCFRAEKiAIFABQaACgkAFBIEKCAIVEAQqak3Nzc0mk2nt2rVG76rSLjKBdDqdOFfVS8VAIJ0ODJTSWOJzK1yEBJlTvP76628aaGJi4tGjR1999RXZySNGy5gKis6sSlE3qJin1q1bd+bMGbqr2KEnMgl+pCiZTCJGy5yKOYAAKoqf9BdEkTPkC2qRK14OiU8//ZTutUeMVhoVLGTD42HJAy05PmhhjX2+hCDPJ+m4j7WxbtFAVO4WGrarw93S54kmk/FoPJFOhkftSos0QjItKFSwQ6gKQuj02u128nX87LPPyGbCruj69es5JL744gvqWVdXhxitQCqy6ahnwGo22z1xIeFz8HutTl8iy9pYt2RoxEndrEOhtBAe4amgloRXBMVsGQ0LcY+N9Un42KgW20i4Kqm4ffu2FP0PHz48ceIE1Q8ej4dHgqyzn3jiCfKBRIxWUF0hEsKnNzZvQoONvF+VBQ3KD5TGEQaCRd5p8cSz0dthQQgNV3cGNTk5mWMgFovxtQSZZh88eJDWqdasWYMArdAMKhey7qjUgdKfUDwpyFmUhoqBAiroaQVl/MfRrLZ7lZ30w4cP8yTkRD8/fbTCtm3b6CvWx6uHCrr2C4nASJ+jx2o0VyTH+wzmCmWgdGCwuqnoFsXX1lLiRDBQ1kQm8rQXVFQPFfSNCgdWQjhHAglBpoKabBT61gEvKz+c3HMtrHIIu1nRIT4l5P2ROyoIUY/Dwg1RbXXFQUXvvfdeLnGij1PYunUr2cdLu0BFxdUVIgG6GZTTE5W6JmndSZkrBFpMkhqlVSbNGpT8kXvhUYLBbB+V17XS0VA0XY1U7Ofk9/tpliAYNm/e3NnZmWsHFWVPBbR4Wr9+/e7du62cqLZ+6qmn+MY9e/ZQN5wrUFEroo/xbGhoIIeVDgPRJ75Rh5aWFpwrUFEromSJwKCpwOivA2kXdaCPqMK5AhU1JFpl6unpOWYg2kUdcJZABQSBCggCFRAEKiAIVEAQqIAgUAEt5hmHVlbrRNFbpRs3bmxtbQUVK0+FCVoy0entK0GvvPLK+fPnaYP+9ow+rB5UgIoqp6L0zzSiuyBtNhtNGqACVIAKlUAFqAAVoAJUgIolo0Jzc/UcfdIgUFG2VGjngLnOFe5otupuo15BKpz+FHeZmZm80o24rnAqJK8b2YjAQnsSXpuuIZqOD1r13G66YCoi18TNpiOuWIYeNCGyK3uusJF7E5ma8ZvsFm1y+CBvAjI4UwzRtD5ooEJDhfRg2nuEAHGMRaQ5JCNPH00XJzPZ6TFxJukmelL+ftBTrhkUmyDEGCcohPCoxaxriKb1QXMjg9JQ0er0TRMUdpGATMRlbzU1dZzzz4htJlPHtQi1Xutg31PBC2CiNCp+/OMf/+Uvf+HtheghNS5pXcEu/eSZ6SBrWcntT88QTeuDBip064pUxOVoMhEUNBXIHZpcMWn+kLlIpcDEHOcKeqvuz3/+s4REKpUii+ulr7aHQ0IyHE6mQ0NmDRVsHxmiaX3QUG3rZFCtTu90JjN58e1INuV3Kj2ucY8cvpmskkdBc8ig6A74Dz/8MBgMkp/Q8qxBUdHA/MiduekhZ4hGE4hoiKb1QetDXaFTV4gTQ8p/gxKo4AW5qWNsmp8rUtMiOJgryn1l1jIqLj7lkyatIZqODxqo0NQV9ovBmWwqeE6/rmCtLHfqJkzARZlRoRG3DqXJoKA51RWp6UlWVxAB/d6Ydg1KYcHunc6itihrKpzjSWnxCVTgvW1QAYEKUAGBClCB8AUVoAJUgIoapQL3V5QFFVC5CVSssDqhJdZiXblABQSBCggCFRAEKiAIVGANCsIaVEVTUQ6L+jgM/jBABagAFaACVIAKUAEqQEX5UcHdYmEfDafhXwAqqpYKdkcpH9vMuEM/1vNU0C3d5HJg1RuqBM+PedzAVAn3PCEca5wK6qPXpXCoxQvxyqSild1zmvc0MHVc9E/LN66mYt6lsEPTDceVP4ylf8XloqKPnDzkS3467hvMG6VxM0JBpOpQwWxAsrK/oJ0c19KBsYk0P5doPDnZq4THw7J157j8wtxTLIO+uPSYrHncjrLxTygIR3K2EcWFo9M76b9IZgam1nPBlOLwscRUlMVhLP0rLvtcYXX6EllyTJvnXGFzRwUh6rax7+nQcIENodaTU4zxqGfAKmHEXlj1lD52Qzmz4LE4RgJh30C5UqHEJBeOnFh06O9ZitSlTA5jCV9xKaiYxao/P0nMToXOMCIX6bTMhCrEdTw5eWYkG2gtFQnfYI+1EuoKo3C0+2aWaa4op8NYwldcrrmCUptQPCkI+Vxp/nUFRXJWyaNUIa7jyakqIZQX4hvlA8tndhVHRbdsLWtaWSqW/TCW8BWXiQq6ijMP8j4HXZTnMlfotIu5UyIhUOJTdK7QKaz1qMiVLI5cglVJVDT1k0HazBJZj5dOxfIfxpK+4jJRQd8oiq2SVWZCmD8Vou0m5U52yoYkLlheJE8cOp6culRwTxnwhcUPDJCokD9EoFLqCrLqJzf+pfuMlxKpWP7DWOpXXK4MyumJyv6Z0UB0fnUFWzBic448R7CqXawt2AdgSPtHtZ6culRwT7lOkMovIzt7VgwVzIV5Jnihe0njoBwPY+lfEX/xURnvVyhLouIivWQ+zjcVrJYu+crsih7G0r8iqMB723hvG1SAClABKkAFqAAVoAJUgApQASpARe1QAcHNAFIJjpflb8IJKiDIDCogCFRAEKiAoKWiAoKwMAUqIKiQiv8H/uaTYxGeHbgAAAAASUVORK5CYII="},6694:(e,t,n)=>{n.d(t,{A:()=>o});const o=n.p+"assets/images/playhack-logo-xmas-451ccd5e2d37685bc0f90e7e5a066b00.jpg"},28453:(e,t,n)=>{n.d(t,{R:()=>a,x:()=>r});var o=n(96540);const i={},s=o.createContext(i);function a(e){const t=o.useContext(s);return o.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function r(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),o.createElement(s.Provider,{value:t},e.children)}}}]);
\ No newline at end of file
diff --git a/assets/js/8d62f5ef.67d24143.js b/assets/js/8d62f5ef.67d24143.js
deleted file mode 100644
index fc5e0a70b..000000000
--- a/assets/js/8d62f5ef.67d24143.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(self.webpackChunk_playcanvas_blog=self.webpackChunk_playcanvas_blog||[]).push([[25173],{84652:(e,t,a)=>{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>s,default:()=>m,frontMatter:()=>o,metadata:()=>c,toc:()=>h});var n=a(74848),r=a(28453);const o={authors:"nathan",slug:"playhack-december-player-character",title:"PLAYHACK December - Player Character"},s=void 0,c={permalink:"/playhack-december-player-character",editUrl:"https://github.com/playcanvas/blog/tree/main/blog/2014-12-08-playhack-december-player-character.md",source:"@site/blog/2014-12-08-playhack-december-player-character.md",title:"PLAYHACK December - Player Character",description:"**PLAYHACK** is our fun monthly game building session. Throughout the month I'll be posting tips and tricks to help you get a game made by the end of the month.\xa0Don\u2019t forget, these are just examples. You can make **any** game you like. Read more about this month's PLAYHACK.",date:"2014-12-08T00:00:00.000Z",tags:[],readingTime:8.095,hasTruncateMarker:!0,authors:[{name:"Nathan",page:{permalink:"/authors/nathan"},key:"nathan"}],frontMatter:{authors:"nathan",slug:"playhack-december-player-character",title:"PLAYHACK December - Player Character"},unlisted:!1,prevItem:{title:"PLAYCANVAS in LUDUM DARE 31",permalink:"/playcanvas-in-ludum-dare-31"},nextItem:{title:"PlayCanvas Update 5/12/14",permalink:"/playcanvas-update-51214"}},l={authorsImageUrls:[void 0]},h=[];function i(e){const t={a:"a",em:"em",img:"img",p:"p",strong:"strong",...(0,r.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(t.p,{children:(0,n.jsxs)(t.em,{children:[(0,n.jsx)(t.strong,{children:"PLAYHACK"})," is our fun monthly game building session. Throughout the month I'll be posting tips and tricks to help you get a game made by the end of the month.\xa0Don\u2019t forget, these are just examples. You can make ",(0,n.jsx)(t.strong,{children:"any"})," game you like. ",(0,n.jsx)(t.a,{href:"https://blog.playcanvas.com/playhack-december-jolly-santa/",children:"Read more"})," about this month's PLAYHACK."]})}),"\n",(0,n.jsx)(t.p,{children:(0,n.jsx)(t.img,{alt:"PLAYHACK Logo",src:a(6694).A+"",width:"603",height:"218"})})]})}function m(e={}){const{wrapper:t}={...(0,r.R)(),...e.components};return t?(0,n.jsx)(t,{...e,children:(0,n.jsx)(i,{...e})}):i(e)}},6694:(e,t,a)=>{a.d(t,{A:()=>n});const n=a.p+"assets/images/playhack-logo-xmas-451ccd5e2d37685bc0f90e7e5a066b00.jpg"},28453:(e,t,a)=>{a.d(t,{R:()=>s,x:()=>c});var n=a(96540);const r={},o=n.createContext(r);function s(e){const t=n.useContext(o);return n.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function c(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:s(e.components),n.createElement(o.Provider,{value:t},e.children)}}}]);
\ No newline at end of file
diff --git a/assets/js/8d62f5ef.74a1959b.js b/assets/js/8d62f5ef.74a1959b.js
new file mode 100644
index 000000000..0cd9ad527
--- /dev/null
+++ b/assets/js/8d62f5ef.74a1959b.js
@@ -0,0 +1 @@
+"use strict";(self.webpackChunk_playcanvas_blog=self.webpackChunk_playcanvas_blog||[]).push([[25173],{84652:(e,t,a)=>{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>s,default:()=>m,frontMatter:()=>o,metadata:()=>c,toc:()=>h});var n=a(74848),r=a(28453);const o={authors:"nathan",slug:"playhack-december-player-character",title:"PLAYHACK December - Player Character"},s=void 0,c={permalink:"/playhack-december-player-character",editUrl:"https://github.com/playcanvas/blog/tree/main/blog/2014-12-08-playhack-december-player-character.md",source:"@site/blog/2014-12-08-playhack-december-player-character.md",title:"PLAYHACK December - Player Character",description:"**PLAYHACK** is our fun monthly game building session. Throughout the month I'll be posting tips and tricks to help you get a game made by the end of the month.\xa0Don\u2019t forget, these are just examples. You can make **any** game you like. Read more about this month's PLAYHACK.",date:"2014-12-08T00:00:00.000Z",tags:[],readingTime:8.095,hasTruncateMarker:!0,authors:[{name:"Nathan Patel",title:"Technical Blogger",page:{permalink:"/authors/nathan"},key:"nathan"}],frontMatter:{authors:"nathan",slug:"playhack-december-player-character",title:"PLAYHACK December - Player Character"},unlisted:!1,prevItem:{title:"PLAYCANVAS in LUDUM DARE 31",permalink:"/playcanvas-in-ludum-dare-31"},nextItem:{title:"PlayCanvas Update 5/12/14",permalink:"/playcanvas-update-51214"}},l={authorsImageUrls:[void 0]},h=[];function i(e){const t={a:"a",em:"em",img:"img",p:"p",strong:"strong",...(0,r.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(t.p,{children:(0,n.jsxs)(t.em,{children:[(0,n.jsx)(t.strong,{children:"PLAYHACK"})," is our fun monthly game building session. Throughout the month I'll be posting tips and tricks to help you get a game made by the end of the month.\xa0Don\u2019t forget, these are just examples. You can make ",(0,n.jsx)(t.strong,{children:"any"})," game you like. ",(0,n.jsx)(t.a,{href:"https://blog.playcanvas.com/playhack-december-jolly-santa/",children:"Read more"})," about this month's PLAYHACK."]})}),"\n",(0,n.jsx)(t.p,{children:(0,n.jsx)(t.img,{alt:"PLAYHACK Logo",src:a(6694).A+"",width:"603",height:"218"})})]})}function m(e={}){const{wrapper:t}={...(0,r.R)(),...e.components};return t?(0,n.jsx)(t,{...e,children:(0,n.jsx)(i,{...e})}):i(e)}},6694:(e,t,a)=>{a.d(t,{A:()=>n});const n=a.p+"assets/images/playhack-logo-xmas-451ccd5e2d37685bc0f90e7e5a066b00.jpg"},28453:(e,t,a)=>{a.d(t,{R:()=>s,x:()=>c});var n=a(96540);const r={},o=n.createContext(r);function s(e){const t=n.useContext(o);return n.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function c(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:s(e.components),n.createElement(o.Provider,{value:t},e.children)}}}]);
\ No newline at end of file
diff --git a/assets/js/b0b988e7.7b627382.js b/assets/js/b0b988e7.7b627382.js
deleted file mode 100644
index 6ed094a42..000000000
--- a/assets/js/b0b988e7.7b627382.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(self.webpackChunk_playcanvas_blog=self.webpackChunk_playcanvas_blog||[]).push([[64714],{19696:a=>{a.exports=JSON.parse('{"author":{"name":"Nathan","page":{"permalink":"/authors/nathan"},"key":"nathan","count":3},"listMetadata":{"permalink":"/authors/nathan","page":1,"postsPerPage":10,"totalPages":1,"totalCount":3,"blogDescription":"Blog","blogTitle":"Blog"}}')}}]);
\ No newline at end of file
diff --git a/assets/js/b0b988e7.ad83c5bc.js b/assets/js/b0b988e7.ad83c5bc.js
new file mode 100644
index 000000000..2a9214d88
--- /dev/null
+++ b/assets/js/b0b988e7.ad83c5bc.js
@@ -0,0 +1 @@
+"use strict";(self.webpackChunk_playcanvas_blog=self.webpackChunk_playcanvas_blog||[]).push([[64714],{19696:a=>{a.exports=JSON.parse('{"author":{"name":"Nathan Patel","title":"Technical Blogger","page":{"permalink":"/authors/nathan"},"key":"nathan","count":3},"listMetadata":{"permalink":"/authors/nathan","page":1,"postsPerPage":10,"totalPages":1,"totalCount":3,"blogDescription":"Blog","blogTitle":"Blog"}}')}}]);
\ No newline at end of file
diff --git a/assets/js/b266de79.aa436a99.js b/assets/js/b266de79.7ebc10d4.js
similarity index 68%
rename from assets/js/b266de79.aa436a99.js
rename to assets/js/b266de79.7ebc10d4.js
index 610968bbf..1d8984f8e 100644
--- a/assets/js/b266de79.aa436a99.js
+++ b/assets/js/b266de79.7ebc10d4.js
@@ -1 +1 @@
-"use strict";(self.webpackChunk_playcanvas_blog=self.webpackChunk_playcanvas_blog||[]).push([[93518],{34369:e=>{e.exports=JSON.parse('{"archive":{"blogPosts":[{"id":"playcanvas-engine-hits-2-0-0","metadata":{"permalink":"/playcanvas-engine-hits-2-0-0","editUrl":"https://github.com/playcanvas/blog/tree/main/blog/2024-08-22-playcanvas-engine-hits-2-0-0.md","source":"@site/blog/2024-08-22-playcanvas-engine-hits-2-0-0.md","title":"PlayCanvas Engine Hits 2.0.0","description":"Today marks a major milestone for the PlayCanvas Engine, the open-source JavaScript runtime that powers thousands of interactive graphical apps and tools on the web. We are bubbling with excitement to announce the release of version 2.0.0!","date":"2024-08-22T00:00:00.000Z","tags":[{"inline":true,"label":"engine","permalink":"/tags/engine"}],"readingTime":3.56,"hasTruncateMarker":true,"authors":[{"name":"Will Eastcott","title":"CEO","description":"Will Eastcott is an entrepreneur and veteran technologist of the games industry with experience at EA, Sony, and Activision. He has been credited in many AAA game franchises such as GTA, Call of Duty and Max Payne. He is best known for co-founding PlayCanvas, the web graphics creation platform. As CEO, he has championed the company\'s mission to make graphical web app development more accessible and collaborative through open-source technologies and cloud-based tools.","page":{"permalink":"/authors/will"},"socials":{"x":"https://x.com/willeastcott","linkedin":"https://www.linkedin.com/in/willeastcott/","github":"https://github.com/willeastcott"},"imageURL":"https://media.licdn.com/dms/image/v2/D4D03AQF9YXIeHZW9kg/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1688995668268?e=1730332800&v=beta&t=UmAwlJ0nPBmnMczQ4wknx1jSDJ2anqurm2b15iLm3pM","key":"will"}],"frontMatter":{"authors":"will","slug":"playcanvas-engine-hits-2-0-0","title":"PlayCanvas Engine Hits 2.0.0","tags":["engine"]},"unlisted":false,"nextItem":{"title":"Create 3D Gaussian Splat Apps with the PlayCanvas Editor","permalink":"/create-3d-gaussian-splat-apps-with-the-playcanvas-editor"}},"content":"Today marks a major milestone for the PlayCanvas Engine, the [open-source](https://github.com/playcanvas/engine) JavaScript runtime that powers thousands of interactive graphical apps and tools on the web. We are bubbling with excitement to announce the release of version 2.0.0!\\n\\n\x3c!-- truncate --\x3e\\n\\nFor the full details, visit our GitHub:\\n\\n[**RELEASE NOTES**](https://github.com/playcanvas/engine/releases/tag/v2.0.0)\\n\\nIt\'s not every day we do a major version bump of the Engine. Let\'s take a walk down memory lane to see how we got here:\\n\\n* October 2010: Coding of the Engine begins!\\n* 24 October 2011: [Engine migrated to GitHub](https://github.com/playcanvas/engine/commit/e5bf014e738d5bfc92ece1d6c0f50ad71bf4dd90)\\n* 4 June 2014: [Engine goes open source](https://blog.playcanvas.com/playcanvas-goes-open-source/)\\n* 24 April 2018: [Engine hits 1.0.0](https://blog.playcanvas.com/playcanvas-engine-reaches-1-0-0/)\\n\\nBack in 2018, the Engine was bumped to 1.0.0 because we adhere to [semantic versioning](https://semver.org/) which dictates:\\n\\n> If your software is being used in production, it should probably already be 1.0.0. If you have a stable API on which users have come to depend, you should be 1.0.0. If you\u2019re worrying a lot about backward compatibility, you should probably already be 1.0.0.\\n\\nWith hindsight, the Engine should probably have reached 1.0.0 some years before. It had long been stable and was already being used heavily in production!\\n\\n### Why Bump to 2.0.0\\n\\nSemantic versioning says that you perform a major version bump when you introduce breaking changes. To say we go out of our way to avoid introducing breaking changes in an understatement. But since 1.0.0, we had done 73 minor versions along with countless patch releases. Along the way, the Engine accumulated a considerable amount of \\"cruft\\".\\n\\n:::info[cruft _noun_ `INFORMAL - COMPUTING`]\\n\\nbadly designed, unnecessarily complicated, or unwanted code or software.\\n\\n:::\\n\\nIt was getting to the point where parts of the codebase were restricting our ability to advance the Engine\'s capabilities. And so, we took to the decision to do a spring clean. \ud83e\uddf9 Here are some highlights of what we removed:\\n\\n* **WebGL 1 support.** This is the big one. Today, devices that support WebGL 1 but not WebGL 2 is less than 2% and this number is only going to get smaller. And supporting WebGL 1 was making it extraordinarily difficult to construct the foundations we needed in order to support WebGPU. Cheerio, WebGL 1 - it\'s been fun! \ud83d\udc4b\\n* **Scripts 1.0 support.** The very first `ScriptComponent` implementation was deprecated with the introduction of [Scripts 2.0](https://blog.playcanvas.com/playcanvas-scripts-2-0/) back in 2016. So after 8 years of deprecation, it\'s gone! We are now focused on delivering our new ESM-based scripting system, due soon.\\n* **AudioSourceComponent.** This component was the precursor to the Engine\'s [`SoundComponent`](https://api.playcanvas.com/classes/Engine.SoundComponent.html). `AudioSourceComponent` has been deprecated for many years so it\'s time for it to go.\\n* **...and several other public API symbols.** What we are left with is a cleaner, tighter codebase that can power us into the future!\\n\\n### What if my Project Breaks\\n\\nNever fear! Today\'s release is more relevant to \'Engine-only\' users who pull the engine from [NPM](https://www.npmjs.com/package/playcanvas). At a time of their choosing, these developers can upgrade to 2.0.0 and give it a try. The vast majority of projects will update without modification.\\n\\nEditor users will gain access to Engine 2.0.0 in the coming weeks. At that point, moving to 2.0.0 will be _opt in_ for existing projects. We plan to support the last Engine 1.x release in the Editor for at least a year after Engine 2 becomes available. This should be plenty of time for developers to migrate. New projects will automatically use 2.0.0. We will release more information about the transition for Editor users in the coming weeks.\\n\\nIn the meantime, we ask NPM users to try Engine 2.0.0 and give us your feedback as soon as you can. We hope you like it! Head over to the [Forum](https://forum.playcanvas.com/), [Discord](https://discord.gg/RSaMRzg) or [GitHub](https://github.com/playcanvas/engine) to have your say. :ear:\\n\\n### What\'s New\\n\\nThe 2.0.0 release isn\'t just about breaking changes! We have some exciting features to announce by way of some brand new Engine examples:\\n\\n\\n\\nCheck \'em out:\\n\\n* [Custom Shaders: Cross Hatching](https://playcanvas.vercel.app/#/graphics/shader-hatch)\\n* [Custom Shaders: Used with Skinning and Instancing](https://playcanvas.vercel.app/#/graphics/instancing-gooch)\\n* [Screen Space Ambient Occlusion](https://playcanvas.vercel.app/#/graphics/ambient-occlusion)\\n* [Hardware Instancing](https://playcanvas.vercel.app/#/graphics/instancing-custom)\\n* [glTF Hardware Instancing Extension](https://playcanvas.vercel.app/#/graphics/instancing-glb)\\n\\n### Thanking the Open Source Community\\n\\nWe _could not_ have reached this point without the amazing open source community:\\n\\n\\n\\nThank you for your incredible contributions. \ud83d\ude4f"},{"id":"create-3d-gaussian-splat-apps-with-the-playcanvas-editor","metadata":{"permalink":"/create-3d-gaussian-splat-apps-with-the-playcanvas-editor","editUrl":"https://github.com/playcanvas/blog/tree/main/blog/2024-06-05-create-3d-gaussian-splat-apps-with-the-playcanvas-editor.md","source":"@site/blog/2024-06-05-create-3d-gaussian-splat-apps-with-the-playcanvas-editor.md","title":"Create 3D Gaussian Splat Apps with the PlayCanvas Editor","description":"CLICK HERE to open in a new tab. Credits: Splats scanned at the V&A Museum. HDRI from Poly Haven.","date":"2024-06-05T00:00:00.000Z","tags":[{"inline":true,"label":"gaussian-splats","permalink":"/tags/gaussian-splats"},{"inline":true,"label":"supersplat","permalink":"/tags/supersplat"}],"readingTime":2.6,"hasTruncateMarker":true,"authors":[{"name":"Will Eastcott","title":"CEO","description":"Will Eastcott is an entrepreneur and veteran technologist of the games industry with experience at EA, Sony, and Activision. He has been credited in many AAA game franchises such as GTA, Call of Duty and Max Payne. He is best known for co-founding PlayCanvas, the web graphics creation platform. As CEO, he has championed the company\'s mission to make graphical web app development more accessible and collaborative through open-source technologies and cloud-based tools.","page":{"permalink":"/authors/will"},"socials":{"x":"https://x.com/willeastcott","linkedin":"https://www.linkedin.com/in/willeastcott/","github":"https://github.com/willeastcott"},"imageURL":"https://media.licdn.com/dms/image/v2/D4D03AQF9YXIeHZW9kg/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1688995668268?e=1730332800&v=beta&t=UmAwlJ0nPBmnMczQ4wknx1jSDJ2anqurm2b15iLm3pM","key":"will"}],"frontMatter":{"authors":"will","slug":"create-3d-gaussian-splat-apps-with-the-playcanvas-editor","title":"Create 3D Gaussian Splat Apps with the PlayCanvas Editor","tags":["gaussian-splats","supersplat"]},"unlisted":false,"prevItem":{"title":"PlayCanvas Engine Hits 2.0.0","permalink":"/playcanvas-engine-hits-2-0-0"},"nextItem":{"title":"A Faster SuperSplat with PWA Support","permalink":"/a-faster-supersplat-with-pwa-support"}},"content":"import ReactPlayer from \'react-player\'\\n\\n
\\n \\n
\\n_[CLICK HERE](https://playcanv.as/e/p/cLkf99ZV/) to open in a new tab. Credits: Splats scanned at the [V&A Museum](https://www.vam.ac.uk/). HDRI from [Poly Haven](https://polyhaven.com/a/sepulchral_chapel_rotunda)._\\n\\nWe have big news for the 3D Gaussian Splat community - the PlayCanvas Editor now has fully integrated support for splats!\\nLearn how to quickly build stunning, interactive 3DGS applications today.\\n\\n\x3c!-- truncate --\x3e\\n\\n:::note[What you need]\\n\\n\ud83e\udd33 A smartphone \\n\ud83d\udcbb A computer with a web browser \\n\u23f1\ufe0f A small amount of time\\n\\n:::\\n\\nThe application above shows several splats assembled in a single application, with animation and post effects spicing up the visuals. Let\'s check out how it was built.\\n\\n### Step 1: Clean in SuperSplat \ud83e\uddf9\\n\\nAfter [capturing the statues](https://developer.playcanvas.com/user-manual/graphics/gaussian-splatting/#creating-splats) to PLY format, our first stop is [SuperSplat](https://playcanvas.com/supersplat/editor?load=https://raw.githubusercontent.com/willeastcott/assets/main/statues/narcissus.compressed.ply), the open source tool for editing and optimizing 3D Gaussian Splats. Here, in a little over a minute, we can isolate the statue from the background and align it with the origin:\\n\\n\\n\\n \\nOnce we are done, we can download the splat using our [compressed PLY format](https://blog.playcanvas.com/compressing-gaussian-splats). In this case, our downloaded PLY is **only 1.56MB**!\\n\\n### Step 2: Import into the Editor \ud83d\udea7\\n\\nNow that we have a clean, compressed PLY, we simply need to drop it into the Editor\'s Asset Panel. And from there, drag it into the viewport to add it to the scene. Let\'s do that (along with a cube map for a photographic backdrop):\\n\\n\\n\\n \\nThe PlayCanvas Editor is a powerful visual environment for building and publishing 3D scenes. You can:\\n\\n* Grab useful scripts (and other assets) from the Asset Store. Here, we import an Orbit Camera script.\\n* Create beautiful user interfaces, using either HTML or PlayCanvas\' built-in UI system.\\n* Add sound, physics, VR/AR support and much, much more.\\n\\n### Step 3: Add Animation and Post Effects \u2728\\n\\nWhat really makes the demo pop is the transitions that fade the statues in and out.\\n\\n\\n\\n \\nWith the Editor, you can customize the shader code that renders your splats to apply stunning animation effects. For the transition between statues, individual splats are transformed and recolored over time, while a full-screen bloom effect is ramped up and down.\\n\\n### Resources\\n\\nToday\'s release makes working with 3D Gaussian Splats both easy and fun! We\'ve shown you how to build a virtual gallery or museum but the possibilities are endless. With 3D Gaussian Splats in the PlayCanvas Editor, you can target many verticals: product visualization (furniture, clothing, consumer electronics), automotive, education, travel and so much more.\\n\\nTo get started, here is an useful list of resources:\\n\\n* [Statue Project](https://playcanvas.com/project/1224723/overview/3d-gaussian-splat-statues) - feel free to fork it, explore and experiment.\\n* [3D Gaussian Splatting](https://developer.playcanvas.com/user-manual/graphics/gaussian-splatting/) User Guide\\n* [SuperSplat](https://playcanvas.com/supersplat/editor) (don\'t forget to [install the PWA](https://blog.playcanvas.com/a-faster-supersplat-with-pwa-support#pwa-support))\\n\\n### Go Forth and Create\\n\\nWe hope you love today\'s update as much as we do! \u2764\ufe0f\\n\\nBut let us know what you think by heading over to the [forum](https://forum.playcanvas.com) or [ping us on X](https://x.com/playcanvas)!"},{"id":"a-faster-supersplat-with-pwa-support","metadata":{"permalink":"/a-faster-supersplat-with-pwa-support","editUrl":"https://github.com/playcanvas/blog/tree/main/blog/2024-05-22-a-faster-supersplat-with-pwa-support.md","source":"@site/blog/2024-05-22-a-faster-supersplat-with-pwa-support.md","title":"A Faster SuperSplat with PWA Support","description":"Today, we are announcing the latest release of SuperSplat, the open source tool for editing and optimizing 3D Gaussian Splats. If you don\'t have a PLY file to hand, here\'s an example!","date":"2024-05-22T00:00:00.000Z","tags":[{"inline":true,"label":"gaussian-splats","permalink":"/tags/gaussian-splats"},{"inline":true,"label":"supersplat","permalink":"/tags/supersplat"}],"readingTime":1.82,"hasTruncateMarker":true,"authors":[{"name":"Will Eastcott","title":"CEO","description":"Will Eastcott is an entrepreneur and veteran technologist of the games industry with experience at EA, Sony, and Activision. He has been credited in many AAA game franchises such as GTA, Call of Duty and Max Payne. He is best known for co-founding PlayCanvas, the web graphics creation platform. As CEO, he has championed the company\'s mission to make graphical web app development more accessible and collaborative through open-source technologies and cloud-based tools.","page":{"permalink":"/authors/will"},"socials":{"x":"https://x.com/willeastcott","linkedin":"https://www.linkedin.com/in/willeastcott/","github":"https://github.com/willeastcott"},"imageURL":"https://media.licdn.com/dms/image/v2/D4D03AQF9YXIeHZW9kg/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1688995668268?e=1730332800&v=beta&t=UmAwlJ0nPBmnMczQ4wknx1jSDJ2anqurm2b15iLm3pM","key":"will"}],"frontMatter":{"authors":"will","slug":"a-faster-supersplat-with-pwa-support","title":"A Faster SuperSplat with PWA Support","tags":["gaussian-splats","supersplat"]},"unlisted":false,"prevItem":{"title":"Create 3D Gaussian Splat Apps with the PlayCanvas Editor","permalink":"/create-3d-gaussian-splat-apps-with-the-playcanvas-editor"},"nextItem":{"title":"Build WebGPU Apps Today with PlayCanvas","permalink":"/build-webgpu-apps-today-with-playcanvas"}},"content":"import ReactPlayer from \'react-player\'\\n\\nToday, we are announcing the latest release of [SuperSplat](https://playcanvas.com/supersplat/editor), the open source tool for editing and optimizing 3D Gaussian Splats. If you don\'t have a PLY file to hand, [here\'s an example](https://playcanvas.com/supersplat/editor?load=https://raw.githubusercontent.com/playcanvas/engine/main/examples/assets/splats/biker.ply)!\\n\\n\\n\\n\x3c!-- truncate --\x3e\\n\\n[Version 0.17.1](https://github.com/playcanvas/supersplat/releases/tag/v1.17.1) focuses on two key areas: performance and PWA support.\\n\\n## Performance Improvements\\n\\nSuperSplat is now *over 2x faster on the GPU*! \ud83c\udfc3\\n\\nCompare before and after (notice GPU time dropping from 32ms to 13.5ms for the bike scene):\\n\\n\\n\\nThis is thanks to the [v1.71.0 release](https://github.com/playcanvas/engine/releases/tag/v1.71.0) of the PlayCanvas Engine, which includes a dramatic overhaul of how splats are processed by the GPU. For the technical details, take a look at [this pull request](https://github.com/playcanvas/engine/pull/6357).\\n\\nThe result is that SuperSplat can now throw around millions of splats and still maintain a silky smooth frame rate. Try it for yourself!\\n\\n## PWA Support\\n\\nA Progressive Web App (PWA) is a web application that provides a native app-like experience, including the ability to install it on a user\'s home screen or desktop.\\n\\nFrom today, SuperSplat is shipping with PWA support! \ud83c\udf89\\n\\n\\n\\n \\nTo install SuperSplat as a PWA:\\n\\n1. Visit [https://playcanvas.com/supersplat/editor](https://playcanvas.com/supersplat/editor).\\n2. Hit the `Install SuperSplat` button in the address bar.\\n\\n:::tip\\n\\nFor your convenience, pin SuperSplat to the Taskbar (Windows) or add it do the Dock (macOS).\\n\\n:::\\n\\n### PLY File Association\\n\\nWith SuperSplat installed as a PWA, your operating system can now open launch PLY files directly into the tool. Simply right-click on a PLY file and select SuperSplat to open it.\\n\\n\\n\\n \\nYou can also set SuperSplat as the default tool to open your PLYs. Then, you can simply double-click a PLY file to open it instantly in SuperSplat!\\n\\n## Your Feedback Matters\\n\\nWe hope you love today\'s update! \u2764\ufe0f\\n\\nThe SuperSplat community has grown a lot in recent weeks and we want to get your feedback. What other features would you like the PWA to get? Are you still experiencing any performance problems? What is still missing from SuperSplat? Let us know by heading over to the [forum](https://forum.playcanvas.com) or [ping us on X](https://x.com/playcanvas)!"},{"id":"build-webgpu-apps-today-with-playcanvas","metadata":{"permalink":"/build-webgpu-apps-today-with-playcanvas","editUrl":"https://github.com/playcanvas/blog/tree/main/blog/2024-04-18-build-webgpu-apps-today-with-playcanvas.md","source":"@site/blog/2024-04-18-build-webgpu-apps-today-with-playcanvas.md","title":"Build WebGPU Apps Today with PlayCanvas","description":"It\'s here! \ud83e\udd73 Today, we\'re excited to announce that WebGPU support has officially arrived in the PlayCanvas Editor.","date":"2024-04-18T00:00:00.000Z","tags":[{"inline":true,"label":"webgpu","permalink":"/tags/webgpu"},{"inline":true,"label":"graphics","permalink":"/tags/graphics"},{"inline":true,"label":"editor","permalink":"/tags/editor"}],"readingTime":2.105,"hasTruncateMarker":true,"authors":[{"name":"Will Eastcott","title":"CEO","description":"Will Eastcott is an entrepreneur and veteran technologist of the games industry with experience at EA, Sony, and Activision. He has been credited in many AAA game franchises such as GTA, Call of Duty and Max Payne. He is best known for co-founding PlayCanvas, the web graphics creation platform. As CEO, he has championed the company\'s mission to make graphical web app development more accessible and collaborative through open-source technologies and cloud-based tools.","page":{"permalink":"/authors/will"},"socials":{"x":"https://x.com/willeastcott","linkedin":"https://www.linkedin.com/in/willeastcott/","github":"https://github.com/willeastcott"},"imageURL":"https://media.licdn.com/dms/image/v2/D4D03AQF9YXIeHZW9kg/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1688995668268?e=1730332800&v=beta&t=UmAwlJ0nPBmnMczQ4wknx1jSDJ2anqurm2b15iLm3pM","key":"will"}],"frontMatter":{"authors":"will","slug":"build-webgpu-apps-today-with-playcanvas","title":"Build WebGPU Apps Today with PlayCanvas","tags":["webgpu","graphics","editor"]},"unlisted":false,"prevItem":{"title":"A Faster SuperSplat with PWA Support","permalink":"/a-faster-supersplat-with-pwa-support"},"nextItem":{"title":"Using Visual Studio Code with PlayCanvas","permalink":"/using-visual-studio-code-with-playcanvas"}},"content":"It\'s here! \ud83e\udd73 Today, we\'re excited to announce that WebGPU support has officially arrived in the PlayCanvas Editor.\\n\\n\\n\\n\x3c!-- truncate --\x3e\\n\\n## WebGPU on the Rise\\n\\nSince its inception back in 2010, PlayCanvas has been layered on top of WebGL. In 2017, we were proud to launch support for [WebGL 2.0](https://blog.playcanvas.com/mozilla-launches-webgl-2-with-playcanvas/) in partnership with our friends at Mozilla. April 2023 marked the beginning of a new era for web graphics when Google enabled WebGPU by default in Chrome 113. Since then, WebGPU adoption has exploded and today, [Web3D Survey](https://web3dsurvey.com/webgpu) reports that **62.19% of end users can now run WebGPU**. With [Firefox and Safari due to launch their WebGPU support](https://caniuse.com/webgpu) in the not-too-distant future, expect this number to rise dramatically in 2024.\\n\\n## Why WebGPU Matters\\n\\nIf you enable WebGPU for your PlayCanvas project, you may not notice much difference to begin with. In fact, we have put a great deal of effort into ensuring your WebGL projects look identical under WebGPU. But over time, there is are a great deal of opportunities to achieve performance improvements due to WebGPU\'s reduced driver overhead.\\n\\nAnother key feature unique to WebGPU is support for Compute Shaders which allow for general computation on the GPU. Support for Compute Shaders landed in [Engine v1.70.0](https://github.com/playcanvas/engine/releases/tag/v1.70.0). Here you can see Computer Shaders in action simulating 1 million particles on the GPU:\\n\\n
\\n \\n
\\n\\n[Click here](https://playcanvas.github.io/#/compute/particles) to run it for yourself in a WebGPU-enabled browser (i.e. Chrome or Edge).\\n\\nIn short, WebGPU represents the future for PlayCanvas and you can expect some incredible advances in performance and functionality over the coming months.\\n\\n## Getting Started with WebGPU\\n\\nWebGPU support in PlayCanvas is still considered \'Beta\'. There are still some unimplemented features (for example, the run-time lightmapper is still not supported). Therefore, you have to currently \'opt in\' to WebGPU support. To do this, open your Project\'s Settings in the Inspector and expand the `RENDERING` section. Then update `Graphics Devices` to include `WebGPU (beta)`.\\n\\n\\n\\nOnce we are satisfied WebGPU support has matured enough, it will become the default.\\n\\n## Your Feedback is Important\\n\\nSince WebGPU support is new, we rely on the community for feedback. What works and what doesn\'t? Please [submit an issue](https://github.com/playcanvas/editor/issues) if you discover any problems or kick off a new thread on the [forum](https://forum.playcanvas.com/) if you want to discuss WebGPU support in more detail. We want to hear what your opinions! \ud83d\udc42"},{"id":"using-visual-studio-code-with-playcanvas","metadata":{"permalink":"/using-visual-studio-code-with-playcanvas","editUrl":"https://github.com/playcanvas/blog/tree/main/blog/2024-04-15-using-visual-studio-code-with-playcanvas.md","source":"@site/blog/2024-04-15-using-visual-studio-code-with-playcanvas.md","title":"Using Visual Studio Code with PlayCanvas","description":"Visual Studio Code is massively popular. In the Stack Overflow 2023 Developer Survey, Visual Studio Code was ranked the most popular developer environment tool among 86,544 respondents, with 73.71% reporting that they use it.","date":"2024-04-15T00:00:00.000Z","tags":[{"inline":true,"label":"javascript","permalink":"/tags/javascript"},{"inline":true,"label":"open-source","permalink":"/tags/open-source"},{"inline":true,"label":"scripting","permalink":"/tags/scripting"},{"inline":true,"label":"workflow","permalink":"/tags/workflow"}],"readingTime":1.325,"hasTruncateMarker":true,"authors":[{"name":"Will Eastcott","title":"CEO","description":"Will Eastcott is an entrepreneur and veteran technologist of the games industry with experience at EA, Sony, and Activision. He has been credited in many AAA game franchises such as GTA, Call of Duty and Max Payne. He is best known for co-founding PlayCanvas, the web graphics creation platform. As CEO, he has championed the company\'s mission to make graphical web app development more accessible and collaborative through open-source technologies and cloud-based tools.","page":{"permalink":"/authors/will"},"socials":{"x":"https://x.com/willeastcott","linkedin":"https://www.linkedin.com/in/willeastcott/","github":"https://github.com/willeastcott"},"imageURL":"https://media.licdn.com/dms/image/v2/D4D03AQF9YXIeHZW9kg/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1688995668268?e=1730332800&v=beta&t=UmAwlJ0nPBmnMczQ4wknx1jSDJ2anqurm2b15iLm3pM","key":"will"}],"frontMatter":{"authors":"will","slug":"using-visual-studio-code-with-playcanvas","title":"Using Visual Studio Code with PlayCanvas","tags":["javascript","open-source","scripting","workflow"]},"unlisted":false,"prevItem":{"title":"Build WebGPU Apps Today with PlayCanvas","permalink":"/build-webgpu-apps-today-with-playcanvas"},"nextItem":{"title":"Massive Upgrade for the PlayCanvas Developer Site","permalink":"/massive-upgrade-for-the-playcanvas-developer-site"}},"content":"[Visual Studio Code](https://code.visualstudio.com/) is massively popular. In the Stack Overflow 2023 Developer Survey, Visual Studio Code was ranked the most popular developer environment tool among 86,544 respondents, with **73.71%** reporting that they use it.\\n\\nAny PlayCanvas developer building directly on top of the Engine will very likely opt to use VS Code. But if you use the PlayCanvas Editor, you will normally rely on the built-in, browser-based Code Editor. Today, we\'re excited to give you another option by launching an open-source **Visual Studio Code Extension for PlayCanvas**.\\n\\n\\n\\n\x3c!-- truncate --\x3e\\n\\n## Benefits\\n\\nThe PlayCanvas Code Editor is actually built on the [Monaco Editor](https://github.com/microsoft/monaco-editor), the beating heart of VS Code. So why use VS Code instead of the PlayCanvas Code Editor?\\n\\n* [GitHub Copilot](https://github.com/features/copilot) - leverage AI to help you write PlayCanvas code faster.\\n* Powerful IntelliSense tools (code completion, parameter info, quick info, and member lists).\\n* Leverage a huge library of other extensions to accelerate your development.\\n\\n## We \u2764\ufe0f Open Source\\n\\nAs you might expect, we have open sourced the VS Code extension under a liberal MIT license!\\n\\n* [VS Code Extension on GitHub](https://github.com/playcanvas/vscode-extension)\\n\\nSo if you find a bug or have a suggestion, please do [log an issue](https://github.com/playcanvas/vscode-extension/issues). And for the more adventurous, consider making a code contribution!\\n\\nWe want you to feel empowered to make these tools your own. Let\'s make them awesome together! \ud83d\ude4c\\n\\n## Get Started Now\\n\\nIf this all sounds great to you, why not give it a try? Head over to the User Manual for instructions on how to get started:\\n\\n[READ THE DOCS](https://developer.playcanvas.com/user-manual/scripting/vscode-extension/)"},{"id":"massive-upgrade-for-the-playcanvas-developer-site","metadata":{"permalink":"/massive-upgrade-for-the-playcanvas-developer-site","editUrl":"https://github.com/playcanvas/blog/tree/main/blog/2024-02-08-massive-upgrade-for-playcanvas-developer-site.md","source":"@site/blog/2024-02-08-massive-upgrade-for-playcanvas-developer-site.md","title":"Massive Upgrade for the PlayCanvas Developer Site","description":"Today, we are excited to announce the brand new PlayCanvas Developer Site and API Reference Manual!","date":"2024-02-08T00:00:00.000Z","tags":[{"inline":true,"label":"documentation","permalink":"/tags/documentation"},{"inline":true,"label":"tutorial","permalink":"/tags/tutorial"},{"inline":true,"label":"open-source","permalink":"/tags/open-source"}],"readingTime":3.435,"hasTruncateMarker":true,"authors":[{"name":"Will Eastcott","title":"CEO","description":"Will Eastcott is an entrepreneur and veteran technologist of the games industry with experience at EA, Sony, and Activision. He has been credited in many AAA game franchises such as GTA, Call of Duty and Max Payne. He is best known for co-founding PlayCanvas, the web graphics creation platform. As CEO, he has championed the company\'s mission to make graphical web app development more accessible and collaborative through open-source technologies and cloud-based tools.","page":{"permalink":"/authors/will"},"socials":{"x":"https://x.com/willeastcott","linkedin":"https://www.linkedin.com/in/willeastcott/","github":"https://github.com/willeastcott"},"imageURL":"https://media.licdn.com/dms/image/v2/D4D03AQF9YXIeHZW9kg/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1688995668268?e=1730332800&v=beta&t=UmAwlJ0nPBmnMczQ4wknx1jSDJ2anqurm2b15iLm3pM","key":"will"}],"frontMatter":{"authors":"will","slug":"massive-upgrade-for-the-playcanvas-developer-site","title":"Massive Upgrade for the PlayCanvas Developer Site","tags":["documentation","tutorial","open-source"]},"unlisted":false,"prevItem":{"title":"Using Visual Studio Code with PlayCanvas","permalink":"/using-visual-studio-code-with-playcanvas"},"nextItem":{"title":"Compressing Gaussian Splats","permalink":"/compressing-gaussian-splats"}},"content":"Today, we are excited to announce the brand new [PlayCanvas Developer Site](https://developer.playcanvas.com/) and [API Reference Manual](https://api.playcanvas.com/)!\\n\\n## Developer Site\\n\\nLet\'s start by taking a look at the new Developer Site, home of the PlayCanvas User Manual and Tutorials.\\n\\n\\n\\n\x3c!-- truncate --\x3e\\n\\n### Migrating from Metalsmith to Docusaurus\\n\\nSince PlayCanvas was born in 2011, the basic look of the Developer Site has changed little. The content was written in Markdown and converted to a static HTML site using a tool called [Metalsmith](https://metalsmith.io/). Customizing the site to our needs meant we had to write many Metalsmith plugins (for localization, navigation and more) and a lot of HTML and CSS to style the pages as we wanted.\\n\\n13 years later, the world of static site generators has moved on with lots of exciting new options! We evaluated [Docusaurus](https://docusaurus.io/) and decided to migrate over to it.\\n\\n### What\'s New\\n\\nSo what made Docusaurus so compelling? First up, the migration was going to be straightforward because, like Metalsmith, Docusaurus consumes Markdown and outputs a static site. And much of the customization we did for Metalsmith is supplied \'out-of-the-box\' with Docusaurus. So that\'s all great. But the new site comes with lots of cool new features we known you\'re going to love:\\n\\n#### \ud83d\udd0d Powerful Search\\n\\nThe site now integrates [Algolia](https://www.algolia.com/) for advanced searching of the documentation.\\n\\n\\n\\nThis is a quantum leap over what came before. Try it (by pressing `CTRL + K`) - you\'ll be amazed. \ud83e\udd2f\\n\\n#### \ud83c\udf12 Light and Dark Modes\\n\\nEasily switch between light and dark themes (by clicking on the sun/moon icon top-right).\\n\\n\\n\\n#### \ud83c\udf0f Language Selection\\n\\nSwitch language from the nav-bar. At the moment, we\'re shipping with Japanese translations.\\n\\n\\n\\n#### \ud83d\udc69\u200d\ud83d\udcbb Edit on GitHub\\n\\nAs you might expect, [the Developer Site is Open Source (MIT)](https://github.com/playcanvas/developer.playcanvas.com). Every page now has a handy link to the content on GitHub. Spot a mistake? Now can you fix it yourself and improve the docs for the community!\\n\\n#### \ud83e\udded Easier Navigation\\n\\nAt the top of each page, you\'ll find \'Sidebar Breadcrumbs\' which allow you to click back up the page hierarchy.\\n\\n\\n\\nTo the right of each page, you\'ll find a table of contents which can be a big help, especially for long pages.\\n\\n\\n\\nAnd at the bottom of each page, you\'ll find \'Previous\' and \'Next\' links that allow you to read through the User Manual sequentially.\\n\\n\\n\\n## API Reference\\n\\nWe are not just launching a new Developer Site today. We are also launching our brand new, upgraded [API Reference Manual](https://api.playcanvas.com/)!\\n\\n\\n\\n### Migrating from JSDoc to TypeDoc\\n\\nSince the start, we have relied on [JSDoc](https://jsdoc.app/) to generate our API reference manual. But since then, we have seen the introduction of [TypeDoc](https://typedoc.org/) which offers some major advantages over JSDoc.\\n\\n#### \ud83c\udfa8 High Quality Default Theme\\n\\nWhile it\'s not perfect, the default TypeDoc theme is gorgeous and a big improvement over our custom JSDoc theme. It provides:\\n\\n* Light and dark modes (like the main developer site)\\n* Links to the source code on GitHub\\n* Grouping of API into related categories\\n* Powerful search (press `/` to activate it)\\n* ...and much more!\\n\\nIt\'s a great foundation for us to begin with and we can customize the default theme to our requirements as needed.\\n\\n#### \ud83d\udd17 Easy Combining of APIs\\n\\nDid you know that PlayCanvas develops frameworks and libraries other than the PlayCanvas Engine? There\'s [PCUI](https://github.com/playcanvas/pcui), a front-end framework for web-based tools. There\'s the [PlayCanvas Editor API](https://github.com/playcanvas/editor-api) for automating the interface. And more! The new API reference collects all of the PlayCanvas APIs into a single manual.\\n\\n\\n\\n## Open Source FTW\\n\\nEverything we are announcing today is 100% open source.\\n\\n* [Developer Site on GitHub](https://github.com/playcanvas/developer.playcanvas.com)\\n* [API Reference Site on GitHub](https://github.com/playcanvas/api-reference)\\n\\nWe want to empower to community to get involved and make PlayCanvas better for everybody. It\'s never been easier to submit your first pull request on GitHub. So why wait - get started today! \u2764\ufe0f"},{"id":"compressing-gaussian-splats","metadata":{"permalink":"/compressing-gaussian-splats","editUrl":"https://github.com/playcanvas/blog/tree/main/blog/2023-12-05-compressing-gaussian-splats.md","source":"@site/blog/2023-12-05-compressing-gaussian-splats.md","title":"Compressing Gaussian Splats","description":"Introduction","date":"2023-12-05T00:00:00.000Z","tags":[{"inline":true,"label":"gaussian-splats","permalink":"/tags/gaussian-splats"},{"inline":true,"label":"compression","permalink":"/tags/compression"},{"inline":true,"label":"supersplat","permalink":"/tags/supersplat"}],"readingTime":3.625,"hasTruncateMarker":true,"authors":[{"name":"Donovan Hutchence","title":"Staff Software Engineer","page":{"permalink":"/authors/donovan"},"socials":{"x":"https://x.com/slimbuck7","linkedin":"https://www.linkedin.com/in/dhutchence/","github":"https://github.com/slimbuck"},"imageURL":"https://media.licdn.com/dms/image/v2/C4D03AQE9f98jnVdkOQ/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1587112296905?e=1730332800&v=beta&t=gSIRVC1dWfjFPeSHQRt4YeEygFoD0e4s8xbHu7woGoY","key":"donovan"}],"frontMatter":{"authors":"donovan","slug":"compressing-gaussian-splats","title":"Compressing Gaussian Splats","tags":["gaussian-splats","compression","supersplat"]},"unlisted":false,"prevItem":{"title":"Massive Upgrade for the PlayCanvas Developer Site","permalink":"/massive-upgrade-for-the-playcanvas-developer-site"},"nextItem":{"title":"Boost Your Efficiency: Editor gets Support for AVIF and WebP","permalink":"/avif-webp-land-in-editor"}},"content":"### Introduction\\n\\n[**3D Gaussian Splatting**](https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/) is a new method for digitizing and rendering real world objects. With gaussian splatting, you can digitize a scene from a few photos using services like [Luma Labs](https://lumalabs.ai/) or [Polycam](https://poly.cam/). These services take the set of photos and generate a 3d Gaussian Splat scene in [PLY format]().\\n\\nFor example, this is a Gaussian Splat scene rendered in PlayCanvas.\\n
\\n \\n
\\n\\n\x3c!-- truncate --\x3e\\n\\n### What is a Splat?\\n\\nGaussian Splat Scenes are not made up of polygons and textures. Instead, they are made up of many (up to millions) of individual, unconnected blobs called _splats_. A splat is just a particle in space with size, orientation, color and opacity.\\n\\nBelow you can see a single brown splat selected. The splat bounding box shows its orientation and size:\\n\\n\\n\\nThe gaussian part of the name comes from the shape of splat itself: the splat opacity has a gaussian falloff from its center to its edge.\\n\\n### Engine Support\\n\\nThe PlayCanvas team has been adding support to the engine for loading and rendering Gaussian Splat PLY files:\\n\\n[](https://playcanvas.github.io/#/loaders/splat-many)\\n\\nSince the resulting files are often messy and require cleaning, we released [SuperSplat](https://playcanvas.com/super-splat), a tool for cleaning and processing gaussian splat PLY files:\\n\\n[](https://playcanvas.com/super-splat?load=https://code.playcanvas.com/viewer/guitar-cleaned.ply)\\n\\n### PLY Format\\n\\nHowever, the default gaussian splat PLY format as exported by training tools is large.\\n\\nThis is because the uncompressed format stores a large amount of data _per splat_:\\n\\n| Name | Data Format | Bytes |\\n| --------------------------- | ----------- | ----- |\\n| Position | 3 x float | 12 |\\n| Orientation | 4 x float | 16 |\\n| Scale | 3 x float | 12 |\\n| Spherical harmonics / color | 48 x float | 192 |\\n| Total | | 232 |\\n\\nFor example, the original `guitar.ply` scene file takes **132.8 MB** (**32 MB** excluding spherical harmonic data).\\n\\n### Compressed PLY Format\\n\\nSo we introduced a _compressed PLY_ format for use in runtime applications. The compressed PLY file format ignores the unused spherical harmonic data and stores the rest of the elements in quantized integers.\\n\\nThe format can be summarized as follows:\\n\\n- Split the scene into chunks of 256 splats\\n- For each chunk, store the min and max (x, y, z) for position and scale in floating point\\n- For each splat in the chunk, store a normalized and quantized value for position and scale (relative to chunk extents) and orientation and color\\n\\nThis data layout results in the following data _per chunk_:\\n\\n| Name | Data Format | Bytes |\\n| -------------- | ----------- | ----- |\\n| Position bound | 6 x float | 24 |\\n| Scale bound | 6 x float | 24 |\\n| Total | | 48 |\\n\\nAnd the following data _per splat_:\\n\\n| Name | Data Format | Bytes |\\n| ----------- | ---------------------- | ----- |\\n| Position | uint32 (11, 10, 11) | 4 |\\n| Orientation | uint32 (2, 10, 10, 10) | 4 |\\n| Scale | uint32 (11, 10, 11) | 4 |\\n| Color | uint32 (8, 8, 8, 8) | 4 |\\n| Total | | 16 |\\n\\nAs a result, the compressed version of `guitar.ply` takes only **8.7 MB**.\\n\\n### Do It Yourself\\n\\nThe easiest way to generate a compressed PLY file yourself is using the [SuperSplat tool](https://playcanvas.com/super-splat). Load the PLY file into SuperSplat and export it again using the \'Compressed Ply File\' option:\\n\\n[](https://playcanvas.com/super-splat)\\n\\nIf you are interested in the file format specifics, see [this code](https://github.com/playcanvas/engine/blob/a86bd8be0cfd4e39e9ba5e5466acb6875ab9906e/extras/splat/splat-data.js#L257) which demonstrates how to decompress the file data.\\n\\nSee [this editor project](https://playcanvas.com/project/1165904/overview/gaussiansplatdemo) for an example of loading and rendering a compressed gaussian splat PLY file. Or you can [run it here](https://playcanv.as/p/69cnpevQ/).\\n\\n### Summary and Future\\n\\nWe have introduced a new compressed PLY format for gaussian splatting which is roughly 4x smaller than uncompressed data and can be used in realtime applications.\\n\\nIn future we hope to:\\n\\n- store splats hierarchically for optimized rendering and culling\\n- implement realtime splat LOD\\n- test skinning and animation of gaussian splats\\n- further compress gaussian splat data\\n- optimize WebGPU rendering\\n\\n### References\\n\\nThe compressed format is largely based on the fine work of Aras Pranckevi\u010dius and his [blog posts](https://aras-p.info/)."},{"id":"avif-webp-land-in-editor","metadata":{"permalink":"/avif-webp-land-in-editor","editUrl":"https://github.com/playcanvas/blog/tree/main/blog/2023-10-11-avif-webp-lands-in-editor.md","source":"@site/blog/2023-10-11-avif-webp-lands-in-editor.md","title":"Boost Your Efficiency: Editor gets Support for AVIF and WebP","description":"AVIF + WebP Support","date":"2023-10-11T00:00:00.000Z","tags":[{"inline":true,"label":"editor","permalink":"/tags/editor"},{"inline":true,"label":"textures","permalink":"/tags/textures"},{"inline":true,"label":"workflow","permalink":"/tags/workflow"},{"inline":true,"label":"compression","permalink":"/tags/compression"}],"readingTime":2.215,"hasTruncateMarker":true,"authors":[{"name":"Mark Lundin","title":"Software Engineer","page":{"permalink":"/authors/mark"},"socials":{"x":"https://x.com/mark_lundin","linkedin":"https://www.linkedin.com/in/marklundin2/","github":"https://github.com/marklundin"},"imageURL":"https://media.licdn.com/dms/image/v2/C5603AQF-25U0u8HhVA/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1632995375227?e=1730332800&v=beta&t=mEnvFh7BINcBDtz7_F0_Sv_V9FKtzZUdCAKWyTZ-Qco","key":"mark"}],"frontMatter":{"authors":"mark","slug":"avif-webp-land-in-editor","title":"Boost Your Efficiency: Editor gets Support for AVIF and WebP","tags":["editor","textures","workflow","compression"]},"unlisted":false,"prevItem":{"title":"Compressing Gaussian Splats","permalink":"/compressing-gaussian-splats"},"nextItem":{"title":"glTF Viewer 4.0 Adds WebGPU Support","permalink":"/gltf-viewer-adds-webgpu-support"}},"content":"\\n\\n**Today we\'re excited to announce that PlayCanvas now supports WebP and AVIF images natively in the editor. Two new, efficient image formats built for the web.**\\n\\nThis is not just an exciting update for us but a game-changer for anyone looking to optimize their projects for faster load times and better quality.\\n\\n\x3c!-- truncate --\x3e\\n\\nJPGs and PNGs have served us well. They\'re reliable and you can use them everywhere. AVIF and WebP are the new kids on the block. They offer better compression with a wider range of features like alpha channels, HDR and wide color gamut. But how do these formats work in practice and are they really that good? Let\'s dive in.\\n\\n_Take a look at the following images:_\\n\\n\\n\\nThey look strikingly similar, but the file size tells a different story. The AVIF image on the left has a file size of just 18Kb, and the JPG counterpart over 4 times larger at 74Kb! This level of compression is not just impressive on its own. When you\'re working on a project with multiple textures, switching to AVIF and WebP formats could considerably reduce your overall load time.\\n\\nWhat does this mean for you? Well, that\'s simple: it\'s all about efficiency and speed without sacrificing quality. Textures are one of the biggest assets in a 3D project and AVIF and WebP significantly reduce image sizes compared to traditional JPEGs and PNGs and that means faster load times and happier users. \ud83d\ude4c\\n\\n#### AVIF all the things?\\n\\nOk, so browser support for AVIF is good, but not great according to [caniuse](https://caniuse.com/?search=avif), so always [check support](https://dev.to/nucliweb/detect-avif-image-support-to-use-in-your-css-4pen) and use the right format for your users. Different formats are better suited for different content. It\'s not a one-size-fits-all solution, so we encourage you to explore various options and choose the one best for you.\\n\\n#### Getting Started\\n\\nEasy! Just start uploading your AVIF and WebP files into the Asset Panel, and for those of you wanting to convert your existing assets, you can now right-click on your asset in the Asset Panel and quickly convert your asset into a PNG, JPG, WebP or an AVIF.\\n\\n\\n\\nSo there you have it! This is not just a feature release; it\'s our way of saying we\'re listening and we care. We\'re excited to hear your thoughts on these new features. Give them a try and share your experiences with us.\\n\\nTill then, keep creating, keep innovating!"},{"id":"gltf-viewer-adds-webgpu-support","metadata":{"permalink":"/gltf-viewer-adds-webgpu-support","editUrl":"https://github.com/playcanvas/blog/tree/main/blog/2023-10-10-gltf-viewer-adds-webgpu-support.md","source":"@site/blog/2023-10-10-gltf-viewer-adds-webgpu-support.md","title":"glTF Viewer 4.0 Adds WebGPU Support","description":"We\'re thrilled to announce the launch of the open source glTF Viewer 4.0, an update that supercharges your 3D model viewing experience with powerful features and support for the latest web technologies!","date":"2023-10-10T00:00:00.000Z","tags":[{"inline":true,"label":"ar","permalink":"/tags/ar"},{"inline":true,"label":"gltf","permalink":"/tags/gltf"},{"inline":true,"label":"viewer","permalink":"/tags/viewer"},{"inline":true,"label":"webgpu","permalink":"/tags/webgpu"},{"inline":true,"label":"webxr","permalink":"/tags/webxr"}],"readingTime":3.655,"hasTruncateMarker":true,"authors":[{"name":"Will Eastcott","title":"CEO","description":"Will Eastcott is an entrepreneur and veteran technologist of the games industry with experience at EA, Sony, and Activision. He has been credited in many AAA game franchises such as GTA, Call of Duty and Max Payne. He is best known for co-founding PlayCanvas, the web graphics creation platform. As CEO, he has championed the company\'s mission to make graphical web app development more accessible and collaborative through open-source technologies and cloud-based tools.","page":{"permalink":"/authors/will"},"socials":{"x":"https://x.com/willeastcott","linkedin":"https://www.linkedin.com/in/willeastcott/","github":"https://github.com/willeastcott"},"imageURL":"https://media.licdn.com/dms/image/v2/D4D03AQF9YXIeHZW9kg/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1688995668268?e=1730332800&v=beta&t=UmAwlJ0nPBmnMczQ4wknx1jSDJ2anqurm2b15iLm3pM","key":"will"}],"frontMatter":{"authors":"will","slug":"gltf-viewer-adds-webgpu-support","title":"glTF Viewer 4.0 Adds WebGPU Support","tags":["ar","gltf","viewer","webgpu","webxr"]},"unlisted":false,"prevItem":{"title":"Boost Your Efficiency: Editor gets Support for AVIF and WebP","permalink":"/avif-webp-land-in-editor"},"nextItem":{"title":"PlayCanvas Adds Sketchfab Integration","permalink":"/playcanvas-adds-sketchfab-integration"}},"content":"We\'re thrilled to announce the launch of the [open source](https://github.com/playcanvas/model-viewer) **glTF Viewer 4.0**, an update that supercharges your 3D model viewing experience with powerful features and support for the latest web technologies!\\n\\n[](/img/gltf-viewer-4.png) \\n_[\\"Cyber Samurai\\"](https://skfb.ly/ooZKG) by KhoaMinh is licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)._\\n\\n[**TRY IT NOW**](https://playcanvas.com/viewer?load=https://s3.eu-west-1.amazonaws.com/static.playcanvas.com/models/IridescentDishWithOlives.glb&default&skybox.value=Abandoned%20Tank%20Farm&skybox.background=Projective%20Dome&debug.grid=false&shadowCatcher.enabled=true&default)\\n\\nThis new release is chock-full with enhancements aimed at providing more realistic, insightful, and versatile viewing options for your glTF files. Let\'s dive into the headline features of glTF Viewer 4.0.\\n\\n\x3c!-- truncate --\x3e\\n\\n#### New WebGPU Renderer\\n\\n\\n\\nTopping the list of today\'s updates is support for WebGPU! WebGPU heralds a new era in graphics and compute capabilities, offering enhanced performance and efficiency. Users can now select WebGPU as their default renderer, and don\'t worry if your platform doesn\'t support it yet - the viewer gracefully falls back to WebGL 2, and subsequently WebGL 1, depending on API availability. Note that WebGPU support is considered beta for the moment and you\'ll need to proactively enable it and refresh the viewer to check it out:\\n\\n[](/img/gltf-viewer-webgpu-toggle.gif)\\n\\nAlso make sure you\'re running the viewer in a browser that supports WebGPU. At time of writing, this means Google Chrome!\\n\\n#### Enhanced WebXR AR Mode\\n\\n
\\n \\n
\\n\\nTake your 3D models into the real world with our revamped WebXR Augmented Reality (AR) mode! Available currently on Android devices, this enhanced AR mode lets you view any model in your actual environment, complete with intuitive new controls that allow you to accurately position and rotate objects in the real world. Let\'s hope Apple decides to roll out WebXR support on iOS soon! \ud83d\ude4f\\n\\n#### Frame Selected Node\\n\\n
\\n \\n
\\n\\nNavigating large scenes can be a pain - Viewer 4.0 addresses this by allowing you to select a node in the scene via the hierarchy panel on the left. You can then press \'F\' on the keyboard to frame that node and recenter the orbit camera on that node\'s position.\\n\\n#### Better Immersion with Projective Sky Dome\\n\\n
\\n \\n
\\n\\n_[\\"130\\"](https://skfb.ly/6R9Ow) by mononofu is licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)._\\n\\nExperience realistic photographic skies with our new projective sky dome! While previous versions allowed for skyboxex with an infinite projection, 4.0 introduces a dome-shaped skybox projection that incorporates a flat ground plane. This warps the skybox texture to have a more believable appearance, delivering a more authentic and immersive perspective, melding your 3D models with strikingly realistic backdrops.\\n\\n#### Debug and Inspect with Render Mode\\n\\n[](/img/gltf-viewer-render-mode.gif)\\n\\nEnsuring that developers can seamlessly troubleshoot and inspect glTF files, the new render mode allows you to select and display individual inputs/outputs of the render pipeline, including albedo, emissive, normals, gloss, AO, and more. This new level of insight is invaluable for debugging, making it even easier to work with your glTF data.\\n\\n#### Enhanced Realism with VSM Shadows\\n\\nThe addition of Variance Shadow Mapping (VSM) casts your 3D scenes in a new light, literally! Shadows aren\'t merely aesthetic; they provide context and depth, especially in AR mode, assisting to ground your object naturally within its real-world environment. Explore scenes with a newfound depth and realism that draws viewers into the experience, both in standard and AR viewing modes. Find the new shadow controls in the Light Settings panel:\\n\\n[](/img/gltf-viewer-light-settings.png)\\n\\n### Join Our Open Source Community\\n\\nWe\'re not just excited to share these innovations with you; we\'re eager to hear your thoughts and welcome your contributions! If there\'s a feature you\'re longing for, please don\'t hesitate to [submit your requests](https://github.com/playcanvas/model-viewer/issues).\\n\\nBetter yet, become an active contributor to our codebase! Our open-source community thrives on collaboration and fresh perspectives. So, dive right in, [explore the code](https://github.com/playcanvas/model-viewer), and let\'s shape the future of 3D model viewing together! Your expertise and insights could help shape the next release.\\n\\n[**GO TO GITHUB NOW**](https://github.com/playcanvas/model-viewer)\\n\\n### Conclusion\\n\\nWith glTF Viewer 4.0, we\'re redefining the standards of 3D model viewing. From WebGPU-powered rendering to WebXR-powered AR, this update is designed to inspire, assist, and elevate your work with glTF data.\\n\\nSo stay creative, friends, and we\'ll see you on the [forums](https://forum.playcanvas.com/)! \ud83d\udc4b"},{"id":"playcanvas-adds-sketchfab-integration","metadata":{"permalink":"/playcanvas-adds-sketchfab-integration","editUrl":"https://github.com/playcanvas/blog/tree/main/blog/2023-06-26-playcanvas-adds-sketchfab-itegration.md","source":"@site/blog/2023-06-26-playcanvas-adds-sketchfab-itegration.md","title":"PlayCanvas Adds Sketchfab Integration","description":"Today, we\'re excited to announce the integration of Sketchfab into the PlayCanvas Editor!","date":"2023-06-26T00:00:00.000Z","tags":[{"inline":true,"label":"asset-store","permalink":"/tags/asset-store"},{"inline":true,"label":"editor","permalink":"/tags/editor"},{"inline":true,"label":"sketchfab","permalink":"/tags/sketchfab"},{"inline":true,"label":"workflow","permalink":"/tags/workflow"}],"readingTime":2.165,"hasTruncateMarker":true,"authors":[{"name":"Will Eastcott","title":"CEO","description":"Will Eastcott is an entrepreneur and veteran technologist of the games industry with experience at EA, Sony, and Activision. He has been credited in many AAA game franchises such as GTA, Call of Duty and Max Payne. He is best known for co-founding PlayCanvas, the web graphics creation platform. As CEO, he has championed the company\'s mission to make graphical web app development more accessible and collaborative through open-source technologies and cloud-based tools.","page":{"permalink":"/authors/will"},"socials":{"x":"https://x.com/willeastcott","linkedin":"https://www.linkedin.com/in/willeastcott/","github":"https://github.com/willeastcott"},"imageURL":"https://media.licdn.com/dms/image/v2/D4D03AQF9YXIeHZW9kg/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1688995668268?e=1730332800&v=beta&t=UmAwlJ0nPBmnMczQ4wknx1jSDJ2anqurm2b15iLm3pM","key":"will"}],"frontMatter":{"authors":"will","slug":"playcanvas-adds-sketchfab-integration","title":"PlayCanvas Adds Sketchfab Integration","tags":["asset-store","editor","sketchfab","workflow"]},"unlisted":false,"prevItem":{"title":"glTF Viewer 4.0 Adds WebGPU Support","permalink":"/gltf-viewer-adds-webgpu-support"},"nextItem":{"title":"Moving from WordPress to Jekyll - A Case Study","permalink":"/moving-from-wordpress-to-jekyll-a-case-study"}},"content":"Today, we\'re excited to announce the integration of Sketchfab into the PlayCanvas Editor!\\n\\n
\\n \\n
\\n\\n_[\\"Spartan Armour\\"](https://skfb.ly/6QVvM) by McCarthy3D is licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)._\\n\\nWith today\'s launch you\'ll have instant access to Sketchfab\'s enormous library of high quality 3D content right inside the Editor.\\n\\n\x3c!-- truncate --\x3e\\n\\n### What Is Sketchfab?\\n\\n[Sketchfab](https://sketchfab.com/) is a platform that allows users to publish, share and discover 3D content on the web. You can think of it as a sort of \\"YouTube for 3D models\\". The platform hosts millions of 3D models in various formats, including glTF, OBJ, STL, and more.\\n\\nUsers can upload their 3D models to Sketchfab and embed them in other websites much like one would embed a YouTube video. This has made it popular for showcasing 3D models for a wide range of uses including game assets, 3D scans, architectural models, and educational content.\\n\\n### Accessing Sketchfab from the Asset Store\\n\\nEarlier this month, we [announced](https://blog.playcanvas.com/announcing-the-new-playcanvas-asset-store/) the brand new PlayCanvas Asset Store. The Asset Store is an in-Editor panel of useful assets that you can use in your projects. With a few clicks, you can browse, discover and import various types of content. So it is the obvious place to incorporate the vast Sketchfab content library.\\n\\nTo access the Sketchfab library, simply hit the Asset Store button on the Asset Panel header and select the SKETCHFAB filter on the left.\\n\\n[](/img/asset-store-sketchfab-open.gif)\\n\\n### Authorizing PlayCanvas to Access Sketchfab\\n\\nIf you want to import models from Sketchfab into PlayCanvas, you first need to create a Sketchfab account. Then, the first time you try to import a Sketchfab model in the Asset Store, you will be asked to authorize PlayCanvas to access your Sketchfab account.\\n\\n[](/img/asset-store-sketchfab-authorize.gif)\\n\\nIt\'s so quick and easy!\\n\\n### New License and Author Info for Assets\\n\\nSketchfab assets are all tagged with a license and an author. When you import Sketchfab content into your PlayCanvas projects, it\'s important that we preserve this information. Now, when you click on any imported asset, you\'ll see we\'ve added links to license and author information in the Inspector panel.\\n\\n[](/img/asset-store-sketchfab-license.gif)\\n\\nThis ensures that the author\'s licensing wishes are respected and that they recieve credit for their work.\\n\\n### Go Forth and Create\\n\\nWith today\'s release, it\'s never been easier to create interactive 3D experiences for the web. We hope you love the new Sketchfab integration. But, as always, we _love_ to hear your opinions so head over to the [forum](https://forum.playcanvas.com/t/playcanvas-integrates-sketchfab/31885) and join the conversation. Happy creating! \ud83d\udea7\ud83d\udc77"},{"id":"moving-from-wordpress-to-jekyll-a-case-study","metadata":{"permalink":"/moving-from-wordpress-to-jekyll-a-case-study","editUrl":"https://github.com/playcanvas/blog/tree/main/blog/2023-06-07-moving-from-wordpress-to-jekyll-a-case-study.md","source":"@site/blog/2023-06-07-moving-from-wordpress-to-jekyll-a-case-study.md","title":"Moving from WordPress to Jekyll - A Case Study","description":"Welcome to the new PlayCanvas blog! \ud83d\ude0e","date":"2023-06-07T00:00:00.000Z","tags":[{"inline":true,"label":"jekyll","permalink":"/tags/jekyll"},{"inline":true,"label":"wordpress","permalink":"/tags/wordpress"},{"inline":true,"label":"github","permalink":"/tags/github"}],"readingTime":6.745,"hasTruncateMarker":true,"authors":[{"name":"Will Eastcott","title":"CEO","description":"Will Eastcott is an entrepreneur and veteran technologist of the games industry with experience at EA, Sony, and Activision. He has been credited in many AAA game franchises such as GTA, Call of Duty and Max Payne. He is best known for co-founding PlayCanvas, the web graphics creation platform. As CEO, he has championed the company\'s mission to make graphical web app development more accessible and collaborative through open-source technologies and cloud-based tools.","page":{"permalink":"/authors/will"},"socials":{"x":"https://x.com/willeastcott","linkedin":"https://www.linkedin.com/in/willeastcott/","github":"https://github.com/willeastcott"},"imageURL":"https://media.licdn.com/dms/image/v2/D4D03AQF9YXIeHZW9kg/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1688995668268?e=1730332800&v=beta&t=UmAwlJ0nPBmnMczQ4wknx1jSDJ2anqurm2b15iLm3pM","key":"will"}],"frontMatter":{"authors":"will","slug":"moving-from-wordpress-to-jekyll-a-case-study","title":"Moving from WordPress to Jekyll - A Case Study","tags":["jekyll","wordpress","github"]},"unlisted":false,"prevItem":{"title":"PlayCanvas Adds Sketchfab Integration","permalink":"/playcanvas-adds-sketchfab-integration"},"nextItem":{"title":"Announcing the New PlayCanvas Asset Store","permalink":"/announcing-the-new-playcanvas-asset-store"}},"content":"Welcome to the new PlayCanvas blog! \ud83d\ude0e\\n\\n\\n\\nWe have made the decision to move from WordPress to [Jekyll](https://jekyllrb.com/) and although it\'s early days, so far, we are very pleased with the results. Therefore, we thought it might be useful to explain the reasons why we did this and describe the process we followed.\\n\\n\x3c!-- truncate --\x3e\\n\\n### Some History\\n\\nWe originally started our blog back in February 2012 with a very [low-key post](https://blog.playcanvas.com/hello-world/). We installed our own instance of WordPress and that functioned fine for a few years. However, in 2015, we decided that we didn\'t want to deal with managing a WordPress instance ourselves and so we signed up for [WPEngine](https://wpengine.com/), a WordPress hosting service. And there we stayed until today.\\n\\n### A Better Way?\\n\\nOver the years, some frustrations over WordPress did start to materialize. Customizing styling was tricky, bulk editing posts was not an option, managing media through the media library was quite inconvenient, and so on. On top of that, our blog was continuing to grow in popularity and WPEngine costs were rising. A team member suggested Jekyll as an alternative so we ran some tests to see what would be involved in a migration.\\n\\n### Migrating from WordPress to Jekyll\\n\\nFirst up, I should say that you should make use of ChatGPT when you attempt something like this. It made it a smooth and pleasant experience for me. But so you can see how straightforward it was, here are the key steps:\\n\\n1. **Export Your WordPress Content.** On your WordPress Dashboard, navigate to Tools > Export and select \'All Content\'. Click \'Download Export File\'. You should receive an XML file that contains all of your site\u2019s content.\\n2. **Convert WordPress Export to Jekyll Format.** To convert your WordPress export file to a Jekyll-friendly format, you can use a tool called `exitwp`. Here are the steps:\\n\\n - Clone or download `exitwp` from its [GitHub repository](https://github.com/thomasf/exitwp).\\n - Put your WordPress XML file in the `wordpress-xml` directory in the `exitwp` directory.\\n - Run `exitwp` (requires Python and some libraries, see the repo\'s `README` for details).\\n - This will generate a `_posts` directory containing all your blog posts converted into markdown format that Jekyll understands.\\n\\n3. **Setting Up Jekyll.**\\n\\n - Install Ruby and Jekyll. For installation guide refer to [Jekyll\'s official site](https://jekyllrb.com/docs/installation/).\\n - Create a new Jekyll site by typing `jekyll new my-awesome-site` in the command line.\\n - Replace the `_posts` directory in your new Jekyll site with the `_posts` directory that `exitwp` generated.\\n - You can run your site locally to see how it looks by using the command `bundle exec jekyll serve`.\\n - Then, open your browser to http://localhost:4000.\\n\\nOnce I had our blog post content successfully exported to a basic Jekyll site, I checked everything in to a new [GitHub repository](https://github.com/playcanvas/blog). From this point, I wanted any change I made to the content to be tracked via Git version control - I mean, why wouldn\'t you?\\n\\nThe initial migration to Markdown worked OK but it wasn\'t perfect. There was a lot of superfluous whitespace in the generated Markdown and syntax was broken in places. So I took the opportunity to do a wholesale spring clean of the Markdown content.\\n\\n#### Prettier\\n\\n[Prettier](https://prettier.io/) is a great code formatter but you may not know that it also has built-in support for Markdown formatting. I installed Prettier via NPM and ran it over all Markdown files:\\n\\n```shell\\nprettier --write _posts\\n```\\n\\nAll formatting was now beautifully consistent.\\n\\n#### Linkinator\\n\\n[Linkinator](https://github.com/JustinBeckwith/linkinator) is a tool for reporting broken hyperlinks in both Markdown and HTML. I scanned the locally generated Jekyll site with it using the following command:\\n\\n```shell\\nlinkinator . --server-root _site --recurse --verbosity error\\n```\\n\\nI fixed well over 100 dead links. The older the post, the more dead links there tended to be (as you might expect). While I was always able to use Linkinator on the published WordPress site, it was never convenient to skip from post to post and edit hyperlinks in the WordPress UI (we have over 220 posts). So I never made the time for it. Now I was able to quickly search and replace links in Visual Studio Code and it was a breeze.\\n\\nAside from giving your readers a better experience, here\'s what ChatGPT has to say about dead links:\\n\\n> While Google\'s algorithms are complex and multifaceted, it is generally understood that having too many broken or dead links on your site can negatively affect your website\'s SEO ranking, albeit indirectly.\\n\\nSo this step was definitely worth doing!\\n\\n#### Media Library\\n\\nEvery WordPress blog has a Media Library that contains all of the images and videos referenced by your posts. The PlayCanvas Media Library had over 1GB of content. However, I noticed that a large amount of content was not actually referenced by any post. I wanted to ensure that I exported only the _used_ media (since I didn\'t want to bloat the GitHub repository).\\n\\n- **Step 1:** Install WordPress plugin [Media Cleaner](https://wordpress.org/plugins/media-cleaner/) and use it to delete unused media files.\\n- **Step 2:** Install WordPress plugin [Export Media Library](https://wordpress.org/plugins/export-media-library/) to download all remaining media files in a ZIP.\\n\\nThis process cut the Media Library from 1GB to about 550MB. I simply extracted the ZIP contents to `assets/media` (relative to the root folder of my Jekyll site) and then did a search and replace of all media embed links to use the new file location. Easy!\\n\\n### Hosting on GitHub Pages\\n\\nI now had a fully exported and functional Jekyll blog. But where to host it? One option was to throw the static site onto a file server and put it behind a CDN. But wait, the file-set of the Jekyll blog now sits in GitHub, so why not use [GitHub Pages](https://pages.github.com/) to host the site? There are some great benefits:\\n\\n- Deployment is a breeze to set up.\\n- It\'s a managed platform so you just don\'t need to worry about maintenance and security as you would with your own infra.\\n- And, oh yeah, **it\'s free!**\\n\\n### The Pros and Cons\\n\\nSo now that we\'re migrated over, let\'s quickly summarize the key benefits:\\n\\n- **Free:** Costs for our blog are now a pleasing $0.\\n- **Secure:** We have eliminated a potential attack vector. No more password reset confirmation emails!\\n- **Powerful Tooling:** There\'s a vast array of free and open source tools to lint and format Markdown.\\n- **Easy Editing:** Choose any editor you like such as the awesome Visual Studio Code.\\n- **Versioned:** All content is now under version control providing revision history, diffing and so on.\\n- **Collaborative:** We can take contributions to the blog via GitHub\'s pull request mechanism.\\n- **Customizable:** We have fine-grain control over the HTML and styling of posts using Jekyll themes.\\n- **Platform Agnostic:** Markdown makes it trivial to migrate to any other platform in the future.\\n\\nOK, but what about the cons? \ud83e\udd14 I have to tell you - I genuinely can\'t think of any! Sure, we don\'t have comments enabled now but as I understand it, Disqus has a [Jekyll integration](https://help.disqus.com/en/articles/1935528-jekyll-installation-instructions) that we can turn on at some point. Seriously, feel free to ping me on [Twitter](https://twitter.com/willeastcott/) if you think I\'m missing something here. But I wish we had taken this step years ago.\\n\\n### Open Source and Open to Contributions\\n\\nAs mentioned, the blog is now on [GitHub](https://github.com/playcanvas/blog). Take a moment to go check out the repository and hit that Star button if you like what we\'ve done here. \u2b50 We\'ve taken the step to open source the blog\'s content under an [Attribution-NonCommercial 4.0 International](https://github.com/playcanvas/blog/blob/main/LICENSE.md) license. This is part of our continuing drive to open source most of what we do.\\n\\nUltimately, we want **you** to get involved and help us make the blog better and better over time. Consider submitting issues and pull requests yourself.\\n\\n- Submit spelling and grammar fixes.\\n- Submit design tweaks and improvements.\\n- Submit your own posts showcasing your PlayCanvas projects.\\n\\nIt\'s your blog! \ud83d\ude0a\\n\\nThat\'s all from me. Looking forward to many-a-future blogs posts on this new platform. \ud83d\udc4b"},{"id":"announcing-the-new-playcanvas-asset-store","metadata":{"permalink":"/announcing-the-new-playcanvas-asset-store","editUrl":"https://github.com/playcanvas/blog/tree/main/blog/2023-04-26-announcing-the-new-playcanvas-asset-store.md","source":"@site/blog/2023-04-26-announcing-the-new-playcanvas-asset-store.md","title":"Announcing the New PlayCanvas Asset Store","description":"The PlayCanvas Asset Store is the first place that users tend to go to find content for their projects. This is especially true for new users who want to get started as quickly as possible. Up until now, the Store has not been particularly easy to use and the content has not changed in quite a long time. In short, a complete overhaul and refresh has been long overdue. So today, we are incredibly excited to announce a major upgrade for the PlayCanvas Asset Store!","date":"2023-04-26T00:00:00.000Z","tags":[{"inline":true,"label":"asset-store","permalink":"/tags/asset-store"},{"inline":true,"label":"editor","permalink":"/tags/editor"},{"inline":true,"label":"news","permalink":"/tags/news"},{"inline":true,"label":"workflow","permalink":"/tags/workflow"}],"readingTime":3.44,"hasTruncateMarker":true,"authors":[{"name":"Will Eastcott","title":"CEO","description":"Will Eastcott is an entrepreneur and veteran technologist of the games industry with experience at EA, Sony, and Activision. He has been credited in many AAA game franchises such as GTA, Call of Duty and Max Payne. He is best known for co-founding PlayCanvas, the web graphics creation platform. As CEO, he has championed the company\'s mission to make graphical web app development more accessible and collaborative through open-source technologies and cloud-based tools.","page":{"permalink":"/authors/will"},"socials":{"x":"https://x.com/willeastcott","linkedin":"https://www.linkedin.com/in/willeastcott/","github":"https://github.com/willeastcott"},"imageURL":"https://media.licdn.com/dms/image/v2/D4D03AQF9YXIeHZW9kg/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1688995668268?e=1730332800&v=beta&t=UmAwlJ0nPBmnMczQ4wknx1jSDJ2anqurm2b15iLm3pM","key":"will"}],"frontMatter":{"authors":"will","slug":"announcing-the-new-playcanvas-asset-store","title":"Announcing the New PlayCanvas Asset Store","tags":["asset-store","editor","news","workflow"]},"unlisted":false,"prevItem":{"title":"Moving from WordPress to Jekyll - A Case Study","permalink":"/moving-from-wordpress-to-jekyll-a-case-study"},"nextItem":{"title":"Initial WebGPU support lands in PlayCanvas Engine 1.62!","permalink":"/initial-webgpu-support-lands-in-playcanvas-engine-1-62"}},"content":"The PlayCanvas Asset Store is the first place that users tend to go to find content for their projects. This is especially true for new users who want to get started as quickly as possible. Up until now, the Store has not been particularly easy to use and the content has not changed in quite a long time. In short, a complete overhaul and refresh has been long overdue. So today, we are incredibly excited to announce a major upgrade for the PlayCanvas Asset Store!\\n\\nFirst up, check out a little example of building a city scene using content taken from the Asset Store. A skybox, a pack of 3D city block meshes and a camera control script are imported and the city is built via drag and drop. And _not one single line of code is needed_!\\n\\n
\\n \\n
\\n\\nNow, let\'s examine some of the key highlights that make the new Asset Store so special.\\n\\n\x3c!-- truncate --\x3e\\n\\n### Built Right In To The Editor\\n\\nIt should be possible to grab assets quickly, right from within the Editor itself. Why should you have to open a new tab and go hunting around the web? So to keep things as convenient as possible, the ASSET STORE button (in the Editor\'s Assets Panel) now opens a nicely designed, responsive Asset Store panel.\\n\\n[](/img/asset-store-open.gif)\\n\\nOne really cool benefit of selecting assets from within the Editor is that the currently selected Asset folder is known. This means you have compete control over where your imported assets will be saved.\\n\\n### Preview Store Assets Before Import\\n\\nSometimes, an asset thumbnail just isn\'t sufficient to tell if a particular asset is what you want.\\n\\n[](/img/asset-store-viewer.gif)\\n\\nOur new Store allows you to select a Store Item and preview it in an appropriate viewer (glTF Viewer for 3D models, Texture Viewer for textures and cubemaps).\\n\\n### Find What You Want Quickly\\n\\nAs the number of store items continues to grow, it\'s going to be incredibly important for you to be able to narrow down your assets searches. As a result, the new Store comes with powerful searching, sorting and filtering capabilities.\\n\\n[](/img/asset-store-search.gif)\\n\\nYou can filter by asset type, search asset names and descriptions and then order search results on a host of criteria.\\n\\n### Fresh New Content\\n\\nRecently, the Asset Store content was, let\'s just say, beginning to show its age. We are now in a world of HDR skyboxes, PBR materials and high polygon meshes. So it made sense to erase old store items and refresh the Store with better, more modern content. We have selected a broad variety of Creative Commons assets from fantastic content sources such as [kenney.nl](https://kenney.nl/), [HDRi Haven](https://hdri-haven.com/), [Sketchfab](https://sketchfab.com/) and Khronos\' [glTF Sample Models](https://github.com/KhronosGroup/glTF-Sample-Models).\\n\\n[](/img/asset-store-content.gif)\\n\\nIf you would have any suggestions for content you would like to be added to the Store, please do let us know!\\n\\n### The Future\\n\\nThere\'s still so much we want to do with the new Asset Store! But here are some things we have in mind:\\n\\n1. **More Content.** The important thing to say about today\'s update is that it delivers the core infrastructure on which we can iterate. It is now exceptionally easy for us to populate the store with more content. So, in the near term, you can expect to see the range of content expand quite rapidly.\\n\\n2. **Third Party Stores.** Now that we have a solid foundation in place for the Store, we have the ability to host third party stores within the same UI and maximize your choice.\\n\\n3. **More Asset Types.** Today\'s launch offers models, fonts, textures, skyboxes and scripts. Next, we want to add audio assets and template assets (AKA prefabs). Template assets in particular are very exciting because you would be able to import fully interactive, visual entities into your projects (such as a drivable vehicle or a controllable character).\\n\\nWhat would _you_ like to see us add to the Asset Store next? Let us know on the [forum](https://forum.playcanvas.com/).\\n\\nHappy creating, friends!"},{"id":"initial-webgpu-support-lands-in-playcanvas-engine-1-62","metadata":{"permalink":"/initial-webgpu-support-lands-in-playcanvas-engine-1-62","editUrl":"https://github.com/playcanvas/blog/tree/main/blog/2023-03-31-initial-webgpu-support-lands-in-playcanvas-engine-1-62.md","source":"@site/blog/2023-03-31-initial-webgpu-support-lands-in-playcanvas-engine-1-62.md","title":"Initial WebGPU support lands in PlayCanvas Engine 1.62!","description":"WebGPU is a cutting-edge technology that promises to revolutionize the way 3D graphics are handled on the web. As the successor to WebGL, WebGPU provides faster and more efficient rendering capabilities for complex 3D graphics and simulations.","date":"2023-03-31T00:00:00.000Z","tags":[{"inline":true,"label":"webgpu","permalink":"/tags/webgpu"}],"readingTime":3.11,"hasTruncateMarker":true,"authors":[{"name":"Martin Valigursky","title":"Software Engineer","page":{"permalink":"/authors/martin"},"socials":{"x":"https://x.com/ValigurskyM","linkedin":"https://www.linkedin.com/in/martin-valigursky/","github":"https://github.com/mvaligursky"},"imageURL":"https://media.licdn.com/dms/image/v2/C4D03AQE4bZqBMHaTuw/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1589921553525?e=1730332800&v=beta&t=RekQph-b_JA1byxs2u6gCIGCWESMP7q5w3vW7VHEooI","key":"martin"}],"frontMatter":{"authors":"martin","slug":"initial-webgpu-support-lands-in-playcanvas-engine-1-62","title":"Initial WebGPU support lands in PlayCanvas Engine 1.62!","tags":["webgpu"]},"unlisted":false,"prevItem":{"title":"Announcing the New PlayCanvas Asset Store","permalink":"/announcing-the-new-playcanvas-asset-store"},"nextItem":{"title":"WebXR AR Made Easy with PlayCanvas","permalink":"/webxr-ar-made-easy-with-playcanvas"}},"content":"WebGPU is a cutting-edge technology that promises to revolutionize the way 3D graphics are handled on the web. As the successor to WebGL, WebGPU provides faster and more efficient rendering capabilities for complex 3D graphics and simulations.\\n\\nPlayCanvas has been at the forefront of this new technology and has been working on adding WebGPU support to its platform.\\n\\nWith WebGPU, we can expect to see more immersive and interactive 3D experiences on the web in the future.\\n\\n[](/img/webgpu-area-lights-demo.jpg) \\n[_PlayCanvas WebGPU Clustered Area Lights Demo_](https://playcanvas.com/demos/arealights/)\\n\\n\x3c!-- truncate --\x3e\\n\\n## Refactoring of WebGL engine\\n\\nBefore adding support for WebGPU, it\'s important to discuss the significant amount of refactoring work that was required on our existing WebGL engine. Implementing deeper architectural changes while preserving backwards compatibility required a significant amount of meticulous care.\\n\\n- To enable support for WebGPU, we needed to establish a clear separation of graphics technology that could be shared between WebGL and WebGPU. This involved a significant refactoring effort to extract WebGL-specific code into a separate set of classes.\\n- PlayCanvas utilizes a collection of shader chunks to produce GLSL shaders that implement advanced material properties and lighting modes, as well as custom shader chunks defined by users. However, since WebGPU employs the WGSL language, we used glslang and tint WASM modules to dynamically convert these shaders on-the-fly with injecting support for uniform buffers and other modifications.\\n- The PlayCanvas engine lacked explicit render passes, making the rendering process more rigid and harder to extend. This was solved by implementing a FrameGraph that allowed us to describe the rendering process as a set of render passes, their dependencies, and associated targets, which created a more flexible and performant rendering architecture.\\n- Unlike WebGL, which sets render state and shaders using a custom API, WebGPU specifies all those through render pipelines. To support both rendering APIs with optimal performance, we needed to refactor the render states into standalone objects that are efficient to compare and set up.\\n- To support the WebGPU platform, we need to undergo a significant refactoring to organize uniforms into uniform buffers.\\n- To facilitate the asynchronous creation of WebGPU device, we have introduced a new async API to create a graphics device, which is the primary breaking change required to adopt WebGPU.\\n\\n## What is left to do\\n\\n- Our primary objective is to align the WebGPU implementation with that of WebGL, and while we have made significant progress towards this goal, there are still some features that are missing. Furthermore, several smaller details require cleanup and rectification.\\n- We need to incorporate it into the Editor environment for both launched and published applications. Currently, only WebGL is available in this environment.\\n- Our primary objective is to achieve full parity with WebGL, but initially, we are focusing on basic implementations of some concepts to deliver a working implementation, which will need to be extended to meet our performance objectives.\\n- WebGPU provides developers with access to Compute Shaders, which enables more efficient parallel processing of data on the GPU. This feature can significantly improve the performance of complex algorithms and simulations, which may have been impractical to run on the CPU. With access to Compute Shaders, we can bring new visual features to the next level, such as advanced particle systems, post-processing and global illumination techniques.\\n\\n## Engine examples\\n\\nAs an early pre-release of WebGPU, we have updated several engine examples to use it, which can be accessed on [https://playcanvas.github.io](https://playcanvas.github.io/). To use WebGPU, the Chrome Canary browser is required, with the \'chrome://flags/#enable-unsafe-webgpu\' flag enabled.\\n\\n[](/img/playcanvas-examples-browser-webgpu.jpg)\\n\\n[PlayCanvas Examples Browser](https://playcanvas.github.io/)\\n\\nLet us know what you think in the [forums](https://forum.playcanvas.com/t/engine-release-v1-62/30360)!\\n\\n### Attributions\\n\\n[Oldsmobile Cutlass Supreme Sedan \'71](https://sketchfab.com/3d-models/oldsmobile-cutlass-supreme-sedan-71-78f76d386a4341b0b71745bdc50fd5ab) by Barbo is licensed under [Creative Commons Attribution](https://creativecommons.org/licenses/by/4.0/)"},{"id":"webxr-ar-made-easy-with-playcanvas","metadata":{"permalink":"/webxr-ar-made-easy-with-playcanvas","editUrl":"https://github.com/playcanvas/blog/tree/main/blog/2023-03-16-webxr-ar-made-easy-with-playcanvas.md","source":"@site/blog/2023-03-16-webxr-ar-made-easy-with-playcanvas.md","title":"WebXR AR Made Easy with PlayCanvas","description":"We are excited to announce the launch of our WebXR AR Starter Kit, available in the New Project dialog today!","date":"2023-03-16T00:00:00.000Z","tags":[{"inline":true,"label":"ar","permalink":"/tags/ar"},{"inline":true,"label":"webxr","permalink":"/tags/webxr"}],"readingTime":0.815,"hasTruncateMarker":true,"authors":[{"name":"Steven Yau","title":"Partner Relations Manager","page":{"permalink":"/authors/steven"},"socials":{"x":"https://x.com/yaustar","linkedin":"https://www.linkedin.com/in/stevenyau/","github":"https://github.com/yaustar"},"imageURL":"https://media.licdn.com/dms/image/v2/D4E03AQE3XHwaxq_kNw/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1688384458829?e=1730332800&v=beta&t=9FiqE0-B2UZTHVhkbbT8xgdBF62LkcApkcP9HOvuOUM","key":"steven"}],"frontMatter":{"authors":"steven","slug":"webxr-ar-made-easy-with-playcanvas","title":"WebXR AR Made Easy with PlayCanvas","tags":["ar","webxr"]},"unlisted":false,"prevItem":{"title":"Initial WebGPU support lands in PlayCanvas Engine 1.62!","permalink":"/initial-webgpu-support-lands-in-playcanvas-engine-1-62"},"nextItem":{"title":"Draco Mesh Compression Arrives in the PlayCanvas Editor","permalink":"/draco-mesh-compression-arrives-in-the-playcanvas-editor"}},"content":"We are excited to announce the launch of our WebXR AR Starter Kit, available in the New Project dialog today!\\n\\n[](/img/webar-xr-starterkit-project-dialog.jpg)\\n\\n[WebXR](https://immersiveweb.dev/) is a technology that powers immersive and interactive AR and VR experiences to be accessed through supported web browsers. This allows us to build memorable, engaging content and share them with just a URL. No installs needed!\\n\\n\x3c!-- truncate --\x3e\\n\\nThe starter kit comes with all you need to kickstart your AR experience for WebXR including:\\n\\n- Real world light estimation\\n- AR shadow renderer\\n- AR object resizing and positioning controls\\n- Physics raycasting\\n- And more!\\n\\nLook how quickly you can create AR experiences below!\\n\\n
\\n \\n
\\n\\n[Pacman Arcade + animation](https://sketchfab.com/3d-models/pacman-arcade-animation-0b43f85af5384ea4bac5d6e2d3cbd008) by Daniel Br\xfcck is licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)\\n\\n[Try it on your device](https://playcanv.as/p/inoDeWOQ/)\\n\\nGive the Starter Kit a try today at [playcanvas.com](https://playcanvas.com) where you can use it for free!"},{"id":"draco-mesh-compression-arrives-in-the-playcanvas-editor","metadata":{"permalink":"/draco-mesh-compression-arrives-in-the-playcanvas-editor","editUrl":"https://github.com/playcanvas/blog/tree/main/blog/2023-02-28-draco-mesh-compression-arrives-in-the-playcanvas-editor.md","source":"@site/blog/2023-02-28-draco-mesh-compression-arrives-in-the-playcanvas-editor.md","title":"Draco Mesh Compression Arrives in the PlayCanvas Editor","description":"We are thrilled to announce the immediate availability of Draco Mesh Compression in the PlayCanvas Editor! Our latest feature allows developers to compress meshes using Google\'s Draco technology, reducing file sizes and enhancing the end-user experience.","date":"2023-02-28T00:00:00.000Z","tags":[{"inline":true,"label":"compression","permalink":"/tags/compression"},{"inline":true,"label":"editor","permalink":"/tags/editor"},{"inline":true,"label":"gltf","permalink":"/tags/gltf"},{"inline":true,"label":"performance","permalink":"/tags/performance"}],"readingTime":1.655,"hasTruncateMarker":true,"authors":[{"name":"Will Eastcott","title":"CEO","description":"Will Eastcott is an entrepreneur and veteran technologist of the games industry with experience at EA, Sony, and Activision. He has been credited in many AAA game franchises such as GTA, Call of Duty and Max Payne. He is best known for co-founding PlayCanvas, the web graphics creation platform. As CEO, he has championed the company\'s mission to make graphical web app development more accessible and collaborative through open-source technologies and cloud-based tools.","page":{"permalink":"/authors/will"},"socials":{"x":"https://x.com/willeastcott","linkedin":"https://www.linkedin.com/in/willeastcott/","github":"https://github.com/willeastcott"},"imageURL":"https://media.licdn.com/dms/image/v2/D4D03AQF9YXIeHZW9kg/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1688995668268?e=1730332800&v=beta&t=UmAwlJ0nPBmnMczQ4wknx1jSDJ2anqurm2b15iLm3pM","key":"will"}],"frontMatter":{"authors":"will","slug":"draco-mesh-compression-arrives-in-the-playcanvas-editor","title":"Draco Mesh Compression Arrives in the PlayCanvas Editor","tags":["compression","editor","gltf","performance"]},"unlisted":false,"prevItem":{"title":"WebXR AR Made Easy with PlayCanvas","permalink":"/webxr-ar-made-easy-with-playcanvas"},"nextItem":{"title":"How to make your HTML5 Games Awesome!","permalink":"/how-to-make-your-html5-games-awesome"}},"content":"We are thrilled to announce the immediate availability of Draco Mesh Compression in the PlayCanvas Editor! Our latest feature allows developers to compress meshes using Google\'s Draco technology, reducing file sizes and enhancing the end-user experience.\\n\\n\x3c!-- truncate --\x3e\\n\\nAt its core, [Draco Mesh Compression](https://google.github.io/draco/) reduces the amount of data needed to represent 3D graphics without compromising visual quality. The technology achieves this by applying a lossy compression algorithm to the mesh data. With less data to transfer, the result is faster load times and lower bandwidth costs for your applications.\\n\\nThe open source [PlayCanvas Engine](https://github.com/playcanvas/engine) has been able to load Draco-compressed glTF 2.0 files for quite some time now. But now you can generate these Draco-compressed glTF files in the Editor at import time. Check out how easy it is to use:\\n\\n
\\n \\n
\\n\\n_[\\"1972 Datsun 240k GT\\"](https://skfb.ly/6VtZu) by Karol Miklas is licensed under [Creative Commons Attribution-ShareAlike](https://creativecommons.org/licenses/by-sa/4.0/)._\\n\\nIn the example above, a **49.9MB** GLB file is crunched down to only **3.67MB**. That\'s a **92.6% reduction is file size**! And for the majority of scenes, you should notice _no difference in terms of visual quality_. The only cost is decompression time when the compressed GLB is downloaded by an end user, but this should be significantly less than what is saved in terms of download time.\\n\\nTo enable the feature, open your Project Settings in the Inspector, expand the Asset Tasks panel and edit the Mesh Compression setting. Then, simply Re-Import any existing FBX or GLB and compression will be applied. Any FBX or GLB subsequently imported will also respect your mesh compression setting. Read more on the [Developer Site](https://developer.playcanvas.com/user-manual/assets/import-pipeline/#mesh-compression).\\n\\nWe believe that mesh compression is going to take many types of applications to the next level, particularly e-commerce applications like product configurators, which need to load detailed meshes as fast as possible.\\n\\nGet started with PlayCanvas today and make your WebGL dreams a reality!"},{"id":"how-to-make-your-html5-games-awesome","metadata":{"permalink":"/how-to-make-your-html5-games-awesome","editUrl":"https://github.com/playcanvas/blog/tree/main/blog/2023-02-07-how-to-make-your-html5-games-awesome.md","source":"@site/blog/2023-02-07-how-to-make-your-html5-games-awesome.md","title":"How to make your HTML5 Games Awesome!","description":"How To Make Your HTML5 Games Awesome","date":"2023-02-07T00:00:00.000Z","tags":[{"inline":true,"label":"gamedev","permalink":"/tags/gamedev"},{"inline":true,"label":"html5","permalink":"/tags/html-5"},{"inline":true,"label":"webgl","permalink":"/tags/webgl"}],"readingTime":11.64,"hasTruncateMarker":true,"authors":[{"name":"Paulo Oliveira","title":"Associate Partner Support Engineer","page":{"permalink":"/authors/paulo"},"socials":{"linkedin":"https://www.linkedin.com/in/paulo-oliveira-ninitoph/"},"imageURL":"https://media.licdn.com/dms/image/v2/C4D03AQGBCxiefbbUlw/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1654615914498?e=1730332800&v=beta&t=430DvI8uuos5obx7BhhHBqhT5TnNTv3q91eOOg6diII","key":"paulo"}],"frontMatter":{"authors":"paulo","slug":"how-to-make-your-html5-games-awesome","title":"How to make your HTML5 Games Awesome!","tags":["gamedev","html5","webgl"]},"unlisted":false,"prevItem":{"title":"Draco Mesh Compression Arrives in the PlayCanvas Editor","permalink":"/draco-mesh-compression-arrives-in-the-playcanvas-editor"},"nextItem":{"title":"PlayCanvas now supports Microsoft volumetric video playback","permalink":"/playcanvas-now-supports-microsoft-volumetric-video-playback"}},"content":"import ReactPlayer from \'react-player\'\\n\\n\\n\\nThe quality of a video game is often determined by how polished it is. It\'s the attention to detail and the finishing touches that can make a good game great. In this article, we\'ll take a look at the importance of polish in game development and how it can significantly enhance the overall experience.\\n\\n\x3c!-- truncate --\x3e\\n\\nWe\'ll use [Space Rocks!](https://playcanvas.com/project/1029772/overview/space-rocks), a simple Asteroids game created with the PlayCanvas game engine to showcase how even the smallest details can make a big impact.\\n\\n> [Game juice](https://www.youtube.com/watch?v=Fy0aCDmgnxg) is a design term to refer to the small visual and audio effects that are added to a game to make it feel more satisfying to play. This can include things like screen shakes, particle effects, and sound effects that are triggered when the player takes certain actions. Game juice is all about enhancing the overall feel of a game and making it more immersive and enjoyable.\\n\\nParticularly, we\'ll explore how game polish can be achieved through **game juice**.\\n\\n\\n\\n[Play it here!](https://playcanvas.com/project/1014332/overview/space-rocks)\\n\\n## How it started\\n\\n\\n\\nThis was our starting point before we added game juice. While the game is fully functional and plays well, it lacks the visual and audio effects that would make it truly engaging. As a result, it feels a bit dull and uninteresting.\\n\\nHowever, with the right attention to detail and some careful implementation of game juice, we can transform this basic Asteroids game into something much more exciting and satisfying to play.\\n\\n## What can we improve?\\n\\nTo think about what should have game juice, I always try to narrow down the most common interaction or core mechanic of the game. In our case, that would probably be:\\n\\n- Shooting\\n- Destroying asteroids\\n- Colliding with asteroids\\n\\nWith those three key pieces in mind, let\'s start thinking about how we can improve them.\\n\\n## For shooting\\n\\nIt\'s not very interesting right now:\\n\\n\\n\\nIf we want to change that, there\'s a few key things we can do. We can increase the fire rate through a script that allows us to easily control by decreasing the fire cooldown.\\n\\n```javascript\\nGun.attributes.add(\'cooldown\', {\\n type: \'number\',\\n default: 0.25,\\n title: \'Cooldown\',\\n description: \'How long the gun has to wait between firing each bullet\'\\n});\\n\\nGun.prototype.update = function (dt) {\\n this._cooldownTimer -= dt;\\n\\n if (this.app.mouse.isPressed(pc.MOUSEBUTTON_LEFT) && this.canFire()) {\\n this.fireBullet();\\n }\\n};\\n```\\n\\nIn fact, while we\'re at it, let\'s make shooting a bit more unpredictable. Let\'s add some spread to our shots!\\n\\n```javascript\\nGun.attributes.add(\'spread\', {\\n type: \'number\',\\n default: 10,\\n title: \'Bullet Spread\',\\n description: \'Up to how many degrees each bullet should vary in Y rotation.\'\\n});\\n\\nGun.prototype.applySpreadOn = function (bullet) {\\n var rotation = this.entity.getEulerAngles();\\n rotation.y += getRandomDeviation(this.spread);\\n bullet.setEulerAngles(rotation);\\n};\\n```\\n\\nA simple but impactful change! Here\'s how it looks with values I put in for some fun:\\n\\n\\n\\nI highly encourage you to play with these values to see what\'s fun for you!\\n\\nIt\'s getting better, but still not there. Let\'s think about more visual aspects now. What more can we do to make it more visually appealing?\\n\\nPlayCanvas has a nice feature that allows you to have tons of lights in your scene with very little performance impact! It\'s called \u2728 Clustered Lighting \u2728.\\n\\n