红军战士 · 模型
by EpicCoder88283 lines7.2 KB
<!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 {
image-rendering: pixelated;
image-rendering: crisp-edges;
border-radius: 8px;
box-shadow: 0 0 60px rgba(180, 40, 30, 0.2), 0 0 0 1px rgba(120, 60, 40, 0.3);
}
.label {
position: fixed;
bottom: 24px;
left: 50%;
transform: translateX(-50%);
color: rgba(200, 150, 120, 0.4);
font-size: 13px;
letter-spacing: 0.3em;
pointer-events: none;
}
</style>
</head>
<body>
<canvas id="c" width="480" height="540"></canvas>
<div class="label">红 军 战 士</div>
<script>
(() => {
'use strict';
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
const W = 480, H = 540;
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);
ctx.imageSmoothingEnabled = false;
// ==================== 颜色 ====================
const C = {
hat: '#7a8299',
coat: '#6b7280',
pants: '#5a626f',
wrap: '#8b8578',
shoe: '#8b6f47',
skin: '#e8b88a',
star: '#d02020',
collar: '#d02020',
belt: '#3a2418',
buckle: '#8a6a40',
gun: '#2a2018',
gunHi: '#4a3828',
wood: '#5a3a20',
woodHi: '#7a5030',
eye: '#2a1a10',
mouth: '#a06848',
};
// ==================== 工具 ====================
function px(x, y, w, h, color) {
ctx.fillStyle = color;
ctx.fillRect(x, y, w, h);
}
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('');
}
function roundRect(x, y, w, h, r) {
r = Math.min(r, w / 2, h / 2);
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
ctx.closePath();
}
// ==================== 绘制战士 ====================
// 基础单位是"像素块",绘制时整体放大
// 角色原始尺寸 32×48,放大 9 倍 = 288×432
const PX = 9;
const BASE_X = 24; // 原始坐标偏移
const BASE_Y = 6;
// 帧计数器(用于行走动画)
let frame = 0;
function drawSoldier(pose, f) {
const ox = BASE_X * PX;
const oy = BASE_Y * PX;
const S = PX;
// 便捷绘制:原始坐标 → 屏幕坐标
const p = (x, y, w, h, color) => {
ctx.fillStyle = color;
ctx.fillRect(ox + x * S, oy + y * S, w * S, h * S);
};
// ---- 动画偏移 ----
let lLegY = 0, rLegY = 0, lArmY = 0, rArmY = 0;
if (pose === 'walk') {
const s = Math.sin(f * 0.18);
lLegY = Math.round(s * 2);
rLegY = -Math.round(s * 2);
lArmY = -Math.round(s * 1.5);
rArmY = Math.round(s * 1.5);
}
// ============ 腿 ============
// 左腿
p(10, 28 + lLegY, 5, 8, C.pants);
// 右腿
p(17, 28 + rLegY, 5, 8, C.pants);
// 绑腿(横条纹)
for (let i = 0; i < 5; i++) {
const col = i % 2 === 0 ? C.wrap : shade(C.wrap, -0.16);
p(10, 36 + i + lLegY, 5, 1, col);
p(17, 36 + i + rLegY, 5, 1, col);
}
// 草鞋
p(9, 41 + lLegY, 7, 3, C.shoe);
p(16, 41 + rLegY, 7, 3, C.shoe);
p(9, 42 + lLegY, 7, 1, shade(C.shoe, -0.12));
p(16, 42 + rLegY, 7, 1, shade(C.shoe, -0.12));
// ============ 躯干 ============
p(9, 18, 14, 13, C.coat);
// 衣领
p(11, 18, 3, 3, C.collar);
p(18, 18, 3, 3, C.collar);
// 口袋
p(11, 23, 4, 3, shade(C.coat, -0.1));
p(17, 23, 4, 3, shade(C.coat, -0.1));
p(11, 23, 4, 1, shade(C.coat, -0.2));
p(17, 23, 4, 1, shade(C.coat, -0.2));
// 腰带
p(9, 28, 14, 2, C.belt);
p(15, 28, 2, 2, C.buckle);
// 躯干暗面(右侧)
p(22, 18, 1, 13, shade(C.coat, -0.1));
// ============ 手臂 ============
// 左臂
p(5, 19 + lArmY, 4, 9, C.coat);
p(5, 28 + lArmY, 4, 3, C.skin);
p(5, 19 + lArmY, 1, 9, shade(C.coat, -0.12));
// 右臂
p(23, 19 + rArmY, 4, 9, C.coat);
p(23, 28 + rArmY, 4, 3, C.skin);
p(26, 19 + rArmY, 1, 9, shade(C.coat, -0.12));
// ============ 头部 ============
p(10, 10, 12, 8, C.skin);
// 眼睛
p(12, 13, 2, 2, C.eye);
p(18, 13, 2, 2, C.eye);
// 嘴
p(15, 16, 2, 1, C.mouth);
// 耳朵
p(9, 13, 1, 3, shade(C.skin, -0.08));
p(22, 13, 1, 3, shade(C.skin, -0.08));
// 下巴暗面
p(10, 17, 12, 1, shade(C.skin, -0.06));
// ============ 军帽 ============
// 帽身
p(9, 2, 14, 6, C.hat);
// 顶部高光
p(9, 2, 14, 1, shade(C.hat, 0.1));
// 帽檐
p(7, 8, 18, 2, shade(C.hat, -0.22));
p(7, 9, 18, 1, shade(C.hat, -0.35));
// 五角星
p(14, 3, 4, 4, C.star);
p(15, 4, 2, 2, shade(C.star, 0.25));
// ============ 汉阳造步枪 ============
// 枪管
p(25, 13, 2, 19, C.gun);
p(25, 13, 1, 19, C.gunHi);
// 枪托
p(24, 32, 4, 6, C.wood);
p(24, 32, 4, 1, C.woodHi);
// 枪机
p(24, 22, 3, 2, '#3a2a1a');
// ============ 瞄准状态 ============
if (pose === 'aim') {
// 手臂前伸
// 右臂改画到前方
p(23, 20, 4, 4, C.coat);
p(24, 23, 3, 2, C.skin);
}
}
// ==================== 主循环 ====================
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.12)';
ctx.lineWidth = 1;
for (let x = 0; x <= W; x += 30) {
ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, H); ctx.stroke();
}
for (let y = 0; y <= H; y += 30) {
ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(W, y); ctx.stroke();
}
// 地面阴影
ctx.fillStyle = 'rgba(0,0,0,0.3)';
ctx.beginPath();
ctx.ellipse(W / 2, BASE_Y * PX + 44 * PX, 70, 14, 0, 0, Math.PI * 2);
ctx.fill();
// 三种姿势轮播(每 120 帧切换)
const poseIndex = Math.floor(frame / 120) % 3;
const poses = ['stand', 'walk', 'aim'];
const pose = poses[poseIndex];
drawSoldier(pose, frame);
// 姿势名称
ctx.fillStyle = 'rgba(200, 150, 120, 0.3)';
ctx.font = '12px system-ui, sans-serif';
ctx.textAlign = 'center';
ctx.fillText(pose.toUpperCase(), W / 2, H - 16);
}
loop();
})();
</script>
</body>
</html>Game Source: 红军战士 · 模型
Creator: EpicCoder88
Libraries: none
Complexity: complex (283 lines, 7.2 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-murvw036" to link back to the original. Then publish at arcadelab.ai/publish.