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,
},
},
}
}
由于代码太长了,在这里免费下载