<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Lizhiyu's Blog]]></title><description><![CDATA[Bringing the world closer together through play]]></description><link>https://lizhiyu.me</link><generator>RSS for Node</generator><lastBuildDate>Tue, 14 Apr 2026 21:20:42 GMT</lastBuildDate><atom:link href="https://lizhiyu.me/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to use SCP command]]></title><description><![CDATA[The SCP (Secure Copy) command in the command line interface:The basic syntax for the SCP command is:
scp [options] source_file [user@]host1:destination_file

The key elements are:

[options]: Various options you can use to customize the SCP command, ...]]></description><link>https://lizhiyu.me/how-to-use-scp-command</link><guid isPermaLink="true">https://lizhiyu.me/how-to-use-scp-command</guid><category><![CDATA[cli]]></category><dc:creator><![CDATA[Lizhiyu]]></dc:creator><pubDate>Fri, 03 Nov 2023 16:00:00 GMT</pubDate><content:encoded><![CDATA[<p>The SCP (Secure Copy) command in the command line interface:The basic syntax for the SCP command is:</p>
<pre><code class="lang-plaintext">scp [options] source_file [user@]host1:destination_file
</code></pre>
<p>The key elements are:</p>
<ul>
<li><p><code>[options]</code>: Various options you can use to customize the SCP command, such as <code>-r</code> for recursive copying, <code>-C</code> for compression, <code>-P</code> for specifying the port, etc.</p>
</li>
<li><p><code>source_file</code>: The file or directory you want to copy, which can be local or on a remote host.</p>
</li>
<li><p><code>[user@]host1</code>: The remote host you want to copy the file to, along with the username if required.</p>
</li>
<li><p><code>destination_file</code>: The location on the remote host where you want to copy the file.</p>
</li>
</ul>
<p>Some common examples of using SCP:</p>
<ul>
<li><p>Copy a local file to a remote host:</p>
<pre><code class="lang-plaintext">  scp local_file.txt user@remote_host:/remote/directory/
</code></pre>
</li>
<li><p>Copy a remote file to the local machine:</p>
<pre><code class="lang-plaintext">  scp user@remote_host:/remote/file.txt /local/directory/
</code></pre>
</li>
<li><p>Copy a directory recursively from local to remote:</p>
<pre><code class="lang-plaintext">  scp -r local_directory/ user@remote_host:/remote/directory/
</code></pre>
</li>
<li><p>Copy between two remote hosts:</p>
<pre><code class="lang-plaintext">  scp user1@host1:/remote/file.txt user2@host2:/remote/directory/
</code></pre>
</li>
</ul>
<p>The SCP command uses SSH for secure authentication and encryption, so you'll need to have SSH access to the remote host. You may also need to specify the SSH private key file using the <code>-i</code> option if you're using key-based authentication.Overall, the SCP command provides a simple and secure way to transfer files between local and remote systems over a network.</p>
]]></content:encoded></item><item><title><![CDATA[Encounter "Support for password authentication was removed."]]></title><description><![CDATA[From 2021-08-13, GitHub is no longer accepting account passwords when authenticating Git operations. You need to add a PAT (Personal Access Token) instead, and you can follow the below method to add a PAT on your system.

Create Personal Access Token...]]></description><link>https://lizhiyu.me/encounter-support-for-password-authentication-was-removed</link><guid isPermaLink="true">https://lizhiyu.me/encounter-support-for-password-authentication-was-removed</guid><dc:creator><![CDATA[Lizhiyu]]></dc:creator><pubDate>Sat, 14 Oct 2023 16:00:00 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>From 2021-08-13, GitHub is no longer accepting account passwords when authenticating Git operations. You need to add a <strong>PAT (Personal Access Token)</strong> instead, and you can follow the below method to add a PAT on your system.</p>
</blockquote>
<h2 id="heading-create-personal-access-token-on-github">Create Personal Access Token on GitHub</h2>
<p>From your GitHub account, go to <strong>Settings</strong> → <strong>Developer Settings</strong> → <strong>Personal Access Token</strong> → <strong>Tokens (classic)</strong> → <strong>Generate New Token</strong> (Give your password) → <strong>Fillup the form</strong> → click <strong>Generate token</strong> → <strong>Copy the generated Token</strong>, it will be something like <code>ghp_sFhFssdfdfgfhtLjmks4Tzuzgthdvfsrta</code></p>
<p>on My Ubuntu:</p>
<p>For Linux, you need to configure the local GIT client with a username and email address,</p>
<pre><code class="lang-plaintext">$ git config --global user.name "your_github_username"
$ git config --global user.email "your_github_email"
$ git config -l
</code></pre>
<p>Once GIT is configured, we can begin using it to access GitHub. Example:</p>
<pre><code class="lang-plaintext">$ git clone https://github.com/YOUR-USERNAME/YOUR-REPOSITORY
&gt; Cloning into `YOUR-REPOSITORY`...
Username: &lt;type your username&gt;
Password: &lt;type your password or personal access token (GitHub)
</code></pre>
<p>Now cache the given record in your computer to remembers the token:</p>
<pre><code class="lang-plaintext">$ git config --global credential.helper cache
</code></pre>
<p>If needed, anytime you can delete the cache record by:</p>
<pre><code class="lang-plaintext">$ git config --global --unset credential.helper
$ git config --system --unset credential.helper
</code></pre>
<p>Now try to pull with <code>-v</code> to verify</p>
<pre><code class="lang-plaintext">$ git pull -v
</code></pre>
]]></content:encoded></item><item><title><![CDATA[如何将静态页面部署到Github Page，并绑定自定义域名]]></title><description><![CDATA[在仓库主页选择setting

点击pages

选择分支branch

输入自定义域名custom domain（设置好后点击save,系统将自动在项目根目录生成CNAME文件，文件内容为设置的当前域名）



在域名提供商处，添加DNS设置(以阿里云为例)

配置CNAME
  

配置IP,创建一个A类记录指向185.199.108.153



github将根据访问域名路由项目仓库（仓库名称为iddz.fun,并且根目录有CNAME文件）

最后，通过访问自定义域名，完成静态网站的部署...]]></description><link>https://lizhiyu.me/github-page</link><guid isPermaLink="true">https://lizhiyu.me/github-page</guid><category><![CDATA[github,web deploy]]></category><dc:creator><![CDATA[Lizhiyu]]></dc:creator><pubDate>Wed, 04 Oct 2023 16:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1713886590566/81f08458-e6cc-4429-b620-0a0a5bf61c34.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<ul>
<li><p>在仓库主页选择setting</p>
<ul>
<li><p>点击pages</p>
<ul>
<li><p>选择分支branch</p>
</li>
<li><p>输入自定义域名custom domain（设置好后点击save,系统将自动在项目根目录生成CNAME文件，文件内容为设置的当前域名）</p>
</li>
</ul>
</li>
<li><p>在域名提供商处，添加DNS设置(以阿里云为例)</p>
<ul>
<li><p>配置CNAME</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713885869080/8da64483-ae80-4469-a6fc-1cd46c178b42.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>配置IP,创建一个A类记录指向<code>185.199.108.153</code></p>
</li>
</ul>
</li>
<li><p>github将根据访问域名路由项目仓库（仓库名称为iddz.fun,并且根目录有CNAME文件）</p>
</li>
<li><p>最后，通过访问自定义域名，完成静态网站的部署</p>
</li>
<li><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713886320430/cecf39b2-aa34-454b-9f96-f892c2b55c1c.png" alt class="image--center mx-auto" /></p>
</li>
</ul>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[熟悉熟悉官方文档，逐步深入Babylon.js]]></title><description><![CDATA[文档的组织结构

通过前一篇文章的了解，我们会对Babylon.js有了一个大致的了解，整个文档主要是想带你逐步深入掌握这个Babylon.js所提供的所有内容。 内容主要分为一个概览和9个主要部分，这些部分包含章节，还有API详解和强大的文档和playground搜索功能。
- 1.Babylon.js特性 - Babylon.js是一个功能完备的游戏和渲染引擎，具有广泛的特性。这个部分将带你了解这些特性，并帮助你编码和使用它们。- 2.将Babylon.js添加到你的Web项目中 - 有多种...]]></description><link>https://lizhiyu.me/babylonjs</link><guid isPermaLink="true">https://lizhiyu.me/babylonjs</guid><category><![CDATA[WebGL]]></category><category><![CDATA[webgpu]]></category><category><![CDATA[webgame]]></category><category><![CDATA[web3d]]></category><dc:creator><![CDATA[Lizhiyu]]></dc:creator><pubDate>Mon, 25 Sep 2023 16:23:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1713887016488/9a5c7c76-1f9d-43f7-b034-dfc1026dcc13.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-5pah5qgj55qe57ue57uh57ut5p6e">文档的组织结构</h3>
<hr />
<p>通过前一篇文章的了解，我们会对Babylon.js有了一个大致的了解，整个文档主要是想带你逐步深入掌握这个Babylon.js所提供的所有内容。 内容主要分为一个概览和9个主要部分，这些部分包含章节，还有API详解和强大的文档和playground搜索功能。</p>
<p>- 1.Babylon.js特性 - Babylon.js是一个功能完备的游戏和渲染引擎，具有广泛的特性。这个部分将带你了解这些特性，并帮助你编码和使用它们。<br />- 2.将Babylon.js添加到你的Web项目中 - 有多种方式可以在你的web应用中使用Babylon.js并设置Babylon.js项目。通过这个部分内容，你能了解到如何轻松地将Babylon.js融入自己的项目，并打造出别具一格的产品，并且还为你提供一些项目模板和支持。<br />- 3.Playground - 对Babylon.js的核心资源进行详细的查看，了解如何使用它以及它所提供的许多功能设施，包括搜索所有playground以查找其他人所做的示例（这一点真心觉得很棒）。<br />- 4.工具和资源 - Babylon.js有许多令人难以置信的工具，使开发过程简单而愉快。它还有几个内容库供你完全免费使用。这个部分将帮助你理解并利用他们。<br />- 5.为Babylon.js准备素材 - 从模型和动画，到纹理，LUTS和HDR图像，在这个部分学习准备Babylon.js内容的工作流和方法。<br />- 6.Babylon.js跨平台 - 在这个部分，你将学习如何扩展Babylon.js，并使用它开发几乎任何你能想象的平台的原生应用。<br />- 7.贡献 - 对于那些想要扩展Babylon.js边界的人，这个部分描述了需要什么以及如何为Babylon.js的代码库和文档做出贡献。<br />- 8.引导式学习 - 有时候边做边学是最好的。这个部分帮助你做到这一点。这些逐步的文章和教程将帮助你从社区的一些最好和最聪明的老师那里学习（babylon.js 社区的维护者似乎是真的闲，有时候很简单甚至看起来有些幼稚的问题都会得到很详细的回答，这一点是真的佩服）。<br />-9.社区扩展 - 在这个部分，你将找到由极其有才华和热情的社区构建的对Babylon.js的令人难以置信的资源和扩展。</p>
<h3 id="heading-5aac5l2v5y675l255so5pah5qgj">如何去使用文档</h3>
<hr />
<p>Babylon.js是一个丰富的API，希望尽可能简单地利用GPU的强大功能来提升浏览器的上网体验。你可以通过用javascript或typescript编写Babylon.js场景代码来利用这个强大的API。为了保持一致性并最大化文档的可访问性，这些页面中找到的所有示例代码都将用javascript提供。</p>
<p>文档页面布局有几个特性，你需要了解这些特性，以便在学习旅程中获得最大的收益。 首先，最左边的面板是导航窗格。这个窗格是你导航到想去的地方的方式。</p>
<p><img src="https://doc.babylonjs.com/img/home/home1.jpg" alt="Navigation Pane" /></p>
<p>接下来是内容窗格。它位于中间，包含主要的内容和信息。</p>
<p><img src="https://doc.babylonjs.com/img/home/home2.jpg" alt="Content Pane" /></p>
<p>一些页面有很多信息，这些信息被组织成子部分。目录窗格使得在较长的页面中导航变得容易。</p>
<p><img src="https://doc.babylonjs.com/img/home/home4.jpg" alt="Table of Contents" /></p>
<p>右边的面板是示例窗格。它提供了对当前页面上每个playground（实时示例）的快速访问。</p>
<p><img src="https://doc.babylonjs.com/img/home/home3.jpg" alt="Examples Pane" /></p>
<p>最后，如果你在示例窗格中选择任何一个示例，它将在页面内容顶部的playground窗格内加载该示例。</p>
<p><img src="https://doc.babylonjs.com/img/home/home5.jpg" alt="Examples Pane" /></p>
<h3 id="heading-5ywz5lqo54mi5pys5o6n5yi255qe6k05pio">关于版本控制的说明</h3>
<hr />
<p>Babylon.js文档是以Babylon.js的最新主版本为参考编写的。<a target="_blank" href="https://doc.babylonjs.com/typedoc">API 文档</a>是针对最新的每日构建生成的。</p>
<p>如果你想使用最新版本在本地安装Babylon.js，请从<a target="_blank" href="https://www.npmjs.com/package/@babylonjs/core">npm页面</a>选择最新的发布版本，然后运行：</p>
<pre><code class="lang-plaintext">npm i @babylonjs/core@preview
</code></pre>
<p>不要对使用最新版本感到害怕。Babylon的npm发布版本被认为是稳定的，并且已经过团队的全面测试。</p>
]]></content:encoded></item><item><title><![CDATA[Pomelo]]></title><description><![CDATA[MMO 架构

框架中的组件

服务器抽象

目录结构

请求／响应、广播示例

RPC调用抽象

可插拔组件]]></description><link>https://lizhiyu.me/pomelo</link><guid isPermaLink="true">https://lizhiyu.me/pomelo</guid><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[Lizhiyu]]></dc:creator><pubDate>Mon, 15 Aug 2022 01:56:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/YSvyCDfQJrk/upload/v1660528566926/j2Bq_2AmWi.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<ul>
<li>MMO 架构
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660530447301/HZG8e2Xdi.png" alt="mmo-arch.png" /></li>
<li>框架中的组件
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660528132891/eOSHlwwyg.png" alt="pomelo-arch.png" /></li>
<li>服务器抽象
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660528130690/-pue5j7K2.png" alt="serverAbst.png" /></li>
<li>目录结构
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660530466307/lPdpIhUKb.png" alt="serverAbsDir.png" /></li>
<li>请求／响应、广播示例
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660528128116/M7_2QpqG-.png" alt="req-resp.png" /></li>
<li>RPC调用抽象
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660528124991/QoHoJFTEC.png" alt="rpc.png" /></li>
<li>可插拔组件
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660528088852/be3kXe34s.png" alt="components.png" /></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[从零开始，与BabylonJS一起，开启你的Web3D之旅吧]]></title><description><![CDATA[欢迎来到 BabylonJS 零基础教程，本教程为萌新导向。无论你是Web开发的专家，富有想象力的设计师，还是充满好奇心的学生，只要你对3D相关的内容感兴趣。本教程都是你学习BabylonJS的绝佳开始，我们强烈建议每一个BabylonJS的初学者都从本教程开始学习。

