cocos creator 摘星星范例

资源获取

下载资源,并从Dashboard中打开项目

目录说明

ProjectName(项目文件夹)
├──assets
	├──audio【音频文件夹】
	├──textures【材质贴图】
	├──mikado_outline_shadow

1,新建场景:

新建场景命名为game,并使用场景编辑器打开

2,了解Canvas

场景内会有一个Canvas节点(画布节点/渲染根节点)可以在属性检查其中修改宽高和缩放:场景中负责图像显示的节点都放在Canvas下,保证这些子节点随着Canvas一起缩放并适应屏幕大小

3,设置场景图像(美术部分)

添加背景:将背景图像拖拽到Canvas节点上,作为其子节点

修改背景尺寸:选中background节点,拖拽和调整属性检查器,调整大小

添加地面:将地面图像拖拽到Canvas节点上,作为子节点ground,并调整和背景图像的顺序关系

层级管理器中的从上到下的顺序,决定了渲染的顺序

(后渲染的图像会盖住先渲染的,或者说,顺序越靠下的节点越靠近玩家)

添加主角:拖拽主角的位图到Canvas下,作为子节点Player,排在ground下面

主角配置:修改锚点Anchor,把锚点设置在主角底部(Anchor熟悉的y设置为0)

锚点(0,0)表示在节点的左下角,(1,1)表示在右上角,(0.5,0.5)表示在节点的中心

4,脚本编写(功能部分)

引擎中建立scripts文件夹,新建Player脚本

cc.Class({					# 全局方法,参数是一个原型对象,用于创建类
    extends: cc.Component,	 # 继承自组件类
    properties: {			 # 相关属性
    },
    start () {

    },
});

在properties中,声明主角的属性

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

5,关联脚本

选择Player节点,添加组件,选择Player脚本

此时即可在属性检查其中查看组件的相关属性:跳跃高度,移动速度等……

6,控制代码

在Player脚本中添加方法,增加跳跃功能

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

    # 个人理解这个函数组装并返回了一个重复的跳跃缓动
    runJumpAction () {		# 跳跃方法
        // 跳跃上升
    	# 创建一个上升的缓动,y轴跳跃距离
        # sineOut:正选曲线缓出函数,由快到慢
        var jumpUp = cc.tween().by(this.jumpDuration, {y: this.jumpHeight}, {easing: 'sineOut'});
        // 下落
         # 创建一个下落的缓动
         # sineIn:正弦曲线缓入函数,运动由慢到快。
        var jumpDown = cc.tween().by(this.jumpDuration, {y: -this.jumpHeight}, {easing: 'sineIn'});

        // 创建一个缓动,按 jumpUp、jumpDown 的顺序执行动作
        # 将上升和下降合并成一个跳跃的缓动
        var tween = cc.tween().sequence(jumpUp, jumpDown)
        // 不断重复
        # 返回重复的跳跃缓动
        return cc.tween().repeatForever(tween);
    },

缓动系统cc.tween是cocos creator在2.0.9提供的新API,类似动作系统cc.Action,区别是cc.tween提供了链式创建的方法(按链式顺序),可以对任何对象操作,并对对象的任意属性进行缓动

简单来说,缓动系统cc.tween比动作系统cc.Action好

缓动系统的to和by:

to:对属性进行绝对值计算

by:对属性进行相对值计算

cc.tween(node)
  .to(1, {scale: 2})      // node.scale === 2
  .by(1, {scale: 2})      // node.scale === 4 (2 + 2)
  .by(1, {scale: 1})      // node.scale === 5
  .to(1, {scale: 2})      // node.scale === 2
  .start()

to和by的参数列表:

参数1:持续时间,数字Number类型

参数2:对象{scale: 2, position: cc.v3(100, 100, 100)},缩放和向量坐标对象

参数3:对象{progress:{自定义函数},easing:'缓动效果函数'}

7,键盘事件

添加键盘事件响应函数,onKeyUp和onKeyDown

// Player.js
    runJumpAction: function () {	# 在前面,这个函数返回了一个组装好的跳跃缓动
        //...
    },

    onKeyDown (event) {
        // 按键后,进行一个标记,a和d分别都进行标记
        switch(event.keyCode) {
            case cc.macro.KEY.a:
                this.accLeft = true;
                break;
            case cc.macro.KEY.d:
                this.accRight = true;
                break;
        }
    },

    onKeyUp (event) {
        // 放开按键后,取消标记
        switch(event.keyCode) {
            case cc.macro.KEY.a:
                this.accLeft = false;
                break;
            case cc.macro.KEY.d:
                this.accRight = false;
                break;
        }
    },

修改onLoad方法

// Player.js
    onLoad: function () {
        // 初始化跳跃动作
        # 加载之前设计的组装好的跳跃缓动
        var jumpAction = this.runJumpAction();
        # 用then将跳跃缓动插入到动作队列中
        cc.tween(this.node).then(jumpAction).start()

        // 加速度方向开关
        # 默认关掉
        this.accLeft = false;
        this.accRight = false;
        // 主角当前水平方向速度
        # 初始化为0
        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);
    },

修改update函数,增加设置,这个函数每帧调用一次

// Player.js
# 这个dt和其他引擎中的delta是一个意思
    update: function (dt) {
        // 根据当前加速度方向每帧更新速度
        if (this.accLeft) {
            # 左键a持续按下时,x轴的速度逐渐减少
            # accel是之前定义的加速度
            this.xSpeed -= this.accel * dt;
        }
        else if (this.accRight) {
            this.xSpeed += this.accel * dt;
        }

        // 限制主角的速度不能超过最大值
        if (Math.abs(this.xSpeed) > this.maxMoveSpeed) {
            # 检测速度的绝对值,如果超过设置的最大速度,则使用最大速度
            # 用当前速度除以当前速度的绝对值,即只留下了方向
            this.xSpeed = this.maxMoveSpeed * this.xSpeed / Math.abs(this.xSpeed);
        }

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

8,制作星星的预制体Prefab

重复生成的节点可以保存成Prefab预制体,用来动态生成

从资源管理器拖拽图片到场景即可生成一个节点,将节点拖回资源管理器即可

创建预制体的关联脚本

// Star.js
    properties: {
        // 星星和主角之间的距离小于这个数值时,就会完成收集
        pickRadius: 0,	# 后面根据素材的情况,设置为60
    },

9,控制脚本

创建Game脚本,并作为脚本组件添加到Canvas节点上

// Game.js
    properties: {
        // 这个属性引用了星星预制资源
        starPrefab: {
            default: null,
            type: cc.Prefab
        },

        // 星星产生后消失时间的随机范围
            # 星星持续时间范围
        maxStarDuration: 0,
        minStarDuration: 0,

        // 地面节点,用于确定星星生成的高度
        ground: {
            default: null,
            type: cc.Node
        },

        // Player 节点,用于获取主角弹跳的高度,和控制主角行动开关
        player: {
            default: null,
            type: cc.Node
        }
    },

属性声明常用的参数:

default:设置属性的默认值,这个默认值仅在组件第一次添加到节点上时才会用到
type:限定属性的数据类型,详见 CCClass 进阶参考:type 参数
visible:设为 false 时,不在 属性检查器 中显示该属性
serializable:设为 false 则不序列化(保存)该属性
displayName:在 属性检查器 中显示指定的属性名
tooltip:在 属性检查器 中添加属性的 tooltip

10,星星随机生成脚本(预制体)

修改Game脚本

// Game.js
    onLoad: function () {
        // 获取地平面的 y 轴坐标
        this.groundY = this.ground.y + this.ground.height/2;
        // 生成一个新的星星
        this.spawnNewStar();
    },

    spawnNewStar: function() {
        // 使用给定的模板在场景中生成一个新节点
        # cc.instantiate克隆指定的任意类型的对象,或者从 Prefab 实例化出新节点
        var newStar = cc.instantiate(this.starPrefab);
        // 将新增的节点添加到 Canvas 节点下面
        this.node.addChild(newStar);
        // 为星星设置一个随机位置
        newStar.setPosition(this.getNewStarPosition());
        // 在星星脚本组件上保存 Game 对象的引用
    	newStar.getComponent('Star').game = this;
    },

    getNewStarPosition: function () {
        var randX = 0;
        // 根据地平面位置和主角跳跃高度,随机得到一个星星的 y 坐标
        var randY = this.groundY + Math.random() * this.player.getComponent('Player').jumpHeight + 50;
        // 根据屏幕宽度,随机得到一个星星 x 坐标
        var maxX = this.node.width/2;
        randX = (Math.random() - 0.5) * 2 * maxX;
        // 返回星星坐标
        return cc.v2(randX, randY);
    },

在Star脚本中添加

// Star.js
    getPlayerDistance: function () {
        // 根据 Player 节点位置判断距离
        var playerPos = this.game.player.getPosition();
        // 根据两点位置计算两点之间距离
        var dist = this.node.position.sub(playerPos).mag();
        return dist;
    },

    onPicked: function() {
        // 当星星被收集时,调用 Game 脚本中的接口,生成一个新的星星
        this.game.spawnNewStar();
        // 然后销毁当前星星节点
        this.node.destroy();
    },
    # 每帧刷新更新距离,距离到达就调用收集函数
    update: function (dt) {
        // 每帧判断星星和主角之间的距离是否小于收集距离
        if (this.getPlayerDistance() < this.pickRadius) {
            // 调用收集行为
            this.onPicked();
            return;
        }
    },

11,得分界面

创建节点-创建渲染节点-Label(文字),名称修改为score,调整位置

在属性管理器中将组件的String属性填入Score:0,设置Font Size=50

拖拽位图字体资源到Label的Font属性中

在Game脚本中的属性中增加分数的引用

// Game.js
    properties: {
        // ...
        // score label 的引用
        scoreDisplay: {
            default: null,
            type: cc.Label
        }
    },
        # 积分初始化
    onLoad: function () {
        // ...
        // 初始化计分
        this.score = 0;
    },
        # 得分函数
    gainScore: function () {
        this.score += 1;
        // 更新 scoreDisplay Label 的文字
        this.scoreDisplay.string = 'Score: ' + this.score;
    },

在Star脚本中,修改onPicked方法

// Star.js
    onPicked: function() {
        // 当星星被收集时,调用 Game 脚本中的接口,生成一个新的星星
        this.game.spawnNewStar();

        // 调用 Game 脚本的得分方法
        this.game.gainScore();

        // 然后销毁当前星星节点
        this.node.destroy();
    },

添加星星计时消失的逻辑

在Game中添加计时需要的变量声明

// Game.js
    onLoad: function () {
        // ...

        // 初始化计时器
        this.timer = 0;
        this.starDuration = 0;

        // 生成一个新的星星
        this.spawnNewStar();

        // 初始化计分
        this.score = 0;
    },

增加重置计时器的逻辑

// Game.js
    spawnNewStar: function() {
        // ...

        // 重置计时器,根据消失时间范围随机取一个值
        this.starDuration = this.minStarDuration + Math.random() * (this.maxStarDuration - this.minStarDuration);
        this.timer = 0;
    },

加入计时器更新合判断是否超过时间限制的逻辑

// Game.js
    update: function (dt) {
        // 每帧更新计时器,超过限度还没有生成新的星星
        // 就会调用游戏失败逻辑
        if (this.timer > this.starDuration) {
            this.gameOver();
            return;
        }

        this.timer += dt;
    },

添加gameOver方法,用于失败后重新加载场景

// Game.js
    gameOver: function () {
        // 停止 Player 节点的跳跃动作
        this.player.stopAllActions(); 

        // 重新加载场景 game
        cc.director.loadScene('game');
    }

cc.director 是一个管理游戏逻辑流程的单例对象。由于 cc.director 是一个单例,所以您不需要调用任何构造函数或创建函数,使用它的标准方法是调用 cc.director.methodName()

添加星星的视觉效果

// Star.js
    update: function() {
        // ...

        // 根据 Game 脚本中的计时器更新星星的透明度
        var opacityRatio = 1 - this.game.timer/this.game.starDuration;
        var minOpacity = 50;
        this.node.opacity = minOpacity + Math.floor(opacityRatio * (255 - minOpacity));
    }

12,音效

在Player脚本的属性中添加引用声音文件资源的属性

// Player.js
    properties: {
        // ...

        // 跳跃音效资源
        jumpAudio: {
            default: null,
            type: cc.AudioClip
        },
    },

在Player的runJumpAction方法中插入播放音效的回调

// Player.js
    runJumpAction: function () {
        // 跳跃上升
        var jumpUp = cc.tween().by(this.jumpDuration, {y: this.jumpHeight}, {easing: 'sineOut'});

        // 下落
        var jumpDown = cc.tween().by(this.jumpDuration, {y: -this.jumpHeight}, {easing: 'sineIn'});

        // 创建一个缓动
        var tween = cc.tween()
                        // 按 jumpUp,jumpDown 的顺序执行动作
                        .sequence(jumpUp, jumpDown)
                        // 添加一个回调函数,在前面的动作都结束时调用我们定义的 playJumpSound() 方法
                        .call(this.playJumpSound, this);

        // 不断重复
        return cc.tween().repeatForever(tween);
    },

    playJumpSound: function () {
        // 调用声音引擎播放声音
        cc.audioEngine.playEffect(this.jumpAudio, false);
    },

在Game脚本中添加得分音效的引用属性

// Game.js
    properties: {
        // ...

        // 得分音效资源
        scoreAudio: {
            default: null,
            type: cc.AudioClip
        }
    },

添加播放音效的代码

// Game.js
    gainScore: function () {
        this.score += 1;
        // 更新 scoreDisplay Label 的文字
        this.scoreDisplay.string = 'Score: ' + this.score.toString();

        // 播放得分音效
        cc.audioEngine.playEffect(this.scoreAudio, false);
    },

完成,以及改进方向

  • 加入简单的开始菜单界面,在游戏运行的一开始显示开始按钮,点击按钮后才会开始游戏
  • 为游戏失败加入简单的菜单界面,游戏失败后点击按钮才会重新开始
  • 限制主角的移动不能超过视窗边界
  • 为主角的跳跃动作加入更细腻的动画表现
  • 为星星消失的状态加入计时进度条
  • 收集星星时加入更华丽的效果
  • 为触屏设备加入输入控制
posted @ 2021-10-23 10:29  景北斗  阅读(397)  评论(0编辑  收藏  举报