用Javascript写一个植物大战僵尸(全网最详细)

1.实现效果

文章目录如下

2.实现代码

index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport" />
  <title>植物大战僵尸v1.2</title>
  <link rel="stylesheet" href="css/common.css">
  <link rel="stylesheet" href="css/style.css">
</head>
<body>
<div id="js-startGame-btn" class="startGame-btn">点击开始游戏</div>
<div id="js-intro-game" class="intro-game">
  欢迎来到 David Yang 的 JavaScript 版植物大战僵尸<br>
  个人博客:<a href="http://www.yangyunhe.me" target="_blank">http://www.yangyunhe.me</a><br>
  程序开发的目的是为了学习,所有版权归 David Yang 所有
</div>
<div id="js-log-btn" class="check-log-btn change-log-btn">点击查看更新日志</div>
<!-- 游戏主场景 -->
<div class="content-box">
  <canvas id="canvas" width="1400" height="600"></canvas>
</div>
<!-- 更新日志 -->
<div class="update-log" style="display: none;">
  <div class="title">更新日志</div>
  <dl>
    <dt>2017.11.27</dt>
    <dd>添加植物-向日葵,可自动生成阳光</dd>
    <dt>2017.11.28</dt>
    <dd>添加植物-坚果墙,修复添加新植物使同行处于攻击状态的僵尸攻击判定失效bug</dd>
    <dd>添加植物-樱桃炸弹</dd>
    <dt>2017.11.29</dt>
    <dd>添加小铲车,可一次性清除整行僵尸,调整植物冷却时间</dd>
    <dt>2018.2.6</dt>
    <dd>添加植物-食人花</dd>
  </dl>
  <input class="close-log-btn change-log-btn" type="button" value="关闭"></input>
</div>
<!-- 卡片列表 -->
<ul class="cards-list">
  <li class="cards-item" data-section="sunflower">
    <div class="card-intro">
      <span>向日葵</span>
      <span>冷却时间:5秒</span>
      <span>向日葵是你收集额外阳光必不可少的植物。为什么不多种一些呢?</span>
    </div>
  </li>
  <li class="cards-item" data-section="wallnut">
    <div class="card-intro">
      <span>坚果墙</span>
      <span>冷却时间:12秒</span>
      <span>墙果具备坚硬的外壳,你可以使用他们来保护其他植物</span>
    </div>
  </li>
  <li class="cards-item" data-section="peashooter">
    <div class="card-intro">
      <span>豌豆射手</span>
      <span>冷却时间:7秒</span>
      <span>向敌人射出豌豆</span>
    </div>
  </li>
  <li class="cards-item" data-section="repeater">
    <div class="card-intro">
      <span>双发豌豆射手</span>
      <span>冷却时间:10秒</span>
      <span>向敌人连续射出两发豌豆</span>
    </div>
  </li>
  <li class="cards-item" data-section="gatlingpea">
    <div class="card-intro">
      <span>加特林射手</span>
      <span>冷却时间:15秒</span>
      <span>向敌人快速射出豌豆</span>
    </div>
  </li>
  <li class="cards-item" data-section="chomper">
    <div class="card-intro">
      <span>食人花</span>
      <span>冷却时间:15秒</span>
      <span>食人花能吞下所有自己可以吞下的僵尸</span>
    </div>
  </li>
  <li class="cards-item" data-section="cherrybomb">
    <div class="card-intro">
      <span>樱桃炸弹</span>
      <span>冷却时间:25秒</span>
      <span>樱桃炸弹能够炸飞一片区域(以鼠标点击位置为中心9格内)的所有僵尸。</span>
    </div>
  </li>
</ul>
<!-- 游戏控制按键 -->
<div class="menu-box">
  <div id="pauseGame" class="contro-btn">暂停</div>
  <div id="restartGame" class="contro-btn">开始游戏</div>
