Cocos 2D
text::TypeScript
official::Cocos Creator手册
official::CocosAPI
源码路径:\Cocos\Creator\3.8.0\resources\resources\3d\engine
一、引擎基础 1 初始配置
初始配置:
设置中文:File——Preferences——General——Language
默认编译器:File——Preferences——Program Manager——Default Script Editor
默认浏览器:File——Preferences——Program Manager——Default Browser
竖屏游戏:
Vscode:
文件后缀:文件——首选项——设置——文本编辑器——文件——Exclude——新加**/*.meta
2 界面
类似Untiy。
3 机制
渲染机制:需要一个标识来证明节点状态发生改变了才会重新读取信息,否则不渲染。
函数重载:函数内部通过多个if else
对入参进行判断来实现重载。
update (deltaTime: number ){ this .node .position .x += 1 ; } update (deltaTime: number ){ const p = this .node .position ; p.x += 1 ; this .node .position = p; }
4 项目目录结构
文件夹:
资源文件夹(assets):存放所有的本地资源、脚本和第三方库文件。assets 中的每个文件在导入项目后都会生成一个相同名字的 .meta
文件,用于存储对应的资源配置和索引信息。
资源库(library):library
是将 assets
中的资源导入后生成的,在这里文件的结构和资源的格式将被处理成最终游戏发布时需要的形式。当 library
丢失或损坏的时候,只要删除整个 library
文件夹再打开项目,就会重新生成资源库。
本地设置(local):local
文件夹中包含该项目的本机上的配置信息,包括编辑器面板布局,窗口大小,位置等信息。开发者不需要关心这里的内容。
扩展插件文件夹(packages):packages
文件夹用于放置此项目的自定义扩展插件。如需手动安装扩展插件,可以手动创建此文件夹。如需卸载扩展插件,在 packages
中删除对应的文件夹即可。
项目设置(settings):settings
里保存项目相关的设置,如构建发布菜单里的包名、场景和平台选择等。
临时文件夹(temp):temp
是临时文件夹,用于缓存一些 Cocos Creator 在本地的临时文件。这个文件夹可以在关闭 Cocos Creator 后手动删除,开发者不需要关心这里面的内容。
project.json:project.json
文件和 assets
文件夹一起,作为验证 Cocos Creator 项目合法性的标志,只有包括了这两个内容的文件夹才能作为 Cocos Creator 项目打开。开发者不需要关心里面的内容。
构建目标(build):在使用主菜单中的 项目 -> 构建发布… 使用默认发布路径发布项目后,编辑器会在项目路径下创建 build
目录,并存放所有目标平台的构建工程。
5 内置属性 @property ([SpriteFrame ]) cardsSF : SpriteFrame [] = [];@property ({type : Number }) public hp : number ;
二、常见控制组件 1 Canvas
默认的Canvas的左下角为世界原点。
对于在Canvas中的物体,当Position为(0,0)时是处在画布中心。
在Canvas中,越下面的组件图层越在前。
2 Node
位置,旋转,缩放。
Mobility:节点的可移动性。不同的可移动性会导致节点在光照上有不同的特性和表现。
Layer:层级。
official::节点和组件文档
3 Camera
摄像机。
三、常见UI组件
official::UI 组件
1 cc.Label
文本组件。
official::Label 组件参考
official::字体资源
const label = this .node .getComponent (Label );label.string = "Hello World" ;
2 cc.Sprite
图片组件。
属性
功能说明
CustomMaterial
图片材质
Color
颜色
Sprite Atlas
Sprite 显示图片资源所属的 Atlas 图集资源。
Sprite Frame
渲染 Sprite 使用的 SpriteFrame 图片资源。
Grayscale
灰度渲染模式
Size Mode
指定 Sprite 的尺寸,包括自定义尺寸(CUSTOM),裁剪透明尺寸(TRIMMED),不裁剪(RAW)
Type
渲染模式,包括普通(SIMPLE)、九宫格(SLICED)、平铺(TILED)、填充(FILLED)
Trim
勾选后将在渲染时去除原始图像周围的透明像素区域,该项仅在 Type 设置为SIMPLE 时生效。
Fill —
填充方式,该项仅在 Type 设置为 FILLED 时生效。
按钮组件。
4 cc.UIOpacity
透明度。0透明,255不透明。
5 cc.Layout
布局组件。
Type:横向,纵向,矩阵。
Affected By Scale:子节点缩放影响布局(当子节点物体缩放后,布局会动态调整间隔,否则默认是缩放为1时的间隔)
6 cc.BoxCollider2D
碰撞。
Sensor:触发器。
7 cc.Rigidbody2D
刚体。
Enabled Contact Listener:只有开启了刚体的碰撞监听,刚体发生碰撞时才会回调到对应的组件上
Type:静态,动态,动力学,动画(动力学的衍生)
8 关节组件
official::2D 物理关节
DistanceJoint2D:距离关节。将关节两端的刚体约束在一个最大范围内。超出该范围时,刚体的运动会互相影响。
FixedJoint2D:固定关节。根据两个点将两个物体固定在一起。
HingeJoint2D:铰链关节。刚体会围绕一个共同点来旋转(自身中心点要拉至对方中心点位置)。
MouseJoint2D:可以用鼠标拖拽物理
RelativeJoint2D:相对关节。控制两个刚体间的相对运动。(双偷盗宝)
SliderJoint2D:滑动关节。两个刚体位置间的角度是固定的,它们只能在一个指定的轴上滑动。
SpringJoint2D:弹簧关节。将关节两端物体像弹簧一样连接在一起。
WheelJoint2D:轮子关节。模拟车轮。
四、资源 1 图片
official::图集资源
Sprite-frame:
Trim Type:默认auto,自动裁剪透明像素。选择None不裁。
Texture:
Filter Mode:图片处理方式
Nearest(None):像素图片处理。
Bilinear:一般图片处理。
2 声音 概念
official::AudioSource 组件参考
offcial::Web Audio和DOM Audio的兼容性说明
属性
@property (AudioClip ) bgmClip : AudioClip = null ;_audioSource : AudioSource = null ;
方法
this ._audioSource = new AudioSource ();this ._audioSource .clip = this .bgmClip ;this ._audioSource .volume = 0.5 ;this ._audioSource .loop = true ;this ._audioSource .play ();
案例
initBgm ( ) { if (this .bgmClip ) { this ._audioSource = new AudioSource (); this ._audioSource .clip = this .bgmClip ; this ._audioSource .loop = true ; this ._audioSource .play (); } }
3 粒子
official::2D 粒子
Particle Designer:粒子效果制作器
在线粒子编辑器
const {ccclass, property} = cc._decorator ;@ccclass ("ParticleController" )export default class ParticleController extends cc.Component { @property (cc.ParticleSystem2D ) particleSystem2D : cc.ParticleSystem2D = null ; start ( ) { this .particleSystem2D .resetSystem (); } playParticle ( ) { this .particleSystem2D .play (); } pauseParticle ( ) { this .particleSystem2D .pause (); } destroyParticle ( ) { this .particleSystem2D .node .destroy (); } }
4 骨骼动画
Spine官网: 专注于游戏的2D动画软件 (esotericsoftware.com)
传统的动画:一般是对一个物体对象进行位移、旋转、缩放、变形,然后把关键帧的信息记录下来,在播放的时候按照关键帧时间对物体对象进行位移、旋转、缩放、变形,并在关键帧与关键帧之间做插值运算。
骨骼动画的特点:需要做动画的物体对象本身不记录位移、旋转、缩放、变形信息,而是通过了第三方的“骨骼”物体记录动画信息,然后物体对象本身只记录受到骨骼物体影响的权重。在播放的时候,通过骨骼物体的关键帧和物体对象记录的权重,让动画重现。
骨骼动画好处:
骨骼动画是影响到顶点级别的动画,而且可以多根骨骼根据权重影响同一个顶点,不论是2D或者3D使用,都可以让动画做得更丰富。
做到了对象和动画分离,我们只需要记录了物体对于骨骼的蒙皮权重,就可以单独的去制作骨骼的动画,在保证蒙皮信息和骨骼信息一致的情况下,还可以多个物体之间共享动画。
相对于2D的逐帧动画大大的节省资源容量。
骨骼动画所需资源有:
.json/.skel
:骨骼数据,骨骼数据以及动画数据
.png
:图集纹理,切割好的图片
.txt/.atlas
:图集数据,管理皮肤贴图
使用时,拖动.json
文件到层级管理器或场景中即可。
5 预制件 方法
const fab = instantiate (Prefab );
案例
const {ccclass, property} = cc._decorator ;@ccclass ('PrefabExample' )export default class PrefabExample extends cc.Component { @property (cc.Prefab ) prefab : cc.Prefab = null ; createPrefab ( ) { let newNode = cc.instantiate (this .prefab ); this .node .addChild (newNode); } accessPrefabComponent ( ) { let prefabNode = cc.instantiate (this .prefab ); let customComponent = prefabNode.getComponent ("CustomComponent" ); if (customComponent) { customComponent.customFunction (); } } clonePrefab ( ) { let clonedPrefab = cc.instantiate (this .prefab ); this .node .parent .addChild (clonedPrefab); } destroyPrefab (prefabNode: cc.Node ) { prefabNode.destroy (); } }
6 Resource
official::资源加载
动态加载资源,资源需要放在resources
文件夹下
方法
resources.load ("test_assets/prefab" , Prefab , (err, prefab ) => { const newNode = instantiate (prefab); this .node .addChild (newNode); }); resources.load ("test_assets/anim" , AnimationClip , (err, clip ) => {s this .node .getComponent (Animation ).addClip (clip, "anim" ); }); resources.load ('images/background/spriteFrame' , SpriteFrame , function (err, spriteFrame ) { self.getComponent (Sprite ).spriteFrame = spriteFrame; spriteFrame.addRef (); }); this .spriteFrame .decRef ();this .spriteFrame = null ;
五、脚本 1 新建脚本 import { _decorator, Component , Node } from 'cc' ;const { ccclass, property } = _decorator;@ccclass ('Test' )export class Test extends Component { start ( ) { } update (deltaTime: number ) { } }
@ccclass ('Test' )export class Test extends Components { _value : number ; label : Label ; get value (){ return this ._value ; } set value (val ){ this ._value = val; } constructor ( ) { super (); this ._value = 10 ; } start ( ) { this .label = this .node .getComponent (Label ); this .label .string = this .value .toString (); } update (deltaTime: number ) { this .value += 1 ; this .label .string = this .value .toString (); } }
2 自定义属性 import {CCInteger , CCFloat } from 'cc' ;@ccclass ('GameRoot' )export class GameRoot extends Component { @property (Node ) button :Node ; @property ([SpriteFrame ]); @property (CCInteger ) HP :Number ; @property (CCFloat ) _MP :Number ; Exp :Number ; start ( ) {}
3 生命周期
official::生命周期回调
onLoad ( ){}onEnable ( ){}start ( ){}update ( ){}lateUpdate ( ){}onDisable ( ){}onDestroy ( ){}
4 组件节点操作 const comp = this .node .getComponent (name);let children = this .node .children ; let comp = this .node .getChildByName ("name" ); let childCount = this .node .childrenCount ;this .comp .addChild (egg);let node = cc.find ("Weapon" [,this .node ]);let node = cc.find ("Player/Weapon" [,this .node ]);let node = cc.find ("Canvas/Player/Weapon" [,this .node ]);this .node .parent = parentNode;this .node .removeFromParent ();this .node .active = true / false ;
5 基础属性操作 属性
this .node .position ;this .node .rotation ;this .node .scale ;
方法
import {Vec3 } from 'cc' ;this .node .getPosition (Vec3 );this .node .setPosition (x,y[,z]);this .node .setPosition (new Vec3 (x,y,z));this .node .angle += 1 ;
案例
update (deltaTime: number ){ this .node .position .x += 1 ; } update (deltaTime: number ){ const p = this .node .position ; p.x += 1 ; this .node .position = p; } curPos = new Vec3 (); update (deltaTime: number ){ this .node .getPosition (this .curPos ); this .curPos .x += 100 * deltaTime; this .node .setPosition (this .curPos ); }
6 时空控制 方法
this .schedule (func, interval);this .unschedule (func);director.loadScene ("scene" ); director.preloadScene ("table" , function ( ) { cc.log ("Next scene preloaded" ); }); game.addPersistRootNode (myNode); game.removePersistRootNode (myNode); director.pause (); director.resume (); director.end (); game.pause (); game.resume (); game.end ();
案例
startCreateEggs (gap: number ){ this .unschedule (this .startOneEgg ); this .schedule (this .startOneEgg , gap); } bindRestartEvent ( ){ this .restartButton .node .on (Node .EventType .TOUCH_END , () => { director.resume (); director.loadScene ("scene" ); }); } bindExitEvent ( ){ this .exitButton .node .on (Node .EventType .TOUCH_END , () => { director.end (); }); }
7 事件
official::事件系统
属性
Node .EventType .TOUCH_START Node .EventType .TOUCH_MOVE Node .EventType .TOUCH_END Node .EventType .TOUCH_CANCEL
方法
this .node .on (event, func, this );input.on (Input .EventType , func, this ); this .node .off (event, func, this );input.off (Input .EventType , func, this ); const uiPos = event.getLocation ();const delta = event.getDelta ();const dx = event.getDeltaX ();const dy = event.getDeltaY ();const uiPos = event.getUILocation ();const delta = event.getUIDelta ();const dx = delta.x ;const dy = delta.y ;
案例
import {EventTouch } from 'cc' ;start ( ){ this .node .on (Node .EventType .TOUCH_START , this .onTouchStart , this ); this .node .on (Node .EventType .TOUCH_MOVE , this .onTouchMove , this ); this .node .on (Node .EventType .TOUCH_END , this .onTouchEnd , this ); this .node .on (Node .EventType .TOUCH_CANCEL , this .onTouchCancel , this ); } onTouchStart (event: EventTouch ) {}onTouchMove (event: EventTouch ) {}onTouchEnd (event: EventTouch ) {}onTouchCancel (event: EventTouch ){}import {Input } from 'cc' ;start ( ){ input.on (Input .EventType .KEY_DOWN , this .onKeyDown , this ); } onKeyDown (event: EventKeyboard ){ console .log (event.keyCode === KeyCode .KEY_W ) } onTouchStart (event: EventTouch ) { this .node .setScale (0.9 ,0.9 ); } onTouchEnd (event: EventTouch ) { this .node .setScale (1.0 ,1.0 ); } onTouchMove (event: EventTouch ) { const x = this .node .position .x ; const y = this .node .position .y ; const delta = event.getUIDelta (); const dx = delta.x ; const dy = delta.y ; this .node .setPosition (x + dx,y + dy); } onTouchStart (event: EventTouch ) { const uiPos = event.getUILocation (); const transform = this .node .getComponent (UITransform ); const nodePos = transform.convertToNodeSpaceAR (new Vec3 (uiPos.x ,uiPos.y ,0 )); const dx = nodePos.x > 0 ? 50 : -50 ; this .node .setPosition (this .node .position .x + dx, this .node .position .y ); }
const { ccclass, property } = cc._decorator ;@ccclass ('CustomEventListener' )export default class CustomEventListener extends cc.Component { static readonly CUSTOM_EVENT : string = "custom_event" ; onLoad ( ) { this .node .on (CustomEventListener .CUSTOM_EVENT , this .onCustomEvent , this ); } start ( ) { setTimeout (() => { this .node .emit (CustomEventListener .CUSTOM_EVENT , "Custom event data" ); }, 3000 ); } onCustomEvent (data: string ) { cc.log ("Custom event received: " + data); } onDestroy ( ) { this .node .off (CustomEventListener .CUSTOM_EVENT , this .onCustomEvent , this ); } }
8 物理碰撞
official::2D 物理系统
单体碰撞:单个碰撞体有自己的碰撞逻辑。
属性
方法
collider.on (Contact2DType , func, this ); PhysicsSystem2D .instance .on (Contact2DType , func, this );
案例
let collider = this .getComponent (Collider2D );if (collider) { collider.on (Contact2DType .BEGIN_CONTACT , this .onBeginContact , this ); }onBeginContact (selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null ) { console .log ('onBeginContact' ); }const comp = this .player .getComponent (Collider2D );comp.on (Contact2DType .BEGIN_CONTACT , (selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null ) => { director.once (Director .EVENT_AFTER_PHYSICS , () => { otherCollider.node .destroy (); }, this ); }, this );
六、动画 1 tween动画
official::缓动系统
方法
tween (obj).tag (number ) .to (time, vec3) .by (time, vec3) .union () .repeatForever () .start () .stop () Tween .stopAll ()Tween .stopAllByTag (0 );Tween .stopAllByTarget (this .node );
案例
const obj = {n : 0 ;}const comp = this .labelNode .getComponent (Label );tween (obj) .to (3 , {n : 1000 }, { onUpdate : (target,ratio ) => { comp.string = `${obj.n.toFixed(2 )} ` ; }, easing : 'elasticOut' }) .start (); const obj = {y : 0 }tween (obj) .to (0.5 , {n : 200 }, { onUpdate : (target, ratio ) => { this .node .setPosition (0 , obj.y ) }, easing : 'quadOut' }) .to (0.5 , {n : 0 }, { onUpdate : (target, ratio ) => { this .node .setPosition (0 , obj.y ) }, easing : 'quadIn' }) .union () .repeatForever () .start (); makeCardTurn (node, isBack, id? ){ return new Promise <void >(resolve => { tween (node) .to (0.3 , {scale : new Vec3 (0 , 1 , 1 )}) .call (() => { const sprite = node.getComponent (Sprite ); sprite.spriteFrame = isBack ? this .cardManager .getCardBeiSF () : this .cardManager .getCardSFById (id); }) .to (0.3 , { scale : new Vec3 (0.2 ,1 ,1 )} ) .call (() => resolve ()) .start (); }); }
2 Animation
组件:Animation
动画片段:Clip(面板——动画编辑器进行制作)
3 Marionette
七、辅助工具 1 Tiled
official::Tiled
使用流程:
新建项目——新建地图集——新建图层。
Q1:[scene] cannot read property ‘vb’ of null
A1:项目——宏配置——BATCHER2D_MEM_INCREMENT改大点
八、常见问题
九、发行 1 微信环境
微信环境对ES6的安全考虑:
不支持使用eval
执行JS代码
不支持使用new Function
创建函数
2 打包
打包