像素塔防游戏:像素守卫者
下面是一个完整的像素风格塔防游戏HTML实现。玩家需要在地图上建造防御塔来阻止敌人到达终点。
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>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Press Start 2P', cursive, sans-serif;
}
body {
background: linear-gradient(135deg, #1a1a2e, #16213e);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
color: #fff;
overflow-x: hidden;
}
header {
text-align: center;
margin-bottom: 20px;
width: 100%;
padding: 15px;
background: rgba(0, 0, 0, 0.6);
border-radius: 10px;
box-shadow: 0 0 15px rgba(0, 255, 255, 0.5);
border: 2px solid #00ffff;
}
h1 {
font-size: 2.5rem;
margin-bottom: 10px;
color: #ffd700;
text-shadow: 0 0 10px rgba(255, 215, 0, 0.7);
letter-spacing: 2px;
}
.subtitle {
font-size: 1.2rem;
color: #00ffff;
margin-bottom: 15px;
}
.game-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 20px;
max-width: 1200px;
width: 100%;
}
.game-info {
background: rgba(0, 0, 0, 0.7);
border-radius: 10px;
padding: 20px;
width: 300px;
box-shadow: 0 0 15px rgba(0, 255, 255, 0.3);
border: 2px solid #00ffaa;
}
.stats {
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 2px solid #444;
}
.stat {
display: flex;
justify-content: space-between;
margin: 10px 0;
font-size: 0.9rem;
}
.stat-value {
color: #ffd700;
}
.towers {
margin-bottom: 20px;
}
h2 {
font-size: 1.3rem;
color: #00ffff;
margin-bottom: 15px;
text-align: center;
}
.tower-list {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.tower {
background: rgba(40, 40, 60, 0.8);
border-radius: 8px;
padding: 12px;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
border: 2px solid #555;
}
.tower:hover {
transform: translateY(-5px);
background: rgba(60, 60, 100, 0.8);
border-color: #00ffff;
box-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
}
.tower.selected {
border-color: #ffd700;
background: rgba(80, 60, 20, 0.8);
box-shadow: 0 0 15px rgba(255, 215, 0, 0.7);
}
.tower-icon {
width: 50px;
height: 50px;
margin: 0 auto 10px;
background: #333;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.8rem;
}
.tower-name {
font-size: 0.85rem;
margin-bottom: 5px;
color: #ffd700;
}
.tower-cost {
font-size: 0.8rem;
color: #00ffaa;
}
.controls {
display: flex;
flex-direction: column;
gap: 10px;
}
button {
background: linear-gradient(to bottom, #ff6b6b, #c0392b);
border: none;
border-radius: 5px;
color: white;
padding: 12px;
font-size: 1rem;
cursor: pointer;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 1px;
box-shadow: 0 5px 0 #8c1c13;
}
button:hover {
background: linear-gradient(to bottom, #ff8e8e, #e74c3c);
transform: translateY(-2px);
}
button:active {
transform: translateY(5px);
box-shadow: 0 0 0 #8c1c13;
}
#start-btn {
background: linear-gradient(to bottom, #4cd964, #27ae60);
box-shadow: 0 5px 0 #1e8449;
}
#start-btn:hover {
background: linear-gradient(to bottom, #5ef97a, #2ecc71);
}
.game-board {
position: relative;
background: rgba(0, 0, 0, 0.8);
border-radius: 10px;
overflow: hidden;
box-shadow: 0 0 20px rgba(0, 255, 255, 0.4);
border: 3px solid #00ffff;
}
#game-canvas {
display: block;
}
.instructions {
margin-top: 20px;
background: rgba(0, 0, 0, 0.6);
border-radius: 10px;
padding: 20px;
width: 100%;
max-width: 900px;
border: 2px solid #ff6b6b;
}
.instructions h2 {
color: #ff6b6b;
text-align: center;
margin-bottom: 15px;
}
.instructions p {
margin-bottom: 10px;
line-height: 1.6;
}
.instructions ul {
padding-left: 25px;
margin: 15px 0;
}
.instructions li {
margin-bottom: 8px;
color: #aaffaa;
}
.key {
display: inline-block;
background: rgba(255, 255, 255, 0.2);
padding: 2px 8px;
border-radius: 4px;
font-family: monospace;
margin: 0 3px;
}
footer {
margin-top: 20px;
text-align: center;
color: #aaa;
font-size: 0.8rem;
padding: 10px;
}
@media (max-width: 900px) {
.game-container {
flex-direction: column;
align-items: center;
}
.game-info {
width: 100%;
max-width: 600px;
}
}
.wave-indicator {
display: flex;
align-items: center;
justify-content: center;
margin-top: 15px;
gap: 10px;
}
.wave-dot {
width: 12px;
height: 12px;
border-radius: 50%;
background: #444;
}
.wave-dot.active {
background: #ffd700;
box-shadow: 0 0 8px #ffd700;
}
</style>
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
</head>
<body>
<header>
<h1>像素塔防:像素守卫者</h1>
<div class="subtitle">建造防御塔,消灭入侵的敌人!</div>
</header>
<div class="game-container">
<div class="game-info">
<div class="stats">
<h2>游戏状态</h2>
<div class="stat">
<span>金币:</span>
<span id="gold" class="stat-value">200</span>
</div>
<div class="stat">
<span>生命值:</span>
<span id="lives" class="stat-value">20</span>
</div>
<div class="stat">
<span>波数:</span>
<span id="wave" class="stat-value">0/10</span>
</div>
<div class="stat">
<span>敌人数量:</span>
<span id="enemies" class="stat-value">0</span>
</div>
<div class="stat">
<span>分数:</span>
<span id="score" class="stat-value">0</span>
</div>
</div>
<div class="towers">
<h2>选择防御塔</h2>
<div class="tower-list">
<div class="tower" data-type="basic" data-cost="50">
<div class="tower-icon">🔫</div>
<div class="tower-name">基础炮塔</div>
<div class="tower-cost">$50</div>
</div>
<div class="tower" data-type="sniper" data-cost="100">
<div class="tower-icon">🎯</div>
<div class="tower-name">狙击塔</div>
<div class="tower-cost">$100</div>
</div>
<div class="tower" data-type="cannon" data-cost="150">
<div class="tower-icon">💣</div>
<div class="tower-name">加农炮</div>
<div class="tower-cost">$150</div>
</div>
<div class="tower" data-type="ice" data-cost="120">
<div class="tower-icon">❄️</div>
<div class="tower-name">冰冻塔</div>
<div class="tower-cost">$120</div>
</div>
</div>
</div>
<div class="controls">
<button id="start-btn">开始游戏</button>
<button id="upgrade-btn">升级防御塔 ($100)</button>
<button id="sell-btn">出售防御塔</button>
<button id="pause-btn">暂停游戏</button>
</div>
<div class="wave-indicator">
<div class="wave-dot"></div>
<div class="wave-dot"></div>
<div class="wave-dot"></div>
<div class="wave-dot"></div>
<div class="wave-dot"></div>
<div class="wave-dot"></div>
<div class="wave-dot"></div>
<div class="wave-dot"></div>
<div class="wave-dot"></div>
<div class="wave-dot"></div>
</div>
</div>
<div class="game-board">
<canvas id="game-canvas" width="600" height="600"></canvas>
</div>
</div>
<div class="instructions">
<h2>游戏指南</h2>
<p>欢迎来到像素塔防!你的任务是阻止敌人穿过地图到达终点。</p>
<ul>
<li>选择左侧的防御塔类型,然后点击地图上的空地建造</li>
<li>每种防御塔都有不同的特性:基础炮塔射速快,狙击塔射程远,加农炮伤害高,冰冻塔减速敌人</li>
<li>消灭敌人获得金币,用于建造或升级防御塔</li>
<li>如果敌人到达终点,你将失去生命值</li>
<li>成功抵御10波敌人即可获胜!</li>
</ul>
<p><strong>提示:</strong> 合理搭配不同防御塔,在敌人路径的关键位置建造防御塔,及时升级强力防御塔!</p>
</div>
<footer>
© 2023 像素塔防游戏 | 使用HTML5 Canvas和JavaScript构建
</footer>
<script>
// 获取Canvas元素和上下文
const canvas = document.getElementById('game-canvas');
const ctx = canvas.getContext('2d');
// 游戏状态
const gameState = {
gold: 200,
lives: 20,
wave: 0,
enemies: 0,
score: 0,
gameStarted: false,
gameOver: false,
gameWon: false,
selectedTower: null,
selectedTowerType: null,
towers: [],
enemiesList: [],
bullets: [],
particles: [],
path: [
{x: 0, y: 300}, {x: 200, y: 300}, {x: 200, y: 100},
{x: 400, y: 100}, {x: 400, y: 300}, {x: 600, y: 300}
],
lastSpawn: 0,
spawnInterval: 1000,
enemyCount: 0,
maxWave: 10
};
// 防御塔类型
const towerTypes = {
basic: {
name: "基础炮塔",
cost: 50,
range: 100,
damage: 10,
fireRate: 800,
color: "#3498db",
bulletColor: "#2980b9"
},
sniper: {
name: "狙击塔",
cost: 100,
range: 200,
damage: 30,
fireRate: 1500,
color: "#9b59b6",
bulletColor: "#8e44ad"
},
cannon: {
name: "加农炮",
cost: 150,
range: 80,
damage: 50,
fireRate: 2000,
color: "#e74c3c",
bulletColor: "#c0392b"
},
ice: {
name: "冰冻塔",
cost: 120,
range: 120,
damage: 5,
fireRate: 1000,
color: "#1abc9c",
bulletColor: "#16a085",
slow: 0.5
}
};
// 敌人类型
const enemyTypes = [
{name: "普通敌人", health: 50, speed: 1.5, color: "#e74c3c", reward: 10},
{name: "快速敌人", health: 30, speed: 3, color: "#3498db", reward: 15},
{name: "重型敌人", health: 150, speed: 0.8, color: "#f39c12", reward: 25},
{name: "飞行敌人", health: 80, speed: 2, color: "#9b59b6", reward: 20}
];
// 防御塔类
class Tower {
constructor(x, y, type) {
this.x = x;
this.y = y;
this.type = type;
this.level = 1;
this.range = towerTypes[type].range;
this.damage = towerTypes[type].damage;
this.fireRate = towerTypes[type].fireRate;
this.lastShot = 0;
this.color = towerTypes[type].color;
this.bulletColor = towerTypes[type].bulletColor;
this.slow = towerTypes[type].slow || 0;
}
draw() {
// 绘制炮塔底座
ctx.fillStyle = "#2c3e50";
ctx.beginPath();
ctx.arc(this.x, this.y, 20, 0, Math.PI * 2);
ctx.fill();
// 绘制炮塔主体
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, 15, 0, Math.PI * 2);
ctx.fill();
// 绘制炮管
ctx.strokeStyle = "#34495e";
ctx.lineWidth = 4;
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.lineTo(this.x + 20, this.y);
ctx.stroke();
// 绘制等级
ctx.fillStyle = "#ffffff";
ctx.font = "bold 12px Arial";
ctx.textAlign = "center";
ctx.fillText("Lv." + this.level, this.x, this.y + 5);
// 如果选中则绘制范围
if (gameState.selectedTower === this) {
ctx.strokeStyle = "rgba(255, 255, 255, 0.3)";
ctx.beginPath();
ctx.arc(this.x, this.y, this.range, 0, Math.PI * 2);
ctx.stroke();
}
}
update() {
// 寻找目标
let target = null;
let minDist = this.range;
for (let enemy of gameState.enemiesList) {
const dx = enemy.x - this.x;
const dy = enemy.y - this.y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < minDist) {
minDist = dist;
target = enemy;
}
}
// 射击
if (target && Date.now() - this.lastShot > this.fireRate) {
gameState.bullets.push(new Bullet(
this.x,
this.y,
target,
this.damage,
this.bulletColor,
this.slow
));
this.lastShot = Date.now();
}
}
upgrade() {
this.level++;
this.damage = Math.floor(this.damage * 1.5);
this.range = Math.floor(this.range * 1.1);
this.fireRate = Math.floor(this.fireRate * 0.9);
}
}
// 敌人类
class Enemy {
constructor(type) {
this.type = type;
this.health = enemyTypes[type].health;
this.maxHealth = enemyTypes[type].health;
this.speed = enemyTypes[type].speed;
this.color = enemyTypes[type].color;
this.reward = enemyTypes[type].reward;
this.pathIndex = 0;
this.x = gameState.path[0].x;
this.y = gameState.path[0].y;
this.slow = 1; // 减速系数 (1=正常)
this.slowTimer = 0;
}
draw() {
// 绘制敌人身体
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, 15, 0, Math.PI * 2);
ctx.fill();
// 绘制敌人眼睛
ctx.fillStyle = "#ffffff";
ctx.beginPath();
ctx.arc(this.x - 5, this.y - 5, 4, 0, Math.PI * 2);
ctx.arc(this.x + 5, this.y - 5, 4, 0, Math.PI * 2);
ctx.fill();
// 绘制血条
const barWidth = 30;
const barHeight = 5;
const healthPercent = this.health / this.maxHealth;
ctx.fillStyle = "#444";
ctx.fillRect(this.x - barWidth/2, this.y - 30, barWidth, barHeight);
ctx.fillStyle = healthPercent > 0.5 ? "#2ecc71" : healthPercent > 0.25 ? "#f1c40f" : "#e74c3c";
ctx.fillRect(this.x - barWidth/2, this.y - 30, barWidth * healthPercent, barHeight);
}
update() {
// 应用减速效果
if (this.slowTimer > 0) {
this.slowTimer--;
} else {
this.slow = 1;
}
// 沿路径移动
if (this.pathIndex < gameState.path.length - 1) {
const targetX = gameState.path[this.pathIndex + 1].x;
const targetY = gameState.path[this.pathIndex + 1].y;
const dx = targetX - this.x;
const dy = targetY - this.y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 2) {
this.pathIndex++;
} else {
this.x += (dx / dist) * this.speed * this.slow;
this.y += (dy / dist) * this.speed * this.slow;
}
} else {
// 到达终点
gameState.lives--;
updateStats();
removeEnemy(this);
if (gameState.lives <= 0) {
gameState.gameOver = true;
}
}
}
takeDamage(damage) {
this.health -= damage;
if (this.health <= 0) {
gameState.gold += this.reward;
gameState.score += this.reward * 10;
updateStats();
createParticles(this.x, this.y, this.color);
removeEnemy(this);
}
}
applySlow(slowFactor, duration) {
this.slow = slowFactor;
this.slowTimer = duration;
}
}
// 子弹类
class Bullet {
constructor(x, y, target, damage, color, slow) {
this.x = x;
this.y = y;
this.target = target;
this.damage = damage;
this.color = color;
this.speed = 5;
this.slow = slow;
}
draw() {
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, 5, 0, Math.PI * 2);
ctx.fill();
}
update() {
// 移动子弹
const dx = this.target.x - this.x;
const dy = this.target.y - this.y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < this.speed) {
// 击中目标
this.target.takeDamage(this.damage);
// 应用减速效果
if (this.slow > 0) {
this.target.applySlow(this.slow, 100);
}
createParticles(this.target.x, this.target.y, this.color);
return false; // 标记为待删除
} else {
// 继续移动
this.x += (dx / dist) * this.speed;
this.y += (dy / dist) * this.speed;
return true; // 子弹继续存在
}
}
}
// 粒子效果类
class Particle {
constructor(x, y, color) {
this.x = x;
this.y = y;
this.color = color;
this.size = Math.random() * 5 + 2;
this.speedX = Math.random() * 3 - 1.5;
this.speedY = Math.random() * 3 - 1.5;
this.life = 30;
}
draw() {
ctx.fillStyle = this.color;
ctx.globalAlpha = this.life / 30;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fill();
ctx.globalAlpha = 1;
}
update() {
this.x += this.speedX;
this.y += this.speedY;
this.life--;
return this.life > 0;
}
}
// 初始化游戏
function initGame() {
gameState.gold = 200;
gameState.lives = 20;
gameState.wave = 0;
gameState.enemies = 0;
gameState.score = 0;
gameState.gameStarted = false;
gameState.gameOver = false;
gameState.gameWon = false;
gameState.selectedTower = null;
gameState.towers = [];
gameState.enemiesList = [];
gameState.bullets = [];
gameState.particles = [];
gameState.enemyCount = 0;
updateStats();
updateWaveDots();
// 重置按钮状态
document.getElementById('start-btn').textContent = "开始游戏";
}
// 更新游戏状态显示
function updateStats() {
document.getElementById('gold').textContent = gameState.gold;
document.getElementById('lives').textContent = gameState.lives;
document.getElementById('wave').textContent = `${gameState.wave}/${gameState.maxWave}`;
document.getElementById('enemies').textContent = gameState.enemiesList.length;
document.getElementById('score').textContent = gameState.score;
}
// 更新波数指示器
function updateWaveDots() {
const dots = document.querySelectorAll('.wave-dot');
dots.forEach((dot, index) => {
if (index < gameState.wave) {
dot.classList.add('active');
} else {
dot.classList.remove('active');
}
});
}
// 创建粒子效果
function createParticles(x, y, color) {
for (let i = 0; i < 10; i++) {
gameState.particles.push(new Particle(x, y, color));
}
}
// 移除敌人
function removeEnemy(enemy) {
const index = gameState.enemiesList.indexOf(enemy);
if (index !== -1) {
gameState.enemiesList.splice(index, 1);
}
}
// 生成敌人
function spawnEnemy() {
if (gameState.enemyCount > 0 && Date.now() - gameState.lastSpawn > gameState.spawnInterval) {
// 根据波数选择敌人类型
let type = 0;
if (gameState.wave > 3) type = Math.floor(Math.random() * 2);
if (gameState.wave > 6) type = Math.floor(Math.random() * 3);
if (gameState.wave > 8) type = Math.floor(Math.random() * 4);
gameState.enemiesList.push(new Enemy(type));
gameState.enemyCount--;
gameState.lastSpawn = Date.now();
}
// 如果所有敌人都已生成且没有剩余敌人,进入下一波
if (gameState.enemyCount === 0 && gameState.enemiesList.length === 0 && gameState.gameStarted) {
gameState.wave++;
updateStats();
updateWaveDots();
if (gameState.wave > gameState.maxWave) {
gameState.gameWon = true;
} else {
// 设置下一波敌人数量
gameState.enemyCount = 5 + gameState.wave * 3;
gameState.spawnInterval = 1000 - Math.min(800, gameState.wave * 70);
}
}
}
// 绘制游戏场景
function drawGame() {
// 绘制背景
ctx.fillStyle = "#2c3e50";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制路径
ctx.strokeStyle = "#7f8c8d";
ctx.lineWidth = 40;
ctx.lineCap = "round";
ctx.beginPath();
ctx.moveTo(gameState.path[0].x, gameState.path[0].y);
for (let i = 1; i < gameState.path.length; i++) {
ctx.lineTo(gameState.path[i].x, gameState.path[i].y);
}
ctx.stroke();
// 绘制起点和终点
ctx.fillStyle = "#27ae60";
ctx.beginPath();
ctx.arc(gameState.path[0].x, gameState.path[0].y, 20, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = "#c0392b";
ctx.beginPath();
ctx.arc(gameState.path[gameState.path.length - 1].x,
gameState.path[gameState.path.length - 1].y, 20, 0, Math.PI * 2);
ctx.fill();
// 绘制敌人
for (let enemy of gameState.enemiesList) {
enemy.draw();
}
// 绘制子弹
for (let bullet of gameState.bullets) {
bullet.draw();
}
// 绘制粒子
for (let particle of gameState.particles) {
particle.draw();
}
// 绘制防御塔
for (let tower of gameState.towers) {
tower.draw();
}
// 绘制游戏状态
if (!gameState.gameStarted) {
ctx.fillStyle = "rgba(0, 0, 0, 0.7)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#ffffff";
ctx.font = "bold 36px 'Press Start 2P'";
ctx.textAlign = "center";
ctx.fillText("像素塔防", canvas.width/2, canvas.height/2 - 40);
ctx.font = "bold 24px 'Press Start 2P'";
ctx.fillText("点击'开始游戏'按钮", canvas.width/2, canvas.height/2 + 20);
ctx.fillText("开始你的防御战!", canvas.width/2, canvas.height/2 + 60);
}
if (gameState.gameOver) {
ctx.fillStyle = "rgba(0, 0, 0, 0.8)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#e74c3c";
ctx.font = "bold 36px 'Press Start 2P'";
ctx.textAlign = "center";
ctx.fillText("游戏结束", canvas.width/2, canvas.height/2 - 40);
ctx.fillStyle = "#ffffff";
ctx.font = "bold 24px 'Press Start 2P'";
ctx.fillText(`最终分数: ${gameState.score}`, canvas.width/2, canvas.height/2 + 20);
ctx.fillText("点击'开始游戏'重新开始", canvas.width/2, canvas.height/2 + 60);
}
if (gameState.gameWon) {
ctx.fillStyle = "rgba(0, 0, 0, 0.8)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#2ecc71";
ctx.font = "bold 36px 'Press Start 2P'";
ctx.textAlign = "center";
ctx.fillText("胜利!", canvas.width/2, canvas.height/2 - 40);
ctx.fillStyle = "#ffffff";
ctx.font = "bold 24px 'Press Start 2P'";
ctx.fillText(`最终分数: ${gameState.score}`, canvas.width/2, canvas.height/2 + 20);
ctx.fillText("成功抵御了所有敌人!", canvas.width/2, canvas.height/2 + 60);
}
}
// 更新游戏状态
function updateGame() {
if (!gameState.gameStarted || gameState.gameOver || gameState.gameWon) return;
// 生成敌人
spawnEnemy();
// 更新敌人
for (let enemy of gameState.enemiesList) {
enemy.update();
}
// 更新防御塔
for (let tower of gameState.towers) {
tower.update();
}
// 更新子弹
for (let i = gameState.bullets.length - 1; i >= 0; i--) {
if (!gameState.bullets[i].update()) {
gameState.bullets.splice(i, 1);
}
}
// 更新粒子
for (let i = gameState.particles.length - 1; i >= 0; i--) {
if (!gameState.particles[i].update()) {
gameState.particles.splice(i, 1);
}
}
}
// 游戏主循环
function gameLoop() {
drawGame();
updateGame();
requestAnimationFrame(gameLoop);
}
// 事件监听器
document.querySelectorAll('.tower').forEach(tower => {
tower.addEventListener('click', () => {
// 移除之前的选择
document.querySelectorAll('.tower').forEach(t => {
t.classList.remove('selected');
});
// 设置新选择
tower.classList.add('selected');
gameState.selectedTowerType = tower.dataset.type;
});
});
canvas.addEventListener('click', (e) => {
if (!gameState.gameStarted || gameState.gameOver || gameState.gameWon) return;
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// 检查是否点击了防御塔
for (let tower of gameState.towers) {
const dx = tower.x - x;
const dy = tower.y - y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 20) {
gameState.selectedTower = tower;
return;
}
}
// 如果没有选择防御塔类型,返回
if (!gameState.selectedTowerType) return;
// 检查是否在路径上
let onPath = false;
ctx.lineWidth = 50;
for (let i = 0; i < gameState.path.length - 1; i++) {
const p1 = gameState.path[i];
const p2 = gameState.path[i + 1];
// 简化检查 - 在实际游戏中需要更精确的碰撞检测
if (Math.abs(x - p1.x) < 30 && Math.abs(y - p1.y) < 30) onPath = true;
if (Math.abs(x - p2.x) < 30 && Math.abs(y - p2.y) < 30) onPath = true;
}
// 检查是否在防御塔上
let onTower = false;
for (let tower of gameState.towers) {
const dx = tower.x - x;
const dy = tower.y - y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 30) {
onTower = true;
break;
}
}
// 如果不在路径上且不在其他防御塔上,并且有足够金币,则建造防御塔
if (!onPath && !onTower && gameState.gold >= towerTypes[gameState.selectedTowerType].cost) {
gameState.towers.push(new Tower(x, y, gameState.selectedTowerType));
gameState.gold -= towerTypes[gameState.selectedTowerType].cost;
updateStats();
}
// 取消选择防御塔
gameState.selectedTower = null;
});
document.getElementById('start-btn').addEventListener('click', () => {
if (gameState.gameOver || gameState.gameWon) {
initGame();
return;
}
if (!gameState.gameStarted) {
gameState.gameStarted = true;
gameState.wave = 1;
gameState.enemyCount = 8;
document.getElementById('start-btn').textContent = "重新开始";
updateStats();
updateWaveDots();
}
});
document.getElementById('upgrade-btn').addEventListener('click', () => {
if (gameState.selectedTower && gameState.gold >= 100) {
gameState.selectedTower.upgrade();
gameState.gold -= 100;
updateStats();
}
});
document.getElementById('sell-btn').addEventListener('click', () => {
if (gameState.selectedTower) {
const index = gameState.towers.indexOf(gameState.selectedTower);
if (index !== -1) {
gameState.gold += Math.floor(towerTypes[gameState.selectedTower.type].cost * 0.7);
gameState.towers.splice(index, 1);
gameState.selectedTower = null;
updateStats();
}
}
});
document.getElementById('pause-btn').addEventListener('click', () => {
gameState.gameStarted = !gameState.gameStarted;
document.getElementById('pause-btn').textContent =
gameState.gameStarted ? "暂停游戏" : "继续游戏";
});
// 启动游戏
initGame();
gameLoop();
</script>
</body>
</html>
游戏特点与功能
这个像素塔防游戏具有以下丰富功能:
-
多种防御塔类型:
- 基础炮塔:平衡的伤害和射速
- 狙击塔:远射程、高伤害但射速慢
- 加农炮:高伤害但射速慢、射程短
- 冰冻塔:减速敌人
-
多样化的敌人:
- 普通敌人:标准属性
- 快速敌人:移动速度快但生命值低
- 重型敌人:生命值高但移动速度慢
- 飞行敌人:平衡型敌人
-
游戏系统:
- 资源管理:使用金币建造和升级防御塔
- 生命系统:敌人到达终点会减少生命值
- 分数系统:击败敌人获得分数
- 波数系统:10波逐渐增强的敌人攻击
-
视觉效果:
- 像素风格的游戏界面
- 粒子爆炸效果
- 动态血条显示
- 防御塔攻击特效
-
用户界面:
- 清晰的游戏状态显示
- 防御塔选择面板
- 波数进度指示器
- 游戏指南和操作说明
游戏玩法指南
- 点击"开始游戏"按钮开始游戏
- 在左侧面板选择防御塔类型
- 点击地图上的空地建造防御塔
- 防御塔会自动攻击范围内的敌人
- 使用金币建造更多防御塔或升级现有防御塔
- 抵御10波敌人攻击即可获胜
- 如果敌人到达终点,生命值降为0则游戏结束
游戏采用了响应式设计,可以在各种设备上流畅运行。祝你游戏愉快!