====== 成语拼拼 ======
最近闲来无聊,业余时间写了个小游戏玩下。
从写demo到申请软著,网上找了个650块的,要30天工作日,等了一个多月吧。不过等等也没关系,反正玩嘛。
看论坛里有些码友说淘宝上可以找到更便宜的,时间也差不多。再一次感叹万能的淘宝呀。
中间正好有时间再改改游戏,等软件著下来就直接上线。
先上个小程序码。
分享一小套自家写的canvas组件,可以用来画排行榜。
先把代码贴出来
class View {
constructor() {
this.x = 0;
this.y = 0;
this.width = 0;
this.height = 0;
this._color = "#ffffff";
this.zorder = 0;
this.children = [];
this.childMaxZorder = 0;
this.render = null;
this.parent = null;
this.marginTop = 0;
this.marginLeft = 0;
}
color (value) {
this._color = value;
return this;
}
size(width, height) {
this.width = width;
this.height = height;
return this;
}
add(view) {
view.parent = this;
this.children.push(view);
this.childMaxZorder++;
view.zorder = this.childMaxZorder;
return this;
}
addTo (container) {
container.add(this);
return this;
}
move(x, y) {
this.x = x;
this.y = y;
return this;
}
remove(view) {
for (var i = 0; i < this.children.length; i++) {
if (this.children[i] == view) {
this.children.splice(i, 1);
}
}
return this;
}
draw(ctx, x, y) { }
}
这是个基础的组件,渲染组件跟render都是继承他的。
class Render extends View {
constructor() {
super();
this.canvas = wx.getSharedCanvas();
this.ctx = this.canvas.getContext('2d');
this.width = 1000;
this.height = 1000;
}
startRender(parent) {
if (!parent) {
this.render = this;
this.size(this.canvas.width, this.canvas.height);
this.ctx.clearRect(this.x, this.y, this.width, this.height);
}
var parent = parent || this;
var children = parent.children;
children.sort(function (a, b) { return a.zorder - b.zorder; });
for (var i = 0; i < children.length; i++) {
var view = children[i];
view.marginTop = parent.marginTop + view.y;
view.marginLeft = parent.marginLeft + view.x;
view.render = this.render;
view.draw(this.ctx, view.marginLeft, view.marginTop);
if (view.children.length > 0) {
this.startRender(view);
}
}
}
}
Render是视图的根本,你可以看成是Stage。
当所有组件都设置好之后,调用startRender即可开始渲染。
class Image extends View {
constructor() {
super();
this.lineWidth = 1;
this.imageUrl = null;
this.imageData = null;
this.isImageLoading = false;
this.fillType = 0; // 0区域,1框, 2图片
}
fillRect(width, height) {
this.fillType = 0;
this.width = width;
this.height = height;
return this;
}
drawRect(width, height) {
this.fillType = 1;
this.width = width;
this.height = height;
return this;
}
image(url) {
this.fillType = 2;
this.imageUrl = url;
return this;
}
draw(ctx, x, y) {
ctx.fillStyle = this._color;
if (this.fillType == 0) {
ctx.fillRect(x, y, this.width, this.height);
}
else if (this.fillType == 1) {
var h = this.height, w = this.width, lw = this.lineWidth;
ctx.fillRect(x, y, w - lw, lw); // top
ctx.fillRect(x + w - lw, y, lw, h); // right
ctx.fillRect(x, y + h - lw, w, lw); // bottom
ctx.fillRect(x, y, lw, h - lw); // left
}
else if (this.fillType == 2) {
if (!!this.imageData && !this.imageData.complete) return;
if (!!this.imageData && this.imageData.complete) {
ctx.drawImage(this.imageData, x, y, this.width, this.height);
}
else if (!this.imageData) {
this.isImageLoading = true;
this.imageData = wx.createImage();
this.imageData.src = this.imageUrl;
this.imageData.onload = () => {
this.isImageLoading = false;
if (!!this.render) this.render.startRender();
};
}
}
}
}
Image组件是用来加载图片,或者是画框框的
例如:
- var render = new Render();
- var image = new Image().addTo(render).image(“cocoscreator.png”);
- render.startRender();
即可完成一张图片的加载及渲染。
在代码里设定了加载完图片之后,自动调用startRender再重新画一次,所以不用担心图片的层次会有什么问题。
class Label extends View {
constructor() {
super();
this._font = "Helvetica";
this._fontSize = 20;
this._textAlign = "center";
this._baseLine = "middle";
this._text = null;
}
fontSize(value) { this._fontSize = value; return this; }
textAlign(value) { this._textAlign = value; return this; }
baseLine(value) { this._baseLine = value; return this; }
text(value) { this._text = value; return this; }
font(value) { this._font = value; return this; }
topleft () { this._textAlign = "left"; this._baseLine = "top"; return this; }
draw(ctx, x, y) {
if (!!this.text) {
ctx.fillStyle = this._color;
ctx.textAlign = this._textAlign;
ctx.textBaseline = this._baseLine;
ctx.font = `${this._fontSize}px ${this._font}`;
ctx.fillText(this._text, x, y);
}
}
}
这个是个Label组件,顾名思义,渲染文本用的。
下面把我小游戏里面用到的两个实例贴出来
class RankListHandler {
constructor() {
this.render = new Render();
this.selfInfo = null;
this.inited = false;
this.keys = {
ScoreKey: "levelScore"
};
this.users = [];
this.friends = [];
this.viewData = null;
this.friend([this.keys.ScoreKey + 0]).then( (res) => {
this.friends = res.data;
} );
this.info(["selfOpenId"]).then( data => {
this.selfInfo = data[0];
this.inited = true;
this.showMT(this.viewData);
} );
}
user (keylist) {
return new Promise( (solve, reject) => {
wx.getUserCloudStorage({
keyList: keylist,
success: res => {
console.log("wx.getFriendCloudStorage success", res);
solve(res);
},
fail: res => {
console.log("wx.getFriendCloudStorage fail", res);
reject(res);
},
});
} );
}
//取出所有好友数据 关卡得分
friend (keylist) {
return new Promise( (solve, reject) => {
wx.getFriendCloudStorage({
keyList: keylist,
success: res => {
console.log("wx.getFriendCloudStorage success", res);
solve(res);
},
fail: res => {
console.log("wx.getFriendCloudStorage fail", res);
reject(res);
},
});
} );
}
info (idlist) {
return new Promise( (solve, reject) => {
wx.getUserInfo({
openIdList: idlist,
lang: 'zh_CN',
success: res => {
console.log("wx.getUserInfo success", res);
var data = res.data;
for (var i = 0; i < data.length; i++) {
var info = data[i];
for (var j = this.users.length - 1; j >= 0; j--) {
if(this.users[j].openId == info.openId) {
this.users.splice(j, 1);
break;
}
}
this.users.push(info);
}
solve(res.data);
},
fail: res => {
console.log("wx.getUserInfo fail", res);
reject(res);
},
});
} );
}
getValueByKey (item, key) {
var valist = item.KVDataList;
for (var i = 0; i < valist.length; i++) {
var info = valist[i];
if(info.key == key) {
return info.value;
}
}
return 0;
}
showNear (data) {
var width = this.sharedCanvas.width,
height = this.sharedCanvas.height,
padding = 10,
border = 3,
titleHeight = 60,
key = data.key;
this.drawBG(width, height, padding, border, titleHeight);
this.friends.sort((a, b) => {
return parseInt(this.getValueByKey(b, key)) - parseInt(this.getValueByKey(a, key));
});
var idx = 0, itemList = [];
for (var i = 0; i < this.friends.length; i++) {
var info = this.friends[i];
if(info.avatarUrl == this.selfInfo.avatarUrl) {
idx = i;
}
}
for (var i = 0; i < 3; i++) {
itemList.push(new View().move(padding + border + (width - padding * 2) * 0.333 * i, padding + 60).addTo(this.render));
itemList[i].size((width - padding * 2) * 0.333 - border * 2, height - titleHeight - (padding) * 2 - border);
}
if(idx > 0) this.drawItem(itemList[0], this.friends[idx - 1], idx - 1, key, "#ffffff");
this.drawItem(itemList[1], this.friends[idx], idx, key, "#00ff00");
if(idx + 1 < this.friends.length) this.drawItem(itemList[2], this.friends[idx + 1], idx + 1, key, "#ffffff");
this.render.startRender();
}
drawItem (container, info, index, key, color) {
log("drawItem", index);
// index
new Label().text(index + 1).move(container.width / 2, 30).color(color).fontSize(36).addTo(container);
// name
new Label().text(info.nickname).move(container.width / 2, 150).color(color).fontSize(26).addTo(container);
// 分数
new Label().text(this.getValueByKey(info, key)).move(container.width / 2, 190).color(color).fontSize(28).addTo(container);
// 底框
var picBg = new Image().color("#ffffff").move(container.width / 2 - 35, 52).fillRect(70, 70).addTo(container);
// avatar
var head = new Image().image(info.avatarUrl).move(5, 5).size(60, 60).addTo(picBg);
}
drawBG (width, height, padding, border, titleHeight) {
// 底色
var di = new Image().fillRect(width, height).color("#6972fe").addTo(this.render);
// 外框
new Image().move(padding, padding).color("#93bff7").drawRect(width - padding * 2, height - padding * 2).addTo(di).lineWidth = border;
// 中间色块
var midColor = new Image().color("#3a41bc").move(padding + border + (width - padding * 2) * 0.333, padding + 60).fillRect((width - padding * 2) * 0.333 - border * 2, height - titleHeight - (padding) * 2 - border).addTo(this.render);
// 内框
new Image().color("#93bff7").move(padding + (width - padding * 2) * 0.333, padding + titleHeight - border).drawRect((width - padding * 2) * 0.333, height - titleHeight - padding * 2 + border).addTo(di).lineWidth = border;
// 标题框
new Image().color("#93bff7").move(padding, padding).drawRect(width - padding * 2, titleHeight).addTo(di).lineWidth = border;
new Label().move(20, 25).text("好友排行榜").baseLine("top").textAlign("left").fontSize(30).addTo(di);
}
showHead() {
var width = this.sharedCanvas.width,
height = this.sharedCanvas.height;
// 底框
var picBg = new Image().color("#ffffff").move(10, 10).fillRect(80, 80).addTo(this.render);
new Image().image(this.selfInfo.avatarUrl).move(5, 5).size(70, 70).addTo(picBg);
new Label().text(this.selfInfo.nickName).color("#F1C100").topleft().move(110, 20).fontSize(30).addTo(this.render);
var sex = this.selfInfo.gender == 1 ? "帅哥" : "美女";
new Label().text(`欢迎您,${sex}`).topleft().move(110, 60).fontSize(24).addTo(this.render);
this.render.startRender();
}
getSelfInfo () {
for (var i = 0; i < this.friends.length; i++) {
var info = this.friends[i];
if(info.avatarUrl == this.selfInfo.avatarUrl) {
return info;
}
}
return null;
}
setScore (data) {
var info = this.getSelfInfo();
if(!!info) {
for (var i = 0; i < data.data.length; i++) {
var item = data.data[i],
exists = false;
for (var i = 0; i < info.KVDataList.length; i++) {
if(info.KVDataList[i].key == item.key) {
if(parseInt(info.KVDataList[i].value) < parseInt(item.value)) {
info.KVDataList[i].value = item.value;
}
exists = true;
break;
}
}
if(!exists) {
info.KVDataList.push({key:item.key,value:parseInt(item.value)});
}
}
for (var i = 0; i < info.KVDataList.length; i++) {
var item = info.KVDataList[i];
}
}
else {
info = {};
info.avatarUrl = this.selfInfo.avatarUrl;
info.nickname = this.selfInfo.nickName;
info.openid = this.selfInfo.openId;
info.KVDataList = [];
for (var i = 0; i < data.data.length; i++) {
var item = data.data[i];
info.KVDataList.push({key:item.key,value:item.value});
}
this.friends.push(info);
}
}
start() {
this.sharedCanvas = wx.getSharedCanvas();
this.sharedCtx = this.sharedCanvas.getContext('2d');
wx.onMessage(data => {
log(data);
if(!!data && data.mt != undefined) {
this.viewData = data;
if(this.inited) {
this.showMT(data);
}
}
});
}
showMT(data) {
if(!data) return;
var messageType = data.mt;
log("showmt", messageType);
switch (messageType) {
case mt.init:
break;
case mt.head:
this.showHead();
break;
case mt.all:
break;
case mt.near:
this.showNear(data);
break;
case mt.score:
this.setScore(data);
break;
}
}
}
new RankListHandler().start();
这里面有两个例子
一个是开始界面左上角那个欢迎您,帅哥,还带你的头像及名字
另外一个是结束界面那里的三人排行榜。
完整的好友排行榜大家可以自己玩玩。写这个组件的时候,我还是按方便使用的习惯来写的,用起来有点像cocos本身的语法,还带了点好久前用jquery的那种多方法连调的特性。
完整的代码在这下载。 index.zip (3.3 KB)
有兴趣可以留个言,希望对大家有帮助。
fourletters.rar (1.0 MB)
项目源代码在这里下载。
只打包了assets目录,要玩的同学建个空项目之后,解压压缩包,覆盖即可。
open目录是排行榜的代码,如上面所说,写了个小框架。体积会小很多。
还有个bat命令,是用来生成微信小程序之后,运行它就可以把开发数据域的代码,跟分享要用的图片拷到小游戏项目里面。