admin 发表于 2023-3-1 19:08:01

【HTML5小游戏】一. 开始游戏的基础 - 一个永不停息的死循环

<h1>【HTML5小游戏】一. 开始游戏的基础 - 一个永不停息的死循环</h1><h2>为什么要说死循环</h2><p>几乎所有的游戏,都会有一个死循环,所以我们也从这个东西来作为这次主题的开端,讲讲用来写游戏的 - 死循环。</p><h2>为什么要有一个死循环</h2><p>游戏的本质,仔细想想,似乎就是一个可以交互的动画片。<br>而动画片是由一秒几十帧的画面组成的。</p><p>那么游戏的这几十帧画面从何而来呢?</p><p>其实,就是来自于这个死循环,是它孜孜不倦的把画面画到了的画布(Canvas)上。<br>(如果你还不清楚 Canvas 是什么,建议先自行了解一下 Canvas ,这里我就不再赘述了)</p><p>除了渲染画面,游戏逻辑,对用户输入的监听,其实也都是需要在这个循环内完成的。</p><p>一遍一遍又一遍,周而复始。</p><h2>怎么写这个死循环</h2><h3>开始循环</h3><p>由于是 HTML5 小游戏,所以这里直接使用了 Javascript 的 setInterval 来循环执行 main 函数<br>我们期望这个循环执行速度尽量快一些,所以使用了最小单位 1毫秒 来执行。</p><pre><code class="lang-js">setInterval(main, 1);

function main() {
// TODO
}</code></pre><h3>main 函数里要做什么?</h3><p>就像前面说的,这个循环很重要的一部分内容,就是要绘制游戏画面。所以 main 函数里的首要任务也是绘制游戏画面<br>当然除了画面之外,还需要处理游戏逻辑,用户输入甚至音频音效。<br>这里先偷个懒,先把除了画面之外的内容暂时放到一个函数里</p><pre><code class="lang-js">function main() {
update(); // 处理游戏逻辑
drawImg(); // 绘制游戏图像
}

function drawImg() {
// TODO
}

function update() {
// TODO
}</code></pre><p>这样这个循环的框架就完成了,但是还缺少一个很重要的东西,时间。</p><p>在执行中,你可能会逐渐发现,游戏的每次循环间隔时间,并不是预期的 1 毫秒。<br>这是因为游戏逻辑的处理和画面的绘制的耗时可能不止 1 毫秒,所以循环间隔其实是不可控的。<br>这时候就需要引入一些变量,来记录循环之间实际的间隔,然后传递给循环内部,以便游戏逻辑处理时使用。</p><p>如何计算循环间隔时间呢?其实很简单,只要在每次循环开始的时候记录一下开始时间就好了。</p><pre><code class="lang-js">let then = new Date()
function main() {
let now = new Date()
let delta = now - then;
update(delta); // 将 delta 传入 update 函数,方便游戏逻辑得知真正的循环间隔时间
drawImg(); // 绘制游戏图像
then = now;
}</code></pre><h3>如何绘制图片</h3><p>绘制图片需要用到 Html5 的标签 Canvas,先把这个 Canvas 放到页面上</p><pre><code class="lang-js">const canvas = document.createElement(&quot;canvas&quot;);
const ctx = canvas.getContext(&quot;2d&quot;);
canvas.width = width;
canvas.height = height;
canvas.style.margin = &quot;0 auto&quot;;
const screen = document.getElementById(&quot;screen&quot;); // 我 html 代码里放了一个 id 为 screen 的 div,但这不重要
screen.appendChild(canvas);</code></pre><p>然后是绘制图片函数,记得每次循环绘制之前先把画布清空,这个很重要。<br>就简单画几个文字上去吧。<br>(gameData 的值的来源,稍后会在 update 函数中说明)</p><pre><code class="lang-js">function drawImg() {
ctx.clearRect(0, 0, cvWidth, cvHeight); // 清除画面
ctx.font = &quot;20px 微软雅黑&quot;; // 设置字体
ctx.fillStyle = &quot;#000&quot;; // 设置颜色
ctx.fillText(&quot;当前时间戳为: &quot; + gameData.currentTimeStamp, 260, 50); // 绘制文字
ctx.fillText(&quot;当前循环耗时(秒): &quot; + gameData.updateDelta, 260, 120); // 绘制文字
ctx.fillText(&quot;当前帧数: &quot; + gameData.fps, 260, 180); // 绘制文字
}</code></pre><h3>游戏逻辑更新</h3><p>由于这次重点是在讲这个主循环,所以游戏逻辑这块,就只更新一点基础数据吧。</p><pre><code class="lang-js">const gameData = {}

