小游戏飞行的子弹
上一章节中我们做了一个资源加载器,所以可以很轻松的加载多个资源了...
本章节我们来实现 打飞机 小游戏中的子弹效果
同样,我们也不使用官方的图片,而是使用下面这张雪碧图
- 图片地址为: https://www.twle.cn/static/i/minigame/bullet.png
- 图片大小为
180x60
- 每个子弹大小为
60x60
下载这张图片,并保存到 images
目录
废话不多说,我们先绘制第一颗飞机
我们从底部正中间发射子弹
game.js
var c = wx.createCanvas(); var ctx = c.getContext('2d'); var bullet = wx.createImage(); function reset() { ctx.save(); ctx.fillStyle = "#fff"; ctx.fillRect(0, 0, c.width, c.height); ctx.restore(); } function draw_copy() { ctx.save(); ctx.textAlign = "center" // 居中对齐 ctx.textBaseline = "middle" //垂直居中绘制 ctx.fillStyle = "#aaa"; ctx.font = "16px Arial" // 字体大小 16 像素 ctx.fillText("简单教程,简单编程", c.width / 2, (c.height - 108)) ctx.fillText(" © 2015-2018 www.twle.cn", c.width / 2, (c.height - 128)); ctx.restore(); } bullet.onload = function () { reset(ctx); ctx.drawImage(bullet, 0, 0, 60,60, c.width / 2 - 30, c.height - 30, 30, 30); draw_copy(); } reset(); bullet.src = 'images/bullet.png'
虽然子弹大小为 60x60
,但我们把它绘制小一点,不然一屏幕都放不下几颗子弹
运行效果如下
对子弹进行封装
因为剧情 (接下里的教程) 需要我们对子弹做一些封装
function Bullet() { // 位置 this.x = 0; this.y = 0; //宽高 this.w = 30; this.h = 30; // 飞行速度 this.speed = -2; // 子弹图片 this.img = null; // 子弹类型 this.kind = 1; // 原图位置 this.rx = 0; this.ry = 0; this.rw = 60; this.rh = 60; this.change_kind = function(k) { this.rx = (k-1) * 60; this.ry = 0; } this.move = function(){ this.y += this.speed; }; this.draw = function(ctx){ ctx.drawImage(this.img,this.rx,this.ry, this.rw,this.rh, this.x-this.w/2,this.y-this.h/2, this.w,this.h); } }
注意
- 我们绘制子弹的时候,
x
和y
所代表的,就是子弹中间的位置- 子弹的飞行方向是从下往上
让子弹飞起来
然后我们就可以把子弹放到动画里,让子弹飞起来
var c = wx.createCanvas(); var ctx = c.getContext('2d'); var bullet = null; function Bullet() { // 位置 this.x = 0; this.y = 0; //宽高 this.w = 30; this.h = 30; // 飞行速度 this.speed = 2; // 子弹图片 this.img = null; // 子弹类型 this.kind = 1; // 原图位置 this.rx = 0; this.ry = 0; this.rw = 60; this.rh = 60; this.change_kind = function (k) { this.rx = (k - 1) * 60; this.ry = 0; } this.move = function () { this.y -= this.speed; }; this.draw = function (ctx) { ctx.drawImage(this.img, this.rx, this.ry, this.rw, this.rh, this.x - this.w / 2, this.y - this.h / 2, this.w, this.h); } } function reset() { ctx.save(); ctx.fillStyle = "#fff"; ctx.fillRect(0, 0, c.width, c.height); ctx.restore(); } function draw_copy() { ctx.save(); ctx.textAlign = "center" // 居中对齐 ctx.textBaseline = "middle" //垂直居中绘制 ctx.fillStyle = "#aaa"; ctx.font = "16px Arial" // 字体大小 16 像素 ctx.fillText("简单教程,简单编程", c.width / 2, (c.height - 108)) ctx.fillText(" © 2015-2018 www.twle.cn", c.width / 2, (c.height - 128)); ctx.restore(); } function animate_it() { reset(ctx); bullet.move(); bullet.draw(ctx); draw_copy(); requestAnimationFrame(animate_it); } bullet = new Bullet(); bullet.x = c.width / 2; bullet.y = c.height - 15; bullet.img = wx.createImage(); bullet.img.onload = function () { reset(ctx); bullet.draw(ctx); draw_copy(); requestAnimationFrame(animate_it); } reset(); bullet.img.src = 'images/bullet.png'
运行演示如下
更多子弹
我们每 500ms
发射一颗子弹
这时候要使用一个数组来存储全部的子弹,还要检查子弹是否超出边界,如果超出了边界就删除子弹
game.js
var c = wx.createCanvas(); var ctx = c.getContext('2d'); var Bullets = { _data: [], move:function() { for (var i in this._data) { this._data[i].move(); if ( this._data[i].y <= 0 ){ this._data.splice(i, 1); } } }, add:function(bullet) { this._data.push(bullet); }, draw:function(ctx) { for (var i in this._data) { this._data[i].draw(ctx); } } } function Bullet() { // 位置 this.x = 0; this.y = 0; //宽高 this.w = 30; this.h = 30; // 飞行速度 this.speed = 2; // 子弹图片 this.img = null; // 子弹类型 this.kind = 1; // 原图位置 this.rx = 0; this.ry = 0; this.rw = 60; this.rh = 60; this.change_kind = function (k) { this.rx = (k - 1) * 60; this.ry = 0; } this.move = function () { this.y -= this.speed; }; this.draw = function (ctx) { ctx.drawImage(this.img, this.rx, this.ry, this.rw, this.rh, this.x - this.w / 2, this.y - this.h / 2, this.w, this.h); } } function reset() { ctx.save(); ctx.fillStyle = "#fff"; ctx.fillRect(0, 0, c.width, c.height); ctx.restore(); } function draw_copy() { ctx.save(); ctx.textAlign = "center" // 居中对齐 ctx.textBaseline = "middle" //垂直居中绘制 ctx.fillStyle = "#aaa"; ctx.font = "16px Arial" // 字体大小 16 像素 ctx.fillText("简单教程,简单编程", c.width / 2, (c.height - 108)) ctx.fillText(" © 2015-2018 www.twle.cn", c.width / 2, (c.height - 128)); ctx.restore(); } function animate_it() { reset(ctx); Bullets.move(); Bullets.draw(ctx); draw_copy(); requestAnimationFrame(animate_it); } function create_bullet(img) { var bullet = new Bullet(); bullet.x = c.width / 2; bullet.y = c.height - 15; bullet.img = img; Bullets.add(bullet); } var bullet_img = wx.createImage(); bullet_img.onload = function() { reset(ctx); create_bullet(bullet_img); Bullets.draw(ctx); draw_copy(); setInterval(function(){create_bullet(bullet_img);},500); requestAnimationFrame(animate_it); } reset(); bullet_img.src = 'images/bullet.png'
运行演示如下
让飞机发射子弹
是时候组装起来让飞机发射子弹了
这时候每个子弹的产生过程是这样的
如果按下了手指,那么创建子弹,如果用户抬起手指,则取消创建子弹
game.js
var Resource = { _res: [], // [][] 资源对象数组, 0 表示资源 1 url 2 类型 3 是否加载完成 _load: function (idx, ops) { var res = this._res[idx]; if (!res[3]) { if (res[2] == "audio") this._load_audio(res[1], idx, ops) if (res[2] == "image") this._load_image(res[1], idx, ops); } }, _load_image: function (src, i, ops) { var img = wx.createImage(); var that = this; that._res[i][0] = img; img.onload = function () { that._res[i][3] = true; } img.src = src; }, _load_audio: function (src, i, ops) { var audio = wx.createInnerAudioContext(); if (ops != null) { for (var k in ops) { audio[k] = ops[k]; } } var that = this; that._res[i][0] = audio; audio.onCanplay(function () { that._res[i][3] = true; }); audio.src = src; }, loaded: function (callback) { for (var i = 0; i < this._res.length; i++) { if (!this._res[i][3]) { var that = this; setTimeout(function () { that.loaded(callback); }, 200); return; } } callback(); }, add: function (src, type, id, ops) { this._res.push([null, src, type, false, id]); this._load(this._res.length-1, ops); }, item: function (src) { for (var i = 0; i < this._res.length; i++) { if (this._res[i][1] == src) { return this._res[i][0]; } } return null; } } var Bullets = { _data: [], move: function () { for (var i in this._data) { this._data[i].move(); if (this._data[i].y <= 0) { this._data.splice(i, 1); } } }, add: function (bullet) { this._data.push(bullet); }, draw: function (ctx) { for (var i in this._data) { this._data[i].draw(ctx); } } } function Bullet() { // 位置 this.x = 0; this.y = 0; //宽高 this.w = 30; this.h = 30; // 飞行速度 this.speed = 2; // 子弹图片 this.img = null; // 子弹类型 this.kind = 1; // 原图位置 this.rx = 0; this.ry = 0; this.rw = 60; this.rh = 60; this.change_kind = function (k) { this.rx = (k - 1) * 60; this.ry = 0; } this.move = function () { this.y -= this.speed; }; this.draw = function (ctx) { ctx.drawImage(this.img, this.rx, this.ry, this.rw, this.rh, this.x - this.w / 2, this.y - this.h / 2, this.w, this.h); } } var c = wx.createCanvas(); var ctx = c.getContext('2d'); var img = wx.createImage(); var plane = wx.createImage(); var bgm = wx.createInnerAudioContext(); bgm.loop = true; var bgCanvas = null; var offset = 0; var w = 0; var h = 0; var screenWidth = c.width; var screenHeight = c.height; // 上一次飞机的位置 var lasted = { x: 0, y: 0 }; // 触摸区域是否在飞机内部 var in_plane = false; // 创建点计时器 var t = null; function init_bg() { bgCanvas = wx.createCanvas(); bgCanvas.width = screenWidth; bgCanvas.height = screenHeight + h; var bgctx = bgCanvas.getContext('2d'); var y = 0; var x = 0; // 底部多绘制一张图片 while (y < screenHeight + h) { x = 0; while (x < screenWidth) { bgctx.drawImage(img, x, y, w, h); x += w; } y += h; } } function reset() { ctx.save(); ctx.fillStyle = "#fff"; ctx.fillRect(0, 0, c.width, c.height); ctx.restore(); } function draw_copy() { ctx.save(); ctx.textAlign = "center" // 居中对齐 ctx.textBaseline = "middle" //垂直居中绘制 ctx.fillStyle = "#aaa"; ctx.font = "16px Arial" // 字体大小 16 像素 ctx.fillText("简单教程,简单编程", c.width / 2, (c.height - 36)) ctx.fillText(" © 2015-2018 www.twle.cn", c.width / 2, (c.height - 18)); ctx.restore(); } function animate_it() { reset(ctx); ctx.drawImage(bgCanvas, 0, offset, screenWidth, screenHeight, 0, 0, screenWidth, screenHeight); Bullets.move(); Bullets.draw(ctx); ctx.drawImage(plane, 0, 0, 128, 128, lasted.x - 64, lasted.y - 64, 128, 128); draw_copy(); offset++; if (offset % h == 0) { offset = 0; } requestAnimationFrame(animate_it); } wx.onShow(function () { bgm.play() }) wx.onAudioInterruptionBegin(function () { // 暂停音乐 bgm.stop(); // 中断动画 //cancelAnimationFrame(animate_it); }) wx.onAudioInterruptionEnd(function () { bgm.play() // 恢复动画 cancelAnimationFrame(animate_it); }) wx.onTouchStart(function (e) { var x = e.touches[0].clientX; var y = e.touches[0].clientY; if (plane_hit(x, y)) { in_plane = true; } if (in_plane) { var img = Resource.item('images/bullet.png') create_bullet(img); t = setInterval(function () { create_bullet(img); }, 200); } }); wx.onTouchEnd(function (e) { if (in_plane) { lasted.x = e.touches[0].clientX; lasted.y = e.touches[0].clientY; in_plane = false; clearInterval(t); t = null; } }); // 触摸移动事件 wx.onTouchMove(function (e) { if (in_plane) { lasted.x = e.touches[0].clientX; lasted.y = e.touches[0].clientY; } }) function plane_hit(x, y) { return x > (lasted.x - 64) && x < (lasted.x + 64) && y > (lasted.y - 64) && y < (lasted.y + 64); } function start() { w = screenWidth > img.width ? img.width : screenWidth; h = img.height * (w / img.width); lasted.x = (screenWidth - 128) / 2; lasted.y = screenHeight - 128; bgm.play() init_bg(); ctx.drawImage(plane, 0, 0, 128, 128, lasted.x, lasted.y, 128, 128); draw_copy(); requestAnimationFrame(animate_it); } reset(); function create_bullet(img) { var bullet = new Bullet(); bullet.x = lasted.x; bullet.y = lasted.y; bullet.img = img; Bullets.add(bullet); } Resource.add('images/bg.jpg', 'image'); Resource.add('audio/bgm.mp3', 'audio'); Resource.add('images/plane.png', 'image'); Resource.add('images/bullet.png', 'image'); Resource.loaded(function () { bgm = Resource.item('audio/bgm.mp3'); img = Resource.item('images/bg.jpg'); plane = Resource.item('images/plane.png'); start(); });
运行演示如下
注意
要注意几点,一定要在绘制灰机之前绘制子弹,不然你可能会看到飞机头上飘过的刚发出的子弹
代码已经越来越长了....还好只有最后几步了