</div>
<!-- 系统自动生成阳光 -->
<img class="sun-img systemSun" src="images/sun.gif" alt="">
<script src="js/common.js"></script>
<script src="js/scene.js"></script>
<script src="js/game.js"></script>
<script src="js/main.js"></script>
</body>
</html>

common.css

@charset 'utf-8';
/*===========================  重置初始样式 start  ===========================*/
* {box-sizing: border-box;}
html,div,body,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td {margin:0;padding:0;border:0;outline:0;font-size:100%;line-height:100%;vertical-align:baseline;background:transparent;}
ol,ul,li,ul li{list-style:none;}
:focus {outline:0;}
ins {text-decoration:none;}
table {border-collapse:collapse;border-spacing:0;}
html,body{height:100%;}article,aside,dialog,details,footer,figure,header,hgroup,menu,media,nav,section{display:block;padding:0;margin:0;}
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,iframe,fieldset,input,textarea,p,blockquote,th,td,button{padding:0;margin:0;}
fieldset,img,button{border:0;}
body,input,select,textarea{font-family:'微软雅黑 Regular','微软雅黑';color:#333;font-size:12px;}

body{background:#fff;}
table{border-collapse:collapse;border-spacing:0;}
a{text-decoration:none;}
a:hover{text-decoration:none;}
/*===========================  重置初始样式 end  ===========================*/


/*===========================  通用样式 start  ===========================*/
/*---------------------------  常用样式 start  ---------------------------*/
.wh_mar{width:1200px;margin:0 auto;}
.fl{float:left;}
.fr{float:right;}
.wh{width:100%;display:inline-block;}
.none{display:none;}
.ov{overflow:hidden;}
.re{position:relative;}
.just{text-align:justify;text-justify:inter-ideograph;}
.wrap{word-break:break-all;word-wrap:break-word;}
/*---------------------------  常用样式 end  ---------------------------*/

/*---------------------------  边距 start  ---------------------------*/
.mt5{margin-top:5px;}.mt10{margin-top:10px;}.mt15{margin-top:15px;}.mt20{margin-top:20px;}.mt25{margin-top:25px;}.mt30{margin-top:30px;}.mt35{margin-top:35px;}.mt40{margin-top:40px;}.mt45{margin-top:45px;}.mt50{margin-top:50px;}
.ml5{margin-left:5px;}.ml10{margin-left:10px;}.ml15{margin-left:15px;}.ml20{margin-left:20px;}.ml25{margin-left:25px;}.ml30{margin-left:30px;}
.mr5{margin-right:5px;}.mr10{margin-right:10px;}.mr15{margin-right:15px;}.mr20{margin-right:20px;}.mr25{margin-right:25px;}.mr30{margin-right:30px;}
.mb5{margin-bottom:5px;}.mb10{margin-bottom:10px;}.mb15{margin-bottom:15px;}.mb20{margin-bottom:20px;}.mb25{margin-bottom:25px;}.mb30{margin-bottom:30px;}
/*---------------------------  边距 end  ---------------------------*/

/*---------------------------  填充 start  ---------------------------*/
.pt5{padding-top:5px;}.pt10{padding-top:10px;}.pt15{padding-top:15px;}.pt20{padding-top:20px;}.pt25{padding-top:25px;}.pt30{padding-top:30px;}
.pl5{padding-left:5px;}.pl10{padding-left:10px;}.pl15{padding-left:15px;}.pl20{padding-left:20px;}.pl25{padding-left:25px;}.pl30{padding-left:30px;}
.pr5{padding-right:5px;}.pr10{padding-right:10px;}.pr15{padding-right:15px;}.pr20{padding-right:20px;}.pr25{padding-right:25px;}.pr30{padding-right:30px;}
.pb5{padding-bottom:5px;}.pb10{padding-bottom:10px;}.pb15{padding-bottom:15px;}.pb20{padding-bottom:20px;}.pb25{padding-bottom:25px;}.pb30{padding-bottom:30px;}
/*---------------------------  填充 end  ---------------------------*/

/*===========================  通用样式 end  ===========================*/


styles.css

/* by:弦云孤赫——David Yang
** github - https://github.com/yangyunhe369
*/
body {
  background: #f5f5d8;
}
.content-box {
  display: block;
  width: 900px;
  height: 600px;
  margin: 0;
  overflow: hidden;
  border: 1px solid black;
  border-top: none;
}
#canvas {
  display: block;
  margin-left: -120px;
  border: 1px solid black;
  background: #000;
}
.intro-game {
  position: absolute;
  left: 460px;
  top: 10px;
  width: 430px;
  font-size: 18px;
  line-height: 25px;
  color: #F60;
}
.intro-game a{
  color: #ffbe93;
}
.startGame-btn {
  position: absolute;
  left: 240px;
  top: 520px;
  width: 321px;
  font-size: 18px;
  color: #FF0;
  height: 69px;
  line-height: 80px;
  text-align: center;
  cursor: pointer;
  background: url(../images/LogoLine.png) no-repeat center/contain;
  z-index: 9;
}
.cards-list {
  display: none;
  position: absolute;
  left: 0;
  top: 0;
  width: 100px;
  z-index: 99;
}
.cards-list .cards-item {
  position: relative;
  float: left;
  width: 100%;
  height: 60px;
  cursor: pointer;
}
.cards-list .cards-item:hover .card-intro {
  display: block;
}
.cards-list .cards-item .card-intro {
  display: none;
  position: absolute;
  left: 100px;
  top: 0;
  width: 150px;
  height: auto;
  padding-bottom: 5px;
  border: 1px solid #000;
  background: #FFFFDD;
  color: #000;
}
.cards-list .cards-item .card-intro span {
  display: inline-block;
  width: 100%;
  height: 20px;
  font-size: 12px;
  line-height: 20px;
  text-align: center;
}
.menu-box {
  display: none;
  position: absolute;
  left: 674px;
  top: 0;
  width: 226px;
  height: 41px;
  line-height: 41px;
}
.menu-box .contro-btn {
  display: none;
  float: left;
  width: 113px;
  height: 100%;
  line-height: inherit;
  color: #00CB08;
  text-align: center;
  font-size: 16px;
  font-weight: bold;
  font-family: '黑体';
  background: url(../images/button.png) center/cover;
  cursor: pointer;
}
.menu-box .contro-btn.show {
  display: block;
}
.sun-img {
  position: absolute;
  left: 300px;
  top: -100px;
  opacity: 1;
  cursor: pointer;
  z-index: 100;
}
.show {
  display: block;
}
.none {
  display: none;
}
.check-log-btn {
  position: absolute;
  left: 600px;
  top: 532px;
  font-size: 14px;
  color: #FF0;
  height: 50px;
  line-height: 50px;
  text-align: center;
  text-decoration: underline;
  cursor: pointer;
  z-index: 9;
}
.update-log {
  /*display: none;*/
  position: absolute;
  left: 130px;
  top: 50px;
  width: 640px;
  height: 500px;
  text-align: center;
  border: 1px solid #FFF;
  background-color: #000;
  color: #FFE;
  z-index: 250;
  padding: 10px;
}
.update-log .title {
  font-size: 16px;
  color: #FF0;
  margin: 10px 0; 
}
.update-log dl {
  text-align: left;
  height: 400px;
  overflow: auto;
}
.update-log dl dd,.update-log dl dt {
  font-size: 14px;
  line-height: 18px;
}
.update-log dl dd {
  padding-left: 30px;
}
.update-log .close-log-btn {
  position: absolute;
  bottom: 20px;
  left: 260px;
  cursor: pointer;
  width: 120px;
  height: 30px;
  line-height: 24px;
  border-left: 3px solid #85411C;
  border-right: 3px solid #4E250C;
  border-top: 3px solid #85411C;
  border-bottom: 3px solid #4E250C;
  background-color: #8F431B;
  color: #FC6;
  font-weight: bold;
  font-size: 14px;
}

style_mobile.css

/* by:弦云孤赫——David Yang
** github - https://github.com/yangyunhe369
*/
body {
  background: #f5f5d8;
}
.content-box {
  display: block;
  width: 100%;
  height: 100%;
  margin: 0;
  overflow: hidden;
  border-top: none;
}
#canvas {
  display: block;
  background: #000;
}
.intro-game {
  position: absolute;
  left: 55%;
  top: 0;
  width: 4.3rem;
  font-size: .2rem;
  line-height: .3rem;
  color: #F60;
}
.intro-game a{
  color: #ffbe93;
}
.startGame-btn {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 3rem;
  font-size: .18rem;
  color: #FF0;
  height: 1rem;
  line-height: 1.1rem;
  text-align: center;
  cursor: pointer;
  -webkit-transform: translateX(-50%);
  transform: translateX(-50%);
  background: url(../images/LogoLine.png) no-repeat center/contain;
  z-index: 9;
}
.cards-list {
  display: none;
  position: absolute;
  left: 0;
  top: 0;
  width: 100px;
  z-index: 99;
}
.cards-list .cards-item {
  position: relative;
  float: left;
  width: 100%;
  height: 60px;
  cursor: pointer;
}
.cards-list .cards-item:hover .card-intro {
  display: block;
}
.cards-list .cards-item .card-intro {
  display: none;
  position: absolute;
  left: 100px;
  top: 0;
  width: 150px;
  height: auto;
  padding-bottom: 5px;
  border: 1px solid #000;
  background: #FFFFDD;
  color: #000;
}
.cards-list .cards-item .card-intro span {
  display: inline-block;
  width: 100%;
  height: 20px;
  font-size: 12px;
  line-height: 20px;
  text-align: center;
}
.menu-box {
  display: none;
  position: absolute;
  right: 0;
  top: 0;
  width: 2.5rem;
  height: .45rem;
  line-height: .45rem;
}
.menu-box .contro-btn {
  float: left;
  width: 1.25rem;
  height: .45rem;
  line-height: .45rem;
  color: #00CB08;
  text-align: center;
  font-size: .22rem;
  font-weight: bold;
  font-family: '黑体';
  background: url(../images/button.png) center/contain;
  cursor: pointer;
}
.sun-img {
  position: absolute;
  left: 300px;
  top: -100px;
  opacity: 1;
  cursor: pointer;
  z-index: 100;
}
.show {
  display: block;
}
.none {
  display: none;
}
.check-log-btn {
  position: absolute;
  left: 5%;
  top: .2rem;
  font-size: .18rem;
  color: #FF0;
  height: .2rem;
  line-height: .2rem;
  text-align: center;
  text-decoration: underline;
  cursor: pointer;
  z-index: 9;
}
.update-log {
  /*display: none;*/
  position: absolute;
  left: 50%;
  top: 50%;
  width: 80%;
  height: 70%;
  padding: .1rem;
  -webkit-transform: translate(-50%, -50%);
  transform: translate(-50%, -50%);
  text-align: center;
  border: 1px solid #FFF;
  background-color: #000;
  color: #FFE;
  z-index: 250;
}
.update-log .title {
  font-size: .22rem;
  color: #FF0;
  margin: .1rem 0;
}
.update-log dl {
  text-align: left;
  height: 400px;
  overflow: auto;
}
.update-log dl dd,.update-log dl dt {
  font-size: 14px;
  line-height: 18px;
}
.update-log dl dd {
  padding-left: 30px;
}
.update-log .close-log-btn {
  position: absolute;
  bottom: 20px;
  left: 260px;
  cursor: pointer;
  width: 120px;
  height: 30px;
  line-height: 24px;
  border-left: 3px solid #85411C;
  border-right: 3px solid #4E250C;
  border-top: 3px solid #85411C;
  border-bottom: 3px solid #4E250C;
  background-color: #8F431B;
  color: #FC6;
  font-weight: bold;
  font-size: 14px;
}

