粒子大战模拟器edit icon

创建者:
邓朝元
Fork(复制)
下载
嵌入
BUG反馈
index.html
index.html
            
            <!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>
        :root {
            --bg-color: #f5f5f5;
            --panel-bg: #ffffff;
            --text-color: #333333;
            --border-color: #dddddd;
            --btn-bg: #4a90e2;
            --btn-hover: #357abd;
            --grid-bg: #e8e8e8;
            --shadow: 0 2px 8px rgba(0,0,0,0.1);
        }

        [data-theme="dark"] {
            --bg-color: #1a1a2e;
            --panel-bg: #16213e;
            --text-color: #e0e0e0;
            --border-color: #3a3a5c;
            --btn-bg: #0f3460;
            --btn-hover: #1a5276;
            --grid-bg: #0a0a1a;
            --shadow: 0 2px 8px rgba(0,0,0,0.4);
        }

        [data-theme="green"] {
            --bg-color: #f0f7f0;
            --panel-bg: #e8f5e9;
            --text-color: #2e7d32;
            --border-color: #a5d6a7;
            --btn-bg: #4caf50;
            --btn-hover: #388e3c;
            --grid-bg: #c8e6c9;
            --shadow: 0 2px 8px rgba(76,175,80,0.2);
        }

        [data-theme="sepia"] {
            --bg-color: #f4ecd8;
            --panel-bg: #f9f1e7;
            --text-color: #5c4033;
            --border-color: #c4a882;
            --btn-bg: #8b6914;
            --btn-hover: #6d5210;
            --grid-bg: #e8dcc8;
            --shadow: 0 2px 8px rgba(139,105,20,0.2);
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            background: var(--bg-color);
            color: var(--text-color);
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            align-items: center;
            padding: 20px;
            transition: all 0.3s ease;
        }

        h1 {
            font-size: 1.8em;
            margin-bottom: 20px;
            font-weight: 600;
        }

        .container {
            display: flex;
            gap: 20px;
            flex-wrap: wrap;
            justify-content: center;
            max-width: 1200px;
        }

        .panel {
            background: var(--panel-bg);
            border: 1px solid var(--border-color);
            border-radius: 8px;
            padding: 20px;
            box-shadow: var(--shadow);
            transition: all 0.3s ease;
        }

        .control-panel {
            min-width: 280px;
        }

        .control-group {
            margin-bottom: 15px;
        }

        .control-group label {
            display: block;
            margin-bottom: 5px;
            font-size: 0.9em;
            font-weight: 500;
        }

        button {
            padding: 10px 20px;
            border: none;
            border-radius: 5px;
            background: var(--btn-bg);
            color: white;
            font-size: 14px;
            cursor: pointer;
            transition: all 0.2s ease;
            margin: 3px;
        }

        button:hover {
            background: var(--btn-hover);
            transform: translateY(-1px);
        }

        button:disabled {
            opacity: 0.5;
            cursor: not-allowed;
            transform: none;
        }

        select, input {
            padding: 8px 12px;
            border: 1px solid var(--border-color);
            border-radius: 4px;
            background: var(--bg-color);
            color: var(--text-color);
            font-size: 14px;
            width: 100%;
            transition: all 0.3s ease;
        }

        .grid-container {
            display: flex;
            flex-direction: column;
            align-items: center;
        }

        #battleGrid {
            border: 2px solid var(--border-color);
            border-radius: 4px;
            image-rendering: pixelated;
            box-shadow: var(--shadow);
        }

        .stats-panel {
            min-width: 280px;
        }

        .particle-info {
            margin-bottom: 12px;
            padding: 10px;
            border-radius: 5px;
            border: 1px solid var(--border-color);
            font-size: 0.85em;
        }

        .particle-header {
            display: flex;
            align-items: center;
            gap: 8px;
            margin-bottom: 6px;
            font-weight: 600;
        }

        .color-indicator {
            width: 16px;
            height: 16px;
            border-radius: 3px;
            border: 1px solid rgba(0,0,0,0.2);
        }

        .stat-row {
            display: flex;
            justify-content: space-between;
            margin: 3px 0;
        }

        .territory-bar {
            width: 100%;
            height: 6px;
            background: var(--border-color);
            border-radius: 3px;
            margin-top: 5px;
            overflow: hidden;
        }

        .territory-fill {
            height: 100%;
            border-radius: 3px;
            transition: width 0.3s ease;
        }

        .speed-control {
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .speed-control input[type="range"] {
            flex: 1;
        }

        .theme-buttons {
            display: flex;
            flex-wrap: wrap;
            gap: 5px;
        }

        .theme-btn {
            padding: 6px 12px;
            font-size: 12px;
        }

        .status {
            text-align: center;
            margin-top: 10px;
            font-size: 0.9em;
            padding: 8px;
            background: var(--bg-color);
            border-radius: 4px;
            border: 1px solid var(--border-color);
        }
    </style>
</head>
<body>
    <h1>⚔️ 粒子大战模拟器</h1>
    
    <div class="container">
        <div class="panel control-panel">
            <div class="control-group">
                <label>主题设置</label>
                <div class="theme-buttons">
                    <button class="theme-btn" onclick="setTheme('default')">默认</button>
                    <button class="theme-btn" onclick="setTheme('dark')">暗色</button>
                    <button class="theme-btn" onclick="setTheme('green')">护眼</button>
                    <button class="theme-btn" onclick="setTheme('sepia')">复古</button>
                </div>
            </div>

            <div class="control-group">
                <label>网格大小</label>
                <select id="gridSize">
                    <option value="50">50 × 50</option>
                    <option value="80" selected>80 × 80</option>
                    <option value="100">100 × 100</option>
                    <option value="150">150 × 150</option>
                </select>
            </div>

            <div class="control-group">
                <label>扩张速度</label>
                <div class="speed-control">
                    <input type="range" id="speed" min="1" max="100" value="50">
                    <span id="speedValue">50</span>
                </div>
            </div>

            <div class="control-group">
                <label>初始领土大小</label>
                <input type="number" id="initSize" value="5" min="2" max="20">
            </div>

            <div class="control-group">
                <button id="startBtn" onclick="startBattle()">🚀 开始大战</button>
                <button id="pauseBtn" onclick="pauseBattle()" disabled>⏸ 暂停</button>
                <button id="resetBtn" onclick="resetBattle()">🔄 重置</button>
            </div>

            <div class="status" id="status">准备就绪,点击"开始大战"开始模拟</div>
        </div>

        <div class="grid-container">
            <div class="panel" style="padding: 10px;">
                <canvas id="battleGrid"></canvas>
            </div>
        </div>

        <div class="panel stats-panel">
            <div class="control-group">
                <label>粒子状态</label>
            </div>
            <div id="particleStats"></div>
        </div>
    </div>

    <script>
        // 粒子类型定义
        const PARTICLE_TYPES = [
            { 
                name: "烈焰军团", 
                color: "#e74c3c", 
                attack: 8, 
                defense: 5, 
                expansionRate: 1.2 
            },
            { 
                name: "寒冰部落", 
                color: "#3498db", 
                attack: 6, 
                defense: 8, 
                expansionRate: 1.0 
            },
            { 
                name: "自然之灵", 
                color: "#27ae60", 
                attack: 7, 
                defense: 7, 
                expansionRate: 1.1 
            },
            { 
                name: "暗影教团", 
                color: "#8e44ad", 
                attack: 9, 
                defense: 4, 
                expansionRate: 1.3 
            }
        ];

        let grid = [];
        let gridSize = 80;
        let canvas, ctx;
        let cellSize;
        let isRunning = false;
        let animationId = null;
        let battleSpeed = 50;
        let totalCells = 0;
        let tickCount = 0;

        // 初始化
        function init() {
            canvas = document.getElementById('battleGrid');
            ctx = canvas.getContext('2d');
            
            updateGridSize();
            updateStats();
            
            document.getElementById('gridSize').addEventListener('change', updateGridSize);
            document.getElementById('speed').addEventListener('input', function() {
                battleSpeed = parseInt(this.value);
                document.getElementById('speedValue').textContent = battleSpeed;
            });
        }

        function updateGridSize() {
            gridSize = parseInt(document.getElementById('gridSize').value);
            
            // 计算画布大小(限制最大显示尺寸为600px)
            const maxSize = 600;
            cellSize = Math.floor(maxSize / gridSize);
            canvas.width = gridSize * cellSize;
            canvas.height = gridSize * cellSize;
            
            if (!isRunning) {
                resetBattle();
            }
        }

        function setTheme(theme) {
            document.body.setAttribute('data-theme', theme);
        }

        function resetBattle() {
            if (animationId) {
                cancelAnimationFrame(animationId);
                animationId = null;
            }
            isRunning = false;
            tickCount = 0;
            
            // 初始化网格
            grid = Array(gridSize).fill(null).map(() => Array(gridSize).fill(-1));
            
            // 设置初始领土
            const initSize = parseInt(document.getElementById('initSize').value);
            const corners = [
                {x: 0, y: 0},
                {x: gridSize - initSize, y: 0},
                {x: 0, y: gridSize - initSize},
                {x: gridSize - initSize, y: gridSize - initSize}
            ];
            
            PARTICLE_TYPES.forEach((particle, index) => {
                const corner = corners[index];
                for (let i = 0; i < initSize; i++) {
                    for (let j = 0; j < initSize; j++) {
                        grid[corner.x + i][corner.y + j] = index;
                    }
                }
            });
            
            renderGrid();
            updateStats();
            
            document.getElementById('startBtn').disabled = false;
            document.getElementById('pauseBtn').disabled = true;
            document.getElementById('status').textContent = '准备就绪,点击"开始大战"开始模拟';
        }

        function startBattle() {
            if (!isRunning) {
                isRunning = true;
                document.getElementById('startBtn').disabled = true;
                document.getElementById('pauseBtn').disabled = false;
                document.getElementById('status').textContent = '战斗进行中...';
                gameLoop();
            }
        }

        function pauseBattle() {
            isRunning = false;
            if (animationId) {
                cancelAnimationFrame(animationId);
                animationId = null;
            }
            document.getElementById('startBtn').disabled = false;
            document.getElementById('pauseBtn').disabled = true;
            document.getElementById('status').textContent = '战斗已暂停';
        }

        function gameLoop() {
            if (!isRunning) return;
            
            const speedFactor = battleSpeed / 50;
            const ticksPerFrame = Math.ceil(speedFactor * 2);
            
            for (let i = 0; i < ticksPerFrame; i++) {
                tick();
                tickCount++;
            }
            
            renderGrid();
            updateStats();
            
            // 检查是否结束
            if (checkBattleEnd()) {
                isRunning = false;
                document.getElementById('startBtn').disabled = false;
                document.getElementById('pauseBtn').disabled = true;
                return;
            }
            
            animationId = requestAnimationFrame(gameLoop);
        }

        function tick() {
            const newGrid = grid.map(row => [...row]);
            const directions = [[0,1],[0,-1],[1,0],[-1,0]];
            
            // 收集所有边界细胞
            const borderCells = [];
            
            for (let x = 0; x < gridSize; x++) {
                for (let y = 0; y < gridSize; y++) {
                    if (grid[x][y] !== -1) {
                        // 检查相邻的空细胞或敌方细胞
                        for (const [dx, dy] of directions) {
                            const nx = x + dx;
                            const ny = y + dy;
                            
                            if (nx >= 0 && nx < gridSize && ny >= 0 && ny < gridSize) {
                                const neighbor = grid[nx][ny];
                                if (neighbor === -1 || (neighbor !== grid[x][y] && neighbor !== -1)) {
                                    borderCells.push({
                                        owner: grid[x][y],
                                        fromX: x,
                                        fromY: y,
                                        toX: nx,
                                        toY: ny,
                                        target: neighbor
                                    });
                                }
                            }
                        }
                    }
                }
            }
            
            // 随机打乱边界细胞处理顺序
            shuffleArray(borderCells);
            
            // 处理扩张和战斗
            for (const cell of borderCells) {
                const currentOwner = newGrid[cell.toX][cell.toY];
                
                if (currentOwner === -1) {
                    // 空地扩张
                    const particle = PARTICLE_TYPES[cell.owner];
                    if (Math.random() < particle.expansionRate * 0.3) {
                        newGrid[cell.toX][cell.toY] = cell.owner;
                    }
                } else if (currentOwner !== cell.owner) {
                    // 战斗判定
                    const attacker = PARTICLE_TYPES[cell.owner];
                    const defender = PARTICLE_TYPES[currentOwner];
                    
                    const attackPower = attacker.attack * (0.7 + Math.random() * 0.6);
                    const defensePower = defender.defense * (0.7 + Math.random() * 0.6);
                    
                    if (attackPower > defensePower) {
                        newGrid[cell.toX][cell.toY] = cell.owner;
                    }
                }
            }
            
            grid = newGrid;
        }

        function shuffleArray(array) {
            for (let i = array.length - 1; i > 0; i--) {
                const j = Math.floor(Math.random() * (i + 1));
                [array[i], array[j]] = [array[j], array[i]];
            }
        }

        function renderGrid() {
            ctx.fillStyle = getComputedStyle(document.documentElement).getPropertyValue('--grid-bg');
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            
            for (let x = 0; x < gridSize; x++) {
                for (let y = 0; y < gridSize; y++) {
                    if (grid[x][y] !== -1) {
                        ctx.fillStyle = PARTICLE_TYPES[grid[x][y]].color;
                        ctx.fillRect(y * cellSize, x * cellSize, cellSize, cellSize);
                    }
                }
            }
        }

        function updateStats() {
            const counts = Array(PARTICLE_TYPES.length).fill(0);
            
            for (let x = 0; x < gridSize; x++) {
                for (let y = 0; y < gridSize; y++) {
                    if (grid[x][y] !== -1) {
                        counts[grid[x][y]]++;
                    }
                }
            }
            
            totalCells = gridSize * gridSize;
            
            const statsContainer = document.getElementById('particleStats');
            statsContainer.innerHTML = PARTICLE_TYPES.map((particle, index) => {
                const percentage = ((counts[index] / totalCells) * 100).toFixed(1);
                const isAlive = counts[index] > 0;
                
                return `
                    <div class="particle-info" style="opacity: ${isAlive ? 1 : 0.4}">
                        <div class="particle-header">
                            <div class="color-indicator" style="background: ${particle.color}"></div>
                            <span>${particle.name}</span>
                        </div>
                        <div class="stat-row">
                            <span>⚔️ 战力: ${particle.attack}</span>
                            <span>🛡️ 防御: ${particle.defense}</span>
                        </div>
                        <div class="stat-row">
                            <span>领土: ${counts[index]}</span>
                            <span>占比: ${percentage}%</span>
                        </div>
                        <div class="territory-bar">
                            <div class="territory-fill" style="width: ${percentage}%; background: ${particle.color}"></div>
                        </div>
                    </div>
                `;
            }).join('');
        }

        function checkBattleEnd() {
            const activeTypes = new Set();
            for (let x = 0; x < gridSize; x++) {
                for (let y = 0; y < gridSize; y++) {
                    if (grid[x][y] !== -1) {
                        activeTypes.add(grid[x][y]);
                    }
                }
            }
            
            if (activeTypes.size === 0) return true;
            
            // 检查是否全部占领且只剩一个势力
            if (activeTypes.size === 1) {
                const winner = Array.from(activeTypes)[0];
                document.getElementById('status').textContent = 
                    `🏆 战斗结束!${PARTICLE_TYPES[winner].name}获得胜利!`;
                return true;
            }
            
            return false;
        }

        // 页面加载时初始化
        window.addEventListener('load', init);
    </script>
</body>
</html>
        
编辑器加载中
预览
控制台