本系列文章对应游戏代码已开源 Sinuous game

每个游戏都会包含场景和角色。要实现一个游戏角色,就要清楚角色在场景中的位置,以及它的运动规律,并能通过数学表达式表现出来。

场景坐标

canvas 2d的场景坐标系采用平面笛卡尔坐标系统,左上角为原点(0,0),向右为x轴正方向,向下为y轴正方向,坐标系统的1个单位相当于屏幕的1个像素。这对我们进行角色定位至关重要。

Enemy粒子

游戏中的敌人为无数的红色粒子,往同一个方向做匀速运动,每个粒子具有不同的大小。

入口处通过一个循环来创建Enemy粒子,随机生成粒子的位置x, y。并保证每个粒子都位于上图坐标系所在象限中。由于 map.width <= x <= 2 * map.width,所以粒子最开始是看不到的。

//index.js
function createEnemy(numEnemy) {
enemys = [];
for (let i = 0; i < numEnemy; i++) {
const x = Math.random() * map.width + map.width;
const y = Math.random() * map.height;
enemys.push(new Enemy({x, y}));
}
}

接下来只要在update中给粒子一个位移偏量speed,粒子就会做匀速运动。speed越大,速度越快。

update() {
this.x -= this.speed; //speed为位移偏量
this.y += this.speed;
}

由于红色粒子看起来是无穷无尽的,而我们只是创建了有限个粒子,所以需要在粒子离开视界的时候重置粒子的位置。视界之外的位置开始运动,并保证该位置的随机性。

//Enemy.js
update() {
this.x -= this.speed; //speed为位移偏量
this.y += this.speed; //粒子从左边离开视界
if (this.x < -10) {
this.x = map.width + 10 + Math.random() * 30;
}
//粒子从底部离开视界
if (this.y > map.height + 10) {
this.y = -10 + Math.random() * -30;
}
}

可以用一张图来直观地表示Enemy粒子的运动过程

Player粒子

玩家粒子则由鼠标控制,在上一节中我们已经简单介绍了游戏中的鼠标交互。

而在手机上的实现还略有差别。手机上的做法是监听手指的位移量并让Player粒子做偏移。而不是每次touch都重置粒子的位置,这样体验就会好很多。

//Player.js
if (isMobile) {
self.moveTo(self.x, self.y);
window.addEventListener('touchstart', e => {
e.preventDefault();
self.touchStartX = e.touches[0].pageX;
self.touchStartY = e.touches[0].pageY;
});
//手机上用位移计算位置
window.addEventListener('touchmove', e => {
e.preventDefault();
let moveX = e.touches[0].pageX - self.touchStartX;
let moveY = e.touches[0].pageY - self.touchStartY;
self.moveTo(self.x + moveX, self.y + moveY);
self.touchStartX = e.touches[0].pageX;
self.touchStartY = e.touches[0].pageY;
});
} else {
let left = (document.getElementById("game").clientWidth -
document.getElementById("world").clientWidth)/2;
window.addEventListener('mousemove', (e = window.event) => {
self.moveTo(e.clientX - left - 10, e.clientY - 30);
});
}

Player 粒子值得一讲的就是它飘逸的尾巴。在经过反复尝试了多次后才实现这个效果。

首先想到要让尾巴长度固定,那么在每次render的时候,都在尾部渲染固定数量的粒子。那粒子的位置怎么判断呢?
在每次render的时候,我们往数组添加一个粒子,记录此时的Player坐标,当数组达到一定长度时,删除尾部粒子,添加新粒子。这样尾巴就记录了Player一个短时间内的各个时间点位置。看起来就像是"跟随"在Player粒子后面了。

//Player.js
render() {
self.recordTail();
} recordTail() {
let self = this;
//保持尾巴粒子个数不变
if (self.tail.length > self.tailLen) {
self.tail.splice(0, self.tail.length - self.tailLen);
}
self.tail.push({
x: self.x,
y: self.y
});
}

这样只是记录了一些尾巴上点的位置,我们需要把各个点连起来。这里需要用到lineTo方法。

具体代码实现:

