021 - creator笔记
实用技能
- 分享图片要放在打包后的
wechatgame/res/raw-assers
路径下 - 分享功能
shareCallback: function(){
//要在Start方法中开启
wx.showShareMenu({withShareTicket: true}); //开启右上角的分享按钮
//分享按钮事件监听
cc.loader.loadRes("share", function (err, data) {
//share为分享的图片名称这是路径(assets/resources/share)
wx.onShareAppMessage(function (res) {
return {
title: "掌上鱼塘,充实休闲时光!",//分享的标题
path: '/pages/index/index',
// imageUrl: 'assets/resources/shareImg',
imageUrl: cc.url.raw('shareImg.png'), // 分享图片要放在 wechatgame/res/raw-assers 路径下
// query: 'shareMsg='+'分享卡片上所带的信息' // query最大长度(length)为2048
success(res) {
console.log(res)
},
fail(res) {
console.log(res)
}
}
})
});
}
001 初识creator
- internal 引擎自带内部资源
- 项目按钮 – 渠道项目所在文件夹位置
- 编辑器 – 编辑器安装目录
- packages: 项目插件的目录,如果你把编辑器插件,放到这个目录下,这个项目就会有这个插件,只有这个项目才可以使用
- build-templates:你要发布哪些文件到发布包中,你就把你的文件放到这个文件夹下,打包完成后,cocos自动build-templates拷贝过去, 替换
- creator.d.ts:开发者—》vscode工作流程—>智能提示的文件—>生成creator.d.ts
- API的说明文件,说明哪些接口,怎么用,哪些参数,这个比文档详细百倍
- cocos搜索要多搜索几次才能出来。需要匹配版本号。
- 阻止广告回调:https://my.oschina.net/u/2396236/blog/2870412
源码
- engine目录:是我们的纯.js的实现,建议一般看这个目录
- cocos2d-x目录:是C/C++实现的,你不用管,发布回要用到
- 不同的内核,驱动同一份代码, 跨平台, H5负责H5平台
- native平台:windows, mac, iOS, android, 以后我们课程里面说native, 引擎内核C/C++,性能好
- H5平台:浏览器,xxxx小游戏, facebook, …
002 创建项目与组件化开发 - 重点
- resources 文件夹
- 配置设计分辨率:canvas节点:canvas大小
- 所有的组件都继承自component
- mainCamera 摄像机
- properties可被编辑器可视化看到、不可被可视化看到的
- 组件实例流程:所有组件
onLoad
都调完,再掉start
。start
是下一帧才调用。 当组件A需要组件B的数据时,将B初始化onLoad,将A初始化start
- 所有
update(dt)
调用完后才调lateUpdate()
- 组件销毁的时候回调用
onDestory
- 删除某个组件的时候
this.destory()
; enable == true
时会被调用,enable == false
时不会被调用.
003 场景树和节点
-
查找节点:
cc.find(“Canvas/node1/node2”) cc.find("item2",this.node)
可以找儿子、孙子 -
this.node.getChildByName('')
// 只能找儿子,不能找孙子。有同名的,返回第一个找到的。 -
添加孩子:将A添加到B下。
B.addChild(A)
; -
从B下删除孩子c
C.removeFromParent(B)
-
找itme2.sprite组件
item2.getComponent(cc.sprite)
004 Vec2与Node的数据属性和方法
- Vec2:lerp:向量的插值:A.lerp(B,0.5);后边百分比就是A–B向量的中间百分比的点
var va = new Vec2(100,100);
console.log(va) // 100,100
var vb = cc.v2(200,200);
console.log(vb) // 200,200
var vc = va.lerp(vb,0.5);
console.log(vc) // 150,150
vc.addSelf(vb); // vc: 350,350
-
var v1= cc.v2(10,10); v2 = cc.v2(5,3)
-
加法\减法
add
:addSelf:A.addSelf(B)
—>A = A +B
; 向量相加:每个分量相加,向量相减,是每个分量相减- add:
A.add(B)
—>C = A +B
;
- add:
-
乘法
nul
:mulSelf:A.mulSelf(B)
–》(x,y) * 5--->(5x, 5y)
- mul:
A.nul(B)
—>C = A * B
;
- mul:
-
取反
negSelf
:var v= cc.v2(10,10); c = v.negSelf() // c {-10,-10}
-
点乘
dot
:v1.dot(v2)--> (x1*y1,x2*y2)
可以求向量的夹角 -
向量的膜(长度)
mag
,已知长、宽求斜边长度var v = cc.v2(10, 10); v1.mag() // return 14.142135623730951;
-
叉乘:
-
位置:
this.node.getPosition();this.node.setPosition(cc.v2(300,100));this.node.x = 300; this.node.y = 100;
-
锚点:坐标点
-
旋转:
rotation
是顺时针;angle
是逆时针,编辑器是正值是逆时针 -
扭曲:
skew
平行四边形 -
缩放:
scaleX、scaleY
scale为-1时,精灵镜像 -
分组:
group``groupIndex
-
绘制顺序:
zIndex默认为0
:先绘制上面,再绘制下面的。可修改zIndex
·item.zIndex = 2;
·zIndex越大,越先绘制,可改变兄弟的绘制顺序
005 sprite精灵组件
- atlas 图集
- spriteFrame 散图
- Type: 精灵的模式:5种模式: Simple, FIll, Sliced, Tiled, Mesh模式
- sizeMode 图片大小模式:Custom(用户自定义模式)Raw(原始大小模式)
- RAW: 大小是那么大,但内容是裁调透明像素后的内容,拉伸到全尺寸。原始图片,不变形
- trim: 勾选后:裁掉周围透明像素后剩下的。
- Blend:图片opacity混合的规则:0.6,src:图片的opacity,dst:屏幕像素的opacity.
opacity*src + (1-opacity)*dst
和屏幕混合的比例呈现 - 材质:配置文件:
Materials
- scale为-1时,精灵镜像
- 修改sprite
var sp = this.node.getComponent(cc.Sprite); sp.spriteFrame = this.img;
// cc.SpriteFrame 资源对象- sprite的模式:
- simple模式 拉伸到目标大小
- tiled模式 平铺到地面。像一块块地板砖,铺满地面
- sliced 九宫格模式
- fill模式 百分比模式:
fillType
水平、垂直、扇形(逆时针)fillStart
(0–1) 、fillRange
(可以为负值) 开始点,长度范围
006 cc.Component
Schedule
:定时器- 不断触发一个定时器。
- callback回调函数
- interval 出发时间,隔多长时间触发一次,如果为0,则每帧都会触发
- repeat: 调用次数:repeat+ 1次
- delay:多长时间后开始定时器
this.schedule( function(){ console.log('定时器') },1,cc.macro.REPEAT_FOREVER,5)
- 没有返回值
- 不断触发一个定时器。
scheduleOnce
:一次性定时器- callback回调函数
- delay 多长时间后触发,不传下一帧触发定时器
unschedule
取消某个定时器。`this.unschedule(callback回调函数名称)
unscheduleAllCallbacks
取消掉所有定时器- 如果节点被隐藏,定时器不会触发
this.node.active = false``this.node.enable = false
that.unschedule(that.time15Callback); // 停止15秒倒计时
that.schedule(this.time15Callback, 0.05);
// 倒计时 15秒
this.time15Callback = function () {
var that = this;
if (this.beginTime > 0) {
this.beginTime = this.beginTime - 0.1;
this.timeTextNode.getComponent(cc.Label).string = this.beginTime.toFixed(1) + 's';
} else {
this.timeTextNode.getComponent(cc.Label).string = '0s';
that.fishLostTip('鱼跑了!');
that.fishEnd(); // 钓鱼结束
}
};
var liqiSubCallback = function () {
var that = this;
if (that.yongLiJinDuBar.progress <= 1) {
that.yongLiJinDuBar.progress += 0.02;
} else {
that.redBg.opacity = 120;
that.unschedule(liqiSubCallback);
}
}
that.schedule(liqiSubCallback, 0.1);
007事件响应
this.node.on(“事件类型”,回调函数,target(在回调函数里的this是谁))
- 事件冒泡、事件上报(除非被截断)
- 触摸事件:
- 触摸按下 start
- 触摸移动 move
- 触摸抬起(节点内部抬起) end
- 触摸抬起(节点外部抬起) cancel
- 注销事件:
off
cc.Class({
start(){
this.node.on(cc.Node.EventType.TOUCH_START, this.on_touch_start,this);
this.node.on(cc.Node.EventType.TOUCH_END, this.on_touch_end,this); // 节点内部弹起
this.node.on(cc.Node.EventType.TOUCH_CANCEL,this.on_touch_cancel,this); // 节点外部弹起
this.node.on(cc.Node.EventType.TOUCH_MOVE, this.on_touch_move,this); // 移出去还是有的
},
on_touch_start(e){
会传参数
console.log(e)
e.stopPropagation();// 停止事件冒泡;使用!!!
e.stopPropagationImmediate();// 停止事件往上上报啊;停止事件冒泡
点击事件
}
})
- 键盘事件:cc.SystemEventcc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN[事件类型],[回调函数]this.on_key_down,this[target])
- 监听
cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN,this.on_key_down,this)
- 监听
cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP,this.on_key_up,this)
- 注销
cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN,this.on_key_down,this)
- 监听
switch(e.keyCode){
case 32:
console.log("space");
break;
}
- 重力感应类似
- 自定义事件
- 原则:节点自己监听自己的事件
- 原则: 节点派送自己的事件
- 不可以派送给其他节点,但是可以派送给同一个节点不同组件实例
this.node.on("my_event",this.on_custom_event,this)
- 触发事件:
this.node.emit("my_event","blake");
触发my_event,传入blake,在函数中会打印出来
on_custom_event(e) { console.log(e); } 学会自定义
008 2D坐标空间转换
- 屏幕坐标系[世界坐标系原点(0,0)]
- 触摸事件的相对触摸坐标是相对屏幕的
- 获取屏幕坐标:
e.getLocation()
- 获取距离上次坐标的变化。
e.getDelta()
- 相对坐标–>屏幕坐标:
convertToWorldSpaceAR
哪个节点转的,就是以哪个节点为原点 - 世界坐标–>相对坐标:
convertToNodeSpaceAR
this.node.on(cc.Node.EventType.TOUCH_MOVE,function(e){
var offset = e.getDelta();
console.log(offset) // offset 就是距离上一次触摸位置,到当前位置的变化(dx,dy);
},this)
this.node.convertToWorldSpace(cc.v2(100,100)); // 把相对node的坐标(100,100), 转成世界坐标
this.node.convertToNodeSpace(cc.v2(100,100)); // 把世界坐标转成Node坐标
/**
* 特例
**/
// 1.把当前节点坐标,转成世界坐标
// step1
var screen_pos = this.node.convertToWorldSpaceAR(cc.v2(0,0));
// step2:相对坐标系是父坐标
screen_pos = this.node.parent.convertToWorldSpaveAR(this.node.getPosition());
// 2.将当前的节点,是知道世界的某个位置
var pos = this.node.parent.converToNodeSpaceAR(cc.v2(100,100));
this.node.setPosition(pos)
// 3.将A坐标转到B坐标空间 先转到世界坐标系,再转到B坐标
// 点哪儿,节点去哪儿
this.node.on(cc.Node.EventType.TOUCH_START,function(e){
var cpos = e.getLocation();
var npos = this.red.parent.convertToNodeSpaceAR(cpos);
this.red.setPosition(npos);
},this)
009Action的使用详解
- actioin
- 平移:
- moveTo(时间,变化) // 移动到指定位置
- moveBy(时间,变化) // 相对移动多少距离
- 旋转
- cc.rotateBy(时间,变化) // 在多长时间内旋转 正–》顺时针
- cc.rotateTo(时间,变化) // 在多长时间内旋转到 180度 逆时针为正
- 渐变
- cc.fadeTo(0.5,128); // 渐变显示[0,255]
- cc.fadeIn(0.5); // 渐入
- cc.fadeOut(0.5);// 淡出
- 缩放
- scaleTo(0.5,1.2) // 放大到1.2
- scaleBy()
- 函数 callFunc 并不是立即执行,下一帧才执行
var func = cc.callFunc(function () { console.log('callback') }); this.node.runAction(func);
- 顺序执行
cc.sequence([m1,r1,fa])
- 循环容器:
- cc.repeatForever无限循环
- cc.repeat(r,3)循环次数
- 停止
this.stopAllActions();
停止所有this.stopAction(m1)
停止指定action
- 缓动对象 ease
- var m2 = m1.easing(cc.easeBackOut());// 超出后回来
010 Label组件
- 字体:楷体、宋体、Arial
- 修改字体
var label = this.node.getComponent(cc.Label);
label.string = "将"
- 修改
overflow
为shrink
,显示框拉长,可以自动往后排列显示 - 使用自定义字库【TTF】
- 将字库文件ttf文件拖到
font
框中,将use System font
选中框去掉 - 使用专业工具裁剪个别需要的字体,加入项目中,节省空间。选子模。百度搜索字体裁剪工具
- 将字库文件ttf文件拖到
- 美术字【位图字库】
- .fnt 位图,
- .fnt 文件字符编码–》(c,y,w,h)–>Rect,位图里面割出我们一个小的文字
- 字符个数有限,如果有了他显示不了的字符,就不显示
- 专门工具bmpfont,Mac
- 找字库
- 使用这个打开字库,写一个要的字符的集合:0123456789+
- 导出这些字符–》图片里面生成一个.fnt文件,
- 用的字一定要在字库中有
011 Button、预制体prefab、AudioSource
- button
- interaction 是否可点.选中可点
- 过渡效果:
- 颜色
- 缩放、大小
- 图片
- 按钮点击事件。
- 传入的值: 为String型
var num = parseInt(fromData)
// 转为数值型e.target.getComponent(cc.Button).interaction = 'false'
// 禁止点击
- prefab.预制体
- 使用预制体实例实例化
- 预制体右上角【选择】节点是哪个预制体;【回退】回退到和预制体一样;【保存】
- 代码:
var b = cc.instantiate(this.btPrefab);this.node.addChild(b)
cc.Class({
properties: {
btPrefab: cc.Prefab,
},
start(){
var b = cc.instantiate(this.btPrefab);
this.node.addChild(b)
}
})
- AudioSource
- AudioClip:声音资源
- Volumn:音量大小
- Mute:是否静音
- loop: 是否循环播放
- Preload:是否预加载
- 声音格式: ogg,mp3,
var as = this.node.getComponent(cc.AudioSource);
as.play(); // 播放
as.pause(); // 暂停
as.resume();
as.rewind();
012 Mask、Layout、ScrollView
- Mask
- 大小
- 类型
- 根据图片透明度 IMAGE_STENCIL
- 裁剪任意形状
- 裁掉多余部分内容–滚动列表,把多余部分裁剪掉
- Layout 排列方式
- 某个物理,按规律来排列、竖直、水平
+ScrollView - 滚动条一般不需要,可以隐藏
- view滚动列表可视区域大小 – 内容、超出这个view不能让显示出来
- content:内容
- 代码加内容: content + Layout
- scrollView 事件 e 传入的code对应的事件
- 某个物理,按规律来排列、竖直、水平
cc.Class({
extends: cc.Component,
properties: {
cscrollview: {
type: cc.ScrollView,
default: null
},
item_prefab: cc.Prefab
},
start() {
var content = this.cscrollview.content;
for (var i = 0; i < 10; i++) {
var b = cc.instantiate(this.item_prefab);
b.getChildByName('name').getComponent(cc.Label).string = '伽罗 太华' + i;
content.addChild(b)
}
},
});
013屏幕适配与widget组件
- 1.屏幕适配
- 游戏层:
- 背景:把主要内容显示好,全部覆盖到所有的屏幕,不能留黑边
- 地图:不用适配。
- 大地图–》滚动
- 特殊:内容在一个屏幕显示完成;
- 地图设计:所有分辨率的公共区域内
- 角色:不适配
- UI层:所有UI放入一个屏幕内
- 游戏层:
- 2.游戏引擎如何适配
- 【1】设计分辨率,美术资源设计分辨率,自己调整
- 【2】2个维度:width,height
- 固定一个维度
- 定宽:960640–》1920(1280),等比缩放–>缩放系数scale=2, 100100–》5050
- 定高: 960*640–》(1620)*1080;scale = 1.6;高度一致,考虑如何把1920宽的内容放在1620宽内就行
- 适配:
- Canvas节点,Canvas组件实例,设计分辨率
- 固定高度:所有分辨率高度–》设计分辨率的高度–》逻辑分辨率–》比例–》宽度上调整
- 固定宽度:所有分辨率宽度–》按设计分辨率宽度–》逻辑分辨率–》比例–》高度上调整
- Canvas节点,Canvas组件实例,设计分辨率
- 背景图适配:
- 做一个比较大的背景图,能适配最大的手机
- 拉伸、缩放
- 内容大小适配:100平房子–内容:90平房子
- 间隙变小、不影响美观
- 放不下内容、内容重叠
- 根据分辨率,做适配,自定义脚本
if(cc.winSize.height < 960){ // 960 ipad this.node.scale = 0.8; }
- 留海屏、水滴屏
- 不要做设计
- 非要这样
- 看不见
- 根据对应手机型号,调整位置,在留海以下
014 帧动画–手写代码
015 物理引擎
- 开启物理引擎
EnablePhy.js
- 必须在
onLoad
中开启let physicsManager = cc.director.getPhysicsManager(); physicsManager.enabled = true
- 必须在
- 开启物理引擎重力值
- 添加RigidBody:刚体
- type 刚体类型
- Static 零质量,零速度,可以手动移动。
- Kinematic 零质量,可以被设置速度。
- Dynamic 有质量,可以设置速度,力等
- Animated:Kinematic 类型的扩展,可以被动画控制动画效果。
- 物理形状:Colider,
PhysicsBoxColider
,碰撞器:物理组件 – Colider – Box - 线性速度
- 摩擦力
- 反弹
- type 刚体类型
- 分组配对
- 碰撞回调-- 需要先开启碰撞监听
016 碰撞系统
- 碰撞盒子
var collisionManager = cc.director.getCollisionManager();
开启碰撞盒子
碰撞系统:
(1) 是否开启了碰撞系统;
(2) 是否给节点上编辑了正确得碰撞系统得组件;
(3) 是否配置了正确得碰撞关系;
(4)响应和碰撞器是否再一个节点上;
(5) 碰撞得回掉是否写对了;
如果是物理系统:
(1)是否开启了物理系统;
(2) 节点上是否加了物理碰撞器组件,不要加碰器组件;
(3) 碰撞器上是否勾选上了允许碰撞监听;
(4) 看下配置关系是否允许碰撞;
(5) 看下节点上得碰撞器,是否和脚本是同一个节点;
(6)检查名字是否对。
- 碰撞盒子PhysicsBoxColider:物理组件 – Colider – Box
- 关节组件RopeJoint:物理组件 – Joint – Rope
017 Graphic
cc.Class({
onLoad(){
var ctx = this.node.getComponent(cc.Graphics);
ctx.clear(); // 清除所有
// 绘制蓝色矩形框
ctx.lineWidth = 2;
ctx.strokeColor = new cc.Color().fromHEX('#0000ff');
ctx.rect(20, 20, 250, 200);
ctx.stroke();
// 绘制绿色矩形填充
ctx.rect(20, 20, 150, 100);
ctx.fillColor = cc.Color.GREEN;
ctx.fill();
// 从(100,100)到(300,300)画一条宽为3像素的绿色直线
ctx.lineWidth = 3;
// ctx.strokeColor = cc.Color.GREEN;
ctx.strokeColor = new cc.Color().fromHEX('#0000ff');
ctx.moveTo(100, 100);
ctx.lineTo(300, 300);
ctx.lineTo(0, 300);
ctx.stroke();
// 贝塞尔曲线
ctx.lineWidth = 3;
ctx.strokeColor = new cc.Color().fromHEX('#0000ff');
ctx.moveTo(-20, 150);
ctx.quadraticCurveTo(14,81,76,14);
ctx.stroke(); // 实际地绘制出通过 moveTo() 和 lineTo() 等路径方法定义的路径。默认颜色是黑色。
graphic.close(); // 闭合起来
graphic.fill(); // 用于填充当前的图像(路径),默认颜色是白色。
graphic.clear(); // 清除所有
}
})
018 摄像机
- culling Mask: 这个摄像机拍摄的物体的类型;
- depth 根据摄像机的depth来决定哪个摄像机先绘制,越小越先绘制,越大越后绘制
- clearFlags: 摄像机清屏设置;
- 两个摄像机,都渲染的话,场景中所有的物体都会绘制两遍。选择性渲染。
- 坐标:
var wPos = this.camera.getScreenToWorldPoint[getCameraToWorldPoint](cLocation);
// 摄像机坐标转世界坐标this.camera.getWorldToCameraPoint()
// 世界坐标转摄像机坐标
this.node.on(cc.Node.EventType.TOUCH_START, function (e) {
var cLocation = e.getLocation();// 除非camera(0,0)否则你要转换一下
var wPos = this.camera.getScreenToWorldPoint(cLocation);// 将摄像机坐标转成世界坐标系
var pos = this.map.convertToNodeSpaceAR(wPos);// 将世界坐标系转为地图坐标系
this.footBall.setPosition(pos);
this.cameraNode.setPosition(pos);
}, this);
019 代码加载资源
- 加载本地资源必须放在
resources
文件夹中
cc.loader.load("http://lz.zyd1994.top/app/images/93cc173b4f394638be05966b11a25ac2.jpg", function(err, ret){
if(err){
cc.error(err.messager || err);
return;
}
this.bgSprite.spriteFrame.setTexture(ret);
this.bgSprite.node.setContentSize(ret.width, ret.height);
}.bind(this));
cc.loader.load({url:"http://webfs.yun.kugou.com/202005281516/5de3b1cfdc75d1b24e85550eb81c5c6e/G189/M00/1C/0B/_Q0DAF4gAHSAVO6IAEXF4exBMeE000.mp3",type:"mp3"}, function(err, ret){
if(err){
cc.error(err.messager || err);
return;
}
this.bgAudio.clip = ret;
this.bgAudio.play();
}.bind(this));
// 加载本地资源,assets/resources
cc.loader.loadRes("images/disk", cc.SpriteFrame, function(err, ret){
if(err){
cc.error(err.messager || err);
return;
}
this.bgSprite.spriteFrame = ret;
//this.bgSprite.node.setContentSize(ret.width, ret.height);
}.bind(this));
cc.loader.loadRes("bg", function(err, ret){
if(err){
cc.error(err.messager || err);
return;
}
this.bgAudio.clip = ret;
this.bgAudio.play();
}.bind(this));
020 属性绑定
- 1.属性声明
- default:
- type:
- tooltip:提示信息
- displayName:显示名称
- 2.属性下拉列表:
- 1.创建枚举
var sextype = cc.Enum({ 未知:0, 男:1,女:2})
- 2.type:cc.Enum(自定义枚举类型)
type: cc.Enum(sextype),default: sextype.男,
- 3.动态设置属性的可见性(级联下拉列表)
- 1.创建枚举
- 3.滑动条
- slide:true显示滑块
- min\max\step
- 4.回调函数
- type:cc.Component.EventHandler
- 5.自定义属性类
properties: {
age:{
type:cc.Integer,
default:18,
tooltip:'年龄大小',
visible(){
return (this.sex == sextype.未知)
}
}
}
021 打包
- 打包后可以修改项目名称
- 打包h5小游戏–Web Desktop
- 修改预览分辨率
- 放在本地nodeJs环境中
- localhost:6080/DrawLine_WD
022 creator 安卓打包发布
// 百度小游戏 加广告
onLoad(){
// 广告
this.rewardedVideoAd = swan.createRewardedVideoAd({ adUnitId: '000000', appSid: '000000' })
},
// 广告
showAd() {
var that = this;
that.rewardedVideoAd.show()
.then()
.catch(err => console.log(err));
that.rewardedVideoAd.onClose((res) => {
if (res.isEnded) { //用户是否在视频播放完成后关闭广告
console.log('用户在视频播放完成后关闭广告');
// 复活
cc.find('Canvas').getChildByName('tankuang').active = false; // 关掉弹窗
window.D.oldScore = that.score;
cc.director.loadScene('main');
} else {
console.log('激励视频关闭');
that.showTip();
}
})
},
常识
- 屏幕宽高:
cc.winSize.width,cc.winSize.height
- 图片引擎默认带Trim,裁掉周边透明像素。要使用原始大小:
SizeMode:RAW
去掉Trim
- 组件调用: var globalJs = require(“frame_anim”);
- 分组: 球、地面碰撞时:分组ball–ground配对很重要、需要进行碰撞的要勾选对
- 项目:每个元素单独写他的方法,单独用脚本。不要混合在一起。角色是角色、场景是场景、camera用一个
- 角色运动算法+摄像机跟随==>009test–测试项目–camera2场景