cocos creator 制作第一个游戏 官方教程(二) 主角脚本

分类栏目:cocos教程

324

上一节说的是场景的布置,下面说下主角的基本脚本

Cocos Creator 开发游戏的一个核心理念就是让内容生产和功能开发可以流畅的并行协作,我们在上个部分着重于处理美术内容,而接下来就是通过编写脚本来开发功能的流程,之后我们还会看到写好的程序脚本可以很容易的被内容生产者使用。

如果您从没写过程序也不用担心,我们会在教程中提供所有需要的代码,只要复制粘贴到正确的位置就可以了,之后这部分工作可以找您的程序员小伙伴来解决。下面让我们开始创建驱动主角行动的脚本吧。

创建脚本

  1. 首先在 资源管理器 中右键点击 assets 文件夹,选择 新建 -> 文件夹

    new folder

  2. 右键点击 New Folder,选择 重命名 将其重命名为 scripts,之后我们所有的脚本都会存放在这里
  3. 右键点击 scripts 文件夹,选择 新建 -> JavaScript,创建一个 JavaScript 脚本
  4. 将新建脚本的名字改为 Player,双击这个脚本,打开代码编辑器

注意: Cocos Creator 中脚本名称就是组件的名称,这个命名是大小写敏感的!如果组件名称的大小写不正确,将无法正确通过名称使用组件!

编写组件属性

在打开的 Player 脚本里已经有了预先设置好的一些代码块,如下所示:

cc.Class({
    extends: cc.Component,

    properties: {
        // foo: {
        //     // ATTRIBUTES:
        //     default: null,        // The default value will be used only when the component attaching
        //                           // to a node for the first time
        //     type: cc.SpriteFrame, // optional, default is typeof default
        //     serializable: true,   // optional, default is true
        // },
        // bar: {
        //     get () {
        //         return this._bar;
        //     },
        //     set (value) {
        //         this._bar = value;
        //     }
        // },
    },
    // LIFE-CYCLE CALLBACKS:

    // onLoad () {},

    start () {

    },
    // update (dt) {},
});

我们来大概了解一下这些代码的作用。首先我们可以看到一个全局的 cc.Class() 方法,什么是 cc呢?cc 是 Cocos 的简称,Cocos 引擎的主要命名空间,引擎代码中所有的类、函数、属性和常量都在这个命名空间中定义。而 Class() 就是 cc 模块下的一个方法,这个方法用于声明 Cocos Creator 中的类。为了方便区分,我们把使用 cc.Class 声明的类叫做 CCClass。Class() 方法的参数是一个原型对象,在原型对象中以键值对的形式设定所需的类型参数,就能创建出所需要的类。

例如:

    var Sprite = cc.Class({
        name: "sprite"
    });

以上代码用 cc.Class() 方法创建了一个类型,并且赋给了 Sprite 变量。同时还将类名设为 sprite。类名用于序列化,一般可以省略。

对于 cc.Class 的详细学习可以参考 使用 cc.Class 声明类型

现在我们回到脚本编辑器看回之前的代码,这些代码就是编写一个组件(脚本)所需的结构。具有这样结构的脚本就是 Cocos Creator 中的 组件(Component),他们能够挂载到场景中的节点上,提供控制节点的各种功能。我们先来设置一些属性,然后看看怎样在场景中调整他们。

找到 Player 脚本里的 properties 部分,将其改为以下内容并保存:

// Player.js
    //...
    properties: {
        // 主角跳跃高度
        jumpHeight: 0,
        // 主角跳跃持续时间
        jumpDuration: 0,
        // 最大移动速度
        maxMoveSpeed: 0,
        // 加速度
        accel: 0,
    },
    //...

Cocos Creator 规定一个节点具有的属性都需要写在 properties 代码块中,这些属性将规定主角的移动方式,在代码中我们不需要关心这些数值是多少,因为我们之后会直接在 属性检查器 中设置这些数值。以后在游戏制作过程中,我们可以将需要随时调整的属性都放在 properties 中。

现在我们可以把 Player 组件添加到主角节点上。在 层级管理器 中选中 Player 节点,然后在 属性检查器 中点击 添加组件 按钮,选择 添加用户脚本组件 -> Player,为主角节点添加 Player 组件。

add player component

现在我们可以在 属性检查器 中(需要选中 Player 节点)看到刚添加的 Player 组件了,按照下图将主角跳跃和移动的相关属性设置好:

player properties

