CocosCreator经典入门项目之flappybird

开发环境

CocosCreator v2.3.1

node.js v10.16.0

vscode 1.46.1

游戏引擎概念

可以理解为一套已经编写好的代码,它封装了对底层接口的使用,是游戏开发的核心功能提供者。

一般分为6各部分:

  1. 图像渲染:控制电脑对游戏绘画的绘画操作,直接影响游戏质量
  2. 音频UI:提供音频特效,以及游戏UI部分,让游戏与用户交互更好
  3. 设备输入:键盘、鼠标、陀螺仪等
  4. 脚本引擎:提供脚本接口,为游戏开发者提供“笔墨”
  5. 网络引擎:数据交互模块,用服务器为客户端提供交互
  6. 物理引擎(高级):模拟现实的物理效果(重力加速度、物体间的碰撞等)。

关于Cocos Creator

项目结构

ProjectName(项目文件夹)
├──assets 资源文件夹----------用来放置游戏中所有的本地资源、脚本和第三方库文件
├──library 资源库----------------这里文件的结构和资源格式将被处理成最终游戏发布时需要的形式
├──local 本地设置-------------存放项目本机删的配置信息(编辑器面板布局、窗口大小、位置等)
├──packages 扩展插件文件夹—存放项目的自定义扩展插件
├──settings 项目设置-------------保存项目的相关设置,如构建发布菜单里的包名、场景和平台选择
├──temp 临时文件夹----------用于缓存CocosCreator在本地的临时文件
└──project.json 验证文件-------------作为验证CocosCreator项目合法性的标志

下面开始进入真正的项目上手

配置代码编辑环境

Visual Studio Code (以下简称 VS Code)是微软新推出的轻量化跨平台 IDE,支持 Windows、Mac、Linux 平台,安装和配置非常简单。使用 VS Code 管理和编辑项目脚本代码,可以轻松实现语法高亮、智能代码提示等功能。

安装 Cocos Creator API 适配插件

该操作会将 Cocos Creator API 适配插件安装到 VS Code 全局的插件文件夹中,安装成功后在 控制台 会显示绿色的提示:VS Code extension installed to ...。这个插件的主要功能是为 VS Code 编辑状态下注入符合 Cocos Creator 组件脚本使用习惯的语法提示。

在项目中生成智能提示数据

如果希望在代码编写过程中自动提示 Cocos Creator 引擎 API,需要通过菜单生成 API 智能提示数据并自动放进项目路径下。

选择主菜单的 开发者 -> VS Code 工作流 -> 更新 VS Code 智能提示数据。该操作会将根据引擎 API 生成的 creator.d.ts 数据文件复制到项目根目录下(注意是在 assets 目录外面),操作成功时会在 控制台 显示绿色提示:API data generated and copied to ...

使用 VS Code 激活脚本编译

使用外部文本编辑器修改项目脚本后,要重新激活 Cocos Creator 窗口才能触发脚本编译,我们在新版本的 Creator 中增加了一个预览服务器的 API,可以通过向特定地址发送请求来激活编辑器的编译。

新建项目

1.新建一个空白项目

2.资源管理器

然后你需要在资源管理中创建项目中最重要场景、脚本文件,以及导入游戏所需要的纹理(图片资源)。这里的文件夹不是默认创建的,你需要手动创建,便于管理你的项目。需要说明的是resources是CocosCreator(以下简述cocos)中特殊的资源文件夹,所有需要通过cc.loader.loadRes动态加载(后续会提到这个方法)的资源,都必须放置在resources文件夹和它的子文件夹下。如果一份资源仅仅是被resources中的其他资源所依赖,而不需要直接被cc.loader.loadRes调用,就不需要放在resources文件夹里。

3.场景

在scenes中新建场景,scenes–右键–新建scenes场景,在 Cocos Creator 中,游戏场景(Scene) 是开发时组织游戏内容的中心,也是呈现给玩家所有游戏内容的载体。游戏场景中一般会包括以下内容:

  • 场景图像和文字(Sprite,Label)
  • 角色
  • 以组件形式附加在场景节点上的游戏逻辑脚本

