We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
每个游戏都会包含场景和角色。要实现一个游戏角色,就要清楚角色在场景中的位置,以及它的运动规律,并能通过数学表达式表现出来。
场景坐标
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小游戏(四) 撞击吧粒子-炫酷技能的实现 》
The text was updated successfully, but these errors were encountered:
学习了
Sorry, something went wrong.
No branches or pull requests
每个游戏都会包含场景和角色。要实现一个游戏角色,就要清楚角色在场景中的位置,以及它的运动规律,并能通过数学表达式表现出来。
场景坐标
canvas 2d的场景坐标系采用平面笛卡尔坐标系统,左上角为原点(0,0),向右为x轴正方向,向下为y轴正方向,坐标系统的1个单位相当于屏幕的1个像素。这对我们进行角色定位至关重要。
Enemy粒子
游戏中的敌人为无数的红色粒子,往同一个方向做匀速运动,每个粒子具有不同的大小。
入口处通过一个循环来创建Enemy粒子,随机生成粒子的位置x, y。并保证每个粒子都位于上图坐标系所在象限中。由于 map.width <= x <= 2 * map.width,所以粒子最开始是看不到的。
接下来只要在update中给粒子一个位移偏量speed,粒子就会做匀速运动。speed越大,速度越快。
由于红色粒子看起来是无穷无尽的,而我们只是创建了有限个粒子,所以需要在粒子离开视界的时候重置粒子的位置。视界之外的位置开始运动,并保证该位置的随机性。
可以用一张图来直观地表示Enemy粒子的运动过程
Player粒子
玩家粒子则由鼠标控制,在上一节中我们已经简单介绍了游戏中的鼠标交互。
而在手机上的实现还略有差别。手机上的做法是监听手指的位移量并让Player粒子做偏移。而不是每次touch都重置粒子的位置,这样体验就会好很多。
Player 粒子值得一讲的就是它飘逸的尾巴。在经过反复尝试了多次后才实现这个效果。
首先想到要让尾巴长度固定,那么在每次render的时候,都在尾部渲染固定数量的粒子。那粒子的位置怎么判断呢?
在每次render的时候,我们往数组添加一个粒子,记录此时的Player坐标,当数组达到一定长度时,删除尾部粒子,添加新粒子。这样尾巴就记录了Player一个短时间内的各个时间点位置。看起来就像是"跟随"在Player粒子后面了。
这样只是记录了一些尾巴上点的位置,我们需要把各个点连起来。这里需要用到lineTo方法。
具体代码实现:
如果只是连接各点,那只能画出Player划过的轨迹,我们还要给尾巴加上惯性效果,注意到上面有这两行代码
每一次render中,让尾巴中的每个点x-1.5, y-1.5。实际上就是让粒子沿着左下方的方向运动,这跟Enemy粒子的方向是一致的。实现了尾巴惯性摆动的效果。
接下来就是添加尾巴上的生命点,这个就比较简单,只需在尾巴上间隔的某些点,画出圆形就可以了
Skill粒子
Skill粒子实际上可以看做是Enemy中的一种特殊粒子,具有和Enemy一样的运动规律。代码中的Skill也是继承自Enemy的(这有点奇怪..)
Skill粒子具有不同的属性和颜色, 实现起来也很简单。
到此游戏中的角色都介绍完了,下一节要讲的是 《从零开始开发一款H5小游戏(四) 撞击吧粒子-炫酷技能的实现 》
The text was updated successfully, but these errors were encountered: