从零开始开发一款H5小游戏(二) 创造游戏世界,启动发条
本系列文章对应游戏代码已开源 Sinuous game
上一节介绍了canvas的基础用法,了解了游戏开发所要用到的API。这篇文章开始,我将介绍怎么运用这些API来完成各种各样的游戏效果。这个过程更重要的是参透一些游戏开发的思路和想法,而不是仅仅知道怎么写代码来完成这个游戏。
先用一张图来了解一下整个游戏的构成。
Map表示整个背景地图,作用很简单,就是渲染黑色背景。
Player 表示玩家粒子,它尾巴中带有生命点,我们用Life类来表示。
Enemy为红色的敌人粒子,因为技能粒子和Enemy粒子具有很多共性,所以Skill粒子继承自Enemy粒子。
粒子之间撞击后有爆炸效果,用Paticle来表示爆炸粒子。
简单来说,游戏就是一帧一帧图像的叠加播放,并通过捕获用户反馈来实现游戏中的人机交互。
图像的逐帧播放可以类比为放映电影,通过在荧幕上连续投放图像来产生动作的效果。
首先要创建这样一个荧幕, 并设置银幕的大小。
//index.js
const canvas = document.getElementById('world');
canvas.width = window.innerWidth > 1000 ? 1000 : window.innerWidth;
canvas.height = window.innerHeight;
在游戏中,荧幕对应一个地图,我们将这个地图抽象为一个类,并提供基本的渲染方法。
//Map.js
/**
* 地图类
*/
class Map {
init(options) {
this.canvas = options.canvas;
this.ctx = this.canvas.getContext('2d');
this.width = options.width;
this.height = options.height;
}
clear() {
this.ctx.clearRect(0, 0, this.width, this.height);
}
render() {
this.clear();
this.ctx.fillStyle = "black";
this.ctx.fillRect(0, 0, this.width, this.height);
}
}
export default new Map();
在入口处初始化地图
map.init({
canvas,
width: window.innerWidth > 1000 ? 1000 : window.innerWidth,
height: window.innerHeight
});
荧幕准备好后,怎么放映图像,对应于游戏中的放映机是什么呢?
想想在js中用于定时执行的方法有哪些,setInterval, setTimeout, requestAnimationFrame?
setInterval这个方法在游戏中是不能用的。由于js是单线程,setInterval开启的定时循环间隔会受到CPU使用情况的影响,同时电脑对setInterval的最短间隔也有不同的要求。由于游戏对帧率的要求比较高,所以在游戏中应该避免使用setInterval来执行定时任务。由于无法把握每帧执行的具体时间,setTimeout也有会遇到类似的问题。
懂的人已经懂了,现代的H5游戏开发都是通过requestAnimationFrame来执行循环播放的。它的优势就是能根据浏览器的实时渲染帧率来执行函数,使的动画播放比较流畅。而不会因为函数的执行时间跟定时器时间不同导致的播放卡顿现象。
一般requestAnimationFrame每帧的绘制时间是1000/60 ms。也就是每秒能绘制60帧。好就好在时间不需要我们自己设置,而是浏览器的内在机制。在不同的浏览器中方法名会有所不同,我们通过下面的方法来定义一个requestAnimationFrame函数
const raf = window.requestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.oRequestAnimationFrame
|| window.msRequestAnimationFrame
|| function(callback) {
window.setTimeout(callback, 1000 / 60); //每帧1000/60ms
};
有个这个方法,我们如有神助。只需要在一个动画方法中使用raf调用自身方法。就能实现循环调用的功能,并且如丝般顺滑。使用如下:
(function animate() {
map.render();
raf(animate);
})();
这样就会不断调用map的render方法,实现逐帧播放。只不过map的render方法只是把画布涂黑,所以看起来并没有什么变化。
我们的游戏中有玩家粒子,敌人粒子,还有技能粒子,撞击爆破等效果。我们的游戏就是不断地往animate这个方法中添加内容,在每一帧中渲染多个不同东西,看起来就是整个游戏画面了。我们可以想象一下未来啊animate方法是这样的。
(function animate() {
map.render();
player.render();
enemy.render();
skill.render();
effect.render();
raf(animate);
})();
我们需要扩展player, enemy...等等的render方法。让它们表现出不同的效果。
这样渲染出来的画面还是死的,怎样让每一帧渲染出来的图像有所不同,实现动画的效果呢?
在每个物体类中,都有一个update方法,该方法用于改变物体的位移形状等,所以每一帧渲染出来的画面都会不一样。
//通过update方法来控制物体位移或形态变化
update() {
this.x += 1;
this.y += 1;
}
render() {
cxt.fillRect(this.x, this.y, 10, 10);
}
在animate中,我们需要在每次render后调用update方法
(function animate() {
map.render();
player.render();
player.update();
raf(animate);
})();
这样,借助于游戏的发条,player就动起来了!我们前面所过,游戏就是逐帧播放和人机交互。那怎样来处理玩家反馈呢?
在PC和手机中的所谓玩家反馈通常是鼠标的点击滑动以及手势等动作。通过监听鼠标或手势事件来改变物体的属性,达到控制物体变化的目的。例如让player跟随鼠标移动。
window.addEventListener('mousemove', (e) => {
self.x = e.clientX;
self.y = e.clientY;
});
达到的效果跟update方法本质上是一致的。
至此整个游戏基本原理已经讲得差不多了,下一节要讲的是如何创建各种粒子,还有player那条会动的尾巴。敬请期待《从零开始开发一款H5小游戏(三) 攻守阵营,赋予粒子新的生命》
从零开始开发一款H5小游戏(二) 创造游戏世界,启动发条的更多相关文章
- 从零开始开发一款H5小游戏(三) 攻守阵营,赋予粒子新的生命
本系列文章对应游戏代码已开源 Sinuous game. 每个游戏都会包含场景和角色.要实现一个游戏角色,就要清楚角色在场景中的位置,以及它的运动规律,并能通过数学表达式表现出来. 场景坐标 canv ...
- 手牵手,使用uni-app从零开发一款视频小程序 (系列上 准备工作篇)
系列文章 手牵手,使用uni-app从零开发一款视频小程序 (系列上 准备工作篇) 手牵手,使用uni-app从零开发一款视频小程序 (系列下 开发实战篇) 前言 好久不见,很久没更新博客了,前段时间 ...
- 手牵手,使用uni-app从零开发一款视频小程序 (系列下 开发实战篇)
系列文章 手牵手,使用uni-app从零开发一款视频小程序 (系列上 准备工作篇) 手牵手,使用uni-app从零开发一款视频小程序 (系列下 开发实战篇) 扫码体验,先睹为快 可以扫描下微信小程序的 ...
- 零基础开发一款微信小程序商城
零基础开发一款微信小程序商城 一个朋友问我能不能帮忙做个商城?我一个完整网页都写不出的 菜鸟程序员,我该怎么拒绝呢?好吧,看在小程序这么火的形势下,我还是答应了!找了个开源项目,差不多花了三天时间搞定 ...
- 从零开始开发一款app,所想到的
我在知乎上看到这个问题http://www.zhihu.com/question/27645587.我在阅读了各位大牛的答案后,再加上自己的思考,就有了这篇文章的内容. 从零开始开发一款app ...
- 开发一款图片压缩工具(二):使用 pngquant 实现图片压缩
上一篇我尝试使用了 pillow 库对 png 图片进行了压缩,效果不好.这次我换用 pngquant 来压缩.pngquant 是用于 PNG 图像有损压缩的命令行实用程序和库.压缩程序会显著减小文 ...
- 【转载】如何从零开始开发一款嵌入式产品(20年的嵌入式经验分享学习,来自STM32神舟系列开发板设计师的总结
[好文章值得分享,摘自作者:jesse] 来源:www.armjishu.com作者:jesse转载请注明出处 我的另一篇文章:<STM32嵌入式入门必看之文章-----介绍非常详细!(学STM ...
- 转:从零开始开发一款Android App (from 简书)
转: http://www.jianshu.com/p/a58d15ef5c8b
- Egret白鹭H5小游戏开发入门(二)
前言: 昨天的文章中简单的介绍了Egret白鹭引擎从安装到基本的使用配置等问题,今天着重介绍H5小游戏开发的起步阶段,如Wing面板的使用,素材的处理,类的说明,开始布局等等. 整体概况: 根据上一篇 ...
随机推荐
- go 中 sort 如何排序,源码解读
sort 包源码解读 前言 如何使用 基本数据类型切片的排序 自定义 Less 排序比较器 自定义数据结构的排序 分析下源码 不稳定排序 稳定排序 查找 Interface 总结 参考 sort 包源 ...
- C# Tab键TabIndex使用问题(顺序,不起作用,跳过某个元素等问题)
C#.net语言,winform程序.一个画面中有多个控件,但是在添加的时候顺序是错的,所以现在想改Tab顺序,需要用到TabIndex ,如何设置控件TabIndex 1.选中窗口控件-右键-属性, ...
- Vue-router路由判断页面是否登录,未登录跳转到登录页面
在index.js中 //定义路由 const router = new Router({ routes, strict: process.env.NODE_ENV !== 'production', ...
- c# TextBox只能输入数字的处理方法(完整版各种情况考虑在内,可根据需求灵活修改)
//选择文本框的事件窗口,找到按键输入的方法KeyPress,双击建立新的方法. /// <summary> /// textBox只能输入数字的处理方法 /// </summary ...
- hexo部署到github时,提示typeError [ERR_INVALID_ARG_TYPE] The “mode“ argument must be integer. Receive
hexo部署到github时,提示typeError [ERR_INVALID_ARG_TYPE]: The "mode" argument must be integer. Re ...
- LeetCode-107-二叉树的层序遍历 II
二叉树的层序遍历 II 题目描述:给定一个二叉树,返回其节点值自底向上的层序遍历. (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历) 示例说明请见LeetCode官网. 来源:力扣(Leet ...
- C#发送邮件,可带有Excel附件
记录工作中遇到发邮件带附件的需求,新建控制台应用程序,目录下创建好所需要的定义好的Excel模板! class Program { static string newExcelPath = strin ...
- 微信小程序结合laravel完成签到功能
前端样式未做处理,可将后端数据传至前端进行处理 1.wxml页面 <!--pages/signIn/signIn.wxml--> <view class='signIn'> & ...
- tensorflow源码解析之common_runtime-executor-上
目录 核心概念 executor.h Executor NewLocalExecutor ExecutorBarrier executor.cc structs GraphView ExecutorI ...
- 『德不孤』Pytest框架 — 14、Pytest中的conftest.py文件
目录 1.conftest.py文件介绍 2.conftest.py的注意事项 3.conftest.py的使用 4.不同位置conftest.py文件的优先级 5.conftest.py中Fixtu ...