🎮ArcadeLab

红军战士 - Roblox 比例建模

by TurboKoala83
395 lines10.6 KB
▶ Play
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>红军战士 - Roblox 比例建模</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);
  }
  .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);
  }
</style>
</head>
<body>
<div class="info">★ 中国工农红军 · 湘江战役后 ★</div>
<canvas id="game" width="500" height="600"></canvas>

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

// ========== 配色 ==========
const C = {
  uniform:    '#5A6B7C', // 灰蓝军装主色
  uniformDark:'#3D4A57', // 军装阴影
  uniformLite:'#6E8090', // 军装高光
  collar:     '#C41E3A', // 红领章 / 红五星
  skin:       '#E8B88A', // 肤色
  skinDark:   '#C99A6E', // 肤色阴影
  hat:        '#4A5A6B', // 八角帽
  hatDark:    '#37444F', // 帽檐阴影
  bind:       '#8A8A8A', // 绑腿
  straw:      '#9A7A4A', // 草鞋
  strawDark:  '#7A5A2A', // 草鞋深色
  wood:       '#5A3A1A', // 枪托木色
  metal:      '#4A4A4A', // 枪管金属
  belt:       '#3A2A1A', // 皮带
  strap:      '#8B7355', // 挎包带
};

// ========== Roblox R6 比例尺寸 ==========
// 总高约 210px,头大身子短,方块感强
const S = {
  headW: 52, headH: 52,
  torsoW: 84, torsoH: 76,
  armW: 36, armH: 76,
  legW: 44, legH: 84,
  gap: 2, // 肢体之间的缝隙
};

// ========== 绘制工具 ==========
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();
}

