初识cocos,实现《FlappyBird》h5游戏

一、cocos creator 简介

  1. Cocos Creator 是由 Cocos 引擎 (Cocos Engine)团队开发的一款 跨平台 2D/3D 游戏开发工具 ,专为游戏开发者设计,支持 可视化编辑 + 脚本开发 的一体化工作流。 它基于 JavaScript / TypeScript 编写,支持 WebGL、iOS、Android、H5、PC、微信小游戏、QQ小游戏、支付宝小游戏 等多平台一键发布。

  2. cocos有着完善的游戏功能支持,包含精灵、动作、动画、粒子特效、场景转换、事件、文件 IO、数据持久化、骨骼动画、3D等,只使用 Cocos2d-x 引擎,你就能完成一款游戏的开发,因为 Cocos2d-x 提供了游戏开发所需的一切。

精通 Cocos2d-x 很难,但是上手 Cocos2d-x 很容易

相关链接

二、核心概念

1. 导演 (Director) - 游戏的总指挥

导演是一个全局的、唯一的控制中心。它的核心职责是:

  • 控制游戏流程:负责游戏的启动、循环、暂停、继续和结束。
  • 管理场景生命周期:负责执行场景的加载、切换、清理和销毁。

在代码中,可以通过 director.loadScene('SceneName') 来指挥场景切换。

2. 场景 (Scene) - 游戏的舞台或关卡

在游戏开发过程中,你可能需要一个主菜单,几个关卡和一个结束场景, 可以使用 场景(Scene) 组织所有这些分开的部分

  • 一个独立的容器 :每个场景都是一个以 .scene 为后缀的文件。
  • 节点的根 :每个场景都包含一个完整的节点树,这棵树定义了当前关卡或界面的所有对象及其层级关系。这就是图中"包含"所表示的关系。
  • 一个独立的游戏单元:例如,"开始菜单"、"第一关"、"Boss战"、"游戏结束"界面通常都是不同的场景。
3. 节点 (Node) - 构建世界的积木
  • 树状结构 :节点之间通过父子关系组织起来,形成一个树形结构。变换父节点(移动、旋转、缩放)会直接影响其所有子节点。
  • 功能的载体 :节点本身是空白的,通过添加组件 (Component) (如图中的Sprite、Camera、Light、YourScript)来获得渲染、物理、声音、逻辑等具体功能。
  • 无处不在:场景中的一切,无论是可见的精灵、UI,还是不可见的逻辑控制节点,都是节点。
4.精灵(Sprite)-游戏的主角

所有的游戏都有 精灵(Sprite) 对象,精灵是您在屏幕上移动的对象,它能被控制,是一个能通过改变自身的属性:角度,位置,缩放,颜色等,变成可控制动画的 2D 图像。

三、基本开发环境了解

1、编译器界面简介

2、游戏运行&扫码预览

3、浏览器中打开

四、使用cocos实现flappybird

1. 游戏元素分析

如上图所示,游戏元素主要包含

  • 背景,水平方向移动
  • 地面,水平方向移动
  • 管道,上下成对出现,豁口位置随意变动,水平方向移动
  • 鸟,玩家控制的主角
  • 分数,通过一组管道分数+1

2. 背景生成与移动

游戏的背景是一个不断重复向左移动的图片,所以需要两张循环移动的背景图片

操作步骤:

1)完成新建游戏场景后,在游戏场景下新建空节点Bg(见下图1)

2)Bg节点下添加bg1,bg2两张图片,并且设置宽高和项目宽高一致(见下图2、3)

3)编写script代码bg.ts,控制两张图片的移动(核心代码如下)

4)在Bg节点的属性中添加自定义脚本bg.ts,并且把bg1,bg2,拖到对应的移动property上(见下图4)

