微信小游戏快速上手
egret微信小游戏开发指南
菜鸟|Egret微信小游戏好友排行榜教程
小程序与小游戏获取用户信息接口调整,请开发者注意升级
openDataContext
文件夹里面有一个index.js
文件,这个文件中可以绑定上传积分等信息到微信后台,从微信后台获取用户信息和积分数据,清除微信后台积分数据,以及微信后台获取微信好友排行榜数据;(这个文件中可以获取微信相关数据
)index.js
文件还有一个SharedCanvas
,是主域和开放数据域都可以访问的一个离屏画布;主域和开放数据域的分工不同,在主域中创建离屏画布并添加到显示容器中,主域通知共享域去获取微信好友列表信息,然后根据这些信息去绘制排行榜;(主域中创建添加移除销毁离屏画板,并传递分数时间等信息给共享域;共享域接受到信息后经过一系列的数据判断处理后去绘制排行榜
);在egret
中有一个Platform.ts
类,对应编译后微信小游戏中的platform.js
文件;主要用于获取平台数据接口的,包括微信授权登录获取用户信息,分享小游戏给好友,分享小游戏到群;
Platform.ts
中全部代码
//正式版中只有方法声明
declare interface Platform {
//获取用户信息
getUserInfo(): Promise<any>;
//微信授权登录
login(): Promise<any>;
//分享转发,主要用于被动转发
showSharemMenu():Promise<any>
//主动转发
shareAppMessage():Promise<any>
openDataContext:any
}
//测试版
class DebugPlatform implements Platform {
async getUserInfo() {
return { nickName: "username" }
}
async login() {
}
async showSharemMenu() {
}
async shareAppMessage() {
}
openDataContext
}
//设置为全局
if (!window.platform) {
window.platform = new DebugPlatform();
}
declare let platform: Platform;
declare interface Window {
platform: Platform
}
platform.js
中全部代码
class WxgamePlatform {
name = 'wxgame'
//登录
login() {
return new Promise((resolve, reject) => {
wx.login({
success: (res) => {
resolve(res)
}
})
})
}
//获取用户信息
getUserInfo() {
let windowWidth = wx.getSystemInfoSync().windowWidth;
let windowHeight = wx.getSystemInfoSync().windowHeight;
return new Promise((resolve, reject) => {
let button = wx.createUserInfoButton({
type: 'text',
text: '获取用户信息',
style: {
left: windowWidth/2 - 100,
top: windowHeight/2 - 20,
width: 200,
height: 40,
lineHeight: 40,
backgroundColor: '#008888',
color: '#ffffff',
textAlign: 'center',
fontSize: 16,
borderRadius: 4
}
});
//允许按钮
button.onTap((res) => {
// button.hide();
var userInfo = res.userInfo;
resolve(userInfo);
console.log(res);
button.destroy();
});
//关闭按钮
button.offTap((res) => {
resolve(res);
button.destroy();
});
});
}
//被动分享
showSharemMenu(){
console.log("微信分享");
return new Promise((resolve,reject) => {
//显示当前页面的转发按钮
wx.showShareMenu({
withShareTicket:true,
success:(res)=>{
console.log("success", res);
},
fail:(res)=>{
console.log("fail", res);
},
complete:(res)=>{
console.log("complete",res);
}
});
//被动转发
wx.onShareAppMessage(() => {
return {
title: '捷达小飞车',
imageUrl: 'openDataContext/assets/icon_first.png' // 图片 URL
}
});
});
}
//主动转发
shareAppMessage() {
return new Promise((resolve, reject) => {
wx.shareAppMessage({
title: '捷达小飞车',
imageUrl: 'openDataContext/assets/icon_first.png' // 图片 URL
})
})
}
openDataContext = new WxgameOpenDataContext();
}
//下面代码自动生成不要动
class WxgameOpenDataContext {
createDisplayObject(type, width, height) {
const bitmapdata = new egret.BitmapData(sharedCanvas);
bitmapdata.$deleteSource = false;
const texture = new egret.Texture();
texture._setBitmapData(bitmapdata);
const bitmap = new egret.Bitmap(texture);
bitmap.width = width;
bitmap.height = height;
if (egret.Capabilities.renderMode == "webgl") {
const renderContext = egret.wxgame.WebGLRenderContext.getInstance();
const context = renderContext.context;
////需要用到最新的微信版本
////调用其接口WebGLRenderingContext.wxBindCanvasTexture(number texture, Canvas canvas)
////如果没有该接口,会进行如下处理,保证画面渲染正确,但会占用内存。
if (!context.wxBindCanvasTexture) {
egret.startTick((timeStarmp) => {
egret.WebGLUtils.deleteWebGLTexture(bitmapdata.webGLTexture);
bitmapdata.webGLTexture = null;
return false;
}, this);
}
}
return bitmap;
}
postMessage(data) {
const openDataContext = wx.getOpenDataContext();
openDataContext.postMessage(data);
}
}
window.platform = new WxgamePlatform();
Main.ts
中runGame
方法中添加: private async runGame() {
//微信授权登录
await platform.login()
//如果已经授权登录就跳过授权登录步骤
if(!egret.localStorage.getItem("nickName")||!egret.localStorage.getItem("avatarUrl")){
await this.loginAndGetUserInfo();
}
await this.loadResource();
this.createGameScene();
//游戏页面都加载完成后展示分享按钮
await platform.showSharemMenu();
const result = await RES.getResAsync("description_json")
this.startAnimation(result);
}
//授权登录获取用户信息
private async loginAndGetUserInfo() {
const userInfo = await platform.getUserInfo();//微信授权登录
console.log("userInfo",userInfo);
if(userInfo){//名称和图片保存在本地
egret.localStorage.setItem("nickName",userInfo.nickName);
egret.localStorage.setItem("avatarUrl",userInfo.avatarUrl);
}
}
微信登录授权获取用户信息效果如下:
微信被动分享需要注意的是platform.showSharemMenu()
必须在创建游戏场景后调用,否则会卡住;添加后会模拟器出现转发
和取消
选项效果如下:
微信主动转发分享:即我们自己添加一个分享按钮点击后分享给好友,代码如下
//转发按钮点击事件
this.forwardBtn.addEventListener(egret.TouchEvent.TOUCH_TAP,()=>{
//主动转发事件
platform.shareAppMessage();
},this);
openDataContext
文件夹的index.js
文件中进行操作wx.getUserCloudStorage
方法获取用户托管信息,wx.setUserCloudStorage
写入用户信息,相关代码, //获取当前用户托管数据当中对应 key 的数据。该接口只可在开放数据域下使用,比较当前分数是否比存储的分数大,如果大就更新数据
function getUserCloudData(data) {
nickName = data.nickName;
myCurrentScore = data.wxgame.score;
console.log("当前分数", myCurrentScore);
wx.getUserCloudStorage({
keyList: ["gameScoreData"],
success: (re) => {
console.log("[wx]success", re);
let userData = re.KVDataList;
console.log("userData", userData);
myShowRank = ShowRanking.ShowLite;
if (userData.length > 0) {
let value = userData[0].value;
let json = JSON.parse(value);
console.log("json", json);
maxScore = json.wxgame.score ? json.wxgame.score : 0;
console.log("maxScore", maxScore);
console.log("最大分数", maxScore);
if (maxScore < myCurrentScore) {//如果当前分数大于最大分数则更新最大分数
updateUserDataToCloud(data);
}else {
if(totalGroup.length <= 0){
preloadFriendData();
} else {
console.log("有数据并且不用更新数据,就直接绘制");
renderDirty = true;//重绘标记
requestAnimationFrameID = requestAnimationFrame(loop);//每一帧绘制
}
}
} else { //第一次为空
updateUserDataToCloud(data);
}
}, fail: (re) => {
console.log("[wx]fail", re);
}, complete: (re) => {
console.log("[wx]complete", re);
}
})
}
//对用户托管数据进行写数据操作。允许同时写多组 KV 数据。上传分数等信息到微信云
function updateUserDataToCloud(data) {
console.log("上传分数", data);
let userKVData = {
key: "gameScoreData",
value: JSON.stringify(data)
}
console.log(userKVData);
wx.setUserCloudStorage({
KVDataList: [userKVData],
success: (re) => {
console.log("[wx]success", re);
preloadFriendData();
}, fail: (re) => {
console.log("[wx]fail", re);
}, complete: (re) => {
console.log("[wx]complete", re);
}
});
}
wx.getFriendCloudStorage
;下面是获取用户信息,并对用户图像进行预加载,然后根据分数排序相关代码//获取好友排行榜数据信息
function preloadFriendData() {
wx.getFriendCloudStorage({
keyList: ["gameScoreData"],
success: (re) => {
console.log("[wx]success", re);
let dataArr = re.data;
var tempArr = [];
let imagUrl = [];
console.log("dataArr", dataArr);
for (var i = 0; i < dataArr.length; i++) {
let objc = dataArr[i];
let oj = {};
oj.key = i;
oj.name = objc.nickname;
oj.url = objc.avatarUrl
oj.score = 0;
if (objc.KVDataList.length > 0) {
let userSetData = objc.KVDataList[0];
let score = JSON.parse(userSetData.value).wxgame.score;
console.log("分数", score);
oj.score = score;
}
imagUrl.push(objc.avatarUrl);
tempArr.push(oj);
}
console.log("tempArr", tempArr);
totalGroup = null;
totalGroup = tempArr;
console.log("totalGroup1", totalGroup);
if (imagUrl.length > 0) {
preloadAvatarUrl(imagUrl);
}
}, fail: (re) => {
console.log("[wx]fail", re);
}, complete: (re) => {
console.log("[wx]complete", re);
}
});
}
//先预加载图片资源
function preloadAvatarUrl(avatarUrlList) {
console.log("imgs", avatarUrlList);
let preloaded = 0;
let assetsAvatar = [];
for (var j = 0; j < avatarUrlList.length; j++) {
const img = wx.createImage();
img.onload = () => {
preloaded++;
if (preloaded == avatarUrlList.length) {
console.log("所有头像加载完成");
console.log("图片", assetsAvatar);
for (var i = 0; i < totalGroup.length; i++) {
let objc = totalGroup[i];
objc.img = assetsAvatar[i];
}
let data = totalGroup.sort(createComprisonFunction("score", false));
for (var i = 0; i < totalGroup.length; i++) {
let objc = totalGroup[i];
objc.key = i + 1;
}
totalGroup = data;
console.log("totalGroup2", totalGroup);
renderDirty = true;//重绘标记
requestAnimationFrameID = requestAnimationFrame(loop);//每一帧绘制
}
}
img.src = avatarUrlList[j];
assetsAvatar.push(img);
}
}
//对象数组,根据对象的key进行排序
function createComprisonFunction(propertyName, isSequence) { //true为顺序,false为逆序
return function (object1, object2) {
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if (isSequence == true) { //顺序排序
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
} else {
if (value1 < value2) {
return 1;
} else if (value1 > value2) {
return -1;
} else {
return 0;
}
}
}
}
group
,添加到group
中;但这group
的必须是舞台的宽高的等比例缩放,不然在共响域绘制就没法计算出正确的位置;this.gameOverBitmap = platform.openDataContext.createDisplayObject(null, this.gameOverGroup.width, this.gameOverGroup.height);
this.gameOverGroup.addChild(this.gameOverBitmap);
//游戏结束绘制分数等信息
private gameOverGroup: eui.Group;
// private gameOVerMask: egret.Shape;
private gameOverBitmap: egret.Bitmap;
//上传分数到微信
private upLoadMyScore() {
egret.log("gameOverGroup",this.gameOverGroup);
egret.log("gameOverGroup.width",this.gameOverGroup.width);
if(this.gameOverGroup){
//gameOverBitmap 宽高比必须是stage的宽高比,否则变形
this.gameOverBitmap = platform.openDataContext.createDisplayObject(null, this.gameOverGroup.width, this.gameOverGroup.height);
this.gameOverGroup.addChild(this.gameOverBitmap);
}
//发送消息给子域
platform.openDataContext.postMessage({
wxgame: {
score:this.score,
update_time:new Date().getTime(),
},
nickName: egret.localStorage.getItem("nickName"),
width:this.gameOverBitmap.width,
height:this.gameOverBitmap.height,
command: "updateMyScore"
});
}
/**
* 增加来自主域的监听函数
*/
function addOpenDataContextListener() {
console.log('增加监听函数')
wx.onMessage((data) => {
console.log(data);
if (data.command == 'open') {//打开微信好友排行榜
if (!createScene()) return;
myShowRank = ShowRanking.ShowAll;
if (totalGroup.length > 0) {
renderDirty = true;//重绘标记
// requestAnimationFrameID = requestAnimationFrame(loop);//每一帧绘制
} else {
preloadFriendData();
}
} else if (data.command == 'goBack' && requestAnimationFrameID) {
console.log("goBack");
//返回时重新绘制简易排行榜
myShowRank = ShowRanking.ShowLite;
renderDirty = true;//重绘标记
} else if (data.command == 'loadRes' && !hasLoadRes) {
/**
* 加载资源函数
* 只需要加载一次
*/
// console.log('加载资源')
preloadAssets();
} else if (data.command == "updateMyScore") {//更新用户信息
if (!createScene()) return;
console.log("data", data);
getUserCloudData(data);
} else if (data.command == "loadLastPage") {//加载上一页
if (page > 0) {
buttonClick(0);
}
} else if (data.command == "loadNextPage") {//加载下一页
//在next按钮的范围内
if ((page + 1) * perPageMaxNum < totalGroup.length) {
buttonClick(1);
}
} else if (data.command == "startPlayGame"){
console.log("startPlayGame");
//开始游戏,停止进行循环绘图
cancelAnimationFrame(requestAnimationFrameID);
requestAnimationFrameID = null
}
});
}
效果如下:gameSubContextThirdScriptError Cannot read property 'width' of undefined;at requestAnimationFrame callback function TypeError: Cannot read property 'width' of undefined
时必须在Main.ts
加载资源时通知共享域预加载图片资源,及调用platform.openDataContext.postMessage({command:'loadRes'});
.private async loadResource() {
try {
const loadingView = new LoadingUI();
this.stage.addChild(loadingView);
await RES.loadConfig("resource/default.res.json", "resource/");
await this.loadTheme();
await RES.loadGroup("preload", 0, loadingView);//第三个参数只要实现RES.PromiseTaskReporter协议的onProgress方法既可以
this.stage.removeChild(loadingView);
//添加一行代码:加载排行榜资源,否则在展示排行榜时报错gameSubContextThirdScriptError Cannot read property 'width' of undefined;at requestAnimationFrame callback function TypeError: Cannot read property 'width' of undefined
platform.openDataContext.postMessage({command:'loadRes'});
}
catch (e) {
console.error(e);
}
}
//500x888 //画板区域
//500x604 //绘制区域
//500 x 255 蓝色图片
// let scale = 500/640;
let topBackGroundHeight = 255/888*sharedCanvas.height;
//设置图尺寸乘以下面宽度放大的倍数就是真实尺寸
let widthScale = sharedCanvas.width / 640;
fontScale = sharedCanvas.height/1136;
loop
方法是一个递归调用,开启会一直循环调用,只要renderDirty
设置为true
就会绘制图像/**
* 循环函数
* 每帧判断一下是否需要渲染
* 如果被标脏,则重新渲染
*/
function loop() {
if (renderDirty) {
context.setTransform(1, 0, 0, 1, 0, 0);
context.clearRect(0, 0, sharedCanvas.width, sharedCanvas.height);
if (myShowRank == ShowRanking.ShowLite) {
console.log("绘制精简版排行榜");
drawLiteRankPanel();
} else if (myShowRank == ShowRanking.ShowAll) {
console.log("绘制全部排行榜");
drawRankPanel();
}
renderDirty = false;
}
// console.log("递归死循环调用");
requestAnimationFrameID = requestAnimationFrame(loop);
}
所以在退出排行榜重新开始游戏时就停止循环绘制调用//开始游戏,停止进行循环绘图
cancelAnimationFrame(requestAnimationFrameID);
requestAnimationFrameID = null
requestAnimationFrame(loop)
调用一次即可,renderDirty
设置为true
就会安装canvas去绘制,所以在涉及到绘制界面刷新改变时就将renderDirty
设置为true
即可.//创建图片容器
const img = wx.createImage();
//图片加载完成时回调
img.onload = () => {
}
//本地图片资源通过图片传入图片路径,网络图片资源传入图片url
img.src = assetsUrl[asset];
相关源码已经上传
egret游戏源码
转换为微信小游戏源码
其他参考
用Egret开发微信小游戏之二
第一次使用Egret开发微信小游戏经验总结(包括排行榜排序,每周一数据清零,超越好友等处理)
微信好友排行榜 - 白鹭对接
KVData到底应该怎么定义啊?
微信小游戏如何调用关系链Api