【原创博文,转载请注明出处!】
(文末有新方案,请耐心看完)昨天参考了麒麟子的CocosCreator开源项目,然后自己也封装了一个游戏弹窗的小模块,做了点记录,方便使用CocosCreato开发游戏的小伙伴们参考。
方案一
看完了效果图,让我们一起开始今天的任务。首先在CocosCreator开发工具中,我们在Canvas的safe_area目录层级下面新建一个alert节点作为这个弹窗的父节点,这样方便我们代码控制alert的显示与隐藏。
针对现今主流设备越来越多的异形屏就会涉及到“安全区域”这个概念,从而设计的时候,需要考虑所有能响应事件的控件都避开这个“安全区域”)
这里列举一下弹窗涉及到的控件:一个背景视图,两个Label和两个Button。其中一个Label用于显示主题,一个Label用于显示提示文字。两个Button给用户提供选择选择入口(当然我们后面通过代码控制“取消”按钮是否显示)下面是这个弹窗UI的层级结构:
那么开始我们今天的代码吧,Let Go!!!
新建一个脚本Alert.js并定义如下属性:
properties: {
_alert:null, //提示框
_btnOK:null, //提示框确定按钮
_btnCancel:null, //提示框取消按钮
_title:null, //提示框标题
_content:null, //提示框内容
_btnOKCallback:null, //点击确定按钮的回调事件
},
然后我们在Alert.js的onLoad()方法里获取上面定义的属性:
onLoad () {
//配置AppStart.js以后才可以判断
// if(cc.vv == null){
// return;
// }
cc.log('Alert.js onLoad');
this._alert = cc.find("Canvas/safe_area/alert");
this._title = cc.find("Canvas/safe_area/alert/background/title").getComponent(cc.Label);
this._content = cc.find("Canvas/safe_area/alert/background/content").getComponent(cc.Label);
this._btnOK = cc.find("Canvas/safe_area/alert/background/btn_ok").getComponent(cc.Button);
this._btnCancel = cc.find("Canvas/safe_area/alert/background/btn_cancel").getComponent(cc.Button);
if (this._btnCancel instanceof cc.Button) {
console.log('是个Button ');
}else{
console.log('是个鬼');
}
this._btnOK.active = false;
//下面这段代码开启是否显示
// this._alert.active = false;
cc.vv.alert = this;
},
下面我们需要额外提供3个API,分别作用为:
a:_btnOK按钮的点击回调;
b:_btnCancel按钮的点击回调;
c:弹出的显示功能show;
onBtnClicked:function(event){
if(event.target.name == "btn_ok"){
if(this._btnOKCallback){
this._btnOKCallback();
}
}
this._alert.active = false;
this._btnOKCallback = null;
console.log("这是全新定义的clicked!!");
},
cancelBtnClicked:function(){
cc.log('我被点中了');
this._alert.active = false;
},
/**
* title:弹框标题
* content:弹框显示内容
* callback:点击“确定”按钮的回调事件
* needCancel:是否需要显示“取消”按钮
*/
show:function(title,content,callback,needCancel){
console.log('paras -----> : ',title,content,callback,needCancel);
this._alert.active = true;
this._btnOKCallback = callback;
this._title.string = title;
this._content.string = content;
if(needCancel){
// "确定" 和 "取消"都显示
//注意:这里面都是对节点node 操作,this._btnCancel.active(或者.x)什么的操作都是无效的☹️
console.log("needCancel ? true");
this._btnCancel.node.active = true;
this._btnOK.node.x = -239.5;
this._btnCancel.node.x = 239.5;
if(this._btnOK){
cc.log('也是存在的啊!');
}
}
else{
//不需要显示“取消”按钮
console.log("needCancel ? false");
this._btnCancel.node.active = false;
this._btnOK.node.x = 0;
}
},
OK,有了上面这些核心部分我们就已经完成今天的任务了。
需要注意的是:我们在onLoad()方法里已经获取到对应的控件,但是设置该控件的显示与隐藏并不像原生平台API那样直接设置Button.hidden = YES or NO 这么简单了。在CocosCreator里,设置控件的显示与隐藏都是在节点的基础上操作的,所以需要类似于“xxx.node.active = true or false; ”
下面我们来看看Alert.js的应用:
cc.vv = {};
cc.vv.alert = require("Alert");
cc.vv.alert.show("提示啦", "钻石不足,创建房间失败!",this.clickCallback,true);
clickCallback(){
cc.log("do clickCallback task.");
},
第一句“cc.vv = {};”,可以理解为给cc添加了一个“vv”的对象。由于cc是全局的,所以cc.vv也可以在全局使用,我们定义了cc.vv对象,当然可以继续给vv这个对象增加其子对象如cc.vv.alert。。。,这样就可以在全局使用cc.vv.alert对象。还记得在Alert.js的onLoad()方法里,我们进行了“cc.vv.alert = this;”的操作,这样我们就给这个全局的alert对象赋值了,后面就可以放肆地使用这个cc.vv.alert对象了。嗯,不知道这样理解对不对,如果您有其他的理解,请留言指教,我将不甚感激/(ㄒoㄒ)/
好啦,今天的记录到此为止O(∩_∩)O
方案二(强烈推荐)
2018-05-24日更新。
昨天发现了一个问题:上面这个游戏弹窗功能上没问题。但是实际使用中存在不足之处。一般游戏中很多个场景都会有弹窗显示的,那按照我们之前的做法不就得每个场景下都拖一个这样的弹窗视图集吗?游戏那么多场景,想起来就挺麻烦的。所以对于复杂游戏场景,需要换个思路彻底解决这个问题。
参考了网上其他方案并与部分游戏开发者讨论之后,经过实践我觉得接下来这个方案很棒,这也是我投入到项目中的方案。
也就是:
①:在CocosCreator场景编辑器中设置好弹窗的UI布局
②:然后将整个弹窗拖到CocosCreator资源管理器中生成一个prefab文件。
③:将prefab文件放到资源管理器assets目录的resources目录下。
④:将弹窗所需要用到的资源文件也放入资源管理器assets目录的resources目录下。
⑤:写一个脚本动态控制弹窗的控件。便于在项目需要用到的地方显示弹窗。
我新改写的prefab弹窗代码如下:
import { ENFILE } from "constants";
/*
* @Author: RephontilZhou
* @Date: 2018-05-18 17:39:48
* @Last Modified by: Damo
* @Last Modified time: 2018-05-23 19:57:35
*/
var Alert = {
_alert: null, // prefab
_detailLabel: null, // 内容
_cancelButton: null, // 确定按钮
_enterButton: null, // 取消按钮
_enterCallBack: null, // 回调事件
_animSpeed: 0.3, // 动画速度
_sprite: null, //人物
};
cc.Class({
extends: cc.Component,
properties: {
},
statics:{
/**
* detailString : 内容 string 类型.
* enterCallBack: 确定点击事件回调 function 类型.
* neeCancel: 是否展示取消按钮 bool 类型 default YES.
* spritePath: 动态加载弹框中精灵图片的resources路径
* duration: 动画速度 default = 0.3.
*/
show(detailString, enterCallBack, needCancel, spritePath, animSpeed) {
var self = this;
// 判断
if (Alert._alert != undefined) return;
Alert._animSpeed = animSpeed ? animSpeed : Alert._animSpeed;
cc.loader.loadRes("prefabs/Alert", cc.Prefab, function (error, prefab) {
if (error) {
cc.error(error);
return;
}
var alert = cc.instantiate(prefab);
Alert._alert = alert;
// 动画
var cbFadeOut = cc.callFunc(self.onFadeOutFinish, self);
var cbFadeIn = cc.callFunc(self.onFadeInFinish, self);
self.actionFadeIn = cc.sequence(cc.spawn(cc.fadeTo(Alert._animSpeed, 255), cc.scaleTo(Alert._animSpeed, 1.0)), cbFadeIn);
self.actionFadeOut = cc.sequence(cc.spawn(cc.fadeTo(Alert._animSpeed, 0), cc.scaleTo(Alert._animSpeed, 2.0)), cbFadeOut);
Alert._detailLabel = cc.find("alertBackground/detailLabel", alert).getComponent(cc.Label);
Alert._cancelButton = cc.find("alertBackground/cancelButton", alert);
Alert._enterButton = cc.find("alertBackground/enterButton", alert);
Alert._sprite = cc.find("alertBackground/sprite", alert).getComponent(cc.Sprite);
if (Alert._sprite.spriteFrame) {
console.log("Alert._sprite");
}else{
console.log("找不到");
}
// 添加点击事件
Alert._enterButton.on('click', self.onButtonClicked, self);
Alert._cancelButton.on('click', self.onButtonClicked, self);
// 父视图
Alert._alert.parent = cc.find("Canvas");
// 展现 alert
self.startFadeIn();
self.configAlert(detailString, enterCallBack, needCancel, spritePath,animSpeed);
});
// 参数
self.configAlert = function (detailString, enterCallBack, needCancel, spritePath, animSpeed) {
// 回调
Alert._enterCallBack = enterCallBack;
// 内容
Alert._detailLabel.string = detailString;
// 是否需要取消按钮
if (needCancel || needCancel == undefined) { // 显示
Alert._cancelButton.active = true;
} else { // 隐藏
Alert._cancelButton.active = false;
Alert._enterButton.x = 0;
}
if (spritePath == null || spritePath == undefined) {
spritePath = "Alert/alert_baby";
}
cc.loader.loadRes(spritePath, cc.SpriteFrame, function (err, spriteFrame){
console.log(spritePath);
if (spriteFrame) {
console.log("恭喜发财");
if (Alert._sprite) {
Alert._sprite.spriteFrame = spriteFrame;
}
}
});
};
// 执行弹进动画
self.startFadeIn = function () {
cc.eventManager.pauseTarget(Alert._alert, true);
Alert._alert.position = cc.p(0, 0);
Alert._alert.setScale(2);
Alert._alert.opacity = 0;
Alert._alert.runAction(self.actionFadeIn);
};
// 执行弹出动画
self.startFadeOut = function () {
cc.eventManager.pauseTarget(Alert._alert, true);
Alert._alert.runAction(self.actionFadeOut);
};
// 弹进动画完成回调
self.onFadeInFinish = function () {
cc.eventManager.resumeTarget(Alert._alert, true);
};
// 弹出动画完成回调
self.onFadeOutFinish = function () {
self.onDestory();
};
// 按钮点击事件
self.onButtonClicked = function(event){
if(event.target.name == "enterButton"){
console.log("确认按钮");
if(self._enterCallBack){
self._enterCallBack();
}
}else{
console.log("取消按钮");
}
self.startFadeOut();
};
// 销毁 alert
self.onDestory = function () {
Alert._alert.destroy();
Alert._enterCallBack = null;
Alert._alert = null;
Alert._detailLabel = null;
Alert._cancelButton = null;
Alert._enterButton = null;
Alert._animSpeed = 0.3;
Alert._sprite = null;
};
}
},
start () {
},
});