js 复制代码
// bg.ts 核心代码
    @property(Node)
    target1ToMove: Node = null;
    @property(Node)
    target2ToMove: Node = null;
    
    update(deltaTime: number) { //delta 为每帧所需要的时间,游戏运行过程中 每detal秒执行一次
        const moveDistance = this.moveSpeed * deltaTime
        let p1 = this.target1ToMove.getPosition();
        this.target1ToMove.setPosition(p1.x - moveDistance, p1.y)
        let p2 = this.target2ToMove.getPosition();
        this.target2ToMove.setPosition(p2.x - moveDistance, p2.y)

        if(p1.x<-730){
          p2 =  this.target2ToMove.getPosition();
          this.target1ToMove.setPosition(p2.x+728, p2.y) 
        }
         if(p2.x<-730){
          p1 =  this.target1ToMove.getPosition();
          this.target2ToMove.setPosition(p1.x+728, p1.y) 
        }
        
    }

注意地面的生成与移动和背景流程相似并且可以共用bg.ts,这里不再赘述

3.管道的生成与移动

由于在游戏中管道是不断重复动态生成的节点(如下左图),我们将它做成预制体prefab ,预制体,字面意思,还未使用前预先制作好的节点资源,属性等同于普通节点,可以看做一个预先制作还没展示出来的普通的节点,预制体会被大量运用于一些重复的节点比如子弹迎面而来的怪物

操作步骤:

1)制作管道节点(与背景类似),并且转为prefab(如下右图)

2)制作管道生成器空节点,作为管道节点的父节点,编写PipeSpawner.ts,不断动态生产管道

3)编写pipe.js 控制管道向左移动

kotlin 复制代码
// PipeSpawner.ts
@property(Prefab)
pipePrefab: Prefab = null
@property
spawnRate:number = 0.5 // 相邻管道出现的时间间隔
private timer:number = 0

update(deltaTime: number) {
    this.timer += deltaTime
    if(this.timer> this.spawnRate){
        this.timer = 0
        const pipeInst = instantiate(this.pipePrefab) // 实例化预制件,instantiate为coco提供的方法
        this.node.addChild(pipeInst)
        const y = math.randomRangeInt(-100,200) // 豁口位置
        pipeInst.setPosition(0, y)
    }   
}
kotlin 复制代码
// pipe.ts 管道左移动关键函数
update(deltaTime: number) {
    const p = this.node.position
    this.node.setPosition(p.x - this.moveSpeed * deltaTime, p.y)
    if(p.x < -900){
        this.node.destroy()
    }
}

4.小鸟生成和上升降落

在游戏过程中小鸟的由三部分组成,小鸟拍翅膀动画的播放,小鸟下落,点击屏幕小鸟上升效果如下图所示

1)小鸟拍翅:其拍翅是一个纯粹的图片帧动画,一直循环,没有相关的逻辑,因此直接创建一个Animation clip,其模式为Loop。

首先新增一个Bird节点,并且新增Animati在资源管理器的 assets/animations 下新建[动画剪辑]名为 bird_idle,最后添加动画帧图片,操作视频>>>

2) 小鸟下落: 在游戏中没有点击屏幕的时候小鸟是直接收到重力影响做自由落体运动

在cocos中刚体(RigidBody) 组件可以模拟真实物理世界中的带有质量和重力加速度的物体,因此在游戏中我们直接给小鸟节点添加【动态刚体】组件即可

DYNAMIC (动态刚体)

  • 特点: 具有质量,可以受到力的作用,并且会受到重力的影响。
  • 用途: 适用于会进行真实物理交互的游戏元素,如角色、弹跳的球等。

3)小鸟向上飞行:当玩家点击屏幕时,小鸟会"扑腾"一下,获得一个向上的初始速度,从而实现跳跃效果

这里我们注册一个点击回调函数,每次点击时给小鸟刚体组件的线性速度向量赋值,在后续每一帧中,物理引擎会自动根据重力(gravity)对 linearVelocity 进行更新:

