CocosCreator学习1:做一个简单的游戏

把计步器写好了,到了写游戏场景、控件什么的时候,傻眼了。想做一个简单的地图,可以在地图上点击选择城市,发现用Cocos2D-X代码码出来好麻烦,尤其是城市位置问题,需要调试去找对像素区域做一个按钮控制,整个人都不好了。本来还想尝试用Tiled map来做,想法很逗比,应该不能用。今晚被长辈连番教育中,说到了撞墙走弯路才想起来,当初光顾着学引擎看代码了,竟然忘了还有CocosCreator,这么简单实用的工具我竟然没有去用,太傻了。而且现在距离比较提交程序结果时间也比较紧了,直接用CocosCreator做游戏时最明智的。下面将转向CocosCreator的学习。

CocosCreator有很详细的入门教程,下面我就跟随这个教程来进行学习。
http://www.cocos.com/docs/creator/getting-started/quick-start.html#–2

打开项目

教程中提供了一个简单游戏的初始版本,我就从这个游戏入手进行学习。从CocosCreator选择打开其他项目,打开start_project文件夹,选中即可打开。注意下载的start_project不能放在CocosCreator路径下。

打开后如图:
CocosCreator学习1:做一个简单的游戏_第1张图片

在资源管理器面板中,可以看到项目中的文件。项目资源的根目录叫asserts,对应于解压初始版本得到的文件夹名。其中标注bf图标的文件是位图字体,是一种字体资源,由fnt文件和同名的png图片文件共同组成。

创建场景

下面来创建游戏场景。游戏场景一般包括:场景图像(Sprite)、文字(Label)、角色、以组件形式附加在场景节点上的游戏逻辑脚本。在资源管理器中点击asserts目录前面的加号,选择Scene,会创建一个New Scene的场景文件。
CocosCreator学习1:做一个简单的游戏_第2张图片
双击New Scene,就会在场景编辑器和层级管理器中打开这个场景。在层级管理器中可以看到当前场景的所有节点和他们的层级关系。

Canvas画布节点/渲染根节点

现在New Scene中只有一个Canvas节点,称为画布节点或渲染根节点,点击选中Canvas可以在属性检查器中看到他的属性。
CocosCreator学习1:做一个简单的游戏_第3张图片
CocosCreator学习1:做一个简单的游戏_第4张图片
其中的Design Resolution属性用来指定游戏分辨率,Fit Hight和Fit Width是分辨率自适应匹配方法。只需要把图像放到Canvas下,就可以完成对不同分辨率的自适应,非常简单。

设置场景图像

增加背景图像

在资源管理器中将textures中background图像拖到
层级管理器的Canvas下即可增加背景图像,会看到Canvas下增加了一个background子节点。
CocosCreator学习1:做一个简单的游戏_第5张图片
点击文件->保存场景或使用快捷键ctrl+s保存场景后,在场景编辑器中便会显示增加的背景图像。
CocosCreator学习1:做一个简单的游戏_第6张图片
点击窗口左上角工具栏的第四格矩形变换工具,然后选中场景编辑器中的背景图像,可以实现对图像尺寸、位置、锚点的修改。
CocosCreator学习1:做一个简单的游戏_第7张图片
当然也可以直接使用属性检查器来直接设置这些参数。
在实际的应用开发中背景图像一般都是要大于显示范围的,这样可以让背景图覆盖整个屏幕,不出现穿帮的情况。

增加地面
使用和增加背景图像相同的方法,将地面图像拖到层级管理器的Canvas上,将ground节点移到background节点下方。
CocosCreator学习1:做一个简单的游戏_第8张图片
在层级管理器中,下方的节点的渲染顺序在上方节点之后,也就是说下方节点会遮挡上方节点。
CocosCreator学习1:做一个简单的游戏_第9张图片
使用相同的方法可以对地面图像的尺寸、位置、锚点进行修改。

增加主角
同样的方法,将主角图像player拖到ground下方。在之后的动画中,需要主角接触地面并跳跃,因此将锚点改为最下方,即y值为0,然后将它拖到地面上。
CocosCreator学习1:做一个简单的游戏_第10张图片

编写脚本

创建并添加脚本
脚本也就是游戏的逻辑规则,需要代码实现。
首先在资源管理器中右键点击assets文件夹,新建->文件夹,创建一个名为New Folder的文件夹,然后对其右击选择新建->JavaScript,创建一个JavaScript脚本NewScript,重命名为Player,双击即可打开编写界面。Cocos Creator 中脚本名称就是组件的名称。
在Player中的properties部分增加以下代码:

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

这些属性用于规定主角的移动方式,可以在属性检查器中直接设置属性的数值。

然后在层级编辑器中选择player,在属性检查器中选择添加组件,选择增加用户脚本组件->Player,即可为主角添加组件。

现在可以在属性检查器看到Player组件并修改属性数值了。
CocosCreator学习1:做一个简单的游戏_第11张图片

跳跃和移动代码
在properties代码块下面定义setJumpAction方法:

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

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

修改onLoad: function:

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

onLoad方法会在场景加载完成后立即执行,所以在这里进行初始化操作。
保存脚本运行一下试试,可以看到player不断的在进行跳跃动作。

在setJumpAction下面定义setInputControl方法来实现用A和D控制主角的移动操作:

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

    setInputControl: function () {
        var self = this;
        // 添加键盘事件监听
        cc.eventManager.addListener({
            event: cc.EventListener.KEYBOARD,
            // 有按键按下时,判断是否是我们指定的方向控制键,并设置向对应方向加速
            onKeyPressed: function(keyCode, event) {
                switch(keyCode) {
                    case cc.KEY.a:
                        self.accLeft = true;
                        self.accRight = false;
                        break;
                    case cc.KEY.d:
                        self.accLeft = false;
                        self.accRight = true;
                        break;
                }
            },
            // 松开按键时,停止向该方向的加速
            onKeyReleased: function(keyCode, event) {
                switch(keyCode) {
                    case cc.KEY.a:
                        self.accLeft = false;
                        break;
                    case cc.KEY.d:
                        self.accRight = false;
                        break;
                }
            }
        }, self.node);
    },

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

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

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

        // 初始化键盘输入监听
        this.setInputControl();
    },

最后修改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;
    },

这样就可以实现对主角移动的控制了。

制作Prefab脚本-星星
这里增加游戏规则,从随机位置生成星星,用主角接住星星即可得分。
对于重复生成的节点,可以保存成Prefab(预置)资源,作为动态生成节点时的模板。
这里直接将assets/textures/star资源到场景中,位置随意,给星星增加一个脚本,当主角碰到星星时,让星星消失。

// Star.js
    properties: {
        // 星星和主角之间的距离小于这个数值时,就会完成收集
        pickRadius: 0
    },

给star节点增加脚本,将pickRadius值设置为60。将层级管理器中的star节点拖到资源管理器的assets文件夹下,将场景中的star节点删除。star现在即为Prefab资源。

添加游戏控制脚本
在assets/scripts文件夹下添加游戏的主逻辑脚本Game,添加生成星星需要的属性:

// 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
        }
    },

然后将Game添加到Canvas节点上,然后将star这个Prefab资源拖到Canvas属性检查器Game组件中的Star Prefab属性上。把层级管理器中的ground和Player节点也拖到组件中相应属性处即可。设置Min Star Duration和Max Star Duration值为3和5,之后生成星星时,会在这两个值之间随机取值作为星星消失前经过的时间。

在随机位置生成星星
修改Game脚本,在onLoad方法后面添加星星的逻辑:

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

    spawnNewStar: function() {
        // 使用给定的模板在场景中生成一个新节点
        var newStar = cc.instantiate(this.starPrefab);
        // 将新增的节点添加到 Canvas 节点下面
        this.node.addChild(newStar);
        // 为星星设置一个随机位置
        newStar.setPosition(this.getNewStarPosition());
    },

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

添加主角碰触收集星星的行为
重点在于星星要随时获取主角节点的位置,才能判断与主角的距离是否小于可收集距离。因此将Game组件的实例传给星星并保存,然后可以随时通过game.player来访问主角节点。
在Game脚本的spawnNewStar方法最后添加如下代码:

// Game.js
    spawnNewStar: function() {
        // ...
        // 将 Game 组件的实例传入星星组件
        newStar.getComponent('Star').game = this;
    },

然后在star脚本里onLoad方法后面添加getPlayerDistance和onPicked方法:

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

    onPicked: function() {
        // 当星星被收集时,调用 Game 脚本中的接口,生成一个新的星星
        this.game.spawnNewStar();
        // 然后销毁当前星星节点
        this.node.destroy();
    },

在update方法中添加每帧判断距离,如果距离小于pickRadius属性规定值,就执行收集行为:

// Star.js
    update: function (dt) {
        // 每帧判断和主角之间的距离是否小于收集距离
        if (this.getPlayerDistance() < this.pickRadius) {
            // 调用收集行为
            this.onPicked();
            return;
        }
    },

现在可以看效果了。
CocosCreator学习1:做一个简单的游戏_第12张图片

