<!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>
:root {
--bg-color: #f0f2f5;
--panel-bg: rgba(255, 255, 255, 0.9);
--text-color: #333;
--accent-color: #007bff;
--border-radius: 12px;
--shadow: 0 4px 12px rgba(0,0,0,0.15);
}
/* 主题定义 */
body.theme-cyber {
--bg-color: #0d1117;
--panel-bg: rgba(22, 27, 34, 0.85);
--text-color: #58a6ff;
--accent-color: #2ea043;
--shadow: 0 0 15px rgba(88, 166, 255, 0.2);
}
body.theme-retro {
--bg-color: #e6dcc3;
--panel-bg: rgba(230, 220, 195, 0.9);
--text-color: #5c4b37;
--accent-color: #8b4513;
--shadow: 2px 2px 0px rgba(92, 75, 55, 0.2);
}
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; -webkit-tap-highlight-color: transparent; }
body {
font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
background-color: var(--bg-color);
color: var(--text-color);
overflow: hidden;
transition: background-color 0.5s, color 0.5s;
height: 100vh;
width: 100vw;
}
canvas {
display: block;
width: 100%;
height: 100%;
}
/* UI 控制面板 */
#ui-layer {
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 15px;
padding: 15px 25px;
background: var(--panel-bg);
backdrop-filter: blur(10px);
border-radius: var(--border-radius);
box-shadow: var(--shadow);
z-index: 100;
transition: all 0.3s ease;
border: 1px solid rgba(128,128,128,0.1);
}
.control-group {
display: flex;
align-items: center;
gap: 10px;
}
label { font-size: 14px; font-weight: 600; }
select, button {
padding: 8px 12px;
border-radius: 6px;
border: 1px solid rgba(128,128,128,0.3);
background: rgba(255,255,255,0.1);
color: var(--text-color);
cursor: pointer;
font-size: 14px;
outline: none;
transition: 0.2s;
}
button:hover { filter: brightness(1.1); }
button:active { transform: scale(0.95); }
.btn-primary { background: var(--accent-color); color: white; border: none; font-weight: bold; }
/* 状态信息 */
#stats-bar {
position: absolute;
bottom: 20px;
left: 20px;
background: var(--panel-bg);
padding: 10px 20px;
border-radius: var(--border-radius);
font-size: 14px;
pointer-events: none;
box-shadow: var(--shadow);
}
/* 移动端适配 */
@media (max-width: 768px) {
#ui-layer {
top: auto;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
width: 90%;
justify-content: space-between;
flex-wrap: wrap;
padding: 10px;
}
.control-group { flex-direction: column; align-items: flex-start; gap: 5px; }
select, button { width: 100%; text-align: center; }
#stats-bar {
bottom: 100px; /* 让出按钮空间 */
left: 50%;
transform: translateX(-50%);
width: 90%;
text-align: center;
}
}
</style>
</head>
<body>
<canvas id="simCanvas"></canvas>
<div id="ui-layer">
<div class="control-group">
<label>主题:</label>
<select id="themeSelect">
<option value="modern">现代简约</option>
<option value="cyber">赛博朋克</option>
<option value="retro">复古羊皮</option>
</select>
</div>
<div class="control-group">
<button id="restartBtn" class="btn-primary">重置模拟</button>
</div>
<div class="control-group">
<button id="pauseBtn">暂停/继续</button>
</div>
</div>
<div id="stats-bar">
存活国家:<span id="nationCount">0</span> | 当前帧率:<span id="fpsCount">0</span>
</div>
<script>
/**
* 国家大战模拟器核心逻辑
*/
const canvas = document.getElementById('simCanvas');
const ctx = canvas.getContext('2d');
// 配置参数
const CONFIG = {
baseNationCount: 15, // 基础国家数量
growthRate: 0.05, // 基础生长速度
attackRange: 40, // 攻击距离
colors: [
'#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8',
'#F7DC6F', '#BB8FCE', '#F1948A', '#82E0AA', '#85C1E9'
]
};
// 全局状态
let nations = [];
let isPaused = false;
let animationId;
let lastTime = 0;
let frameCount = 0;
let lastFpsTime = 0;
// 主题配置映射
const THEMES = {
modern: { bg: '#f0f2f5', grid: '#e1e4e8', text: '#333' },
cyber: { bg: '#0d1117', grid: '#21262d', text: '#58a6ff' },
retro: { bg: '#e6dcc3', grid: '#d4c5a3', text: '#5c4b37' }
};
let currentTheme = 'modern';
// 国家类
class Nation {
constructor(id, x, y, radius, color, powerMultiplier) {
this.id = id;
this.x = x;
this.y = y;
this.baseRadius = radius; // 初始大小代表领土潜力
this.radius = radius;
this.color = color;
this.power = 100 * powerMultiplier; // 战力 (兵力)
this.maxPower = 1000 * powerMultiplier;
this.alive = true;
this.name = `国-${id}`;
// 差异化属性
this.growthSpeed = (Math.random() * 0.5 + 0.5) * powerMultiplier; // 恢复速度
this.aggression = Math.random(); // 侵略性
}
update(dt) {
if (!this.alive) return;
// 兵力自然恢复
if (this.power < this.maxPower) {
this.power += this.growthSpeed * dt;
}
// 领土随兵力微调 (视觉反馈)
const targetRadius = this.baseRadius + (this.power / this.maxPower) * 10;
this.radius += (targetRadius - this.radius) * 0.1;
}
draw(ctx) {
if (!this.alive) return;
// 绘制领土
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = this.color;
ctx.fill();
// 边框
ctx.lineWidth = 2;
ctx.strokeStyle = 'rgba(0,0,0,0.2)';
ctx.stroke();
// 绘制兵力条
const barWidth = this.radius * 1.5;
const barHeight = 6;
const barX = this.x - barWidth / 2;
const barY = this.y - this.radius - 15;
// 背景条
ctx.fillStyle = 'rgba(0,0,0,0.3)';
ctx.fillRect(barX, barY, barWidth, barHeight);
// 前景条
const pct = Math.min(1, this.power / this.maxPower);
ctx.fillStyle = '#fff';
ctx.fillRect(barX, barY, barWidth * pct, barHeight);
// 文字
ctx.fillStyle = THEMES[currentTheme].text;
ctx.font = 'bold 12px Arial';
ctx.textAlign = 'center';
ctx.fillText(Math.floor(this.power), this.x, this.y + 4);
}
}
// 初始化画布尺寸
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resize);
resize();
// 初始化游戏
function initGame() {
nations = [];
const w = canvas.width;
const h = canvas.height;
// 根据屏幕大小动态调整国家数量
const area = w * h;
const count = Math.floor(CONFIG.baseNationCount * (area / (800*600)));
for (let i = 0; i < count; i++) {
// 随机位置 (留出边距)
const margin = 50;
const x = Math.random() * (w - margin * 2) + margin;
const y = Math.random() * (h - margin * 2) + margin;
// 随机属性:领土大小和战力倍率
// 有些国家天生就是大国 (半径大,兵力多)
const isBigPower = Math.random() > 0.8;
const sizeMult = isBigPower ? (Math.random() * 0.5 + 1.2) : (Math.random() * 0.5 + 0.6);
const powerMult = isBigPower ? (Math.random() * 0.5 + 1.5) : (Math.random() * 0.5 + 0.8);
const radius = 25 * sizeMult;
const color = CONFIG.colors[i % CONFIG.colors.length];
nations.push(new Nation(i, x, y, radius, color, powerMult));
}
updateStats();
}
// 碰撞与战斗逻辑
function checkCollisions() {
for (let i = 0; i < nations.length; i++) {
let n1 = nations[i];
if (!n1.alive) continue;
for (let j = i + 1; j < nations.length; j++) {
let n2 = nations[j];
if (!n2.alive) continue;
const dx = n1.x - n2.x;
const dy = n1.y - n2.y;
const dist = Math.sqrt(dx*dx + dy*dy);
const minDist = n1.radius + n2.radius;
// 如果领土接触
if (dist < minDist) {
// 计算重叠程度作为战斗强度
const overlap = minDist - dist;
// 简单的战斗公式:兵力多的一方侵蚀兵力少的一方
// 引入随机波动,避免绝对 deterministic
const powerDiff = n1.power - n2.power;
if (powerDiff > 0) {
// n1 攻击 n2
const damage = overlap * 0.5 * (n1.power / 100);
n2.power -= damage;
// n1 也会消耗一点兵力
n1.power -= damage * 0.2;
} else {
// n2 攻击 n1
const damage = overlap * 0.5 * (n2.power / 100);
n1.power -= damage;
n2.power -= damage * 0.2;
}
// 死亡判定
if (n1.power <= 0) killNation(n1);
if (n2.power <= 0) killNation(n2);
}
}
}
}
function killNation(nation) {
nation.alive = false;
nation.power = 0;
nation.radius = 0; // 视觉上消失
// 可选:将领土分给附近的赢家,这里简化为直接消失产生真空地带
// 或者让击败者瞬间获得大量领土
// 这里我们让击败者稍微变大一点作为奖励
// (需要在循环外处理,简化起见暂略)
}
// 渲染循环
function loop(timestamp) {
if (!isPaused) {
const dt = timestamp - lastTime;
lastTime = timestamp;
// FPS 计算
frameCount++;
if (timestamp - lastFpsTime >= 1000) {
document.getElementById('fpsCount').innerText = frameCount;
frameCount = 0;
lastFpsTime = timestamp;
}
// 清屏
ctx.fillStyle = THEMES[currentTheme].bg;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制网格背景 (装饰)
drawGrid();
// 更新和绘制国家
// 先排序,让兵力少的先画,兵力多的在上面 (可选)
nations.sort((a, b) => a.power - b.power);
nations.forEach(n => {
n.update(dt);
n.draw(ctx);
});
checkCollisions();
updateStats();
}
animationId = requestAnimationFrame(loop);
}
function drawGrid() {
ctx.strokeStyle = THEMES[currentTheme].grid;
ctx.lineWidth = 1;
ctx.beginPath();
const gridSize = 50;
for (let x = 0; x < canvas.width; x += gridSize) {
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
}
for (let y = 0; y < canvas.height; y += gridSize) {
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
}
ctx.stroke();
}
function updateStats() {
const aliveCount = nations.filter(n => n.alive).length;
document.getElementById('nationCount').innerText = aliveCount;
if (aliveCount <= 1 && nations.length > 1 && !isPaused) {
// 游戏结束提示 (可选)
// console.log("统一天下!");
}
}
// UI 事件绑定
document.getElementById('restartBtn').addEventListener('click', () => {
initGame();
});
document.getElementById('pauseBtn').addEventListener('click', () => {
isPaused = !isPaused;
if (!isPaused) {
lastTime = performance.now();
loop(lastTime);
}
});
document.getElementById('themeSelect').addEventListener('change', (e) => {
currentTheme = e.target.value;
document.body.className = `theme-${currentTheme}`;
});
// 启动
initGame();
lastTime = performance.now();
loop(lastTime);
</script>
</body>
</html>
index.html
index.html