🎮ArcadeLab

汉阳造步枪 - 真实比例 + 摄像机系统

by TurboKoala83
377 lines11.8 KB
▶ Play
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>汉阳造步枪 - 真实比例 + 摄像机系统</title>
<style>
  body { margin: 0; background: #1a1a2e; display: flex; justify-content: center; align-items: center; height: 100vh; overflow: hidden; font-family: 'Microsoft YaHei', sans-serif; }
  canvas { background: linear-gradient(180deg, #16213e 0%, #0f3460 100%); border-radius: 12px; box-shadow: 0 0 40px rgba(196, 30, 58, 0.3); cursor: grab; }
  canvas:active { cursor: grabbing; }
  .info { position: absolute; top: 20px; left: 50%; transform: translateX(-50%); color: #c41e3a; font-size: 14px; letter-spacing: 4px; text-shadow: 0 0 10px rgba(196, 30, 58, 0.8); pointer-events: none; }
  .controls { position: absolute; bottom: 20px; color: #888; font-size: 12px; letter-spacing: 2px; text-align: center; width: 100%; pointer-events: none; }
</style>
</head>
<body>
<div class="info">★ 汉阳造步枪 · 真实比例 + 全方位观察 ★</div>
<div class="controls">左键拖拽平移 | 右键拖拽旋转 | 滚轮缩放 | 双击重置视角</div>
<canvas id="game" width="900" height="600"></canvas>

<script>
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');

// ========== 摄像机系统 ==========
const camera = {
  x: canvas.width / 2,   // 摄像机中心 X
  y: canvas.height / 2,  // 摄像机中心 Y
  zoom: 1.0,             // 缩放比例
  rotation: 0,           // 旋转角度(弧度)
  isDragging: false,
  dragButton: 0,         // 0: 左键平移, 2: 右键旋转
  lastX: 0,
  lastY: 0
};

// ========== 枪械配色 ==========
const GUN_COLORS = {
  wood:       '#5E3A20', // 枪托木质主色(深栗色)
  woodDark:   '#3A2210', // 木质阴影
  woodLite:   '#8A5A3A', // 木质高光
  metal:      '#4A4A4A', // 金属主色
  metalDark:  '#2A2A2A', // 金属阴影
  metalLite:  '#9A9A9A', // 金属高光
  strap:      '#6A5A3A', // 枪背带(皮革/帆布)
};

// ========== 绘制工具 ==========
function roundRect(ctx, x, y, w, h, r) {
  ctx.beginPath();
  ctx.moveTo(x + r, y);
  ctx.lineTo(x + w - r, y);
  ctx.quadraticCurveTo(x + w, y, x + w, y + r);
  ctx.lineTo(x + w, y + h - r);
  ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
  ctx.lineTo(x + r, y + h);
  ctx.quadraticCurveTo(x, y + h, x, y + h - r);
  ctx.lineTo(x, y + r);
  ctx.quadraticCurveTo(x, y, x + r, y);
  ctx.closePath();
}

// =====================================================================
// 汉阳造步枪 - 真实比例建模(基于照片结构)
// 原点 (0,0) 在【扳机位置】
// x 轴正方向为枪口方向(向右),y 轴正方向向下
// =====================================================================
function drawHanyangRifle(ctx) {
  const C = GUN_COLORS;

  // ---------------------------------------------------------
  // 1. 枪托后部(木质)- 带有优雅的向下弯曲
  // ---------------------------------------------------------
  ctx.fillStyle = C.wood;
  ctx.beginPath();
  ctx.moveTo(-30, -8);       // 顶部靠前(机匣后方)
  ctx.lineTo(-150, -5);      // 顶部靠后(枪托上沿)
  ctx.quadraticCurveTo(-170, 5, -165, 25); // 枪托后部弧形向下
  ctx.lineTo(-140, 35);      // 枪托下沿后部
  ctx.quadraticCurveTo(-80, 45, -40, 25); // 底部平滑弧线(握把处)
  ctx.lineTo(-30, 10);       // 底部靠前
  ctx.closePath();
  ctx.fill();

  // 枪托木质纹理与立体感
  ctx.fillStyle = C.woodDark;
  ctx.fillRect(-160, -5, 120, 3); // 顶部阴影
  ctx.fillStyle = C.woodLite;
  ctx.fillRect(-160, 30, 100, 2);  // 底部高光

  // 枪托后部的金属托底板
  ctx.fillStyle = C.metalDark;
  ctx.beginPath();
  ctx.moveTo(-165, 25);
  ctx.quadraticCurveTo(-175, 10, -170, -5);
  ctx.lineTo(-162, -5);
  ctx.quadraticCurveTo(-168, 10, -160, 30);
  ctx.closePath();
  ctx.fill();

  // 握把凹陷处(更符合人体工学)
  ctx.fillStyle = C.woodDark;
  ctx.beginPath();
  ctx.arc(-70, 32, 15, 0, Math.PI, true);
  ctx.fill();

  // ---------------------------------------------------------
  // 2. 机匣与枪栓(金属结构)
  // ---------------------------------------------------------
  // 机匣底座
  ctx.fillStyle = C.metalDark;
  ctx.fillRect(-30, -12, 50, 24);

  // 枪栓拉柄 - 直拉栓 + 向下弯曲的柄
  ctx.fillStyle = C.metal;
  ctx.beginPath();
  ctx.moveTo(-10, -10);
  ctx.lineTo(-15, -25);
  ctx.lineTo(-5, -25);
  ctx.lineTo(0, -10);
  ctx.closePath();
  ctx.fill();

  // 枪栓球状把手
  ctx.fillStyle = C.metalLite;
  ctx.beginPath();
  ctx.arc(-10, -25, 6, 0, Math.PI * 2);
  ctx.fill();
  ctx.fillStyle = C.metal;
  ctx.beginPath();
  ctx.arc(-9, -26, 3, 0, Math.PI * 2);
  ctx.fill();

  // 扳机护圈
  ctx.strokeStyle = C.metal;
  ctx.lineWidth = 3;
  ctx.beginPath();
  ctx.arc(0, 15, 10, 0, Math.PI);
  ctx.stroke();

  // 扳机
  ctx.fillStyle = C.metalDark;
  ctx.fillRect(-2, 10, 4, 12);

  // ---------------------------------------------------------
  // 3. 枪身中部 - 木质前护木(包裹枪管)
  // ---------------------------------------------------------
  ctx.fillStyle = C.wood;
  ctx.beginPath();
  ctx.moveTo(-30, -12);
  ctx.lineTo(180, -12);   // 护木顶部延伸到枪管箍
  ctx.lineTo(180, 12);    // 护木底部
  ctx.lineTo(-30, 12);
  ctx.closePath();
  ctx.fill();

  // 护木立体感
  ctx.fillStyle = C.woodLite;
  ctx.fillRect(-30, -12, 210, 3); // 顶部高光
  ctx.fillStyle = C.woodDark;
  ctx.fillRect(-30, 9, 210, 3);   // 底部阴影

  // 护木上的金属箍(老式步枪特征,固定护木)
  ctx.fillStyle = C.metalDark;
  ctx.fillRect(80, -14, 8, 28);  // 前箍
  ctx.fillRect(160, -14, 8, 28); // 后箍

  // ---------------------------------------------------------
  // 4. 枪管(金属)- 从护木前端伸出
  // ---------------------------------------------------------
  const barrelStart = 180;
  const barrelLength = 220;

  ctx.fillStyle = C.metal;
  ctx.fillRect(barrelStart, -7, barrelLength, 14);

  // 枪管高光与阴影(圆柱感)
  ctx.fillStyle = C.metalLite;
  ctx.fillRect(barrelStart, -6, barrelLength, 3);
  ctx.fillStyle = C.metalDark;
  ctx.fillRect(barrelStart, 5, barrelLength, 2);

  // ---------------------------------------------------------
  // 5. 表尺照门(汉阳造特征:立框式表尺)
  // ---------------------------------------------------------
  ctx.fillStyle = C.metalDark;
  ctx.fillRect(20, -20, 15, 10); // 表尺底座

  ctx.fillStyle = C.metal;
  ctx.fillRect(25, -35, 4, 15);  // 表尺立框

  // 表尺游标
  ctx.fillStyle = C.metalLite;
  ctx.fillRect(22, -30, 10, 6);

  // 照门缺口
  ctx.fillStyle = '#1A1A1A';
  ctx.fillRect(26, -35, 2, 4);

  // ---------------------------------------------------------
  // 6. 枪口与准星
  // ---------------------------------------------------------
  const muzzleX = barrelStart + barrelLength;

  // 枪口
  ctx.fillStyle = C.metalDark;
  ctx.fillRect(muzzleX - 8, -9, 8, 18);

  // 准星底座
  ctx.fillStyle = C.metal;
  ctx.fillRect(muzzleX - 25, -18, 10, 10);

  // 刀片形准星
  ctx.fillStyle = C.metalLite;
  ctx.beginPath();
  ctx.moveTo(muzzleX - 20, -18);
  ctx.lineTo(muzzleX - 15, -18);
  ctx.lineTo(muzzleX - 17.5, -28);
  ctx.closePath();
  ctx.fill();

  // ---------------------------------------------------------
  // 7. 刺刀(带刀柄和卡扣结构)
  // ---------------------------------------------------------
  const bayonetLength = 110;

  // 刺刀座(连接枪口)
  ctx.fillStyle = C.metalDark;
  ctx.fillRect(muzzleX - 4, -5, 10, 14);

  // 刺刀刀柄(木质/金属)
  ctx.fillStyle = C.wood;
  ctx.fillRect(muzzleX + 6, -4, 30, 10);
  ctx.fillStyle = C.metalDark;
  ctx.fillRect(muzzleX + 36, -4, 10, 10); // 刀格

  // 刺刀刀身
  ctx.fillStyle = C.metalLite;
  ctx.beginPath();
  ctx.moveTo(muzzleX + 46, -3);
  ctx.lineTo(muzzleX + bayonetLength, -1);
  ctx.lineTo(muzzleX + bayonetLength + 5, 0);
  ctx.lineTo(muzzleX + bayonetLength, 1);
  ctx.lineTo(muzzleX + 46, 3);
  ctx.closePath();
  ctx.fill();

  // 血槽
  ctx.fillStyle = '#FFFFFF';
  ctx.fillRect(muzzleX + 55, -1, bayonetLength - 65, 1);

  // 刀柄上的固定环
  ctx.fillStyle = C.metalDark;
  ctx.fillRect(muzzleX + 15, -7, 4, 15);

  // ---------------------------------------------------------
  // 8. 枪背带(帆布/皮革,自然下垂)
  // ---------------------------------------------------------
  ctx.strokeStyle = C.strap;
  ctx.lineWidth = 5;
  ctx.beginPath();
  ctx.moveTo(-140, 35); // 从枪托后部挂载
  ctx.quadraticCurveTo(-50, 70, 50, 40); // 自然下垂
  ctx.lineTo(160, 12);   // 连接到前护木箍
  ctx.stroke();

  // 背带环
  ctx.fillStyle = C.metalDark;
  ctx.beginPath();
  ctx.arc(-140, 35, 4, 0, Math.PI * 2);
  ctx.fill();
  ctx.beginPath();
  ctx.arc(160, 12, 4, 0, Math.PI * 2);
  ctx.fill();
}

// =====================================================================
// 主循环 + 摄像机变换
// =====================================================================
function animate() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // 背景网格(用于参照平移和旋转)
  ctx.strokeStyle = 'rgba(255, 255, 255, 0.05)';
  ctx.lineWidth = 1;
  for (let i = 0; i < canvas.width; i += 60) {
    ctx.beginPath(); ctx.moveTo(i, 0); ctx.lineTo(i, canvas.height); ctx.stroke();
  }
  for (let i = 0; i < canvas.height; i += 60) {
    ctx.beginPath(); ctx.moveTo(0, i); ctx.lineTo(canvas.width, i); ctx.stroke();
  }

  // ===== 应用摄像机变换 =====
  ctx.save();
  ctx.translate(camera.x, camera.y);      // 1. 平移到摄像机中心
  ctx.rotate(camera.rotation);            // 2. 旋转
  ctx.scale(camera.zoom, camera.zoom);    // 3. 缩放
  ctx.translate(-camera.x, -camera.y);    // 4. 偏移回来,确保在摄像机中心缩放旋转

  // 在画布中央绘制枪械
  const centerX = canvas.width / 2;
  const centerY = canvas.height / 2;
  
  ctx.save();
  ctx.translate(centerX - 60, centerY); // 将枪械原点偏移到屏幕中间偏左
  drawHanyangRifle(ctx);
  ctx.restore();

  ctx.restore();

  // ===== 绘制 UI 信息 =====
  ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
  ctx.font = '12px sans-serif';
  ctx.fillText(`缩放: ${camera.zoom.toFixed(2)}x | 旋转: ${(camera.rotation * 180 / Math.PI).toFixed(0)}° | 平移: (${camera.x.toFixed(0)}, ${camera.y.toFixed(0)})`, 20, 30);

  requestAnimationFrame(animate);
}

// =====================================================================
// 交互事件监听(拖拽平移、右键旋转、滚轮缩放)
// =====================================================================
canvas.addEventListener('contextmenu', e => e.preventDefault()); // 禁用右键菜单

canvas.addEventListener('mousedown', (e) => {
  camera.isDragging = true;
  camera.dragButton = e.button; // 0: 左键, 2: 右键
  camera.lastX = e.clientX;
  camera.lastY = e.clientY;
});

canvas.addEventListener('mousemove', (e) => {
  if (!camera.isDragging) return;

  const dx = e.clientX - camera.lastX;
  const dy = e.clientY - camera.lastY;

  if (camera.dragButton === 0) {
    // 左键:平移
    camera.x += dx;
    camera.y += dy;
  } else if (camera.dragButton === 2) {
    // 右键:旋转
    camera.rotation += dx * 0.005;
  }

  camera.lastX = e.clientX;
  camera.lastY = e.clientY;
});

canvas.addEventListener('mouseup', () => {
  camera.isDragging = false;
});

canvas.addEventListener('mouseleave', () => {
  camera.isDragging = false;
});

// 滚轮缩放
canvas.addEventListener('wheel', (e) => {
  e.preventDefault();
  const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
  camera.zoom *= zoomFactor;
  // 限制缩放范围
  camera.zoom = Math.max(0.2, Math.min(5.0, camera.zoom));
}, { passive: false });

// 双击重置视角
canvas.addEventListener('dblclick', () => {
  camera.x = canvas.width / 2;
  camera.y = canvas.height / 2;
  camera.zoom = 1.0;
  camera.rotation = 0;
});

// 启动主循环
animate();
</script>
</body>
</html>

Game Source: 汉阳造步枪 - 真实比例 + 摄像机系统

Creator: TurboKoala83

Libraries: none

Complexity: complex (377 lines, 11.8 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: game-turbokoala83-mup4h0cx" to link back to the original. Then publish at arcadelab.ai/publish.