由Uncaught TypeError: this.player.startMoveAt is not a function引申

Simulator: libpng warning: iCCP: known incorrect sRGB profile

E/jswrapper (271): ERROR: Uncaught TypeError: this.player.startMoveAt is not a function, location: assets/scripts/Game.js:0:0

详细错误见下图:

以下内容全部来自于网络,如有侵权,及时联系。

Cocos Creator 访问节点和组件(摘自官方文档)

你可以在属性检查器里修改节点和组件,也能在脚本中动态修改。动态修改的好处是能够在一段时间内连续地修改属性、过渡属性,实现渐变效果。脚本还能够响应玩家输入,能够修改、创建和销毁节点或组件,实现各种各样的游戏逻辑。要实现这些效果,你需要先在脚本中获得你要修改的节点或组件。

在本篇教程,我们将介绍如何

获得组件所在的节点

获得其它组件

使用属性检查器设置节点和组件

查找子节点

全局节点查找

访问已有变量里的值

获得组件所在的节点

获得组件所在的节点很简单,只要在组件方法里访问this.node变量:

start:function (){

var node =this.node;

node.x =100;

    }

获得其它组件

你会经常需要获得同一个节点上的其它组件,这就要用到getComponent这个 API,它会帮你查找你要的组件。

start:function (){

var label =this.getComponent(cc.Label);

var text =this.name +' started';

// Change the text in Label Component

 label.string = text;

    }

你也可以为getComponent传入一个类名。对用户定义的组件而言,类名就是脚本的文件名,并且区分大小写。例如 "SinRotate.js" 里声明的组件,类名就是 "SinRotate"。

var label =this.getComponent("cc.Label");

在节点上也有一个getComponent方法,它们的作用是一样的:

start:function (){

cc.log(this.node.getComponent(cc.Label) ===this.getComponent(cc.Label) );// true

    }

如果在节点上找不到你要的组件,getComponent将返回 null,如果你尝试访问 null 的值,将会在运行时抛出 "TypeError" 这个错误。因此如果你不确定组件是否存在,请记得判断一下:

start:function (){

var label =this.getComponent(cc.Label);

if(label) {

label.string ="Hello";

        }

else{

cc.error("Something wrong?");

        }

    }

获得其它节点及其组件

仅仅能访问节点自己的组件通常是不够的,脚本通常还需要进行多个节点之间的交互。例如,一门自动瞄准玩家的大炮,就需要不断获取玩家的最新位置。Cocos Creator 提供了一些不同的方法来获得其它节点或组件。

利用属性检查器设置节点

最直接的方式就是在属性检查器中设置你需要的对象。以节点为例,这只需要在脚本中声明一个 type 为cc.Node的属性:

// Cannon.js

cc.Class({

    extends: cc.Component,

    properties: {

// 声明 player 属性

        player: {

            default:null,

            type: cc.Node

        }

    }

});

这段代码在properties里面声明了一个player属性,默认值为 null,并且指定它的对象类型为cc.Node。这就相当于在其它语言里声明了public cc.Node player = null;。脚本编译之后,这个组件在属性检查器中看起来是这样的:

接着你就可以将层级管理器上的任意一个节点拖到这个 Player 控件:

这样一来它的 player 属性就会被设置成功,你可以直接在脚本里访问 player:

// Cannon.js

var Player =require("Player");

cc.Class({

extends: cc.Component,

properties: {

// 声明 player 属性

        player: {

            default:null,

            type: cc.Node

        }

    },

start:function (){

        var playerComp =this.player.getComponent(Player);

        this.checkPlayer(playerComp);

    },

// ...

});

利用属性检查器设置组件

在上面的例子中,如果你将属性的 type 声明为 Player 组件,当你拖动节点 "Player Node" 到属性检查器,player 属性就会被设置为这个节点里面的 Player 组件。这样你就不需要再自己调用getComponent啦。

// Cannon.js

var Player =require("Player");

cc.Class({

extends: cc.Component,

properties: {

// 声明 player 属性,这次直接是组件类型

        player: {

                default:null,

                type: Player

        }

    },

start:function (){

        var playerComp =this.player;

        this.checkPlayer(playerComp);

    },

// ...

});

你还可以将属性的默认值由null改为数组[],这样你就能在属性检查器中同时设置多个对象。

不过如果需要在运行时动态获取其它对象,还需要用到下面介绍的查找方法。

查找子节点