本教程将与你一起，在浏览器上打造一个BabylonJS的小项目。你将了解到BabylonJS的演习场（playground），从最基础的引擎核心开始，完成一个可供发布的.html网页文件。
演习场（playground）
演习场...]]></description><link>https://lizhiyu.me/babylonjsweb3d</link><guid isPermaLink="true">https://lizhiyu.me/babylonjsweb3d</guid><category><![CDATA[WebGL]]></category><dc:creator><![CDATA[Lizhiyu]]></dc:creator><pubDate>Thu, 21 Jul 2022 16:12:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1658421509496/AUMyDFnFn.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1658421252897/Jwqbfh4sf.png" alt="Screen Shot 2022-07-22 at 00.33.32.png" /></p>
<blockquote>
<p>欢迎来到 BabylonJS 零基础教程，本教程为萌新导向。无论你是Web开发的专家，富有想象力的设计师，还是充满好奇心的学生，只要你对3D相关的内容感兴趣。本教程都是你学习BabylonJS的绝佳开始，我们强烈建议每一个BabylonJS的初学者都从本教程开始学习。</p>
</blockquote>
<p>本教程将与你一起，在浏览器上打造一个BabylonJS的小项目。你将了解到BabylonJS的演习场（playground），从最基础的引擎核心开始，完成一个可供发布的.html网页文件。</p>
<h3 id="heading-playground">演习场（playground）</h3>
<p>演习场是你开发BabylonJS程序最重要的工具，注意，没有之一。</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1658421347293/kOkoHCp2b.jpeg" alt="playground.jpeg" /></p>
<p>在演习场中，你可以随时随地实践你的想法，打造你的实验项目。将代码写入左侧的类似VS Code的编辑界面中，在右侧就能立马运行显示结果。</p>
<p>演习场将是你学习旅途中的贴心小伙伴，因为它提供了全宇宙最科学的循环学习体系，输入代码-&gt;察看结果，让你在反复实践中找到感觉。除此之外，还有一点十分重要，你可以随时保存或分享演习场的内容。这在你学习的过程中遇到问题时变得十分有用，你可以通过<a target="_blank" href="https://forum.babylonjs.com/">Babylon.js 论坛</a>寻求帮助，官方的开发者和社区工作人员随时为你效劳。</p>
<p>当我们在帮助你解决问题的时候，最好的方法就是你能将问题通过演练场分享出来，这样一来，我们就能快速地定位到问题的关键所在。所以，只要你遇到任何问题，保存你的演练场，分享到论坛。你一定会被我们迅雷不及掩耳的回复速度所震撼。</p>
<p>说了这么多，既然演练场这重要，我们就来实操一下吧。</p>
<p>新建一个页面打开下方链接：</p>
<p><a target="_blank" href="https://playground.babylonjs.com/">Babylon.js Playground</a></p>
<h3 id="heading-5yib5bu65l2g55qe56ys5lia5liq5zy65pmv">创建你的第一个场景</h3>
<p>欢迎来到演习场，你只需要在左边修改/创建代码，就能在右边窗口看见实时运行结果。下面是我们为你提供的一个默认模版场景。快速地瞄一眼代码编辑窗口的绿色注释文字，这些文字会告诉你每一行代码的作用，贴心吧？</p>
<p>最好的学习方法就是在敲代码，边敲边学，边学边敲。</p>
<p>来让我们来尝试给这个默认场景做些调整。</p>
<p>20-24行代码：</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Our built-in 'sphere' shape. //引擎内建的球体。</span>
<span class="hljs-keyword">var</span> sphere = BABYLON.MeshBuilder.CreateSphere(<span class="hljs-string">"sphere"</span>, {diameter: <span class="hljs-number">2</span>, segments: <span class="hljs-number">32</span>}, scene);

<span class="hljs-comment">// Move the sphere upward 1/2 its height //将球体移动到它一半高度的位置,上面创建直径设置的是2，所以这里设置位置的y属性指为1</span>
sphere.position.y = <span class="hljs-number">1</span>;
</code></pre>
<p>选中这几行代码，删掉它！</p>
<p>你已经做出了第一个改动！</p>
<p>感觉还不错吧？</p>
<p>但...等一下，场景当中怎么没有发生任何事情？</p>
<p>因为我们需要在修改了代码之后，需要重新启动演练场才能看到变更结果。</p>
<p>你可以点击启动按钮</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1658421373423/9YF1Zc_hY.jpeg" alt="run.jpeg" /></p>
<p>，也可以按组合键<code>ALT+ENTER</code></p>
<p>场景中的这个球不见了，神奇吧！</p>
<p><a target="_blank" href="https://playground.babylonjs.com/#2KRNG9">点击查看Playground结果</a></p>
<blockquote>
<p>如果你遇到任何问题，可以通过打开链接，对比自己的代码来查找错误</p>
</blockquote>
<p>让我们来做一点别的改动。</p>
<p>找到创建地面的代码所在的行，在下面加上如下代码：</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> groundMaterial = <span class="hljs-keyword">new</span> BABYLON.StandardMaterial(<span class="hljs-string">"Ground Material"</span>, scene);
ground.material = groundMaterial;
ground.material.diffuseColor = BABYLON.Color3.Red();
</code></pre>
<p>再此运行（按组合键ALT+ENTER 或 点击播放按钮）</p>
<p><a target="_blank" href="https://playground.babylonjs.com/#2KRNG9#1">点击查看Playground结果</a></p>
<p>恭喜！你成功创建了一个新的材质，并将材质赋值给了地面，还将材质的漫反射通道设置为红色！</p>
<p>很简单吧？如果你还有点懵也没关系，在这里我们需要知道的就是在左边输入代码，在右边察看结果😀</p>
<p>接下来，让我们来再做第一小改动。</p>
<p>找到这一行代码：</p>
<pre><code class="lang-typescript">ground.material.diffuseColor = BABYLON.Color3.Red();
</code></pre>
<p>用下面两行代码替换掉它：</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> groundTexture = <span class="hljs-keyword">new</span> BABYLON.Texture(Assets.textures.checkerboard_basecolor_png.rootUrl, scene);
ground.material.diffuseTexture = groundTexture;
</code></pre>
<p><a target="_blank" href="https://playground.babylonjs.com/#2KRNG9#2">点击查看Playground结果</a></p>
<p>恭喜+1！你创建了你的第一个Texture并赋值给了地面材质。</p>
<p>让我们继续，在你所有创建和设置地面相关的代码后面，敲几个回车，留出点空隙来展示演习场提供给我们的大杀器--提示模板。</p>
<p>输入<code>import</code> 你将看到界面上生成了一个列表，列表里边是代码片段，每一个片段都是一个小的功能实现。</p>
<p>让我们选择 <code>Import a Mesh w/Callback</code> ，再按下回车。</p>
<p>如下代码将立即出现在你的输入框里：</p>
<pre><code class="lang-typescript">BABYLON.SceneLoader.ImportMesh(<span class="hljs-string">"meshName"</span>, <span class="hljs-string">"url to the mesh parent directory"</span>, <span class="hljs-string">"Mesh filename.fileextension"</span>, scene, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">newMeshes</span>)</span>{

});
</code></pre>
<p>我们需要在模版代码的基础上做一些小小的润色：</p>
<ul>
<li>1.删掉<code>meshName</code>,注意需要保留引号。</li>
<li>2.将<code>"url to the mesh parent directory"</code>（包括双引号） 替换为<code>Assets.meshes.Yeti.rootUrl</code></li>
<li><p>3.将"Mesh filename.fileextension" （包括双引号）替换为<code>Assets.meshes.Yeti.filename</code></p>
</li>
<li><p>4.在<code>BABYLON.SceneLoader.ImportMesh</code> 这一行下面，在<code>"});"</code>之前，输入：</p>
<pre><code class="lang-typescript">newMeshes[<span class="hljs-number">0</span>].scaling = <span class="hljs-keyword">new</span> BABYLON.Vector3(<span class="hljs-number">0.1</span>, <span class="hljs-number">0.1</span>, <span class="hljs-number">0.1</span>);
</code></pre>
</li>
<li><p>5.运行</p>
<p><a target="_blank" href="https://playground.babylonjs.com/#2KRNG9#3">点击查看Playground结果</a></p>
</li>
</ul>
<p>  恭喜+1！你成功创建了一个.gltf格式的动画模型，并调整了它的缩放比例来适配地面。</p>
<p>  最后，让我们把创建好的场景动起来，让他跟随我们的鼠标滑动而调整镜头显示内容。</p>
<p>  删除5-9行的代码，输入createarc，在提示代码模版列表中选择 <code>Create An Arc Rotate Camera w/Degree</code>。</p>
<p>  再次运行，按住鼠标拖动或者触摸拖动场景，相机的视角就会跟随你的拖动和滑动变化啦。</p>
<blockquote>
<p>本篇为翻译BabylonJS官方教程的开始,后续将陆续更新相关系列文章</p>
</blockquote>
<p><a target="_blank" href="https://doc.babylonjs.com/journey/theFirstStep">原文链接</a></p>
]]></content:encoded></item><item><title><![CDATA[Utilize Parcel  To Enjoy  TypeScrpit Out Of The Box]]></title><description><![CDATA[If you are code with TypeScript mostly, you may counter some hassles when running or debugging with tranditional web tools. Parcel may be the hassles killer.


Zero config with parcel index.html

Debug TS with soure map

Hot reloading


and more amaz...]]></description><link>https://lizhiyu.me/utilize-parcel-to-enjoy-typescrpit-out-of-the-box</link><guid isPermaLink="true">https://lizhiyu.me/utilize-parcel-to-enjoy-typescrpit-out-of-the-box</guid><category><![CDATA[parcel]]></category><dc:creator><![CDATA[Lizhiyu]]></dc:creator><pubDate>Mon, 18 Jul 2022 14:33:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660141649106/cdHO0x9Ki.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>If you are code with TypeScript mostly, you may counter some hassles when running or debugging with tranditional web tools. Parcel may be the hassles killer.</p>
</blockquote>
<ul>
<li><p>Zero config with parcel index.html</p>
</li>
<li><p>Debug TS with soure map</p>
</li>
<li><p>Hot reloading</p>
</li>
</ul>
<p>and more amazing features <a target="_blank" href="https://parceljs.org/">https://parceljs.org/</a>.</p>
<ul>
<li><p><code>mkdir parcel--starter-demo</code></p>
</li>
<li><p><code>cd ./parcel--starter-demo</code></p>
</li>
<li><p><code>yarn init -y</code> || <code>npm init -y</code></p>
</li>
<li><p><code>yarn add parcel --dev</code> || <code>npm install -D parcel</code></p>
</li>
<li><p><code>touch main.ts</code></p>
</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> content_string:<span class="hljs-built_in">string</span> = <span class="hljs-string">"Hello Parcel!"</span>;
<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"content"</span>)!.innerHTML = content_string;
</code></pre>
<p><code>touch index.html</code></p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"X-UA-Compatible"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"IE=edge"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Document<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"content"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./main.ts"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"moudule"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"parcel-demo"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"MIT"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"dev"</span>:<span class="hljs-string">"parcel ./index.html"</span>
  },
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"parcel"</span>: <span class="hljs-string">"^2.7.0"</span>
  }
}
</code></pre>
<p><code>yarn start</code> || <code>npm run start</code></p>
<p>So easy, is it?</p>
<p>Enjoy coding.</p>
]]></content:encoded></item><item><title><![CDATA[Concurrently 一键启动客户端和服务器]]></title><description><![CDATA[在日常的全栈开发过程中,往往会同时启动服务器和客户端工程,分别启动服务器和客户端往往会不可避免的造成一定时间浪费和开发流程繁杂.本着能躺着就别坐着的原则,在万能的npm库中找到了concurrently

使用concurrently同时运行多个命令
如下面package.json内的脚本代码所示,concurrently将服务端及客户端项目的启动合并在一起了.妈妈再也不用担心我浪费时间在启动项目上了😊.
"scripts": {
    "start": "concurrently --ki...]]></description><link>https://lizhiyu.me/concurrently</link><guid isPermaLink="true">https://lizhiyu.me/concurrently</guid><dc:creator><![CDATA[Lizhiyu]]></dc:creator><pubDate>Thu, 30 Jun 2022 05:55:13 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>在日常的全栈开发过程中,往往会同时启动服务器和客户端工程,分别启动服务器和客户端往往会不可避免的造成一定时间浪费和开发流程繁杂.本着能躺着就别坐着的原则,在万能的npm库中找到了<strong>concurrently</strong></p>
</blockquote>
<h3 id="heading-concurrentlyhttpswwwnpmjscompackageconcurrently">使用<a target="_blank" href="https://www.npmjs.com/package/concurrently">concurrently</a>同时运行多个命令</h3>
<p>如下面<code>package.json</code>内的脚本代码所示,concurrently将服务端及客户端项目的启动合并在一起了.妈妈再也不用担心我浪费时间在启动项目上了😊.</p>
<pre><code class="lang-json"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"concurrently --kill-others-on-fail \"npm run start-server\" \"npm run start-client\""</span>,
    <span class="hljs-attr">"start-server"</span>: <span class="hljs-string">"ts-node server/src/index.ts"</span>,
    <span class="hljs-attr">"start-client"</span>: <span class="hljs-string">"cd client &amp;&amp; npm run start"</span>
    }
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Your  Very  First  Start  Of  Three.js]]></title><description><![CDATA[使用Three.js创建一个3D网页一般都会经过以下几个步骤：

引入three.js库
创建场景
创建相机
设置相机位置
将相机添加到场景
创建几何结构
创建材质
生成3D网格对象
将3D对象添加到场景中
创建渲染器
设置渲染器尺寸
将渲染器DOM节点添加到网页DOM树
执行渲染器渲染方法

//引入three.js库
import * as THREE from 'three';

//创建场景
let scene = new THREE.Scene();

//创建相机
let camer...]]></description><link>https://lizhiyu.me/your-very-first-start-of-threejs</link><guid isPermaLink="true">https://lizhiyu.me/your-very-first-start-of-threejs</guid><category><![CDATA[ThreeJS]]></category><dc:creator><![CDATA[Lizhiyu]]></dc:creator><pubDate>Mon, 13 Jun 2022 15:15:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660142489261/yqRdSrl_L.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>使用Three.js创建一个3D网页一般都会经过以下几个步骤：</p>
<ul>
<li>引入three.js库</li>
<li>创建场景</li>
<li>创建相机</li>
<li>设置相机位置</li>
<li>将相机添加到场景</li>
<li>创建几何结构</li>
<li>创建材质</li>
<li>生成3D网格对象</li>
<li>将3D对象添加到场景中</li>
<li>创建渲染器</li>
<li>设置渲染器尺寸</li>
<li>将渲染器DOM节点添加到网页DOM树</li>
<li>执行渲染器渲染方法</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-comment">//引入three.js库</span>
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">'three'</span>;

<span class="hljs-comment">//创建场景</span>
<span class="hljs-keyword">let</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();

<span class="hljs-comment">//创建相机</span>
<span class="hljs-keyword">let</span> camera = <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(<span class="hljs-number">75</span>, <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight, <span class="hljs-number">.1</span>, <span class="hljs-number">1000</span>);
<span class="hljs-comment">//设置相机位置</span>
camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">10</span>);
<span class="hljs-comment">//将相机添加到场景</span>
scene.add(camera);

<span class="hljs-comment">//创建几何结构</span>
<span class="hljs-keyword">let</span> boxGeometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>);
<span class="hljs-comment">//创建材质</span>
<span class="hljs-keyword">let</span> boxMaterial = <span class="hljs-keyword">new</span> THREE.MeshBasicMaterial({ color: <span class="hljs-number">0x00ff00</span> });
<span class="hljs-comment">//生成3D网格对象</span>
<span class="hljs-keyword">let</span> boxMesh = <span class="hljs-keyword">new</span> THREE.Mesh(boxGeometry, boxMaterial);
<span class="hljs-comment">//将3D对象添加到场景中</span>
scene.add(boxMesh)