kotlin 复制代码
// bird.ts 
onLoad () {
    input.on(Input.EventType.TOUCH_START, this.onTouchStart, this); // 注册屏幕touch事件
    this.rgd2D = this.getComponent(RigidBody2D)
}
onTouchStart(event: EventTouch) {
    this.rgd2D.linearVelocity = new Vec2(0,10) // 获得一个y 轴方向 10px 每秒的速度
    this.node.angle = 30
}
// 重力效果伪代码示例
// rgd2D.linearVelocity.y -= gravity * deltaTime;

5.小鸟的碰撞与得分

在 Cocos 中实现碰撞主要包括添加物理组件(如物理世界组件、刚体组件、碰撞体组件),并编写碰撞回调函数,来处理碰撞事件。具体步骤为:首先,创建一个带有物理组件的场景,然后为需要碰撞的节点添加刚体和碰撞体组件,最后通过脚本监听碰撞事件,并在回调函数中实现具体的逻辑

1)给小鸟,管道,地面添加刚体组件和碰撞组件(上节介绍过添加刚体组件不再赘述刚体组件),注意管道没有重力作用,所以刚体类型要选择Kinematic 类型

运动学 (Kinematic)

  • 特点: 没有质量,不受重力影响,但可以通过设置其速度来进行移动或旋转。
  • 用途: 用于需要通过代码控制移动的物体,且不希望它们受重力影响,例如自动移动的平台。

添加碰撞组件,tag用来标记不同的节点\

2) 给小鸟注册碰撞函数

kotlin 复制代码
// bird.ts
onLoad () {
    input.on(Input.EventType.TOUCH_START, this.onTouchStart, this);
     // 注册单个碰撞体的回调函数
    let collider = this.getComponent(Collider2D);
    if (collider) {
        collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
        collider.on(Contact2DType.END_CONTACT, this.onEndContact, this);
    }
    this.rgd2D = this.getComponent(RigidBody2D)
}
// 碰撞函数
onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null){
    if(otherCollider.tag === Tags.PIPE || otherCollider.tag === Tags.LAND){ // 碰撞到地面或者管道
     GameManager.inst().transitionToGameOverState() // 小鸟落地游戏结束
    }
}
public disableControlNotRgd(){ // 小鸟碰撞后 动画不再播放,并且不能点击飞翔(在游戏控制中心中调用)
    this.getComponent(Animation).enabled = false
    this._canControl = false
}

3)小鸟穿过管道安全区,游戏得分

安全区设置\

得分示意\

scss 复制代码
// bird.ts
// 碰撞结束回调
onEndContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null){
 if(otherCollider.tag === Tags.PIPE_MIDDLe){ // 穿过安全区
     GameManager.inst().addScore()
 }
 
}

6.游戏状态管理

Cocos 中 GameManager 的作用是作为一个中心节点,负责管理整个游戏的生命周期和游戏相关的节点及数据,例如负责生成、访问、删除跑道等游戏元素,从而集中管理数据和功能,提高代码的可维护性和健壮性

1)新建 gameReadyUI、gamingUI、gameOverUI 页面,可根据附件材料直接拖拽绘制\
2)三种游戏状态下各个节点情况介绍

节点 游戏未开始 游戏进行中 游戏已结束
地面/背景 静止 移动 静止
小鸟 动画静止/不可控制 动画播放/可控制 动画静止/不可控制
管道生成器 未开始生成 开始生成 暂停生成
gameReadyUI show hide hide
gamimgUI hide show hide
gameOverUI hide hide show(且更新得分)
kotlin 复制代码
// GameManage.ts
@property 
moveSpeed: number = 100;
@property(Bird)
bird: Bird = null
@property(Label)
scoreLabel:Label = null
// ...... 引入一些需要控制的节点,背景,管道生成器,三个界面UI等
onLoad() {
    GameManager._inst = this
}
protected start(): void {
    this.transitionToReadyState()
}

