抗战大刀 - 独立建模
by TurboKoala83336 lines11.1 KB
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>抗战大刀 - 独立建模</title>
<style>
body { margin: 0; background: #0a0a12; overflow: hidden; font-family: 'Microsoft YaHei', sans-serif; touch-action: none; }
canvas { display: block; cursor: grab; }
canvas:active { cursor: grabbing; }
.info { position: absolute; top: 10px; left: 50%; transform: translateX(-50%); color: #8A9A7A; font-size: 12px; letter-spacing: 3px; text-shadow: 0 0 10px rgba(138, 154, 122, 0.8); pointer-events: none; z-index: 10; }
.controls { position: absolute; bottom: 10px; left: 50%; transform: translateX(-50%); color: #666; font-size: 11px; text-align: center; pointer-events: none; z-index: 10; width: 100%; }
</style>
</head>
<body>
<div class="info">★ 抗战大刀 · 独立建模 ★</div>
<div class="controls">左键拖拽平移 | 右键拖拽旋转 | 滚轮缩放 | 双击重置视角</div>
<canvas id="game"></canvas>
<script>
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
let W = window.innerWidth;
let H = window.innerHeight;
canvas.width = W;
canvas.height = H;
window.addEventListener('resize', () => {
W = window.innerWidth; H = window.innerHeight;
canvas.width = W; canvas.height = H;
});
// ========== 大刀配色 ==========
const D = {
blade: '#8A8A8A', // 刀刃主色
bladeDark: '#5A5A5A', // 刀刃阴影
bladeLite: '#B0B0B0', // 刀刃高光
bladeEdge: '#C8C8C8', // 锋刃
bloodGroove:'#6A6A6A', // 血槽
back: '#4A4A4A', // 刀背
guard: '#B8963A', // 铜护手
guardDark: '#8A6A1A', // 护手阴影
handle: '#3A2A1A', // 刀柄木质
wrap: '#5A6B7C', // 缠布(蓝灰色)
wrapDark: '#3D4A57', // 缠布阴影
ring: '#8A8A8A', // 刀环
ringDark: '#5A5A5A', // 刀环阴影
redCloth: '#C41E3A', // 红绸
};
// ========== 摄像机 ==========
const camera = {
x: W / 2, y: H / 2,
zoom: 1.0, rotation: 0,
isDragging: false, dragButton: 0, lastX: 0, lastY: 0,
};
// =====================================================================
// 绘制抗战大刀(R6方块风格)
// 原点 (0,0) 在【护手中心】,x 轴正方向为刀尖方向
// 参考真实尺寸:全长约90cm,刃长59.5cm,柄长21.5cm
// 代码比例:刀身长约 420px,刀柄长约 150px
// =====================================================================
function drawDadao(ctx) {
// ---------------------------------------------------------
// 1. 刀身(刀背 + 刀面 + 刀刃)
// 刀身从护手处向后逐渐加宽,前宽后窄
// 刀尖上挑呈雁翅形
// ---------------------------------------------------------
const bladeStartX = 10; // 刀身起点(护手右侧)
const bladeLength = 420;
const bladeEndX = bladeStartX + bladeLength;
// 刀背线(上边缘):从护手处平直延伸,前端斜下
// 刀刃线(下边缘):从护手处较窄,向前逐渐加宽
const backY0 = -14; // 刀背起点高度
const backY1 = -18; // 刀背前端高度
const edgeY0 = 6; // 刀刃起点高度
const edgeY1 = 14; // 刀刃前端高度(前宽后窄)
const tipY = -28; // 刀尖上挑
// 刀身主轮廓
ctx.fillStyle = D.blade;
ctx.beginPath();
ctx.moveTo(bladeStartX, backY0); // 刀背起点
ctx.lineTo(bladeEndX - 40, backY1); // 刀背前端
ctx.lineTo(bladeEndX, tipY); // 刀尖(雁翅上挑)
ctx.lineTo(bladeEndX - 15, edgeY1); // 刀尖下沿
ctx.lineTo(bladeStartX, edgeY0); // 刀刃起点
ctx.closePath();
ctx.fill();
// 刀面高光(上半部分)
ctx.fillStyle = D.bladeLite;
ctx.beginPath();
ctx.moveTo(bladeStartX, backY0);
ctx.lineTo(bladeEndX - 40, backY1);
ctx.lineTo(bladeEndX - 20, (backY1 + tipY) / 2);
ctx.lineTo(bladeStartX, backY0 + 8);
ctx.closePath();
ctx.fill();
// 刀面阴影(下半部分)
ctx.fillStyle = D.bladeDark;
ctx.beginPath();
ctx.moveTo(bladeStartX, edgeY0);
ctx.lineTo(bladeEndX - 15, edgeY1);
ctx.lineTo(bladeEndX - 30, edgeY1 - 6);
ctx.lineTo(bladeStartX, edgeY0 - 6);
ctx.closePath();
ctx.fill();
// 刀背(上边缘的一条厚线)
ctx.strokeStyle = D.back;
ctx.lineWidth = 4;
ctx.beginPath();
ctx.moveTo(bladeStartX, backY0);
ctx.lineTo(bladeEndX - 40, backY1);
ctx.stroke();
// 锋刃(下边缘的亮线)
ctx.strokeStyle = D.bladeEdge;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(bladeStartX, edgeY0);
ctx.lineTo(bladeEndX - 15, edgeY1);
ctx.stroke();
// 刀尖锋刃
ctx.strokeStyle = D.bladeEdge;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(bladeEndX - 40, backY1);
ctx.lineTo(bladeEndX, tipY);
ctx.lineTo(bladeEndX - 15, edgeY1);
ctx.stroke();
// ---------------------------------------------------------
// 2. 血槽(刀身靠近刀背处的两条凹槽)
// ---------------------------------------------------------
ctx.strokeStyle = D.bloodGroove;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(bladeStartX + 20, backY0 + 6);
ctx.lineTo(bladeEndX - 60, backY1 + 8);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(bladeStartX + 20, backY0 + 12);
ctx.lineTo(bladeEndX - 60, backY1 + 14);
ctx.stroke();
// ---------------------------------------------------------
// 3. 护手(元宝形,铜质)
// ---------------------------------------------------------
const guardW = 30;
const guardH = 40;
const guardX = bladeStartX - guardW + 2;
const guardY = -guardH / 2;
ctx.fillStyle = D.guard;
ctx.beginPath();
ctx.moveTo(guardX, guardY); // 左上
ctx.lineTo(guardX + guardW, guardY - 4); // 右上(向外弯)
ctx.lineTo(guardX + guardW, guardY + guardH); // 右下
ctx.lineTo(guardX, guardY + guardH + 4); // 左下(向外弯)
ctx.closePath();
ctx.fill();
// 护手阴影
ctx.fillStyle = D.guardDark;
ctx.fillRect(guardX, guardY + guardH - 6, guardW, 6);
// 护手高光
ctx.fillStyle = '#D4B04A';
ctx.fillRect(guardX + 2, guardY + 2, 4, guardH - 4);
// ---------------------------------------------------------
// 4. 刀柄(木质,缠布)
// ---------------------------------------------------------
const handleStartX = guardX - 5;
const handleLength = 150;
const handleEndX = handleStartX - handleLength;
const handleW = 16;
const handleY = -handleW / 2;
// 刀柄主体(木质)
ctx.fillStyle = D.handle;
ctx.fillRect(handleEndX, handleY, handleLength, handleW);
// 刀柄高光
ctx.fillStyle = '#5A3A2A';
ctx.fillRect(handleEndX, handleY, handleLength, 3);
// 刀柄缠布(蓝灰色,一圈一圈)
ctx.strokeStyle = D.wrap;
ctx.lineWidth = 4;
for (let i = 0; i < 12; i++) {
const wx = handleEndX + 10 + i * 11;
ctx.beginPath();
ctx.moveTo(wx, handleY - 1);
ctx.lineTo(wx + 6, handleY + handleW + 1);
ctx.stroke();
}
// 缠布阴影(增加立体感)
ctx.strokeStyle = D.wrapDark;
ctx.lineWidth = 1;
for (let i = 0; i < 12; i++) {
const wx = handleEndX + 10 + i * 11;
ctx.beginPath();
ctx.moveTo(wx + 4, handleY - 1);
ctx.lineTo(wx + 10, handleY + handleW + 1);
ctx.stroke();
}
// ---------------------------------------------------------
// 5. 刀环(刀柄末端的圆环,带红绸)
// ---------------------------------------------------------
const ringX = handleEndX - 5;
const ringY = 0;
const ringR = 14;
// 刀环(圆环)
ctx.strokeStyle = D.ring;
ctx.lineWidth = 5;
ctx.beginPath();
ctx.arc(ringX, ringY, ringR, 0, Math.PI * 2);
ctx.stroke();
// 刀环高光
ctx.strokeStyle = '#B0B0B0';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(ringX - 2, ringY - 2, ringR - 2, 0, Math.PI * 2);
ctx.stroke();
// 红绸(从刀环上飘出)
ctx.strokeStyle = D.redCloth;
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(ringX - ringR, ringY - 5);
ctx.quadraticCurveTo(ringX - ringR - 20, ringY - 20, ringX - ringR - 35, ringY - 10);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(ringX - ringR, ringY + 5);
ctx.quadraticCurveTo(ringX - ringR - 15, ringY + 18, ringX - ringR - 30, ringY + 10);
ctx.stroke();
// ---------------------------------------------------------
// 6. 细节:刀身与护手连接处的套环
// ---------------------------------------------------------
ctx.fillStyle = D.bladeDark;
ctx.fillRect(bladeStartX - 4, backY0 - 4, 8, edgeY0 - backY0 + 8);
}
// =====================================================================
// 主循环
// =====================================================================
function animate() {
ctx.clearRect(0, 0, W, H);
ctx.fillStyle = '#0F1A0F';
ctx.fillRect(0, 0, W, H);
// 背景网格
const gridSize = 80;
ctx.strokeStyle = 'rgba(74, 90, 74, 0.12)';
ctx.lineWidth = 1;
for (let x = 0; x < W; x += gridSize) {
for (let y = 0; y < H; y += gridSize) {
ctx.strokeRect(x, y, gridSize, gridSize);
}
}
// 参考线(护手中心)
ctx.strokeStyle = 'rgba(74, 90, 74, 0.3)';
ctx.lineWidth = 1;
ctx.beginPath(); ctx.moveTo(0, H / 2); ctx.lineTo(W, H / 2); ctx.stroke();
ctx.beginPath(); ctx.moveTo(W / 2, 0); ctx.lineTo(W / 2, H); 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(W / 2, H / 2);
drawDadao(ctx);
ctx.restore();
ctx.restore();
// UI 信息
ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';
ctx.font = '12px sans-serif';
ctx.textAlign = 'left';
ctx.fillText(`缩放: ${camera.zoom.toFixed(2)}x | 旋转: ${(camera.rotation * 180 / Math.PI).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 = W / 2; camera.y = H / 2;
camera.zoom = 1.0; camera.rotation = 0;
});
requestAnimationFrame(animate);
</script>
</body>
</html>Game Source: 抗战大刀 - 独立建模
Creator: TurboKoala83
Libraries: none
Complexity: complex (336 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-muqvmps8" to link back to the original. Then publish at arcadelab.ai/publish.