🎮ArcadeLab

像素小人 · 阿岁

by HyperComet30
360 lines13.5 KB
▶ Play
<!DOCTYPE html>
<html lang="zh">
<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;
            user-select: none;
        }
        body {
            background: linear-gradient(145deg, #3b4a5c 0%, #1f2a36 100%);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            font-family: 'Segoe UI', 'Courier New', monospace;
        }
        .game-wrapper {
            background: #2d3a47;
            padding: 2rem 2.5rem 2.5rem 2.5rem;
            border-radius: 4rem 4rem 3rem 3rem;
            box-shadow: 0 20px 30px rgba(0,0,0,0.6), inset 0 1px 4px rgba(255,255,255,0.08);
            border-bottom: 6px solid #1b232c;
        }
        canvas {
            display: block;
            margin: 0 auto;
            width: 320px;
            height: 320px;
            image-rendering: crisp-edges;    /* 像素风格 */
            image-rendering: pixelated;
            background: #b4d0e0;
            border-radius: 32px;
            box-shadow: 0 0 0 2px #171f27, 0 12px 28px rgba(0,0,0,0.7);
            transition: filter 0.1s;
            cursor: pointer;
        }
        canvas:active {
            filter: brightness(0.96) drop-shadow(0 0 4px #b8d6ff);
        }
        .speech-bubble {
            margin-top: 24px;
            background: #fcf3e6;
            padding: 14px 18px;
            border-radius: 60px 60px 60px 8px;
            font-size: 1.4rem;
            font-weight: 600;
            color: #1f2a30;
            text-align: center;
            letter-spacing: 0.02em;
            box-shadow: 0 6px 0 #baa99b, inset 0 2px 8px rgba(255, 245, 225, 0.7);
            border: 2px solid #ece1d2;
            transition: 0.15s ease;
            min-height: 5rem;
            display: flex;
            align-items: center;
            justify-content: center;
            word-break: break-word;
            font-family: 'Courier New', 'Segoe UI', monospace;
        }
        .speech-bubble span {
            display: inline-block;
            background: #ffe7d0;
            padding: 0 12px;
            border-radius: 40px;
            line-height: 1.4;
        }
        .hint {
            margin-top: 16px;
            color: #b9cbdc;
            font-size: 0.9rem;
            text-align: center;
            letter-spacing: 1px;
            opacity: 0.8;
            font-weight: 300;
            text-shadow: 0 2px 3px #0f151c;
        }
        .hint i {
            font-style: normal;
            background: #1e2b38;
            padding: 2px 14px;
            border-radius: 30px;
            margin: 0 4px;
        }
        footer {
            margin-top: 10px;
            color: #657e92;
            font-size: 0.7rem;
            text-align: center;
            letter-spacing: 0.5px;
        }
    </style>
</head>
<body>
<div class="game-wrapper">
    <canvas id="pixelCanvas" width="320" height="320"></canvas>

    <!-- 说话气泡 -->
    <div class="speech-bubble" id="speechBubble">
        <span id="dialogueText">✨ 点我呀 ✨</span>
    </div>
    <div class="hint">
        <i>👆 点击小人 · 阿岁会说话</i>
    </div>
    <footer>✦ 像素阿岁 · M字刘海 ✦</footer>
</div>

<script>
    (function() {
        const canvas = document.getElementById('pixelCanvas');
        const ctx = canvas.getContext('2d');
        const dialogueSpan = document.getElementById('dialogueText');

        // 像素尺寸:每个像素块 8x8 ,画布 320x320 => 40x40 网格
        const PIXEL_SIZE = 8;
        const GRID = 40;

        // 颜色定义 (方便调整)
        const COLORS = {
            skin: '#f7d9b0',
            skinShadow: '#eac29a',
            hair: '#1f1f2b',          // 黑色短发
            hairLight: '#333345',
            eyeWhite: '#f2f4f0',
            eyePupil: '#1c1c24',
            eyeHighlight: '#fafafa',
            shirt: '#eef2f5',          // 白衬衫
            shirtShadow: '#d3d9df',
            collar: '#c8d0d8',
            pants: '#1e232b',          // 黑色裤子
            pantsLight: '#2f3640',
            belt: '#36404a',
            mouth: '#c27a7a',
            blush: '#f5c0b0',
            // 背景辅助
            bg: '#b4d0e0',
        };

        // ---------- 绘制像素小人 (阿岁) ----------
        function drawPixelCharacter() {
            // 清空画布 (背景)
            ctx.fillStyle = COLORS.bg;
            ctx.fillRect(0, 0, 320, 320);

            // ---------- 辅助函数: 填充像素块 (x,y 网格坐标) ----------
            function fillPixel(col, row, color) {
                const x = col * PIXEL_SIZE;
                const y = row * PIXEL_SIZE;
                ctx.fillStyle = color;
                ctx.fillRect(x, y, PIXEL_SIZE, PIXEL_SIZE);
            }

            // ---------- 绘制身体 (裤子+衬衫) ----------
            // 裤子 (黑色) 从 row 24~31, col 15~25
            for (let r = 24; r <= 31; r++) {
                for (let c = 15; c <= 25; c++) {
                    // 裤子稍微有深浅变化
                    let color = COLORS.pants;
                    if ((r === 24 || r === 31) && (c >= 17 && c <= 23)) color = COLORS.pantsLight;
                    if (r === 25 && (c === 16 || c === 24)) color = COLORS.pantsLight;
                    if (r === 30 && c === 18) color = '#3d4754';
                    if (r === 30 && c === 22) color = '#3d4754';
                    fillPixel(c, r, color);
                }
            }
            // 腰带 (深色  row 23)
            for (let c = 15; c <= 25; c++) {
                if (c >= 16 && c <= 24) fillPixel(c, 23, COLORS.belt);
            }

            // 白衬衫 (row 18~23, col 14~26)
            for (let r = 18; r <= 23; r++) {
                for (let c = 14; c <= 26; c++) {
                    // 留出脖子区域 (row 18~19 中间偏上让给皮肤)
                    if (r <= 19 && c >= 18 && c <= 22) continue;
                    // 衬衫阴影 (腋下/褶皱)
                    let color = COLORS.shirt;
                    if (r === 23 && (c === 14 || c === 26)) color = COLORS.shirtShadow;
                    if (r === 22 && (c === 14 || c === 26)) color = COLORS.shirtShadow;
                    if (r === 20 && (c === 14 || c === 26)) color = COLORS.shirtShadow;
                    if (r === 21 && c === 15) color = COLORS.shirtShadow;
                    if (r === 21 && c === 25) color = COLORS.shirtShadow;
                    // 衣领 (白色 更亮)
                    if (r === 19 && (c === 17 || c === 23)) color = '#f8fafc';
                    if (r === 20 && (c === 17 || c === 23)) color = COLORS.collar;
                    if (r === 20 && c === 18) color = '#dde3ea';
                    if (r === 20 && c === 22) color = '#dde3ea';
                    fillPixel(c, r, color);
                }
            }

            // 脖子 (皮肤) row 18~19, col 19~21
            for (let r = 18; r <= 19; r++) {
                for (let c = 19; c <= 21; c++) {
                    fillPixel(c, r, COLORS.skin);
                }
            }
            // 脖子阴影
            fillPixel(18, 19, COLORS.skinShadow);
            fillPixel(22, 19, COLORS.skinShadow);

            // ---------- 头部 (皮肤 + 头发) ----------
            // 脸部皮肤: row 8~18, col 14~26 (但头发会覆盖部分)
            for (let r = 8; r <= 18; r++) {
                for (let c = 14; c <= 26; c++) {
                    // 耳朵区域 (在两边)
                    if ((c === 13 || c === 27) && r >= 12 && r <= 16) {
                        fillPixel(c, r, COLORS.skin);
                        continue;
                    }
                    // 主要脸部区域 (留出发型位置)
                    if (c >= 14 && c <= 26) {
                        // 下巴小修饰
                        if (r === 18 && (c === 14 || c === 26)) continue; // 留给衬衫
                        fillPixel(c, r, COLORS.skin);
                    }
                }
            }
            // 脸颊微红 (row 15~16, col 14~15 和 25~26)
            fillPixel(14, 15, COLORS.blush);
            fillPixel(15, 15, COLORS.blush);
            fillPixel(14, 16, COLORS.blush);
            fillPixel(25, 15, COLORS.blush);
            fillPixel(26, 15, COLORS.blush);
            fillPixel(25, 16, COLORS.blush);

            // 眼睛 (白色) 左眼: col 17~18, row 12~13 ; 右眼: col 22~23, row 12~13
            for (let r = 12; r <= 13; r++) {
                for (let c = 17; c <= 18; c++) {
                    fillPixel(c, r, COLORS.eyeWhite);
                }
                for (let c = 22; c <= 23; c++) {
                    fillPixel(c, r, COLORS.eyeWhite);
                }
            }
            // 瞳孔 (黑色) 左: col 18, row 12 ; 右: col 22, row 12
            fillPixel(18, 12, COLORS.eyePupil);
            fillPixel(22, 12, COLORS.eyePupil);
            // 瞳孔高光
            fillPixel(19, 11, COLORS.eyeHighlight);
            fillPixel(23, 11, COLORS.eyeHighlight);

            // 嘴巴 (微笑小点) row 16, col 20
            fillPixel(20, 16, COLORS.mouth);
            fillPixel(21, 16, COLORS.mouth);
            fillPixel(20, 17, COLORS.mouth);
            fillPixel(21, 17, COLORS.mouth);

            // ---------- 黑色短发 + M字刘海 ----------
            // 头顶头发 (row 5~9)
            for (let r = 5; r <= 9; r++) {
                for (let c = 13; c <= 27; c++) {
                    if (r === 5 && (c >= 18 && c <= 22)) continue; // M字刘海凹陷
                    if (r === 6 && (c >= 19 && c <= 21)) continue; 
                    if (r === 7 && (c === 19 || c === 21)) continue;
                    // 发色
                    let color = COLORS.hair;
                    if (r === 5 || r === 6) color = COLORS.hairLight;
                    fillPixel(c, r, color);
                }
            }
            // 鬓角 / 侧发 (row 10~12)
            for (let r = 10; r <= 12; r++) {
                fillPixel(13, r, COLORS.hair);
                fillPixel(27, r, COLORS.hair);
            }
            // M字刘海 突出部分 (中间凹陷两侧凸起)
            // 左侧刘海凸起
            fillPixel(16, 7, COLORS.hair);
            fillPixel(17, 6, COLORS.hair);
            fillPixel(17, 7, COLORS.hair);
            fillPixel(18, 6, COLORS.hair);
            // 右侧刘海凸起
            fillPixel(23, 6, COLORS.hair);
            fillPixel(24, 6, COLORS.hair);
            fillPixel(23, 7, COLORS.hair);
            fillPixel(24, 7, COLORS.hair);
            // 中间刘海 (M 字凹陷已处理, 但补一些碎发)
            fillPixel(19, 7, COLORS.hair);
            fillPixel(21, 7, COLORS.hair);
            fillPixel(20, 6, COLORS.hairLight);
            fillPixel(20, 7, COLORS.hair);
            // 发顶边缘
            fillPixel(14, 6, COLORS.hair);
            fillPixel(26, 6, COLORS.hair);
            fillPixel(15, 5, COLORS.hairLight);
            fillPixel(25, 5, COLORS.hairLight);
            // 头顶加一点头发层次
            for (let c = 14; c <= 26; c++) {
                if (c === 18 || c === 19 || c === 20 || c === 21 || c === 22) continue;
                fillPixel(c, 5, COLORS.hairLight);
            }
            // 后脑勺 (补齐)
            for (let r = 8; r <= 9; r++) {
                for (let c = 13; c <= 27; c++) {
                    if (c >= 16 && c <= 24) continue; // 面部区域
                    fillPixel(c, r, COLORS.hair);
                }
            }

            // 小耳朵 (皮肤 加上一点阴影)
            fillPixel(13, 13, COLORS.skin);
            fillPixel(13, 14, COLORS.skinShadow);
            fillPixel(27, 13, COLORS.skin);
            fillPixel(27, 14, COLORS.skinShadow);

            // 补一些衬衫细节 (衣领)
            fillPixel(18, 19, '#e8edf2');
            fillPixel(22, 19, '#e8edf2');
        }

        // ---------- 说话逻辑 ----------
        function speak() {
            dialogueSpan.textContent = '❤️ 好爱你呀阿岁。 ❤️';
            // 轻微动画反馈 (气泡闪一下)
            const bubble = document.getElementById('speechBubble');
            bubble.style.transform = 'scale(0.97)';
            setTimeout(() => {
                bubble.style.transform = 'scale(1)';
            }, 100);
        }

        // 重置默认话语 (可点击后持续显示,但保留初始)
        function resetDialogue() {
            // 仅当内容为初始占位时才重置,但为了友好,我们保留最后一次说话内容
            // 如果希望点击后永远显示“好爱你呀阿岁。”,则无需重置。
            // 但第一次加载显示占位,点击后覆盖。
        }

        // ---------- 绑定事件 ----------
        canvas.addEventListener('click', (e) => {
            speak();
        });

        // 触摸设备支持
        canvas.addEventListener('touchstart', (e) => {
            e.preventDefault();
            speak();
        }, { passive: false });

        // 绘制角色
        drawPixelCharacter();

        // 额外: 双击重置为默认提示 (演示)
        canvas.addEventListener('dblclick', () => {
            dialogueSpan.textContent = '✨ 点我呀 ✨';
        });

        // 初始化占位
        dialogueSpan.textContent = '✨ 点我呀 ✨';

        console.log('✨ 像素阿岁已加载 — 点击小人就会说“好爱你呀阿岁。”');
    })();
</script>
</body>
</html>

Game Source: 像素小人 · 阿岁

Creator: HyperComet30

Libraries: none

Complexity: complex (360 lines, 13.5 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-hypercomet30" to link back to the original. Then publish at arcadelab.ai/publish.