cocos creator实现wordle游戏(三)
接着上篇教程 微信小游戏实战--cocos creator实现wordle游戏(二)我们继续游戏界面的设计,准确的说上篇教程只是完成了游戏界面的基础设置,同时也介绍了节点创建的步骤。这篇教程才算是正式进入了游戏的界面设计。 先在“Canvas”节点下创建一个“home”空节点并且挂载widget组件,操作方式在上篇教程中已经介绍,这里不再累述。“home”空节点的高宽,widget配置和bg背景节点一样。
布局分析
在最开始时,我将界面分解为3部分:顶部的用户头像,昵称显示区域。中间的单词输入区域。底部的键盘区域。对顶部区域做了适配顶端配置,顶部区域做了适配底部的配置,中间区域设置和顶部和底部边距,都是通过widget组件实现的。按照该设计思路,层级管理器中的结构如下:
- top: 宽720,高200(打算放两排组件,第一排是用户头像,第二排是其余功能按钮。一排100像素应该够了)
- bottom:宽720,高400 (一个键盘按钮的高度设计为80,4行按钮差320,算上行间距400应该够了)
用户头像,昵称布局
在top节点下再创建一个空节点“head”和一个Label控件“name”,然后再在head空节点下创建两个精灵控件“headimage”,“headborder”。Label控件和精灵控件都在2D对象下。headimage用于显示用户的头像,name用于显示用户的昵称。层级管理器中结构如下:
之所以要将headimage嵌套在空节点head下是因为,Cococs Creator中精灵都是矩形的,而我们要实现的是一个圆圈,所以需要将headimage嵌套在head下,并且再增加一个headborder精灵给头像套一个圈圈。具体的设置步骤如下:
- head:高宽为100像素,添加一个MASK遮罩组件,设置遮罩类型为Ellipse椭圆,Segments 64
通过这个配置就可以将head下的headimage精灵的四角遮住,并且呈圆角切割,看上去矩形的图像就被切割为圆形了,但是还是还能看出锯齿。所以headborder精灵就派上用场了,先通过图片处理软件生成一个100 X 100的圆圈图片,然后利用抠图软件将背景扣掉,剩下圆圈即可。还记得上篇教程介绍的资源管理器吗?生成好的图片就需要放在资源管理器中resources->Texture文件夹下,只需要在资源管理器中右键点击Texture文件夹,选择“在资源管理器中”打开这个文件夹了,然后将生成好的图片复制到该文件夹下即可,推荐使用这种方式,因为如果随着你的项目多了起来,每个项目下都有Textrue文件夹,你直接在电脑上去找的话,可能会找到别的项目下的这个文件夹。在Cocos Creator的资源管理器中打开文件夹就不会出现这种问题了。抠图软件网上很多,但是要收费的,这里给大家推荐一个免费的,而且是网页版的不需要安装,点我。这个游戏中用到的图片都很简单,用windows自带的画图软件就可以实现,具体步骤就不再介绍了,比较简单。
headimage和headborder的高宽都是 100,其他属性使用默认的即可,但要注意在层级管理器中顺序,headborder是在headimage下面,这样的话在显示的时候headborder会在headimage上面,这样才能显示出圆圈,当然你也可以在代码里面设置他们的层级来达到这个效果。
headboder中,设置图片为刚才制作好的圆圈图片,设置如下:
- 通过Position属性:我们要实现的效果就是,头像显示在左上角,上边距5像素,左边距5像素。具体的属性设置如下
- 通过widget组件: 在head节点上添加widget组件,设置如下:
继续用户昵称 name(类型是Label)的设置,如下:
字体颜色,设置为黑色也就是RGB(0,0,0)
清空默认自带的“Label”字符
字体大小,根据自己的喜好来设置
内容显示方式:有4种分别为none:效果就是内容超出Label宽度了就看不到超出部分。Clamp:内容超出Label宽度了会自动换行。Shrink:内容超出Label宽度后会自动缩小以完全显示。Resiz_height:内容超出Label宽度后自动换行,并配合EnableWrap属性自动调整高度。需要注意一下的是,刚创建的Label默认显示方式为none,这个时候是无法改变Label的大小的,这个坑了我好久才发现······
宽度和高度就根据自己的喜好来设置了,我设置的是宽100高50.再根据刚才介绍的布局方法设置Position位置。最后在场景编辑器中的效果如下:
功能按钮和答案提示区域布局
在用户头像的下方,摆放一些功能按钮和答案提示区域,这里我们会用到Layout控件,右键点击top节点,选择“创建”-->“UI组件”-->“Layout(布局)”,命名为“Layout_top”,然后在Layout_top下“创建”-->“UI组件”-->“Button(按钮)”,依次创建“how”,“score”,“rank”,“btn_blank”,“btn_blank”5个按钮,层级管理器中显示结构如下:
- 只需要设置高度60,和子节点的按钮高度一样。
- 设置显示方式为HORIZONTAL(水平)。
- 设置ResizeMode为CONTAINER(容器)
- 子节点的间距
- 设置排列方式为LEFT_TO_RIGHT从左至右
做好如上配置后,Layout_top的大小会自动调整好宽度为320,子节点也会按照间距5像素,从左至右排列好。然后,在层级管理器中可以先鼠标左键点击head,然后按住Ctrl键,再鼠标左键点击Layout_top,再点击上方的左对齐按钮,可快速将Layout_top在水平位置设置为和head一样,最后再设置一下Y轴位置,可以按照前面介绍的根据Position位置设置,也可以拖动那个绿色的箭头来设置。如下所示:
接着再右键点击top节点,创建一个名为“Layout_topright”的Layout控件,设置如下:
- 高度还是设置为60,因为它的子节点的高度也是60
- 展示方式还是水平。
- ResizeMode设置为Container(容器)
- 左边距设置为5,保持和Layout_top有一定的间隙。
- 子节点排列方式还是从左到右。 在Layout_topright节点下,创建5个Button(按钮),名称规则为Layout_topright_ + index,按钮高宽均为60,设置好按钮图标,如下所示:
中间字母输入区域布局
游戏中字母输入区域就是一个5 X 6 的格子区域,如果只是单纯从布局上来说,完全可以利用一个Layout控件就可以实现,配置如下:
- 设置显示模式为:GRID
- ResizMode:Container(容器)
- 起始排列方式:HORIZONTAL(水平)
- 水平间距:5
- 纵向间距:5
- 纵向排列顺序:TOP_TO_BOTTOM(从上到下)
- 水平排列顺序:LEFT_TO_RIGHT
- 固定方式:FIXED_COL(列)
- 固定列数:5 这样的配置,在使用代码为Layout组件生成30个子节点时,就会按照从左到右,从上到下的顺序,一次生成并排列好30个子节点。但是,我没有采用这个设计思路,WTF?没采用还写出来干嘛?额,这里说点题外话,由于我一直坚信能够快速上手一门技术的最好方式就是去实践,去完成一个小而完整的例子。在实践过程中,会遇到很多问题,这些问题的产生也是验证自己对功能实现的设计,而写这个教程也是记录学习的过程。所以,虽然有些内容跟实现这个游戏无关,但是对于掌握一门技术,一个工具来说还是值得记录的。
因为游戏的进程是一个单词一个单词的猜测,一排5个格子输入完成后,会步入下一排,所以前一排就必须锁定,让玩家不能再操作了。为了方便程序的控制,最终打算还是采用创建6个水平布局的Layout控件来实现。操作也很简单:
右键点击top节点,选择“创建”-->“UI组件”-->“Layout(布局)”,命名为“Layout_middle1”。配置如下:
- 展示方式:HORIZONTAL(水平)
- ResizMode:CONTAINER(容器)
- 左边距:10
- 右边距:10
- 子节点间距:10
- 排列方式:LEFT_TORIGHT(从左到右) 高度设置100,因为后续通过代码加载的子节点为边长100的正方形,宽度不用设置,设置ResiMode为CONTAINER后会自动匹配宽度。创建好后,默认位置在场景编辑器中处于水平中央,此时可以通过Ctrl + C,Ctrl + V的形式复制另外5个Layout出来,如上图所示。此时在场景编辑器中他们将会处在同一位置,只需要调整他们的Position即可,X不需要设置,因为已经在水平中央了,只需要根据父节点的高度和锚点计算各个Layout的Y值即可。最终如下图所示:
底部键盘按钮布局
底部键盘按钮的布局方式同上,创建一个Layou命名为“Layot_bottom1”,设置如下:
制作Prefab
这里先对Prefab做个简单介绍。Prefab也可以视为一个节点,在场景中,如果某一部分会被重复使用多次,就可以将该部分设计为一个Prefab,通过代码动态调用来达到简化页面设计的目的。在这个游戏中字母输入框就可以被设计为一个Prefab,如果Cocos Creator中Label控件可以设置边框的话,就不用这么麻烦了。由于Label控件不能设置边框,我们需要再添加一个精灵来实现一个带边框的Label,具体思路就是Label设计大小为 95 X 95,精灵设计大小为 100 X 100,Label显示在精灵上面,精灵的背景图为一个矩形方框图(需要扣掉背景),方框的线条宽度为5。这样就可以显示出一个边框为5像素的Label了。接下来我们就开始制作这个prefab:
- 首先,我们在Layout_middle1 下创建一个空节点命名为“btn_middle”,大小设置100 X 100,然后在属性检查器中点击添加组件,添加一个Button组件,这个是为了后续实现点击事件。
- 然后,在该空节点下创建一个精灵和一个Label。精灵设置大小100 X 100,Label大小95 X 95。在层级管理器中,它们的结构如下:
- 加载plist文件,方便在代码中控制背景图的切换。
- 设置初始状态的背景图。
- 通过添加组件按钮,添加一个UIOpacity组件,用于后续实现闪烁动画效果。
Label的设置:
- 设置字体颜色为黑色
- 清空默认的字符“Label”
- 字体大小设置为60
- 显示方式为SHRINK,默认为none,此时不能设置Label大小
- 加粗
根据个人的喜好和页面美观要求可做不同的设置。
- 完成以上设置以后,我们左键点击“btn_middle”节点,将它拖拽到下方资源管理器中resuorces-->prefab文件夹中,就完成了一个prefab的创建。如下图所示,可以看到prefab是绿色的。
- 最后我们将Layout_middle1下的btn_middle删除掉,因为我们是通过代码来动态加载这个prefab就不需要在设计期出现了。
- 按照同样的方式制作键盘按钮的prefab,名称为btn_keyborde,制作方式一样就不再累述了。只是大小不一样而已。键盘按钮的大小设置为61 X 80
动态加载Prefab
创建脚本文件:右键点击资源管理器中scripts文件夹,选择“创建”-->“脚本(Typescript)”,命名为home,之所以这样命名是为了和home节点保持一致。然后左键点击层级管理器中的home节点,将home脚本文件拖拽到home节点的属性检查器中,完成脚本文件的挂载,
import { _decorator, Component,tween,Sprite,ImageAsset, Node, Label,Button,Tween, instantiate,assetManager, UIOpacity, Vec3, Color, SpriteFrame } from 'cc';
export class gamemanager extends Component {
// [1]
private homeNode : Node;
private static _instance : gamemanager;
static get instance () {
if (this._instance) {
return this._instance
}
else {
this._instance = new gamemanager();
return this._instance;
}
}
init(aNode) {//初始化场景home节点
gamemanager.instance.homeNode = aNode;
}
}
在gamemanager中实现prefab的加载,代码如下:
//添加prefab组件,layout组件上设置了布局,只需要添加到对应的layout下即可完成布局
addNode (prefab,parentNode,icount,keybordeArray) {
for (let i = 0 ; i < icount; i++) {
let newNode = instantiate(prefab);
if (keybordeArray) {
newNode.name = keybordeArray[i];
newNode.getChildByName('Label').getComponent(Label).string = keybordeArray[i];
}
else {
newNode.name = parentNode.name + '_' + (i + 1).toString();
}
parentNode.addChild(newNode);
}
}
参数说明:
- prefab:要加载的prefab
- parentNode:添加到哪个节点下,就是前面创建的Layout节点
- icount:需要添加多少个prefab,字母如数区域一排添加5个,键盘按钮区域第一排10,第二排9个,第三排7个
- keybordeArray:键盘按钮的显示内容如“A” 。 代码逻辑:使用instantiate函数将Prefab初始化为节点,如果keybordeArray不为null则说明是在键盘区域加载prefab,需要获取到键盘按钮节点下的Label控件,来显示对应的字母。最后在parentNode下添加该节点完成prefab的动态加载。 在gamemanager中实现,页面布局。代码如下:
init_layout (btn_iddle,btn_keyborde) {
let homeNode = gamemanager.instance.homeNode;
//中间按钮布局
let topnode = homeNode.getChildByName('top');
let layoutnodename = '';
for (let i = 0; i < 6; i++){
layoutnodename = 'Layout_middle' + (i + 1).toString();
const layoutnode = topnode.getChildByName(layoutnodename);
gamemanager.instance.addNode(btn_iddle,layoutnode,5,null);
}
//底部键盘布局
let layout1 = topnode.getChildByName('Layout_bottom1');
let layout2 = topnode.getChildByName('Layout_bottom2');
let layout3 = topnode.getChildByName('Layout_bottom3');
let keybordearray1 = ['Q','W','E','R','T','Y','U','I','O','P'];
let keybordearray2 = ['A','S','D','F','G','H','J','K','L'];
let keybordearray3 = ['Z','X','C','V','B','N','M'];
gamemanager.instance.addNode(btn_keyborde,layout1,10,keybordearray1);
gamemanager.instance.addNode(btn_keyborde,layout2,9,keybordearray2);
gamemanager.instance.addNode(btn_keyborde,layout3,7,keybordearray3);
}
参数说明:
- btn_middle,btn_keborde分别为中间字母输入框和键盘按钮prefab 代码逻辑:根据home节点,通过Layout节点的名称获取到Layout控件的节点,在Layout节点下加载prefab 在home.ts脚本文件中,添加prefab属性,代码如下:
import { _decorator, Component,Prefab,Button,Color,Label,Sprite, Widget, UITransform, VideoPlayer } from 'cc';
import { gamemanager } from './gamemanager';
const { ccclass, property } = _decorator;
@ccclass('home')
export class home extends Component {
@property({type : Prefab,visible : true})
private btn_middle = null;
@property({type : Prefab,visible : true})
private btn_keyborde = null;
onLoad () {
}
onEnable () {
}
start () {
}
}
代码保存后 ,切换到Cocos Creator中,在home节点的属性检查器中就可以看到,prefab属性了
在home.ts脚本的onLoad调用gamemanager实现动态加载prefab完成布局。在onLoad中调用gamemanager的布局函数,实现布局。代码如下:
import { _decorator, Component,Prefab,Button,Color,Label,Sprite, Widget, UITransform, VideoPlayer } from 'cc';
import { gamemanager } from './gamemanager';
const { ccclass, property } = _decorator;
@ccclass('home')
export class home extends Component {
@property({type : Prefab,visible : true})
private btn_middle = null;
@property({type : Prefab,visible : true})
private btn_keyborde = null;
onLoad () {
//获取home节点
gamemanager.instance.init(this.node);
//初始化游戏界面
gamemanager.instance.init_layout(this.btn_middle,this.btn_keyborde);
}
onEnable () {
}
start () {
}
}
由于home.ts脚本文件挂载到了home节点下,在home.ts中,this.node即代表home节点,this.btn_middle,this.btn_keyborde是通过在Cocos Creator中关联加载的。完成这些代码后,运行程序,