🎮ArcadeLab

红军战士 · 方块人

by EpicCoder88
390 lines11.0 KB
▶ Play
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>红军战士 · 方块人</title>
<style>
  * { margin: 0; padding: 0; box-sizing: border-box; }
  html, body {
    height: 100%;
    background: #0d0a08;
    overflow: hidden;
    font-family: system-ui, -apple-system, "PingFang SC", sans-serif;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  canvas {
    border-radius: 10px;
    box-shadow: 0 0 70px rgba(180, 40, 30, 0.22), 0 0 0 1px rgba(120, 60, 40, 0.3);
  }
  .label {
    position: fixed;
    bottom: 20px;
    left: 50%;
    transform: translateX(-50%);
    color: rgba(200, 150, 120, 0.35);
    font-size: 13px;
    letter-spacing: 0.4em;
    pointer-events: none;
  }
</style>
</head>
<body>
<canvas id="c" width="520" height="600"></canvas>
<div class="label">红 军 战 士</div>

<script>
(() => {
  'use strict';

  const canvas = document.getElementById('c');
  const ctx = canvas.getContext('2d');
  const W = 520, H = 600;

  const dpr = Math.min(window.devicePixelRatio || 1, 2);
  canvas.width = W * dpr;
  canvas.height = H * dpr;
  canvas.style.width = W + 'px';
  canvas.style.height = H + 'px';
  ctx.scale(dpr, dpr);

  // ==================== 颜色 ====================
  const C = {
    hat:    '#6a7a99',
    coat:   '#6a7078',
    pants:  '#555c64',
    wrap:   '#8a8578',
    shoe:   '#7a5f3a',
    skin:   '#e8b88a',
    star:   '#d02020',
    collar: '#d02020',
    belt:   '#3a2418',
    buckle: '#c8a040',
    gun:    '#2a2018',
    gunHi:  '#4a3828',
    wood:   '#6a4a28',
    woodHi: '#8a6040',
    eye:    '#1a1208',
    mouth:  '#a06848',
  };

  // ==================== 工具 ====================
  function shade(hex, amt) {
    const n = hex.replace('#', '');
    let r = parseInt(n.substr(0, 2), 16);
    let g = parseInt(n.substr(2, 2), 16);
    let b = parseInt(n.substr(4, 2), 16);
    r = Math.max(0, Math.min(255, Math.round(r + amt * 255)));
    g = Math.max(0, Math.min(255, Math.round(g + amt * 255)));
    b = Math.max(0, Math.min(255, Math.round(b + amt * 255)));
    return '#' + [r, g, b].map(v => v.toString(16).padStart(2, '0')).join('');
  }

  // ==================== 绘制立体方块 ====================
  // 斜投影:正面矩形 + 顶面 + 右侧面
  const DEPTH = 16;      // 深度像素
  const OX = DEPTH * 0.5; // 8
  const OY = -DEPTH * 0.5; // -8

  function drawBox(x, y, w, h, color) {
    // 顶面(最亮)
    ctx.fillStyle = shade(color, 0.2);
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.lineTo(x + w, y);
    ctx.lineTo(x + w + OX, y + OY);
    ctx.lineTo(x + OX, y + OY);
    ctx.closePath();
    ctx.fill();

    // 右侧面(最暗)
    ctx.fillStyle = shade(color, -0.22);
    ctx.beginPath();
    ctx.moveTo(x + w, y);
    ctx.lineTo(x + w + OX, y + OY);
    ctx.lineTo(x + w + OX, y + h + OY);
    ctx.lineTo(x + w, y + h);
    ctx.closePath();
    ctx.fill();

    // 正面(中等)
    ctx.fillStyle = color;
    ctx.fillRect(x, y, w, h);
  }

  // 五角星
  function drawStar(cx, cy, outer, inner, points, rot, color) {
    ctx.beginPath();
    for (let i = 0; i < points * 2; i++) {
      const r = i % 2 === 0 ? outer : inner;
      const a = rot + i * Math.PI / points - Math.PI / 2;
      const x = cx + Math.cos(a) * r;
      const y = cy + Math.sin(a) * r;
      if (i === 0) ctx.moveTo(x, y);
      else ctx.lineTo(x, y);
    }
    ctx.closePath();
    ctx.fillStyle = color;
    ctx.fill();
  }

  // ==================== 角色尺寸 ====================
  // Roblox R6 比例(放大版)
  const LEG_W = 30, LEG_H = 60;
  const TORSO_W = 60, TORSO_H = 60;
  const ARM_W = 30, ARM_H = 60;
  const HEAD_W = 60, HEAD_H = 30;
  const HAT_W = 66, HAT_H = 26;

  const CX = 260;          // 中心 x
  const GROUND_Y = 520;    // 脚底 y

  // ==================== 绘制角色 ====================
  function drawSoldier(pose, frame) {
    // ---- 动画偏移 ----
    let lLegY = 0, rLegY = 0, lArmY = 0, rArmY = 0;
    if (pose === 'walk') {
      const s = Math.sin(frame * 0.14);
      lLegY = Math.round(s * 4);
      rLegY = -Math.round(s * 4);
      lArmY = -Math.round(s * 3);
      rArmY = Math.round(s * 3);
    }

    // ---- 位置计算 ----
    const legY = GROUND_Y - LEG_H;
    const torsoY = legY - TORSO_H;
    const armY = torsoY;
    const headY = torsoY - HEAD_H;
    const hatY = headY - 12;

    const leftArmX = CX - TORSO_W / 2 - ARM_W;
    const rightArmX = CX + TORSO_W / 2;
    const leftLegX = CX - LEG_W;
    const rightLegX = CX;
    const torsoX = CX - TORSO_W / 2;
    const headX = CX - HEAD_W / 2;
    const hatX = CX - HAT_W / 2;

    // ---- 1. 左臂 ----
    drawBox(leftArmX, armY + lArmY, ARM_W, ARM_H, C.coat);

    // ---- 2. 右臂 ----
    drawBox(rightArmX, armY + rArmY, ARM_W, ARM_H, C.coat);

    // ---- 3. 左腿 ----
    drawBox(leftLegX, legY + lLegY, LEG_W, LEG_H, C.pants);
    // 绑腿(左腿正面横条纹)
    for (let i = 0; i < 5; i++) {
      const wy = legY + lLegY + 32 + i * 5;
      ctx.fillStyle = i % 2 === 0 ? C.wrap : shade(C.wrap, -0.15);
      ctx.fillRect(leftLegX, wy, LEG_W, 3);
    }
    // 草鞋(左)
    ctx.fillStyle = C.shoe;
    ctx.fillRect(leftLegX - 2, legY + lLegY + LEG_H - 6, LEG_W + 4, 6);
    ctx.fillStyle = shade(C.shoe, -0.15);
    ctx.fillRect(leftLegX - 2, legY + lLegY + LEG_H - 3, LEG_W + 4, 3);

    // ---- 4. 右腿 ----
    drawBox(rightLegX, legY + rLegY, LEG_W, LEG_H, C.pants);
    for (let i = 0; i < 5; i++) {
      const wy = legY + rLegY + 32 + i * 5;
      ctx.fillStyle = i % 2 === 0 ? C.wrap : shade(C.wrap, -0.15);
      ctx.fillRect(rightLegX, wy, LEG_W, 3);
    }
    ctx.fillStyle = C.shoe;
    ctx.fillRect(rightLegX - 2, legY + rLegY + LEG_H - 6, LEG_W + 4, 6);
    ctx.fillStyle = shade(C.shoe, -0.15);
    ctx.fillRect(rightLegX - 2, legY + rLegY + LEG_H - 3, LEG_W + 4, 3);

    // ---- 5. 躯干 ----
    drawBox(torsoX, torsoY, TORSO_W, TORSO_H, C.coat);

    // 领章
    ctx.fillStyle = C.collar;
    ctx.fillRect(torsoX + 10, torsoY + 4, 12, 10);
    ctx.fillRect(torsoX + TORSO_W - 22, torsoY + 4, 12, 10);
    // 领章高光
    ctx.fillStyle = shade(C.collar, 0.2);
    ctx.fillRect(torsoX + 10, torsoY + 4, 12, 3);
    ctx.fillRect(torsoX + TORSO_W - 22, torsoY + 4, 12, 3);

    // 口袋
    ctx.fillStyle = shade(C.coat, -0.08);
    ctx.fillRect(torsoX + 8, torsoY + 22, 16, 14);
    ctx.fillRect(torsoX + TORSO_W - 24, torsoY + 22, 16, 14);
    // 口袋盖
    ctx.fillStyle = shade(C.coat, -0.18);
    ctx.fillRect(torsoX + 8, torsoY + 22, 16, 3);
    ctx.fillRect(torsoX + TORSO_W - 24, torsoY + 22, 16, 3);

    // 腰带
    ctx.fillStyle = C.belt;
    ctx.fillRect(torsoX, torsoY + TORSO_H - 12, TORSO_W, 10);
    // 铜扣
    ctx.fillStyle = C.buckle;
    ctx.fillRect(torsoX + TORSO_W / 2 - 5, torsoY + TORSO_H - 11, 10, 8);
    ctx.fillStyle = shade(C.buckle, 0.2);
    ctx.fillRect(torsoX + TORSO_W / 2 - 5, torsoY + TORSO_H - 11, 10, 3);

    // 躯干正面暗面(右侧)
    ctx.fillStyle = shade(C.coat, -0.06);
    ctx.fillRect(torsoX + TORSO_W - 4, torsoY, 4, TORSO_H);

    // ---- 6. 头 ----
    drawBox(headX, headY, HEAD_W, HEAD_H, C.skin);

    // 眼睛
    ctx.fillStyle = C.eye;
    ctx.fillRect(headX + 14, headY + 10, 6, 6);
    ctx.fillRect(headX + HEAD_W - 20, headY + 10, 6, 6);
    // 眼睛高光
    ctx.fillStyle = 'rgba(255,255,255,0.5)';
    ctx.fillRect(headX + 14, headY + 10, 2, 2);
    ctx.fillRect(headX + HEAD_W - 20, headY + 10, 2, 2);

    // 嘴
    ctx.fillStyle = C.mouth;
    ctx.fillRect(headX + HEAD_W / 2 - 4, headY + 21, 8, 3);

    // 下巴暗面
    ctx.fillStyle = shade(C.skin, -0.08);
    ctx.fillRect(headX + 4, headY + HEAD_H - 4, HEAD_W - 8, 4);

    // ---- 7. 军帽 ----
    drawBox(hatX, hatY, HAT_W, HAT_H, C.hat);

    // 帽檐(正面下方深色带)
    ctx.fillStyle = shade(C.hat, -0.28);
    ctx.fillRect(hatX, hatY + HAT_H - 6, HAT_W, 6);
    // 帽檐高光
    ctx.fillStyle = shade(C.hat, -0.1);
    ctx.fillRect(hatX, hatY + HAT_H - 6, HAT_W, 2);

    // 五角星
    drawStar(
      hatX + HAT_W / 2,
      hatY + HAT_H / 2 - 2,
      9, 4, 5, -Math.PI / 2,
      C.star
    );
    // 五角星高光
    drawStar(
      hatX + HAT_W / 2 - 1,
      hatY + HAT_H / 2 - 3,
      4, 1.8, 5, -Math.PI / 2,
      'rgba(255,255,255,0.35)'
    );

    // ---- 8. 步枪 ----
    if (pose === 'aim') {
      // 持枪:斜跨在胸前
      ctx.save();
      ctx.translate(CX, torsoY + 30);
      ctx.rotate(-0.35);

      // 枪管
      ctx.fillStyle = C.gun;
      ctx.fillRect(-45, -4, 70, 6);
      // 枪管高光
      ctx.fillStyle = C.gunHi;
      ctx.fillRect(-45, -4, 70, 2);
      // 枪托
      ctx.fillStyle = C.wood;
      ctx.fillRect(20, -8, 24, 14);
      ctx.fillStyle = C.woodHi;
      ctx.fillRect(20, -8, 24, 3);
      // 枪机
      ctx.fillStyle = shade(C.gun, 0.1);
      ctx.fillRect(5, -8, 8, 5);

      ctx.restore();

      // 双手前伸
      ctx.fillStyle = C.skin;
      ctx.fillRect(CX - 10, torsoY + 38, 12, 10);
      ctx.fillRect(CX + 10, torsoY + 42, 12, 10);

    } else {
      // 站立/行走:步枪竖在右侧
      const gunX = rightArmX + ARM_W + 2;
      const gunY = armY + 10;

      // 枪管
      ctx.fillStyle = C.gun;
      ctx.fillRect(gunX, gunY, 6, 70);
      ctx.fillStyle = C.gunHi;
      ctx.fillRect(gunX, gunY, 2, 70);
      // 枪托
      ctx.fillStyle = C.wood;
      ctx.fillRect(gunX - 2, gunY + 60, 10, 22);
      ctx.fillStyle = C.woodHi;
      ctx.fillRect(gunX - 2, gunY + 60, 10, 4);
      // 枪机
      ctx.fillStyle = shade(C.gun, 0.12);
      ctx.fillRect(gunX - 2, gunY + 35, 8, 5);
    }
  }

  // ==================== 主循环 ====================
  let frame = 0;

  function loop() {
    requestAnimationFrame(loop);
    frame++;

    // 清屏
    ctx.clearRect(0, 0, W, H);

    // 背景
    const g = ctx.createLinearGradient(0, 0, 0, H);
    g.addColorStop(0, '#12100d');
    g.addColorStop(0.5, '#1a1613');
    g.addColorStop(1, '#12100d');
    ctx.fillStyle = g;
    ctx.fillRect(0, 0, W, H);

    // 背景网格
    ctx.strokeStyle = 'rgba(80, 50, 40, 0.1)';
    ctx.lineWidth = 1;
    for (let x = 0; x <= W; x += 40) {
      ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, H); ctx.stroke();
    }
    for (let y = 0; y <= H; y += 40) {
      ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(W, y); ctx.stroke();
    }

    // 地面
    ctx.fillStyle = '#1a1410';
    ctx.fillRect(0, GROUND_Y, W, H - GROUND_Y);
    ctx.fillStyle = 'rgba(120, 80, 60, 0.15)';
    ctx.fillRect(0, GROUND_Y, W, 3);

    // 地面阴影
    ctx.fillStyle = 'rgba(0,0,0,0.35)';
    ctx.beginPath();
    ctx.ellipse(CX, GROUND_Y + 6, 80, 16, 0, 0, Math.PI * 2);
    ctx.fill();

    // 姿势轮播
    const poseIndex = Math.floor(frame / 140) % 3;
    const poses = ['stand', 'walk', 'aim'];
    const pose = poses[poseIndex];

    drawSoldier(pose, frame);

    // 姿势名称
    ctx.fillStyle = 'rgba(200, 150, 120, 0.25)';
    ctx.font = '13px system-ui, sans-serif';
    ctx.textAlign = 'center';
    ctx.fillText(pose.toUpperCase(), CX, H - 16);
  }

  loop();
})();
</script>
</body>
</html>

Game Source: 红军战士 · 方块人

Creator: EpicCoder88

Libraries: none

Complexity: complex (390 lines, 11.0 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-epiccoder88-murvydtv" to link back to the original. Then publish at arcadelab.ai/publish.