//Player.js
renderTail() {
let self = this;
let tails = self.tail, prevPot, nextPot;
map.ctx.beginPath();
map.ctx.lineWidth = 2;
map.ctx.strokeStyle = self.color; for(let i = 0; i < tails.length - 1; i++) {
prevPot = tails[i];
nextPot = tails[i + 1];
if (i === 0) {
map.ctx.moveTo(prevPot.x, prevPot.y);
} else {
map.ctx.lineTo(nextPot.x, nextPot.y);
} //保持尾巴最小长度,并有波浪效果
prevPot.x -= 1.5;
prevPot.y += 1.5;
} map.ctx.stroke(); self.renderLife();
}

如果只是连接各点,那只能画出Player划过的轨迹,我们还要给尾巴加上惯性效果,注意到上面有这两行代码

prevPot.x -= 1.5;
prevPot.y += 1.5;

每一次render中,让尾巴中的每个点x-1.5, y-1.5。实际上就是让粒子沿着左下方的方向运动,这跟Enemy粒子的方向是一致的。实现了尾巴惯性摆动的效果。

接下来就是添加尾巴上的生命点,这个就比较简单,只需在尾巴上间隔的某些点,画出圆形就可以了

//Player.js
//渲染生命值节点
renderLife() {
let self = this;
for(let j = 1; j <= self.livesPoint.length; j++) {
let tailIndex = j * 5;
let life = self.livesPoint[j - 1];
life.render(self.tail[tailIndex]);
}
} //Life.js
render(pos) {
let self = this; //粒子撞击后不渲染
if (!this.dead) {
map.ctx.beginPath();
map.ctx.fillStyle = self.color;
map.ctx.arc(pos.x, pos.y, 3, 0, 2 * Math.PI, false);
map.ctx.fill();
}
}

Skill粒子

Skill粒子实际上可以看做是Enemy中的一种特殊粒子,具有和Enemy一样的运动规律。代码中的Skill也是继承自Enemy的(这有点奇怪..)

Skill粒子具有不同的属性和颜色,实现起来也很简单。

//Skill.js
const COLORS = {
shield: '#007766',
gravity: '#225599',
time: '#665599',
minimize: '#acac00',
life: '#009955'
};
const TEXTS = {
shield: '盾',
gravity: '力',
time: '慢',
minimize: '小',
life: '命'
}; render() {
var self = this; map.ctx.beginPath(); self.color = COLORS[self.type]; map.ctx.fillStyle = self.color;
map.ctx.arc(self.x, self.y, self.radius, 0, Math.PI*2, false);
map.ctx.fill();
}

到此游戏中的角色都介绍完了,下一节要讲的是 《从零开始开发一款H5小游戏(四) 撞击吧粒子-炫酷技能的实现》

本文转载于:猿2048https://www.mk2048.com/blog/blog.php?id=h2hjccjakaa

从零开始开发一款H5小游戏(三) 攻守阵营,赋予粒子新的生命的更多相关文章

  1. 从零开始开发一款H5小游戏(二) 创造游戏世界,启动发条

    本系列文章对应游戏代码已开源 Sinuous game 上一节介绍了canvas的基础用法,了解了游戏开发所要用到的API.这篇文章开始,我将介绍怎么运用这些API来完成各种各样的游戏效果.这个过程更 ...

  2. Egret白鹭H5小游戏开发入门(三)

    前言: 在上一篇文章中着重介绍了H5小游戏开发的起步阶段,如Wing面板的使用,素材的处理,类的说明等等,那么今天主要是涉及到场景的创建,loading的修改等等的代码编写. 对于这一节,我在讲解的过 ...

  3. Egret白鹭H5小游戏开发入门(二)

    前言: 昨天的文章中简单的介绍了Egret白鹭引擎从安装到基本的使用配置等问题,今天着重介绍H5小游戏开发的起步阶段,如Wing面板的使用,素材的处理,类的说明,开始布局等等. 整体概况: 根据上一篇 ...

  4. 【沙龙报名中】与微信&云开发官方团队零距离互动,揭秘爆款微信小游戏背后的技术!

    有人说 微信小程序游戏的百花齐放 活像十几年前的4399小游戏称霸互联网的景象 " 歪,斗地主吗,三缺二, 不用下app,小程序就能玩,我保证不抢地主让你抢!" ...... &q ...

  5. Egret白鹭H5小游戏开发入门(一)

    前言: 好久没更新博客了,以前很多都不会,所以常常写博客总结,倒是现在有点点经验了就懒了.在过去的几个月里,在canvas游戏框架方面,撸过了CreateJS,玩得了Egret,又学过PIXI.js. ...

  6. 开发H5小游戏

    Egret白鹭H5小游戏开发入门(一)   前言: 好久没更新博客了,以前很多都不会,所以常常写博客总结,倒是现在有点点经验了就懒了.在过去的几个月里,在canvas游戏框架方面,撸过了CreateJ ...

  7. .Net Core ORM选择之路,哪个才适合你 通用查询类封装之Mongodb篇 Snowflake(雪花算法)的JavaScript实现 【开发记录】如何在B/S项目中使用中国天气的实时天气功能 【开发记录】微信小游戏开发入门——俄罗斯方块

    .Net Core ORM选择之路,哪个才适合你   因为老板的一句话公司项目需要迁移到.Net Core ,但是以前同事用的ORM不支持.Net Core 开发过程也遇到了各种坑,插入条数多了也特别 ...

  8. 手牵手,使用uni-app从零开发一款视频小程序 (系列上 准备工作篇)

    系列文章 手牵手,使用uni-app从零开发一款视频小程序 (系列上 准备工作篇) 手牵手,使用uni-app从零开发一款视频小程序 (系列下 开发实战篇) 前言 好久不见,很久没更新博客了,前段时间 ...

  9. 我用Axure制作了一款火影小游戏 | PM老猫

    Axure不仅仅是一个原型工具,除了原型之外还可以用来制作一些静态网页,这点对于不懂代码或前端的同学来说挺实用.之前整理了一版<Axure函数自查表>,因为感觉内容太多又对前端样式及脚本感 ...