这些数值除了 jumpDuration 的单位是秒之外,其他的数值都是以像素为单位的,根据我们现在对 Player组件的设置:我们的主角将能够跳跃 200 像素的高度,起跳到最高点所需的时间是 0.3 秒,最大水平方向移动速度是 400 像素每秒,水平加速度是 350 像素每秒。

这些数值都是建议,一会等游戏运行起来后,您完全可以按照自己的喜好随时在 属性检查器 中修改这些数值,不需要改动任何代码。

编写跳跃和移动代码

下面我们添加一个方法,来让主角跳跃起来,在 properties: {...}, 代码块的下面,添加叫做 setJumpAction 的方法:

// Player.js
    properties: {
        //...
    },

    setJumpAction: function () {
        // 跳跃上升
        var jumpUp = cc.moveBy(this.jumpDuration, cc.v2(0, this.jumpHeight)).easing(cc.easeCubicActionOut());
        // 下落
        var jumpDown = cc.moveBy(this.jumpDuration, cc.v2(0, -this.jumpHeight)).easing(cc.easeCubicActionIn());
        // 不断重复
        return cc.repeatForever(cc.sequence(jumpUp, jumpDown));
    },

这里就需要了解一下 Cocos Creator 的 动作(Action)系统 了。由于动作系统比较复杂,这里就简单的介绍一下。

在 Cocos Creator 中,动作 简单来说就是 节点的位移、缩放和旋转。

例如在上面的代码中,moveBy() 方法的作用是在规定的时间内移动指定的一段距离,第一个参数就是我们之前定义主角属性中的跳跃时间,第二个参数是一个 Vec2(表示 2D 向量和坐标)类型的对象,为了更好的理解,我们可以看看官方给的函数说明:

/**
 * !#en
 * Moves a Node object x,y pixels by modifying its position property.                                  <br/>
 * x and y are relative to the position of the object.                                                 <br/>
 * Several MoveBy actions can be concurrently called, and the resulting                                <br/>
 * movement will be the sum of individual movements.
 * !#zh 移动指定的距离。
 * @method moveBy
 * @param {Number} duration duration in seconds
 * @param {Vec2|Number} deltaPos
 * @param {Number} [deltaY]
 * @return {ActionInterval}
 * @example
 * // example
 * var actionTo = cc.moveBy(2, cc.v2(windowSize.width - 40, windowSize.height - 40));
 */
cc.moveBy = function (duration, deltaPos, deltaY) {
    return new cc.MoveBy(duration, deltaPos, deltaY);
};

可以看到,方法 moveBy 一共可以传入三个参数,前两个参数我们已经知道,第三个参数是 Number 类型的 Y 坐标,我们可以发现第二个参数是可以传入两种类型的,第一种是 Number 类型,第二种才是 Vec2类型,如果我们在这里传入的是 Number 类型,那么默认这个参数就是 X 坐标,此时就要填第三个参数,为 Y 坐标。上面的例子中 cc.moveBy(this.jumpDuration, cc.v2(0, this.jumpHeight)) 第二个参数传入的是使用 cc.v2 方法构建的 Vec2 类型对象,这个类型表示的是一个坐标,即有 X 坐标也有 Y 坐标,因为不需要再传入第三个参数!同时注意官方的一段话 x and y are relative to the position of the object.,这句话的意思是传入的 X、Y 坐标都是相对于节点当前的坐标位置,而不是整个坐标系的绝对坐标。

了解了参数的含义之后,我们再来关注 moveBy() 方法的返回值,看官方说明可以知道,这个方法返回的是一个 ActionInterval 类型的对象,ActionInterval 在 Cocos 中是一个表示时间间隔动作的类,这种动作在一定时间内完成。到这里我们就可以理解代码 cc.moveBy(this.jumpDuration, cc.v2(0, this.jumpHeight)).easing(cc.easeCubicActionOut()) 前一部分 的意思了,它的意思就是构造一个 ActionInterval 类型的对象,这个对象表示在 jumpDuration 的时间内,移动到相对于当前节点的 (0,this.jumpHeight) 的坐标位置,简单来说,就是一个向上跳跃的动作。

那么 后半部分 easing(cc.easeCubicActionOut()) 的作用是什么呢?easing 是 ActionInterval 类下的一个方法,这个方法可以让时间间隔动作呈现为一种缓动运动,传入的参数是一个缓动对象,返回一个 ActionInterval 类型对象,这里传入的是使用 easeCubicActionInOut 方法构建的缓动对象,EaseCubicInOut 是按三次函数缓动进入并退出的动作,具体曲线可参考下图:

详细内容可参考 API