function update(modifer) {
gameData.currentTimeStamp = new Date().getTime()
gameData.updateDelta = modifer
gameData.fps = Math.floor(1 / modifer)
}</code></pre><h3>最终的效果图</h3><p></p><h3>完整的代码</h3><pre><code class="lang-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;title&gt;&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div id=&quot;screen&quot;&gt;&lt;/div&gt;
&lt;/body&gt;
&lt;script&gt;
    // 创建 canvas
    const canvas = document.createElement(&quot;canvas&quot;);
    const ctx = canvas.getContext(&quot;2d&quot;);

    // 改变量用于存储上一帧开始的时间
    let then;

    // Canvas 宽高
    const cvWidth = 600;
    const cvHeight = 500;

    // 存储游戏数据的变量
    const gameData = {
      currentTimeStamp: new Date().getTime(),
    };

    function init() {
      then = Date.now();
      createCanvas(cvWidth, cvHeight);
      // 开启死循环
      setInterval(main, 1);
    }

    // 添加 canvas 到网页里
    function createCanvas(width, height) {
      canvas.width = width;
      canvas.height = height;
      canvas.style.margin = &quot;0 auto&quot;;
      let screen = document.getElementById(&quot;screen&quot;);
      screen.appendChild(canvas);
    }

    // 主循环函数
    function main() {
      const now = Date.now();
      const delta = now - then; // 计算每次循环的实际耗时,方便在代码中统一时间相关的参数(例如速度之类)
      update(delta / 1000); // 处理游戏逻辑
      drawImg(); // 绘制游戏图像
      then = now;
    }

    // 可以处理一下游戏逻辑,这次来用它来更新一些基本变量
    function update(modifier) {
      gameData.currentTimeStamp = new Date().getTime();
      gameData.updateDelta = modifier;
      gameData.fps = Math.floor(1 / modifier);
    }

    // 绘制游戏图像, 这次就简单的绘制一些文字就好
    function drawImg() {
      ctx.clearRect(0, 0, cvWidth, cvHeight); // 清除画面
      ctx.font = &quot;20px 微软雅黑&quot;; // 设置字体
      ctx.fillStyle = &quot;#000&quot;; // 设置颜色
      ctx.fillText(&quot;当前时间戳为: &quot; + gameData.currentTimeStamp, 260, 50); // 绘制文字
      ctx.fillText(&quot;当前循环耗时(秒): &quot; + gameData.updateDelta, 260, 120); // 绘制文字
      ctx.fillText(&quot;当前帧数: &quot; + gameData.fps, 260, 180); // 绘制文字
    }

    window.onload = init(); // 页面加载后执行 init 函数
&lt;/script&gt;
&lt;/html&gt;</code></pre>
                        <div class="post-copyright">
                            <div class="alert" role="alert">最后编辑时间为: January 11th , 2022 at 11:21 pm<br>本文由 <ahref="https://ma-zhe.com/index.php/author/1/">Warren Ma</a> 创作,采用 <ahref="https://creativecommons.org/licenses/by/4.0/">知识共享署名 4.0</a> 国际许可协议进行许可<br>可自由转载、引用,但需署名作者且注明文章出处</div>
                        </div>
                        <div class="post-tags"></div>
页: [1]
查看完整版本: 【HTML5小游戏】一. 开始游戏的基础 - 一个永不停息的死循环