前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Golang语言社区】H5游戏开发从零开始开发一款H5小游戏(三) 攻守阵营,赋予粒子新的生命

【Golang语言社区】H5游戏开发从零开始开发一款H5小游戏(三) 攻守阵营,赋予粒子新的生命

作者头像
李海彬
发布2018-03-27 10:31:50
8110
发布2018-03-27 10:31:50
举报
文章被收录于专栏:Golang语言社区Golang语言社区
每个游戏都会包含场景和角色。要实现一个游戏角色,就要清楚角色在场景中的位置,以及它的运动规律,并能通过数学表达式表现出来。

场景坐标

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

Enemy粒子

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

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

代码语言:javascript
复制
//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都重置粒子的位置,这样体验就会好很多。

代码语言:javascript
复制
//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粒子后面了。

代码语言:javascript
复制
//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粒子的方向是一致的。实现了尾巴惯性摆动的效果。

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

代码语言:javascript
复制
//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小游戏(四) 撞击吧粒子-炫酷技能的实现》。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-06-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Golang语言社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档