🎮ArcadeLab

汉阳造步枪 - 精修版(无刺刀) + 摄像机系统

by TurboKoala83
357 lines11.1 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,
  y: canvas.height / 2,
  zoom: 1.0,
  rotation: 0,
  isDragging: false,
  dragButton: 0,
  lastX: 0,
  lastY: 0
};

// =====================================================================
// 汉阳造步枪 - 精修版(无刺刀)
// 原点 (0,0) 在【扳机位置】
// x 轴正方向为枪口方向(向右),y 轴正方向向下
// =====================================================================
function drawHanyangRifle(ctx) {
  
  // ---------------------------------------------------------
  // 1. 枪托后部(木质)- 根据照片精修的流畅曲线
  // ---------------------------------------------------------
  // 使用渐变增强木质立体感
  const stockGrad = ctx.createLinearGradient(0, -15, 0, 45);
  stockGrad.addColorStop(0, '#4A2E15');
  stockGrad.addColorStop(0.5, '#6B4A2A');
  stockGrad.addColorStop(1, '#3A2210');

  ctx.fillStyle = stockGrad;
  ctx.beginPath();
  ctx.moveTo(-25, -8);       // 顶部:连接机匣
  ctx.lineTo(-130, -5);      // 枪托上沿
  ctx.quadraticCurveTo(-160, 5, -155, 28); // 优美的向下弧度
  ctx.lineTo(-130, 38);      // 底部后段
  ctx.quadraticCurveTo(-60, 48, -35, 28);  // 底部:平滑的握把弧线
  ctx.lineTo(-25, 12);       // 底部:连接扳机护圈
  ctx.closePath();
  ctx.fill();

  // 木质高光
  ctx.fillStyle = 'rgba(255, 255, 255, 0.1)';
  ctx.beginPath();
  ctx.moveTo(-25, -8);
  ctx.lineTo(-130, -5);
  ctx.lineTo(-130, 0);
  ctx.lineTo(-25, -3);
  ctx.closePath();
  ctx.fill();

  // 握把底部的防滑纹理(对应照片中的握把凹槽)
  ctx.strokeStyle = 'rgba(0, 0, 0, 0.2)';
  ctx.lineWidth = 1;
  for (let i = 0; i < 3; i++) {
    ctx.beginPath();
    ctx.arc(-75, 32, 12 + i * 4, 0.2, Math.PI - 0.2);
    ctx.stroke();
  }

  // 枪托尾端的金属托底板
  ctx.fillStyle = '#2A2A2A';
  ctx.beginPath();
  ctx.moveTo(-155, 28);
  ctx.quadraticCurveTo(-165, 15, -160, -5);
  ctx.lineTo(-152, -5);
  ctx.quadraticCurveTo(-157, 15, -148, 30);
  ctx.closePath();
  ctx.fill();

  // ---------------------------------------------------------
  // 2. 下护木(木质)- 从扳机延伸到枪管前端
  // ---------------------------------------------------------
  const lowerGrad = ctx.createLinearGradient(0, -10, 0, 20);
  lowerGrad.addColorStop(0, '#6B4A2A');
  lowerGrad.addColorStop(0.5, '#8A5A3A');
  lowerGrad.addColorStop(1, '#4A2E15');

  ctx.fillStyle = lowerGrad;
  ctx.beginPath();
  ctx.moveTo(-25, -8);
  ctx.lineTo(170, -8);   // 下护木顶部
  ctx.lineTo(170, 15);   // 下护木底部
  ctx.lineTo(-25, 15);
  ctx.closePath();
  ctx.fill();

  // ---------------------------------------------------------
  // 3. 上护木(木质)- 覆盖枪管上方,中间留出缝隙
  // ---------------------------------------------------------
  const upperGrad = ctx.createLinearGradient(0, -20, 0, -8);
  upperGrad.addColorStop(0, '#4A2E15');
  upperGrad.addColorStop(1, '#6B4A2A');

  ctx.fillStyle = upperGrad;
  ctx.beginPath();
  ctx.moveTo(-25, -20);   // 上护木后段
  ctx.lineTo(150, -20);   // 上护木前端(比下护木短一点)
  ctx.lineTo(150, -8);
  ctx.lineTo(-25, -8);
  ctx.closePath();
  ctx.fill();

  // 上护木的立体阴影线
  ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
  ctx.fillRect(-25, -10, 175, 2);

  // ---------------------------------------------------------
  // 4. 枪管(金属)- 位于上下护木之间,向前延伸
  // ---------------------------------------------------------
  const barrelStart = 150;
  const barrelLength = 200;

  // 金属渐变(圆柱感)
  const metalGrad = ctx.createLinearGradient(0, -6, 0, 6);
  metalGrad.addColorStop(0, '#6A6A6A');
  metalGrad.addColorStop(0.5, '#9A9A9A');
  metalGrad.addColorStop(1, '#3A3A3A');

  ctx.fillStyle = metalGrad;
  ctx.fillRect(barrelStart, -6, barrelLength, 12);

  // 枪管顶部高光
  ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';
  ctx.fillRect(barrelStart, -5, barrelLength, 2);

  // ---------------------------------------------------------
  // 5. 金属枪箍(固定上下护木和枪管)
  // ---------------------------------------------------------
  const clampPositions = [80, 155];
  clampPositions.forEach(x => {
    ctx.fillStyle = '#333333';
    ctx.fillRect(x, -22, 10, 38); // 金属箍主体
    ctx.fillStyle = '#666666';
    ctx.fillRect(x, -22, 2, 38);  // 左侧高光
    ctx.fillStyle = '#111111';
    ctx.fillRect(x + 8, -22, 2, 38); // 右侧阴影
  });

  // ---------------------------------------------------------
  // 6. 机匣、枪栓与扳机(金属)
  // ---------------------------------------------------------
  // 机匣底座
  ctx.fillStyle = '#2A2A2A';
  ctx.fillRect(-25, -15, 40, 30);

  // 机匣顶部高光
  ctx.fillStyle = '#555555';
  ctx.fillRect(-25, -15, 40, 2);

  // 枪栓拉柄(向下弯曲的柄 + 球头)
  ctx.fillStyle = '#4A4A4A';
  ctx.beginPath();
  ctx.moveTo(-5, -12);
  ctx.quadraticCurveTo(-10, -28, -5, -32);
  ctx.lineTo(5, -28);
  ctx.quadraticCurveTo(0, -25, 3, -12);
  ctx.closePath();
  ctx.fill();

  // 球状拉柄头
  ctx.fillStyle = '#888888';
  ctx.beginPath();
  ctx.arc(0, -32, 7, 0, Math.PI * 2);
  ctx.fill();
  ctx.fillStyle = '#AAAAAA';
  ctx.beginPath();
  ctx.arc(-2, -34, 3, 0, Math.PI * 2);
  ctx.fill();

  // 扳机护圈(金属弧线)
  ctx.strokeStyle = '#555555';
  ctx.lineWidth = 3;
  ctx.beginPath();
  ctx.arc(0, 18, 12, 0, Math.PI);
  ctx.stroke();

  // 扳机
  ctx.fillStyle = '#333333';
  ctx.fillRect(-2, 12, 4, 14);

  // ---------------------------------------------------------
  // 7. 表尺照门(立框式表尺)
  // ---------------------------------------------------------
  ctx.fillStyle = '#333333';
  ctx.fillRect(35, -22, 15, 12); // 底座

  ctx.fillStyle = '#666666';
  ctx.fillRect(40, -40, 4, 18);  // 立框

  // 表尺游标
  ctx.fillStyle = '#999999';
  ctx.fillRect(37, -32, 10, 6);

  // 缺口
  ctx.fillStyle = '#1A1A1A';
  ctx.fillRect(41, -40, 2, 4);

  // ---------------------------------------------------------
  // 8. 枪口与准星(无刺刀)
  // ---------------------------------------------------------
  const muzzleX = barrelStart + barrelLength;

  // 枪口帽
  ctx.fillStyle = '#333333';
  ctx.fillRect(muzzleX - 5, -8, 5, 16);

  // 准星底座
  ctx.fillStyle = '#555555';
  ctx.fillRect(muzzleX - 20, -14, 8, 6);

  // 刀片状准星
  ctx.fillStyle = '#999999';
  ctx.beginPath();
  ctx.moveTo(muzzleX - 16, -14);
  ctx.lineTo(muzzleX - 12, -14);
  ctx.lineTo(muzzleX - 14, -22);
  ctx.closePath();
  ctx.fill();

  // ---------------------------------------------------------
  // 9. 枪背带(皮革,连接枪托和枪箍)
  // ---------------------------------------------------------
  // 前挂环
  ctx.fillStyle = '#222222';
  ctx.beginPath();
  ctx.arc(85, 16, 4, 0, Math.PI * 2);
  ctx.fill();

  // 后挂环
  ctx.beginPath();
  ctx.arc(-145, 38, 4, 0, Math.PI * 2);
  ctx.fill();

  // 背带主体
  ctx.strokeStyle = '#5A4A3A';
  ctx.lineWidth = 5;
  ctx.beginPath();
  ctx.moveTo(-145, 38);
  ctx.quadraticCurveTo(-50, 75, 20, 55); // 自然下垂的弧度
  ctx.lineTo(85, 16);
  ctx.stroke();

  // 背带边缘线(增加质感)
  ctx.strokeStyle = 'rgba(0,0,0,0.3)';
  ctx.lineWidth = 1;
  ctx.beginPath();
  ctx.moveTo(-145, 36);
  ctx.quadraticCurveTo(-50, 72, 20, 52);
  ctx.stroke();
}

// =====================================================================
// 主循环 + 摄像机变换
// =====================================================================
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);
  ctx.rotate(camera.rotation);
  ctx.scale(camera.zoom, camera.zoom);
  ctx.translate(-camera.x, -camera.y);

  // 在画布中央绘制枪械
  ctx.save();
  ctx.translate(canvas.width / 2 - 50, canvas.height / 2); // 原点居中偏左
  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;
  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();
  camera.zoom *= e.deltaY > 0 ? 0.9 : 1.1;
  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 (357 lines, 11.1 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-mup4jsee" to link back to the original. Then publish at arcadelab.ai/publish.