当玩家运行游戏时,就会载入游戏场景,游戏场景加载后就会自动运行所包含组件的游戏脚本,实现各种各样开发者设置的逻辑功能。所以除了资源以外,游戏场景是一切内容创作的基础。现在,让我们来新建一个场景。入门项目flappybird只需要新建一个场景,你完成这个项目后的效果大致是这个亚子。

4.场景编辑器、层级管理器、属性检查器

双击你所创建的bird场景,cocos就会在 场景编辑器层级管理器 中打开这个场景。打开场景后, 层级管理器 中会显示当前场景中的所有节点和它们的层级关系。我们刚刚新建的场景中只有一个名叫 Canvas 的节点,Canvas 可以被称为画布节点或渲染根节点,点击选中 Canvas,可以在 属性检查器 中看到他的属性。

从资源包里面的texture目录下将名为的sky背景图片拖到Canvas中,作为游戏背景。调整Canvas和sky的size尺寸大小。sky的size至少要大于Canvas,不然你制作的游戏可能会有很大的黑边。然后用相似的方法把bird0(其他两张是为了配合作出简易帧动画,模拟小鸟的飞行)、pipe1(下管道)、pipe2(上管道)添加到Canvas下。上下管道为一组,我复制了4组,一共5组。通过脚本控制背景和每组管道向左移动来达到小鸟持续向前飞行的效果。

5.节点绑定

需要注意,Canvas下的元素都是以node节点的形式来被管理的。在script中新建脚本文件—game.js,将其拖入Canvas中,或者直接绑定到Canvas上。确保在加载场景时脚本被一并加载。

6.生命周期回调

Cocos Creator 为组件脚本提供了生命周期的回调函数。用户只要定义特定的回调函数,Creator 就会在特定的时期自动执行相关脚本,用户不需要手工调用它们。

目前提供给用户的生命周期回调函数主要有:

  • onLoad onLoad 回调会在节点首次激活时触发,比如所在的场景被载入,或者所在节点被激活的情况下。而且onLoad 总是会在任何 start 方法调用前执行,通常我们会在 onLoad 阶段去做一些初始化相关的操作。
  • start start 回调函数会在组件第一次激活前,也就是第一次执行 update 之前触发。start 通常用于初始化一些需要经常修改的数据,这些数据可能在 update 时会发生改变。
  • update 游戏开发的一个关键点是在每一帧渲染前更新物体的行为,状态和方位。这些更新操作通常都放在 update 回调中。以下四个回调函数在此项目中不会用到
  • lateUpdate
  • onDestroy
  • onEnable
  • onDisable

主要代码

game.js