接下来在 onLoad 方法里调用刚添加的 setJumpAction 方法,然后执行 runAction 来开始动作:

// Player.js
    onLoad: function () {
        // 初始化跳跃动作
        this.jumpAction = this.setJumpAction();
        this.node.runAction(this.jumpAction);
    },

onLoad 方法会在场景加载后立刻执行,所以我们会把初始化相关的操作和逻辑都放在这里面。我们首先将循环跳跃的动作传给了 jumpAction 变量,之后调用这个组件挂载的节点下的 runAction 方法,传入循环跳跃的 Action 从而让节点(主角)一直跳跃。保存脚本,然后我们就可以开始第一次运行游戏了!

点击 Cocos Creator 编辑器上方正中的 预览游戏 按钮 preview,Cocos Creator 会自动打开您的默认浏览器并开始在里面运行游戏,现在应该可以看到我们的主角——紫色小怪兽在场景中间活泼的蹦个不停了。

移动控制

只能在原地傻蹦的主角可没前途,让我们为主角添加键盘输入,用 A 和 D 来控制他的跳跃方向。在 setJumpAction 方法的下面添加键盘事件响应函数:

// Player.js
    setJumpAction: function () {
        //...
    },

    onKeyDown (event) {
        // set a flag when key pressed
        switch(event.keyCode) {
            case cc.macro.KEY.a:
                this.accLeft = true;
                break;
            case cc.macro.KEY.d:
                this.accRight = true;
                break;
        }
    },

    onKeyUp (event) {
        // unset a flag when key released
        switch(event.keyCode) {
            case cc.macro.KEY.a:
                this.accLeft = false;
                break;
            case cc.macro.KEY.d:
                this.accRight = false;
                break;
        }
    },

然后修改 onLoad 方法,在其中加入向左和向右加速的开关,以及主角当前在水平方向的速度。最后再调用 cc.systemEvent,在场景加载后就开始监听键盘输入:

// Player.js
    onLoad: function () {
        // 初始化跳跃动作
        this.jumpAction = this.setJumpAction();
        this.node.runAction(this.jumpAction);

        // 加速度方向开关
        this.accLeft = false;
        this.accRight = false;
        // 主角当前水平方向速度
        this.xSpeed = 0;

        // 初始化键盘输入监听
        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);
        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this);   
    },

    onDestroy () {
        // 取消键盘输入监听
        cc.systemEvent.off(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);
        cc.systemEvent.off(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this);
    },

有 Android 开发经验的同学比较好理解,这里的监听器实质上就和 Android 里的 OnClickListener 差不多,在 cocos 中通过 systemEvent 来监听系统 全局 事件。(鼠标、触摸和自定义事件的监听和派发的详细内容请参考 监听和发射事件。)这里通过向 systemEvent 注册了一个键盘响应函数,在函数中通过 switch 判断键盘上的 A 和 D 是否被按下或松开,若按下就执行对应的操作。

最后修改 update 方法的内容,添加加速度、速度和主角当前位置的设置:

// Player.js
    update: function (dt) {
        // 根据当前加速度方向每帧更新速度
        if (this.accLeft) {
            this.xSpeed -= this.accel * dt;
        } else if (this.accRight) {
            this.xSpeed += this.accel * dt;
        }
        // 限制主角的速度不能超过最大值
        if ( Math.abs(this.xSpeed) > this.maxMoveSpeed ) {
            // if speed reach limit, use max speed with current direction
            this.xSpeed = this.maxMoveSpeed * this.xSpeed / Math.abs(this.xSpeed);
        }

        // 根据当前速度更新主角的位置
        this.node.x += this.xSpeed * dt;
    },

update 在场景加载后就会每帧调用一次,我们一般把需要经常计算或及时更新的逻辑内容放在这里。在我们的游戏中,根据键盘输入获得加速度方向后,就需要每帧在 update 中计算主角的速度和位置。

保存脚本后,点击 预览游戏 来看看我们最新的成果。在浏览器打开预览后,用鼠标点击一下游戏画面(这是浏览器的限制,要点击游戏画面才能接受键盘输入),然后就可以按 A 和 D 键来控制主角左右移动了!

感觉移动起来有点迟缓?主角跳的不够高?希望跳跃时间长一些?没问题,这些都可以随时调整。只要为 Player 组件设置不同的属性值,就可以按照您的想法调整游戏。这里有一组设置可供参考:

Jump Height: 150
Jump Duration: 0.3
Max Move Speed: 400
Accel: 1000

这组属性设置会让主角变得灵活无比,至于如何选择,就看您想做一个什么风格的游戏了。