You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何实现Canvas画布上下文跟随玩家的视口功能?

如何实现Canvas画布上下文跟随玩家的视口功能?

嘿,我看到你在尝试给Canvas游戏加跟随玩家的视口,之前用translate的思路是对的,但方向和绘制顺序搞反了,导致效果不对。我来帮你调整代码,让玩家稳稳保持在屏幕中心,同时正确显示游戏世界的内容~

核心思路

视口的本质是把整个游戏世界向玩家移动的反方向平移,这样玩家看起来就固定在屏幕中心了。比如玩家往右走,我们就把游戏世界往左移,玩家在屏幕里的位置不变,周围的物体跟着移动。

修改后的完整代码

我基于你的代码做了调整,每一步都加了注释:

// Get the canvas element and its context
const canvas = document.createElement("canvas");
document.body.append(canvas);
// 设置画布大小(视口尺寸),可以根据需求调整
canvas.width = 800;
canvas.height = 600;
// 可选:设置画布样式,防止页面缩放导致拉伸
canvas.style.border = "1px solid #000";
canvas.style.display = "block";
canvas.style.margin = "0 auto";

const ctx = canvas.getContext("2d");

// Player coords and update method that moves the player based on input
const player = {
  x: 0,
  y: 0,
  size: 10, // 把玩家尺寸抽成变量,方便计算中心
  vel: 2,
  update(){
    if(key.left) this.x -= this.vel;
    if(key.right) this.x += this.vel;
    if(key.up) this.y -= this.vel;
    if(key.down) this.y += this.vel;
  }
};

// Keys pressed (Used for movement)
const key = {
  left: false,
  right: false,
  up: false,
  down: false
}

// Get Input
window.addEventListener('keydown', (e) =>
{
  switch(e.code){
    case "KeyW":
      key["up"] = true;
      break;
    case "KeyS":
      key["down"] = true;
      break;
    case "KeyA":
      key["left"] = true;
      break;
    case "KeyD":
      key["right"] = true;
      break;
  }
});

// Relieve Input
window.addEventListener('keyup', (e) =>
{
  switch(e.code){
    case "KeyW":
      key["up"] = false;
      break;
    case "KeyS":
      key["down"] = false;
      break;
    case "KeyA":
      key["left"] = false;
      break;
    case "KeyD":
      key["right"] = false;
      break;
  }
});

function update(){
  // 1. 清空整个画布
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // 2. 更新玩家位置
  player.update();

  // 3. 计算视口偏移量:屏幕中心 - 玩家位置(反方向平移)
  const viewportOffsetX = canvas.width / 2 - player.x - player.size / 2;
  const viewportOffsetY = canvas.height / 2 - player.y - player.size / 2;

  // 4. 保存当前上下文状态,然后平移整个游戏世界
  ctx.save();
  ctx.translate(viewportOffsetX, viewportOffsetY);

  // 5. 绘制所有游戏元素(静态物体 + 玩家),这里用的是游戏世界的绝对坐标
  // 绘制静态物体
  ctx.fillRect(100, 100, 10, 10);
  ctx.fillRect(100, 120, 10, 10);
  // 绘制玩家(游戏世界坐标)
  ctx.fillRect(player.x, player.y, player.size, player.size);

  // 6. 恢复上下文状态,避免影响后续绘制
  ctx.restore();

  requestAnimationFrame(update);
}

update();

关键修改点说明

  1. 画布尺寸固定:先给canvas设置明确的宽高,这样视口大小清晰,不会随页面变化。
  2. 偏移量计算viewportOffsetX = 屏幕中心X - 玩家X - 玩家一半宽度,这样玩家的中心刚好和屏幕中心对齐(如果玩家是正方形,减去一半尺寸是为了让玩家中心和屏幕中心重合)。
  3. 绘制顺序调整:先平移上下文,再绘制所有元素——这样静态物体和玩家都会跟着视口移动,而不是只平移玩家。
  4. 上下文状态管理:用save()restore()包裹平移操作,确保每次绘制完都回到初始状态,不会影响下一轮绘制。

额外优化:限制视口不超出游戏世界

如果你的游戏世界有边界(比如不能走到空白区域),可以给偏移量加个限制:

// 假设游戏世界的宽高是1000x800
const worldWidth = 1000;
const worldHeight = 800;

// 计算偏移量时加入边界限制
const viewportOffsetX = Math.max(
  0, // 左边界:视口不能超出世界左侧
  Math.min(
    worldWidth - canvas.width, // 右边界:视口不能超出世界右侧
    canvas.width / 2 - player.x - player.size / 2
  )
);

const viewportOffsetY = Math.max(
  0, // 上边界
  Math.min(
    worldHeight - canvas.height, // 下边界
    canvas.height / 2 - player.y - player.size / 2
  )
);

这样视口就不会跑到游戏世界外面去啦~

备注:内容来源于stack exchange,提问作者VkQuads

火山引擎 最新活动