RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1601710
Accepted
DaYa
DaYa
Asked:2024-12-05 18:12:10 +0000 UTC2024-12-05 18:12:10 +0000 UTC 2024-12-05 18:12:10 +0000 UTC

用于在 JavaScript 中播放的相机

  • 772

关于开放式多人游戏,我想到了几个想法。我使用的语言是JavaScript。由于我不太了解他,所以出现了一个问题。

有一个有条件的游戏世界。目前 - 画布600x400像素。此阶段的玩家用圆圈表示。由于使用websocket实现的python服务器,他们可以在画布上移动并看到彼此的动作。

问题

我们需要实现一个摄像头来跟踪玩家在更大的游戏世界中移动。

尝试解决问题

说实话,由于对技术的了解不够,我只找到了实现的信息ctx.translate(),但据我了解,这种方式并不适合我。因为有了它,游戏世界就会相对于玩家移动。我预见这对我的想法不起作用。

我想要什么?

如果有人能帮助我回答如何实现这样的相机,以便它在大世界范围内舒适地工作,那就太好了。作为示例,我可以给出diep.io、ponytown、surviv.io等。

材料

由于我不明白到底应该提供什么,所以我将提供整个客户端部分。

名为script.js的JavaScript 文件

HTML&СSS 文件分别名为index.html、index_style.css

javascript
  • 1 1 个回答
  • 96 Views

