javascript開發(fā)植物大戰(zhàn)僵尸

簡介

這是我給社團學弟寫的一個小demo,一個簡易版植物大戰(zhàn)僵尸,基本上涉及了不少簡單而且重要的小知識,對學習前端入門應該還是有些幫助的,現在我?guī)Т蠹襾矸治龇治鲞@個小demo

github地址:https://github.com/likaixuan/record/blob/master/demo/植物大戰(zhàn)僵尸

`8BSJ`5T~SW$RW)0B%G{$V0.png

demo實現的功能點

1.掉落星星、點擊吃掉星星增加積分

2.購買植物并放置在草地上、購買需要支付一定的星星、星星余額不足不能購買星星

3.植物與僵尸有血量、射速、護甲值一類的屬性

4.定時發(fā)送炮彈、產生僵尸、炮彈觸碰僵尸與僵尸觸碰植物時,會經過自身屬性轉換傷害、根據血量不同展示
不同的狀態(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;
            }
            /*星星數量*/
            
            #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軸位置是生成的隨機數 而且這個隨機數是在一個區(qū)間內的(不能超出地圖)這個,通過offset家族的一系列屬性 我們可以判斷星星是不是掉落出界了,出界就刪除這個節(jié)點。

點擊星星加分數
//事件委托
            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);

                }

            }
//設置星星數 減去傳負數、加則傳正數
            function setStar(n) {
                star += n;
                starNumber.innerText = star;
            }

這個我們需要做的就是判斷當這個星星圖片被點擊的時候去給總星數加一個數值、但是我們怎么知道他什么時候被點擊呢? 那么大家肯定說給這個星星添加點擊事件呀,那么問題來了,星星是間隔一段時間創(chuàng)建的一個,也就是說我們要給每個星星都綁定一個單擊事件,這樣其實不是最優(yōu)解,更好的辦法是我們可以對星星的父容器設置一個點擊事件

事件冒泡:就比如我們這個星星是在地圖上面的,我們點擊星星的時候它是觸發(fā)星星還是地圖的click事件呢?答案是先觸發(fā)上面的星星再觸發(fā)下面的地圖,這個其實就是事件冒泡的一個簡單理解(不懂的話可以百度、谷歌一下事件冒泡、事件捕獲)
我們通過給父節(jié)點地圖設置點擊事件,當我們點擊在地圖上方的星星時,其實也是會逐漸往下冒泡的,而我們的點擊事件會有一個默認的參數 event(事件對象) 它有一個屬性 event.target 她就是當前觸發(fā)此事件的目標節(jié)點,比如我點擊在星星上 這個event.target 就是星星。所以就有了我上方的判斷,判斷是不是img標簽而且class叫star 如果點擊的是星星那么就用我們設置的setStar方法去設置總星數。

選擇植物
//事件委托
            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數,上面加了一個if判斷就是為了讓總星數小于該道具star數不能選中該道具,選中的則會加一個樣式,這里需要注意的一點是,明確 html負責結構、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);
                        //植物數組
                        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數、并將新添加的植物添加到數組里,我們是通過一個數組來維護植物列表的、再來判斷植物的攻擊力是不是為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數
                             * 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]);
                                //從數組中刪除
                                bullet.splice(i, 1);
                                break;
                            }
                            //打到地圖外 刪除子彈
                            if(bullet[i].offsetLeft + bullet[i].offsetWidth > road.offsetWidth) {
                                bullet[i].parentNode.removeChild(bullet[i]);
                                //從數組中刪除
                                bullet.splice(i, 1);
                            }

                        }
                    }
                }
            }, 20);

子彈這方面的思路是這樣的,我每一段時間就在有攻擊力的植物面前產生子彈,子彈(所有子彈存在一個數組里)會一直被定時器去控制移動,檢測到子彈與僵尸碰撞時(生成僵尸和子彈類似就不貼代碼了),就會給僵尸減掉一定血量、血量是經過計算的、具體代碼大家可以看看github的完整示例,代碼都很簡單。

基本情況其實就是這樣,那么我們再來總結捋一捋

各種屬性例如血量、攻擊力都存放在html5的自定義屬性里,添加的植物會放置到植物數組里、定時器添加的僵尸會放置到僵尸數組里,子彈會間隔一段時間在有攻擊力的植物面前添加一顆(其實就是定時器遍歷植物列表去添加子彈),定時器會遍歷子彈讓子彈去移動,也會遍歷僵尸讓僵尸去移動,僵尸移動到植物上,以及子彈碰到僵尸都會有一個傷害,這個傷害會由我們單獨封裝的函數去計算。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容