有时候,游戏场景中会有很多个相同类型的对象,像是炮塔、敌人和特效,它们通常都有一个全局的脚本来统一管理。如果用属性检查器来一个一个将它们关联到这个脚本上,那工作就会很繁琐。为了更好地统一管理这些对象,我们可以把它们放到一个统一的父物体下,然后通过父物体来获得所有的子物体:

// CannonManager.js

cc.Class({

extends: cc.Component,

start:function (){

this.cannons = [];

this.cannons =this.node.getChildren();

    }

});

这里的getChildren是cc.Node原有的一个 API,可以获得一个包含所有子节点的数组。

你还可以使用getChildByName:

this.node.getChildByName("Cannon 01");

如果子节点的层次较深,你还可以使用cc.find,cc.find将根据传入的路径进行逐级查找:

cc.find("Cannon 01/Barrel/SFX",this.node);

全局名字查找

当cc.find只传入第一个参数时,将从场景根节点开始逐级查找:

this.backNode = cc.find("Canvas/Menu/Back");

访问已有变量里的值

如果你已经在一个地方保存了节点或组件的引用,你也可以直接访问它们,一般有两种方式:

通过全局变量访问

你应当很谨慎地使用全局变量,当你要用全局变量时,应该很清楚自己在做什么,我们并不推荐滥用全局变量,即使要用也最好保证全局变量只读。

让我们试着定义一个全局对象window.Global,这个对象里面包含了backNode和backLabel两个属性。

// Globals.js, this file can have any name

window.Global = {

backNode:null,

backLabel:null,

};

由于所有脚本都强制声明为 "use strict",因此定义全局变量时的window.不可省略。

接着你可以在合适的地方直接访问并初始化Global:

// Back.js

cc.Class({

extends: cc.Component,

onLoad:function (){

Global.backNode =this.node;

Global.backLabel =this.getComponent(cc.Label);

    }

});

初始化后,你就能在任何地方访问到Global里的值:

// AnyScript.js

cc.Class({

    extends: cc.Component,

// start 会在 onLoad 之后执行,所以这时 Global 已经初始化过了

start:function(){

vartext ='Back';

Global.backLabel.string = text;

    }

});

访问全局变量时,如果变量未定义将会抛出异常。

添加全局变量时,请小心不要和系统已有的全局变量重名。

你需要小心确保全局变量使用之前都已初始化和赋值。

通过模块访问

如果你不想用全局变量,你可以使用require来实现脚本的跨文件操作,让我们看个示例:

// Global.js, now the filename matters

module.exports= {

backNode:null,

backLabel:null,

};

每个脚本都能用require+ 文件名(不含路径) 来获取到对方 export 的对象。

// Back.js

// this feels more safe since you know where the object comes from

varGlobal=require("Global");

cc.Class({

    extends: cc.Component,

onLoad:function(){

Global.backNode =this.node;

Global.backLabel =this.getComponent(cc.Label);

    }

});

// AnyScript.js

// this feels more safe since you know where the object comes from

varGlobal=require("Global");

cc.Class({

    extends: cc.Component,

// start 会在 onLoad 之后执行,所以这时 Global 已经初始化过了

start:function(){

vartext ="Back";

Global.backLabel.string = text;

    }

});



Cocos Creator中模块化脚本(官方文档摘录)

Cocos Creator 允许你将代码拆分成多个脚本文件,并且让它们相互调用。要实现这点,你需要了解如何在 Cocos Creator 中定义和使用模块,这个步骤简称为模块化。

如果你还不确定模块化究竟能做什么,模块化相当于:

C/C++ 中的include

C# 中的using

Java 和 Python 中的import

HTML 中的<link>

模块化使你可以在 Cocos Creator 中引用其它脚本文件:

访问其它文件导出的参数

调用其它文件导出的方法

使用其它文件导出的类型

使用或继承其它 Component

Cocos Creator 中的 JavaScript 使用和 Node.js 几乎相同的 CommonJS 标准来实现模块化,简单来说:

每一个单独的脚本文件就构成一个模块

每个模块都是一个单独的作用域

以同步的require方法来引用其它模块

设置module.exports为导出的变量

如果你还不太明白,没关系,下面会详细讲解。

在本文中,“模块”和“脚本”这两个术语是等价的。所有“备注”都属于进阶内容,一开始不需要了解。

不论模块如何定义,所有用户代码最终会由 Cocos Creator 编译为原生的 JavaScript,可直接在浏览器中运行。