添加计分牌
在层级管理器的Canvas中右键创建新节点->创建渲染节点->Label(文字),命名为score。将position设为(0,180),string设为score:0,Font Size设为50。从资源管理器中将assets/mikado_outline_shadow位图字体资源拖到Font属性中。
CocosCreator学习1:做一个简单的游戏_第13张图片

添加得分逻辑
在Game的properties里添加score需要的属性:

// Game.js
    properties: {
        // ...
        // score label 的引用
        scoreDisplay: {
            default: null,
            type: cc.Label
        }
    },

在onLoad方法里添加计分用的变量初始化:

// Game.js
    onLoad: function () {
        // ...
        // 初始化计分
        this.score = 0;
    },

在update方法后面添加gainScore的新方法:

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

将score节点拖到Canvas属性的Game组件中的Score Display属性中。
在Star脚本的onPicked方法中添加gainScore的调用:

// Star.js
    onPicked: function() {
        // 当星星被收集时,调用 Game 脚本中的接口,生成一个新的星星
        this.game.spawnNewStar();
        // 调用 Game 脚本的得分方法
        this.game.gainScore();
        // 然后销毁当前星星节点
        this.node.destroy();
    },

这样收集到星星,分数就会变化了。
CocosCreator学习1:做一个简单的游戏_第14张图片

失败判断和重新开始
加入给星星加入计时消失的逻辑,在Game脚本的onLoad方法的spawNewStar调用之前加入需要的变量声明:
// Game.js
onLoad: function () {
// …
// 初始化计时器
this.timer = 0;
this.starDuration = 0;
// 生成一个新的星星
this.spawnNewStar();
// 初始化计分
this.score = 0;
},
然后在spawnNewStar方法最后加入重置计时器的逻辑,其中this.minStarDuration和this.maxStarDuration是我们一开始声明的Game组件属性,用来规定星星消失时间的随机范围:

// Game.js
    spawnNewStar: function() {
        // ...
        // 重置计时器,根据消失时间范围随机取一个值
        this.starDuration = this.minStarDuration + cc.random0To1() * (this.maxStarDuration - this.minStarDuration);
        this.timer = 0;
    },

在update方法中加入计时器更新和判断超过时限的逻辑:

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

最后加入gameOver方法,游戏失败重新加载场景。

// Game.js
    gameOver: function () {
        this.player.stopAllActions(); //停止 player 节点的跳跃动作
        cc.director.loadScene('game');
    }

然后修改Star脚本,加入即将消失星星的提示效果,在update方法中加入以下代码:

// 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));
    }

这样游戏画面和逻辑就全部完成了,可以说游戏已经基本完成了。

加入音效
首先加入跳跃音效,在Player脚本中引用声音文件资源jumpAudio属性:

// Player.js
    properties: {
        // ...
        // 跳跃音效资源
        jumpAudio: {
            default: null,
            url: cc.AudioClip
        },
    },

修改setJumpAction方法,插入播放音效的回调,并通过添加playJumpSound方法来播放声音:

// Player.js
    setJumpAction: function () {
        // 跳跃上升
        var jumpUp = cc.moveBy(this.jumpDuration, cc.p(0, this.jumpHeight)).easing(cc.easeCubicActionOut());
        // 下落
        var jumpDown = cc.moveBy(this.jumpDuration, cc.p(0, -this.jumpHeight)).easing(cc.easeCubicActionIn());
        // 添加一个回调函数,用于在动作结束时调用我们定义的其他方法
        var callback = cc.callFunc(this.playJumpSound, this);
        // 不断重复,而且每次完成落地动作后调用回调来播放声音
        return cc.repeatForever(cc.sequence(jumpUp, jumpDown, callback));
    },

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

然后加入得分音效,在Game脚本的properties添加引用声音文件:

// Game.js
    properties: {
        // ...
        // 得分音效资源
        scoreAudio: {
            default: null,
            url: cc.AudioClip
        }
    },

在gainScore方法添加播放声音的代码:

// Game.js
    gainScore: function () {
        this.score += 1;
        // 更新 scoreDisplay Label 的文字
        this.scoreDisplay.string = 'Score: ' + this.score.toString();
        // 播放得分音效
        cc.audioEngine.playEffect(this.scoreAudio, false);
    },

在层级管理器中,将assets/audio/jump声音资源拖到Player节点的组件Jump Audio属性上。将assets/audio/score资源拖拽到Canvas节点Game组件的Score Audio属性上。
大功告成,一个完整的游戏就做好了。

你可能感兴趣的:(cocos2d-x,Cocos2D-X)