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

JavaScript 2D数组瓦片地图Canvas滚动实现技术咨询

实现Canvas瓦片地图滚动(固定显示3x3区域)

这个需求的核心是反向滚动逻辑:我们不让玩家在Canvas里移动,而是让玩家始终固定在画布中心,通过调整地图的绘制偏移量,来营造玩家在地图中行走的视觉错觉。同时只绘制玩家周围的3x3瓦片,既保证性能,又严格符合你要的显示范围。

核心思路拆解

  • 定义瓦片大小:800x800的Canvas要显示3x3瓦片,所以每个瓦片的尺寸是 800/3 ≈ 266.67px,刚好填满画布。
  • 玩家位置用瓦片坐标表示(比如初始在地图的(1,1)位置,对应tileMap里的空地)。
  • 地图偏移量:通过计算偏移量,让玩家所在的瓦片始终处于Canvas中心,绘制地图时加上这个偏移量,就实现了地图滚动。
  • 只绘制玩家周围3x3的瓦片:循环遍历[玩家X-1, 玩家X+1][玩家Y-1, 玩家Y+1]的瓦片范围,避免绘制整个大地图。

完整实现代码

<canvas id='draw' width=800 height=800 style="border:1px solid #000;"></canvas>

<script>
// 1. 基础变量定义
const canvas = document.getElementById('draw');
const ctx = canvas.getContext('2d');
const tileSize = canvas.width / 3; // 每个瓦片的尺寸,刚好填满3x3区域
const tileMap = [
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
];

// 玩家的瓦片坐标(初始在地图第二行第二列的空地)
let playerTileX = 1;
let playerTileY = 1;

// 地图绘制偏移量,用来控制滚动
let offsetX = (canvas.width/2) - (playerTileX * tileSize);
let offsetY = (canvas.height/2) - (playerTileY * tileSize);

// 2. 绘制地图的函数
function drawMap() {
  // 清空画布
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  
  // 只绘制玩家周围3x3的瓦片:从playerTileX-1到playerTileX+1,Y同理
  for (let y = playerTileY - 1; y <= playerTileY + 1; y++) {
    for (let x = playerTileX - 1; x <= playerTileX + 1; x++) {
      // 检查瓦片是否在地图范围内
      if (y >= 0 && y < tileMap.length && x >=0 && x < tileMap[y].length) {
        const tileType = tileMap[y][x];
        // 设置瓦片颜色:0=灰色障碍物,1=白色空地
        ctx.fillStyle = tileType === 0 ? '#666' : '#fff';
        // 计算瓦片在Canvas上的实际绘制位置:瓦片坐标*尺寸 + 偏移量
        const drawX = x * tileSize + offsetX;
        const drawY = y * tileSize + offsetY;
        ctx.fillRect(drawX, drawY, tileSize, tileSize);
        
        // 可选:绘制瓦片边框,方便观察
        ctx.strokeStyle = '#333';
        ctx.strokeRect(drawX, drawY, tileSize, tileSize);
      }
    }
  }
  
  // 绘制玩家(固定在Canvas中心)
  ctx.fillStyle = '#ff0000';
  ctx.beginPath();
  ctx.arc(canvas.width/2, canvas.height/2, tileSize/4, 0, Math.PI*2);
  ctx.fill();
}

// 3. 处理玩家移动的函数
function movePlayer(dx, dy) {
  // 计算新的玩家瓦片坐标
  const newTileX = playerTileX + dx;
  const newTileY = playerTileY + dy;
  
  // 检查新位置是否是空地(1),且在地图范围内
  if (
    newTileY >= 0 && newTileY < tileMap.length &&
    newTileX >=0 && newTileX < tileMap[newTileY].length &&
    tileMap[newTileY][newTileX] === 1
  ) {
    // 更新玩家的瓦片坐标
    playerTileX = newTileX;
    playerTileY = newTileY;
    
    // 重新计算地图偏移量:让玩家所在瓦片回到中心
    offsetX = (canvas.width/2) - (playerTileX * tileSize);
    offsetY = (canvas.height/2) - (playerTileY * tileSize);
    
    // 重绘地图
    drawMap();
  }
}

// 4. 绑定键盘事件,监听方向键
document.addEventListener('keydown', (e) => {
  switch(e.key) {
    case 'ArrowLeft':
      movePlayer(-1, 0);
      break;
    case 'ArrowRight':
      movePlayer(1, 0);
      break;
    case 'ArrowUp':
      movePlayer(0, -1);
      break;
    case 'ArrowDown':
      movePlayer(0, 1);
      break;
  }
});

// 初始绘制地图
drawMap();
</script>

关键逻辑解释

  1. 偏移量计算offsetX = (canvas.width/2) - (playerTileX * tileSize) 这个公式的作用是,让玩家所在的瓦片的中心点,刚好和Canvas的中心点重合。当玩家的瓦片坐标变化时,偏移量也跟着变化,地图就会向相反方向滚动。
  2. 3x3瓦片绘制:我们只遍历玩家周围的3个瓦片范围,避免了绘制整个17x3的大地图,性能更优。
  3. 移动判断:每次移动前先检查目标位置是否是空地(tileMap[newY][newX] === 1),同时确保不超出地图边界,避免错误。

你可以直接运行这段代码,按方向键就能看到效果:红色的玩家始终在画布中心,地图随着按键左右滚动,始终只显示玩家周围的3x3瓦片区域,完全符合你的需求。

内容的提问来源于stack exchange,提问作者mnjq2006

火山引擎 最新活动