引用模块

require

除了 Cocos Creator 提供的接口,所有用户定义的模块都需要调用require来访问。例如我们有一个组件定义在Rotate.js:

// Rotate.js

cc.Class({

extends: cc.Component,

// ...

});

现在要在别的脚本里访问它,可以:

var Rotate =require("Rotate");

require返回的就是被模块导出的对象,通常我们都会将结果立即存到一个变量(var Rotate)。传入require的字符串就是模块的文件名,这个名字不包含路径也不包含后缀,而且大小写敏感。

require 完整范例

接着我们就可以使用 Rotate 派生一个子类,新建一个脚本SinRotate.js:

// SinRotate.js

var Rotate = require("Rotate");

var SinRotate = cc.Class({

extends: Rotate,

update:function (dt) {

this.rotation += this.speed * Math.sin(dt);

    }

});

这里我们定义了一个新的组件叫 SinRotate,它继承自 Rotate,并对update方法进行了重写。

同样的这个组件也可以被其它脚本接着访问,只要用require("SinRotate")。

备注:

require可以在脚本的任何地方任意时刻进行调用。

游戏开始时会自动 require 所有脚本,这时每个模块内部定义的代码就会被执行一次,所以之后无论又被 require 几次,返回的始终是同一份实例。

调试时,可以随时在Developer Tools的Console中 require 项目里的任意模块。

定义模块

定义组件

每一个单独的脚本文件就是一个模块,例如前面新建的脚本Rotate.js:

// Rotate.js

var Rotate = cc.Class({

extends: cc.Component,

properties: {

speed:1

    },

update:function (){

this.transform.rotation +=this.speed;

    }

});

当你在脚本中声明了一个组件,Cocos Creator 会默认把它导出,其它脚本直接 require 这个模块就能使用这个组件。

定义普通 JavaScript 模块

模块里不单单能定义组件,实际上你可以导出任意 JavaScript 对象。假设有个脚本config.js

// config.js

var cfg = {

moveSpeed:10,

version:"0.15",

showTutorial:true,

load:function (){

// ...

    }

};

cfg.load();

现在如果我们要在其它脚本中访问config对象:

// player.js

var config =require("config");

cc.log("speed is", config.moveSpeed);

结果会有报错:"TypeError: Cannot read property 'moveSpeed' of null",这是因为cfg没有被导出。由于 require 实际上获取的是目标脚本内的module.exports变量,所以我们还需要在config.js的最后设置module.exports = config:

// config.js - v2

var cfg = {

moveSpeed:10,

version:"0.15",

showTutorial:true,

load:function (){

// ...

    }

};

cfg.load();

module.exports = cfg;

这样player.js便能正确输出:"speed is 10"。

那为什么定义 Component 时可以不用设置exports? 因为 Component 是 Cocos Creator 中的特殊类型,如果一个脚本定义了 Component 却没有声明exports,Cocos Creator 会自动将exports设置为 Component。

备注:

在module上增加的其它变量是不能导出的,也就是说exports不能替换成其它变量名,系统只会读取exports这个变量。

更多示例

导出变量

module.exports默认是一个空对象({}),可以直接往里面增加新的字段。

// foobar.js:

module.exports.foo =function (){

cc.log("foo");

};

module.exports.bar =function (){

cc.log("bar");

};

// test.js:

var foobar =require("foobar");

foobar.foo();// "foo"

foobar.bar();// "bar"

module.exports的值可以是任意 JavaScript 类型。

// foobar.js:

module.exports = {

FOO:function (){

this.type ="foo";

    },

bar:"bar"

};

// test.js:

var foobar =require("foobar");

var foo =newfoobar.FOO();

cc.log(foo.type);// "foo"

cc.log(foobar.bar);// "bar"

封装私有变量

每个脚本都是一个单独的作用域,在脚本内使用var定义的局部变量,将无法被模块外部访问。我们可以很轻松的封装模块内的私有变量:

// foobar.js:

var dirty =false;

module.exports = {

setDirty:function (){

dirty =true;

    },

isDirty:function (){

returndirty;

    },

};

// test1.js:

var foo =require("foobar");

cc.log(typeoffoo.dirty);// "undefined"

foo.setDirty();

// test2.js:

var foo =require("foobar");

cc.log(foo.isDirty());// true

循环引用

请参考属性延迟定义

第三方模块引用

请参考第三方模块引用文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270