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); // 每隔三到五秒钟调一次函数
}