精华 小游戏中操作开放数据域的实现思路(Phaser篇)
发布于 1 年前 作者 aleafworld 2836 次浏览 来自 分享

小游戏中的开放数据域可用来保存游戏数据,可实现排行榜等功能,但真正实现起来似乎还不太容易。 以下是实现思路,提供参考:

1、参考官方文档,在game.json中增加openDataContext的配置项,并创建相应的open目录和index.js文件: { “deviceOrientation”: “portrait”, “openDataContext”: “open” }

2、在小游戏版的phaser引擎文件的合适位置增加: Phaser.XTexture = function(xCanvas,x,y,w,h){ return new PIXI.Texture(new PIXI.BaseTexture(xCanvas),new PIXI.Rectangle(x,y,w,h)); };

3、在游戏场景中创建一个以sharedCanvas作为Texture的精灵: var openDataContext = wx.getOpenDataContext(); var sharedCanvas = openDataContext.canvas; var pad = game.add.sprite(0,100, Phaser.XTexture(sharedCanvas,0,0,150,100));

4、用openDataContext.postMessage()发送通信数据: openDataContext.postMessage({action:action, data: ‘hello!’});

5、在index.js中设计通信数据接收和处理逻辑(这个index.js就像服务端): wx.onMessage(function(data){ if(data.action==“save”){ wx.setUserCloudStorage({ KVDataList:[{key:“data”,value:data.data}], success:function(){ console.log(“Save OK …”); } }); }else if(data.action==“get”){ wx.getUserCloudStorage({ keyList:[“data”], success:function(getData){ var dataValue = getData.KVDataList[0].value; console.log("Get OK : " + dataValue); var sharedCanvas = wx.getSharedCanvas(); var ctx = sharedCanvas.getContext(‘2d’); // 直接画出你想要的数据 ctx.clearRect(0,0,150,100); ctx.fillStyle = “rgb(250, 250, 250)”; ctx.font = “24px Arial”; ctx.textAlign = “left”; ctx.textBaseline = “top”; ctx.fillText("Data : " + dataValue, 5, 5); } }); } });

6、完成:在index.js操作sharedCanvas,画出的内容会自动更新到游戏场景中对应的精灵,因为精灵是以这个Canvas作为Texture创建的。

以上是方法在开发工具中测试通过,也许还有更好的实现方法,时间仓促,有待完善。 Demo请到群文件中找!

【适用小游戏开发的Phaser引擎请看站长的相关文章】

18 回复

老哥的思路还是很棒啊! 我之前的思路也分享一下,本来想用bitmapData中的canvas去绘制sharedCanvas,但是报错说sharedCanvas只能被主域canvas绘制,故放弃。 但是用主域的canvas去绘制sharedCanvas的时候,又会看不到,原因是被phaser后面绘制场景的逻辑给覆盖了。 所以后来想到一个办法就是设置game.renderType = Phaser.HEADLESS,这样不会绘制场景,就不会覆盖sharedCanvas了,这种方法是可行的。但是场景就看不到了。 当然也可以改源码去把sharedCanvas放到最后绘制,这样会在最上层,不过都不如老哥方法优雅。 没有了解PIXI,不太清楚为什么 PIXI.BaseTexture(xCanvas) 就可以绘制,回头要试试。

那么这里我也抛出几个问题,看看大家有没有好的解决方案: 1、开放域能否引入phaser? 2、开放域的事件如何实现?

主域虽然可以操作sharedCanvas,但是主域得不到数据,只能画一些背景边框,数据只能在开放域中画。 如果两边都画,为了避免重叠区域遮挡问题,就要控制绘画的先后顺序。可以在主域画完背景后发送一个信息通知开放域画上数据。 而我的思路是把所有绘画动作都交给开放域这边,主域只负责读取就行,而且开放域也只是画头像和数据而已,不需要太复杂。如果要复杂的,可在主域中通过group组合一个排行榜面板,开放域绘制的画板只是作为一个子元素嵌入其中即可。(以上只是推想,还没实测)

@channingbreeze

1、引入Phaser应该可以,但估计不能实例化Game。引入一个渲染器我觉得还是有用的,可以考虑把phaser中的pixi部分引进去;

2、应该也不需要什么事件吧?可以通过在主域事件的回调中postMessage传送相应命令,就像上面传送一个action和一个data,在开放域中执行相应的动作,就像一个后端。

经过上面方法实现了主域和开放域的通信

下面是index.js中完整的排行榜代码:

// 主域和开放域的通信接口
wx.onMessage(function(data){
	if(data.action=="get"){
		_clear();
		// 读取本用户数据
		wx.getUserCloudStorage({
			keyList:["data"],
			success:function(getData){
				if(data.data > parseInt(getData.KVDataList[0].value)){
					// 提交的数据大于以前的数值,则保存后再画
					wx.setUserCloudStorage({
						KVDataList : [{key:"data",value:data.data.toString()}],
						success : _draw
					});
				}else{
					_draw();
				}
			}
		});
	}else if(data.action=="cmd"){
		// Do something else ...
	}
});

// 清除画板,并画个Loading
var _clear = function(){
	var sharedCanvas = wx.getSharedCanvas();
	var ctx = sharedCanvas.getContext('2d');

	ctx.clearRect(0,0,200,355);
	ctx.fillStyle = "rgba(64,64,64,0.5)";
	ctx.fillRect(0, 0, 200, 355);

	ctx.fillStyle = "rgb(250, 250, 250)";
	ctx.font = "12px Arial";
	ctx.textAlign = "center";
	ctx.textBaseline = "top";
	ctx.fillText("Loading ... ", 100, 10);
};

// 画排行榜
var _draw = function(){
	// 读取好友数据
	wx.getFriendCloudStorage({
		keyList:["data"],
		success:function(res){
			var sharedCanvas = wx.getSharedCanvas();
			var ctx = sharedCanvas.getContext('2d');

			ctx.clearRect(0,0,200,355);
			ctx.fillStyle = "rgba(64,64,64,0.5)";
			ctx.fillRect(0, 0, 200, 355);

			res.data.sort(_sorter); // 先排个序
			for (var i=0; i<res.data.length; i++){
				// console.log(" ### ",i);
				// console.log("avatarUrl: " ,res.data[i].avatarUrl);
				// console.log("nickname: " ,res.data[i].nickname);
				// console.log("openid: " ,res.data[i].openid);
				// console.log("KVDataList: " ,res.data[i].KVDataList);

				ctx.fillStyle = "rgba(255,255,255,0.1)";
				ctx.fillRect(5, i*35 + 5, 190, 30);

				// 头像
				var avatar = wx.createImage();
				avatar.src = res.data[i].avatarUrl;
				avatar.onload = (function(c,a,i){
					return function(){
						// console.log("drawing : " + i);
						c.drawImage(a, 30, i*35+8, 24, 24);
					};
				})(ctx,avatar,i); // 这里是异步执行,要做个闭包处理

				ctx.fillStyle = "rgb(250, 250, 250)";
				ctx.font = "12px Arial";
				ctx.textAlign = "left";
				ctx.textBaseline = "top";
				ctx.fillText(i+1, 10, i*35 + 12); // 名次
				ctx.fillText(res.data[i].nickname, 60, i*35 + 12); // 昵称
				ctx.textAlign = "right";
				ctx.fillText(res.data[i].KVDataList[0].value, 185, i*35 + 12); // 分数

			}
		}
	});
};

// 排序函数(降序)
var _sorter = function(data1, data2){
	var num1 = parseInt(data1.KVDataList[0].value);
	var num2 = parseInt(data2.KVDataList[0].value);
	if(num1 > num2){
		return -1;
	}else if(num1 < num2){
		return 1;
	}else{
		return 0;
	}            
};

谢谢 已经实现了 渲染canvas的方法如下面 有两种 Phaser.XTexture直接在game.js里面创建也可以,如果在引擎里面找不到合适位置的话,相对来说,这个灵活一点。注释掉的部分也是可以实现渲染canvas的,不过是按照canvas大小来画,比较不方便,也许还可以优化吧,反正代码都是东拼西凑的。。。。。。

@CJBKing 添加按钮不是主域的事情么?跟开放域无关啊

@aleafworld 比如说开放域离要实现排行榜的分页要做按钮,这按钮在开放域里面绘制吧,在开放域绘制的话这个按钮点击事件,不得在开放域里实现嘛?如果不是在主域里添加按钮,要怎么显示在开放域里啊?开放域和主域使用的不是同一个Canvas啊

@CJBKing 按钮放在主域,响应时用postMessage发送信息给开放域,开放域收到信息立即把相应页面绘制出来即可,这个过程会有延时。

@aleafworld 嗯嗯,解决了,谢谢

@channingbreeze @aleafworld 之前我在绘制排行榜的时候用楼主的方法效果不错,现在新的项目有个问题就是game.renderType是用WEBGL方式渲染的,排行榜在模拟器中显示不出来,将渲染方式改为game.renderType = Phaser.HEADLESS排行榜是可以显现出来的,但是用HEADLESS时我的游戏会卡顿,所以为了游戏流畅性,我选择WEBGL渲染,这时我的排行榜用楼主的方法就显示不出来了,那这样我的排行榜该怎么来做啊?请两位大神帮忙解答下,谢谢了

@CJBKing 官网有说明“开放数据域的所有 canvas 只支持 2d 渲染模式”

下面是关于开放数据域的官方指南入口: https://developers.weixin.qq.com/minigame/dev/tutorial/open-ability/open-data.html

@aleafworld 嗯,找到解决方案了,我把开放域的数据拿到主域里,采用Phaser的方法进行绘制排行榜就好了

老哥,在主域用sprite作为容器后,请问,排行耪的滑动要怎么实现

@MengShaofei 用crop函数,配合拖拽动作

@CJBKing 问下,怎么把开放域的数据拿到主域里的??????

@chenlin666 按官方文档的意思数据是不能拿到主域里的,但前段时间测试过把数据作为子对象挂在sharecanvas上,然后在主域中可以读取这个子对象,这不知是不是Bug,你可以试试

回到顶部