cc.Class({
    extends: cc.Component,

    properties: {
        skyNode: cc.Node,//定义天空节点
        pipeNode: cc.Node,//定义管道节点
        birdNode: cc.Node,//定义小鸟节点
        clickLayerNode: cc.Node,//定义监听节点  监听鼠标点击事件
        scoreNode: cc.Node,//定义得分节点    总得分节点
        buttonNode: cc.Node,//定义按钮节点   开始游戏按钮
        numberNode: cc.Node,//定义数字节点   加分combo
        overNode: cc.Node,//定义游戏结束节点  结束按钮
        spriteFrame: {//定义精灵框架节点,
            default: [],//数组类型,将会绑定bird0、bird1、bird2三张图片精灵,通过在update()方法中不断更换,形成动画
            type: cc.SpriteFrame //图片精灵类型
        },
        clip: {//定义音效节点
            default: [],//同样为数组类型,便于绑定多个资源。后续学习,可尝试使用动态加载
            type: cc.AudioClip  //音频类型
        }
    },
    onClickButton() {//设置点击按钮方法
        this.num = 0;//将num重置为0
        this.sign = true;//设置控制游戏是否继续的标识符为真
        this.buttonNode.active = false;//让按钮节点不可见
        this.overNode.active = false;//让控制“游戏结束”文本的overNode节点不可见
        this.birdNode.y = 50;//点击按钮后小鸟的位置归位
        this.power = 0;//将力量因素变为0,防止小鸟复活后下落太快
        this.scoreNode.getComponent(cc.Label).string = "" + this.num;//将分数节点的string值变为0,this.scoreNode.getComponent(cc.Label).string
        let list = this.pipeNode.children;//用一个list存储金属管道的子节点(.children)
        for (let i = 0; i < list.length; i++) {//设置一个循环,终止条件是i小于list的长度
            let child = list[i];//let 一个child变量用于在循环中存储每个list[i]
            child.x += 1000;//将管道节点的x右移1000
        }
        cc.audioEngine.playMusic(this.clip[0], true);
    },
    onClickBagButton(event, data) {  //定义背包按钮方法
        // cc.log(event, data);
        if (data == "bag") {//判断传过来的参数等于某event的CustomEventData
            this.showBag();  // 调用显示背包函数
        }
    },
    showBag() {  //定义显示背包函数
        if (this.bagPrefab == null) {  //如果资源加载没有成功
            setTimeout(() => {  //设置延时0.5s后继续调用显示bag方法
                this.showBag();
            }, 500);
            return;
        }
        //资源加载完成
        let node = null;    //定义一个node赋值为空
        if(this.isOpen){  //判断背包是否打开,this.isOpen初始值为false
            node = cc.find("Canvas/panelBag");
            node.active = true;
        }
        else{
            node = cc.instantiate(this.bagPrefab);  //加载具体的预置资源并赋值给node
            cc.find("Canvas").addChild(node);  //将node节点添加到Canvas幕布下
        }
        node.opacity = 0;//设置node节点的透明度为0;
        node.scale = 0.1;//设置node节点的初始缩放为0.1;
        let ac = cc.spawn(  //封装并行的动画并赋值给ac
            cc.fadeIn(0.5),  //0.5s的速度淡入
            cc.scaleTo(0.5,1),//0.5s的速度缩放到1
        );
        node.runAction(ac); //用runAction函数执行封装好的ac
        this.isOpen = true;//将背包打开参数赋值true
    },
    gameOver() {    //设置游戏结束方法
        this.sign = false;//游戏结束,将游戏继续标识符变为false
        this.checkStill = false;//检查游戏是否进行参数变为false
        this.buttonNode.active = true;//游戏结束,让开始按钮this.buttonNode为可见
        this.overNode.active = true;//游戏结束,让“游戏结束”文本的overNode节点可见
        cc.audioEngine.stopMusic(this.clip[0]);  //游戏结束停止背景音乐
    },
    addScore() {    //设置加分方法
        this.numberNode.opacity = 255;//让分数节点numberNode的.opacity元素(透明度)为255
        this.num++;//让num值++
        this.scoreNode.getComponent(cc.Label).string = "" + this.num;//让分数节点的string元素=空的的字符串“” 加num
        this.numberNode.y = this.birdNode.y;//让加分combo节点numberNode的y和小鸟节点的y相等
        this.numberNode.runAction(//让加分combo节点numberNode渐入渐出runAction,spawn,fadeOut,moveBy
            cc.spawn(
                cc.fadeOut(0.5),
                cc.moveBy(0.5, cc.v2(0, 50))
            )
        )
        cc.audioEngine.playEffect(this.clip[2]);  //加分音乐
    },

    // LIFE-CYCLE CALLBACKS:

    onLoad() {
        // cc.director.getCollisionManager().enabled = true;     //打开碰撞管理系统cc.director.getCollisionManager().
        this.bagPrefab = null;//定义bagPrefab变量赋值为空
        // cc.loader.loadRes(路径,资源类型,回调函数);error如果错误,打印错误信息,data为加载成功的资源
        cc.loader.loadRes("prefab/panelBag", cc.Prefab, (error, data) => {
            if (error) {//判断如果错误信息不为空
                cc.log(error);//打印错误信息
                return;
            }
            this.bagPrefab = data; //将加载到的资源赋值给this.bafPrefab
        });
    },

    start() {
        this.isOpen = false;//定义背包是否打开变量并置为false;
        this.num = 0;//将num参数变为0,防止游戏再开始时,得分继承上局的分数
        this.scoreNode.getComponent(cc.Label).string = "" + 0;//设置分数初始值为0
        this.speed = 5;//设置相对位移速度为5
        this.power = 0;//设置力量参数为0
        this.checkStill = true;//检查游戏是否进行参数变为true
        this.curFrame = 0;//定义一个变量用于循环skinnode列表
        this.sign = false;//定义一个标识符控制游戏是否开始,初始值为false
        this.checkState = false;// false-非碰撞检测状态 true-碰撞检测状态
        this.up = 0;//
        this.clickLayerNode.on(cc.Node.EventType.TOUCH_START, () => {//谁.on(cc.Node.EventType类型.事件,执行匿名方法() =>)
            this.power = 4;//将初始的力量参数直接由0置为4,确保每次监听到点击小鸟有比较明显的上升
            this.up++;
            cc.audioEngine.playEffect(this.clip[1]);
        })

        cc.audioEngine.playMusic(this.clip[0], true);
    },

    update(dt) {
        if (!this.sign) {//设置让游戏暂停的标识符
            return;
        }
        cc.log(2);
        this.skyNode.x -= this.speed;//通过控制天空节点的x值来控制背景移动
        this.birdNode.y += this.power + this.up;//通过小鸟节点的y值和初始的力量参数来控制小鸟自由下落的速度
        this.power -= 0.2;//通过让power小幅度值渐变达到让小鸟平滑移动
        this.birdNode.angle = this.speed * this.power;//通过力量和速度参数控制小鸟上升和下降的角度
        if (this.skyNode.x < -1200) {//判断当背景和起点相隔1200像素时,让背景节点skyNode.x归位=0(无限循环背景)
            this.skyNode.x = 0;
        }
        //node.children即管道节点的子节点
        let list = this.pipeNode.children;//用一个list存储金属管道的子节点(.children)
        let checkNode = null;//定义检查管道节点变量并赋值为null
        for (let i = 0; i < list.length; i++) {//用循环赋值所有管道节点 并赋予速度
            let child = list[i];//let一个child变量来存储list中的每个元素
            child.x -= this.speed;//控制管道节点的x-=this.speed
            // cc.log(child);
            if (child.x < -600) {
                child.x = 600;//判断child出界< -600时让child归位到600
                child.y = (Math.random() - 0.5) * 200;//通过Math.random()(其值在0-1之间)函数来控制管道通过口随机出现,缓和值为200
            }
            let dis = Math.abs(this.birdNode.x - child.x)//let一个变量dis用于计算小鸟和管道的x的差值(Math.abs取绝对值)
            let width = (this.birdNode.width + child.children[0].width) / 2;//定义width变量用于存储小鸟和管道碰撞的临界值
            if (dis < width) {//判断 dis <= width,就给检查管道节点变量赋值child
                checkNode = child;
            }
        }
        if(this.birdNode.y + this.birdNode.height / 2 > cc.winSize.height / 2  //判断小鸟的头部大于屏幕的上边缘或者小鸟的底部小于屏幕的下边缘
            || this.birdNode.y - this.birdNode.height / 2 < -cc.winSize.height / 2){
                this.gameOver();//执行游戏结束方法
            }
        if(checkNode) {//直接判断checkNode,如果未被赋值即为空
            this.checkState = true;//状态从无到有,将检查状态变量checkState赋值为true
            //判断矩形小鸟的上边框(.y + height/2)大于等于通道上边框(checkNode.y+100)或者小鸟的下边框小于等于通道
            if (this.birdNode.y + this.birdNode.height / 2 > checkNode.y + 100
                || this.birdNode.y - this.birdNode.height / 2 < checkNode.y) {
                this.gameOver();//游戏结束
            }
        }else{
            if (this.checkState && this.checkStill) {//判断检查状态变量和检查游戏是否进行变量是否都为true(&&)
                this.addScore();//调用加分函数
            }
            this.checkStill = true; //将检查游戏是否进行变量赋值true
            this.checkState = false;//将检查状态变量赋值为false
            this.up = 0;
        }
        this.curFrame++;//小鸟帧率变量自增
        if (this.curFrame > 2) {//判断如果帧率变量大于2,将帧率变量变为0;
            this.curFrame = 0;
        }
        if (this.birdNode.y) {//如果力量参数大于0
            //让小鸟节点的Sprite组件的spriteFrame参数随帧率参数变化
            this.birdNode.getComponent(cc.Sprite).spriteFrame = this.spriteFrame[this.curFrame];
        }
    }
})