3.javascript核心代码

/* by:弦云孤赫——David Yang
** github - https://github.com/yangyunhe369
*/
// 封装打印日志方法
const log = console.log.bind(console)
// 生成图片对象方法
const imageFromPath = function (src) {
  let img = new Image()
  img.src = './images/' + src
  return img
}
// 原生动画参数
const keyframesOptions = {
  iterations: 1,
  iterationStart: 0,
  delay: 0,
  endDelay: 0,
  direction: 'alternate',
  duration: 3000,
  fill: 'forwards',
  easing: 'ease-out',
}
// 图片素材路径
const allImg = {
  startBg: 'coverBg.jpg',                         // 首屏背景图
  bg: 'background1.jpg',                          // 游戏背景
  bullet: 'bullet.png',                           // 子弹普通状态
  bulletHit: 'bullet_hit.png',                    // 子弹击中敌人状态
  sunback: 'sunback.png',                         // 阳光背景框
  zombieWon: 'zombieWon.png',                     // 僵尸胜利画面
  car: 'car.png',                                 // 小汽车图片
  loading: {                                      // loading 画面
    write: {
      path: 'loading/loading_*.png',
      len: 3,
    },
  },
  plantsCard: {                                               // 植物卡片
    sunflower: {  // 向日葵
      img: 'cards/plants/SunFlower.png',
      imgG: 'cards/plants/SunFlowerG.png',
    },
    peashooter: { // 豌豆射手
      img: 'cards/plants/Peashooter.png',
      imgG: 'cards/plants/PeashooterG.png',
    },
    repeater: { // 双发射手
      img: 'cards/plants/Repeater.png',
      imgG: 'cards/plants/RepeaterG.png',
    },
    gatlingpea: { // 加特林射手
      img: 'cards/plants/GatlingPea.png',
      imgG: 'cards/plants/GatlingPeaG.png',
    },
    cherrybomb: { // 樱桃炸弹
      img: 'cards/plants/CherryBomb.png',
      imgG: 'cards/plants/CherryBombG.png',      
    },
    wallnut: {  // 坚果墙
      img: 'cards/plants/WallNut.png',
      imgG: 'cards/plants/WallNutG.png',
    },
    chomper: {  // 食人花
      img: 'cards/plants/Chomper.png',
      imgG: 'cards/plants/ChomperG.png',
    },
  },
  plants: {                                                   // 植物 
    sunflower: {  // 向日葵
      idle: {
        path: 'plants/sunflower/idle/idle_*.png',
        len: 18,
      },
    },
    peashooter: { // 豌豆射手
      idle: {
        path: 'plants/peashooter/idle/idle_*.png',
        len: 8,
      },
      attack: {
        path: 'plants/peashooter/attack/attack_*.png',
        len: 8,
      },
    },
    repeater: { // 双发射手
      idle: {
        path: 'plants/repeater/idle/idle_*.png',
        len: 15,
      },
      attack: {
        path: 'plants/repeater/attack/attack_*.png',
        len: 15,
      },
    },
    gatlingpea: { // 加特林射手
      idle: {
        path: 'plants/gatlingpea/idle/idle_*.png',
        len: 13,
      },
      attack: {
        path: 'plants/gatlingpea/attack/attack_*.png',
        len: 13,
      },
    },
    cherrybomb: { // 樱桃炸弹
      idle: {
        path: 'plants/cherrybomb/idle/idle_*.png',
        len: 7,
      },
      attack: {
        path: 'plants/cherrybomb/attack/attack_*.png',
        len: 5,
      },
    },
    wallnut: { // 坚果墙
      idleH: { // 血量高时动画
        path: 'plants/wallnut/idleH/idleH_*.png',
        len: 16,
      },
      idleM: { // 血量中等时动画
        path: 'plants/wallnut/idleM/idleM_*.png',
        len: 11,
      },
      idleL: { // 血量低时动画
        path: 'plants/wallnut/idleL/idleL_*.png',
        len: 15,
      },
    },
    chomper: { // 食人花
      idle: { // 站立动画
        path: 'plants/chomper/idle/idle_*.png',
        len: 13,
      },
      attack: { // 攻击动画
        path: 'plants/chomper/attack/attack_*.png',
        len: 8,
      },
      digest: { // 消化阶段动画
        path: 'plants/chomper/digest/digest_*.png',
        len: 6,
      }
    },
  },
  zombies: {                                            // 僵尸
    idle: { // 站立动画
      path: 'zombies/idle/idle_*.png',
      len: 31,
    },
    run: { // 移动动画
      path: 'zombies/run/run_*.png',
      len: 31,
    },
    attack: { // 攻击动画
      path: 'zombies/attack/attack_*.png',
      len: 21,
    },
    dieboom: { // 被炸死亡动画
      path: 'zombies/dieboom/dieboom_*.png',
      len: 20,
    },
    dying: { // 濒死动画
      head: {
        path: 'zombies/dying/head/head_*.png',
        len: 12,
      },
      body: {
        path: 'zombies/dying/body/body_*.png',
        len: 18,
      },
    },
    die: { // 死亡动画
      head: {
        path: 'zombies/dying/head/head_*.png',
        len: 12,
      },
      body: {
        path: 'zombies/die/die_*.png',
        len: 10,
      },
    },
  }
}