// ========== 核心:绘制红军战士 ==========
function drawSoldier(ctx, cx, groundY, scale = 1, bob = 0) {
  ctx.save();
  ctx.translate(cx, groundY);
  ctx.scale(scale, scale);
  ctx.translate(0, bob); // 呼吸浮动

  const headW = S.headW, headH = S.headH;
  const torsoW = S.torsoW, torsoH = S.torsoH;
  const armW = S.armW, armH = S.armH;
  const legW = S.legW, legH = S.legH;

  // 坐标计算:从地面往上算
  const legTopY    = -legH;              // -84
  const torsoTopY  = legTopY - torsoH;   // -160
  const headTopY   = torsoTopY - headH;  // -212
  const centerX    = 0;

  // ===== 1. 腿部(两条方块腿 + 绑腿 + 草鞋)=====
  const legOffset = torsoW / 4; // 两腿间距
  [-1, 1].forEach(side => {
    const lx = centerX + side * legOffset - legW / 2;
    const ly = legTopY;

    // 裤子主体(灰蓝)
    ctx.fillStyle = C.uniform;
    roundRect(ctx, lx, ly, legW, legH * 0.55, 3);
    ctx.fill();

    // 裤子阴影
    ctx.fillStyle = C.uniformDark;
    ctx.fillRect(lx, ly, 6, legH * 0.55);

    // 绑腿(从膝盖到脚踝,螺旋缠绕感用横线条表现)
    const bindY = ly + legH * 0.5;
    const bindH = legH * 0.42;
    ctx.fillStyle = C.bind;
    roundRect(ctx, lx - 1, bindY, legW + 2, bindH, 2);
    ctx.fill();

    // 绑腿横线(模拟缠绕)
    ctx.strokeStyle = '#6A6A6A';
    ctx.lineWidth = 1.2;
    for (let i = 0; i < 5; i++) {
      const yy = bindY + 4 + i * (bindH - 8) / 4;
      ctx.beginPath();
      ctx.moveTo(lx - 1, yy);
      ctx.lineTo(lx + legW + 1, yy);
      ctx.stroke();
    }

    // 草鞋
    const shoeY = bindY + bindH;
    const shoeH = legH * 0.08;
    ctx.fillStyle = C.straw;
    roundRect(ctx, lx - 3, shoeY, legW + 6, shoeH, 3);
    ctx.fill();

    // 草鞋纹理
    ctx.strokeStyle = C.strawDark;
    ctx.lineWidth = 1;
    for (let i = 0; i < 3; i++) {
      ctx.beginPath();
      ctx.moveTo(lx - 3, shoeY + 2 + i * 2);
      ctx.lineTo(lx + legW + 3, shoeY + 2 + i * 2);
      ctx.stroke();
    }
  });

  // ===== 2. 躯干(灰蓝军装 + 红领章 + 腰带 + 挎包带)=====
  const torsoX = centerX - torsoW / 2;

  // 军装主体
  ctx.fillStyle = C.uniform;
  roundRect(ctx, torsoX, torsoTopY, torsoW, torsoH, 4);
  ctx.fill();

  // 左侧阴影(立体感)
  ctx.fillStyle = C.uniformDark;
  ctx.fillRect(torsoX, torsoTopY, 8, torsoH);

  // 右侧高光
  ctx.fillStyle = C.uniformLite;
  ctx.fillRect(torsoX + torsoW - 6, torsoTopY, 6, torsoH);

  // 红领章(左右各一个)
  ctx.fillStyle = C.collar;
  ctx.fillRect(torsoX + 12, torsoTopY + 2, 14, 8);
  ctx.fillRect(torsoX + torsoW - 26, torsoTopY + 2, 14, 8);

  // 中缝(衣服中间的开口线)
  ctx.strokeStyle = C.uniformDark;
  ctx.lineWidth = 1.5;
  ctx.beginPath();
  ctx.moveTo(centerX, torsoTopY + 12);
  ctx.lineTo(centerX, torsoTopY + torsoH - 4);
  ctx.stroke();

  // 腰带(深棕色)
  const beltY = torsoTopY + torsoH * 0.55;
  ctx.fillStyle = C.belt;
  ctx.fillRect(torsoX, beltY, torsoW, 7);

  // 腰带扣(黄铜色)
  ctx.fillStyle = '#B8963A';
  ctx.fillRect(centerX - 5, beltY + 1, 10, 5);

  // 斜挎的挎包带(从左肩到右腰)
  ctx.strokeStyle = C.strap;
  ctx.lineWidth = 4;
  ctx.beginPath();
  ctx.moveTo(torsoX + 6, torsoTopY + 4);
  ctx.lineTo(torsoX + torsoW - 8, beltY + 3);
  ctx.stroke();

  // 干粮袋(斜挎在腰间的小布袋)
  ctx.fillStyle = '#A89878';
  roundRect(ctx, torsoX + torsoW - 24, beltY - 6, 18, 14, 3);
  ctx.fill();
  ctx.strokeStyle = '#7A6858';
  ctx.lineWidth = 1;
  ctx.stroke();

  // ===== 3. 手臂(两条方块臂,左手持枪)=====
  // 左臂(持枪臂,稍微抬高)
  const leftArmX = torsoX - armW + 2;
  ctx.fillStyle = C.uniform;
  roundRect(ctx, leftArmX, torsoTopY + 4, armW, armH - 8, 3);
  ctx.fill();
  ctx.fillStyle = C.uniformDark;
  ctx.fillRect(leftArmX, torsoTopY + 4, 5, armH - 8);

  // 左手(肤色方块)
  ctx.fillStyle = C.skin;
  roundRect(ctx, leftArmX + 4, torsoTopY + armH - 12, armW - 8, 12, 3);
  ctx.fill();

  // 右臂(自然下垂)
  const rightArmX = torsoX + torsoW - 2;
  ctx.fillStyle = C.uniform;
  roundRect(ctx, rightArmX, torsoTopY + 4, armW, armH - 8, 3);
  ctx.fill();
  ctx.fillStyle = C.uniformLite;
  ctx.fillRect(rightArmX + armW - 5, torsoTopY + 4, 5, armH - 8);

  // 右手
  ctx.fillStyle = C.skin;
  roundRect(ctx, rightArmX + 4, torsoTopY + armH - 12, armW - 8, 12, 3);
  ctx.fill();

  // ===== 4. 步枪(汉阳造,斜扛在左肩)=====
  ctx.save();
  // 以左肩为轴心旋转
  ctx.translate(leftArmX + armW / 2, torsoTopY + 10);
  ctx.rotate(-0.35);

  // 枪托(木质)
  ctx.fillStyle = C.wood;
  roundRect(ctx, -10, -8, 24, 50, 4);
  ctx.fill();

  // 枪管(金属)
  ctx.fillStyle = C.metal;
  ctx.fillRect(-4, -70, 8, 70);

  // 枪管高光
  ctx.fillStyle = '#7A7A7A';
  ctx.fillRect(-4, -70, 3, 70);

  // 刺刀(细长金属)
  ctx.fillStyle = '#8A8A8A';
  ctx.fillRect(-1.5, -95, 3, 25);

  // 扳机护圈
  ctx.strokeStyle = C.metal;
  ctx.lineWidth = 2;
  ctx.beginPath();
  ctx.arc(2, 18, 5, 0, Math.PI);
  ctx.stroke();

  ctx.restore();

  // ===== 5. 头部(大方块 + 五官 + 八角帽)=====
  const headX = centerX - headW / 2;

  // 脖子(细短方块)
  ctx.fillStyle = C.skinDark;
  ctx.fillRect(centerX - 8, torsoTopY - 4, 16, 6);

  // 头部主体
  ctx.fillStyle = C.skin;
  roundRect(ctx, headX, headTopY, headW, headH, 6);
  ctx.fill();

  // 头部左侧阴影
  ctx.fillStyle = C.skinDark;
  ctx.fillRect(headX, headTopY, 6, headH);

  // 眼睛(两个深色小方块,坚毅神情)
  ctx.fillStyle = '#2A1A0A';
  ctx.fillRect(headX + 12, headTopY + 22, 7, 5);
  ctx.fillRect(headX + headW - 19, headTopY + 22, 7, 5);

  // 眉毛(斜向下的坚毅眉)
  ctx.strokeStyle = '#2A1A0A';
  ctx.lineWidth = 2;
  ctx.beginPath();
  ctx.moveTo(headX + 10, headTopY + 18);
  ctx.lineTo(headX + 20, headTopY + 20);
  ctx.stroke();
  ctx.beginPath();
  ctx.moveTo(headX + headW - 10, headTopY + 18);
  ctx.lineTo(headX + headW - 20, headTopY + 20);
  ctx.stroke();

  // 嘴巴(一条短横线)
  ctx.strokeStyle = '#8A5A3A';
  ctx.lineWidth = 1.5;
  ctx.beginPath();
  ctx.moveTo(headX + 20, headTopY + 40);
  ctx.lineTo(headX + headW - 20, headTopY + 40);
  ctx.stroke();

  // ===== 6. 八角帽 =====
  const hatW = headW + 14;
  const hatH = 22;
  const hatX = centerX - hatW / 2;
  const hatY = headTopY - hatH + 4;

  // 帽身(八角形轮廓,用多边形模拟)
  ctx.fillStyle = C.hat;
  ctx.beginPath();
  ctx.moveTo(hatX + 10, hatY);
  ctx.lineTo(hatX + hatW - 10, hatY);
  ctx.lineTo(hatX + hatW, hatY + 8);
  ctx.lineTo(hatX + hatW - 4, hatY + hatH);
  ctx.lineTo(hatX + 4, hatY + hatH);
  ctx.lineTo(hatX, hatY + 8);
  ctx.closePath();
  ctx.fill();

  // 帽檐阴影
  ctx.fillStyle = C.hatDark;
  ctx.fillRect(hatX + 2, hatY + hatH - 4, hatW - 4, 4);

  // 帽子左侧阴影
  ctx.fillStyle = C.hatDark;
  ctx.beginPath();
  ctx.moveTo(hatX, hatY + 8);
  ctx.lineTo(hatX + 8, hatY + 2);
  ctx.lineTo(hatX + 8, hatY + hatH);
  ctx.lineTo(hatX + 2, hatY + hatH);
  ctx.closePath();
  ctx.fill();

  // 红五星(帽徽,位于帽子正前方)
  const starCX = centerX;
  const starCY = hatY + 9;
  const starR = 7;
  ctx.fillStyle = C.collar;
  ctx.beginPath();
  for (let i = 0; i < 5; i++) {
    const angle = (i * 72 - 90) * Math.PI / 180;
    const x1 = starCX + starR * Math.cos(angle);
    const y1 = starCY + starR * Math.sin(angle);
    const angle2 = ((i + 1) * 72 - 90) * Math.PI / 180;
    const innerR = starR * 0.4;
    const x2 = starCX + innerR * Math.cos(angle2 - Math.PI / 5);
    const y2 = starCY + innerR * Math.sin(angle2 - Math.PI / 5);
    if (i === 0) ctx.moveTo(x1, y1);
    else ctx.lineTo(x1, y1);
    ctx.lineTo(x2, y2);
  }
  ctx.closePath();
  ctx.fill();

  // 五角星描边(暗红)
  ctx.strokeStyle = '#8A0A1A';
  ctx.lineWidth = 1;
  ctx.stroke();

  ctx.restore();
}

// ========== 待机动画 ==========
let time = 0;
function animate() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  time += 0.02;
  // 呼吸浮动:整体轻微上下
  const bob = Math.sin(time) * 1.5;

  // 画一个简单的地面阴影
  const groundY = canvas.height - 60;
  ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
  ctx.beginPath();
  ctx.ellipse(canvas.width / 2, groundY + 5, 70, 12, 0, 0, Math.PI * 2);
  ctx.fill();

  // 绘制战士
  drawSoldier(ctx, canvas.width / 2, groundY, 1.6, bob);

  requestAnimationFrame(animate);
}

animate();
</script>
</body>
</html>

Game Source: 红军战士 - Roblox 比例建模

Creator: TurboKoala83

Libraries: none

Complexity: complex (395 lines, 10.6 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: roblox-turbokoala83" to link back to the original. Then publish at arcadelab.ai/publish.