九、V2EX微信小程序开发
1.组件模块化设计
a.为什么要对小程序进行组件模块化设计?
微信小程序有代码大小限制
提高代码的复用率
b.如何进行组件模块化设计?
通过WXML的import和include来使用文件模板
使用WXSS的@import来引用WXSS文件
使用JS的require来引用JS文件
2.wx.request方法使用详解
a.wx.request接口实现和服务端的交互(GET,POST,PUT,DELETE等)
b.setData()参数格式:需要先定义that = this,不然页面结构会出错。
十、ThinkPHP5后台接口微信小程序
1.总体架构
a.服务端(处理客户端发送的数据):
ThinkPHP5+MySQL构建REST API
b.客户端(面向用户):
向服务端请求数据,完成自身行为逻辑
c.CMS(本质是对数据库的增删改查):
向服务端请求数据,实现相关功能
2.开发环境安装
a.安装xampp(百度搜索下载)
使用xampp来安装PHP,Apache和mySql
b.安装ThinkPHP5(百度搜索下载)
c.安装PHPStorm
十一、微信小游戏FlyBirds开发
1.小游戏的特点
a.快速体验,短生命周期,转化率高
b.体验优于手机网页
c.不需要像App一样下载注册
2.小游戏的展望
a.是一个趋势,替代过重的APP和体验差的手机网页
b.快速引流,引导用户向APP过渡
c.将作为一种开发理念在更多互联网入口平台流行
3.小游戏的模块分解
game.js:是小游戏全局的入口文件,是小游戏必须有的一个文件
main.js:程序主类,主要用来初始化canvas和一些全局对象,各个精灵和绑定点击事件
Director.js:程序导演类,用来控制游戏的逻辑和精灵的创建与销毁,控制游戏主循环
DataStore.js:存储游戏需要长期保存的变量和需要定时销毁的变量
Resources.js:游戏的资源
ResourceLoader.js:资源加载器,保证游戏是在图片加载完成后开始主循环
Sprite.js:游戏精灵的基类,背景,陆地,铅笔,小鸟等都是它的子类
Background.js:背景类
Land.js:陆地类
UpPencil.js:上半部分铅笔类
DownPencil.js:下半部分铅笔类
Birds.js:小鸟类
Score.js:计分器类
StartButton.js:重新开始按钮类
4.小游戏的编程步骤详解
a.先将小游戏的图片素材导入项目中,并建立一个数组来映射它们
在Resources.js中写入如下代码:
export const Resources = [
['background', 'res/background.png'],
['land', 'res/land.png'],
['pencilUp', 'res/pie_up.png'],
['pencilDown', 'res/pie_down.png'],
['birds', 'res/birds.png'],
['startButton', 'res/start_button.png']
];
b.设计资源加载器功能,在ResourceLoader.js中写如下代码:
//资源文件加载器,确保canvas在图片资源加载完成后才进行渲染
import {Resources} from "./Resources.js"; //导入图片数组
export class ResourceLoader {
constructor() { //构造函数
this.map = new Map(Resources); //将Resources中的数组赋给变量map
for (let [key, value] of this.map) { //for循环,注意ES6的写法
const image = wx.createImage(); //创建image实例
image.src = value; //将image实例的src地址设为数组的value值
this.map.set(key, image); //将map的格式设为每个key对应的value值是image实例
}
}
onLoaded(callback) { //加载函数
let loadedCount = 0;
for (let value of this.map.values()) { //for循环遍历上面绑定的image实例
value.onload = () => { //这里使用了ES6的箭头写法,这样写的好处是清晰明了,不会错乱
loadedCount++; //每遍历一个image实例加一次
if (loadedCount >= this.map.size) { //当遍历完整个map时,返回加载成功
callback(this.map);
}
}
}
}
static create() { //这里使用了工厂模式,这个函数的作用是创建一个加载器实例
return new ResourceLoader();
}
}
c.设计初始化游戏的文件,作为游戏开始的入口,在main.js中写入以下代码:
(注意:main.js中的内容是在开发过程中不断更新的,不是一次就写好的)
//初始化整个游戏的精灵,作为游戏开始的入口
import {ResourceLoader} from "./js/base/ResourceLoader.js"; //导入加载器
import {BackGround} from "./js/runtime/BackGround.js";
import {DataStore} from "./js/base/DataStore.js";
import {Director} from "./js/Director.js";
import {Land} from "./js/runtime/Land.js";
import {Birds} from "./js/player/Birds.js";
import {StartButton} from "./js/player/StartButton.js";
import {Score} from "./js/player/Score.js";
import {ApiExamples} from "./js/ApiExamples.js";
export class Main { //程序主类
constructor() {
this.canvas = wx.createCanvas(); //创建画布
this.ctx = this.canvas.getContext('2d'); //设置画布的显示类型为2D
this.dataStore = DataStore.getInstance(); //创建变量缓存器实例
this.director = Director.getInstance(); //创建导演类实例
const loader = ResourceLoader.create(); //创建资源加载器实例
loader.onLoaded(map => this.onResourceFirstLoaded(map)); //调用加载器的加载资源函数
}
//创建背景音乐
createBackgroundMusic() {
const bgm = wx.createInnerAudioContext(); //微信的创建背景音乐Api
bgm.autoplay = true;
bgm.loop = true;
bgm.src = 'audios/bgm.mp3'; //背景音乐的路径,不宜过大
}
onResourceFirstLoaded(map) { //该函数的作用是当重新开始游戏时
//将资源重置成第一次加载的状态,避免资源重复加载
this.dataStore.canvas = this.canvas;
this.dataStore.ctx = this.ctx;
this.dataStore.res = map;
this.createBackgroundMusic(); //创建背景音乐
const examples = new ApiExamples();
// examples.getUserInfo();
// examples.login();
// examples.getSettings();
// examples.httpExample();
// examples.socketExample();
// examples.download();
this.init();
}
init() {
//首先重置游戏是没有结束的
this.director.isGameOver = false; //设置一个变量来标记游戏是否结束
this.dataStore
.put('pencils', []) //创建存储一组铅笔实例的数组
.put('background', BackGround) //创建背景类
.put('land', Land) //创建陆地类
.put('birds', Birds) //创建小鸟类
.put('score', Score) //创建计分器类
.put('startButton', StartButton); //创建开始按钮类
this.registerEvent();
//创建铅笔要在游戏逻辑运行之前
this.director.createPencil(); //运行前先创建一组铅笔
this.director.run(); //初始化各种资源后,开始运行游戏
}
registerEvent() { //这个事件是在浏览器中创建点击屏幕事件
// this.canvas.addEventListener('touchstart', e => {
// //屏蔽掉JS的事件冒泡
// e.preventDefault();
// if (this.director.isGameOver) {
// console.log('游戏开始');
// this.init();
// } else {
// this.director.birdsEvent();
// }
// });
wx.onTouchStart(() => { //微信小程序的触摸事件
if (this.director.isGameOver) { //判断游戏是否已结束
console.log('游戏开始'); //已结束则将资源初始化
this.init();
} else { //未结束则调用导演类的小鸟事件
this.director.birdsEvent(); //这个函数的作用是刷新小鸟的位置
}
});
}
}
d.设计导演类,控制游戏逻辑,导演类要设计成单例模式
(注意:导演类也是边开发边完善的,不是一次写好的)
在Director.js文件中写入代码:
//导演类,控制游戏的逻辑
import {DataStore} from "./base/DataStore.js";
import {UpPencil} from "./runtime/UpPencil.js";
import {DownPencil} from "./runtime/DownPencil.js";
export class Director {
static getInstance() {
if (!Director.instance) { //如果导演类不存在则创建
Director.instance = new Director(); //导演类只创建一个实例,这就是单例模式
}
return Director.instance; //存在则返回该导演类实例
}
constructor() {
this.dataStore = DataStore.getInstance();
this.moveSpeed = 2;
}
createPencil() { //创建一组铅笔
const minTop = DataStore.getInstance().canvas.height / 8; //这是铅笔的最小高度
const maxTop = DataStore.getInstance().canvas.height / 2; //这是铅笔的最大高度
const top = minTop + Math.random() * (maxTop - minTop); //铅笔的高度取最大高度和最小高度之前的随机值
this.dataStore.get('pencils').push(new UpPencil(top)); //创建UpPecil和DownPencil实例,将它们存入 dataStore中的pencils数组中
this.dataStore.get('pencils').push(new DownPencil(top));
}
birdsEvent() { //循环设置小鸟图片的渲染位置
for (let i = 0; i <= 2; i++) { //以此来达到用户点击屏幕小鸟会往上位移的效果
this.dataStore.get('birds').y[i] =
this.dataStore.get('birds').birdsY[i];
}
this.dataStore.get('birds').time = 0; //重置小鸟的下落时间,效果更好
}
//判断小鸟是否和铅笔撞击
static isStrike(bird, pencil) { //传入的参数为小鸟和铅笔的模型
let s = false;
if (bird.top > pencil.bottom || //小鸟和铅笔撞击的全部4种情况
bird.bottom < pencil.top || //即上下左右
bird.right < pencil.left ||
bird.left > pencil.right
) {
s = true; //满足其中一种则返回True
}
return !s; //否则返回False
}
//判断小鸟是否撞击地板和铅笔
check() {
const birds = this.dataStore.get('birds');
const land = this.dataStore.get('land');
const pencils = this.dataStore.get('pencils');
const score = this.dataStore.get('score');
//地板的撞击判断
if (birds.birdsY[0] + birds.birdsHeight[0] >= land.y) { //当小鸟的位置刚好碰到地板时
console.log('撞击地板啦');
this.isGameOver = true;
return;
}
//小鸟的边框模型
const birdsBorder = { //设置小鸟的边框模型
top: birds.y[0],
bottom: birds.birdsY[0] + birds.birdsHeight[0],
left: birds.birdsX[0],
right: birds.birdsX[0] + birds.birdsWidth[0]
};
const length = pencils.length;
for (let i = 0; i < length; i++) { //循环遍历铅笔数组
const pencil = pencils[i];
const pencilBorder = { //设置铅笔数组里所有的铅笔模型
top: pencil.y,
· bottom: pencil.y + pencil.height,
left: pencil.x,
right: pencil.x + pencil.width
};
if (Director.isStrike(birdsBorder, pencilBorder)) { //循环检测小鸟和每支铅笔是否有碰撞
console.log('撞到水管啦');
this.isGameOver = true; //为真则游戏结束
return;
}
}
//加分逻辑
if (birds.birdsX[0] > pencils[0].x + pencils[0].width //当小鸟刚好越过一组铅笔时
&& score.isScore) {
wx.vibrateShort({ //当小鸟每越过一组铅笔,调用微信的振动Api
success: function () { //让屏幕振动
console.log('振动成功');
}
});
score.isScore = false; //设计一个标志位,实现每触发一次记一次分
score.scoreNumber++; //计分器加一分
}
}
run() { //游戏运行函数,游戏主逻辑
this.check();
if (!this.isGameOver) { //判断isGameOver是否为False,为False表示游戏未结束,正常运行
this.dataStore.get('background').draw(); //先从dataStore中获取背景图片并渲染
const pencils = this.dataStore.get('pencils'); //接着从dataStore中获取铅笔数组
if (pencils[0].x + pencils[0].width <= 0 && //如果铅笔数组的第一组铅笔刚好到达画布的左侧
pencils.length === 4) { //并且铅笔数组目前有2组铅笔
pencils.shift(); //就将铅笔数组的第一组铅笔类剔除
pencils.shift(); //并且将第二组铅笔(3,4)变为第一组铅笔(1,2)
this.dataStore.get('score').isScore = true; //当一组铅笔销毁时,将计分器标志位设为True以计分
}
if (pencils[0].x <= (DataStore.getInstance().canvas.width - pencils[0].width) / 2 && //设计当第一组铅笔运行到画布靠左侧的时候
pencils.length === 2) { //并且数组中只有一组铅笔时
this.createPencil(); //创建一组新的铅笔
} //这样就实现了循环创建铅笔的功能并且将运行至画布外的铅笔销毁
this.dataStore.get('pencils').forEach(function (value) { //遍历铅笔数组,渲染铅笔图片
value.draw();
});
this.dataStore.get('land').draw(); //渲染陆地图片
this.dataStore.get('score').draw(); //渲染计分器
this.dataStore.get('birds').draw(); //渲染小鸟图片
let timer = requestAnimationFrame(() => this.run()); //这里调用了循环动画渲染Api,性能好
this.dataStore.put('timer', timer);
} else { //isGameOver为True则表示游戏结束了
console.log('游戏结束');
this.dataStore.get('startButton').draw(); //游戏结束时在屏幕中央绘制开始按钮图片
cancelAnimationFrame(this.dataStore.get('timer')); //销毁循环动画Api
this.dataStore.destroy(); //销毁资源
//触发微信小游戏垃圾回收
wx.triggerGC();
}
}
}
e.设计精灵的基类,在Sprite.js文件中写入代码:
//精灵的基类,负责初始化精灵加载的资源和大小以及位置
import {DataStore} from "./DataStore.js";
export class Sprite {
constructor(img = null,
srcX = 0,
srcY = 0,
srcW = 0,
srcH = 0,
x = 0, y = 0,
width = 0, height = 0) {
this.dataStore = DataStore.getInstance();
this.ctx = this.dataStore.ctx;
this.img = img;
this.srcX = srcX;
this.srcY = srcY;
this.srcW = srcW;
this.srcH = srcH;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
static getImage(key){ //静态方法获取对应的图片对象
return DataStore.getInstance().res.get(key);
}
draw(img = this.img, //给这些参数一个初始值
srcX = this.srcX,
srcY = this.srcY,
srcW = this.srcW,
srcH = this.srcH,
x = this.x,
y = this.y,
width = this.width,
height = this.height) {
this.ctx.drawImage( //Canvas画布渲染图片的方法
img, //参数img表示传入的img对象
srcX, //要裁剪的起始X坐标
srcY, //要裁剪的起始Y坐标
srcW, //裁剪的宽度
srcH, //裁剪的高度
x, //放置的x坐标
y, //放置的y坐标
width, //要使用的宽度
height //要使用的高度
);
}
}
版权声明:内容来源于互联网和用户投稿 如有侵权请联系删除