总结

上述关于小鸟、钢管是否碰撞的检测是通过数学计算的方法实现的。不过我的项目文件中还保留了使用cocos碰撞组件的版本,具体表现为每个pipe节点、entrance节点身上的BoxCollider组件,以及bird.js。另外还有我学习制作的一个背包,主要通过预置(cc.prefab)节点实现了背包窗口以及在窗口中动态加载道具图片(sprite)。本人也在学习阶段,有问题欢迎评论区交流。笔者后续还准备分享一些其他项目!包括单机和局域网联机版本的对比项目;可能还会用到一些优化策略如:资源管理器、网络通讯管理器、信号槽等,不足之处希望各位大佬轻喷。

以上就是CocosCreator经典入门项目之flappybird的详细内容,更多关于CocosCreator制作flappybird的资料请关注我们其它相关文章!

(0)

相关推荐

  • 如何使用CocosCreator对象池

    前言: 在运行时进行节点的创建( cc.instantiate )和销毁( node.destroy )操作是非常耗费性能的,因此我们在比较复杂的场景中,通常只有在场景初始化逻辑( onLoad )中才会进行节点的创建,在切换场景时才会进行节点的销毁.如果制作有大量敌人或子弹需要反复生成和被消灭的动作类游戏,我们要如何在游戏进行过程中随时创建和销毁节点呢?这里就需要对象池的帮助了. 对象池就是一组可回收的节点对象,我们通过创建cc.NodePool的实例来初始化一种节点的对象池.通常当我们有多个

  • 详解CocosCreator项目结构机制

     一.项目文件夹结构 初次创建并打开一个 Cocos Creator 项目后,开发者的项目文件夹将会包括以下结构: 下面我们将会介绍每个文件夹的功能. 1.资源文件夹(assets) assets 将会用来放置游戏中所有的本地资源.脚本和第三方库文件.只有在 assets 目录下的内容才能显示在 资源管理器 中.assets 中的每个文件在导入项目后都会生成一个相同名字的 .meta 文件,用于存储对应的资源配置和索引信息..meta 文件需要一并提交到版本控制系统,详见 资源管理注意事项 --

  • 如何用CocosCreator实现射击小游戏

    分析下制作步骤: 1. 准备好资源,搭建场景 资源的话可以自己到网上找,也可以直接用我的也行:创建好相应文件夹,把资源放到res文件夹下: 搭建场景: 第一步:创建一个单色精灵(Script) bg 背景, 设置好颜色,加一个Widget组件,使其充满屏幕: 第二步: 在bg节点下创建top和button空节点作为顶与底部,然后在两个空节点加入带刺的节点(直接将图片拖到top层级管理器就可以),现在我们需要给top与button节点添加一个Layout组件,属性设置如图,这样可以看到屏幕上下都有

  • 详解cocoscreater预制体prefab

    预制体prefab 什么是预制体,字面意思,还未使用前预先制作好的节点资源,属性等同于普通节点,可以看做一个预先制作还没展示出来的普通的节点 怎么创建预制体 1.在层级管理器处先创建普通的节点,然后把这个节点拖拽到资源管理器的assets文件夹下,出于方便管理会统一建立一个Prefab文件夹下统一存放预制体 双击预制体节点可以看到它呈现蓝色,这时候就可以删除还留在场景场景里的节点,需要使用该节点时,通过预制体创建 这时候可以看到属性检查器里的属性,和普通节点一致 预制体的作用 1.批量创建相同类

  • 如何在CocosCreator中利用常驻节点做图层管理

    CocosCreator版本:2.3.4 一般游戏都有图层管理,比如 sceneLayer 场景层 panelLayer 弹框层 tipLayer   提示框层 cocos里的场景不是持久化的,每次切换都会自动destroy,如果在场景上放这些图层,那么每个scene都要放一遍?然后再获取,这样很麻烦. 加载场景使用的是cc.director.loadScene,scene的容器node貌似是director上的一个nodeActivator 现在如果不考虑scene的容器或者cocos的顶层容

  • CocosCreator通用框架设计之网络

    前言 在 Cocos Creator 中发起一个 http 请求是比较简单的,但很多游戏希望能够和服务器之间保持长连接,以便服务端能够主动向客户端推送消息,而非总是由客户端发起请求,对于实时性要求较高的游戏更是如此.这里我们会设计一个通用的网络框架,可以方便地应用于我们的项目中. 使用websocket 在实现这个网络框架之前,我们先了解一下 websocket.websocket 是一种基于 tcp 的全双工网络协议,可以让网页创建持久性的连接,进行双向的通讯.在 Cocos Creator

  • CocosCreator如何实现划过的位置显示纹理

    1.项目需求 项目需要有一个功能:是当一个光点走过的路径,这个路径的位置就都亮起来的功能. 2.资料内容 功能类似这位大神的橡皮擦功能:https://forum.cocos.org/t/2-0-8/74246 但是,项目的需求还要经过的路径周围有模糊的外边--也就是从中心到边缘越来越暗. 所以对于借鉴了网上大神的shader例子,类似以下的示例: 在大神的肩膀上做了一些改动,来实现项目的需求. 3.项目示例 以下是我自己的测试项目的示例: (请忽略这渣渣的画质,懒得装录屏软件了) 4.项目代码

  • 整理CocosCreator常用知识点

    一.场景加载 cc.director.loadScene('场景名称');//场景跳转 cc.director.preloadScene('场景名称');//预加载场景 cc.director.getScene();//获取当前场景 二.查找节点 1,节点查找 node = cc.find("Canvas/bg");//路径访问节点 性能消耗相对较大 this.node.getChildByName('name');//名称获取子节点 性能消耗较小 node.getComponent(

  • CocosCreator ScrollView优化系列之分帧加载

    一. 前言 JS是单线程的,也就意味着所有任务需要排队,只有当前一个任务结束了,后一个任务才会执行.如果前一个任务耗时很长,后一个任务就不得不一直等着. Cocos Creator 是采用 Java Script/Type Script语言开发,本质上是JS,同样会拥有以上特征.特别地,如果使用不当,极有可能导致界面卡顿. 比如:在为一个ScrollView的Content创建500个节点的的时候,可能就会出现下面界面卡死的问题 PS:本来加载过程中有一个loading对话框,因为卡死了,就感觉

  • 全面讲解CocosCreator热更新

    前言 本文主要引用cocos关于热更的官方文档,并在此基础上,总结sprout当前热更流程. 什么是热更 热更(新)本质上是从服务器下载需要的资源到本地,并且可以执行新的游戏逻辑,让新资源可以被游戏所使用,它可以使开发者在不发布新版本的情况下,修复 BUG 和发布功能,让开发者得以绕开苹果的审核机制,避免长时间的审核等待以及多次被拒造成的成本. Cocos 默认的热更新机制并不是基于补丁包更新的机制,传统的热更新经常对多个版本之间分别生成补丁包,按顺序下载补丁包并更新到最新版本.Cocos 的热

  • 游戏开发中如何使用CocosCreator进行音效处理

    在游戏开发中,我们经常需要使用音效来营造游戏氛围,因此本文给大家总结下 Cocos Creator 游戏开发中音效组件的封装和使用. 一. Cocos Creator 中音频播放基础 1. 基础知识 [1]AudioSource 组件官方文档:http://docs.cocos.com/creator/manual/zh/audio/audio.html [2]cc.audioEngine官方文档:http://docs.cocos.com/creator/manual/zh/audio/aud

  • 怎样在CocosCreator中使用游戏手柄

    一.场景布置 二. 添加手柄监听器 1.监听事件的变化 由原先的mouse系列的转换为touch系列的 touchstart 触摸按下,相当于 mousedown touchmove 触摸移动,相当于 mousemove touchend 触摸抬起,相当于 mouseup touchcancel 触摸取消,被其他事件终止,相当于按下了ESC键 2.坐标设定 当触摸按下随推动位置变化(要用世界坐标转换),触摸抬起后回归原位(直接设定0,0坐标默认相对坐标). setPosition()设定的为相对

随机推荐