源码现已上架,点击购买
前言
上个月,突然一款小游戏《羊了个羊》突然爆火,本来并没有啥兴趣的,但是突然我们团队策划找上
我,问我开发一款羊了个羊,难不难?
突然说想开发这款羊来蹭下热度,开发一款属于我们的羊游戏,并且带上ikun的元素,通过此举来表达我们对鸽鸽的爱慕之情;
本想拒绝,但是作为一个ikun,弘扬鸽鸽义不容辞,加上策划和美术如此热情,那我就说,先做个demo出来把。
于是第二天,趁着公司没啥事,就开始手撸代码,于是乎,一个简单的demo就出来了,当时的画风还是这样的。
该说不说,是不是有点像羊的样子了,现在,让我们看下核心代码具体怎么实现的把
Cocos3.6.x现已推出,欢迎大家学习 点击学习
核心实现逻辑
首先,我们需要构建卡片数据,用来生成我们的卡片数据,一份卡片数据大概长这样(最初我是照着羊,
一个一个手动填写的)
cards是一个数组,装载着每张卡片的数据
其中id表示卡片的唯一标识,gridx和gridy表示卡片的网格坐标,当然也可以用小数点,生成到Cocos场
景做个位置转换就行了.有了这份数据后,我们就可以生成场景的卡片了。实现逻辑:
/**根据关卡数据里卡牌数据加载卡牌并初始化卡牌 */
for (let i = 0; i < level.cards.length; i++) {
let card = level.cards[i];
// console.log(card);
let node = cc.instantiate(this.item_card);
this.content.addChild(node, card.index);
let com = node.getComponent(item_card);
com.init(card.id, card.gridx, card.gridy, card.index, Game.getRandType());
node.setPosition(card.gridx * GRID_WIDTH, card.gridy * GRID_HEIGHT);
this._allItems.push(com);
node.on(cc.Node.EventType.TOUCH_START, this.onClickItem, this);
}
大家有注意到,卡片数据还有一份index的字段我们没有介绍,index代表卡片的层级关系,
用来表示卡片的遮挡关系,index相对较大的卡片总在index较小的卡片之上,
如果底下的卡片跟上面的卡片位置有重叠,那么底下的卡片就会变灰,不可点击。具体实现逻辑:
/**
* 判断桌面是否有新卡牌是否要冒泡,在上面显示,是则由灰色变白,可点击
*/
public openNew() {
for (let i = 0; i < this._allItems.length; i++) {
let isCanOpen: boolean = true;
for (let j = 0; j < this._allItems.length; j++) {
if (i == j) {
continue;
}
/**判断卡片间层级关系,并判断卡片距离 */
if (this._allItems[i].index < this._allItems[j].index) {
if (Math.abs(this._allItems[i].gridx - this._allItems[j].gridx) < 1 && Math.abs(this._allItems[i].gridy - this._allItems[j].gridy) < 1) {
isCanOpen = false;
}
}
}
if (isCanOpen) {
this._allItems[i].open();
} else {
this._allItems[i].close();
}
}
}
另外,为了避免Cocos渲染错乱,我们在添加卡片的时候,还需要将index作为节点的z序传进去
this.content.addChild(node, card.index);
以上就是我花四小时的想法,并且实现的逻辑,其他的逻辑比较简单,就不列出,有兴趣的朋友可以留言探讨,另外,为了方便策划,后续我还开发了关卡编辑器,好友排行榜的功能。
关于关卡编辑器
很多开发者,不懂怎么做关卡编辑器,只能让策划配表来实现卡片数据,但是配表有个弊端,就是无法实时预览,对策划很不友好
关卡编辑器其实逻辑也不难,无非就是如何序列化与反序列化的实现。
我们先声明关卡数据结构
export class Level {
// randNum: number[];
// cards: ICard[];
public randNum: string[] = [];
public cards: ICard[] = [];
public level: number = 0;
public difficulty: number = 0;
public reward: number = 0;
}
export interface ICard {
id: number;
gridx: number,
gridy: number,
index: number,
}
其中最重要的还是cards数组,和刚刚加载的卡片数据逻辑是一样的,其他的关卡数据,无非是一些奖励,难度等配置。根据需求配置,先声明Level类型的实例对象
private _level: Level = new Level();
此后,我们每创建一张牌(具体数据可以通过editorBox输入,或者用鼠标事件实现),就让他在界面显示,并将数据push进我们的level字段中。
/**
* 创建卡牌
* @param data 卡片数据
*/
public createCard(data: ICard) {
console.log(data);
if (this.canCanCreate(data)) {
let node = cc.instantiate(this.item_card);
let com = node.getComponent(item_card);
com.init(data.id, data.gridx, data.gridy, data.index, Math.floor(Math.random() * 10));
node.setPosition(data.gridx * GRID_WIDTH, data.gridy * GRID_HEIGHT);
com.initIndex(data.index);
this._allItems.push(com);
this.content.addChild(node, data.index);
data.id = data.id;
node.on(cc.Node.EventType.TOUCH_START, this.onClickCard, this);
this._level.cards.push(data);
this._preCreateCard = cc.v2(data.gridx, data.gridy);
this.openNew();
this.initNum();
} else {
// this.showToast();
}
}
等到我们创建差不多完成的时候,只需要将json数据下载保存为json格式的文本保存到本地,就大功告成了
public onClickCreate() {
if (this._level.cards.length % 3 !== 0) {
alert('卡片数必须为3的倍数才能保证消除');
return;
}
let str = this.edit_rand.string;
str = str.replace(/,/g, ',');
this._level.randNum = str.split(',');
this._level.level = Number(this.edit_level.string) || 0;
this._level.difficulty = Number(this.edit_diff.string) || 0;
this._level.reward = Number(this.edit_reward.string) || 1;
this.saveAs(`level_${this._level.level}.json`, this._level);
}
其中saveAs为js保存数据下载到本地的方法,这个百度就可以百度到。
保存后文件就长这样
相信大家都会用bundle.load()的方法加载json文件把?
那么,现在数据已经序列完成了,如何读取数据并且反序列化呢,其实也很简单
public onClickLoad() {
var inputObj = document.createElement('input')
inputObj.setAttribute('id', '_ef');
inputObj.setAttribute('type', 'file');
inputObj.setAttribute("style", 'visibility:hidden');
document.body.appendChild(inputObj);
//选中文件时触发的方法
let self = this;
inputObj.onchange = () => {
if (window.FileReader) {
var file = inputObj.files[0];
var reader = new FileReader();
reader.onload = function (event: any) {
self.onLoadFile(event.target.result)
}
reader.readAsText(file);
}
};
inputObj.click();
}
这段方法是点击后,会弹一个框让你选择文件的功能(之前是加载,现在是读取,同样可以百度到),其中后面的event.target.result就是我们的json文本内容了,这时候我们只要稍加解析就好了
//加载本地数据(反序列化)
private onLoadFile(text) {
try {
this._allItems.length = 0;
let json: Level = JSON.parse(text);
this._level.level = json.level || 0;
this._level.randNum = json.randNum || [];
this._level.cards.length = 0;
this._level.difficulty = json.difficulty || 0;
this._level.reward = json.reward || 0;
this.content.removeAllChildren();
for (let i = 0; i < json.cards.length; i++) {
const data = json.cards[i];
this.createCard(data);
}
this.edit_level.string = this._level.level.toString();
this.edit_rand.string = this._level.randNum.join(',');
this.edit_diff.string = this._level.difficulty.toString();
this.edit_reward.string = this._level.reward.toString();
} catch (error) {
console.error(error);
}
}
初始下level数据,并且将cards数据解析出来就好了。到此,关卡编辑器的所有核心逻辑也全部完成了
关于微信开放域(好友排行榜)
好友排行榜这块,可以参考下皮皮的帖子点击跳转
我是全程仿他的帖子来做的。。。好吧,我也是第一次弄这个
我们先建立排行榜的ui
如图所示,其中在需要显示子域数据的地方,如图main节点,添加SubContextView(注意main的size,
这个很重要,一会会用到),一会,我们在子域里,将会在这里添加Scrollview组件。
在关键时候,比如玩家通关,调用微信上传数据的接口
public uploadScore(value: number) {
if (this.channel == Channel.WECHAT) {
console.log('[uploadScore]', value);
wx.setUserCloudStorage({
KVDataList: [{
key: 'score',
value: value.toString()
}],
success: () => {
console.log('[setScore]', 'success');
},
fail: () => {
console.log('[setScore]', 'fail');
}
});
}
}
我们打开ui脚本 添加如下代码
protected onLoad() {
this.getRank();
}
protected onClose() {
this.node.destroy();
}
protected onDestroy() {
}
/**
* 获取排行榜
*/
public getRank() {
wx.postMessage({
event: 'getRank'
});
}
其中wx.postMessage是主域向子域发送获取排行榜的消息,其实这里第一次打开时,子域是接收不到消
息的,因为这时子域还没有创建完成,但是为什么要加呢,主要是为了重复进入时,刷新用户的数据
有测试过,在子域onEanble函数调用getRank函数,但是onEanble并没有执行到,不知道为啥。
点击构建,选择微信小游戏
开放数据域代码目录写wxSubContext,至于为啥是wxSubContext呢,等下会提到
准备好这些,其他东西就跟主域没关系了,现在,打开陈皮皮子域工程
打开唯一的场景,还记得我们刚刚说的main尺寸嘛,这里唯一要注意的点就是,子域场景的size,一定要跟主域中SubContextView组件的size一样,不然会导致变形。
代码皮皮都帮我们写好了,点击构建就好了
要注意的是,这里的小游戏名称,跟我们刚刚主包构建时候填写的小游戏开放数据域目录,是对应的
发布平台是微信小游戏开放数据域,发布路径是主包的构建好的wechatgame目录下
两个包都构建完成,这时我们在运行微信开发者工具,就能看到好友排行了
如果没有数据,又没有报错,可能是你还没有上传数据,哈哈!
最后
我是ikun,我为鸽鸽代言,守护最好的坤坤,你干嘛哦哎呦!我与小黑子不共戴天
点击购买源码