由于代码太长了,在这里免费下载

植物大战僵尸免费下载

  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的植物大战僵尸保龄球馆卡的示例: ```html <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>植物大战僵尸保龄球馆卡</title> <style> canvas { border: 1px solid #000; } </style> </head> <body> <canvas id="myCanvas" width="800" height="600"></canvas> <script> // 获取画布对象 const canvas = document.getElementById("myCanvas"); const ctx = canvas.getContext("2d"); // 加载图片资源 const bgImg = new Image(); bgImg.src = "bg.png"; const pinImg = new Image(); pinImg.src = "pin.png"; const ballImg = new Image(); ballImg.src = "ball.png"; const plantImg = new Image(); plantImg.src = "plant.png"; const zombieImg = new Image(); zombieImg.src = "zombie.png"; // 定义变量 let ballX = 400; let ballY = 550; let ballSpeedX = 5; let ballSpeedY = -5; let plantX = 50; let plantY = 500; let zombieX = 700; let zombieY = 500; let pinX = 350; let pinY = 350; // 绘制场景 function draw() { // 绘制背景 ctx.drawImage(bgImg, 0, 0, canvas.width, canvas.height); // 绘制保龄球 ctx.drawImage(ballImg, ballX, ballY, 50, 50); // 绘制植物 ctx.drawImage(plantImg, plantX, plantY, 100, 100); // 绘制僵尸 ctx.drawImage(zombieImg, zombieX, zombieY, 100, 100); // 绘制保龄球瓶 for (let i = 0; i < 10; i++) { ctx.drawImage(pinImg, pinX + i * 60, pinY, 50, 80); } // 移动保龄球 ballX += ballSpeedX; ballY += ballSpeedY; // 碰撞检测 if (ballX + 50 > canvas.width || ballX < 0) { ballSpeedX = -ballSpeedX; } if (ballY < 0) { ballSpeedY = -ballSpeedY; } else if ( ballY + 50 > plantY && ballX > plantX && ballX < plantX + 100 ) { ballSpeedY = -ballSpeedY; } else if ( ballY + 50 > zombieY && ballX > zombieX && ballX < zombieX + 100 ) { ballSpeedY = -ballSpeedY; } // 绘制下一帧 requestAnimationFrame(draw); } // 启动游戏 draw(); </script> </body> </html> ``` 需要注意的是,以上示例只是一个简单的实现,还有很多细节需要完善,比如碰撞检测、分数计算等。如果需要实现更完整的植物大战僵尸保龄球馆卡,可以参考其他相关的教程和示例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值