1. 概述
不管是在現實生活還是當今遊戲中,各式各樣的排名層出不窮。如果我們做好一款遊戲,卻沒有實現排行榜,一定是不完美的。排行榜不僅是玩家了解自己實力的途徑,也是遊戲運營刺激用戶留存的一種途徑。在微信小遊戲中普遍存以下兩種排名
其中好友的排名,需要通過微信子域實現。在子域上下文中,只能調用微信提供相關的api,且數據傳輸只能進不能出。即使在子域中調用雲函數也不行。這個對數據很嚴格,開發略為複雜。但好處也很明顯
- 無需用戶確認授權就可實現排名
- 排名信息均為自己好友,刺激效果更明顯
儘管這樣,我們還是先實現世界排行。世界排行需要用戶授權。早期只需要調用wx.authorize就可以實現,現在很不穩定(好像廢棄了)。所以不得不通過生成一個授權按鈕來實現
2. 微信雲開發
微信小遊戲為開發者提供了一部分免費的雲環境。可以實現文件存儲,數據存儲以及通過雲函數實現服務端接口。開通方式也很簡單,這裏不做說明。既然要實現排名,優先選用雲函數來實現對應的api。要實現雲函數,需要在project.config.json文件中通過屬性cloudfunctionRoot指定雲函數目錄。由於,是通過cocoscretor開發,每次構建發布都會清空輸出內容。為了解決人肉複製粘貼,我們需要通過定製小遊戲構建模板實現微信小遊戲所有代碼的管理。小遊戲地心俠士構建模板如下
從圖中,可以看到獲取openid、獲取世界排名、保存用戶授權信息等雲函數都放在cocoscreator代碼環境中。這樣在開發完成后,通過cocoscreator構建發布,對應的雲函數也會一起打包過去
3. 實現世界排行
3.1 獲取玩家openid
首先在構建模板的cloud-functions文件件中,使用npm初始一個名為getOpenId的node項目。初始好以後,運行npm install wx-server-sdk@latest --save。這樣就建立好了一個雲函數的基本框架。
我們在index.js文件,輸入以下代碼
// author:herbert 464884492
// project:地心俠士 獲取用戶openid
const cloud = require('wx-server-sdk')
cloud.init()
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
return {
event,
openid: wxContext.OPENID,
appid: wxContext.APPID,
unionid: wxContext.UNIONID,
}
}
調用雲函數時,上下文中便可以得到玩家openid和uninid。玩家進入遊戲就先調用此函數,得到玩家的openid用於後邊更新玩家數據和獲取世界排行的條件。
小遊戲端調用雲函數前,需要初始雲環境。因為採用定製構建模板,所以我們直接在模板的game.js文件末尾初始我的雲環境
// author:herbert 464884492
// 地心俠士 初始雲環境
....
wxDownloader.init();
window.boot();
//初始化雲調用
this.wx.cloud.init({
traceUser: true,
env: 'dxxs-dxxs'
});
...
後續調用雲函數中,第一步都是要獲取openid,這裏定義一個全局變量將其保存起來,調用方法如下
// author:herbert 464884492
// 地心俠士 玩家openid
private static openId: string = null;
private static initenv() {
return new Promise((resolve, reject) => {
if (!this.wx) reject();
//直接使用本地緩存
if (this.openId != null) resolve();
// 調用雲函數獲取
this.wx.cloud.callFunction({
name: 'getOpenId',
complete: res => {
this.openId = res.result.openid;
resolve();
}
});
});
}
3.2 動態生成授權按鈕
先看下地心俠士布局界面
上圖中可以看到,地心俠士虛擬了一個遊戲操作區域。玩家聚焦到世界排行時,需要渲染一個授權按鈕在確定的位置。需求很簡單,可考慮到移動端多分辨率,這個操作就變得複雜了。需要做屏幕適配。地心俠士採用自適應寬度的適配策略,配置如下圖
遊戲運行時獲取實際分辨率的寬度與設計的寬度相除,變可知道當前寬度變化比列,鍵盤容器九宮格使用了主鍵widget底部111px,高度161px。確定按鈕寬度105px
微信小遊戲以左上角為原點,通過top和left確定位置。然而,cocoscreator以左下角為原點,所以在計算top值時需要用屏幕寬度 – box上邊緣y坐標。適配代碼如下
// author:herbert 464884492
// 地心俠士 動態生成透明授權按鈕
initUserInfoButton() {
// 獲取設計尺寸
let desingSize: cc.Size = cc.view.getDesignResolutionSize();
// 獲取實際屏幕尺寸
let screenSize: cc.Size = cc.view.getFrameSize();
// 獲取寬度倍率
let widthRate = screenSize.width / desingSize.width;
// 獲取當前倍率下九宮格鍵盤實際高度
let halfKcHeight = 161 * widthRate / 2;
// 獲取當前倍率下確定按鈕實際寬度
let btnwidth = this.btnKeySuer.width * widthRate;
WxCloudFun.createUserinfoButton("",
// 確定按鈕中心點對應小遊戲left值 (屏幕寬度-確定按鈕實際寬度)/2
// 定義實際授權按鈕size為105*40,所以還必須加上對應的偏差值
// 以下代碼中left體現整體適配過程,不考慮中間過程可以直接使用
// (屏幕寬度-授權按鈕)/2 即可得到left值
screenSize.width / 2 - 52.5 * widthRate + (btnwidth - 105) / 2,
// Canvas 適配策略是 Fit Width,所以Canvas下邊沿不一定就是屏幕邊緣
// 通過111*widthRate得到具體下沿值,在加上虛擬鍵盤一半高度,可得到中心位置
// 由於微信原點在左上角,需要保持按鈕處於中心位置,坐標還需要上移一半按鈕高度
screenSize.height - (111 * widthRate + halfKcHeight + 20),
() => {
this.keyCode = cc.macro.KEY.r;
this.scheduleOnce(async () => {
this.dlgRank.active = true;
// 獲取排名數據
await this.getRankInfo();
}, 0);
});
}
3.3 獲取用戶頭像昵稱信息
經過上一步驟的適配操作,只要玩家聚焦到【世界排行】,地心俠士虛擬鍵盤的確定按鈕正上方會覆蓋一個透明的userInfoButton,玩家點擊確定就會喚起授權對話框,然後在對應的回調函數就可以完成用戶數據保存操作
// author:herbert 464884492
// 地心俠士 獲取玩家基本信息
public static createUserinfoButton(text: string, left: number, top: number, cb: Function) {
this.userInfoButton = this.wx.createUserInfoButton({
type: 'text',
text: text,
style: {
left: left,
top: top,
height: 40,
width: 105,
lineHeight: 40,
textAlign: 'center',
fontSize: 16,
backgroundColor: '#ff000000',// 透明
color: '#ffffff',
}
});
this.userInfoButton.hide();
this.userInfoButton.onTap((res) => {
// 將獲取到的用戶數據提交到雲端
this.wx.cloud.callFunction({
name: 'putUserinfo',
data: { ...res.userInfo, openid: this.openId }
});
this.hideUserInfoButton();
cb.call();
});
}
在代碼中,除了傳入玩家微信信息外。我還額外傳遞進入遊戲時就獲取的openid。正常情況下不需要的,因為,雲函數中天然會告訴你openid。不過,我們在後端使用了got獲取玩家頭像保存到雲端文件存儲中。引入此包后,後端就獲取不到openid了,相當奇怪。對應雲平台雲函數代碼如下
// author:herbert 464884492
// 地心俠士 雲函數保存玩家基本信息
const cloud = require('wx-server-sdk')
const got = require('got')
cloud.init()
// 雲函數入口函數
exports.main = async(event, context) => {
const {
nickName,
avatarUrl,
gender,
openid
} = event;
let wxContext = cloud.getWXContext();
// 如果後端拿不到openid就採用前端傳入的openid
wxContext.OPENID = wxContext.OPENID || openid;
const log = cloud.logger()
log.info({
tip: `正在請求頭像地址[${avatarUrl}]`
})
// 獲取頭像數據流
const stream = await got.stream(avatarUrl);
let chunks = [];
let size = 0;
const body = await (async() => {
return new Promise((res, reg) => {
stream.on('data', chunk => {
chunks.push(chunk)
size += chunk.length
log.info({
tip: `正在讀取圖片流信息:[${chunk.length}]`
})
})
stream.on('end', async() => {
const body = Buffer.concat(chunks, size)
log.info({
tip: `正在保存頭像文件:[${size}]`
})
res(body)
})
})
})()
//保存頭像到雲存儲
const {
fileID
} = await cloud.uploadFile({
cloudPath: `avatars/${wxContext.OPENID}.jpg`,
fileContent: body
})
// 添加或更新玩家信息到數據庫
const db = cloud.database()
const {
data
} = await db.collection("dxxs").where({
_openid: wxContext.OPENID
}).get()
const updateData = {
fileId: fileID,
nickName: nickName,
sex: gender == 1 ? '男' : '女',
avatarUrl: avatarUrl
}
if (data.length > 0) {
log.info({
tip: `正在修改數據庫信息:[${size}]`
})
await db.collection("dxxs").doc(data[0]._id).update({
data: updateData
})
} else {
log.info({
tip: `正在添加數據庫信息:[${size}]`
})
await db.collection("dxxs").add({
data: { ...updateData,
_openid: openid
}
})
}
return {
openid: wxContext.OPENID,
appid: wxContext.APPID,
unionid: wxContext.UNIONID
}
}
3.4 獲取排行數據
保存完用戶數據后,通過一個回調函數,實現了玩家排名數據獲取。細心的朋友可以在前邊授權按鈕適配的章節看到await this.getRankInfo();這句代碼。後端雲函數就是一個簡單數據查詢。效果圖如下
從上圖可以看到,我實現了三個維度排名,需要在前端需要傳入排名字段。對應代碼如下
// author:herbert 464884492
// 地心俠士 獲取排名信息
public static async getWorldRanking(field: string = "level") {
const { result } = await this.wx.cloud.callFunction({
name: 'getWordRanking',
data: { order: field }
});
return result.ranks;
}
雲函數代碼如下
// author:herbert 464884492
// 地心俠士 雲函數返回排名信息
const cloud = require('wx-server-sdk')
cloud.init()
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
const db = cloud.database();
const {
order = "level"
} = event;
const openData = await db.collection("dxxs")
.orderBy(order, "asc")
.get()
const ranks = openData.data.map(item => {
return {
openid: item._openid,
[order]: item[order],
nickName: item.nickName,
fileId: item.fileId,
avatarUrl: item.avatarUrl
}
});
return {
ranks: ranks,
openid: wxContext.OPENID,
appid: wxContext.APPID,
unionid: wxContext.UNIONID
}
}
4. 總結
- 微信子域數據很嚴格,數據只進不出。調用雲函數也不行
- 雲函數中使用http請求,可能會得不到openid
- 屏幕適配知道定位原則,也可以很簡單
- avatarUrl通過Sprite現實頭像,需要設置安全域名
- 目前部分華為手機分享截屏出現黑屏使用canvas.toTempFilePath就可以解決
這裡有一個CoscosCreator遊戲開發群,歡迎喜歡聊技術的朋友加入
歡迎感興趣的朋友關注我的訂閱號“小院不小”,或點擊下方二維碼關注。我將多年開發中遇到的難點,以及一些有意思的功能,體會都會一一發布到我的訂閱號中
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※帶您來了解什麼是 USB CONNECTOR ?
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!
※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※教你寫出一流的銷售文案?