随机推荐

  1. 哈工大 信息安全 实验 Snort与单台防火墙联动实验

    XX大学XX学院 <网络攻击与防御> 实验报告 实验报告撰写要求 实验操作是教学过程中理论联系实际的重要环节,而实验报告的撰写又是知识系统化的吸收和升华过程,因此,实验报告应该体现完整性. ...

  2. Pycharm:安装anaconda中没有的第三方库

    Pycharm需要用到的pyKriging第三方库库,但是下载了Anaconda后无法在Pycharm中搜到,之前还能搜到的,所以一定是因为Anaconda的原因,后来经过摸索,终于找到了解决问题的办 ...

  3. pyinstaller:各种错误及解决方法

    1.DLL load failed 说明没有找到某个DLL 解决方法: 在 D:\Anaconda\Anaconda3\Library\bin 下找到缺失的DLL,复制到dist下 2.No modu ...

  4. 文件上传漏洞之js验证

    0x00 前言 只有前端验证=没有验证 0x01 剔除JS 打开burpsuite,进入Proxy的Options,把Remove all JavaScript选上. 设置浏览器代理直接上传PHP木马 ...

  5. pandas模块篇(终章)及初识mataplotlib

    今日内容概要 时间序列 针对表格数据的分组与聚合操作 其他函数补充(apply) 练习题(为了加深对DataFrame操作的印象) mataplotlib画图模块 今日内容详细 时间序列处理 时间序列 ...

  6. HBase面试

    宕机问题: MapReduce读写HBase HBase特点: 1.大:一个表可以有上亿行,上百万列 2.面向列:面向列表(蔟)的存储和权限控制,列(蔟)独立检索 3.稀疏:对于为空(NULL)的列, ...

  7. 服务端&客户端注册进Eureka

    目录 服务端(接口提供方) 创建项目 导入Eureka客户端POM 启动类添加注解 配置YML 暴漏接口 启动服务 集群 配置成功后页面如下 客户端(接口调用方) 修改Yml文件 配置类 启动类添加注 ...

  8. linux下的硬盘分区、格式化、挂载

    linux下的MBR(msdos)分区.格式化.挂载 在linux下,需要使用一块硬盘. 需要进行以下四步: 识别硬盘-----分区规划-----格式化-----挂载 步骤一:分区规划 MBR模式分区 ...

  9. tp 5 实现邮件发送

    参考博客: https://www.cnblogs.com/ccdr/p/14751548.htmlhttps://www.cnblogs.com/ccdr/p/14751548.html 1:qq邮 ...

  10. php使用cvs导出百万条数据,大量数据

    MySQL CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(45) NOT NULL DEFAUL ...