web端使用HTML5开发《植物大战僵尸》小游戏教程

 自制游戏列表

1植物大战僵尸自制HTML5游戏《植物大战僵尸》
2开心消消乐自制HTML5游戏《开心消消乐》
3贪吃蛇自制HTML5游戏《贪吃蛇》
4捕鱼达人自制HTML5游戏《捕鱼达人》
引言

在本文中,我们将一起探索如何使用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)开发增强现实游戏。

提供国人写的强大的html5植物大战僵尸(源码) 写得很棒~占用资源少。 JSPVZ 程序制作进度(2011.1.5) 本程序提供源码由HTML5中文网整理打包下载,该下载包可以使用服务器环境运行,也可以在电脑上双击Index.htm直接运行 另外智能手机可以安装OperaMobile10.1及其以上版本,把代码整个拷贝到手机存储卡上,在浏览器中输入形如“E:/jspvz”形式的地址直接本地运行程序,无需联网 转载使用请勿修改LonelyStar署名,pvz.lonelystar.org网址和PopCap公司版权声明 保留对该JS版植物大战僵尸版权所有 2011.1.5 添加了第二大关的第三小关 修正一大波和最后一波字样无法消失的BUG 2010.12.31 添加了“解谜模式” 调整了程序中关卡对于胜利和失败的算法 几个植物僵尸做了调整 修改了几个BUG 2010.12.27 对初始界面稍作修改 2010.12.9 添加了“靠天吃饭”小游戏 给领带僵尸添加两种形象 修正辣椒爆炸图片的问题 咖啡豆0耗的数据修正 2010.12.8 提高了一下僵尸行走的纵坐标 修正了土豆雷和樱桃炸弹爆炸图片在IE下的问题 调整了一大波僵尸和最后一波僵尸出现的图片效果 2010.12.7 添加了第二大关的两小关 添加了“乱葬岗”小游戏 修改了几个BUG 调整了进度显示 2010.12.1 添加了“贫瘠之地”小游戏 调整游戏初始化界面和选择模式界面 修正第十关IE下运行报错的BUG 修正IE下单机运行有部分图片看不到的问题 修正蘑菇植物无睡眠动画而实际却在睡眠的BUG 修正曾哥蘑菇种植在醒着的大喷菇上仍然睡着的BUG 修正僵尸出场界面显示僵尸种类的BUG 修正地刺和地刺王伤害过高的BUG 加大“僵尸快跑!”的难度
评论 38
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yan-英杰

感谢大佬打赏,我会更加努力创作

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值