红军战士 - Roblox 比例建模
by TurboKoala83395 lines10.6 KB
<!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.