transitionToReadyState(){ // 设置各个节点状态
    this.curGS = GameState.Ready
    this.bird.disableControl()
    this.bgMoving.disableMoving()
    this.landMoving.disableMoving()
    this.pipeSpawner.pause()
    this.gamingUI.active = false
    this.gameReadyUI.node.active = true
    this.gameOverUI.hide()
}
transitionToGamingState(){
     // ...... 更新各个状态
}
transitionToGameOverState(){
    if(this.curGS === GameState.GameOver){
        return
    }
    this.gameOverUI.show(GameData.getScore(), GameData.getBestScore())
    // ...... 更新各个状态
}

addScore(count:number = 1){ // 游戏得分
    GameData.addScore(count)
    this.scoreLabel.string = GameData.getScore().toString()
}
============================
// bird.ts
// 碰撞管道或者地面游戏结束
onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null){
    if(otherCollider.tag === Tags.PIPE || otherCollider.tag === Tags.LAND){
     GameManager.inst().transitionToGameOverState() // 直接调用gameManager 实例方法
    }
}
============================
// gameReadyUI.ts
onTouchStart(){ // 点击页面游戏开始
    GameManager.inst().transitionToGamingState()
}
// gameOverUI.ts
// 当前分数,最高分,奖牌
@property(Label)
curScoreLabel: Label = null
@property(Label)
bestScoreLabel: Label = null
@property(Node)
newSprite:Node = null
@property(Node)
medalArray: Node[] = []

public show(curScore: number, bestScore: number){
    this.node.active = true
    this.curScoreLabel.string = curScore.toString()
    this.bestScoreLabel.string = bestScore.toString()
    if(curScore>bestScore){
        this.newSprite.active = true
    }else{
        this.newSprite.active = false
    }
    // 0-p
    const index = Math.min(Math.floor(curScore/10), 3)
    this.medalArray[index].active = true
}

7.添加游戏音乐

音频管理脚本可直接参考官网例子coco音频播放管理器

scss 复制代码
// gameManager.ts
protected start(): void { // 游戏开始播放背景音乐
    this.transitionToReadyState()
    AudioMgr.inst.play(this.bgAudio)
}
transitionToGameOverState(){ // 游戏结束停止背景音乐,播放over音乐
  AudioMgr.inst.stop()
  AudioMgr.inst.playOneShot(this.gameOverAudio)
 }
 ================
bird.ts
onTouchStart(event: EventTouch) { // 小鸟向上飞音乐
    AudioMgr.inst.playOneShot(this.clickAudio)
}

8. 项目构建

cocos 工具有自带发布构建功能 \
构建模板修改

Cocos Creator 支持对每个项目分别定制构建模板,只需要在项目路径下添加一个 build-templates 目录,里面按照 平台扩展名称 划分子目录。在构建结束后,build-templates/[platform] 目录下所有的文件都会自动按照对应的目录结构复制到对应平台构建生成的工程中,但是工程默认不带构建模板,需要点击创建一下模板才可以

小结

源码及资源下载云盘地址,提取码p8i1

相关推荐
古夕5 小时前
Vue 3 复杂表单父子组件双向绑定的最佳实践
前端·javascript·vue.js
烛阴5 小时前
TypeScript 进阶必修课:解锁强大的内置工具类型(一)
前端·javascript·typescript
anyup5 小时前
太全面啦!总结篇!99% 开发者可能都会遇到的 uView Pro 组件库问题
前端·vue.js·uni-app
૮・ﻌ・5 小时前
CSS基础学习第二天
前端·css·学习·emmet语法
Zayn5 小时前
前端路径别名跳转和提示失效?一文搞懂解决方案
前端·javascript·visual studio code
葡萄城技术团队6 小时前
【SpreadJS V18.2 新特性】Table 与 DataTable 双向转换功能详解
前端
Nicholas_ly6 小时前
copilot
前端
__M__6 小时前
Zalo Mini App 初体验
前端·react.js