自制植物大战僵尸:HTML5与JavaScript实现的简单游戏
引言
在本文中,我们将一起探索如何使用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.ythis.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); // 添加到子弹列表}// 初始化选择框UIvar 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 = truenew 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)开发增强现实游戏。
相关文章:
自制植物大战僵尸:HTML5与JavaScript实现的简单游戏
引言 在本文中,我们将一起探索如何使用HTML5和JavaScript来创建一个简单的植物大战僵尸游戏。这不仅是一项有趣的编程挑战,也是学习游戏开发基础的绝佳机会。 什么是植物大战僵尸? 植物大战僵尸是一款流行的策略塔防游戏,玩家需…...
Istio_1.17.8安装
项目背景 按照istio官网的命令一路安装下来,安装好的istio版本为目前的最新版本,1.22.0。而我的k8s集群的版本并不支持istio_1.22的版本,导致ingress-gate网关安装不上,再仔细查看istio的发布文档,如果用istio_1.22版本…...
[数据集][目标检测]室内积水检测数据集VOC+YOLO格式761张1类别
数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):761 标注数量(xml文件个数):761 标注数量(txt文件个数):761 标注类别…...
17_Vue高级监听器生命周期Vue组件组件通信
文章目录 1. 数据监听器watch2. Vue生命周期3. Vue组件4. Vue组件通信Appendix 1. 数据监听器watch 首先watch需要单独引 import {watch} from vuewatch函数监听ref响应式数据 watch(监听的内容,监听行为)监听行为默认为(newValue,oldValue) let firstname ref…...
【ROS使用记录】—— ros使用过程中的rosbag录制播放和ros话题信息相关的指令与操作记录
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、rosbag的介绍二、rosbag的在线和离线录制三、rosbag的播放相关的指令四、其他rosbag和ros话题相关的指令总结 前言 rosbag是ROS(机器人操作系统…...
Laravel 富文本内容
Laravel 获取富文本的纯文本内容-CSDN博客 Laravel 富文本内容里面的图片添加前缀URL-CSDN博客 Laravel 富文本图片的style样式删除-CSDN博客. Laravel 获取富文本中的所有图片-CSDN博客 富文本字体font-famly删除 $data preg_replace(/(<[^>])style["\][^"…...
Spark Python环境搭建与优化:深入剖析四个方面、五个方面、六个方面及七个关键要点
Spark Python环境搭建与优化:深入剖析四个方面、五个方面、六个方面及七个关键要点 在大数据处理领域,Apache Spark凭借其出色的性能和灵活性备受瞩目。而要在Python中利用Spark的强大功能,首先需要搭建一个稳定且高效的Spark Python环境。本…...
【微信小程序开发】小程序中的上滑加载更多,下拉刷新是如何实现的?
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…...
从 Android 恢复已删除的备份录
本文介绍了几种在 Android 上恢复丢失和删除的短信的方法。这些方法都不能保证一定成功,但您可能能够恢复一些短信或其中存储的文件。 首先要尝试什么 首先,尝试保留数据。如果你刚刚删除了信息,请立即将手机置于飞行模式,方法是…...
如何使用Python中的random模块生成随机数
在Python中,random模块提供了多种用于生成随机数的函数。以下是一些基本示例: 生成随机整数: 使用random.randint(a, b)函数生成一个介于a和b之间的随机整数(包括a和b)。 python复制代码 import random random_int …...
AI大数据处理与分析实战--体育问卷分析
AI大数据处理与分析实战–体育问卷分析 前言:前一段时间接了一个需求,使用AI进行数据分析与处理,遂整理了一下大致过程和大致简要结果(更详细就不方便放了)。 文章目录 AI大数据处理与分析实战--体育问卷分析一、数据…...
C++第二十五弹---从零开始模拟STL中的list(下)
✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】 目录 1、函数补充 2、迭代器完善 3、const迭代器 总结 1、函数补充 拷贝构造 思路: 先构造一个头结点,然后将 lt 类中的元…...
STM32/keil把多个c文件编译为静态库lib
把常用的、不经常修改的代码库编译成lib以后,可以加快整个工程的编译速度。 一个常见的应用场景就是,把ST的标准库或HAL库等编译成lib,这样以后再编译整个工程时,就无需再次编译他们了,可以节省编译时间。当然&#x…...
L45---506.相对名次(java)--排序
1.题目描述 2.知识点 (1)String.join(" ", words) 是 Java 中的一个语法,用于将数组或集合中的元素连接成一个单独的字符串,连接时使用指定的分隔符。这里的 " " 是作为分隔符使用的一个空格字符串。 Strin…...
跨网段路由
跨网段路由通常是指在网络中配置路由,以允许不同子网之间的通信。要设置跨网段的永久路由,取决于你是在操作路由器、交换机这样的网络设备,还是在配置个人计算机(如Windows或Linux系统)。下面是两种常见情况下的简要指…...
HO-3D 数据集
// 由于非刚体的追踪比较困难,所以看看刚体数据集 HOnnotate: A method for 3D Annotation of Hand and Object Poses // cvpr20https://arxiv.org/abs/1907.01481 https://github.com/shreyashampali/ho3d https://paperswithcode.com/paper/ho-3d-a-mult…...
Elasticsearch 认证模拟题 - 8
一、题目 在集群中输入以下指令: PUT phones/_doc/1 {"brand":"Samsumg","model":"Galaxy S9","features":[{"type":"os", "value":"Android"},{"type":&q…...
【Postman接口测试】第四节.Postman接口测试项目实战(中)
文章目录 前言五、Postman断言 5.1 Postman断言介绍 5.2 响应状态码断言 5.3 包含指定字符串断言 5.4 JSON数据断言六、参数化 5.1 Postman参数化介绍 5.2 Postman参数化实现 5.3 针对项目登录接口参数化实现 总结 前言 五、Postman断言 5.1 Postman断言介…...
Hadoop的Windows环境准备
一、将Hadoop传输到Windows中 1、备份副本 cp -r /opt/softs/hadoop3.1.3/ /opt/softs/hadoop3.1.3_temp 2、删除备份的share目录 cd /opt/softs/hadoop3.1.3_temp rm -rf share/ 3、下载到Windows中 重命名去掉_temp 4、删除备份文件 rm -rf /opt/softs/hadoop3.1.3_t…...
使用亮数据代理IP爬取PubMed文章链接和邮箱地址
💂 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】🤟 一站式轻松构建小程序、Web网站、移动应用:👉注册地址🤟 基于Web端打造的:👉轻量化工具创作平台💅 想寻找共同学习交…...
electron调用dll时应用程序闪退
electron调用dll时,直接闪退,且用如下方式监听无任何输出: window-all-closed 或 will-quit 此时需要检查传给dll的参数及参数类型是否正确,特别是使用ffi-napi时调用dll,使用 ref-napi定义类型,经常容易…...
单片机原理及技术(三)—— AT89S51单片机(二)(C51编程)
一、AT89S51单片机的并行I/O端口 1.1 P0口 AT89S51的P0口是一个通用的I/O口,可以用于输入和输出。每个引脚都可以通过软件控制为输入或输出模式。 1.1.1 P0口的工作原理 P0口的工作原理是通过对P0寄存器的读写操作来控制P0口的引脚。 输出模式:当P0口…...
摄影店展示服务预约小程序的作用是什么
摄影店包含婚照、毕业照、写真、儿童照、工作照等多个服务项目,虽然如今人们手机打开便可随时拍照摄影,但在专业程度和场景应用方面,却是需要前往专业门店服务获取。 除了进店,也有外部预约及活动、同行合作等场景,重…...
【数据分析基础】实验一 Python运算符、内置函数、序列基本用法
一、实验目的 熟练运用Python运算符。熟练运用Python内置函数。掌握Python的基本输入输出方法。了解lambda表达式作为函数参数的用法。掌握列表、元组、字典、集合的概念和基本用法。了解Python函数式编程模式。 二、实验内容: 1. 在命令模式测试如下命令&#x…...
【Redis】构建强韧的远程Redis连接与端口保障机制完美指南
【Redis】构建强韧的远程Redis连接与端口保障机制完美指南 大家好 我是寸铁👊 总结了【Redis】构建强韧的远程Redis连接与端口保障机制完美指南✨ 喜欢的小伙伴可以点点关注 💝 前言 在当今的软件开发领域中,远程访问和操作数据存储是极为常见…...
Flowable项目启动报错#java.time.LocalDateTime cannot be cast to java.lang.String
Flowable 项目启动后报错 flow项目第一次启动创建表成功,但是第二次启动时报错信息如下: 1、Error creating bean with name ‘appRepositoryServiceBean’ defined in class 2、Error creating bean with name ‘flowableAppEngine’: FactoryBean t…...
《数字电路》
问答题4*5 在数字电路中,三极管经常工作在哪两种开关状态? 在数字电路中,三极管经常工作在饱和导通状态和截止状态。 时序电路根据输出信号分为哪两类? 时序电路根据输出信号分为莫尔型和米里型两类。 写出三种以上常用的二-十…...
STM32F103 点亮LED闪烁与仿真
STM32F103 点亮LED闪烁与仿真 今天给大家分享一下STM32 流水灯简单的仿真吧,我感觉这个提供有用的,但是自己也是第一次使用,主要是感觉曲线很高级。在PWM中查看脉宽很有用。 code: led.c #include "led.h" #include "delay…...
阿里云服务器发送邮件失败 Could not connect to SMTP host: smtp.xxx.com, port: 465;
最近做了一个发送邮件的功能, 在本地调试完成后,部署到阿里云服务器就一直报错, Could not connect to SMTP host: smtp.qiye.aliyun.com, port: 465; 网上也搜索了很多的资料,最后花了好几个小时才解决, 报错日志如下…...
Socket编程权威指南(二)完美掌握TCP流式协议及Socket编程的recv()和send()
在上一篇文章中,我们学习了Socket编程的基础知识,包括创建Socket、绑定地址、监听连接、接收连接等操作。然而,真正的套接字编程远不止于此。本文将重点介绍TCP 流式协议,什么是粘包问题?如何解决粘包问题 ?…...
网站被采集了 一个栏目不收录/网上推广产品哪个网好
合并分支代码,简单操作: 1、切换到master主干代码 2、到git repositories 视图,点击需要合并的分支,例如v1.1.9 点击merge 进行合并 3、然后push to Upstream 进行提交 还有回退上个版本代码Reset 转载于:https://www.cnblogs.com…...
网站用户权限/在线看seo网站
报错提示 1.网上查资料发现原来css-loader和style-loader是被我全局安装了 2.正确做法cnpm install css-loader style-loader --save 3.打包运行成功 转载于:https://www.cnblogs.com/Cavalary/p/7852817.html...
wordpress语言切换 seo/品牌广告视频
索引是帮助mysql获取数据的数据结构。最常见的索引是Btree索引和Hash索引。不同的引擎对于索引有不同的支持:Innodb和MyISAM默认的索引是Btree索引;而Mermory默认的索引是Hash索引。Hash索引所谓Hash索引,当我们要给某张表某列增加索引时&…...
真人性做爰官方网站/成都seo培训
介绍 本文介绍,在Eclipse插件开发过程中,经常会遇到的有关菜单路径和工具栏路径的使用问题。最常见到的可能就是additions了,那除了这个还有些什么呢,请看下文。 菜单 工具栏 查看原文:http://surenpi.com/2015/08/2…...
网站建设公司-山而/今日的最新消息
HTTP介绍 超文本传输协议(HTTP)的设计目的是保证客户端与服务器之间的通信。HTTP 的工作方式是客户端与服务器之间的请求-应答协议。web 浏览器可能是客户端,而计算机上的网络应用程序也可能作为服务器端。HTTP请求方法 GET - 从指定的资源请…...
阿里网站制作需要多少钱/谷歌竞价推广教程
单击服务和应用程序,右边的配置服务或应用程序选择分配给文件服务器的存储磁盘,GDNS自动生成A记录查看修改后台打印文件夹修改打印机服务器属性返回故障转移集群查看状态正常如下图主节点为PRT-02手动切换主节点部署集群打印机此处按正常打印机部署设置&…...