CocosCreator教程
场景管理
新建一个场景
1.点击左上角的文件,在对话框中点击新建场景。
2.ctrl+n直接创建新的场景。
在创建好一个新的场景之后,我们发现左上角的层级管理器被恢复到默认的状态了。这就表明我们创建新的场景成功了,并且切换到了新的场景当中。
创建好之后,点击保存,将场景文件保存起来。
然后在左下角的资源管理器当中就可以进行手动的场景切换了。
代码中进行切换场景的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| cc.director.loadScene("scene2",function(){
})
cc.director.preloadScene("scene2", function () { })
cc.game.addPersistRootNode(this.node);
cc.game.removePersistRootNode(this.node);
|
键鼠事件、触摸事件、自定义事件
鼠标事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| start(){ this.node.on(cc.Node.EventType.MOUSE_DOWN, e => { console.log("鼠标按下了,坐标是" + e.getLocation()) if (e.getButton() == cc.Event.EventMouse.BUTTON_LEFT) { console.log("鼠标左键被按下了"); } if (e.getButton() == cc.Event.EventMouse.BUTTON_RIGHT) { console.log("鼠标右键被按下了"); } }) this.node.on(cc.Node.EventType.MOUSE_ENTER, e => { console.log("鼠标移入目标区域了,X的坐标是" + e.getLocationX()); }) this.node.on(cc.Node.EventType.MOUSE_LEAVE, e => { console.log("鼠标离开区域了,坐标是" + e.getLocation()); }) this.node.on(cc.Node.EventType.MOUSE_UP, e => { console.log("鼠标松开了"); }) }
|
键盘事件
1 2 3 4 5 6 7 8 9 10 11
| start(){ cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, e => { if (e.keyCode == cc.macro.KEY.w) { console.log("w键被按下了"); } if (e.keyCode == cc.macro.KEY.p) { console.log("p键被按下了"); } }) }
|
触摸事件
1 2 3 4 5 6 7 8 9 10 11 12 13
| start(){ this.node.on(cc.Node.EventType.TOUCH_START, e => { console.log("检测到用户触摸屏幕" + e.getID()); this.node.dispatchEvent(new cc.Event.EventCustom("myMessage1", true)) })
this.node.on("myMessage1", e => { console.log("自定义事件"); }) }
|
碰撞检测
在精灵的属性检查器中添加组件-碰撞组件
1.Box Collider:盒子型碰撞组件,是一个矩形框
2.Circle Collider:圆形碰撞组件,是一个圆形
3.Polygon Collider:多边形碰撞组件,非常耗费资源
他们的一些属性:
Tag:用于区分碰撞时,与哪个组件发生了碰撞
Offset:将碰撞框进行平移
Size:对碰撞框的大小进行修改
在代码中检测碰撞
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| start() { cc.director.getCollisionManager().enabled = true }
onCollisionEnter(other) { console.log("碰撞发生了" + other.tag); }
onCollisionStay(other) { console.log("碰撞持续中"); }
onCollisionExit(other) {
}
|
音频播放
首先要将需要播放的音频放入resources文件夹当中
然后创建一个空的节点,新增一个其他组件-AudioSource
然后将音频文件直接拖入即可以简单进行播放音频
具体代码操作如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| start() {
cc.loader.loadRes("Kevin MacLeod - If I Had a Chicken", cc.AudioClip, (res, clip) => { let audioId: number = cc.audioEngine.playMusic(clip, true) cc.audioEngine.pause(audioId) cc.audioEngine.resume(audioId) cc.audioEngine.stop(audioId) cc.audioEngine.setLoop(audioId, true) cc.audioEngine.setVolume(audioId, 1) }) }
|
物理系统
给一个精灵加上一个刚体即可为该节点加上物理引擎,添加组件->物理组件->Rigid Body
然后需要像开启碰撞检测一样,需要在代码中进行开启,必须写在onload中
1 2 3
| onLoad(){ cc.director.getPhysicsManager().enable = true }
|
开启物理碰撞盒绘制
1 2 3 4 5 6
| cc.director.getPhysicsManager().debugDrawFlags = cc.PhysicsManager.DrawBits.e_aabbBit | cc.PhysicsManager.DrawBits.e_pairBit | cc.PhysicsManager.DrawBits.e_centerOfMassBit | cc.PhysicsManager.DrawBits.e_jointBit | cc.PhysicsManager.DrawBits.e_shapeBit ;
|
给精灵一个力或者速度
1 2 3 4 5 6 7 8 9
| start(){ let rBody = this.getComponent(cc.RigidBody) rBody.applyForce(cc.v2(1000,0),cc.v2(0,0),true) rBody.applyForceToCenter(cc.v2(5000,0),true)
rBody.linearVelocity = cc.v2(50,0) }
|
刚体属性
1.Enabled Contact Listener:开启碰撞检测
2.Bullet:如果刚体高速移动,可能会产生没有检测到碰撞的情况,所以需要勾选上这个框,这样系统就会检测的更加精确,但是会更加消耗性能
3.Type:默认是Dynamic,选择这项会让精灵获得重力;Static:不会受到重力以及速度的影响;Kinematic:这个类型不收到重力的影响,但是会受到速度的影响;Animated:通常与动画进行搭配。
4.Allow Sleep:允许睡眠,用于节省资源。
5.Gravity Scale:受到重力大小的影响。
6.Linear Damping:线性阻尼。
7.Angular Damping:角速度阻尼。
8.Linear Velocity:给一个速度X或Y方向上。
9.Angular Velocity:给一个角速度。
10.Fixed Rotation:保持角度不变。
11.Awake:保持刚体一直处于计算状态。
物理组件-碰撞组件
添加组件->物理组件->Collider即可添加
碰撞属性
1.Friction:摩擦力
2.Restitution:弹性系数
3.Sensor:传感器,勾选之后就不会发生碰撞了,但是会执行碰撞函数中的方法,可以作为存档点,或者触发剧情等功能的使用
开启碰撞
首先要在刚体组件中,将Enable Contact Listener勾上
然后在脚本中的操作如下:
1 2 3 4 5 6 7 8 9 10 11 12
| onBeginContact(contact,self,other){ let points = contact.getWorldManifold().points let normal = contact.getWorldManifold().normal }
onEndContact(contact,self,other){
}
|
射线
打出一条射线
1 2 3 4 5 6 7 8 9 10 11 12 13
| onLoad(){ let results = cc.director.getPhysicsManager().rayCast(this.node.getPosition(),cc.v2(this.node.x,this.node.y+100),cc.rayCastType.Closest) for(let i = 0; i < results.length; i++){ let res = results[i] res.collider.tag res.point res.normal } }
|
动作系统Action(已被缓动系统Tween替代)
延时动作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| start() { let action = cc.moveTo(2, cc.v2(200, 200)) action = cc.moveBy(2, cc.v2(200, 200))
action = cc.rotateTo(2, 100)
action = cc.scaleTo(2, 1.5, 0.5)
action = cc.jumpBy(2, 200, 0, 200, 3)
action = cc.blink(3, 5)
action = cc.fadeOut(3)
action = cc.fadeIn(3)
action = cc.fadeTo(3, 100)
action = cc.tintTo(3, 100, 30, 100) this.node.runAction(action) this.node.stopAction(action) this.node.stopAllActions() action.setTag(33) this.node.stopActionByTag(33) this.node.pauseAllActions() this.node.resumeAllActions() }
|
瞬时动作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| start() { let action = cc.show() action = cc.hide() action = cc.toggleVisibility() action = cc.flipX(true) action = cc.flipY(true) action = cc.callFunc(() => { })
action = cc.fadeOut(1) let action2 = cc.fadeIn(1)
let seq = cc.sequence(action, cc.delayTime(1), action2)
let repeat = cc.repeat(seq, 3) this.node.runAction(repeat)
let move = cc.moveTo(3, 500, 500) let color = cc.tintTo(3, 100, 100, 20)
let spawn = cc.spawn(move, color) this.node.runAction(spawn) }
|
动画系统Animation
想要使用动画功能,必须为节点添加Animation组件,然后将动画片段拖到属性检查器的Animation组件当中进行使用
Animation属性
1.Default Clip:默认片段,默认播放的片段
2.Clips:片段数,后面的数字是几,就会为我们创建几个片段框以便我们进行动画的关联
3.Play On Load:在游戏开始时进行播放
动画制作
在动画编辑器当中点击第一个按钮进行动画的制作
脚本控制
1 2 3 4 5 6 7 8 9 10 11
| start() { let ani = this.getComponent(cc.Animation) ani.play("run") ani.pause() ani.resume() ani.stop() }
|
Canvas、Label、RichText组件
1.Canvas就是用户的主界面,可以调整高适配或者宽适配
2.Label组件->简单的文字组件
3.RichText组件,可以让各种标签包住文字,以达到各种不同的效果
如:
1 2 3 4 5 6
| <color="#"></color> //可以定义文字的颜色 <on click="事件名"></on> //当用户点击被on标签包住的文字时会触发脚本中定义好的方法 <size=33></size> //定义文字的大小 <i></i> //斜体 <outline color=#FFFFFF width=3></outline> //文字描边,可同时配置边框的颜色和宽度 <img src=""></img> //图文混排
|
屏幕适配与遮罩
1.遮罩:
在存在子节点的父节点上添加一个渲染组件->Mask,就可以实现遮罩效果,子节点超出父节点的部分会被隐藏,
在遮罩属性框中,Inverted是反转,勾选上就会让遮罩实现一个反向遮盖的效果
Type属性可以将遮罩的形状定义为方形或者圆形。
2.屏幕适配:
首先,所有的UI的要放在Canvas下,
然后要为进行屏幕适配的组件添加一个组件->UI组件->Widget,然后要在指定位置固定好,这样在不同的设备中,该UI节点会显示在固定的位置,不会乱动。
属性中的Target,可以将其他的节点拖拽过来,然后拖拽过来的节点就会变成名义上的父节点,然后进行相关位置计算,一般不会用到。
UI组件
直接将控件库中的button拖到场景编辑器当中,或者在层级编辑器中创建button组件,就可以生成一个button按钮。
在button的属性栏中,可以定义按钮的默认图片、鼠标移入时的图片、被点击的图片以及被禁用的图片,还可以设计这四种状态不同颜色或者不同的缩放。
为按钮绑定点击事件:
首先创建好脚本并关联在button按钮上,然后在脚本中写好要触发的方法,然后将button属性栏中的click event的数字改成要触发的事件数,
然后将button节点拖入node节点框中,然后选择脚本文件以及方法名,即可绑定好点击事件。
Layout组件
就是一个布局,可以让其中的子节点进行一定方式的排列。
是一个可以滑动的框,可以将超出ScrollView组件外的内容通过滚动条的方式进行浏览。
ScrollView属性:
1.Horizontal:将滚动条水平放置。
2.Vertical:将滚动条垂直放置。
3.Inertia:是否开启滚动的惯性。
4.Brake:从0-1进行取值,选择惯性的大小,为1的时候没有惯性。
5.Elastic:反弹效果,当滚动条到达边缘的时候,是否可以继续拖动,松开再弹回。
6.Bounce Duration:反弹的速度。
7.Scroll Events:滑动触发事件,与按钮操作相同。
拖动这个组件到场景编辑器当中,会自动创建两个子节点,其中
scrollBar是控制滚动条的相关属性。
view是控制视框中相关属性。
view中的content节点是放置文本内容的。
PageView组件
就是一个轮播图组件
可以在精灵属性中,进行每一页的颜色修改等。
ProgressBar组件
就是一个进度条组件
ProgressBar属性:
1.Mode:调整进度条的方向。
2.Total Length:当下面的Progress为0-1时,对应的进度条长度是多少,一般当Progress为1的时候,将此项属性的长度设置为父组件的长度。
3.Reverse:反向。
EditBox组件
就是一个输入框
EditBox节点:
1.TEXT_LABEL:用户输入的文本。
2.PLACEHOLDER_LABEL:默认显示的文本,即占位符,当用户未在输入框输入时,或者将输入框中的文字全部删除之后,就会显示本节点的内容。
EditBox属性:
1.String:输入的文本。
2.Background Image:设置背景。
3.KeyboardReturnType:在手机上,与电脑无关,点击输入框之后,弹出键盘,然后右下角的按键样式。
4.input Flag:输入的标识,比如密码域等。
5.input Mode:单行\多行\邮箱\数字等对键盘样式的控制。
回调:
1.Editing Did Began:开始编辑的时候调用指定的回调;
2.Text Changed:文本发生改变就会调用指定的回调;
3.Editing Did Ended:编辑模式结束(失去焦点)的时候调用指定的回调;
4.Editing Return:按下回车,就会调用指定的回调。
Slider组件
是一个用户可以滑动的一个条,比如将可以用来做音量条,进度条等。
属性:
Slide Events:触发滑动的回调事件。
Toggle组件
是一个单选框,如果将多个Toggle组件置于一个节点之下,然后为这个节点添加一个Toggle Container组件,就可以实现多个项目单选的操作了。
VideoPlayer组件
可以播放本地或者远程的视频,在代码中播放和音频操作一致,这个组件的延迟很大,一般不要使用。
WebView组件
打开指定的网页。
骨骼动画
使用代码控制骨骼动画
1 2 3 4
| let skeletonAnimation = this.node.getComponent(sp.Skeleton);
skeletonAnimation.setAnimation(0, "animation", false)
|
数据存储
以将键值对的方式将数据存储在本地内存当中。
1 2 3 4 5 6 7 8 9 10 11 12
|
cc.sys.localStorage.setItem("key","value")
cc.sys.localStorage.getItem("key")
cc.sys.localStorage.removeItem("key")
cc.sys.localStorage.clear()
|
数据格式-JSON
Json是一种数据格式,还有xml,csv等。
使用JSON.stringify()将对象转换为JSON格式,即Json字符串,然后就可以存入本地缓存当中在需要的时候读取出来,通过JSON.parse()转换为对象就可以正常使用了,常用于保存关卡数据、等非临时数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Person{ name:string id:number sex:string }
let person:Person = new Person() person.id=1 person.name="小红" person.sex="男"
let json = JSON.stringify(person)
let save:Person = Object.assign(new Person(),JSON.parse(json))
|
网络请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| start() { let url = "http://t.yushu.im/v2/movie/in_theaters?apikey=0b2bdeda43b5688921839c8ecb20399b"
let request = cc.loader.getXMLHttpRequest() request.open("GET", url, true) request.onreadystatechange = () => { if (request.readyState == 4 && request.status == 200) { console.log("请求完成了"); console.log(request.responseText); } } request.send() }
|
Socket.io
安装好node.js环境之后,使用npm install –save express
和npm install –save socket.io即可安装好socket.io插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| var app = require('express')() var http = require('http').Server(app) var io = require('socket.io')(http)
http.listen(3000, function () { console.log('server listen on 3000') })
io.on('connection', function (socket) { console.log("有客户端连接") socket.on('message', function (data) { console.log('客户端发来消息' + data) }) })
|
如何运行服务端脚本:
在cmd中进入到脚本文件所在位置,然后使用node myServer.js即可运行服务端脚本文件。
项目中经验
动态修改刚体的重力,以控制静止或者受重力下落
在编辑器中初始设置刚体为static则可以让其保持静止,然后经过每种操作让其受重力下降
1
| this.node.getComponent(cc.RigidBody).type = cc.RigidBodyType.Dynamic
|
强制类型转换
1
| GameModel._ins.posArr[i] = (this.node.name as any) as number
|
使用json文件画格子
- 首先创建一个map.json的文件,在其中编写行列的信息,如下所示为九行五列
1 2 3 4 5 6 7 8 9
| { "map":[ [1,1,1,1,1,1,1,1,1], [1,1,1,1,1,1,1,1,1], [1,1,1,1,1,1,1,1,1], [1,1,1,1,1,1,1,1,1], [1,1,1,1,1,1,1,1,1] ] }
|
- 然后在MainGame.ts文件中创建格子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @property(cc.Asset) map: cc.Asset = null
posArray: cc.Vec2[] = []
mapBorn() { let map = this.map.json.map console.log(map); for (var i = 0; i < map.length; i++) { for (var j = 0; j < map[i].length; j++) { if (map[i][j] == 1) { let block = Tools.newPrefab("Block", this.node) block.x = -240 + 120 * i block.y = -500 + 120 * j this.posArray.push(cc.v2(block.position)) block.destroy() } } } console.log(this.posArray); }
|
- 使用随机数取得数组中格子的位置信息,并且使用完之后将对应格子的位置删除
1 2 3 4 5
| let num = Math.floor(Math.random() * GameModel._ins.mainGame.posArray.length) eagle.x = GameModel._ins.mainGame.posArray[num].x + 360 eagle.y = GameModel._ins.mainGame.posArray[num].y + 640 eagle.angle = Math.random() * 360 GameModel._ins.mainGame.posArray.splice(num, 1)
|
- 然后再进入下一关的时候再重新生成一次格子
生成脚印覆盖住父节点的情况
因为物体生成的脚印必须是保留在原地的,所以不能让脚印绑定在父节点的下面,否则脚印会随着父节点的移动而移动,但是如果直接将脚印绑定在根节点,又会让脚印覆盖到物体的上方,这显然也是不符合逻辑的。
解决办法:在根节点创建一个节点,并将节点置于最上方,然后让父节点生成脚印之后,将脚印的父节点绑定到该节点,就可以完美解决上面的两个问题。
让碰撞体的框体显示出来
1 2 3
| cc.director.getCollisionManager().enabled = true cc.director.getCollisionManager().enabledDebugDraw = true; cc.director.getCollisionManager().enabledDrawBoundingBox = true;
|
动态加载图片资源,节省预制体的制作数量
需要把动态加载的图片资源放入resources文件夹下
1 2 3 4 5 6 7 8 9
| cc.loader.loadRes("enemy0_die", cc.SpriteFrame, (err, res) => { this.node.getComponent(cc.Sprite).spriteFrame = res cc.director.getCollisionManager().enabled = false setTimeout(() => { this.node.destroy() cc.director.getCollisionManager().enabled = true }, 300); })
|
在属性框中生成一个预制体\节点数组列表
1 2
| @property([cc.Prefab]) public PrefabArr: Array<cc.Prefab> = [];
|
效果:

