自制植物大战僵尸:HTML5与JavaScript实现的简单游戏

Yan-英杰 2024-06-14 16:05:03 阅读 65

引言

在本文中,我们将一起探索如何使用HTML5和JavaScript来创建一个简单的植物大战僵尸游戏。这不仅是一项有趣的编程挑战,也是学习游戏开发基础的绝佳机会。

什么是植物大战僵尸?

植物大战僵尸是一款流行的策略塔防游戏,玩家需要种植不同类型的植物来防御进攻的僵尸。我们的目标是复现这款游戏的核心机制,以一个简化的版本呈现。

准备工作

        在开始编码之前,你需要具备基本的HTML、CSS和JavaScript知识。此外,一个代码编辑器(如VS Code或Sublime Text)将帮助你编写和测试代码。

        比如:

HBulider

HTML结构

首先,我们创建HTML页面的基本结构,包括<!DOCTYPE html>, <html>, <head>, 和 <body>标签。在<head>部分,我们定义了页面的元数据和标题。

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>植物大战僵尸</title></head><body></body></html>

CSS样式

接下来,我们添加内联<style>标签来定义游戏的样式。这包括背景、按钮、植物、僵尸和动画等元素的样式。

body { * { margin: 0; padding: 0; /* 设置怪异盒子 */ box-sizing: border-box; } body{ background-color:#949489; } #app { margin: 50px auto; width: 1400px; height: 600px; border: 5px solid #010101; border-radius: 50px; background-image: url(../images/background1.jpg); background-repeat: no-repeat; background-size: cover; position: relative; } #topui { width: 1200px; height: 45px; position: absolute; top: 10px; left: 150px; } /* 能量栏 */ .vessel{ width: 100px; height: 45px; background-image: url(../images/sunback.png); background-repeat: no-repeat; background-size: 100px; font-size: 20px; font-weight: 700; line-height: 30px; padding-left: 15px; text-align: center; position: absolute; top: 0px; left: 0px; border: 1px solid #000; border-color: rgba(0, 0, 0,0); } .energy{ width: 50px; height: 50px; border: 1px solid #000; border-color: rgba(0, 0, 0,0); border-radius: 20px; opacity: 1; position: absolute; top: 0px; left: 0px; } /* 按钮样式 */ .button{ width: 120px; height: 45px; line-height: 41px; border-radius: 5px; color: aliceblue; background-repeat: no-repeat; text-align: center; position: absolute; top: 0px; left: 1080px; border: 1px solid #000; border-color: rgba(0, 0, 0,0); } /* 删除植物 */ .delete{ width: 45px; height: 45px; background-image: url(../images/铁锹.png); background-repeat: no-repeat; background-size: 40px; text-align: center; position: absolute; top: 0px; left: 850px; border: 1px solid #000; border-color: rgba(0, 0, 0,0); } .delete:hover{ transform: scale(1.2); } /* 得分 */ .grade{ width: 200px; height: 30px; text-align: center; line-height: 30px; font-size: 25px; color: #fafcfa; position: absolute; top: 0; left: 400px; border: 1px solid #000; border-color: rgba(0, 0, 0,0); background-color: lightslategrey; border-radius: 20px; } .button:hover{ color: #09f63c; } #leftui { width: 100px; height: 455px; position: absolute; top: 60px; left: 10px; } /* 植物选择框单元样式 */ .plantui{ width: 100px; height: 60px; font-weight: 700; padding-left: 60px; padding-top: 40px; margin-bottom: 5px; border: 1px solid #000; border-color: rgba(0, 0, 0,0); border-radius: 10px; } .plantui:hover{ border: 1px solid #09f63c; } /* 网格坐标样式 */ .geid{ width: 80px; height: 100px; border: 1px solid #000; border-color: rgba(0, 0, 0,0); border-radius: 20px; position: absolute; top: 60px; left: 250px; } /* 植物div */ .plant { width: 80px; height: 100px; text-align: center; color: rgb(249, 250, 251); border: 1px solid #000000; border-color: rgba(0, 0, 0,0); border-radius: 20px; position: absolute; /* opacity: 0.8; */ } /* 植物img */ .plantimg{ width: 80%; margin-top: 0px; margin-left: 10px; position: absolute; top: 25px; left: 0; } .plantspan{ position: absolute; top: 0; left: 25px; } /* 僵尸div */ .zombie { width: 80px; height: 102px; text-align: center; color: rgb(7, 7, 7); border: 1px solid #000; border-color: rgba(0, 0, 0,0); border-radius: 20px; border-radius: 20px; position: absolute; } /* 僵尸img */ .zombieimg{ width: 150%; margin-top: -12px; margin-left: -30px; } /* 子弹 */ .bullet{ width: 10px; height: 10px; margin-top: 35px; margin-left: 60px; background-color: #02a0f5; border: 1px solid #000; border-radius: 50%; position: absolute; } /* 准备游戏样式 */ #go{ width: 255px; height: 108px; position: absolute; top: 246px; left: 573px; background-image: url(../images/loading/loading_0.png); animation: xz 1s infinite; } @keyframes xz{ 0%{ transform: scale(1); } 50%{ transform: scale(1.1); } 100%{ transform: scale(1); } } /* 游戏结束 */ .end{ width: 566px; height: 470px; position: absolute; top: 50px; left: 400px; background-image: url(../images/zombieWon.png); border: 1px solid #000; border-color: rgba(0, 0, 0,0); animation: end 2s infinite; } @keyframes end { 0%{ transform: scale(1); } 50%{ transform: scale(1.1); } 100%{ transform: scale(1); } } /* 游戏胜利 */ .vict{ width: 566px; height: 470px; text-align: center; padding-top: 308px; padding-left: 230px; padding-right: 200px; font-size: 40px; color: white; background-image: url(../images/游戏胜利.png); background-size: 1000px; background-position: left -200px top -50px; background-repeat: no-repeat; border: 1px solid #000; border-color: rgba(0, 0, 0,0); position: absolute; top: 50px; left: 400px; animation: vict 2s infinite; } @keyframes vict { 0%{ transform: scale(1); } 50%{ transform: scale(1.1); } 100%{ transform: scale(1); } } /* 时钟 */ .nz{ width: 100px; height: 50px; margin: 5px; padding-left: 40px; padding-top: 10px; font-weight: 700; color: #fafcfa; line-height: 28px; text-align: center; border-radius: 15px; background-image: url(../images/闹钟.png); background-size: 50px; background-repeat: no-repeat; position: absolute; top: 60; left: 900px; }}

JavaScript逻辑

游戏的核心逻辑将通过JavaScript实现。我们将创建植物、僵尸、子弹等游戏对象,并定义它们的行为和动画。

游戏初始化和全局变量

首先,代码中定义了游戏容器和一些全局变量,用于跟踪游戏状态:

​var game = document.getElementById('app');var gameState = {   // 游戏状态对象属性};

植物属性定义

定义了一个包含不同植物属性的对象PlantUte,每个植物都有名称、价格、图像路径、生命值等属性:

var PlantUte = { // 向日葵 SunFlower:{ name:"向日葵", price:50, uisrc1:"../images/cards/plants/SunFlower.png", uisrc2:"../images/cards/plants/SunFlowerG.png", datasrc:"../images/plants/sunflower/idle/idle_0.png", url:"../images/plants/sunflower/idle/idle_", count:17, hp:100, attack:0, speed:0, range:0, color:"red"},

// 初级豌豆射手Peashooter:{ name:"初级豌豆射手", price:50, uisrc1:"../images/cards/plants/Peashooter.png", uisrc2:"../images/cards/plants/PeashooterG.png", datasrc:"../images/plants/peashooter/attack/attack_0.png", url:"../images/plants/peashooter/attack/attack_", count:7, hp:100, attack:5, speed:500, range:500, color:"chartreuse"},

// 中级豌豆射手 Repeater:{ name:"中级豌豆射手", price:100, uisrc1:"../images/cards/plants/Repeater.png", uisrc2:"../images/cards/plants/RepeaterG.png", datasrc:"../images/plants/repeater/attack/attack_0.png", url:"../images/plants/repeater/attack/attack_", count:14, hp:100, attack:10, speed:500, range:500, color:"chartreuse" }, // 高级豌豆射手 GatlingPea:{ name:"高级豌豆射手", price:200, uisrc1:"../images/cards/plants/GatlingPea.png", uisrc2:"../images/cards/plants/GatlingPeaG.png", datasrc:"../images/plants/gatlingpea/attack/attack_0.png", url:"../images/plants/gatlingpea/attack/attack_", count:12, hp:100, attack:10, speed:300, range:500, color:"chartreuse" }, // 番茄炸弹 CherryBomb:{ name:"番茄炸弹", price:200, uisrc1:"../images/cards/plants/CherryBomb.png", uisrc2:"../images/cards/plants/CherryBombG.png", datasrc:"../images/plants/cherrybomb/idle/idle_0.png", url:"../images/plants/cherrybomb/idle/idle_", count:6, hp:50, attack:100, speed:1000, range:57, color:"rgba(0,0,0,0)" }, // 食人花 Chomper:{ name:"食人花", price:300, uisrc1:"../images/cards/plants/Chomper.png", uisrc2:"../images/cards/plants/ChomperG.png", datasrc:"../images/plants/chomper/attack/attack_0.png", url:"../images/plants/chomper/attack/attack_", count:8, hp:100, attack:20, speed:100, range:57, color:"rgba(0,0,0,0)" }, // 坚果防御 WallNut:{ name:"坚果防御", price:50, uisrc1:"../images/cards/plants/WallNut.png", uisrc2:"../images/cards/plants/WallNutG.png", datasrc:"../images/plants/wallnut/idleH/idleH_0.png", url:"../images/plants/wallnut/idleH/idleH_", count:15, hp:1000, attack:0, speed:0, range:0, color:"red"

UI组件类

定义了多个类来创建游戏的UI组件,例如:

function ToolBar(text,style){ this.text = text; this.element = document.createElement('div'); this.element.className = style; this.element.innerText = this.text; topUI.appendChild(this.element); // 添加到游戏选择UI框 gameState.toolbar.push(this)}

游戏对象类

定义了游戏中的对象类,例如:

Plant:代表一个植物,具有位置、属性和动画。

Zombie:代表一个僵尸,具有生命值、攻击能力和移动速度。

Bullet:代表从植物发射的子弹。

初始化UI

InitUI函数用于初始化游戏界面元素,包括植物选择框、网格线坐标和顶部UI信息栏:

var InitUI = function(){// 初始化网格线坐标 for(var i=0;i<5;i++){ for(var j=0;j<9;j++){ new Geid(parseInt(j*80+240),parseInt(i*100+60)) } }// 创建顶部UI信息栏对象实例new ToolBar(500,"vessel");//能量收集new ToolBar("","delete"); //铲子new ToolBar(0,"grade"); //销毁僵尸数量new ToolBar("00:00","nz"); //游戏时间// 创建UI标签栏对象实例new Labels(PlantUte.SunFlower); //向日葵new Labels(PlantUte.Peashooter); //初级豌豆射手new Labels(PlantUte.Repeater); //中级豌豆射手new Labels(PlantUte.GatlingPea); //高级豌豆射手 // new Labels(PlantUte.CherryBomb); //番茄炸弹new Labels(PlantUte.Chomper); //食人花new Labels(PlantUte.WallNut); //坚果防御}

JavaScript完整代码

<script>var game = document.getElementById('app'); // 获取游戏界面元素 var leftUI = document.getElementById('leftui'); // 获取植物选择框 var topUI = document.getElementById('topui'); // 获取植物选择框 var Go = document.getElementById('go'); // 开始游戏按钮 // 定义游戏状态 var gameState = { plants: [],// 植物列表 zombies: [],// 僵尸列表 energys:[],//能量列表 bullets: [],// 子弹列表 toolbar:[],// 顶部UI栏列表 labels:[],// 植物选择框列表 geids:[], // 网格坐标列表 isOver: "",// 游戏是否结束 occupy:false, // 选中的植物的对象 delete:false,//选择要删除的植物 grade:0,//得分 startTime1:0, //游戏运行时间 startTime2:0, //游戏运行时间 pro:0.01 }; // 定义植物属性 var PlantUte = { // 向日葵 SunFlower:{ name:"向日葵", price:50, uisrc1:"../images/cards/plants/SunFlower.png", uisrc2:"../images/cards/plants/SunFlowerG.png", datasrc:"../images/plants/sunflower/idle/idle_0.png", url:"../images/plants/sunflower/idle/idle_", count:17, hp:100, attack:0, speed:0, range:0, color:"red" }, // 初级豌豆射手 Peashooter:{ name:"初级豌豆射手", price:50, uisrc1:"../images/cards/plants/Peashooter.png", uisrc2:"../images/cards/plants/PeashooterG.png", datasrc:"../images/plants/peashooter/attack/attack_0.png", url:"../images/plants/peashooter/attack/attack_", count:7, hp:100, attack:5, speed:500, range:500, color:"chartreuse" }, // 中级豌豆射手 Repeater:{ name:"中级豌豆射手", price:100, uisrc1:"../images/cards/plants/Repeater.png", uisrc2:"../images/cards/plants/RepeaterG.png", datasrc:"../images/plants/repeater/attack/attack_0.png", url:"../images/plants/repeater/attack/attack_", count:14, hp:100, attack:10, speed:500, range:500, color:"chartreuse" }, // 高级豌豆射手 GatlingPea:{ name:"高级豌豆射手", price:200, uisrc1:"../images/cards/plants/GatlingPea.png", uisrc2:"../images/cards/plants/GatlingPeaG.png", datasrc:"../images/plants/gatlingpea/attack/attack_0.png", url:"../images/plants/gatlingpea/attack/attack_", count:12, hp:100, attack:10, speed:300, range:500, color:"chartreuse" }, // 番茄炸弹 CherryBomb:{ name:"番茄炸弹", price:200, uisrc1:"../images/cards/plants/CherryBomb.png", uisrc2:"../images/cards/plants/CherryBombG.png", datasrc:"../images/plants/cherrybomb/idle/idle_0.png", url:"../images/plants/cherrybomb/idle/idle_", count:6, hp:50, attack:100, speed:1000, range:57, color:"rgba(0,0,0,0)" }, // 食人花 Chomper:{ name:"食人花", price:300, uisrc1:"../images/cards/plants/Chomper.png", uisrc2:"../images/cards/plants/ChomperG.png", datasrc:"../images/plants/chomper/attack/attack_0.png", url:"../images/plants/chomper/attack/attack_", count:8, hp:100, attack:20, speed:100, range:57, color:"rgba(0,0,0,0)" }, // 坚果防御 WallNut:{ name:"坚果防御", price:50, uisrc1:"../images/cards/plants/WallNut.png", uisrc2:"../images/cards/plants/WallNutG.png", datasrc:"../images/plants/wallnut/idleH/idleH_0.png", url:"../images/plants/wallnut/idleH/idleH_", count:15, hp:1000, attack:0, speed:0, range:0, color:"red" } } // 顶部UI类 function ToolBar(text,style){ this.text = text; this.element = document.createElement('div'); this.element.className = style; this.element.innerText = this.text; topUI.appendChild(this.element); // 添加到游戏选择UI框 gameState.toolbar.push(this) } // 植物选择框类 function Labels(object){ this.object = object; this.price=object.price;// 价格 this.uisrc1 = "url("+object.uisrc1+")";// UI图标路径 this.uisrc2 = "url("+object.uisrc2+")"; this.occupy = false;//是否选中 this.element = document.createElement('div'); // 元素节点 this.element.className = 'plantui'; // 添加样式 this.element.style.backgroundImage = this.uisrc1; this.element.innerText=this.price;// 显示价格 leftUI.appendChild(this.element); // 添加到游戏选择UI框 gameState.labels.push(this); // 添加到植物选择框列表 } // 网格坐标类 function Geid(x,y){ this.x = x; this.y = y; this.occupy = false; this.element = document.createElement("div"); this.element.className = "geid"; this.element.style.top = this.y + 'px'; // 设置位置 this.element.style.left = this.x + 'px'; game.appendChild(this.element) gameState.geids.push(this) } // 定义能量类 function EnErgy(object){ this.object = object; this.x = object.x; this.y = object.y+50; this.hp = true; this.element = document.createElement('img'); // 元素节点 this.element.src = "../images/sun.gif" ; this.element.className = 'energy'; // 添加样式 this.element.style.top = this.y + "px"; this.element.style.left = this.x + "px"; game.appendChild(this.element); // 添加到游戏界面 gameState.energys.push(this); } // 定义植物类 function Plant(x, y,object) { this.x = x; this.y = y; this.object = object; this.Animation = { src:object.datasrc, url:object.url, count:object.count, num:0, animation:false } this.set = "set"+this.x+this.y this.name = object.name;//名字 this.hp = object.hp; // 血量 this.attack = object.attack; // 攻击力 this.speed = object.speed; // 攻击速度 this.range = object.range; // 射程 this.color = object.color;//攻击颜色 this.lastAttackTime = 0; // 上次攻击时间 this.element = document.createElement('div'); // 元素节点 this.element.className = 'plant'; // 添加样式 this.element.style.top = this.y + 'px'; // 设置位置 this.element.style.left = this.x + 'px'; this.element2 = document.createElement('img'); // 元素节点 this.element2.className = 'plantimg'; // 添加样式 this.element2.src = this.Animation.src; this.element2.alt = this.name; this.element3 = document.createElement('span'); // 元素节点 this.element3.className = 'plantspan'; // 添加样式 this.element3.innerText = this.hp; game.appendChild(this.element); // 添加到游戏界面 this.element.appendChild(this.element2); // 添加img标签 this.element.appendChild(this.element3); // 添加h1标签 gameState.plants.push(this); // 添加到植物列表 } // 定义僵尸类 function Zombie(x, y) { this.x = x; this.y = y; // 僵尸动画属性 this.Animation = { src:"../images/zombies/run/run_0.png", url:"../images/zombies/run/run_", count:30, num:0, animation:false } this.hp = 100; // 血量 this.attack = 1; // 攻击力 this.speed = 1; // 移动速度 this.speedG = 50; // 攻击速度 this.range = 50; // 射程 this.rice = false; this.lastAttackTime = 0; // 上次攻击时间 this.element = document.createElement('div'); // 元素节点 this.element.className = 'zombie'; // 添加样式 this.element.style.top = this.y + 'px'; // 设置位置 this.element.style.left = this.x + 'px'; this.element2 = document.createElement('img'); // 元素节点 this.element2.className = 'zombieimg'; // 添加样式 this.element2.src = this.Animation.src; this.element3 = document.createElement('span'); // 元素节点 this.element3.className = 'zombiespan'; // 添加样式 this.element3.innerText = this.hp; game.appendChild(this.element); // 添加到游戏界面 this.element.appendChild(this.element3); // 添加img标签 this.element.appendChild(this.element2); // 添加img标签 gameState.zombies.push(this); // 添加到僵尸列表 } // 定义子弹类 function Bullet(plant, target) { this.x = plant.x; this.y = plant.y; this.speed = 5; // 移动速度 this.attack = plant.attack;//攻击大小 this.target = target; // 目标对象 this.element = document.createElement('div'); // 元素节点 this.element.className = 'bullet'; // 添加样式 this.element.style.backgroundColor = plant.color;//子弹颜色 this.element.style.borderColor = plant.color; this.element.style.left = this.x + 'px';// 设置位置 this.element.style.top = this.y + 'px'; game.appendChild(this.element); // 添加到游戏界面 gameState.bullets.push(this); // 添加到子弹列表 } // 初始化选择框UI var InitUI = function(){ // 初始化网格线坐标 for(var i=0;i<5;i++){ for(var j=0;j<9;j++){ new Geid(parseInt(j*80+240),parseInt(i*100+60)) } } // 创建顶部UI信息栏对象实例 new ToolBar(500,"vessel");//能量收集 new ToolBar("","delete"); //铲子 new ToolBar(0,"grade"); //销毁僵尸数量 new ToolBar("00:00","nz"); //游戏时间 // 创建UI标签栏对象实例 new Labels(PlantUte.SunFlower); //向日葵 new Labels(PlantUte.Peashooter); //初级豌豆射手 new Labels(PlantUte.Repeater); //中级豌豆射手 new Labels(PlantUte.GatlingPea); //高级豌豆射手 // new Labels(PlantUte.CherryBomb); //番茄炸弹 new Labels(PlantUte.Chomper); //食人花 new Labels(PlantUte.WallNut); //坚果防御 } // 游戏时间 var Initnz = function(){ let time = Date.now()-gameState.startTime2; var date = new Date(time); var m = (date.getMinutes() < 10 ? '0' + (date.getMinutes()) : date.getMinutes()) + ':'; var s = (date.getSeconds() < 10 ? '0' + (date.getSeconds()) : date.getSeconds()); var strDate = m+s; gameState.toolbar[3].element.innerText = strDate; } // 开始游戏按钮 Go.onclick = function(){ console.log("开始游戏"); InitUI(); gameState.startTime1 = Date.now(); gameState.startTime2 = Date.now(); // 游戏主循环定时器 var Appset = setInterval(function(){ // 游戏时间 Initnz() // 随机生成僵尸 if (Math.random() < gameState.pro) { console.log("生成僵尸:",gameState.pro) new Zombie(1130, parseInt(Math.random()*5)*100+60); } // 选择要种植的植物 gameState.labels.forEach(function(label){ if(label.price <= gameState.toolbar[0].element.innerText){ label.element.style.backgroundImage = label.uisrc1; label.element.style.color = "black"; }else{ label.element.style.backgroundImage = label.uisrc2; label.element.style.color = "red"; } label.element.onclick = function(){ if(gameState.toolbar[0].element.innerText >= label.price){ gameState.occupy = label.object; gameState.geids.forEach(function(geid){ if(geid.occupy){ geid.element.style.borderColor="rgba(251, 4, 4,0.5)"; }else{ geid.element.style.borderColor="rgba(222, 251, 4, 0.759)"; } }) } } }) // 选择生成植物的网格坐标 gameState.geids.forEach(function(geid){ geid.element.onclick = function(){ if(gameState.occupy){ geid.occupy = true new Plant(geid.x,geid.y,gameState.occupy); gameState.toolbar[0].element.innerText -=gameState.occupy.price; gameState.occupy=false; gameState.geids.forEach(function(geid){ geid.element.style.borderColor="rgba(0, 0, 0,0)"; }) } } }) // 僵尸移动 gameState.zombies.forEach(function(zombie) { if(zombie.x>=170){ zombie.x -= zombie.speed; zombie.element.style.left = zombie.x + 'px'; }else{ gameState.isOver = "挑战失败"; } }); // 植物攻击僵尸 gameState.plants.forEach(function(plant) { gameState.zombies.forEach(function(zombie) { if(zombie.y == plant.y){ if (zombie.x - plant.x <= plant.range && zombie.x > plant.x) { if (Date.now() - plant.lastAttackTime >= plant.speed) { new Bullet(plant,zombie); plant.lastAttackTime = Date.now(); if (zombie.hp <= 0) { gameState.toolbar[2].element.innerText = ++gameState.grade; game.removeChild(zombie.element); gameState.zombies.splice(gameState.zombies.indexOf(zombie), 1); } } } } }); }); // 僵尸攻击植物 gameState.zombies.forEach(function(zombie) { gameState.plants.forEach(function(plant) { if(zombie.y == plant.y){ if(zombie.x-plant.x <= zombie.range && zombie.x > plant.x ){ zombie.x = plant.x+zombie.range; zombie.rice = true; if (Date.now() - zombie.lastAttackTime >= zombie.speedG) { plant.hp-=zombie.attack; // zombie.rice = true; plant.element3.innerText = plant.hp; zombie.lastAttackTime = Date.now(); } // zombie.rice = false; }else{ zombie.rice = false; } } // zombie.rice = false; }); }); // 判断该植物是否死亡,释放网格资源 gameState.plants.forEach(function(plant){ gameState.geids.forEach(function(geid){ if(plant.hp<=0){ if(geid.x == plant.x && geid.y == plant.y){ geid.occupy = false; game.removeChild(plant.element); gameState.plants.splice(gameState.plants.indexOf(plant), 1); } } }) }) // 检测子弹是否击中目标 gameState.bullets.forEach(function(bullet) { if (bullet.target && Math.abs(bullet.x - bullet.target.x) < 60) { bullet.target.hp -= bullet.attack; bullet.target.element3.innerText = bullet.target.hp; game.removeChild(bullet.element); gameState.bullets.splice(gameState.bullets.indexOf(bullet), 1); } else { bullet.x += bullet.speed; bullet.element.style.left = bullet.x + 'px'; } }); // 选择要删除的植物 gameState.toolbar[1].element.onclick = function(){ console.log("删除植物"); gameState.delete = true; gameState.geids.forEach(function(geid){ if(geid.occupy){ geid.element.style.borderColor="rgba(222, 251, 4, 0.759)"; }else{ geid.element.style.borderColor="rgba(251, 4, 4,0.5)"; } }) } // 选择删除植物的网格坐标 gameState.plants.forEach(function(plant){ plant.element.ondblclick = function(){ gameState.geids.forEach(function(geid){ if(gameState.delete){ if(geid.x == plant.x && geid.y == plant.y){ geid.occupy = false; gameState.delete = false; plant.hp = 0; game.removeChild(plant.element); gameState.plants.splice(gameState.plants.indexOf(plant), 1); } gameState.geids.forEach(function(geid){ geid.element.style.borderColor="rgba(0, 0, 0,0)"; }) } }) } }) // 游戏难度,每1分钟提升难度 if(Date.now()-gameState.startTime1>=60000){ gameState.startTime1 = Date.now(); gameState.pro = gameState.pro+0.01; console.log("难度升级:",gameState.pro); if(gameState.pro >=0.02){ game.style.backgroundImage = "url(../images/background2.jpg)"; } if(gameState.pro >=0.04){ game.style.backgroundImage = "url(../images/background1.jpg)"; } if(gameState.pro >=0.06){ game.style.backgroundImage = "url(../images/background2.jpg)"; } if(gameState.pro >=0.07){ gameState.isOver = "挑战成功"; } } // 植物动画 gameState.plants.forEach(function(plant){ if(!plant.Animation.animation){ var plantSet = setInterval(function(){ if(plant.name == "坚果防御" && plant.hp<600 && plant.hp >=300){ plant.Animation.src = "../images/plants/wallnut/idleM/idleM_0.png"; plant.Animation.url = "../images/plants/wallnut/idleM/idleM_"; plant.Animation.count = 10; } if(plant.name == "坚果防御" && plant.hp<300){ plant.Animation.src = "../images/plants/wallnut/idleL/idleL_0.png"; plant.Animation.url = "../images/plants/wallnut/idleL/idleL_"; plant.Animation.count = 14; } if(plant.Animation.num<=plant.Animation.count){ plant.element2.src = plant.Animation.url+plant.Animation.num+".png"; plant.Animation.num++; }else{ plant.Animation.num=0; } if(plant.hp<=0){ clearInterval(plantSet); } },100); plant.Animation.animation = !plant.Animation.animation; } }) // 僵尸动画 gameState.zombies.forEach(function(zombie){ if(!zombie.Animation.animation){ var zombieSet = setInterval(function(){ if(zombie.hp>20 && zombie.rice){ zombie.Animation.src = "../images/zombies/attack_0.png"; zombie.Animation.url = "../images/zombies/attack/attack_"; zombie.Animation.count = 20; }else{ zombie.Animation.src = "../images/zombies/run/run_0.png"; zombie.Animation.url = "../images/zombies/run/run_"; zombie.Animation.count = 30; } if(zombie.hp<=20){ zombie.Animation.src = "../images/zombies/dying/body/body_0.png"; zombie.Animation.url = "../images/zombies/dying/body/body_"; zombie.Animation.count = 17; } if(zombie.hp<=5){ zombie.Animation.src = "../images/zombies/die/die_0.png"; zombie.Animation.url = "../images/zombies/die/die_"; zombie.Animation.count = 9; } if(zombie.hp<=1){ zombie.Animation.src = "../images/zombies/dying/head/head_0.png"; zombie.Animation.url = "../images/zombies/dying/head/head_"; zombie.Animation.count = 11; } if(zombie.Animation.num<=zombie.Animation.count){ zombie.element2.src = zombie.Animation.url+zombie.Animation.num+".png"; zombie.Animation.num++; }else{ zombie.Animation.num=0; } if(zombie.hp<=0){ clearInterval(zombieSet); } },50); zombie.Animation.animation = !zombie.Animation.animation; } }) // 产生小太阳 gameState.plants.forEach(function(plant){ if(plant.name == "向日葵"){ plant.name = "向日葵2"; var energyset = setInterval(function(){ if(plant.hp>0){ new EnErgy(plant); }else{ clearInterval(energyset); } },10000) } }) // 销毁小太阳 gameState.energys.forEach(function(energy){ if(energy.hp){ energy.hp = false; var energyYD = setInterval(function(){ if(energy.y>10){ energy.y--; energy.element.style.top = energy.y+"px"; } if(energy.x>140){ energy.x--; energy.element.style.left = energy.x+"px"; } if(energy.x <= 140 && energy.y <=10){ clearInterval(energyYD); gameState.toolbar[0].element.innerText =parseInt(gameState.toolbar[0].element.innerText)+10; game.removeChild(energy.element); gameState.energys.splice(gameState.energys.indexOf(energy), 1); } },10) } }) // 如果游戏结束,停止循环 if (gameState.isOver == "挑战失败") { var End = document.createElement('div'); // 元素节点 End.className = "end"; game.appendChild(End); clearInterval(Appset); End.ondblclick = function(){ game.removeChild(End); location.reload(); } } // 如果游戏通过,停止循环 if (gameState.isOver == "挑战成功") { var End = document.createElement('div'); // 元素节点 End.className = "vict"; End.innerText = gameState.grade; game.appendChild(End); clearInterval(Appset); End.ondblclick = function(){ game.removeChild(End); location.reload(); } } }, 50); setInterval(function(){ gameState.toolbar[0].element.innerText++; }, 1000); game.removeChild(Go); } </script>

游戏初始化

我们将编写一个InitUI函数来初始化游戏界面,包括植物选择框、网格坐标和能量条等。

function InitUI() { // 初始化UI元素}

游戏循环

游戏的主循环将处理僵尸的生成、植物的放置、攻击逻辑和动画更新。

// 游戏主循环示例 setInterval(function(){ gameState.toolbar[0].element.innerText++; }, 1000);

游戏结束条件

我们将添加逻辑来检测游戏是否结束,并显示相应的胜利或失败界面。

// 如果游戏结束,停止循环if (gameState.isOver == "挑战失败") { var End = document.createElement('div'); // 元素节点 End.className = "end"; game.appendChild(End); clearInterval(Appset); End.ondblclick = function(){ game.removeChild(End); location.reload(); } } // 如果游戏通过,停止循环if (gameState.isOver == "挑战成功") { var End = document.createElement('div'); // 元素节点 End.className = "vict"; End.innerText = gameState.grade; game.appendChild(End); clearInterval(Appset); End.ondblclick = function(){ game.removeChild(End); location.reload(); } }

        在本文中,我们创建了一个简单的植物大战僵尸游戏。虽然这个版本缺少了原版游戏的许多特性,但它提供了一个很好的起点,你可以在此基础上继续扩展和完善。

进一步学习

     

在本教程中,我们创建了一个基于HTML5和JavaScript的简化版植物大战僵尸游戏。虽然这个项目是一个很好的开始,但游戏开发的世界非常广阔,提供了许多深入学习的机会。以下是一些建议,可以帮助你进一步提高你的技能:

1. 学习游戏开发框架

推荐框架

Phaser: 一个非常流行的开源2D游戏框架,提供了丰富的功能,如物理引擎、动画支持和粒子效果。 Unity: 一个强大的游戏引擎,支持2D和3D游戏开发,使用C#作为主要编程语言。 Unreal Engine: 一个以性能和视觉效果著称的商业游戏引擎,使用C++和蓝图(一种可视化编程系统)。

学习资源

官方文档和教程在线课程(如Udemy、Coursera)YouTube教程和游戏开发社区
2. 探索高级编程概念

随着你对游戏开发的深入,你将需要掌握更高级的编程概念,如:

面向对象编程(OOP):帮助你构建模块化和可重用的游戏代码。 设计模式:在游戏开发中常用的软件设计模式,如状态模式、观察者模式等。 算法和数据结构:优化游戏性能和AI行为。
3. 理解游戏设计原理

游戏开发不仅仅是编程,还包括游戏设计的各个方面:

关卡设计:学习如何创建有趣且具有挑战性的关卡。 用户体验(UX):理解玩家的需求和期望,设计直观的用户界面。 叙事和角色开发:为你的游戏添加深度和故事性。
4. 参与游戏开发社区

加入游戏开发社区,与其他开发者交流,获取反馈和灵感:

论坛:如GameDev.net、Unity Forum等。 社交媒体群组:如Reddit、Facebook和LinkedIn上的开发者群组。 本地聚会和会议:参加本地的游戏开发聚会和国际游戏开发者大会(GDC)。
5. 实践和构建项目

实践是学习的最佳方式。尝试构建更多的游戏项目,不断挑战自己:

小型项目:从简单的游戏开始,逐步增加项目的复杂度。 参与游戏制作比赛:如Ludum Dare、Global Game Jam等,这些比赛可以激励你在短时间内快速学习和开发。 开源贡献:为开源游戏项目贡献代码,学习团队协作和项目管理。
6. 学习3D图形和动画

如果你对3D游戏开发感兴趣,那么学习3D图形和动画是必不可少的:

3D建模:使用Blender、Maya或3ds Max等工具学习3D建模。 动画:学习关键帧动画和运动捕捉技术。 着色器和材质:为3D模型创建逼真的视觉效果。
7. 掌握音频和音乐制作

音频是游戏中的重要组成部分,学习音频编辑和音乐制作可以极大地增强游戏体验:

音效设计:使用工具如Audacity或FMOD Studio创建和编辑音效。 音乐制作:学习音乐理论,并使用软件如Ableton Live或FL Studio制作背景音乐。
8. 探索虚拟现实(VR)和增强现实(AR)

随着技术的发展,VR和AR为游戏开发提供了新的可能性:

VR游戏开发:学习如何在Unity或Unreal Engine中开发沉浸式VR体验。 AR应用:使用ARKit(iOS)或ARCore(Android)开发增强现实游戏。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。