1. 开启物理引擎debug模式的通用文件,绑定在canvas节点上窝:
// 这个文件用来开启物理引擎,应该是通用的吧,个人觉得
cc.Class({
extends: cc.Component,
properties: {
is_debug: false, // 是否显示调试信息;
},
onLoad () {
// 游戏引擎的总控制
// cc.Director, cc.director 区别呢?
// 大写的cc.Director是一个类, 小写cc.direcotr全局的实例
cc.director.getPhysicsManager().enabled = true; // 开启了物理引擎
// 独立的形状,打开一个调试区域,游戏图像的,逻辑区域;
// 开始调试模式:
if (this.is_debug) { // 开启调试信息
var Bits = cc.PhysicsManager.DrawBits; // 这个是我们要显示的类型
cc.director.getPhysicsManager().debugDrawFlags = Bits.e_jointBit | Bits.e_shapeBit;
}
else { // 关闭调试信息
cc.director.getPhysicsManager().debugDrawFlags = 0;
}
},
start () {
},
// update (dt) {},
});
2. 碰撞刚体类型的不同会有不同的效果 (不是现实中的碰撞效果最好不要用这些刚体碰撞,自己手写检测)
- 和 Dynamtic 类型刚体绑定的物理组件会受重力影响,可以设置速度
- 和 Static 类型刚体绑定的物理组件,不会受重力影响,不可以设置速度,可以通过设置位置让其移动
- 和 Kinematic 类型刚体绑定的物理组件,不受重力影响,可以设置速度
- 绑定了 Dynamic(运动)类型的物理组件不能穿透绑定了 Static(静态)类型的物理组件
- 绑定了 Dynamic 类型的物理组件不能穿透绑定了Kinematic类型的物理组件
- Static 和 Kinematic 不会触发碰撞事件,Static 和 Static,Kinematic 和 Kinematic 不会触发碰撞事件;
3. 为属性设置引用时,只有在属性声明时规定type
为引用类型时(比如我们这里写的cc.Prefab
类型),才能够将资源或节点拖拽到该属性上:
properties: {
kuaiPrefab: {
default: null,
type: cc.Prefab
},
// 方块的等级
grade: 0
},
4. 摄像机的使用: 创建空节点--添加组件--添加其他组件--Camera--target改为1--把对准的目标节点拉过来(绘制以这个节点为中心点,屏幕窗口大小为区域的画面)(像 像素鸟这种游戏就可以使用camera,把鸟的坐标同步到camera上面,将camera节点的x坐标设为鸟的x坐标,要在同一坐标系中)
5. 把普通坐标转换为世界坐标
var w_pos = this.target.convertToWorldSpaceAR(cc.p(0,0));
6. 把世界坐标转成相对与某个节点中的坐标
var pos = this.node.parent.convertToNodeSpaceAR(w_pos);
7. 发布成微信竖屏游戏可以设置canvas大小为640*960,还有 fit height 打勾
8. JavaScript Array some() 方法
var ages = [3, 10, 18, 20];
function checkAdult(age) {
return age >= 18;
}
function myFunction() {
document.getElementById("demo").innerHTML = ages.some(checkAdult);
}
some() 方法用于检测数组中的元素是否满足指定条件(函数提供);
some() 方法会依次执行数组的每个元素;
function(currentValue, index,arr) ---- currentValue(当前元素的值)是必须的,其他两个可选
9. 触摸事件: 先注册监听对应的触摸事件
- cc.Node.EventType.TOUCH_START: 触摸开始;
- cc.Node.EventType.TOUCH_MOVE: 触摸移动;
-
cc.Node.EventType.TOUCH_END: 触摸结束, (物体内部结束)
-
cc.Node.EventType.TOUCH_CANCEL: 触摸结束, (物体外部结束)
-
回掉函数的格式: cc.Touch对象触摸事件对象 {触摸信息,事件信息}
this.node.on(cc.Node.EventType.TOUCH_START, function(t) {
console.log("cc.Node.EventType.TOUCH_START called");
// this 函数里面的this,
// 停止事件传递
t.stopPropagationImmediate();
}, this);
关闭监听用off
// 移除
// this.node.off(cc.Node.EventType.TOUCH_MOVE, this.on_touch_move, this);
// 移除target上所有的注册事件(注意这里参数是this而不是this.node)
// this.node.targetOff(this);
触摸回调函数里面的参数t
t: --> cc.Touch
触摸的位置: 屏幕坐标,左小角(0, 0); getLocation();
getLocation()执行结果是点对象,有x和y
// 距离上一次触摸变化了多少;
var delta = t.getDelta(); // x, y各变化了多少cc.Vec2(x, y)
this.node.x += delta.x;
this.node.y += delta.y;
10. 开发要做的两件事情: 学习系统组件,自己开发组件
11. 获取节点上的其他组件:getComponent (例如label组件)
var vip = this.getComponent(cc.Label);
12. 构建发布微信小游戏的话,竖屏选择 Portrait,横屏选择 landspace,然后选择构建
13. 包体裁剪(减少打包后cocos-2d的文件代码大小),选择 项目--项目设置--模块设置--把没有用的东西取消掉--再构建发布一次
14. 资源远程部署: 微信小游戏现在不允许代码太大,目前是4M,微信小游戏是支持把资源部署到第三方的;
五行代码搭建服务器(npm i express),新建文件 webserver.js
var express = require("express");
var app = express();
app.listen(80);
var path = require("path");
app.use("/",express.static(path.join(process.cwd(),"www_root"))); //www_root 是与当前文件同级的文件夹名,这个文件夹存放index.html等文件
在www_root 新建远程资源文件夹remote ,把微信小游戏源码中的 res 文件夹资源放到我们 remote 文件夹下,打开微信小游戏源码中的 libs/xmldom/wx-downloader,
...
var WXDownloader = window.WXDownloader = function() {
...
this.REMOTE_SERVER_ROOT = ''; // 大概40行的地方,在src/main.js文件中配置这个参数
}
在 src/main.js 这样配置就行(改好后点击微信开发者工具上面的 详情 -- 不校验合法域名打勾)
...
if(true){
require(window._CCSetting.debug ? 'cocos2d-js.js':'cocos2d-js-min.js');
var prevPipe = cc.loader.md5Pipe || cc.loader.assetLoader;
wxDownloader.REMOTE_SERVER_ROOT = "http:127.0.0.1/remote/"; //大概204行的地方,在这里配置第三方服务器的地址
}
15. 名言:没有什么错误是 console.log 调试不出来的,如果有,那就多 console.log 几次;
16. Button组件使用:属性检查器--transition可以选择过渡的效果,按钮点击事件想传参数的话 Button组件--CustomEventData 里面填写(参数都是一个字符串对象)在关卡功能中会用到这种传参数的点击事件
...
on_button_click: function(e,level){ //第二个参数才是编辑器上面填写的,是个字符串类型
level = parseInt(level);
console.log(level);
}
代码中使用button组件
//属性中声明,然后直接在编辑器里面拉取绑定
button: {
default: null,
type: cc.Button
}
获取节点中的button组件与添加button组件
onload: function () {
this.start_button = this.node.getChildByName("ks_up").getComponent(cc.Button); //获得名为xx的子节点上面的button组件
this.red_button = this.node.getChildByName("red_btn").addComponent(cc.Button); //为名为xx的子节点上添加一个button组件
}
为button节点添加一个响应函数(适用于动态添加按钮之后的点击事件)
onload:function(){
var click_event = new cc.Component.EventHandler();
click_event.target = this.node;
click_event.component = "game_scene";
click_event.handler = "on_click";
click_event.customEventData = "red_btn";
this.red_btn.clickEvents = [clcik_event]; //这个数组添加多个就是多个点击事件
}
on_click: function(e,custom){
console.log(custom);
}
代码触发按钮的响应事件,而不用自己去触摸
onload: function(){
this.scheduleOnce(function(){
var click_event = this.red_btn.clickEvents;
for(var i = 0 ; i < click_events.length; i++){
var comp_env_handle = click_events[i];
// 在代码里面触发按钮的响应函数
comp_env_handle.emit(["","on_click"]);
}
}.bind(this),3)
}
on_click: function(e,custom){
console.log(custom);
}
17. 横屏游戏类似捕鱼达人这种可以把canvas设置为1334*750
18. 例如给鱼添加游动轨迹,点击节点--添加组件--添加其他组件--Animation--点击动画编辑器--新建--点击左上角的编辑按钮--在里面选择鱼的节点添加位置position-- 点击+号 -- 拉动动画编辑器中的帧 -- 再调整节点的位置 -- 点击虚线 --调整
19. 获取其他脚本组件中的函数
var gen_map_path = require("gen_map_path"); //这里引入其他文件名
// 组件类,
cc.Class({
extends: cc.Component,
properties: {
map: { // 这里需要声明一下(然后去编辑器里面拖动所对应的节点到map里面)
type: gen_map_path,
default: null,
},
speed: 100,
},
// start函数 组件开始运行之前,调用, 初始入口的好地方;
start () {
this.run_road();
},
run_road() {
var road_set = this.map.get_road_set();
var index = Math.random() * road_set.length;
index = Math.floor(index);
this.road_data = road_set[index]; // 假设从第0条;
if (this.road_data.length < 2) {
return;
}
this.is_walking = false;
this.node.setPosition(this.road_data[0]);
this.next_step = 1; // 下一个要走的路径点;
this.walk_to_next();
},
walk_to_next() {
if (this.next_step >= this.road_data.length) {
this.is_walking = false;
this.run_road();
return;
}
this.is_walking = true;
var src = this.node.getPosition();
var dst = this.road_data[this.next_step];
var dir = cc.pSub(dst, src); //返回两个向量的差(2.0就没有这个方法了)
var len = cc.pLength(dir); //返回指定向量的长度(2.0就没有这个方法了)
this.total_time = len / this.speed;
this.now_time = 0;
this.vx = this.speed * dir.x / len;
this.vy = this.speed * dir.y / len;
// 旋转鱼头
var r = Math.atan2(dir.y, dir.x); // 弧度
var degree = r * 180 / Math.PI;
degree = 360 - degree + 90; // 逆时针--> 顺时针
// this.node.rotation = degree;
this.node.runAction(cc.rotateTo(0.5, degree));
// end
},
// update 组件再游戏画面每次刷新的时候调用, update
// dt: 是距离上一次过去刷新的时间;
update (dt) {
if(this.is_walking === false) {
return;
}
this.now_time += dt;
if (this.now_time > this.total_time) {
dt -= (this.now_time - this.total_time);
}
var sx = this.vx * dt;
var sy = this.vy * dt;
this.node.x += sx;
this.node.y += sy;
if (this.now_time >= this.total_time) {
this.next_step ++;
this.walk_to_next(); // 继续走下一个点;
}
},
});
20. 播放帧动画的脚本组件,感觉是可以通用的,来自捕鱼达人的鱼的帧动画组件(引擎自带的帧动画不用有自己的道理)
cc.Class({
extends: cc.Component,
properties: {
sprite_frames : {
default: [],
type: cc.SpriteFrame,
},
duration: 0.1, // 帧的时间间隔
loop: false, // 是否循环播放
play_onload: false, // 是否在组件加载的时候播放;
},
onLoad: function () {
// 判断一下在组件所挂在的节点上面有没有cc.Sprite组件;
var s_com = this.node.getComponent(cc.Sprite);
if (!s_com) { // 没有cc.Sprite组件,要显示图片一定要有cc.Sprite组件,所以我们添加一个cc.Sprite组件;
s_com = this.node.addComponent(cc.Sprite);
}
this.sprite = s_com; // 精灵组件
// end
this.is_playing = false; // 是否正在播放;
this.play_time = 0;
this.is_loop = false;
this.end_func = null;
// 显示第0个frame;
if (this.sprite_frames.length > 0) {
this.sprite.spriteFrame = this.sprite_frames[0];
}
if (this.play_onload) {
if (!this.loop) {
this.play_once(null);
}
else {
this.play_loop();
}
}
},
// 实现播放一次,
play_once: function(end_func) {
this.play_time = 0;
this.is_playing = true;
this.is_loop = false;
this.end_func = end_func;
},
// end
// 实现循环播放
play_loop: function() {
this.play_time = 0;
this.is_playing = true;
this.is_loop = true;
},
// end
stop_anim: function() {
this.play_time = 0;
this.is_playing = false;
this.is_loop = false;
},
start: function() {
},
// called every frame, uncomment this function to activate update callback
// 每一次刷新的时候需要调用的函数,dt距离上一次刷新过去的时间;
update: function (dt) {
if (this.is_playing === false) { // 没有启动播放,不做处理
return;
}
this.play_time += dt; // 累积我们播放的时间;
// 计算时间,应当播放第几帧,而不是随便的下一帧,
// 否则的话,同样的动画1, 60帧,你在30FPS的机器上你会播放2秒,
// 你在60FPS的机器上你会播放1秒,动画就不同步;
var index = Math.floor(this.play_time / this.duration); // 向下取整数
// index
if (this.is_loop === false) { // 播放一次
if (index >= this.sprite_frames.length) { // 非循环播放结束
// 精灵显示的是最后一帧;
this.sprite.spriteFrame = this.sprite_frames[this.sprite_frames.length - 1];
// end
this.is_playing = false;
this.play_time = 0;
if (this.end_func) { // 调用回掉函数
this.end_func();
}
return;
}
else {
this.sprite.spriteFrame = this.sprite_frames[index];
}
}
else { // 循环播放;
while (index >= this.sprite_frames.length) {
index -= this.sprite_frames.length;
this.play_time -= (this.duration * this.sprite_frames.length);
}
// 在合法的范围之内
this.sprite.spriteFrame = this.sprite_frames[index];
// end
}
},
});
21. 这里面发现一个js 居然不支持这种写法
142 < pos_x < (142+71)
而只能用这种(我到今天才发现的感觉)
pos_x > 142 && pos_x < (142+71)
22. 以后做移动端游戏统一使用touch事件最好,又没有兼容问题(最好不要和点击按钮的事件混着用)
23. 客户端发起http请求: 在需要发请求的脚本文件里面先引
var http = require("http");
var http = {
// calback(err, data)
get: function(url, path, params, callback) {
var xhr = cc.loader.getXMLHttpRequest();
xhr.timeout = 5000;
var requestURL = url + path;
if (params) {
requestURL = requestURL + "?" + params;
}
xhr.open("GET",requestURL, true);
if (cc.sys.isNative){
xhr.setRequestHeader("Accept-Encoding","gzip,deflate","text/html;charset=UTF-8");
}
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300)){
console.log("http res("+ xhr.responseText.length + "):" + xhr.responseText);
try {
var ret = xhr.responseText;
if(callback !== null){
callback(null, ret);
}
return;
} catch (e) {
callback(e, null);
}
}
else {
callback(xhr.readyState + ":" + xhr.status, null);
}
};
xhr.send();
return xhr;
},
post: function(url, path, params, body, callback) {
var xhr = cc.loader.getXMLHttpRequest();
xhr.timeout = 5000;
var requestURL = url + path;
if (params) {
requestURL = requestURL + "?" + params;
}
xhr.open("POST",requestURL, true);
if (cc.sys.isNative){
xhr.setRequestHeader("Accept-Encoding","gzip,deflate","text/html;charset=UTF-8");
}
if (body) {
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.setRequestHeader("Content-Length", body.length);
}
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300)){
try {
var ret = xhr.responseText;
if(callback !== null){
callback(null, ret);
}
return;
} catch (e) {
callback(e, null);
}
}
else {
callback(xhr.readyState + ":" + xhr.status, null);
}
};
if (body) {
xhr.send(body);
}
return xhr;
},
download: function(url, path, params, callback) {
var xhr = cc.loader.getXMLHttpRequest();
xhr.timeout = 5000;
var requestURL = url + path;
if (params) {
requestURL = requestURL + "?" + params;
}
xhr.responseType = "arraybuffer";
xhr.open("GET",requestURL, true);
if (cc.sys.isNative){
xhr.setRequestHeader("Accept-Encoding","gzip,deflate","text/html;charset=UTF-8");
}
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300)){
var buffer = xhr.response;
var dataview = new DataView(buffer);
var ints = new Uint8Array(buffer.byteLength);
for (var i = 0; i < ints.length; i++) {
ints[i] = dataview.getUint8(i);
}
callback(null, ints);
}
else {
callback(xhr.readyState + ":" + xhr.status, null);
}
};
xhr.send();
return xhr;
},
};
module.exports = http;
发请求这样写
http.get("http:127.0.0.1:80","/lucky","name=black&uphone=12312",function(err,ret){
if(err){
return;
}
console.log(ret);
});
24. 使用定时器等函数要注意 this的指向问题,不然哪里错了都不知道,注意,注意,注意
25. 顺序执行两个动作
var m1 = cc.moveTo(0.5,cc.v2(0,56));
var m2 = cc.moveTo(0.5,cc.v2(0,-580));
var end_func = cc.callFunc(function(){
console.log("dd")
}.bind(this));
// 这是定义一个容器来放动作
var seq = cc.sequence([m1,m2,end_func]);
this.rope.runAction(seq);
var m1 = cc.moveTo(0.5,cc.v2(0,56));
var mid_func = cc.callFunc(function(){
console.log("中间");
var cow = this.hit_test();
cow.removeFromParent(); // 把从父节点中删除该节点
}.bind(this));
var m2 = cc.moveTo(0.5,cc.v2(0,-580));
var end_func = cc.callFunc(function(){
console.log("dd")
}.bind(this));
// 这是定义一个容器来放动作
var seq = cc.sequence([m1,mid_func,m2,end_func]); //原来可以在第一个动作执行完就回调
this.rope.runAction(seq);
26. 复制预制体(需要先声明),设置位置,定时器
properties: {
cow_prefab: {
type: cc.Prefab,
default: null
},
cow_root: {
type: cc.Node,
default: null
}
},
gen_one:{
var cow = cc.instantiate(this.cow_prefab);
this.cow_root.addChild(cow);
cow.setPosition(cc.v2(520,-70)); // 设置位置
var time = 3 + Math.random() * 2; // 3到5秒
this.scheduleOnce(this.gen_one.bind(this),time); // 每隔三到五秒钟调一次函数
}
27. 写代码要先写总体逻辑,然后再逐个实现整个逻辑中需要的函数(很重要,提高代码可读性)
28. 声明图片数组集合
rope_imgs: {
type: cc.SpriteFrame,
default: []
}
29.修改精灵节点上的图片
this.rope_sprite.spriteFrame = this.rope_imgs[cow_type]; // spriteFrame 这个属性就是指图片
30. 接入微信小游戏,排行榜功能要用 wx.getFriendCloudStorage() 和 wx.getGroupCloudStorage() 两个 API,这两个 API 的返回结果都是一个对象数组,数组的每一个元素都是一个表示用户数据的对象
31. 开发者需要创建两个项目(子域必须使用全屏窗口,与主域保持一样的设计分辨率和适配模式。)
- 主域项目工程(正常的游戏项目)
- 开放数据域项目工程(通过微信 API 获取用户数据来做排行榜等功能的项目)
32. Mask组件:就是一个视口(想像一个窗口去看外面的世界,只能看到这个窗口大小的视区),所以mask组件的type类型有椭圆,矩形,图片类型;mask有nverted(反向遮罩) 这个属性,mask的子节点大小超过mask的就看不到;椭圆ellipse的时候segements等于64的时候就是相当于一个圆。
33. Layout组件:对子元素进行垂直或者水平布局,或者先水平后垂直布局(type:GRID);面板属性--ResizeMode:node不会对子节点和容器进行大小缩放,child对子节点的大小进行缩放,container对容器的大小进行缩放(注意这个最常用);
34. scrollView滚动视图组件:
- 主要结构: root->view(Mask裁剪超出范围的内容) ---> content(Layout)负责内容排版;
- 滚动列表的每个选项:root(w, h,制定大小,好给Layout用)
- 代码里面使用cc.ScrollView:将选项做成预制体,在代码里面new 出预制体,加入到content节点下;
35. 代码演示scrollView的使用
cc.Class({
extends: cc.Component,
properties: {
// foo: {
// default: null, // The default value will be used only when the component attaching
// to a node for the first time
// url: cc.Texture2D, // optional, default is typeof default
// serializable: true, // optional, default is true
// visible: true, // optional, default is true
// displayName: 'Foo', // optional
// readonly: false, // optional, default is false
// },
// ...
item_prefab: {
type: cc.Prefab,
default: null,
},
opt_item_prefab: {
type: cc.Prefab,
default: null,
},
scrollview: {
type: cc.ScrollView,
default: null,
},
},
// use this for initialization
onLoad: function () {
var item = cc.instantiate(this.item_prefab);
this.node.addChild(item);
for(var i = 0; i < 10; i ++) {
var opt_item = cc.instantiate(this.opt_item_prefab);
this.scrollview.content.addChild(opt_item); //scrollview节点的content属性默认绑定了content节点(也就是视图内容)
}
},
// called every frame, uncomment this function to activate update callback
// update: function (dt) {
// },
});
36. 生命周期:onLoad只会调用一次,官方说法是“onLoad 回调会在组件首次激活时触发”。onEnable则会在节点每次被设置为激活时触发。(例如设置游戏结束显示界面 this.endUI.active = true 之类可以用这个生命周期来播放背景音乐)
37. action动画
// (1)缩放
var delay1 = cc.delayTime(0.5); //延迟0.5秒
var toBigAction1 = cc.scaleTo(0.3, 3); //0.3秒变成3倍大小(默认是线性的放大,即匀速放大)
var action1 = cc.sequence(delay1, toBigAction1); //按顺序执行,先延迟1秒,后缩放
this.birdNode1.runAction(action1); //this.birdNode1执行以上动作
// 注意:这里缩放没有使用缓动动作,默认按线性。
// (2)移动
var delay_startBtn = cc.delayTime(0.2); //延迟0.2秒
//0.75秒内x移动0,y移动-1200,移动曲线按cc.easeCubicActionIn()
var startBtnAction = cc.moveBy(0.75, cc.p(0, -1200)).easing(cc.easeCubicActionIn());
this.startBtn.runAction(cc.sequence(delay_startBtn, startBtnAction)); //this.startBtn执行以上动作
// 注意:这里移动使用了缓动动作,通常也就设计到运动学的情况会用到缓动动作,比如抛物线运动、弹簧。
// (3)透明度
var delay2 = cc.delayTime(0.3); //延迟0.3秒
var fadeAction2 = cc.fadeTo(0.25, 0); //0.25秒透明度从255降到0
var action2 = cc.sequence(delay2, fadeAction2);
this.heartNode1.runAction(action2);
this.heartNode2.runAction(action2.clone()); //这里不能直接用action2,必须action2.clone()
// 注意:action不能同名,同名的话只会执行最后一个。需要重复使用时,可用上面的clone()。
// (4)回调函数
onLoad: function(){
var delay3 = cc.delayTime(0.3);
var fadeAction3 = cc.fadeTo(0.25, 255);
var callback1 = cc.callFunc(this.ladderAnimation, this); //回调函数
var action3 = cc.sequence(delay3, fadeAction3, callback1);
this.mileageNode.runAction(action3);
},
ladderAnimation: function(){
....
},
// 注意:回调函数是一个很有用的设定,比如你可以在点击按钮后,先将按钮飞出屏幕,然后执行跳转场景的函数。
// (5)震屏
var quakeAction = cc.repeat(
cc.sequence(cc.moveBy(0.05,cc.p(2,2)),
cc.moveBy(0.1,cc.p(-4,4)),
cc.moveBy(0.05,cc.p(2,2)),
cc.moveBy(0.1,cc.p(0,-6))),
2);
this.bgNode.runAction(quakeAction);
// 注意:这里用了cc.repeat,其执行了2次。
38. 判断一个节点是否处于Action动画中
// (1)给每个action设置tag,保存当前播放的action tag,通过tag获得当前action,执行以下
// action->isDone() 返回true则action执行完毕。
var action = cc.moveTo(0.1, cc.p(100, 1));
action.setTag(1);
this.action2 = action.clone();
this.node.runAction(action);
var action1 = this.node.getActionByTag(1);
if(action1.isDone()){
cc.log("执行完毕");
}
if(this.action2.getTarget() != null){
cc.log("Action有目标");
}
//通过节点的事件来判断
this.node.runAction(cc.moveBy(2, cc.p(100,100)).setTag(123));
this.node.on("position-changed", function(event){
cc.log('移动了哦');
var action3 = event.currentTarget.getActionByTag(123);
if(action3.isDone()){
cc.log("执行完毕");
}
});
39. action的执行时间包含了回调函数的执行时间。即action的isDone会在回调函数执行完,值才为true。
40. 在for循环中执行Action,Action的执行会在for循环外面。for循环只是启动所有Action,就立刻退出了循环。
41. 存储数据:
// (1)单个数据的存储
cc.sys.localStorage.setItem(key, value)
cc.sys.localStorage.getItem(key)
//存储
cc.sys.localStorage.setItem('gold', 100);
//读取
var g = cc.sys.localStorage.getItem('gold');
// (2)将对象序列化为 JSON 后保存
userData = {
name: 'Tracer',
level: 1,
gold: 100
};
//存储
cc.sys.localStorage.setItem('userData', JSON.stringify(userData));
//读取
var userData = JSON.parse(cc.sys.localStorage.getItem('userData'));
42. 前后台切换
// 1.cocos creator前后台切换
// 当玩家在玩游戏时,突然接了一个电话,此时游戏会被切到后台待机,所有的声音
// 播放都会停止,等打完电话,回到游戏,游戏又会被切回前台来,需要手动播放
// 声音。可使用如下代码
onLoad: function () {
cc.game.on(cc.game.EVENT_HIDE, function(){
console.log("游戏进入后台");
this.doSomeThing();
},this);
cc.game.on(cc.game.EVENT_SHOW, function(){
console.log("重新返回游戏");
this.doSomeThing();
},this);
},
43. 微信开发者工具报错 showShareMenu:fail no permission 确认申请的是一个游戏类目的小程序就行了
====================================================================================1. 动画编辑器里面属性列表的 sample 表示一秒钟有多少帧,speed 表示播放速度,大于1表示加快,小于1表示变慢,wrapmode 表示动画的播放模式
2. 代码中使用animation组件:
anim: { //声明,然后编辑器里面拉取对应带有animation组件的节点(第一种方式)
type: cc.Animation,
default: null
}
this.anim_com = this.node.getChildByName("anim").getComponent(cc.Animation); //获得节点上的Animation组件(第二种方式)
3. Animation组件主要的方法:
play([name], [start_time]), 播放指定的动画,如果没有制定就播放默认的动画;
playAdditive: 与play一样,但是不会停止当前播放的动画;(用的不多)
stop([name]): 停止指定的动画,如果没有指定名字就停止当前播放的动画;
pause/resume: 暂停唤醒动画;
getClips: 返回组件里面带的AnimationClip数组
4. Animation播放事件: 动画组件对象来监听on,不是节点
play : 开始播放时 stop : 停止播放时 pause : 暂停播放时 resume : 恢复播放时
lastframe : 假如动画循环次数大于 1,当动画播放到最后一帧时 finished : 动画播放完成时
5. 动画里面调用代码函数:
1: 插入一个时间到动画里面;
2: 编辑这个时间触发的函数: 名字 + 参数
3: 遍历当前动画组件所挂节点上面所有的脚本或组件,根据这个名字来触发函数;
4: 要慎用,代码和动画之间不易太多的调用;
6. cc.loader
1:有三个默认的Pipeline:
(1) assetLoader: 主要用于加载资源, 加载asset类型资源,和释放这些资源;
(2) downloader: 主要用于下载文件, 文本,图像,脚本,声音,字体, 自定义的download;
(3) loader: 第三个默认的Pipeline,可以加载json, image, plist, fnt, uuid;
2: 资源分为本地(assets目录下)与远程资源;
3: 加载异步的;
7. 本地资源加载:
代码里面加载资源必须要求资源在assets/resources/文件夹下
根据场景的依赖关系来打包我们的资源; 去掉不用的资源
无法判断在代码里面加载的资源,是哪些?-->所有的resources目录下的资源,都会被打包进去,你在代码里面就能加载到它了
如果资源不在代码里面加载,一般不要放到resources目录下 ,如果放到了,这个资源不关你有没有用,都会被打包进去;
资源的url不需要加assets/resources这个部分,路劲不需要加这个前缀;
cc.loader.loadRes(url, onComplete回掉函数);
cc.loader.getRes(url, [type]); 获取资源id;
cc.loader.loadResArray([], type类型, progressCallback, completeCallback);
cc.loader.loadResDir (url [type ] [progressCallback ] [completeCallback ]) 加载一个路径下的资源;
代码演示:
local_load: function() {
// 本地加载声音
// 注意:这里不需要后缀名, assets/resources/这个也不需要
cc.loader.loadRes("bg", function(err, ret){
if(err) {
console.log(err);
return;
}
console.log(ret); // audio clip
this.audio.clip = ret;
this.audio.play();
}.bind(this));
// end
// 本地加载图片
cc.loader.loadRes("img/disk", cc.SpriteFrame, function(err, ret) {
if (err) {
console.log(err);
return;
}
// spriteFrame对象
// 例如我们的游戏,在进入下一个场景之前,有一个资源加载场景,那么到了新的场景后,我们就能够找到这个加载好的资源
// cc.loader.getRes();
this.sprite.spriteFrame = cc.loader.getRes("img/disk", cc.SpriteFrame);
// end
}.bind(this));
// end
},
8. 远程资源加载:
// 加载远程的图片
cc.loader.load("http://127.0.0.1:6080/splash.png", function(err, ret) {
if(err) {
console.log(err);
return;
}
// ret is cc.Texture2D这样对象
this.sprite.spriteFrame.setTexture(ret); //这样就显示出来了
this.sprite.node.setContentSize(ret.getContentSize()); // 使用这个图片的原始大小
// end
}.bind(this));
// 从服务器加载mp3来进行播放, type,就是url不带类型,我们就使用url + type的模式来制定类型
cc.loader.load({url: "http://127.0.0.1:6080/bg.mp3", type: "mp3"}, function(err, ret) {
if (err) {
console.log(err);
return;
}
this.audio.clip = ret;
this.audio.play();
}.bind(this));
// end
// 从服务器加载json文件
cc.loader.load({url: "http://127.0.0.1:6080/project.json", type: "json"}, function(err, ret) {
if (err) {
console.log(err);
return;
}
console.log(ret);
}.bind(this));
// end
//
cc.loader.load({url: "http://127.0.0.1:6080/test.mydata", type: "mydata"}, function(err, ret) {
if (err) {
console.log(err);
return;
}
console.log(ret);
}.bind(this));
9. 每个场景有个自动释放资源,勾选上这个场景的资源会自动释放,不勾选上这个场景的资源不释放
10. 代码加载的资源,默认是不会受这个选项的影响的,除非设置 cc.loader.setAutoRelease(url, brealse)
11. 手动释放资源: loadRes/ releaseRes load / release releaseAsset (资源对象的object);
12. 字体文件直接拖到res文件夹后,再直接拖到层级管理器中的节点就好了
13. 界面适配:
- 背景图做到能适配任何主流的手机分辨率;
- 将界面布局分为9大停靠点
14. 按钮按下时状态变化(Transition)
- NONE
- COLOR 纯色切换
- SPRITE 图片切换
- SCALE 按钮缩放
15. 在脚本中动态添加按钮
// 以下是关卡界面的脚本片段
var btnNode = cc.instantiate(this.levelBtnPrefab); //按钮的预制(预制中无法保存按钮事件)
this.scrollViewContentNode.addChild(btnNode);
btnNode.setPosition(pos[i % 12][0] + 750 * Math.floor(i / 12), pos[i % 12][1]);
//绑定事件
var clickEventHandler = new cc.Component.EventHandler();
clickEventHandler.target = this.node;
clickEventHandler.component = "Level"; //脚本名字
clickEventHandler.handler = "onLevelBtn"; //点击的回调函数
clickEventHandler.customEventData = (i + 1).toString(); //参数
var btn = btnNode.getComponent(cc.Button);
btn.clickEvents.push(clickEventHandler);
16.