<span class="hljs-comment">//创建渲染器</span>
<span class="hljs-keyword">let</span> renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer();
<span class="hljs-comment">//设置渲染器尺寸</span>
renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);
<span class="hljs-comment">//将渲染器DOM节点添加到网页DOM树</span>
<span class="hljs-built_in">document</span>.body.appendChild(renderer.domElement)
<span class="hljs-comment">//执行渲染器渲染方法</span>
renderer.render(scene, camera)
</code></pre>
<p>渲染结果：</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660144621974/4fSB9gMQ1p.png" alt="Screenshot 2022-08-10 231635.png" /></p>
]]></content:encoded></item><item><title><![CDATA[Three.js + Typescript + Ammo.js | A Tiny Shooter Game]]></title><description><![CDATA[按照计划，利用Three.js和Ammo.js制作一个物理小游戏.
通过 学习借鉴以下几个学习资源：

Three.js官方文档
Orillusion官方
MIT 线性代数 1080p高清修复重制
threejs-university
《WebGL编程指南》

对3D和Three.js形成了一个初步的认识。
然后，基于https://github.com/hvidal/WebGL-Shooter项目，完成了一个类塔防的物理射击游戏。Github地址


关键概念
两个主体世界

Three.js...]]></description><link>https://lizhiyu.me/threejs-typescript-ammojs-or-a-tiny-shooter-game</link><guid isPermaLink="true">https://lizhiyu.me/threejs-typescript-ammojs-or-a-tiny-shooter-game</guid><dc:creator><![CDATA[Lizhiyu]]></dc:creator><pubDate>Wed, 25 May 2022 15:24:16 GMT</pubDate><content:encoded><![CDATA[<p>按照计划，利用Three.js和Ammo.js制作一个物理小游戏.
通过 学习借鉴以下几个学习资源：</p>
<ul>
<li><a target="_blank" href="https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene">Three.js官方文档</a></li>
<li><a target="_blank" href="https://space.bilibili.com/1006136755?spm_id_from=333.337.0.0">Orillusion官方</a></li>
<li><a target="_blank" href="https://www.bilibili.com/video/BV13Y4y1q7ZE?p=1">MIT 线性代数 1080p高清修复重制</a></li>
<li><a target="_blank" href="https://en.threejs-university.com/">threejs-university</a></li>
<li><a target="_blank" href="https://item.jd.com/1210283611.html">《WebGL编程指南》</a></li>
</ul>
<p>对3D和Three.js形成了一个初步的认识。</p>
<p>然后，基于<a target="_blank" href="https://github.com/hvidal/WebGL-Shooter">https://github.com/hvidal/WebGL-Shooter</a>项目，完成了一个类塔防的物理射击游戏。<a target="_blank" href="https://github.com/lizhiyu-me/three-ammo-defense-monster">Github地址</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653490446005/K7LQBVVSS.png" alt="QQ图片20220525225341.png" /></p>
<hr />
<h2 id="heading-5ywz6zsu5qac5b1">关键概念</h2>
<h4 id="heading-5lik5liq5li75l2t5liw55wm">两个主体世界</h4>
<ul>
<li>Three.js的视图世界 <code>THREE.Scene</code></li>
<li>Ammo.js的物理世界 <code>Ammo.btDiscreteDynamicsWorld</code></li>
</ul>
<p>通过设置THREE.Object3D 的userData.physicsBody为Ammo.btRigidBody,即使Three.js的物体添加到物理世界。</p>
<p>每帧更新物理世界的坐标,旋转等各项数据到视图世界，完成位移碰撞的及时调整。</p>
<pre><code><span class="hljs-keyword">for</span> (let i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> len; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            <span class="hljs-keyword">var</span> objThree <span class="hljs-operator">=</span> <span class="hljs-built_in">this</span>.rigidBodies[i];
            <span class="hljs-keyword">var</span> motionState <span class="hljs-operator">=</span> objThree.userData.physicsBody.getMotionState();
            <span class="hljs-keyword">if</span> (motionState) {
                motionState.getWorldTransform(<span class="hljs-built_in">this</span>.tempTransform);

                let p <span class="hljs-operator">=</span> <span class="hljs-built_in">this</span>.tempTransform.getOrigin();
                objThree.position.set(p.x(), p.y(), p.z());

                let q <span class="hljs-operator">=</span> <span class="hljs-built_in">this</span>.tempTransform.getRotation();
                objThree.quaternion.set(q.x(), q.y(), q.z(), q.w());
            }
        }
</code></pre><h3 id="heading-5z656ga5yaf5a65">基础内容</h3>
<p>－创建Three.js场景</p>
<pre><code>        <span class="hljs-built_in">this</span>.renderer <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.WebGLRenderer();
        <span class="hljs-built_in">this</span>.renderer.setClearColor(clearColor);
        <span class="hljs-built_in">this</span>.renderer.setPixelRatio(window.devicePixelRatio);
        <span class="hljs-built_in">this</span>.renderer.setSize(window.innerWidth, window.innerHeight);
        element.appendChild(<span class="hljs-built_in">this</span>.renderer.domElement);
        <span class="hljs-built_in">this</span>.scene <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> THREE.Scene();
</code></pre><ul>
<li><p>创建Ammo.js物理世界</p>
<pre><code>      const collisionConfiguration <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Ammo.btDefaultCollisionConfiguration();
      const dispatcher <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Ammo.btCollisionDispatcher(collisionConfiguration);
      const overlappingPairCache <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Ammo.btAxisSweep3(<span class="hljs-keyword">new</span> Ammo.btVector3(<span class="hljs-number">-1000</span>, <span class="hljs-number">-1000</span>, <span class="hljs-number">-1000</span>), <span class="hljs-keyword">new</span> Ammo.btVector3(<span class="hljs-number">1000</span>, <span class="hljs-number">1000</span>, <span class="hljs-number">1000</span>));
      const solver <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Ammo.btSequentialImpulseConstraintSolver();

      <span class="hljs-built_in">this</span>.physicsWorld <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Ammo.btDiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration);
      <span class="hljs-built_in">this</span>.physicsWorld.setGravity(<span class="hljs-keyword">new</span> Ammo.btVector3(<span class="hljs-number">0</span>, <span class="hljs-number">-16</span>, <span class="hljs-number">0</span>));
</code></pre></li>
<li><p>射击逻辑</p>
</li>
</ul>
<pre><code>shoot() {
        <span class="hljs-built_in">this</span>.raycaster.setFromCamera(<span class="hljs-built_in">this</span>.screenCenter, <span class="hljs-built_in">this</span>.camera);

        <span class="hljs-built_in">this</span>.pos.copy(<span class="hljs-built_in">this</span>.raycaster.ray.direction);
        <span class="hljs-built_in">this</span>.pos.add(<span class="hljs-built_in">this</span>.raycaster.ray.origin);
        <span class="hljs-built_in">this</span>.pos.setZ(<span class="hljs-built_in">this</span>.pos.z <span class="hljs-operator">-</span> <span class="hljs-number">10</span>);

        const ball <span class="hljs-operator">=</span> <span class="hljs-built_in">this</span>.factory.createSphere(<span class="hljs-built_in">this</span>.radius, <span class="hljs-built_in">this</span>.mass, <span class="hljs-built_in">this</span>.pos, <span class="hljs-built_in">this</span>.quat, <span class="hljs-built_in">this</span>.ballMaterial);
        ball.castShadow <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
        ball.receiveShadow <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;

        const body <span class="hljs-operator">=</span> ball.userData.physicsBody;
        <span class="hljs-built_in">this</span>.pos.copy(<span class="hljs-built_in">this</span>.raycaster.ray.direction);
        <span class="hljs-built_in">this</span>.pos.multiplyScalar(<span class="hljs-number">160</span>);
<span class="hljs-comment">//调整子弹速度</span>
        body.setLinearVelocity(<span class="hljs-keyword">new</span> Ammo.btVector3(<span class="hljs-built_in">this</span>.pos.x, <span class="hljs-built_in">this</span>.pos.y, <span class="hljs-built_in">this</span>.pos.z));
    }
</code></pre><hr />
<h3 id="heading-5ake5yqg5yaf5a65og">增加内容:</h3>
<ul>
<li>连击实现<pre><code><span class="hljs-comment">//鼠标按下之后，间隔时间超过0.2秒即射击一次</span>
<span class="hljs-keyword">if</span> (isMouseDowning <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> duration <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span><span class="hljs-number">.2</span>) {
              duration <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
              mouseShooter.shoot();
          }
</code></pre></li>
<li>计时作为最终的游戏得分<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">recordTotalTime</span>(<span class="hljs-params"></span>) </span>{
          <span class="hljs-keyword">if</span> (beginTime <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>) {
              beginTime <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Date().getTime();
          } <span class="hljs-keyword">else</span> {
              let _currentTime <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Date().getTime();
              totalTime <span class="hljs-operator">+</span><span class="hljs-operator">=</span> (_currentTime <span class="hljs-operator">-</span> beginTime);
              document.getElementById(<span class="hljs-string">'scoreBar'</span>).innerHTML <span class="hljs-operator">=</span> Math.floor(totalTime <span class="hljs-operator">/</span> <span class="hljs-number">1000</span>) <span class="hljs-operator">+</span> <span class="hljs-string">"."</span> <span class="hljs-operator">+</span> totalTime <span class="hljs-operator">%</span> <span class="hljs-number">1000</span>;
              beginTime <span class="hljs-operator">=</span> _currentTime;
          }
      }
</code></pre></li>
<li>游戏结束判断（物体在斜坡底部掉落）<pre><code><span class="hljs-comment">//斜坡底部的z坐标值计算</span>
<span class="hljs-keyword">var</span> edgeZ <span class="hljs-operator">=</span> Math.cos(groundRotationX) <span class="hljs-operator">*</span> groundScaleZ <span class="hljs-operator">/</span> <span class="hljs-number">2</span>;
</code></pre><pre><code><span class="hljs-comment">//判断斜坡上的物体的z轴坐标是否大于斜坡底部的z坐标值</span>
  <span class="hljs-keyword">private</span> checkGameOver(controls, edgeZ): boolean {
      const len <span class="hljs-operator">=</span> <span class="hljs-built_in">this</span>.rigidBodies_slope.<span class="hljs-built_in">length</span>;
      <span class="hljs-keyword">for</span> (let i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> len; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
          <span class="hljs-keyword">var</span> objThree <span class="hljs-operator">=</span> <span class="hljs-built_in">this</span>.rigidBodies_slope[i];
          <span class="hljs-keyword">if</span> (objThree.position.z <span class="hljs-operator">&gt;</span> edgeZ) {
              controls.enabled <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
              const message <span class="hljs-operator">=</span> document.getElementById(<span class="hljs-string">'message'</span>);
              const blocker <span class="hljs-operator">=</span> document.getElementById(<span class="hljs-string">'blocker'</span>);
              const gameOver <span class="hljs-operator">=</span> document.getElementById(<span class="hljs-string">'gameOver'</span>);
              blocker.style.display <span class="hljs-operator">=</span> <span class="hljs-string">'none'</span>;
              message.style.display <span class="hljs-operator">=</span> <span class="hljs-string">'none'</span>;
              gameOver.style.display <span class="hljs-operator">=</span> <span class="hljs-string">'block'</span>;
              lockPointer(controls);
              document.getElementById(<span class="hljs-string">'score'</span>).innerHTML <span class="hljs-operator">=</span> document.getElementById(<span class="hljs-string">'scoreBar'</span>).innerHTML;
              <span class="hljs-built_in">this</span>.isGameOver <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
              <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
          }
      }
  }
