你的浏览器禁用了JavaScript, 请开启后刷新浏览器获得更好的体验!
首页
热门
推荐
精选
登录
|
注册
原生JS实现的h5小游戏-植物大战僵尸
立即下载
用AI写一个
该例子支持:好用才打赏哦
现在下载学习
发布时间:2018-03-30
314人
|
浏览:11146次
|
收藏
|
分享
技术:javascript + html + css + es6
运行环境:对es6语法属性兼容较好的现代浏览器
概述
基于原生javascript、ES6语法、canvas实现的H5小游戏。
详细
## 项目介绍 本项目是利用原生js实现的h5小游戏-植物大战僵尸,主要结合了一下自己对于h5小游戏的理解,结合面向对象的编程思想进行开发,在实现时使用了部分es6语法,对于es6语法不太熟悉的小伙伴可以先查阅相关资料了解一下。 如有需要,可根据自己的需求修改源码样式、源码配置属性代码,实现个性化定制。 以下为文件目录结构示意图,核心代码在`js`文件夹下的四个`common.js`、`main.js`、`game.js`、`scene.js`js文件中 ![](/contentImages/image/20180329/7Ql7gkmfzHXlKo4wiEc.png) **源码**:https://github.com/yangyunhe369/h5-game-plantsVSzombies ## 演示截图 ![](/contentImages/image/20180329/j0tbhnr7QuDM3H61nFQ.png) **demo**:https://yangyunhe369.github.io/jQuery-Yys-Slider ## 实现功能 - 绘制游戏场景:背景、阳光计分板、植物卡片(用于放置植物)、植物(6种)、僵尸(1种) - 植物和僵尸的攻击判定、死亡判定 - 角色动画由一帧一帧图片不停切换绘制实现,可绘制流畅动画效果 - 角色动画根据角色状态自动切换,植物动画包括(普通形态、攻击形态),僵尸动画包括(普通形态、移动形态、攻击形态、濒死形态、死亡形态) - 阳光自动生成、植物放置需消耗阳光,僵尸随机生成 - 游戏包含僵尸、植物独立胜利条件判定 - 游戏状态:Loading、游戏运行、游戏暂停、游戏结束(玩家胜利)、游戏结束(僵尸胜利) ## 目录结构 ``` . ├─ index.html // 首页html │ ├─ css // css样式资源文件 ├─ images // 图片资源文件 └─ js ├─ common.js // 公共方法 ├─ scene.js // 游戏场景相关类 ├─ game.js // 游戏主要运行逻辑 └─ main.js // 游戏运行主函数 ``` # 核心代码 ## 游戏引擎 游戏核心代码是基于ES6的class的方式抽象了的游戏相关函数 ``` javascript class Game { constructor () { ... state: 0, // 游戏状态值,初始默认为 0 state_LOADING: 0, // 准备阶段 state_START: 1, // 游戏开始 state_RUNNING: 2, // 游戏运行 state_STOP: 3, // 游戏暂停 state_PLANTWON: 4, // 游戏结束,玩家胜利 state_ZOMBIEWON: 5, // 游戏结束,僵尸胜利 canvas: document.getElementById("canvas"), // canvas元素 context: document.getElementById("canvas").getContext("2d"), // canvas画布 timer: null, // 轮询定时器 fps: window._main.fps, // 动画帧数 } init () { // 初始化函数 let g = this ... // 设置轮询定时器 g.timer = setInterval(function () { // 根据游戏状态,在canvas中绘制不同游戏场景 }, 1000/g.fps) ... } } ``` 其实核心逻辑很简单,就是定义一个游戏引擎主函数,生成一个定时器以每秒60帧的频率不停在`canvas`画布上绘制游戏场景相关元素,然后在定时器函数中根据当前游戏状态(`游戏准备`、`游戏开始`、`游戏运行`、`游戏暂停`、`游戏结束`)来绘制对应游戏场景。 `loading`游戏状态:游戏引擎绘制了页面载入图片,并添加了一个开始游戏按钮 `start`游戏状态:游戏开始读条倒计时,提醒用户游戏即将开始 `running`游戏状态:绘制游戏运行时所需所有游戏场景素材 `stop`游戏状态:游戏进入暂停阶段,游戏中如生成阳关、僵尸的定时器都将清除,角色动画处于静止状态 `gameover`游戏状态:分为玩家获得胜利以及僵尸获得胜利两种情况,并分别绘制不同游戏结算画面 ## 游戏场景 在这里我将游戏中所有可控制的元素都归于游戏场景中,并且将这些元素都抽象为类,方便管理,这里包括:`植物类`、`僵尸类`、`阳光计分板类`、`植物卡片类`、`动画类`、`子弹类`。 游戏场景中最核心的两个类为植物类、僵尸类,不过在这两个核心类中都会用到动画类,这里我先介绍一下。 ### 动画类(Animation) ``` javascript class Animation{ constructor (role, action, fps) { let a = { type: role.type, // 动画类型(植物、僵尸等等) section: role.section, // 植物或者僵尸类别 action: action, // 根据传入动作生成不同动画对象数组 images: [], // 当前引入动画图片对象数组 img: null, // 当前显示动画图片 imgIdx: 0, // 当前角色图片序列号 count: 0, // 计数器,控制动画运行 fps: fps, // 角色动画运行速度系数,值越小,速度越快 } Object.assign(this, a) } } ``` 这里用到的images,就是通过new Image()的方式生成并添加到images中组成的动画序列: ![](/contentImages/image/20180329/bfgnSEpqWPznfzDauPf.jpg) 其中type和section用于判断当前需要加载植物或僵尸、哪一个动作所对应动画序列,count和fps用于控制当前动画的播放速度,而img用于表示当前所展示的图片对象,即images[imgIdx],其关系类似于以下代码: ``` javascript // 在全局定时器中每1/60秒计算一次 // 获取动画序列长度 let animateLen = images.length // 计数器自增 count++ // 设置当前显示动画序列号 imgIdx = Math.floor(count / fps) // 当一整套动画完成后重置动画计数器 imgIdx === animateLen - 1 ? count = 0 : count = count // 设置当前显示动画图片 img = images[imgIdx] ``` ### 角色类(Role) ``` javascript class Role{ constructor (obj) { let r = { id: Math.random().toFixed(6) * Math.pow(10, 6), // 随机生成 id 值,用于设置当前角色 ID,区分不同角色 type: obj.type, // 角色类型(植物或僵尸) section: obj.section, // 角色类别(豌豆射手、双发射手...) x: obj.x, // x轴坐标 y: obj.y, // y轴坐标 w: 0, // 角色图片宽度 h: 0, // 角色图片高度 row: obj.row, // 角色初始化行坐标 col: obj.col, // 角色初始化列坐标 isAnimeLenMax: false, // 是否处于动画最后一帧,用于判断动画是否执行完一轮 isDel: false, // 判断是否死亡并移除当前角色 isHurt: false, // 判断是否受伤 } Object.assign(this, r) } } ``` 这里的角色类主要用于抽象植物类和僵尸类的公共属性,基本属性包括`type`、`section`、`x`、`y`、`w`、`h`、`row`、`col`,其中`row`和`col`属性用于控制角色在草坪上绘制的横纵坐标(即`x`轴和`y`轴方向位于第几个方格),`section`属性用于区分当前角色到底是哪一种,如豌豆射手、双发射手、加特林射手、普通僵尸。 ### 植物类(Plant) ``` javascript class Plant{ constructor (obj) { let p = { life: 3, // 角色血量 idle: null, // 站立动画对象 attack: null, // 攻击动画对象 bullets: [], // 子弹对象数组 state: 1, // 保存当前状态值,默认为1 state_IDLE: 1, // 站立不动状态 state_ATTACK: 2, // 攻击状态 } Object.assign(this, p) } // 绘制方法 draw(cxt) { // 根据当前植物的状态,分别绘制正常状态动画,以及受伤时的半透明状态动画 let self = this cxt.drawImage(self[stateName].img, self.x, self.y) } // 更新当前植物状态 update() { // 通过动画计数器计算出当前植物显示动画序列的图片 } // 判断当前植物是否可进入攻击状态 canAttack() { // 通过轮询僵尸对象数组,判断处于当前植物同行的僵尸,且进入草坪内时,即开始攻击僵尸 // 目前仅有三种射手类植物可使用子弹攻击,樱桃炸弹属于范围伤害类植物(判断范围为其周围八个格子内) // 攻击成功时,减少对应僵尸血量,并在僵尸血量到达特殊值时,切换其动画(如濒死状态,死亡状态),在血量为 0 时,从僵尸对象数组中移除当前僵尸 } // 射击方法 shoot() { // 当前植物攻击时, bullets 数组添加子弹对象 let self = this self.bullets[self.bullets.length] = Bullet.new(self) } } ``` 植物类的私有属性包括`idel`、`attack`、`bullets`、`state`,其中`idel`和`attack`为动画对象,相信看过上面关于动画类介绍的小伙伴应该能理解其作用,`bullets`即用于保存当前植物的所有子弹对象(同动画类,子弹类也有属性、方法的配置,这里就不详细叙述了)。 关于植物的状态控制属性,如`isHurt`属性会在植物受伤时,切换为`true`,并由此给动画添加一个透明度,模拟受伤效果;`isDel`属性会在植物血量降为0时,将植物从植物对象数组中移除,即不再绘制当前植物;`state`属性用于植物在两种形态中进行切换,即`普通形态`、`攻击形态`,当前状态值为哪种形态,即播放对应形态动画,对应关系如下: ``` javascript state === state_IDLE => // 播放植物普通形态动画 idle state === state_ATTACK => // 播放植物攻击形态动画 attack ``` 攻击形态的切换,这里就涉及需要循环当前植物对象与所有的僵尸对象所组成的数组,判断是否有僵尸处于当前植物对象的射程内(即处于同一行草坪,且进行屏幕显示范围)。 这里主要介绍了植物类的相关属性,其方法包括初始化植物对象、植物绘制、植物射击、更新植物状态、检测植物是否可攻击僵尸... ### 僵尸类(Zombie) ``` javascript class Zombie{ constructor (obj) { let z = { life: 10, // 角色血量 idle: null, // 站立动画对象 run: null, // 奔跑动画对象 attack: null, // 攻击动画对象 dying: null, // 濒临死亡动画对象 die: null, // 死亡动画对象 state: 1, // 保存当前状态值,默认为1 state_IDLE: 1, // 站立不动状态 state_RUN: 2, // 奔跑状态 state_ATTACK: 3, // 攻击状态 state_DYING: 4, // 濒临死亡状态 state_DIE: 5, // 死亡状态 canMove: true, // 判断当前角色是否可移动 attackPlantID: 0, // 当前攻击植物对象 ID speed: 3, // 移动速度 } Object.assign(this, z) } // 绘制方法 draw() { // 根据当前僵尸的状态,分别绘制正常状态动画,以及受伤时的半透明状态动画 let self = this cxt.drawImage(self[stateName].img, self.x, self.y) } // 更新当前僵尸状态 update() { // 动画计数器计算出当前植物显示动画序列的图片 } // 判断当前僵尸是否可进入攻击状态 canAttack() { // 通过轮询植物对象数组,判断处于当前僵尸同行的植物,且进入其攻击范围内时,即开始攻击植物 // 攻击成功时,当前僵尸 canMove 属性将为 false ,记录其 attackPlantID ,即所攻击植物 id 值,并减少对应植物血量; // 在植物血量为 0 时,切换其动画(进入死亡状态),并从植物对象数组中移除该植物,同时 // 将所有攻击该植物的僵尸的状态切换为移动状态, canMove 属性值改为 true } } ``` 这里可以看到僵尸类的很多属性与植物类类似,就不过多叙述了,由于目前只开发了一种僵尸,所以`section`属性是固定值。 关于僵尸的动画对象可能会比植物复杂一点,包含`idle`、`run`、`attack`、`dying`、`die`五种形态的动画序列,其中`dying`和`die`对应僵尸较低血量(`濒死状态`)和血量为`0`(`死亡状态`)时所播放的动画。 在僵尸的控制属性上,与植物同理,这里僵尸的五种动画对象也对应五种状态值,并随状态值的切换而切换。 这里主要介绍了僵尸类的相关属性,其方法包括初始化`实例化僵尸对象`、`绘制僵尸`、`僵尸攻击`、`更新僵尸状态`、`检测僵尸是否可攻击植物`... ## 游戏主函数 在游戏主函数中,将会把之前所有用到的游戏相关类,进行实例化,并保存在`Main类`中,在这里调用start游戏启动函数,将会开启游戏引擎,开始绘制游戏场景,所以游戏启动函数会在页面加载完成后立即调用。 ``` javascript class Main { constructor () { let m = { allSunVal: 200, // 阳光总数量 loading: null, // loading 动画对象 sunnum: null, // 阳光实例对象 cars: [], // 实例化除草车对象数组 cars_info: { // 初始化参数 x: 170, // x 轴坐标 y: 102, // y 轴坐标 position: [ {row: 1}, {row: 2}, {row: 3}, {row: 4}, {row: 5}, ], }, cards: [], // 实例化植物卡片对象数组 cards_info: { // 初始化参数 x: 0, y: 0, position: [ {name: 'peashooter', row: 1, sun_val: 100}, {name: 'repeater', row: 2, sun_val: 150}, {name: 'gatlingpea', row: 3, sun_val: 200}, ] }, plants: [], // 实例化植物对象数组 zombies: [], // 实例化僵尸对象数组 plants_info: { // 初始化参数 type: 'plant', // 角色类型 x: 250, // 初始 x 轴坐标,递增量 80 y: 92, // 初始 y 轴坐标,递增量 100 len: 0, position: [] // section:植物类别,row:横行坐标(最小值为 5),col:竖列坐标(最大值为 9) }, zombies_info: { // 初始化参数 type: 'zombie', // 角色类型 x: 250, // x轴坐标 y: 15, // y轴坐标 position: [] // section:僵尸类别,row:横行坐标(最小值为 9),col:竖列坐标(最大值为 13) }, zombies_idx: 0, // 随机生成僵尸 idx zombies_row: 0, // 随机生成僵尸的行坐标 zombies_iMax: 50, // 随机生成僵尸数量上限 sunTimer: null, // 全局定时器,用于控制全局定时生成阳光 sunTimer_difference: 20, // 定时生成阳光时间差值(单位:秒) zombieTimer: null, // 全局定时器,用于控制全局定时生成僵尸 zombieTimer_difference: 12, // 定时生成僵尸时间差值(单位:秒) game: null, // 游戏引擎对象 fps: 60, } Object.assign(this, m) } // 此处省略部分函数介绍 ... // 游戏启动函数 start() { // 实例化游戏场景篇中的所有类 } } window._main = new Main() window._main.start() ``` 这里就简单介绍下`plants`、`zombies`对象数组;当游戏运行时,所有种植的植物以及生成的僵尸都会配合其相关初始化参数`plants_info`、`zombies_info`进行实例化再分别保存在`plants`、`zombies`对象数组中。
本实例支付的费用只是购买源码的费用,如有疑问欢迎在文末留言交流,如需作者在线代码指导、定制等,在作者开启付费服务后,可以点击“购买服务”进行实时联系,请知悉,谢谢
感谢
9
手机上随时阅读、收藏该文章 ?请扫下方二维码
相似例子推荐
评论
作者
弦云孤赫
2
例子数量
386
帮助
12
感谢
评分详细
可运行:
4.5
分
代码质量:
4.5
分
文章描述详细:
4.5
分
代码注释:
4.5
分
综合:
4.5
分
作者例子
高仿阴阳师官网轮播图效果的jQuery插件
原生JS实现的h5小游戏-植物大战僵尸