js 时间回调函数
在chrome和node中有效
定时连续回调
iv = setInterval(callback, milsec)
clearInterval(iv)
在chrome和node中有效
定时连续回调
iv = setInterval(callback, milsec)
clearInterval(iv)
有jslint, javascriptlint(jsl), 还有eslint. 貌似eslint是最受欢迎的.
安装eslint
npm i -g eslint
运行:
eslint *.js
在js目录下放一个.eslintrc.json文件, 可以对需要lint的规则做设置.下面是我复制cocos creator的配置,并做了一点修改, 忽略了末尾分号检测.
{
"extends": "eslint:recommended",
"rules": {
"comma-dangle": 0,
"no-console": 0,
"no-constant-condition": 0,
"semi": 0,
"no-unused-vars": 1
},
"env": {
"browser": true,
"node": true,
"es6": true,
"mocha": true
},
"plugins": [
],
"globals": {
"cc": false,
"CC_EDITOR": false,
"CC_DEV": false,
"CC_JSB": false,
"_ccsg": false
}
}
在REPL中比较容易调试和发现问题, 所以这儿介绍REPL的玩法.
node.js服务端需要安装模块.
npm install ws
进入node, 开启服务,等待客户端连接. 为了便于以后向客户端发送消息, 把连接后的ws对象赋值给全局变量thisws
node
>
var thisws
var Wss = require('./ws').Server
var wss = new Wss({port: 3888})
wss.on('connection', function(ws){
thisws = ws
console.log('connected.')
ws.on('message', function(message){
console.log("msg: ",message)
})
})
chrome 打开console,进行连接和监听服务器消息
s = new WebSocket('ws://localhost:3888')
s.onmessage = function(e){console.log(e.data)}
好啦!, 在chrome中send消息可以在node那边收到, 在node中send消息也可以在chrome中收到了
先从chrome中send
s.send('我是chrome') // 在node中将看到这条消息.
再从node中send
thisws.send('我是node服务器') //在chrome中将看到这条消息.
当然,除了chrome自带的WebSocket客户端, 在node中ws也提供客户端, 另外,socket.io也是另一种websocket客户端和服务器的提供者.
onclose = function(e){}
onmessage = function(e){}
onopen = function(){}
onerror = function(){}
send(string)
close()
node ws服务端监听和发送
wss.on('connection',function(ws){})
wss.on('error', function(err){})
wss.on('header',function(array){})
wss.on('listening',function(){})
ws.on('message',function(message_string,flags){})
ws.on('close',function(code, reason){})
ws.on('error',function(err){})
ws.on('open',function(){})
ws.onclose = function(e){}
ws.onerror = function(e){}
ws.onmessage = function(e){}
ws.onopen = function(e){}
ws.readyState
ws.send(message_string, function(error){})
ws.close(code, reason)
// 所有客户端列表
wss.clients
wss.close(function(){})
开始以前,准备好visual studio 2015, 现在社区版是免费的.这玩意儿很大, 需要装个半天的.
先安装由微软推出的Electron打包为uwp工具 (真的是微软推出啊! 微软现在竟然这么拥抱开源...)
npm install -g electron-windows-store
然后进入管理员权限的powershell运行:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
OK, 接下来就打包了, 举我自己的app的例子:
electron-windows-store --input-directory C:\r\vueproj\nhproj\nhpe --output-directory C:\r\vueproj\nhproj\nhpe_uwp --flatten true --package-version 1.0.0.0 --package-name nhpe
哦, 真的好长, 幸好里面的参数还可以简写:
electron-windows-store -i C:\r\vueproj\nhproj\nhpe -o C:\r\vueproj\nhproj\nhpe_uwp -f true -p 1.0.0.0 -n nhpe
需要注意的是, --package-name一项, 名称必须是app运行文件的名称. 比如, 我已经把electron.exe修改为nhpe.exe了,这儿就一定要填写nphe
然后回车后要提示:
? Did you download and install the Desktop App Converter? It is *not* required to run this tool. No
这一步不是必须的, 回答no即可. 如果yes的话, 你需要去这儿安装Desktop App Converter,不过还是懒得麻烦了.
回车后提示:
? You need to install a development certificate in order to run your app. Would you like us to create one? Yes
这一步必须yes, 否则虽然可以打包成功,然而打好的包由于没有签名而不能运行. 继续回车:
? Please enter your publisher identity: CN=idarc
? Please enter the location of your Windows Kit's bin folder: C:\Program Files (x86)\Windows Kits\10\bin\x64
publisher identity这一步必须填写为 CN=xxxx 的格式,否则后面会报错的...
Windows Kit这一步, 如果visual studio 装的OK的话, 直接回车就可以了.
接下来会弹窗让你输入密码. 不用输入密码, 点击"none"即可.
等待一会儿, 如果没有错误的话, 就会在指定的目录下看到nhpe.appx, 双击运行安装即可.
安装完了再运行, 哦, 这货和直接运行electron.exe改名的nhpe.exe为毛没有任何外观上的区别? 微软说区别是可以获取在在其他xbox等上运行的能力,然而我并没有xbox...
参考原文
假如有两个空对象, 判断他们是否相等, 结果一定是false:
var a = {}
var b = {}
a == b // false
因为跟c语言类似, 这儿判断的是地址而不是内容.
那么怎么知道a是空呢?
有三个方法. 这儿先假设有一个对象obj
var obj = {}
方法1
Object.keys(obj).length === 0
方法2
Object.getOwnPropertyNames(obj).length === 0
方法3, 自己判断
// Speed up calls to hasOwnProperty
var hasOwnProperty = Object.prototype.hasOwnProperty;
function isEmpty(obj) {
// null and undefined are "empty"
if (obj == null) return true;
// Assume if it has a length property with a non-zero value
// that that property is correct.
if (obj.length > 0) return false;
if (obj.length === 0) return true;
// If it isn't an object at this point
// it is empty, but it can't be anything *but* empty
// Is it empty? Depends on your application.
if (typeof obj !== "object") return true;
// Otherwise, does it have any properties of its own?
// Note that this doesn't handle
// toString and valueOf enumeration bugs in IE < 9
for (var key in obj) {
if (hasOwnProperty.call(obj, key)) return false;
}
return true;
}
// test it
isEmpty(""), // true
isEmpty(33), // true (arguably could be a TypeError)
isEmpty([]), // true
isEmpty({}), // true
isEmpty({length: 0, custom_property: []}), // true
isEmpty("Hello"), // false
isEmpty([1,2,3]), // false
isEmpty({test: 1}), // false
isEmpty({length: 3, custom_property: [1,2,3]}) // false
在chrome中你只能这么做了:
(function()
{
'use strict';
var foo = 123;//works fine
bar = 345;//ReferenceError: bar is not defined
}());
上面这种模式被称之为IIFE(Immediately Invoked Function Expression)即刻执行的函数表达式.
在node中, 你当然也可以用上这种方式, 不过还有一种方式, 在启动node的时候使用--use-strict参数
node --use_strict
试听win10上的网易云音乐的轻音乐, 发现即便是自动推荐的, 也有很多蛮好听的曲子. 去看了一下作曲者, 竟然很多是国内的原创音乐人. 比如这个: 饭碗的彼岸, 就有好几首好听的曲子《小河》《小樱》。
感觉真是高手在民间啊。
java8及其以前是不支持交互式命令环境REPL的.
Java9 将引入一个JShell来支持. 但目前Java9还没有推出, 怎么办呢? 有一个github的项目, 支持java的REPL
使用前, 需要安装JDK(不是JRE), 以及gradle. 下面是命令行中的安装方法
git clone https://github.com/albertlatacz/java-repl.git
cd java-repl
gradle shadowJar
cd build/libs
java -jar javarepl-dev.jar
需要注意的是,gradle这一步需要执行数分钟, 我的电脑上是8分多钟.
java运行这一步, 需要是类似于jdk1.8.0_111/bin/java这样的目录
由于当前cocos creator音频部分的文档还没有, 这儿简单介绍一下音频部分.
组件可以进行的操作包括播放/暂停/停止/继续
audioSource.play()
audioSource.pause()
audioSource.stop()
audioSource.resume()
可以获取播放进度和总长度
audioSource.getCurrentTime() //单位为秒的浮点数
audioSource.getDuration() //同样是单位为秒的浮点数
从属性浏览器中可以发现还有如下属性:
clip
volume
mute
loop
playOnLoad
preload
引擎是一个cc下的对象cc.audioEngine来操作的.
引擎并不需要去操作一个node对象, 而是直接操作一个资源管理器里面的源文件.所以在定义cc.Class的时候与audioSource不同. 以url的方式定义, 音乐文件(很可能)需要放在resources目录中.
// 在操作audioSource的AudioSourceControl.js文件中, 定义属性为cc.AudioSource类型. 然后, 从资源管理器中拖动一个音乐文件到audiosource节点的clip上. 再从层级管理器中拖动audiosource节点到另一个节点的AudioSourceControl.js属性的audioSource上
cc.Class({
properties: {
audioSource: {
type: cc.AudioSource,
default: null
},
// 省略其他代码
})
// 在操作audioEngine的AudioEngineControl.js文件中, 定义**url**为cc.AudioClip类型. 然后, 从资源管理器中拖动一个音乐文件到使用此AudioEngineControl.js的节点的audio属性上.
// 从示例代码中来看, 这个文件是放在resources目录中的,也就是说, 需要动态加载的.
cc.Class({
properties: {
audio: {
url: cc.AudioClip,
default: null
},
// 省略其他代码
})
使用引擎播放, 每次会产生一个audio id, 记住这个id, 用来在后面对该播放中的音乐进行操作.
// 播放,暂停,继续, 停止
var id = cc.audioEngine.play(this.audio, false, 1); //参数分别为filepath, loop, volume. filepath示例为res/raw-assets/resources/audio/music_logo.mp3, 如果是拖进来的就不用管这个了.
cc.audioEngine.pause(id)
cc.audioEngine.resume(id)
cc.audioEngine.stop(id)
对所有的播放中音乐统一操作:
cc.audioEngine.stopAll();
cc.audioEngine.pauseAll();
cc.audioEngine.resumeAll();
获取各种状态
cc.audioEngine.getCurrentTime(id) // 秒数, 有对应set方法
cc.audioEngine.getDuration(id) // 秒数
cc.audioEngine.getStatus(id) // 返回值-1: 停止状态, 1: 播放状态, 2: 暂停状态.
cc.audioEngine.getVolume(id) // 有对应set方法
cc.Class({
extends: cc.Component,
properties: {
audioSource: {
type: cc.AudioSource,
default: null
},
label: {
type: cc.Label,
default: null
}
},
// use this for initialization
onLoad: function () {
// cc.audioEngine.setMaxWebAudioSize(1024*10);
},
update: function () {
if (!this.label) {
return;
}
var audio = this.audioSource;
this.label.string = audio.getCurrentTime().toFixed(1) + ' s / ' + audio.getDuration().toFixed(1) + ' s';
},
play: function () {
this.audioSource.play();
},
pause: function () {
this.audioSource.pause();
},
stop: function () {
this.audioSource.stop();
},
resume: function () {
this.audioSource.resume();
}
});
cc.Class({
extends: cc.Component,
properties: {
audio: {
url: cc.AudioClip,
default: null
},
label: {
type: cc.Label,
default: null
}
},
onLoad: function () {
this.maxNum = cc.audioEngine.getMaxAudioInstance();
this.audioPool = [];
// check deprecated
['playMusic', 'playEffect'].forEach(function (name) {
if (!cc.audioEngine[name]) {
cc.warn('.' + name + ' is not found!');
}
});
},
update: function () {
if (!this.label) return;
for (var i=0; i<this.audioPool.length; i++) {
var id = this.audioPool[i];
var state = cc.audioEngine.getState(id);
if (state < 0) {
this.audioPool.splice(i, 1);
i--;
}
}
this.label.string = 'Instance: ' + this.audioPool.length + ' / ' + this.maxNum;
},
play: function () {
if (!this.audio) return;
var id = cc.audioEngine.play(this.audio, false, 1);
this.audioPool.push(id);
},
stopAll: function () {
if (!this.audio) return;
cc.audioEngine.stopAll();
},
pauseAll: function () {
if (!this.audio) return;
cc.audioEngine.pauseAll();
},
resumeAll: function () {
if (!this.audio) return;
cc.audioEngine.resumeAll();
},
});
微软目前的开源项目:
箭头函数里使用的this不是它自己的this, 是上层环境的this. 默认情况下, 是window.
cocos creator集electron, node.js, vue.js, 浏览器对象, canvas, webGL于一身, 哦看起来还是很强大的.
/assets 根目录下面主要编辑的文件都在这儿
/library, /temp 可以认为都是临时文件, 貌似为了加速编辑器某些处理产生的, 和assets里的资源是匹配关系, 如果匹配出了问题, 这些都删掉, creator会自动再次生成.
/local 编辑器布局, 一般情况下并没有什么用, 貌似删了也没有关系
/settings 构建发布等相关配置, 蛮重要的,要保留.
package.json 也规定了一些重要的信息, 要保留.
运行后还会生成很多.meta文件, 是一个资源文件对应一个.meta, 里面主要是资源文件的uuid和其他一些信息.
所以, 主要的编辑工作都是在assets里面. 再说说assets, 文件可以包括:
.fire 场景文件, 规定了场景中各个内容, 也就是creator中层级管理器里面显示的那些东东. 其实是一个json文件.
.js 脚本文件, 当然了, 是JavaScript, 这个后面专门说一下
资源文件:
图片 包括.png, jpg等, 这个不消说, 是在场景中添加到精灵里面的;
粒子 以.plist结尾. plist和json文件作用相同, 只不过是xml形式的. 粒子文件的粒子图片可以是独立图片, 也可以是内嵌在plist中的base64数据.独立图片的话, 需要和plist放在一个目录中. cocos支持的粒子文件在windows上可以用Particle Editor编辑.
字体 可以使用ttf字体或者是位图字体.fnt, windows下使用BMFont制作, MAC下使用glyph designer制作. windows下的制作方法查看这儿
动画 动画目前就我所知, 有三种形式:
js脚本很大程度上结合了node.js和浏览器环境, 然而又有所不同.
目前发现与nodejs一致的有:
不一致的有:
与浏览器环境一致的有:
据说还可以使用npm install安装第三方纯js库使用, 尚未试过.
另外根据帮助文档, cocos creator的项目中也有一个类似于electron的index.html文件, 目前暂未开放出来提前修改.
cc.Class({
extends: cc.Component,
properties: {
label: cc.Label,
}
},
accessLabel: function(){
// 访问 this.label
}
}
this.label.node.parent //父节点
this.label.node.children // 子节点的array
this.label.node.parent.children // 父节点的子节点array. 由于父节点是node,所以可以直接使用node下的元素children
this.label.node.getParent() === this.label.node.parent // true. 目前暂不清楚get方法与直接访问元素是否可能有什么区别.
this.label.node.getChildern() === this.label.node.children //true
this.label.node.getChildByName('childname') // 按名字查找
通过cc.find()方法, 可以从根节点到达任意子节点
cc.find('Canvas/Label') //返回Canvas下的Label节点.
this.label.node.x = 123 // 其他在属性检查器中看得到的都可以这样操作
this.label.getComponent(cc.Widget).top = 123 // getComponent可以传入一个cc类型
this.label.getComponent('js_file_name') //可以传入字符串, 获取节点上附加的脚本组件
this.label.addComponent(cc.Layout) //也可以通过addComponent添加组件
var newNode = new cc.Node('newNodeName')
var clonedNode = cc.instantiate(anExistedNode)
附加在节点上的js组件,可以获得节点的生命周期变更通知, 并通过回调函数进行操作.生命周期的顺序是:
onLoad
onEnable
start
update, lastUpdate 依次循环
onDisable
onDestroy
对节点的active赋值true/false, 将触发onEnable/onDisable事件
somenode.active = false // onDisable
somenode.active = true // onEnable
somenode.isValid //true
somenode.destroy()
somenode.isValid //false
__cid__
__classname__
__eventTargets
__instanceId
__onNodeActivated
__preload
__scriptAsset
__scriptUuid
_deserialize
_destroyImmediate
_destruct
_enabled
_getLocalBounds
_id
_instantiate
_isOnLoadCalled
_name
_objFlags
_onPreDestroy
addComponent
constructor
destroy
enabled
enabledInHierarchy
getComponent
getComponentInChildren
getComponents
getComponentsInChildren
isRunning
lateUpdate
name
node
onEnable
onFocusInEditor
onLoad
onLostFocusInEditor
onRestore
playJump
playRun
resetInEditor
schedule
scheduleOnce
start
unschedule
unscheduleAllCallbacks
update
uuid
加载另一个场景
cc.director.loadScene('anotherScene', callSomeFunctionWhenLoaded)
预加载另一个场景
cc.director.preLoadScene('anotherScene', callSomeFunctionWhenPreloaded)
保留一个场景中的节点到下一个场景.
cc.game.addPersistRootNode('nodeName')
取消保留节点
cc.game.removePersistRootNode('nodeName')
somenode.parent = someNewParentNode
var pa = somenode.parent
pa.removeChild(somenode)
pa.addChild(somenode) // 将somenode移动到了父节点树的最后面.
值得注意的是, 虽然可以用pa.children直接访问到children的array, 然而此array做pop/push/shift/unshift等操作, 似乎并不会对节点树有任何影响. 因此还是乖乖用addChild和removeChild吧!
* 还可以在本级使用setSiblingIndex(index), 可以调整自己在本级中的位置为index
somenode.getSiblingIndex() // 例如, 0, 是在最低端
somenode.setSiblingIndex(2) // 例如, 2, 上移两层.
这儿介绍使用nodejs的方式运行, 当然你要装好了nodejs.
以mac版为例, 打开sublime, 选择"Tools->Build System->New Build System...", sublime将打开一个"untitled.sublime-build"文件, 里面有一些json内容, 删掉这些内容, 替换为:
{
"cmd": ["node", "$file"],
"selector": "source.js"
}
保存文件, 取名为"node".
打开一个js文件, 按"command+B"(windows下是ctrl+B)试试, 看console界面有没有输出.
如果输出提示找不到node, 需要修改上面文件的地址为绝对路径.
在mac下打开终端, 输入
which node
我的mac上显示的是
/usr/local/bin/node
将显示node的绝对路径, 复制到上面的文件中, 修改为:
{
"cmd": ["/usr/local/bin/node", "$file"],
"selector": "source.js"
}
再试试!
以下在windows系统中测试过.
默认vim编辑完一个文件后, 要生成一个以~为结尾的备份文件.
不需要该文件, 则需要在用户的根目录如/user/name/下设置 _vimrc 文件.
加入
set nobackup
也可以指定一个其备份的地方:
set backupdir=D:/Program/ Files/Vim /tmp
Safari需要先打开开发菜单,然后在开发菜单里面选择"Responsive Design Mode".