1 个回答

  • Voted
  1. Best Answer
    Leonid
    2024-12-07T22:14:49Z2024-12-07T22:14:49Z

    首先,我们组织对地图上英雄(黑色圆圈)移动的控制。例如,我们可以使用光标设置地图上的移动方向,该函数moveDir将以毫秒为间隔触发inertia_frame英雄( , )inertia_t位置的变化,变化幅度为点击方向上的距离。改变步长可以是10毫秒。pos.xpos.yinertia_l

    英雄的绘制(在新位置或先前位置)以最佳帧速率 ( animationFrame) 进行处理。如果没有变化,则图形预处理器将保持帧不变。

    基于英雄的新位置,我们使用 移动坐标原点translate(pos.x,pos.y),将英雄绘制在“零”位置并返回变换矩阵的设置 - ctx.resetTransform()(或ctx.save() отрисовка ctx.restore())。

    const canvas = document.getElementById('game');
    const ctx = canvas.getContext('2d');
    const w = canvas.width = 600;
    const h = canvas.height = 400;
    
    const r = 10;
    
    const inertia_t = 600;
    const inertia_l = 50;
    const inertia_frame = 10;
    
    let pos = {
      x: w / 2,
      y: h / 2
    };
    
    canvas.addEventListener('click', moveDir);
    
    function drawMe() {
      ctx.clearRect(0, 0, w, h);
      ctx.translate(pos.x, pos.y);
    
      ctx.fill(new Path2D(`M ${-r} 0 A ${r} ${r} 90 1 1 ${r} 0 ${r} ${r} 90 1 1 ${-r} 0 z`));
      ctx.resetTransform();
    
      requestAnimationFrame(drawMe);
    }
    
    requestAnimationFrame(drawMe);
    
    
    function moveDir(e) {
      const dx = e.offsetX - pos.x;
      const dy = e.offsetY - pos.y;
    
      const k = inertia_l / Math.sqrt(dx * dx + dy * dy);
      let step_num = inertia_t / inertia_frame;
      const step_x = dx * k / step_num;
      const step_y = dy * k / step_num;
    
      const moveId = setInterval(() => {
        pos.x += step_x;
        pos.y += step_y;
        step_num--;
    
        if (!step_num) {
          clearInterval(moveId);
        }
      }, inertia_frame);
    
    }
    <canvas id="game"></canvas>

    现在来解决这个问题。在单独的画布上,我们绘制了一个 10000x10000 的世界。为了清楚起见,我们将其划分为 1/100x1/100 的单元格,并通过改变每个条件单元格的填充颜色进行渐变。

    我们将英雄放置在这个大世界的中心(您可以指定任何坐标)。但现在,在绘制英雄时,我们首先使用 放置世界的一部分ctx.drawImage(map, pos.x - w / 2, pos.y - h / 2, w, h, 0, 0, w, h)。也就是说,我们以一张大地图画布作为图像,将片段的开头设置为相对于英雄的位置,并且片段的大小与比赛场地的大小一致。在运动场的画布上,我们将这个片段从 0.0 绘制到运动场的整个尺寸。将世界画布转换为应该更合理BitMap- 位图也作为第一个参数传递给drawImage().

    英雄总是被画在场地的中央。移动函数是一样的,只是移动方向是从比赛场地的中心开始计算的。因此,出于制作目的,最好将英雄移动到单独的画布上,放在其他画布上(使用平庸的画布css z-index)。

    底层画布可以为其他玩家保留;该画布应为地图大小 (10000x10000),在其上放置其他玩家,其坐标通过网络套接字接收。最好将该层的控制权转移给 Web Worker,Web Worker 将保持与服务器的连接,并且播放器将放置在同一 Worker 中关联的 OffscreenCanvas 上。

    第三个,也是世界碎片的最低画布——运动场。可以为对象、建筑物等分配附加层。除了最上面的英雄图层之外,所有图层的绘制方式都与世界 ( ) 完全相同map。

    const canvas = document.getElementById('game');
    const ctx = canvas.getContext('2d');
    const w = canvas.width = 600; // Размеры игрового поля
    const h = canvas.height = 400;
    
    const map_w = 10_000; // Размер карты (мира)
    const map_h = 10_000;
    const map = drawMap(map_w, map_h);
    
    const r = 10;
    
    const inertia_t = 600;
    const inertia_l = 50;
    const inertia_frame = 10;
    
    let pos = {
      x: map_w / 2,
      y: map_h / 2
    };
    
    canvas.addEventListener('click', moveDir);
    
    function drawMe() {
      ctx.clearRect(0, 0, w, h);
      ctx.drawImage(map, pos.x - w / 2, pos.y - h / 2, w, h, 0, 0, w, h);
    
      ctx.fill(new Path2D(`M ${w/2-r} ${h/2} A ${r} ${r} 90 1 1 ${w/2+r} ${h/2} ${r} ${r} 90 1 1 ${w/2-r} ${h/2} z`));
      requestAnimationFrame(drawMe);
    }
    
    requestAnimationFrame(drawMe);
    
    
    function moveDir(e) {
      const dx = e.offsetX - w / 2;
      const dy = e.offsetY - h / 2;
    
      const k = inertia_l / Math.sqrt(dx * dx + dy * dy);
      let step_num = inertia_t / inertia_frame;
      const step_x = dx * k / step_num;
      const step_y = dy * k / step_num;
    
    
      const moveId = setInterval(() => {
        pos.x += step_x;
        pos.y += step_y;
        step_num--;
    
        if (!step_num) {
          clearInterval(moveId);
        }
      }, inertia_frame);
    
    }
    
    
    function drawMap(width, height) {
      const map = document.createElement('canvas');
      const m_ctx = map.getContext('2d');
      const m_w = map.width = width;
      const m_h = map.height = height;
    
    
    
      const cell = {
        w: m_w / 100,
        h: m_h / 100
      };
    
      m_ctx.font = `${cell.w/8}px sans-serif`;
      m_ctx.textAlign = 'center';
      m_ctx.textBaseline = 'middle';
      m_ctx.strokeStyle = 'grey';
    
      for (let raw = 0; raw < 100; raw++) {
        for (let col = 0; col < 100; col++) {
          m_ctx.fillStyle = `rgb(${255-raw},${255-col},${255-raw-col})`;
          m_ctx.fillRect(col * cell.w + 1, raw * cell.h + 1, cell.w - 2, cell.h - 2);
          m_ctx.strokeText(`raw ${raw}, col ${col}`, col * cell.w + cell.w / 2, raw * cell.h + cell.h / 2);
    
        }
      }
      return map;
    }
    <canvas id="game"></canvas>

    • 1

相关问题

  • 第二个 Instagram 按钮的 CSS 属性

  • 由于模糊,内容不可见

  • 弹出队列。消息显示不正确

  • 是否可以在 for 循环中插入提示?

  • 如何将 JSON 请求中的信息输出到数据表 Vuetify vue.js?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5