开启一个间隔时间不确定的计时器
1 2 3 4 5 6 7 8
| timer() { let time: number = Tools.random(3, 5, false); this.scheduleOnce(() => { console.log("do something..."); this.timer(); }, time); }
|
声明二维数组
声明一个存放节点坐标的二维数组,因为没有初始化,所以后续进行push操作的时候会报错。
目前的一种解决办法:先把外层数组的长度确定了。
1 2
| posArr: Array<Array<cc.Vec2>> = [[], [], [], [], []]
|
在碰撞回调中修改节点的属性会报错
比如在开启物理碰撞之后,在碰撞回调中修改节点的角度属性,会报错,如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| onBeginContact(contact, self, other) { if (other.tag == 10) { setTimeOut(() => { if (GameModel._ins.gameState) { this.node.angle = 90 this.play() } }, 10); if (!this.isInQuery) { this.isInQuery = true setTimeout(() => { cc.find("/Canvas/bgSpr").getComponent(bgController).query.push(this.node) }, 200); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| onBeginContact(contact, self, other) { if (other.tag == 10) { setTimeout(() => { if (GameModel._ins.gameState) { this.node.angle = 90 this.play() } }, 10); if (!this.isInQuery) { this.isInQuery = true setTimeout(() => { cc.find("/Canvas/bgSpr").getComponent(bgController).query.push(this.node) }, 200); } } }
|
spine动画关闭了alpha预渲染,透明地方还是有色块
解决办法:在资源压缩的地方,将存放skeleton数据的文件夹排除掉即可。
摘下满天果点击物体下落到指定位置进行三消的解决思路
当点击天上的物体之后,将其按顺序压入数组中,当做栈来使用,每次按顺序选中水果的时候就可以直接使用弹栈(数组中的第一个元素)进行操作,这样就可以按照顺序拿到操作的水果。
动态更新碰撞盒大小
1 2
| otherNode.getComponent(cc.PhysicsBoxCollider).size = cc.size(214, 323); otherNode.getComponent(cc.PhysicsBoxCollider).apply();
|
更改完尺寸之后一定要调用碰撞盒.apply方法,这样才会修改生效。
刚体碰撞回调函数没有持续检测
如果想要让两个紧贴的刚体重新开始碰撞,则让其中一个刚体的碰撞盒大小发生改变即可,记得使用apply方法实现。
这样他们两个之间的碰撞函数就会重新调用。
左右移动物体时有一种缓动的效果
1 2 3 4 5 6
| let delta = e.getDeltaX(); this.touchTargetP = this.touchTargetP.add(cc.v2(delta * 1.3, 0));
this.node.getComponent(cc.RigidBody).linearVelocity = cc.v2(this.touchTargetP.sub(this.node.getPosition()).mul(1250 * dt).x, 0);
|
动态修改节点的分组信息
获取\修改节点分组:this.node.group;
1 2 3 4 5 6 7 8 9
| onEndContact(contact, self, other): void { if (self.tag == 3 && self.node.group == "shootFruit" && other.node.group == "default") { self.node.group = "commonFruit"; this.innerCircle.sensor = false; this.innerCircle.apply(); } }
|
生成一个n等分圆(同时可以在随机范围内进行波动)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
getCirclePoints(r: number, pos: cc.Vec2, count: number, randomScope: number = 60): cc.Vec2[] { let points = []; let radians = (Math.PI / 180) * Math.round(360 / count); for (let i = 0; i < count; i++) { let x = pos.x + r * Math.sin(radians * i); let y = pos.y + r * Math.cos(radians * i); points.unshift(cc.v3(x + Math.random() * randomScope, y + Math.random() * randomScope, 0)); } return points; }
|
生成一个自动居中的格子地图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| createMap(): void { this.initPosArr(); let deltaY: number = (-this.line * this.blockSize + this.blockSize) / 2; let deltaX: number = (-this.line * this.blockSize + this.blockSize) / 2 for (let i: number = 0; i < this.line; i++) { for (let j: number = 0; j < this.line; j++) { let block: cc.Node = Tools.newPrefab("block", this.mapNode); block.width = this.blockSize; block.height = this.blockSize; block.x = deltaX + this.blockSize * i; block.y = deltaY + this.blockSize * j; block.getChildByName("label").getComponent(cc.Label).string = i + "," + j; this.blockInfo[i][j] = new Block(i, j, cc.v2(block.getPosition())); } } }
initPosArr(): void { this.blockInfo = []; for (let i: number = 0; i < this.line; i++) { this.blockInfo.push([]); for (let j: number = 0; j < this.line; j++) { this.blockInfo[i].push(null); } } }
|
缓动系统的精细应用
对数字增长进行缓动,比如数字的增长动画,将数字作为对象的属性,然后对对象进行缓动操作。
同时添加onUpdate事件,可以在缓动的同时进行相应的更新(比如更新Label的内容)。
注意:在onUpdate函数中,this指代Window,而不是当前的脚本,需要在外层使用that来记录一下当前的this指向。
1 2 3 4 5 6 7 8 9 10 11 12
| let currentNum = { a: 0 } let that: MainCtrl = this; cc.tween(currentNum) .to(0.8, { a: 100 }, { onUpdate() { that.furniture.getChildByName("progress").getComponent(cc.Label).string = Math.floor(currentNum.a).toString(); that.furnitureMask.getComponent(cc.Sprite).fillRange = 1 - (Math.floor(currentNum.a) / 100); } }) .start()
|
在屏幕中央或其他节点中使图片样式的(关卡、分数)与Label样式的数字共同实现居中
创建一个含有Layout组件的父节点,并且将类型设置为container,然后将图片和label节点拖入即可,随着label的长度的改变,图片和文本会自动向两侧移动,实现整体的居中。
抖音平台相关开发经验
检测用户在切换游戏到前台时是否是从侧边栏进入小游戏(用于特定游戏发放游戏奖励)
注意:这个方法类似于检测触摸,所以无法检测到用户第一次启动游戏,只能用于游戏从后台切换到前台的时候进行检测。并且调用该方法要在ooLoad方法当中,尽早的开启检测。
1 2 3 4 5 6
| tt.onShow(op => { if (op.scene == "021036" || op.scene == "021001") { } })
|
检测用户启动游戏时(非后台切换到前台)的启动场景
1 2
| tt.getLaunchOptionsSync().scene;
|
标签外挂提示块标签