🎮ArcadeLab

酒店小情人 · 阿岁

by HyperComet30
645 lines21.1 KB
▶ Play
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <title>酒店小情人 · 阿岁</title>
    <style>
        * {
            box-sizing: border-box;
            user-select: none;
            -webkit-tap-highlight-color: transparent;
        }
        body {
            margin: 0;
            min-height: 100vh;
            background: #2b1e10;
            display: flex;
            justify-content: center;
            align-items: center;
            font-family: 'Segoe UI', Roboto, system-ui, sans-serif;
        }
        .game-wrapper {
            background: #4a3520;
            padding: 16px 20px 20px 20px;
            border-radius: 48px 48px 32px 32px;
            box-shadow: 0 20px 30px rgba(0,0,0,0.7), inset 0 2px 4px rgba(255,215,140,0.3);
        }
        .game-container {
            position: relative;
            width: 800px;
            height: 450px;
            background: #1f1a15;
            border-radius: 28px;
            overflow: hidden;
            box-shadow: inset 0 0 0 2px #614c34;
            touch-action: none;
        }
        /* 酒店背景 (手绘暖色) */
        .hotel-bg {
            position: absolute;
            inset: 0;
            background: radial-gradient(circle at 20% 30%, #f9e6c7, #d8b992);
            background-image: 
                linear-gradient(45deg, rgba(200, 160, 120, 0.2) 1px, transparent 1px),
                linear-gradient(-45deg, rgba(160, 120, 80, 0.15) 1px, transparent 1px);
            background-size: 40px 40px, 30px 30px;
            box-shadow: inset 0 0 0 2px #af8a6b;
        }
        /* 酒店装饰: 窗框, 门, 灯 */
        .hotel-bg::before {
            content: "";
            position: absolute;
            top: 40px;
            left: 60px;
            width: 160px;
            height: 120px;
            background: #ead3b0;
            border: 6px solid #7a5d3e;
            border-radius: 40px 40px 12px 12px;
            box-shadow: inset 0 0 0 4px #b4916b, 0 8px 0 #4d3520;
        }
        .hotel-bg::after {
            content: "✦ 小酒店 ✦";
            position: absolute;
            bottom: 28px;
            right: 40px;
            font-size: 28px;
            font-weight: 300;
            color: #ffe3b0;
            text-shadow: 0 4px 0 #5a3f28, 0 6px 10px rgba(0,0,0,0.4);
            letter-spacing: 6px;
            opacity: 0.6;
            transform: rotate(-4deg);
        }
        /* 额外装饰 : 地毯纹路 */
        .carpet {
            position: absolute;
            bottom: 0;
            left: 0;
            width: 100%;
            height: 60px;
            background: repeating-linear-gradient(90deg, #a77b52 0px, #a77b52 20px, #b48b60 20px, #b48b60 40px);
            opacity: 0.3;
            border-top: 2px solid #785a3a;
        }

        /* ----- 小人 ------ */
        .character {
            position: absolute;
            bottom: 60px;
            left: 300px;
            width: 80px;        /* 酒店背景 1/10 宽 (800/10=80) 高度按比例 */
            height: 120px;
            transition: left 0.1s ease;
            z-index: 20;
            pointer-events: none;
            image-rendering: crisp-edges;
        }
        /* 身体 */
        .boy-body {
            position: absolute;
            bottom: 0;
            left: 50%;
            transform: translateX(-50%);
            width: 48px;
            height: 64px;
            background: white;
            border-radius: 12px 12px 6px 6px;
            box-shadow: 0 4px 0 #b0a090, inset 0 -6px 0 #d0c8b8;
            border: 2px solid #ccc0b0;
        }
        /* 衬衫领 */
        .boy-body::before {
            content: "";
            position: absolute;
            top: -2px;
            left: 10px;
            width: 12px;
            height: 12px;
            background: white;
            border: 2px solid #b0a090;
            border-bottom: none;
            border-radius: 8px 8px 0 0;
            transform: rotate(8deg);
        }
        .boy-body::after {
            content: "";
            position: absolute;
            top: -2px;
            right: 10px;
            width: 12px;
            height: 12px;
            background: white;
            border: 2px solid #b0a090;
            border-bottom: none;
            border-radius: 8px 8px 0 0;
            transform: rotate(-8deg);
        }

        /* 裤子 */
        .boy-pants {
            position: absolute;
            bottom: 0;
            left: 50%;
            transform: translateX(-50%);
            width: 50px;
            height: 28px;
            background: #1e1e1e;
            border-radius: 0 0 12px 12px;
            box-shadow: inset 0 4px 0 #2f2f2f;
            border: 2px solid #111;
            border-top: none;
            z-index: 2;
        }
        /* 腿 (走路动画) */
        .legs {
            position: absolute;
            bottom: -4px;
            left: 50%;
            transform: translateX(-50%);
            width: 44px;
            height: 18px;
            display: flex;
            justify-content: space-between;
            z-index: 1;
        }
        .leg {
            width: 12px;
            height: 20px;
            background: #222;
            border-radius: 0 0 6px 6px;
            border: 2px solid #0f0f0f;
            transform-origin: top center;
            transition: transform 0.15s ease;
        }
        .leg.left {
            transform: rotate(8deg);
        }
        .leg.right {
            transform: rotate(-8deg);
        }
        /* 鞋子 */
        .leg::after {
            content: "";
            display: block;
            width: 16px;
            height: 8px;
            background: #2d2d2d;
            border-radius: 6px 6px 0 0;
            margin-top: 12px;
            margin-left: -2px;
            border: 2px solid #1a1a1a;
        }

        /* 头部: 黑发 */
        .head {
            position: absolute;
            top: -10px;
            left: 50%;
            transform: translateX(-50%);
            width: 44px;
            height: 46px;
            background: #f7e3d0;
            border-radius: 60% 60% 40% 40% / 70% 70% 30% 30%;
            border: 2px solid #d6b89a;
            z-index: 10;
        }
        /* 头发 (黑色短发) */
        .hair {
            position: absolute;
            top: -12px;
            left: 50%;
            transform: translateX(-50%);
            width: 54px;
            height: 30px;
            background: #0f0f0f;
            border-radius: 60% 60% 0 0 / 100% 100% 0 0;
            clip-path: polygon(0% 0%, 100% 0%, 90% 70%, 80% 100%, 20% 100%, 10% 70%);
            box-shadow: inset 0 -6px 0 #2a2a2a;
            z-index: 15;
        }
        .hair::after {
            content: "";
            position: absolute;
            top: 4px;
            left: 8px;
            width: 12px;
            height: 12px;
            background: #1a1a1a;
            border-radius: 50%;
            box-shadow: 18px 4px 0 #1a1a1a, 8px 14px 0 #1a1a1a;
        }
        /* 眼睛 + 表情 */
        .eyes {
            position: absolute;
            top: 8px;
            left: 6px;
            width: 32px;
            height: 14px;
            display: flex;
            justify-content: space-between;
            z-index: 20;
        }
        .eye {
            width: 8px;
            height: 10px;
            background: #1f1f1f;
            border-radius: 50%;
            box-shadow: 0 4px 0 #4d3b2b;
        }
        .eye::after {
            content: "";
            display: block;
            width: 3px;
            height: 3px;
            background: white;
            border-radius: 50%;
            margin-top: 2px;
            margin-left: 2px;
        }
        .mouth {
            position: absolute;
            bottom: 8px;
            left: 50%;
            transform: translateX(-50%);
            width: 14px;
            height: 8px;
            background: transparent;
            border-bottom: 4px solid #a15a4a;
            border-radius: 0 0 50% 50%;
            z-index: 20;
        }

        /* 气泡 (说话) */
        .bubble {
            position: absolute;
            bottom: 100px;
            left: 50%;
            transform: translateX(-50%);
            background: rgba(255, 248, 235, 0.96);
            padding: 12px 18px;
            border-radius: 30px 30px 30px 6px;
            border: 2px solid #ccb69b;
            box-shadow: 0 6px 0 #9f8468, 0 12px 20px rgba(0,0,0,0.3);
            font-size: 22px;
            font-weight: 600;
            color: #3f2a17;
            white-space: nowrap;
            z-index: 60;
            letter-spacing: 1px;
            pointer-events: none;
            opacity: 0;
            transition: opacity 0.2s ease;
            font-family: 'Comic Neue', 'Segoe UI', cursive;
        }
        .bubble.show {
            opacity: 1;
        }
        .bubble::after {
            content: "";
            position: absolute;
            bottom: -22px;
            left: 20px;
            border: 12px solid transparent;
            border-top: 14px solid rgba(255, 248, 235, 0.96);
            filter: drop-shadow(0 4px 0 #9f8468);
        }

        /* 控制区域 */
        .controls {
            position: absolute;
            bottom: 20px;
            left: 0;
            right: 0;
            display: flex;
            justify-content: space-between;
            padding: 0 16px;
            z-index: 80;
            pointer-events: none;
        }
        .ctrl-group {
            display: flex;
            gap: 20px;
            pointer-events: auto;
        }
        .ctrl-btn {
            width: 70px;
            height: 70px;
            background: rgba(40, 30, 20, 0.7);
            backdrop-filter: blur(6px);
            border: 3px solid #dbbd9a;
            border-radius: 40px;
            color: #f5ebd8;
            font-size: 32px;
            font-weight: bold;
            box-shadow: 0 6px 0 #3f2e1b, 0 12px 20px rgba(0,0,0,0.5);
            display: flex;
            align-items: center;
            justify-content: center;
            transition: 0.05s linear;
            touch-action: manipulation;
            cursor: pointer;
            text-shadow: 0 2px 0 #1f140a;
            line-height: 1;
        }
        .ctrl-btn:active {
            transform: translateY(5px);
            box-shadow: 0 2px 0 #3f2e1b;
        }
        .ctrl-btn.direction {
            font-size: 36px;
            background: rgba(30, 40, 30, 0.75);
        }
        .ctrl-btn.special {
            background: rgba(120, 50, 60, 0.8);
            border-color: #f7b3a0;
            color: #ffded0;
            font-size: 28px;
        }
        .ctrl-btn.special:active {
            background: rgba(160, 70, 80, 0.9);
        }
        .btn-label {
            position: absolute;
            bottom: -22px;
            left: 50%;
            transform: translateX(-50%);
            color: #dac29c;
            font-size: 12px;
            font-weight: 400;
            letter-spacing: 1px;
            text-shadow: 0 2px 0 #1f140a;
            white-space: nowrap;
        }
        .ctrl-group .ctrl-btn {
            position: relative;
        }

        /* 响应式 */
        @media (max-width: 820px) {
            .game-container {
                width: 96vw;
                height: 54vw;
                max-height: 450px;
            }
            .ctrl-btn {
                width: 60px;
                height: 60px;
                font-size: 26px;
            }
        }
        @media (max-width: 500px) {
            .ctrl-btn {
                width: 50px;
                height: 50px;
                font-size: 20px;
            }
            .bubble {
                font-size: 16px;
                padding: 8px 14px;
                white-space: normal;
            }
        }
    </style>
</head>
<body>
<div class="game-wrapper">
<div class="game-container" id="gameContainer">
    <!-- 背景 -->
    <div class="hotel-bg"></div>
    <div class="carpet"></div>

    <!-- 小人 -->
    <div class="character" id="character">
        <div class="head">
            <div class="hair"></div>
            <div class="eyes">
                <div class="eye"></div>
                <div class="eye"></div>
            </div>
            <div class="mouth"></div>
        </div>
        <div class="boy-body"></div>
        <div class="boy-pants"></div>
        <div class="legs" id="legs">
            <div class="leg left" id="legLeft"></div>
            <div class="leg right" id="legRight"></div>
        </div>
    </div>

    <!-- 说话气泡 -->
    <div class="bubble" id="bubble">好爱你呀阿岁。</div>

    <!-- 控制按钮 -->
    <div class="controls">
        <div class="ctrl-group">
            <div class="ctrl-btn direction" id="btnLeft">◀</div>
            <div class="ctrl-btn direction" id="btnRight">▶</div>
        </div>
        <div class="ctrl-group">
            <div class="ctrl-btn special" id="btnJump">⬆</div>
            <div class="ctrl-btn special" id="btnHeart">❤️</div>
        </div>
    </div>
</div>
</div>

<script>
    (function(){
        const charEl = document.getElementById('character');
        const bubbleEl = document.getElementById('bubble');
        const legLeft = document.getElementById('legLeft');
        const legRight = document.getElementById('legRight');

        // 位置状态
        let posX = 300;          // 初始 left
        const MIN_X = 10;
        const MAX_X = 710;       // 800-80-10
        
        // 行走动画
        let walkCycle = 0;
        let walkInterval = null;

        // 音效 (使用 Web Audio 冒泡泡)
        let audioCtx = null;

        function playBubbleSound() {
            try {
                if (!audioCtx) {
                    audioCtx = new (window.AudioContext || window.webkitAudioContext)();
                }
                // 创建多个小气泡音效 (冒泡)
                const now = audioCtx.currentTime;
                for (let i = 0; i < 6; i++) {
                    const osc = audioCtx.createOscillator();
                    const gain = audioCtx.createGain();
                    osc.connect(gain);
                    gain.connect(audioCtx.destination);
                    // 频率变化 类似气泡
                    osc.type = 'sine';
                    const baseFreq = 400 + Math.random() * 300;
                    osc.frequency.setValueAtTime(baseFreq, now + i * 0.06);
                    osc.frequency.exponentialRampToValueAtTime(baseFreq * 1.8, now + i * 0.06 + 0.1);
                    gain.gain.setValueAtTime(0.08, now + i * 0.06);
                    gain.gain.exponentialRampToValueAtTime(0.001, now + i * 0.06 + 0.15);
                    osc.start(now + i * 0.06);
                    osc.stop(now + i * 0.06 + 0.2);
                }
                // 再加一个小水泡破裂声
                const bufferSize = audioCtx.sampleRate * 0.06;
                const buffer = audioCtx.createBuffer(1, bufferSize, audioCtx.sampleRate);
                const data = buffer.getChannelData(0);
                for (let i = 0; i < bufferSize; i++) {
                    data[i] = (Math.random() * 0.4 - 0.2) * Math.exp(-i / bufferSize * 5);
                }
                const noise = audioCtx.createBufferSource();
                noise.buffer = buffer;
                const gainN = audioCtx.createGain();
                gainN.gain.setValueAtTime(0.2, audioCtx.currentTime);
                gainN.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.1);
                noise.connect(gainN);
                gainN.connect(audioCtx.destination);
                noise.start();
                noise.stop(audioCtx.currentTime + 0.1);
            } catch(e) { /* 静默降级 */ }
        }

        // 显示气泡 + 音效
        function showBubble() {
            bubbleEl.classList.add('show');
            playBubbleSound();
            // 3.5s 后自动隐藏
            clearTimeout(window.bubbleTimeout);
            window.bubbleTimeout = setTimeout(() => {
                bubbleEl.classList.remove('show');
            }, 3500);
        }

        // 爱心按钮
        document.getElementById('btnHeart').addEventListener('click', function(e) {
            e.preventDefault();
            showBubble();
        });

        // 方向: 左/右 
        function moveCharacter(delta) {
            let newX = posX + delta;
            if (newX < MIN_X) newX = MIN_X;
            if (newX > MAX_X) newX = MAX_X;
            posX = newX;
            charEl.style.left = posX + 'px';
            // 走路动画 (腿交替)
            if (delta !== 0) {
                walkCycle = (walkCycle + 1) % 4;
                if (walkCycle < 2) {
                    legLeft.style.transform = 'rotate(12deg)';
                    legRight.style.transform = 'rotate(-12deg)';
                } else {
                    legLeft.style.transform = 'rotate(-8deg)';
                    legRight.style.transform = 'rotate(8deg)';
                }
            } else {
                legLeft.style.transform = 'rotate(4deg)';
                legRight.style.transform = 'rotate(-4deg)';
            }
        }

        // 按钮: 左
        document.getElementById('btnLeft').addEventListener('mousedown', function(e) {
            e.preventDefault();
            if (walkInterval) clearInterval(walkInterval);
            moveCharacter(-10);
            walkInterval = setInterval(() => moveCharacter(-10), 100);
        });
        document.getElementById('btnLeft').addEventListener('mouseup', function(e) {
            e.preventDefault();
            clearInterval(walkInterval);
            walkInterval = null;
            legLeft.style.transform = 'rotate(4deg)';
            legRight.style.transform = 'rotate(-4deg)';
        });
        document.getElementById('btnLeft').addEventListener('mouseleave', function(e) {
            // 防止卡键
            clearInterval(walkInterval);
            walkInterval = null;
            legLeft.style.transform = 'rotate(4deg)';
            legRight.style.transform = 'rotate(-4deg)';
        });

        // 按钮: 右
        document.getElementById('btnRight').addEventListener('mousedown', function(e) {
            e.preventDefault();
            if (walkInterval) clearInterval(walkInterval);
            moveCharacter(10);
            walkInterval = setInterval(() => moveCharacter(10), 100);
        });
        document.getElementById('btnRight').addEventListener('mouseup', function(e) {
            e.preventDefault();
            clearInterval(walkInterval);
            walkInterval = null;
            legLeft.style.transform = 'rotate(4deg)';
            legRight.style.transform = 'rotate(-4deg)';
        });
        document.getElementById('btnRight').addEventListener('mouseleave', function(e) {
            clearInterval(walkInterval);
            walkInterval = null;
            legLeft.style.transform = 'rotate(4deg)';
            legRight.style.transform = 'rotate(-4deg)';
        });

        // 跳跃 (简单上移再落下)
        document.getElementById('btnJump').addEventListener('click', function(e) {
            e.preventDefault();
            // 防止跳跃中再次触发
            if (charEl.dataset.jumping === 'true') return;
            charEl.dataset.jumping = 'true';
            let jumpHeight = -30;
            let step = 0;
            const jumpInterval = setInterval(() => {
                step++;
                if (step <= 6) {
                    charEl.style.bottom = (60 + jumpHeight * (step / 6)) + 'px';
                } else if (step <= 12) {
                    charEl.style.bottom = (60 + jumpHeight * (1 - (step-6) / 6)) + 'px';
                } else {
                    charEl.style.bottom = '60px';
                    clearInterval(jumpInterval);
                    charEl.dataset.jumping = 'false';
                }
            }, 20);
        });

        // 触摸兼容 (移动端)
        ['btnLeft','btnRight','btnJump','btnHeart'].forEach(id => {
            const el = document.getElementById(id);
            el.addEventListener('touchstart', function(e) {
                e.preventDefault();
                if (id === 'btnLeft') {
                    document.getElementById('btnLeft').dispatchEvent(new Event('mousedown'));
                } else if (id === 'btnRight') {
                    document.getElementById('btnRight').dispatchEvent(new Event('mousedown'));
                }
            });
            el.addEventListener('touchend', function(e) {
                e.preventDefault();
                if (id === 'btnLeft') {
                    document.getElementById('btnLeft').dispatchEvent(new Event('mouseup'));
                } else if (id === 'btnRight') {
                    document.getElementById('btnRight').dispatchEvent(new Event('mouseup'));
                }
            });
        });

        // 初始气泡隐藏
        bubbleEl.classList.remove('show');
        // 初始腿位置
        legLeft.style.transform = 'rotate(4deg)';
        legRight.style.transform = 'rotate(-4deg)';
        // 设置小人初始位置
        charEl.style.left = posX + 'px';
        charEl.style.bottom = '60px';
    })();
</script>
</body>
</html>

Game Source: 酒店小情人 · 阿岁

Creator: HyperComet30

Libraries: none

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