</code></pre></li>
</ul>
<p>没想到用three.js和ammo.js做一个小DEMO会花这么多时间去学习,通过这个学习的过程也了解到了3D和物理相关的知识在广度和深度上都是值得去探索的，后面我也会继续在web3D的方向上继续探索学习。</p>
<p>做难而正确的事情，让正确的事情持续发生。</p>
]]></content:encoded></item><item><title><![CDATA[Three Ways To Show Your Games Back In Epic Launcher & Epic 游戏库不见了？消失了？这样找回来]]></title><description><![CDATA[时值51假期，我的一个白嫖了很多Epic商城游戏的朋友。
他跟我说，昨天他打开Epic准备玩《文明6》的时候，突然发现游戏库里面的游戏都不见了，望着往日辛辛苦苦摆白嫖而来的整整100多个游戏,如今变得空空荡荡，他陷入了沉思，去各大网站搜索均无结果，扬言要卸载Epic以泄愤怒。
当我得知此消息后，第一时间通过邮件的方式帮助他联系了Epic官方客服，没想很快就收到对方的回信，确定了本机原因后，对方给出了如下3个解决方案。
最终我的朋友通过第一个解决方案找回了他的所有游戏，于是乎，我的朋友像往常一样，...]]></description><link>https://lizhiyu.me/three-ways-to-show-your-games-back-in-epic-launcher-and-epic</link><guid isPermaLink="true">https://lizhiyu.me/three-ways-to-show-your-games-back-in-epic-launcher-and-epic</guid><dc:creator><![CDATA[Lizhiyu]]></dc:creator><pubDate>Mon, 02 May 2022 14:55:34 GMT</pubDate><content:encoded><![CDATA[<p>时值51假期，我的一个白嫖了很多Epic商城游戏的朋友。
他跟我说，昨天他打开Epic准备玩《文明6》的时候，突然发现游戏库里面的游戏都不见了，望着往日辛辛苦苦摆白嫖而来的整整100多个游戏,如今变得空空荡荡，他陷入了沉思，去各大网站搜索均无结果，扬言要卸载Epic以泄愤怒。</p>
<p>当我得知此消息后，第一时间通过邮件的方式帮助他联系了Epic官方客服，没想很快就收到对方的回信，确定了本机原因后，对方给出了如下3个解决方案。
最终我的朋友通过第一个解决方案找回了他的所有游戏，于是乎，我的朋友像往常一样，进入了他的游戏世界，快乐得像个孙子。</p>
<h3 id="heading-disable-fullscreen-optimization">Disable fullscreen optimization</h3>
<ul>
<li>Shut down Epic Games launcher completely.</li>
<li>Go into the directory where it is installed, right-click on the launcher exe file, and click properties (Epic Games\Launcher\Engine\Binaries\Win64\EpicGamesLauncher.exe).</li>
<li>Go into the Compatibility tab.</li>
<li>Make sure you check the "Disable fullscreen optimizations".</li>
<li>While you are there, you can also check "Run this program as an administrator".
Click Apply and Ok</li>
</ul>
<h3 id="heading-clear-your-launchers-webcache">Clear your launcher’s webcache</h3>
<ul>
<li>Exit the Epic Games Launcher by right-clicking the system tray icon in the bottom-right corner, and then clicking Exit.</li>
<li>Press Windows key + R, type “%localappdata%”, and then press Enter to open a File Explorer window.</li>
<li>Open the Epic Games Launcher folder.</li>
<li>Open the Saved folder.</li>
<li>Click the webcache folder, and then delete it.</li>
<li>If there is a folder called webcache_4147 that can be deleted as well</li>
<li>Restart your computer, and then relaunch the Epic Games Launcher.</li>
</ul>
<h3 id="heading-reinstall-the-epic-games-launcher">Reinstall the Epic Games Launcher</h3>
<blockquote>
<p>Note: The following process will remove all of your installed games.</p>
</blockquote>
<ul>
<li>Close the Epic Games launcher by right-clicking the system tray icon in the bottom right corner and then clicking Exit.</li>
<li>Click Start.</li>
<li>Type "cmd", right-click Command Prompt, and then click Run as administrator.
In the window that opens, type "sfc /scannow", and then press Enter. 
This may take a little while.</li>
<li>Restart your computer.</li>
<li>Click Start.</li>
<li>Type "Add or Remove Programs", and then press Enter.</li>
<li>Select Epic Games Launcher from the list of programs.</li>
<li>Click Uninstall.</li>
<li>Go to www.epicgames.com and click Get Epic Games in the top right corner to download the latest installer.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[HTTPS On Your Own Host Domain For Free]]></title><description><![CDATA[Nowaday, many platform open url with https in default for safe, if your website has no security certificate, it may be not  accessible.

To enable HTTPS on your website, you need to get a certificate (a type of file) from a Certificate Authority (CA)...]]></description><link>https://lizhiyu.me/https-on-your-own-host-domain-for-free</link><guid isPermaLink="true">https://lizhiyu.me/https-on-your-own-host-domain-for-free</guid><dc:creator><![CDATA[Lizhiyu]]></dc:creator><pubDate>Fri, 29 Apr 2022 08:31:26 GMT</pubDate><content:encoded><![CDATA[<p>Nowaday, many platform open url with https in default for safe, if your website has no security certificate, it may be not  accessible.</p>
<blockquote>
<p>To enable HTTPS on your website, you need to get a certificate (a type of file) from a Certificate Authority (CA). Let’s Encrypt is a CA. In order to get a certificate for your website’s domain from Let’s Encrypt, you have to demonstrate control over the domain.</p>
</blockquote>
<p>Follow the instructions with <a target="_blank" href="https://certbot.eff.org/instructions">certbot</a> to get the certificate from  let’s encrypt</p>
<pre><code>sudo http<span class="hljs-operator">-</span>server <span class="hljs-operator">-</span>a <span class="hljs-number">0</span><span class="hljs-number">.0</span><span class="hljs-number">.0</span><span class="hljs-number">.0</span> <span class="hljs-operator">-</span>p <span class="hljs-number">443</span> <span class="hljs-operator">-</span>S <span class="hljs-operator">-</span>K <span class="hljs-operator">/</span>etc<span class="hljs-operator">/</span>letsencrypt<span class="hljs-operator">/</span>live<span class="hljs-operator">/</span>www.dogdogame.com/privkey.pem <span class="hljs-operator">-</span>C <span class="hljs-operator">/</span>etc<span class="hljs-operator">/</span>letsencrypt<span class="hljs-operator">/</span>live<span class="hljs-operator">/</span>www.dogdogame.com/fullchain.pem
</code></pre>]]></content:encoded></item><item><title><![CDATA[Just Do it, Recap On These Blog Days From 3 Months Ago & 写就对了, 回顾这三个月以来的博客时间🔖]]></title><description><![CDATA[自今年2月1号水了一篇自我介绍开始，还差几天就整整三个月了。
起因
全职工作上的项目因为版号、人员的问题处于一个功能完整等待上线的阶段，所以这段时间有更多的时间写一些自己感兴趣的东西，把之前想实现的程序串起来跑一下。
我是从小的时候开始就喜欢博客这个东西，小学初中就爱在QQ空间发一些有的没的现在看起来有点不知所云的文章。再后来微博之类的短文发布流行起来，qq空间慢慢变为小学生空间，微信开始完熟人圈子社交，我就基本上处于一个潜水的状态。发现Hashnode这个平台纯属意外，我甚至都忘了在哪里看到的...]]></description><link>https://lizhiyu.me/just-do-it-recap-on-these-blog-days-from-3-months-ago-and</link><guid isPermaLink="true">https://lizhiyu.me/just-do-it-recap-on-these-blog-days-from-3-months-ago-and</guid><dc:creator><![CDATA[Lizhiyu]]></dc:creator><pubDate>Tue, 26 Apr 2022 17:15:14 GMT</pubDate><content:encoded><![CDATA[<p>自今年2月1号水了一篇自我介绍开始，还差几天就整整三个月了。</p>
<h3 id="heading-6lw35zug">起因</h3>
<p>全职工作上的项目因为版号、人员的问题处于一个功能完整等待上线的阶段，所以这段时间有更多的时间写一些自己感兴趣的东西，把之前想实现的程序串起来跑一下。</p>
<p>我是从小的时候开始就喜欢<strong>博客</strong>这个东西，小学初中就爱在QQ空间发一些有的没的现在看起来有点不知所云的文章。再后来微博之类的短文发布流行起来，qq空间慢慢变为小学生空间，微信开始完熟人圈子社交，我就基本上处于一个潜水的状态。发现Hashnode这个平台纯属意外，我甚至都忘了在哪里看到的，在这里你可以把博客绑定到自己的域名，可以把文章以Markdown的格式自动备份到Github，仅此两点就已深得我心，直接抛弃自建博客网站的异念。</p>
<p>前前后后总共写了20篇，其中有一个”如何做一个多人联机卡牌游戏“系列还在持续更新中，大多数文章都是在代码实现的基础上完成的，文字内容很少，相当于是项目实现历程的文字记录。</p>
<hr />
<blockquote>
<p>上面废话了起因，下面来说说经过（承上启下）。</p>
</blockquote>
<h3 id="heading-andampandamp">经过&amp;体会&amp;感悟</h3>
<p>介于自己以前的浅薄人生经历，有几件事情是写了博客才有幸深刻体会</p>
<ul>
<li>一件事情或者一个技能，知道、会用 与能够清晰地表达出来可以说是两回事。这中间存在一个知识的内化过程，以及发现提出问题-&gt;剖析问题-&gt;分解问题-&gt;解决问题的流程认知。</li>
<li>持续地有规律的坚持一件事情，在还没形成习惯或一定程度的正向反馈的时候，是反人性的，我们都会以各种借口在各个阶段选择放弃。一般会经历前期的兴奋，中期的倦怠，然后就没有然后了，所谓“从入门到放弃”就是很好的诠释。</li>
<li>内容的输出是二次输入，输出会倒逼输入。</li>
<li>写也是想法产出的一部分，下笔或打字的时候是真的会促进思考的。</li>
<li>随时记下灵感，然后组合成一个比较完整的内容。</li>
<li>多看，多听。看博客看新闻看社交媒体看身边的人都在手机上干什么，听播客听视频听周围的人都在说些什么，能够发现很多意想不到的收获，比如对一个产品的需求。</li>
<li>运动会产生多巴胺、血清素、内啡肽，能够提高大脑的活跃度。</li>
<li>有意识的拓展认知的边界，通过网课、书籍、讲座了解一下跟自己所处完全不同行业，完全不同的职能的东西。通过这个动作，有时候会感受到一些好的东西，好的人的共性，要是能把握住这个共性，就很了不起了。</li>
<li>不要所有的东西都泛泛而学，然后感觉自己什么都懂一丢丢，谭警官问你能不能懂的时候，你只会说，“只能懂一点点”，始终上不了成华大道。所以必须要有自己专注的领域，因为社会分工的大环境下，更需要专才，当然一专多能是更好的，但前提就是要有砖才行。在学习的前期，富有余力的情况下，可以考虑先广度后深度的模式。</li>
<li>千万不要做个苦行僧。很多时候我们被一句话、一首歌、一本书、一个人点燃了，然后就想干票大的，自己要怎么怎么头悬梁锥刺股，九九八十一天之后让别人刮目相看。我们要尊重自然，尊重人是自然界的一个生物，不是超脱于三界之外的游神，按照生物喜欢的方式对待自己，认识自己，循循善诱，比如在准备搞事情之前来一杯甜甜的拿铁。</li>
<li>肝不动了就休息，人在睡觉的时候大脑还没睡，大脑还在自主运转，对缓存区的进行整理，对垃圾信息进行回收，休息好了往往事半功倍。</li>
<li>做能让自己兴奋的事情，比如我在写代码实现自己想实现的东西的时候就非常兴奋，看见别人的好项目的时候也是如此，分享自己的拙见的时候也会很明显的感觉到快乐，并且这种兴奋会正向反馈给你，促使你下一次继续做这个事情。这可能就是兴趣的定义吧。我看见好多博主，有一个共同的现象，集中在一段时间内发布了好多内容，后面更新的频率就很慢了，然后就消失了，跟我以前报健身房的经历一模一样。排除一些不可抗力因素，这当中应该是少了一点兴趣成分，要不然怎么爱因斯塔会说“兴趣是最好的老师”呢。</li>
<li>避免掉入规整主义，完美主义的陷阱。感觉自己要开始搞一件大事了，要部署各种软硬件环境，被别人安利各种生产力工具，配上牛逼的电脑，椅子，桌子，键盘，鼠标，耳机，游戏机（逃...）。然后自己都忘了自己要干嘛了。先做起来再说，要把握核心，比如目标是向前走1000米，迈开步走就行了，什么跑鞋，跑袜，运动套装，智能设备，都可以在走的过程中按需添置。学会感受混沌之美，太极之美，中庸之美。鲁迅曾经说过：Just Do It.就是这个道理。</li>
<li>要设定目标，并设定一个需要用劲儿才能实现的目标。唐太宗曾经说过“取法于上,仅得为中,取法于中,故为其下”，意思是你定个小目标相当于啥都得不到。</li>
<li>遵循一个简单的原则，老子曾说，一生二，二生三，三生万物。牛逼的东西往往是简单的，考虑东西不要搞得过于复杂。</li>
<li>人们大都喜闻乐见少年有成，这和喜欢中500万福利彩票一样。种一棵树最好的时间有两个，一个是10年前，一个是现在。认准了，喜欢就去做，什么时候都不晚。日拱一卒，功不唐捐。</li>
<li>成年人，学会放低自己的身段，减少自作多情，大多数时候，一个人取得的成就很大程度上是时代造就的，不要太把自己当回事，顺势而为，实干兴邦。</li>
<li>保持自信，甚至骗自己自信。</li>
<li>坚持主见，批判接受别人的意见建议，辩证看待别人的得失。举个不恰当的例子，路人阿姨看见你撸铁的时候累的上气不接下气，说你这个太辛苦了，但她无法体会你在撸完后有多爽；你看见妹纸在开心地吃冰淇淋，但是你感受不到1个月后她节食天天吃全麦面包的无味。</li>
<li>认识事物的本质，并保持初心。比如写个文章，虽然写得丑且简单，但目的始终是内容的输出,如果加上一些浏览量，点赞量，评论量之类的可以量化的数据上来的时候，往往会让人模糊认知，忘掉初心,仿佛不是在自己写,而是在写给别人看,然后就开始想如何迎合观众。</li>
</ul>
<blockquote>
<p>一口气写了一大堆字，好像有点走偏了，那就尬结一下</p>
</blockquote>
<h3 id="heading-57ut5p6c">结果</h3>
<p>这三个月，相对来说过得很充实，对我产生了一些影响，比如像上面一样疯狂输出垃圾话的能力，这个过程中不仅仅是写博客本身，更多的是写博客带给我的思考，促发我去探索边界，归纳总结，产生新知。</p>
<p>废话不多说，只要能产生自己与自己，或自己与他人之间有意义有深度的东西，就会去写，因为很爽。</p>
]]></content:encoded></item><item><title><![CDATA[Cocos Creator Vs React Vs React-Three-Fiber 使用体验对比，新项目该如何从中选择]]></title><description><![CDATA[首先明确一点，下面针对标题中提到的三个东西的对比主要基于实现类似的功能，在开发体验上的偏感性思考，他们本身从根本上说设计定位就差异很大，甚至是包含扩展关系。
对比

Cocos Creator 是一个比较完整的游戏引擎，有十分方便的可见即可得的可视化编辑器，和大量的开箱即用的可选的功能模块。

React 是一个JS库，生态极其丰富。

React-Three-Fiber (下面称R3F)是Three.js的React 渲染器。


用以上三个工具开发了老少皆宜，人尽皆知的斗地主之后，我逐渐喜欢...]]></description><link>https://lizhiyu.me/cocos-creator-vs-react-vs-react-three-fiber</link><guid isPermaLink="true">https://lizhiyu.me/cocos-creator-vs-react-vs-react-three-fiber</guid><dc:creator><![CDATA[Lizhiyu]]></dc:creator><pubDate>Mon, 25 Apr 2022 17:01:11 GMT</pubDate><content:encoded><![CDATA[<p>首先明确一点，下面针对标题中提到的三个东西的对比主要基于实现类似的功能，在开发体验上的偏感性思考，他们本身从根本上说设计定位就差异很大，甚至是包含扩展关系。</p>
<h3 id="heading-5a55qu">对比</h3>
<ul>
<li><p>Cocos Creator 是一个比较完整的游戏引擎，有十分方便的可见即可得的可视化编辑器，和大量的开箱即用的可选的功能模块。</p>
</li>
<li><p>React 是一个JS库，生态极其丰富。</p>
</li>
<li><p>React-Three-Fiber (下面称R3F)是Three.js的React 渲染器。</p>
</li>
</ul>
<p>用以上三个工具开发了老少皆宜，人尽皆知的斗地主之后，我逐渐喜欢上了React和R3F，主要有一下几个原因：</p>
<ol>
<li>代码简洁，近乎极致的函数式编程，很多东西都是我之前自己想要去重构实现的，不谋而合的感觉很炸</li>
<li>开发流程纯粹，VsCode一把梭，借助于HMR，代码改动后立马刷新界面，整个开发过程几乎没有界面的切换</li>
<li>控制粒度更细，不管是界面视图界面的布局，还是样式，资源都可以很方便地用代码直接操控，而不用依赖任何其他工具</li>
<li>坑基本被踩的差不多了，遇到的问题只要Google一下，解决方案如排山倒海之势，迎面而来</li>
<li>生态繁荣，相关的各种类型的项目很多，最近就看到了好多基于Three.js实现的游戏，而且活跃度很高</li>
<li>R3F也是在React上盖了薄如蝉翼的一层，主要就是把Three.js的一些类封装为react的组件，用jsx表现出来，这也使Three.js的代码变得非常简洁易读，整体上二者的结合带来了易用性又不失灵活度</li>
</ol>
<p>可以说上面说的几条主要都是与自己更加熟悉的Cocos Creator以及之前使用的一些游戏引擎开发体验的对比。</p>
<h3 id="heading-57ut6k66">结论</h3>
<ul>
<li>需要快速开发，适配多平台的项目，现阶段毫不犹豫，Cocos Creator，毕竟自家编辑器加持，开发、调试、发布一条龙服务，谁不爱呢</li>
<li>做感兴趣的项目，实践学到的一些新东西，我会选react和R3F，之后我也会继续基于R3F开发一些小游戏，边学边练</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[How To Set Net Proxy on Nintendo Switch & Switch 开启代理加速设置教程]]></title><description><![CDATA[众所周知，在中国买Switch一般都是买非国行版本（毕竟一个是小卖部，一个是超市，没得选😂）。
然而不管是日版还是港版都会遇到一个非常棘手的问题，下载速度特别特别慢，慢到下载一个大作需要长达一天的时间，并且在下载的过程中还随时可能出现网络错误进而中断，导致前功尽弃，甚至怒砸游戏机，为了我们可爱的第九艺术的载体四肢健全，情绪稳定，下面我们就来介绍一下解决方案。
目前解决方案主要有三种：

第一种方案：买实体卡，不用下载直接起飞开肝（你tm不是在说废话吗？逃...）
第二种方案：购买加速服务，一般...]]></description><link>https://lizhiyu.me/how-to-set-net-proxy-on-nintendo-switch-and-switch</link><guid isPermaLink="true">https://lizhiyu.me/how-to-set-net-proxy-on-nintendo-switch-and-switch</guid><dc:creator><![CDATA[Lizhiyu]]></dc:creator><pubDate>Mon, 25 Apr 2022 15:09:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1650899618461/MmCeS0Y5x.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>众所周知，在中国买Switch一般都是买非国行版本（毕竟一个是小卖部，一个是超市，没得选😂）。</p>
<p>然而不管是日版还是港版都会遇到一个非常棘手的问题，下载速度特别特别慢，慢到下载一个大作需要长达一天的时间，并且在下载的过程中还随时可能出现网络错误进而中断，导致前功尽弃，甚至怒砸游戏机，为了我们可爱的第九艺术的载体四肢健全，情绪稳定，下面我们就来介绍一下解决方案。</p>
<p>目前解决方案主要有三种：</p>
<ul>
<li>第一种方案：买实体卡，不用下载直接起飞开肝（你tm不是在说废话吗？逃...）</li>
<li>第二种方案：购买加速服务，一般有包月 和 包流量两种套餐（因为下载游戏一般都是临时性的，包月服务不是很适合，流量包的性价比也不是很高，所以我要隆重介绍第三种方案）</li>
<li>第三种方案：铛铛铛（尬🤪），接下来就是我们要隆重介绍的内容了（前面的都是废话），不用买实体卡，不用另外买套餐，只要科学上网，就可以免费加速。</li>
</ul>
<p>先上对比图：</p>
<p>加速前：</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650898882578/FT3OClfMx.JPG" alt="IMG_3909.JPG" />
加速后：
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650899126802/dRAp0N5fn.JPG" alt="IMG_3911.JPG" /></p>
<p>可以看到，下载速度提升了20几倍，直接起飞🛫️</p>
<h3 id="heading-54mp5paz5yeg5ash77ya">物料准备：</h3>
<ul>
<li>Switch</li>
<li>笔记本电脑</li>
<li>科学上网装置（这里使用的是ClashX <a target="_blank" href="https://github.com/Fndroid/clash_for_windows_pkg">Windows 下载地址</a>，<a target="_blank" href="https://github.com/yichengchen/clashX/releases">Mac 下载地址</a>)</li>
<li>科学上网服务商（自己有在用的梯子跳过此步骤，没有的话可以试试我用的这个，叫AgentNeo，比较小众，我已经用了好多年了，可以说是不管遇到什么大事小情，始终稳如老狗 <a target="_blank" href="https://agneo.co/?rc=9gmtay3q">AgentNeo注册地址</a>）</li>
</ul>
<h3 id="heading-5lil6z2i5lul57un5q2l6aqk77ya">下面介绍步骤：</h3>
<ul>
<li><p>找到笔记本电脑ip和科学上网代理端口备用</p>
<p> 端口：可以在ClashX里查找，如下图所示，默认一般为7890
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650898449982/zRxRr7oQm.png" alt="Screen Shot 2022-04-25 at 22.50.08.png" />
ip：Windows在cmd内输入ipconfig，Mac&amp;Linux在终端输入ifconfig查看</p>
</li>
<li><p>确认笔记本电脑和Switch连入同一个Wifi</p>
</li>
<li>打开Switch-设置-互联网-互联网设置-当前连接的网络-更改设置-代理服务器设置-打开-输入上一步找到的ip和端口-保存-连接到此网络</li>
</ul>
<blockquote>
<p>如果你的移动设备上装有Shadowrocket，则可以在 <code>设置-代理-代理共享</code> 里找到代理ip和端口，然后通过你的手机或平板进行代理加速</p>
</blockquote>
<p>然后就愉快地玩耍吧</p>
]]></content:encoded></item><item><title><![CDATA[Make a multiplayer card game - Episode 7 | Create 3D graphical interface with Three.js]]></title><description><![CDATA[This section mainly introduces the use ofreact-three-fiber(referred to as R3F bellow) to realize the construction of interactive scenes.
Why Choose R3F

R3F just expresses Three.js in JSX, no extra overhead
Build scenes in a declarative way with reac...]]></description><link>https://lizhiyu.me/make-a-multiplayer-card-game-episode-7-or-create-3d-graphical-interface-with-threejs</link><guid isPermaLink="true">https://lizhiyu.me/make-a-multiplayer-card-game-episode-7-or-create-3d-graphical-interface-with-threejs</guid><category><![CDATA[ThreeJS]]></category><category><![CDATA[React]]></category><category><![CDATA[Game Development]]></category><dc:creator><![CDATA[Lizhiyu]]></dc:creator><pubDate>Sat, 23 Apr 2022 15:52:57 GMT</pubDate><content:encoded><![CDATA[<p>This section mainly introduces the use of<a target="_blank" href="https://docs.pmnd.rs/react-three-fiber/getting-started/introduction">react-three-fiber</a>(referred to as R3F bellow) to realize the construction of interactive scenes.</p>
<h3 id="heading-why-choose-r3f">Why Choose R3F</h3>
<ul>
<li>R3F just expresses Three.js in JSX, no extra overhead</li>
<li>Build scenes in a declarative way with react, including but not limited to components that can easily react to state, are easy to interact with, and can leverage React's ecosystem</li>
</ul>
<h3 id="heading-scenario-construction-implementation">Scenario construction implementation</h3>
<ul>
<li><strong>Card</strong></li>
</ul>
<p>Note that the texture required for rendering is obtained by passing the image address to <code>useTexture</code></p>
<pre><code class="lang-typescript"><span class="hljs-comment">//Card.tsx</span>
<span class="hljs-keyword">import</span> { useTexture } <span class="hljs-keyword">from</span> <span class="hljs-string">"@react-three/drei"</span>
<span class="hljs-keyword">import</span> { Mesh } <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Card</span>(<span class="hljs-params">props: { faceTextureUrl: <span class="hljs-built_in">string</span>, idx: <span class="hljs-built_in">number</span>, beginX: <span class="hljs-built_in">number</span>, serial: <span class="hljs-built_in">number</span> }</span>) </span>{
  <span class="hljs-keyword">let</span> _selectedOffsetY = <span class="hljs-number">.2</span>;
  <span class="hljs-keyword">let</span> _beginX = props.beginX;
  <span class="hljs-keyword">const</span> _texture = useTexture({
    map: props.faceTextureUrl
  })
  <span class="hljs-keyword">return</span> (
    &lt;mesh
      position={[_beginX + props.idx * <span class="hljs-number">.5</span>, <span class="hljs-number">0</span>, props.idx*<span class="hljs-number">0.001</span>]}
      onClick={<span class="hljs-function">(<span class="hljs-params">e: <span class="hljs-built_in">any</span></span>) =&gt;</span> {
        e.stopPropagation();
        <span class="hljs-keyword">let</span> _targetMesh = e[<span class="hljs-string">"eventObject"</span>] <span class="hljs-keyword">as</span> Mesh;
        <span class="hljs-keyword">let</span> _pos = _targetMesh.position;
        <span class="hljs-keyword">let</span> _upY;
        <span class="hljs-keyword">if</span> (_pos.y == <span class="hljs-number">0</span>) _upY = _selectedOffsetY;
        <span class="hljs-keyword">else</span> _upY = <span class="hljs-number">0</span>;
        _targetMesh.position.set(_pos.x, _upY, _pos.z);
      }}
      userData={{<span class="hljs-string">"_d_cardSerial"</span>:props.serial}}
    &gt;
      &lt;boxGeometry args={[<span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">.001</span>]} /&gt;
      &lt;meshStandardMaterial {..._texture} /&gt;
    &lt;/mesh&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Card;
</code></pre>
<ul>
<li><strong>Game Scene</strong></li>
</ul>
<p>First you must declare a Canvas node, because all three.js must be under the Canvas node</p>
<pre><code class="lang-typescript">&lt;Canvas orthographic camera={{ zoom: <span class="hljs-number">50</span>, position: [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">100</span>], rotation: [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>] }}&gt;
          &lt;R3fScene mainHandCards={mainHandCards} outCards={outCards} gameModel={_gameModel}&gt;&lt;/R3fScene&gt;
        &lt;/Canvas&gt;
</code></pre>
<p>In order to facilitate the use of hooks provided by <code>@react-three/fiber</code> (three.js related hooks can only be used under the Canvas node), the game scene node is proposed separately</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> R3fScene = <span class="hljs-function">(<span class="hljs-params">props: { mainHandCards: <span class="hljs-built_in">number</span>[], gameModel, outCards: <span class="hljs-built_in">number</span>[][] }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> {
    scene,
    camera,
  } = useThree();
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(scene.getObjectByName(<span class="hljs-string">"handList"</span>))
  })
  <span class="hljs-keyword">let</span> mainHandCards = props.mainHandCards;
  <span class="hljs-keyword">let</span> _gameModel = props.gameModel;
  _gameModel.context = scene;
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getFaceTextureUrl</span>(<span class="hljs-params">serial</span>): <span class="hljs-title">string</span> </span>{
    <span class="hljs-keyword">let</span> _prefix = <span class="hljs-string">"/faces/"</span>
    <span class="hljs-keyword">let</span> _readableName = _gameModel.getCardReadableName(serial);
    <span class="hljs-keyword">if</span> (_readableName === <span class="hljs-string">"rJkr"</span>) <span class="hljs-keyword">return</span> _prefix + <span class="hljs-string">"Poker_Joker_B.png"</span>
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (_readableName === <span class="hljs-string">"bJkr"</span>) <span class="hljs-keyword">return</span> _prefix + <span class="hljs-string">"Poker_Joker_R.png"</span>
    <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">let</span> _suitDic = { <span class="hljs-number">0</span>: <span class="hljs-string">"D"</span>, <span class="hljs-number">1</span>: <span class="hljs-string">"C"</span>, <span class="hljs-number">2</span>: <span class="hljs-string">"H"</span>, <span class="hljs-number">3</span>: <span class="hljs-string">"S"</span> };
      <span class="hljs-keyword">let</span> _suitNumber: <span class="hljs-built_in">number</span> = serial &gt;&gt; <span class="hljs-number">4</span>;
      <span class="hljs-keyword">return</span> _prefix + <span class="hljs-string">"Poker_"</span> + _suitDic[_suitNumber] + _readableName + <span class="hljs-string">".png"</span>;
    }
  }

  <span class="hljs-keyword">let</span> _cardCount = mainHandCards.length;
  <span class="hljs-keyword">let</span> _beginX = -((_cardCount<span class="hljs-number">-1</span>) * <span class="hljs-number">.5</span>+<span class="hljs-number">1.5</span>)/<span class="hljs-number">2</span> ;
  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;CameraControls /&gt;
      &lt;ambientLight intensity={<span class="hljs-number">0.1</span>} /&gt;
      &lt;directionalLight color=<span class="hljs-string">"white"</span> position={[<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">5</span>]} /&gt;

      &lt;group name=<span class="hljs-string">"handList"</span>&gt;
        {mainHandCards.map(<span class="hljs-function">(<span class="hljs-params">serial: <span class="hljs-built_in">number</span>, idx</span>) =&gt;</span> {
          <span class="hljs-keyword">return</span> (&lt;Card key={<span class="hljs-string">"k"</span> + serial} faceTextureUrl={getFaceTextureUrl(serial)} idx={idx} beginX={_beginX} serial={serial} /&gt;)
        })}
      &lt;/group&gt;
      &lt;group position={[<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">0</span>]} scale={<span class="hljs-number">.6</span>} name=<span class="hljs-string">"out-list-1"</span>&gt;
        {props.outCards[<span class="hljs-number">1</span>].map(<span class="hljs-function">(<span class="hljs-params">serial: <span class="hljs-built_in">number</span>, idx</span>) =&gt;</span> {
          <span class="hljs-keyword">return</span> (&lt;Card key={<span class="hljs-string">"k"</span> + serial} faceTextureUrl={getFaceTextureUrl(serial)} idx={idx} beginX={_beginX} serial={serial} /&gt;)
        })}
      &lt;/group&gt;
      &lt;group position={[<span class="hljs-number">-2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">0</span>]} scale={<span class="hljs-number">.6</span>} name=<span class="hljs-string">"out-list-2"</span>&gt;
        {props.outCards[<span class="hljs-number">2</span>].map(<span class="hljs-function">(<span class="hljs-params">serial: <span class="hljs-built_in">number</span>, idx</span>) =&gt;</span> {
          <span class="hljs-keyword">return</span> (&lt;Card key={<span class="hljs-string">"k"</span> + serial} faceTextureUrl={getFaceTextureUrl(serial)} idx={idx} beginX={_beginX} serial={serial} /&gt;)
        })}
      &lt;/group&gt;
      &lt;group position={[<span class="hljs-number">0</span>, <span class="hljs-number">2</span>, <span class="hljs-number">0</span>]} scale={<span class="hljs-number">.6</span>} name=<span class="hljs-string">"out-list-0"</span>&gt;
        {props.outCards[<span class="hljs-number">0</span>].map(<span class="hljs-function">(<span class="hljs-params">serial: <span class="hljs-built_in">number</span>, idx</span>) =&gt;</span> {
          <span class="hljs-keyword">return</span> (&lt;Card key={<span class="hljs-string">"k"</span> + serial} faceTextureUrl={getFaceTextureUrl(serial)} idx={idx} beginX={_beginX} serial={serial} /&gt;)
        })}
      &lt;/group&gt;
    &lt;/&gt;
  )
}
</code></pre>
<p>In order to reflect the 3D interface, add an orbiting camera (swipe the scene to adjust the camera corner)</p>
<pre><code class="lang-typescript">extend({ OrbitControls });

<span class="hljs-keyword">const</span> CameraControls = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> {
    camera,
    gl: { domElement }
  } = useThree();
  camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">10</span>);
  <span class="hljs-keyword">const</span> controls = useRef();
  useFrame(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> {
    (controls.current <span class="hljs-keyword">as</span> unknown <span class="hljs-keyword">as</span> OrbitControls).update()
  });
  <span class="hljs-keyword">return</span> (
    <span class="hljs-comment">// @ts-ignore</span>
    &lt;orbitControls
      ref={controls}
      args={[camera, domElement]}
      enableZoom={<span class="hljs-literal">false</span>}
      maxAzimuthAngle={<span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">4</span>}
      maxPolarAngle={<span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">4</span>}
      minAzimuthAngle={-<span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">4</span>}
      minPolarAngle={<span class="hljs-number">0</span>}
      rotationSpeed={<span class="hljs-number">0.01</span>}
    /&gt;
  );
};
</code></pre>
<p>Scene complete code：</p>
<pre><code class="lang-typescript"><span class="hljs-comment">//App.tsx</span>
<span class="hljs-keyword">import</span> { useEffect, useRef, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> {
  Canvas,
  useFrame,
  extend,
  useThree,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@react-three/fiber"</span>;
<span class="hljs-keyword">import</span> { OrbitControls } <span class="hljs-keyword">from</span> <span class="hljs-string">"three/examples/jsm/controls/OrbitControls"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>
<span class="hljs-keyword">import</span> Card <span class="hljs-keyword">from</span> <span class="hljs-string">'./component/Card'</span>;
<span class="hljs-keyword">import</span> GameModel <span class="hljs-keyword">from</span> <span class="hljs-string">"./base/src/game/model/GameModel"</span>;
<span class="hljs-keyword">import</span> GameSceneMediator <span class="hljs-keyword">from</span> <span class="hljs-string">"./base/src/game/view/GameSceneMediator"</span>;
<span class="hljs-keyword">import</span> GameSceneView <span class="hljs-keyword">from</span> <span class="hljs-string">"./component/GameSceneView"</span>;

extend({ OrbitControls });

<span class="hljs-keyword">const</span> CameraControls = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> {
    camera,
    gl: { domElement }
  } = useThree();
  camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">10</span>);
  <span class="hljs-keyword">const</span> controls = useRef();
  useFrame(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> {
    (controls.current <span class="hljs-keyword">as</span> unknown <span class="hljs-keyword">as</span> OrbitControls).update()
  });
  <span class="hljs-keyword">return</span> (
    <span class="hljs-comment">// @ts-ignore</span>
    &lt;orbitControls
      ref={controls}
      args={[camera, domElement]}
      enableZoom={<span class="hljs-literal">false</span>}
      maxAzimuthAngle={<span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">4</span>}
      maxPolarAngle={<span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">4</span>}
      minAzimuthAngle={-<span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">4</span>}
      minPolarAngle={<span class="hljs-number">0</span>}
      rotationSpeed={<span class="hljs-number">0.01</span>}
    /&gt;
  );
};

<span class="hljs-keyword">const</span> R3fScene = <span class="hljs-function">(<span class="hljs-params">props: { mainHandCards: <span class="hljs-built_in">number</span>[], gameModel, outCards: <span class="hljs-built_in">number</span>[][] }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> {
    scene,
    camera,
  } = useThree();
  <span class="hljs-keyword">let</span> mainHandCards = props.mainHandCards;
  <span class="hljs-keyword">let</span> _gameModel = props.gameModel;
  _gameModel.context = scene;
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getFaceTextureUrl</span>(<span class="hljs-params">serial</span>): <span class="hljs-title">string</span> </span>{
    <span class="hljs-keyword">let</span> _prefix = <span class="hljs-string">"/faces/"</span>
    <span class="hljs-keyword">let</span> _readableName = _gameModel.getCardReadableName(serial);
    <span class="hljs-keyword">if</span> (_readableName === <span class="hljs-string">"rJkr"</span>) <span class="hljs-keyword">return</span> _prefix + <span class="hljs-string">"Poker_Joker_B.png"</span>
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (_readableName === <span class="hljs-string">"bJkr"</span>) <span class="hljs-keyword">return</span> _prefix + <span class="hljs-string">"Poker_Joker_R.png"</span>
    <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">let</span> _suitDic = { <span class="hljs-number">0</span>: <span class="hljs-string">"D"</span>, <span class="hljs-number">1</span>: <span class="hljs-string">"C"</span>, <span class="hljs-number">2</span>: <span class="hljs-string">"H"</span>, <span class="hljs-number">3</span>: <span class="hljs-string">"S"</span> };
      <span class="hljs-keyword">let</span> _suitNumber: <span class="hljs-built_in">number</span> = serial &gt;&gt; <span class="hljs-number">4</span>;
      <span class="hljs-keyword">return</span> _prefix + <span class="hljs-string">"Poker_"</span> + _suitDic[_suitNumber] + _readableName + <span class="hljs-string">".png"</span>;
    }
  }

  <span class="hljs-keyword">let</span> _cardCount = mainHandCards.length;
  <span class="hljs-keyword">let</span> _beginX = -((_cardCount<span class="hljs-number">-1</span>) * <span class="hljs-number">.5</span>+<span class="hljs-number">1.5</span>)/<span class="hljs-number">2</span> ;
  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;CameraControls /&gt;
      &lt;ambientLight intensity={<span class="hljs-number">0.1</span>} /&gt;
      &lt;directionalLight color=<span class="hljs-string">"white"</span> position={[<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">5</span>]} /&gt;

      &lt;group name=<span class="hljs-string">"handList"</span>&gt;
        {mainHandCards.map(<span class="hljs-function">(<span class="hljs-params">serial: <span class="hljs-built_in">number</span>, idx</span>) =&gt;</span> {
          <span class="hljs-keyword">return</span> (&lt;Card key={<span class="hljs-string">"k"</span> + serial} faceTextureUrl={getFaceTextureUrl(serial)} idx={idx} beginX={_beginX} serial={serial} /&gt;)
        })}
      &lt;/group&gt;
      &lt;group position={[<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">0</span>]} scale={<span class="hljs-number">.6</span>} name=<span class="hljs-string">"out-list-1"</span>&gt;
        {props.outCards[<span class="hljs-number">1</span>].map(<span class="hljs-function">(<span class="hljs-params">serial: <span class="hljs-built_in">number</span>, idx</span>) =&gt;</span> {
          <span class="hljs-keyword">return</span> (&lt;Card key={<span class="hljs-string">"k"</span> + serial} faceTextureUrl={getFaceTextureUrl(serial)} idx={idx} beginX={_beginX} serial={serial} /&gt;)
        })}
      &lt;/group&gt;
      &lt;group position={[<span class="hljs-number">-2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">0</span>]} scale={<span class="hljs-number">.6</span>} name=<span class="hljs-string">"out-list-2"</span>&gt;
        {props.outCards[<span class="hljs-number">2</span>].map(<span class="hljs-function">(<span class="hljs-params">serial: <span class="hljs-built_in">number</span>, idx</span>) =&gt;</span> {
          <span class="hljs-keyword">return</span> (&lt;Card key={<span class="hljs-string">"k"</span> + serial} faceTextureUrl={getFaceTextureUrl(serial)} idx={idx} beginX={_beginX} serial={serial} /&gt;)
        })}
      &lt;/group&gt;
      &lt;group position={[<span class="hljs-number">0</span>, <span class="hljs-number">2</span>, <span class="hljs-number">0</span>]} scale={<span class="hljs-number">.6</span>} name=<span class="hljs-string">"out-list-0"</span>&gt;
        {props.outCards[<span class="hljs-number">0</span>].map(<span class="hljs-function">(<span class="hljs-params">serial: <span class="hljs-built_in">number</span>, idx</span>) =&gt;</span> {
          <span class="hljs-keyword">return</span> (&lt;Card key={<span class="hljs-string">"k"</span> + serial} faceTextureUrl={getFaceTextureUrl(serial)} idx={idx} beginX={_beginX} serial={serial} /&gt;)
        })}
      &lt;/group&gt;
    &lt;/&gt;
  )
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params">props: <span class="hljs-built_in">any</span></span>) </span>{
  <span class="hljs-keyword">let</span> _gameFacade = props.gameFacade;
  <span class="hljs-keyword">let</span> _gameModel: GameModel = _gameFacade.retrieveProxy(<span class="hljs-string">"GameModel"</span>);
  <span class="hljs-keyword">let</span> [mainHandCards, setMainHandCards] = useState(_gameModel.cardsArr);
  <span class="hljs-keyword">let</span> [outCards, setOutCards] = useState(_gameModel.outCards);
  _gameModel.setMainHandCardsHook(setMainHandCards);
  _gameModel.setOutCardsHook(setOutCards);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (_gameFacade.retrieveMediator(<span class="hljs-string">"GameSceneMediator"</span>) == <span class="hljs-literal">null</span>) {
      _gameFacade.registerMediator(<span class="hljs-keyword">new</span> GameSceneMediator(<span class="hljs-literal">null</span>, <span class="hljs-keyword">new</span> GameSceneView()));
    }
  });

  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;div id=<span class="hljs-string">'canvas-container'</span>&gt;
        &lt;Canvas orthographic camera={{ zoom: <span class="hljs-number">50</span>, position: [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">100</span>], rotation: [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>] }}&gt;
          &lt;R3fScene mainHandCards={mainHandCards} outCards={outCards} gameModel={_gameModel}&gt;&lt;/R3fScene&gt;
        &lt;/Canvas&gt;
      &lt;/div&gt;

      &lt;div id=<span class="hljs-string">"status"</span> style={{ display: <span class="hljs-string">'fix'</span>, textAlign: <span class="hljs-string">'center'</span>, fontSize: <span class="hljs-string">"2em"</span>, userSelect: <span class="hljs-string">"none"</span> }}&gt;hello&lt;/div&gt;
      &lt;div id=<span class="hljs-string">'controlPanel-scores'</span> className=<span class="hljs-string">'controlPanel'</span>&gt;
        &lt;button id=<span class="hljs-string">'controlPanel-scores-1'</span> className=<span class="hljs-string">'controlButton'</span>&gt;<span class="hljs-number">1</span>&lt;/button&gt;
        &lt;button id=<span class="hljs-string">'controlPanel-scores-2'</span> className=<span class="hljs-string">'controlButton'</span>&gt;<span class="hljs-number">2</span>&lt;/button&gt;
        &lt;button id=<span class="hljs-string">'controlPanel-scores-3'</span> className=<span class="hljs-string">'controlButton'</span>&gt;<span class="hljs-number">3</span>&lt;/button&gt;
      &lt;/div&gt;

      &lt;div id=<span class="hljs-string">'controlPanel-operation'</span> className=<span class="hljs-string">'controlPanel'</span>&gt;
        &lt;button id=<span class="hljs-string">'controlPanel-operation-pass'</span> className=<span class="hljs-string">'controlButton'</span>&gt;pass&lt;/button&gt;
        &lt;button id=<span class="hljs-string">'controlPanel-operation-play'</span> className=<span class="hljs-string">'controlButton'</span>&gt;play&lt;/button&gt;
      &lt;/div&gt;
    &lt;/&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<h3 id="heading-framework-adaptation-changes">Framework adaptation changes</h3>
<ul>
<li><strong>Getting scene node</strong></li>
</ul>
<pre><code class="lang-typescript">getViewComponent(name: <span class="hljs-built_in">string</span>,isDOM:<span class="hljs-built_in">boolean</span> = <span class="hljs-literal">true</span>,canvasScene:<span class="hljs-built_in">any</span> = <span class="hljs-literal">null</span>) {
        <span class="hljs-keyword">if</span>(isDOM) <span class="hljs-keyword">return</span> <span class="hljs-built_in">document</span>.getElementById(name);
        <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">if</span>(canvasScene) <span class="hljs-keyword">return</span> canvasScene.getObjectByName(name);
        }
    }
</code></pre>
<p>By passing in the scene object of three.js, call the getObjectByName interface to obtain the node with the pre-set name attribute</p>
<ul>
<li><strong>Card value acquisition, card.userData (<code>userData</code> is the loading object of custom attributes in R3F, similar to <code>data-yourAttribute</code> in react, here you can encapsulate a method to decouple GameSceneMediator from card value, making the mediator more reusable )</strong></li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-keyword">private</span> onOutCards_C2S() {
        <span class="hljs-built_in">this</span>.hideControlPanel();
        <span class="hljs-keyword">let</span> _outCardsSerial = [];
        <span class="hljs-keyword">let</span> _cardsContainer = <span class="hljs-built_in">this</span>.mViewClass.getViewComponent(<span class="hljs-string">"handList"</span>,<span class="hljs-literal">false</span>,<span class="hljs-built_in">this</span>.getGameModel().context);
        <span class="hljs-keyword">let</span> _cards = _cardsContainer.children;
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; _cards.length; i++) {
            <span class="hljs-keyword">let</span> _card = _cards[i];
            <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.mViewClass.isCardSelected(_card)) {
                _outCardsSerial.push(_card.userData[<span class="hljs-string">"_d_cardSerial"</span>]||_card[<span class="hljs-string">"_d_cardSerial"</span>] || _card.getAttribute(<span class="hljs-string">"data-card-serial"</span>));
            }
        }
        <span class="hljs-built_in">this</span>.getNetFacade()?.sendNotification(card_game_pb.Cmd.PLAYCARDS_C2S, _outCardsSerial);
    }
</code></pre>
<ul>
<li><strong>Node coordinate adjustment</strong></li>
</ul>
<p>By judging the adjustment of the corresponding css attribute of the node style to the corresponding attribute of the node position object</p>
<pre><code class="lang-typescript">isCardSelected(card) {
        <span class="hljs-keyword">return</span> card.position.y == <span class="hljs-number">.2</span> ? <span class="hljs-literal">true</span> : <span class="hljs-literal">false</span>;
    }
</code></pre>
<p><a target="_blank" href="https://github.com/lizhiyu-me/Make-a-multiplayer-card-game/tree/episode7-r3f">Checkout the repo https://github.com/lizhiyu-me/Make-a-multiplayer-card-game/tree/episode7-r3f</a></p>
<hr />
<p>本节主要介绍利用<a target="_blank" href="https://docs.pmnd.rs/react-three-fiber/getting-started/introduction">react-three-fiber</a>(以下简称R3F)实现交互场景的搭建。</p>
<h3 id="heading-r3f">为什么选择R3F</h3>
<ul>
<li>R3F仅仅是将Three.js用JSX进行表示，没有额外开销</li>
<li>可以用react的声明方式构建场景，包括但不限于组件可轻松对状态做出反应，易于交互，并且可以利用 React 的生态</li>
</ul>
<h3 id="heading-5zy65pmv5p6e5bu65a6e546w">场景构建实现</h3>
<ul>
<li><strong>扑克牌</strong></li>
</ul>
<p>注意此处通过将图片地址传给<code>useTexture</code>，得到渲染需要的纹理</p>
<pre><code class="lang-typescript"><span class="hljs-comment">//Card.tsx</span>
<span class="hljs-keyword">import</span> { useTexture } <span class="hljs-keyword">from</span> <span class="hljs-string">"@react-three/drei"</span>
<span class="hljs-keyword">import</span> { Mesh } <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Card</span>(<span class="hljs-params">props: { faceTextureUrl: <span class="hljs-built_in">string</span>, idx: <span class="hljs-built_in">number</span>, beginX: <span class="hljs-built_in">number</span>, serial: <span class="hljs-built_in">number</span> }</span>) </span>{
  <span class="hljs-keyword">let</span> _selectedOffsetY = <span class="hljs-number">.2</span>;
  <span class="hljs-keyword">let</span> _beginX = props.beginX;
  <span class="hljs-keyword">const</span> _texture = useTexture({
    map: props.faceTextureUrl
  })
  <span class="hljs-keyword">return</span> (
    &lt;mesh
      position={[_beginX + props.idx * <span class="hljs-number">.5</span>, <span class="hljs-number">0</span>, props.idx*<span class="hljs-number">0.001</span>]}
      onClick={<span class="hljs-function">(<span class="hljs-params">e: <span class="hljs-built_in">any</span></span>) =&gt;</span> {
        e.stopPropagation();
        <span class="hljs-keyword">let</span> _targetMesh = e[<span class="hljs-string">"eventObject"</span>] <span class="hljs-keyword">as</span> Mesh;
        <span class="hljs-keyword">let</span> _pos = _targetMesh.position;
        <span class="hljs-keyword">let</span> _upY;
        <span class="hljs-keyword">if</span> (_pos.y == <span class="hljs-number">0</span>) _upY = _selectedOffsetY;
        <span class="hljs-keyword">else</span> _upY = <span class="hljs-number">0</span>;
        _targetMesh.position.set(_pos.x, _upY, _pos.z);
      }}
      userData={{<span class="hljs-string">"_d_cardSerial"</span>:props.serial}}
    &gt;
      &lt;boxGeometry args={[<span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">.001</span>]} /&gt;
      &lt;meshStandardMaterial {..._texture} /&gt;
    &lt;/mesh&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Card;
</code></pre>
<ul>
<li><strong>游戏场景</strong></li>
</ul>
<p>首先必须声明一个Canvas节点，因为three.js的所有都必须在Canvas节点下</p>
<pre><code class="lang-typescript">&lt;Canvas orthographic camera={{ zoom: <span class="hljs-number">50</span>, position: [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">100</span>], rotation: [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>] }}&gt;
          &lt;R3fScene mainHandCards={mainHandCards} outCards={outCards} gameModel={_gameModel}&gt;&lt;/R3fScene&gt;
        &lt;/Canvas&gt;
</code></pre>
<p>为了方便使用<code>@react-three/fiber</code>提供的hook（three.js相关的hook只能在Canvas节点下使用）,将游戏场景节点单独提出来</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> R3fScene = <span class="hljs-function">(<span class="hljs-params">props: { mainHandCards: <span class="hljs-built_in">number</span>[], gameModel, outCards: <span class="hljs-built_in">number</span>[][] }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> {
    scene,
    camera,
  } = useThree();
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(scene.getObjectByName(<span class="hljs-string">"handList"</span>))
  })
  <span class="hljs-keyword">let</span> mainHandCards = props.mainHandCards;
  <span class="hljs-keyword">let</span> _gameModel = props.gameModel;
  _gameModel.context = scene;
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getFaceTextureUrl</span>(<span class="hljs-params">serial</span>): <span class="hljs-title">string</span> </span>{
    <span class="hljs-keyword">let</span> _prefix = <span class="hljs-string">"/faces/"</span>
    <span class="hljs-keyword">let</span> _readableName = _gameModel.getCardReadableName(serial);
    <span class="hljs-keyword">if</span> (_readableName === <span class="hljs-string">"rJkr"</span>) <span class="hljs-keyword">return</span> _prefix + <span class="hljs-string">"Poker_Joker_B.png"</span>
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (_readableName === <span class="hljs-string">"bJkr"</span>) <span class="hljs-keyword">return</span> _prefix + <span class="hljs-string">"Poker_Joker_R.png"</span>
    <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">let</span> _suitDic = { <span class="hljs-number">0</span>: <span class="hljs-string">"D"</span>, <span class="hljs-number">1</span>: <span class="hljs-string">"C"</span>, <span class="hljs-number">2</span>: <span class="hljs-string">"H"</span>, <span class="hljs-number">3</span>: <span class="hljs-string">"S"</span> };
      <span class="hljs-keyword">let</span> _suitNumber: <span class="hljs-built_in">number</span> = serial &gt;&gt; <span class="hljs-number">4</span>;
      <span class="hljs-keyword">return</span> _prefix + <span class="hljs-string">"Poker_"</span> + _suitDic[_suitNumber] + _readableName + <span class="hljs-string">".png"</span>;
    }
  }

  <span class="hljs-keyword">let</span> _cardCount = mainHandCards.length;
  <span class="hljs-keyword">let</span> _beginX = -((_cardCount<span class="hljs-number">-1</span>) * <span class="hljs-number">.5</span>+<span class="hljs-number">1.5</span>)/<span class="hljs-number">2</span> ;
  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;CameraControls /&gt;
      &lt;ambientLight intensity={<span class="hljs-number">0.1</span>} /&gt;
      &lt;directionalLight color=<span class="hljs-string">"white"</span> position={[<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">5</span>]} /&gt;

      &lt;group name=<span class="hljs-string">"handList"</span>&gt;
        {mainHandCards.map(<span class="hljs-function">(<span class="hljs-params">serial: <span class="hljs-built_in">number</span>, idx</span>) =&gt;</span> {
          <span class="hljs-keyword">return</span> (&lt;Card key={<span class="hljs-string">"k"</span> + serial} faceTextureUrl={getFaceTextureUrl(serial)} idx={idx} beginX={_beginX} serial={serial} /&gt;)
        })}
      &lt;/group&gt;
      &lt;group position={[<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">0</span>]} scale={<span class="hljs-number">.6</span>} name=<span class="hljs-string">"out-list-1"</span>&gt;
        {props.outCards[<span class="hljs-number">1</span>].map(<span class="hljs-function">(<span class="hljs-params">serial: <span class="hljs-built_in">number</span>, idx</span>) =&gt;</span> {
          <span class="hljs-keyword">return</span> (&lt;Card key={<span class="hljs-string">"k"</span> + serial} faceTextureUrl={getFaceTextureUrl(serial)} idx={idx} beginX={_beginX} serial={serial} /&gt;)
        })}
      &lt;/group&gt;
      &lt;group position={[<span class="hljs-number">-2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">0</span>]} scale={<span class="hljs-number">.6</span>} name=<span class="hljs-string">"out-list-2"</span>&gt;
        {props.outCards[<span class="hljs-number">2</span>].map(<span class="hljs-function">(<span class="hljs-params">serial: <span class="hljs-built_in">number</span>, idx</span>) =&gt;</span> {
          <span class="hljs-keyword">return</span> (&lt;Card key={<span class="hljs-string">"k"</span> + serial} faceTextureUrl={getFaceTextureUrl(serial)} idx={idx} beginX={_beginX} serial={serial} /&gt;)
        })}
      &lt;/group&gt;
      &lt;group position={[<span class="hljs-number">0</span>, <span class="hljs-number">2</span>, <span class="hljs-number">0</span>]} scale={<span class="hljs-number">.6</span>} name=<span class="hljs-string">"out-list-0"</span>&gt;
        {props.outCards[<span class="hljs-number">0</span>].map(<span class="hljs-function">(<span class="hljs-params">serial: <span class="hljs-built_in">number</span>, idx</span>) =&gt;</span> {
          <span class="hljs-keyword">return</span> (&lt;Card key={<span class="hljs-string">"k"</span> + serial} faceTextureUrl={getFaceTextureUrl(serial)} idx={idx} beginX={_beginX} serial={serial} /&gt;)
        })}
      &lt;/group&gt;
    &lt;/&gt;
  )
}
</code></pre>
<p>为了体现3D界面，添加一个轨道相机（滑动场景即可调整相机转角）</p>
<pre><code class="lang-typescript">extend({ OrbitControls });

<span class="hljs-keyword">const</span> CameraControls = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> {
    camera,
    gl: { domElement }
  } = useThree();
  camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">10</span>);
  <span class="hljs-keyword">const</span> controls = useRef();
  useFrame(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> {
    (controls.current <span class="hljs-keyword">as</span> unknown <span class="hljs-keyword">as</span> OrbitControls).update()
  });
  <span class="hljs-keyword">return</span> (
    <span class="hljs-comment">// @ts-ignore</span>
    &lt;orbitControls
      ref={controls}
      args={[camera, domElement]}
      enableZoom={<span class="hljs-literal">false</span>}
      maxAzimuthAngle={<span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">4</span>}
      maxPolarAngle={<span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">4</span>}
      minAzimuthAngle={-<span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">4</span>}
      minPolarAngle={<span class="hljs-number">0</span>}
      rotationSpeed={<span class="hljs-number">0.01</span>}
    /&gt;
  );
};
</code></pre>
<p>场景完整代码：</p>
<pre><code class="lang-typescript"><span class="hljs-comment">//App.tsx</span>
<span class="hljs-keyword">import</span> { useEffect, useRef, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> {
  Canvas,
  useFrame,
  extend,
  useThree,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@react-three/fiber"</span>;
<span class="hljs-keyword">import</span> { OrbitControls } <span class="hljs-keyword">from</span> <span class="hljs-string">"three/examples/jsm/controls/OrbitControls"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>
<span class="hljs-keyword">import</span> Card <span class="hljs-keyword">from</span> <span class="hljs-string">'./component/Card'</span>;
<span class="hljs-keyword">import</span> GameModel <span class="hljs-keyword">from</span> <span class="hljs-string">"./base/src/game/model/GameModel"</span>;
<span class="hljs-keyword">import</span> GameSceneMediator <span class="hljs-keyword">from</span> <span class="hljs-string">"./base/src/game/view/GameSceneMediator"</span>;
<span class="hljs-keyword">import</span> GameSceneView <span class="hljs-keyword">from</span> <span class="hljs-string">"./component/GameSceneView"</span>;

extend({ OrbitControls });

<span class="hljs-keyword">const</span> CameraControls = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> {
    camera,
    gl: { domElement }
  } = useThree();
  camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">10</span>);
  <span class="hljs-keyword">const</span> controls = useRef();
  useFrame(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> {
    (controls.current <span class="hljs-keyword">as</span> unknown <span class="hljs-keyword">as</span> OrbitControls).update()
  });
  <span class="hljs-keyword">return</span> (
    <span class="hljs-comment">// @ts-ignore</span>
    &lt;orbitControls
      ref={controls}
      args={[camera, domElement]}
      enableZoom={<span class="hljs-literal">false</span>}
      maxAzimuthAngle={<span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">4</span>}
      maxPolarAngle={<span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">4</span>}
      minAzimuthAngle={-<span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">4</span>}
      minPolarAngle={<span class="hljs-number">0</span>}
      rotationSpeed={<span class="hljs-number">0.01</span>}
    /&gt;
  );
};

<span class="hljs-keyword">const</span> R3fScene = <span class="hljs-function">(<span class="hljs-params">props: { mainHandCards: <span class="hljs-built_in">number</span>[], gameModel, outCards: <span class="hljs-built_in">number</span>[][] }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> {
    scene,
    camera,
  } = useThree();
  <span class="hljs-keyword">let</span> mainHandCards = props.mainHandCards;
  <span class="hljs-keyword">let</span> _gameModel = props.gameModel;
  _gameModel.context = scene;
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getFaceTextureUrl</span>(<span class="hljs-params">serial</span>): <span class="hljs-title">string</span> </span>{
    <span class="hljs-keyword">let</span> _prefix = <span class="hljs-string">"/faces/"</span>
    <span class="hljs-keyword">let</span> _readableName = _gameModel.getCardReadableName(serial);
    <span class="hljs-keyword">if</span> (_readableName === <span class="hljs-string">"rJkr"</span>) <span class="hljs-keyword">return</span> _prefix + <span class="hljs-string">"Poker_Joker_B.png"</span>
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (_readableName === <span class="hljs-string">"bJkr"</span>) <span class="hljs-keyword">return</span> _prefix + <span class="hljs-string">"Poker_Joker_R.png"</span>
    <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">let</span> _suitDic = { <span class="hljs-number">0</span>: <span class="hljs-string">"D"</span>, <span class="hljs-number">1</span>: <span class="hljs-string">"C"</span>, <span class="hljs-number">2</span>: <span class="hljs-string">"H"</span>, <span class="hljs-number">3</span>: <span class="hljs-string">"S"</span> };
      <span class="hljs-keyword">let</span> _suitNumber: <span class="hljs-built_in">number</span> = serial &gt;&gt; <span class="hljs-number">4</span>;
      <span class="hljs-keyword">return</span> _prefix + <span class="hljs-string">"Poker_"</span> + _suitDic[_suitNumber] + _readableName + <span class="hljs-string">".png"</span>;
    }
  }

  <span class="hljs-keyword">let</span> _cardCount = mainHandCards.length;
  <span class="hljs-keyword">let</span> _beginX = -((_cardCount<span class="hljs-number">-1</span>) * <span class="hljs-number">.5</span>+<span class="hljs-number">1.5</span>)/<span class="hljs-number">2</span> ;
  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;CameraControls /&gt;
      &lt;ambientLight intensity={<span class="hljs-number">0.1</span>} /&gt;
      &lt;directionalLight color=<span class="hljs-string">"white"</span> position={[<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">5</span>]} /&gt;

      &lt;group name=<span class="hljs-string">"handList"</span>&gt;
        {mainHandCards.map(<span class="hljs-function">(<span class="hljs-params">serial: <span class="hljs-built_in">number</span>, idx</span>) =&gt;</span> {
          <span class="hljs-keyword">return</span> (&lt;Card key={<span class="hljs-string">"k"</span> + serial} faceTextureUrl={getFaceTextureUrl(serial)} idx={idx} beginX={_beginX} serial={serial} /&gt;)
        })}
      &lt;/group&gt;
      &lt;group position={[<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">0</span>]} scale={<span class="hljs-number">.6</span>} name=<span class="hljs-string">"out-list-1"</span>&gt;
        {props.outCards[<span class="hljs-number">1</span>].map(<span class="hljs-function">(<span class="hljs-params">serial: <span class="hljs-built_in">number</span>, idx</span>) =&gt;</span> {
          <span class="hljs-keyword">return</span> (&lt;Card key={<span class="hljs-string">"k"</span> + serial} faceTextureUrl={getFaceTextureUrl(serial)} idx={idx} beginX={_beginX} serial={serial} /&gt;)
        })}
      &lt;/group&gt;
      &lt;group position={[<span class="hljs-number">-2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">0</span>]} scale={<span class="hljs-number">.6</span>} name=<span class="hljs-string">"out-list-2"</span>&gt;
        {props.outCards[<span class="hljs-number">2</span>].map(<span class="hljs-function">(<span class="hljs-params">serial: <span class="hljs-built_in">number</span>, idx</span>) =&gt;</span> {
          <span class="hljs-keyword">return</span> (&lt;Card key={<span class="hljs-string">"k"</span> + serial} faceTextureUrl={getFaceTextureUrl(serial)} idx={idx} beginX={_beginX} serial={serial} /&gt;)
        })}
      &lt;/group&gt;
      &lt;group position={[<span class="hljs-number">0</span>, <span class="hljs-number">2</span>, <span class="hljs-number">0</span>]} scale={<span class="hljs-number">.6</span>} name=<span class="hljs-string">"out-list-0"</span>&gt;
        {props.outCards[<span class="hljs-number">0</span>].map(<span class="hljs-function">(<span class="hljs-params">serial: <span class="hljs-built_in">number</span>, idx</span>) =&gt;</span> {
          <span class="hljs-keyword">return</span> (&lt;Card key={<span class="hljs-string">"k"</span> + serial} faceTextureUrl={getFaceTextureUrl(serial)} idx={idx} beginX={_beginX} serial={serial} /&gt;)
        })}
      &lt;/group&gt;
    &lt;/&gt;
  )
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params">props: <span class="hljs-built_in">any</span></span>) </span>{
  <span class="hljs-keyword">let</span> _gameFacade = props.gameFacade;
  <span class="hljs-keyword">let</span> _gameModel: GameModel = _gameFacade.retrieveProxy(<span class="hljs-string">"GameModel"</span>);
  <span class="hljs-keyword">let</span> [mainHandCards, setMainHandCards] = useState(_gameModel.cardsArr);
  <span class="hljs-keyword">let</span> [outCards, setOutCards] = useState(_gameModel.outCards);
  _gameModel.setMainHandCardsHook(setMainHandCards);
  _gameModel.setOutCardsHook(setOutCards);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (_gameFacade.retrieveMediator(<span class="hljs-string">"GameSceneMediator"</span>) == <span class="hljs-literal">null</span>) {
      _gameFacade.registerMediator(<span class="hljs-keyword">new</span> GameSceneMediator(<span class="hljs-literal">null</span>, <span class="hljs-keyword">new</span> GameSceneView()));
    }
  });

  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;div id=<span class="hljs-string">'canvas-container'</span>&gt;
        &lt;Canvas orthographic camera={{ zoom: <span class="hljs-number">50</span>, position: [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">100</span>], rotation: [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>] }}&gt;
          &lt;R3fScene mainHandCards={mainHandCards} outCards={outCards} gameModel={_gameModel}&gt;&lt;/R3fScene&gt;
        &lt;/Canvas&gt;
      &lt;/div&gt;

      &lt;div id=<span class="hljs-string">"status"</span> style={{ display: <span class="hljs-string">'fix'</span>, textAlign: <span class="hljs-string">'center'</span>, fontSize: <span class="hljs-string">"2em"</span>, userSelect: <span class="hljs-string">"none"</span> }}&gt;hello&lt;/div&gt;
      &lt;div id=<span class="hljs-string">'controlPanel-scores'</span> className=<span class="hljs-string">'controlPanel'</span>&gt;
        &lt;button id=<span class="hljs-string">'controlPanel-scores-1'</span> className=<span class="hljs-string">'controlButton'</span>&gt;<span class="hljs-number">1</span>&lt;/button&gt;
        &lt;button id=<span class="hljs-string">'controlPanel-scores-2'</span> className=<span class="hljs-string">'controlButton'</span>&gt;<span class="hljs-number">2</span>&lt;/button&gt;
        &lt;button id=<span class="hljs-string">'controlPanel-scores-3'</span> className=<span class="hljs-string">'controlButton'</span>&gt;<span class="hljs-number">3</span>&lt;/button&gt;
      &lt;/div&gt;

      &lt;div id=<span class="hljs-string">'controlPanel-operation'</span> className=<span class="hljs-string">'controlPanel'</span>&gt;
        &lt;button id=<span class="hljs-string">'controlPanel-operation-pass'</span> className=<span class="hljs-string">'controlButton'</span>&gt;pass&lt;/button&gt;
        &lt;button id=<span class="hljs-string">'controlPanel-operation-play'</span> className=<span class="hljs-string">'controlButton'</span>&gt;play&lt;/button&gt;
      &lt;/div&gt;
    &lt;/&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<h3 id="heading-5qgg5p626ycc6ywn5ps55yqo">框架适配改动</h3>
<ul>
<li><strong>获取场景节点</strong></li>
</ul>
<pre><code class="lang-typescript">getViewComponent(name: <span class="hljs-built_in">string</span>,isDOM:<span class="hljs-built_in">boolean</span> = <span class="hljs-literal">true</span>,canvasScene:<span class="hljs-built_in">any</span> = <span class="hljs-literal">null</span>) {
        <span class="hljs-keyword">if</span>(isDOM) <span class="hljs-keyword">return</span> <span class="hljs-built_in">document</span>.getElementById(name);
        <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">if</span>(canvasScene) <span class="hljs-keyword">return</span> canvasScene.getObjectByName(name);
        }
    }
</code></pre>
<p>通过传入three.js的场景对象，调用getObjectByName接口获取预先设置好name属性的节点</p>
<ul>
<li><strong>牌值获取card.userData（userData是R3F中自定义属性的装载对象，类似react中的data-yourAttribute, 在这里可以封装一个方法，让GameSceneMediator与牌取值解耦，使mediator的复用性更强）</strong></li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-keyword">private</span> onOutCards_C2S() {
        <span class="hljs-built_in">this</span>.hideControlPanel();
        <span class="hljs-keyword">let</span> _outCardsSerial = [];
        <span class="hljs-keyword">let</span> _cardsContainer = <span class="hljs-built_in">this</span>.mViewClass.getViewComponent(<span class="hljs-string">"handList"</span>,<span class="hljs-literal">false</span>,<span class="hljs-built_in">this</span>.getGameModel().context);
        <span class="hljs-keyword">let</span> _cards = _cardsContainer.children;
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; _cards.length; i++) {
            <span class="hljs-keyword">let</span> _card = _cards[i];
            <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.mViewClass.isCardSelected(_card)) {
                _outCardsSerial.push(_card.userData[<span class="hljs-string">"_d_cardSerial"</span>]||_card[<span class="hljs-string">"_d_cardSerial"</span>] || _card.getAttribute(<span class="hljs-string">"data-card-serial"</span>));
            }
        }
        <span class="hljs-built_in">this</span>.getNetFacade()?.sendNotification(card_game_pb.Cmd.PLAYCARDS_C2S, _outCardsSerial);
    }
</code></pre>
<ul>
<li><strong>节点坐标调整</strong></li>
</ul>
<p>由判断节点style 相应css属性的调整为节点position对象相应属性</p>
<pre><code class="lang-typescript">isCardSelected(card) {
        <span class="hljs-keyword">return</span> card.position.y == <span class="hljs-number">.2</span> ? <span class="hljs-literal">true</span> : <span class="hljs-literal">false</span>;
    }
</code></pre>
<p><a target="_blank" href="https://github.com/lizhiyu-me/Make-a-multiplayer-card-game/tree/episode7-r3f">查看本节相关代码 https://github.com/lizhiyu-me/Make-a-multiplayer-card-game/tree/episode7-r3f</a></p>
]]></content:encoded></item><item><title><![CDATA[Make a multiplayer card game - Episode 6 | Create 2D graphical interface with React]]></title><description><![CDATA[This section mainly uses react to implement UI views. Before this section, a UI view version has been implemented with cocos creator. In order to reuse the basic framework, we abstract the game logic and decouple the logic related to the view engine ...]]></description><link>https://lizhiyu.me/make-a-multiplayer-card-game-episode-6-or-create-2d-graphical-interface-with-react</link><guid isPermaLink="true">https://lizhiyu.me/make-a-multiplayer-card-game-episode-6-or-create-2d-graphical-interface-with-react</guid><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Lizhiyu]]></dc:creator><pubDate>Sun, 10 Apr 2022 20:44:27 GMT</pubDate><content:encoded><![CDATA[<p>This section mainly uses react to implement UI views. Before this section, a UI view version has been implemented with cocos creator. In order to reuse the basic framework, we abstract the game logic and decouple the logic related to the view engine api. The following is an abstracted interface:</p>
<pre><code><span class="hljs-selector-tag">export</span> <span class="hljs-selector-tag">interface</span> <span class="hljs-selector-tag">IGameSceneView</span> {
    <span class="hljs-selector-tag">getViewComponent</span>(<span class="hljs-attribute">name</span>: string),
    <span class="hljs-selector-tag">getNewViewComponent</span>(comp),
    <span class="hljs-selector-tag">getChild</span>(childPath, parent),
    <span class="hljs-selector-tag">addClickListener</span>(comp, handler, target),
    <span class="hljs-selector-tag">setCard</span>(card, name),
    <span class="hljs-selector-tag">setLabel</span>(labelComp, text),
    <span class="hljs-selector-tag">removeAllChildren</span>(parent),
    <span class="hljs-selector-tag">addChild</span>(child, parent),
    <span class="hljs-selector-tag">isCardSelected</span>(card),
    <span class="hljs-selector-tag">toggleCardSelectedStatus</span>(card),
    <span class="hljs-selector-tag">showComponent</span>(comp),
    <span class="hljs-selector-tag">hideComponent</span>(comp)
}
</code></pre><p>Compared with cocos creator, the implementation of react is more operable at the code level. The styles of interface elements are all implemented by handwritten code. The hot-reload that comes with create-react-app also responds to changes very quickly. There is a time to switch editors and wait for the editor to refresh, which is very lightweight.</p>
<p>Code reference https://github.com/lizhiyu-me/Make-a-multiplayer-card-game/tree/episode6-react</p>
<p>The next article mainly introduces the comparison between cocos creator and react implementation in development.</p>
<p>本节主要是用react实现UI视图。
在本节之前，已经用cocos creator实现了一个UI视图版本，为了重用基础框架，我们将游戏逻辑进行抽象，并将与视图引擎api相关的逻辑解耦出来。
下面是抽象出来的一个接口：</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> IGameSceneView {
    getViewComponent(name: <span class="hljs-built_in">string</span>),
    getNewViewComponent(comp),
    getChild(childPath, parent),
    addClickListener(comp, handler, target),
    setCard(card, name),
    setLabel(labelComp, text),
    removeAllChildren(parent),
    addChild(child, parent),
    isCardSelected(card),
    toggleCardSelectedStatus(card),
    showComponent(comp),
    hideComponent(comp)
}
</code></pre>
<p>相对于cocos creator，react的实现在代码层面可操作性较强，体现在界面元素的样式都通过手写代码来实现，create-react-app自带的hot-reload也非常快地反馈变更，不会有切换编辑器和等待编辑器刷新的时间，非常轻量的感觉。</p>
<p>代码参考 <a target="_blank" href="https://github.com/lizhiyu-me/Make-a-multiplayer-card-game/tree/episode6-react">https://github.com/lizhiyu-me/Make-a-multiplayer-card-game/tree/episode6-react</a></p>
<p>下一篇主要介绍cocos creator 与 react 实现在开发上的对比。</p>
]]></content:encoded></item><item><title><![CDATA[Make a multiplayer card game - Episode 5 | Create 2D graphical interface with Cocos Creator]]></title><description><![CDATA[This chapter is almost a month away from the previous chapter. During this period, it is mainly about the transformation and reconstruction of the client. The content of the overall client is almost completely different from the command-line version ...]]></description><link>https://lizhiyu.me/make-a-multiplayer-card-game-episode-5-or-create-2d-graphical-interface-with-cocos-creator</link><guid isPermaLink="true">https://lizhiyu.me/make-a-multiplayer-card-game-episode-5-or-create-2d-graphical-interface-with-cocos-creator</guid><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Lizhiyu]]></dc:creator><pubDate>Sun, 03 Apr 2022 14:43:43 GMT</pubDate><content:encoded><![CDATA[<p>This chapter is almost a month away from the previous chapter. During this period, it is mainly about the transformation and reconstruction of the client. The content of the overall client is almost completely different from the command-line version of the previous chapter.</p>
<p>According to the previous plan, I converted all the code written in JS to TS and transplanted the command-line interactive interface to the conventional game interactive interface (using cocos creator game engine).</p>
<p>Previously, in order to quickly realize the function, the server and client used only one file to encode.
Up to now, the client code of the project has been modularized, and the classic PureMVC library has been introduced to completely decouple the UI interaction and data, network messages and game operation logic, so as to create conditions for extracting the code of the framework independent of the game engine (it is planned to use one framework to access different view layer engines).</p>
<p>PureMVC has been introduced in previous articles. The core idea is to decouple view and data.
The data is processed by proxy and the facade is controlled by proxy.</p>
<p>Compared with the previous version, some changes are involved:
-The difference between socket and web socket API. It mainly includes the names of several monitored events and the interfaces for sending messages.
-UI display layer independent. Command line output and game engine rendering view interactive operation interface.</p>
<p><a target="_blank" href="https://github.com/lizhiyu-me/Make-a-multiplayer-card-game/tree/episode5-UICocosCreator">demo url</a></p>
<p>本章离前一章快一个月了，在这段时间，主要是对客户端的改造和重构，整体客户端的内容几乎完全不同于上一章命令行版本。</p>
<p>按照之前的计划，我将之前用js写的代码全部转为了ts，将命令行交互界面移植到常规的游戏交互界面（利用CocosCreator游戏引擎）。</p>
<p>之前为了快速实现功能，服务器和客户端分别只用一个文件进行编码。
截至目前，项目客户端代码进行了模块化改造，引入了经典的PureMVC库，将UI交互和数据，网络消息和游戏操作逻辑完全解藕，为后面提取与游戏引擎无关的框架（计划用一个框架接入不同视图层引擎）代码创造条件。</p>
<p>在之前的文章里面已经介绍过PureMVC，核心的思想就是视图与数据解藕。
通过一个门面类（Facade）来统管框架，用视图代理（Mediator）控制显示组件逻辑，用数据模型代理（Proxy）存储和处理数据。</p>
<p>相对于上一版，涉及到的一些改动：</p>
<ul>
<li>socket 和 web socket api 的区别。主要是几个监听的事件名字，和发送消息的接口。</li>
<li>ui显示层独立。命令行输出和游戏引擎渲染视图交互操作界面。</li>
</ul>
<p><a target="_blank" href="https://github.com/lizhiyu-me/Make-a-multiplayer-card-game/tree/episode5-UICocosCreator">demo url</a></p>
]]></content:encoded></item><item><title><![CDATA[Combine your computers with Synergy]]></title><description><![CDATA[I used to code on my Mac, but Mac run game engine editor so bad. So I have to work on two computers, one for coding, one for game engine for some game scene.
It's not convenient especially code and edit game scene in the same time.
Synergy give me th...]]></description><link>https://lizhiyu.me/combine-your-computers-with-synergy</link><guid isPermaLink="true">https://lizhiyu.me/combine-your-computers-with-synergy</guid><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Lizhiyu]]></dc:creator><pubDate>Mon, 28 Mar 2022 02:26:46 GMT</pubDate><content:encoded><![CDATA[<p>I used to code on my Mac, but Mac run game engine editor so bad. So I have to work on two computers, one for coding, one for game engine for some game scene.
It's not convenient especially code and edit game scene in the same time.</p>
<p>Synergy give me the answer, it's awsome.</p>
]]></content:encoded></item><item><title><![CDATA[PureMVC | Prior for Episode 5]]></title><description><![CDATA[PureMVC will be included in next  episode.
PureMVC is a framework for creating applications based upon the well-established model–view–controller (MVC) design pattern. The free, open source framework was originally implemented in the ActionScript 3 l...]]></description><link>https://lizhiyu.me/puremvc-or-prior-for-episode-5</link><guid isPermaLink="true">https://lizhiyu.me/puremvc-or-prior-for-episode-5</guid><category><![CDATA[2Articles1Week]]></category><category><![CDATA[javascript framework]]></category><category><![CDATA[framework]]></category><dc:creator><![CDATA[Lizhiyu]]></dc:creator><pubDate>Sat, 26 Mar 2022 09:35:52 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>PureMVC will be included in next  episode.</p>
<p>PureMVC is a framework for creating applications based upon the well-established model–view–controller (MVC) design pattern. The free, open source framework was originally implemented in the ActionScript 3 language for use with Adobe Flex, Flash and AIR, and it has since been ported to nearly all the major web development platforms.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1648287487663/TkUwfryAy.jpg" alt="puremvc.jpg" /></p>
<p>Advantages and disadvantages of PureMVC:</p>
<ul>
<li><p><strong>Advantages </strong></p>
</li>
<li><p>Use intermediaries, agents and commands to realize decoupling, which reduces the coupling among model, view and controller and improves the reuse of some codes</p>
</li>
<li><p>The view interface can be reused</p>
</li>
<li><p>Model data can be reused</p>
</li>
<li><p><strong>Disadvantages </strong></p>
</li>
<li><p>Code redundancy is large. For simple functions, you have to create view, mediator, command, facade, proxy and model scripts</p>
</li>
<li><p>The operation process is a cumbersome process, and the code in mediator will appear complex and difficult to understand, unless you are familiar with the implementation principle of PureMVC</p>
</li>
<li><p><strong>PureMVC features</strong></p>
</li>
<li>The notice shall be delivered through the operation of packing and unpacking</li>
<li>The command / notification is implemented in the observer mode. The command / notification uses the reflection acquisition method in the observer and is executed</li>
<li>No service (this module for network communication can be added according to the structure of MVC)</li>
<li>Data is transmitted through notification. Sendnotification has only one object type parameter. It will feel that the data transmission is limited. You can combine the data into a type / structure for transmission, or expand another parameter for notification.</li>
</ul>
]]></content:encoded></item></channel></rss>