簡介
這是我給社團學弟寫的一個小demo芙沥,一個簡易版植物大戰(zhàn)僵尸诲祸,基本上涉及了不少簡單而且重要的小知識,對學習前端入門應該還是有些幫助的而昨,現(xiàn)在我?guī)Т蠹襾矸治龇治鲞@個小demo
github地址:https://github.com/likaixuan/record/blob/master/demo/植物大戰(zhàn)僵尸
demo實現(xiàn)的功能點
1.掉落星星救氯、點擊吃掉星星增加積分
2.購買植物并放置在草地上、購買需要支付一定的星星歌憨、星星余額不足不能購買星星
3.植物與僵尸有血量着憨、射速、護甲值一類的屬性
4.定時發(fā)送炮彈务嫡、產(chǎn)生僵尸甲抖、炮彈觸碰僵尸與僵尸觸碰植物時,會經(jīng)過自身屬性轉(zhuǎn)換傷害心铃、根據(jù)血量不同展示
不同的狀態(tài)圖片
布局
貼一下代碼
@charset "utf-8";
body,
ul {
padding: 0px;
margin: 0px;
}
/*地圖*/
#map {
position: relative;
width: 1400px;
height: 600px;
background: url(1.jpg);
}
/*路*/
#road {
position: absolute;
z-index: 2;
left: 250px;
top: 200px;
background: url(8.png);
width: 755px;
height: 110px;
z-index: 10;
}
/*植物*/
#road .plant {
position: absolute;
width: 50px;
height: 50px;
top: 0px;
bottom: 0px;
margin: auto 0px;
}
/*僵尸*/
#road .createZombies {
position: absolute;
height: 128px;
width: 66px;
top: 0px;
bottom: 0px;
right: 0px;
margin: auto 0px;
z-index: 1000;
}
/*子彈*/
#road .bullet {
position: absolute;
width: 35px;
height: 35px;
top: 0px;
bottom: 0px;
margin: auto 0px;
z-index: 1001;
}
/*道具欄*/
#props {
position: absolute;
bottom: 0px;
left: 100px;
z-index: 1000;
background: gainsboro;
list-style: none;
text-align: center;
}
#props li {
position: relative;
display: inline-block;
height: 50px;
width: 50px;
}
#props li span {
position: absolute;
padding: 2px;
right: 2px;
top: 2px;
border-radius: 100%;
background: gold;
}
#props li img {
border: 2px solid gray;
height: 100%;
width: 100%;
cursor: pointer;
}
#props li img.action {
border: 3px solid green;
}
/*星星數(shù)量*/
#star-number {
position: absolute;
z-index: 1000;
height: 50px;
width: 50px;
border-radius: 50px;
text-align: center;
line-height: 50px;
background: gold;
color: white;
font-weight: 900;
font-size: 20px;
}
/*降落星星*/
.star {
position: absolute;
width: 50px;
height: 50px;
z-index: 1000;
}
代碼的注釋也很清楚了准谚、我的思路就是給容器div一個背景圖片,也就是咱們看到的地圖去扣,然后道具欄呀柱衔、草地呀都是通過定位的方式去搞定的
JS
掉落星星
//降落星星
setInterval(function() {
//創(chuàng)建一個img標簽
var img = document.createElement("img");
//給img標簽賦值上我們寫的star class
img.className = "star";
//將圖片地址賦值給src
img.src = "star.gif"
map.appendChild(img);
//隨機生成img的水平位置 而且不能超出地圖
img.style.left = Math.random() * (map.offsetWidth - img.offsetWidth) + 'px';
setInterval(function() {
img.style.top = img.offsetTop + 5 + "px";
//碰到地圖邊界 刪除img
if(img.offsetTop >= map.offsetHeight - img.offsetHeight) {
map.removeChild(img);
}
}, 100);
}, starTimer);
就是間隔一段時間創(chuàng)建一個星星、而且星星的x軸位置是生成的隨機數(shù) 而且這個隨機數(shù)是在一個區(qū)間內(nèi)的(不能超出地圖)這個厅篓,通過offset家族的一系列屬性 我們可以判斷星星是不是掉落出界了秀存,出界就刪除這個節(jié)點。
點擊星星加分數(shù)
//事件委托
map.onclick = function(event) {
//當點擊時給star
if(event.target.tagName === "IMG" && event.target.className === "star") {
setStar(10);
//誰被點擊 this就是誰 parentNode 就是 this的 父節(jié)點
event.target.parentNode.removeChild(event.target);
}
}
//設置星星數(shù) 減去傳負數(shù)羽氮、加則傳正數(shù)
function setStar(n) {
star += n;
starNumber.innerText = star;
}
這個我們需要做的就是判斷當這個星星圖片被點擊的時候去給總星數(shù)加一個數(shù)值或链、但是我們怎么知道他什么時候被點擊呢? 那么大家肯定說給這個星星添加點擊事件呀档押,那么問題來了澳盐,星星是間隔一段時間創(chuàng)建的一個祈纯,也就是說我們要給每個星星都綁定一個單擊事件,這樣其實不是最優(yōu)解叼耙,更好的辦法是我們可以對星星的父容器設置一個點擊事件
事件冒泡:就比如我們這個星星是在地圖上面的腕窥,我們點擊星星的時候它是觸發(fā)星星還是地圖的click事件呢?答案是先觸發(fā)上面的星星再觸發(fā)下面的地圖筛婉,這個其實就是事件冒泡的一個簡單理解(不懂的話可以百度簇爆、谷歌一下事件冒泡、事件捕獲)
我們通過給父節(jié)點地圖設置點擊事件爽撒,當我們點擊在地圖上方的星星時入蛆,其實也是會逐漸往下冒泡的,而我們的點擊事件會有一個默認的參數(shù) event(事件對象) 它有一個屬性 event.target 她就是當前觸發(fā)此事件的目標節(jié)點硕勿,比如我點擊在星星上 這個event.target 就是星星哨毁。所以就有了我上方的判斷,判斷是不是img標簽而且class叫star 如果點擊的是星星那么就用我們設置的setStar方法去設置總星數(shù)源武。
選擇植物
//事件委托
map.onclick = function(event) {
//選擇道具
if(event.target.tagName === "IMG" && event.target.className === "plant") {
if(event.target.dataset.star <= star) {
clearStyle();
event.target.className = "action plant";
plant = event.target.cloneNode();
}
}
//當點擊時給star
if(event.target.tagName === "IMG" && event.target.className === "star") {
setStar(10);
//誰被點擊 this就是誰 parentNode 就是 this的 父節(jié)點
event.target.parentNode.removeChild(event.target);
}
}
//清除道具選中樣式
function clearStyle() {
var t = props.getElementsByTagName("img");
for(var i = 0; i < t.length; i++) {
t[i].className = "plant";
}
}
思路跟之前點擊星星一樣扼褪,用事件委托的方式去判斷哪個道具被點擊了,道具身上有自定義屬性粱栖,定義了一系列的屬性话浇,比如護甲、hp闹究、購買所需star數(shù)凳枝,上面加了一個if判斷就是為了讓總星數(shù)小于該道具star數(shù)不能選中該道具,選中的則會加一個樣式跋核,這里需要注意的一點是岖瑰,明確 html負責結(jié)構(gòu)、css負責樣式砂代、js負責控制蹋订,雖然我們可以通過js去設置這個選中邊框,但是我們最好是通過class的方式去設置樣式刻伊,讓它們各司其職
放置植物
road.onclick = function(event) {
//植物可擺放的區(qū)間
if(event.offsetX > 25 && event.offsetX + 50 < this.offsetWidth) {
if(!!plant && event.target.className !== "action plant") {
plant.style.left = event.offsetX - 25 + 'px';
//購買植物減去相應star
setStar(-plant.dataset.star);
//放置植物
this.appendChild(plant);
//植物數(shù)組
plantArr.push(plant);
//戰(zhàn)斗力為零 不發(fā)射子彈
if(parseInt(plant.dataset.damage) !== 0) {
//創(chuàng)建子彈
bullet.push(createBullet(plant.dataset.speed, plant.dataset.damage, event.offsetX + 25));
}
//清除道具選中樣式
clearStyle();
//清除選中道具
plant = null;
}
}
}
// 生成子彈
function createBullet(speed, damage, left) {
/*
* speed 射速
* damage 傷害
*/
var img = document.createElement("img");
img.className = 'bullet';
//設置到創(chuàng)建的子彈標簽上
img.dataset.speed = speed;
img.dataset.damage = damage;
img.style.left = left + 'px';
img.src = '6.gif';
road.appendChild(img);
return img;
}
植物不是隨便位置就能放置的露戒,給路加click就是說,我肯定會放置在這條路上捶箱,因為只有在點擊路的時候才會觸發(fā)放置操作智什、也是通過offset系列屬性去控制放置位置區(qū)間、放置成功時要減去對應的star數(shù)丁屎、并將新添加的植物添加到數(shù)組里荠锭,我們是通過一個數(shù)組來維護植物列表的、再來判斷植物的攻擊力是不是為0 為0說明是土豆一類的植物不會發(fā)射子彈晨川,否則創(chuàng)建子彈并將子彈放置到植物前方
發(fā)射子彈
// 間隔一段時間 生成一波子彈
setInterval(function() {
for(var i = 0; i < plantArr.length; i++) {
//戰(zhàn)斗力不為0
if(parseInt(plantArr[i].dataset.damage) !== 0) {
//創(chuàng)建子彈
bullet.push(createBullet(plantArr[i].dataset.speed, plantArr[i].dataset.damage, plantArr[i].offsetLeft + 25));
}
}
}, 9000);
// 讓子彈飛
setInterval(function() {
for(var i = 0; i < bullet.length; i++) {
bullet[i].style.left = bullet[i].offsetLeft + parseInt(bullet[i].dataset.speed) + "px";
for(var j = 0; j < zombiesArr.length; j++) {
//打到僵尸身上了 -30的原因是 圖片有空白
if(bullet[i].offsetLeft + bullet[i].offsetWidth - 30 >= zombiesArr[j].offsetLeft) {
/*
* data-star 所需star數(shù)
* data-hp hp
* data-defense 防御力
* data-damage 攻擊力
* data-speed 攻速
*/
if(bullet[i].offsetLeft - zombiesArr[j].offsetLeft - zombiesArr[j].offsetWidth < 5) {
//計算傷害
calcDamage(zombiesArr[j], bullet[i], '11.gif');
//受傷狀態(tài)
zombiesState(j, zombiesArr[j], zombiesArr);
//從地圖中刪除
road.removeChild(bullet[i]);
//從數(shù)組中刪除
bullet.splice(i, 1);
break;
}
//打到地圖外 刪除子彈
if(bullet[i].offsetLeft + bullet[i].offsetWidth > road.offsetWidth) {
bullet[i].parentNode.removeChild(bullet[i]);
//從數(shù)組中刪除
bullet.splice(i, 1);
}
}
}
}
}, 20);
子彈這方面的思路是這樣的证九,我每一段時間就在有攻擊力的植物面前產(chǎn)生子彈删豺,子彈(所有子彈存在一個數(shù)組里)會一直被定時器去控制移動,檢測到子彈與僵尸碰撞時(生成僵尸和子彈類似就不貼代碼了)愧怜,就會給僵尸減掉一定血量呀页、血量是經(jīng)過計算的、具體代碼大家可以看看github的完整示例拥坛,代碼都很簡單蓬蝶。
基本情況其實就是這樣,那么我們再來總結(jié)捋一捋
各種屬性例如血量猜惋、攻擊力都存放在html5的自定義屬性里疾党,添加的植物會放置到植物數(shù)組里、定時器添加的僵尸會放置到僵尸數(shù)組里惨奕,子彈會間隔一段時間在有攻擊力的植物面前添加一顆(其實就是定時器遍歷植物列表去添加子彈),定時器會遍歷子彈讓子彈去移動竭钝,也會遍歷僵尸讓僵尸去移動梨撞,僵尸移動到植物上,以及子彈碰到僵尸都會有一個傷害香罐,這個傷害會由我們單獨封裝的函數(shù)去計算卧波。