• Stars
    star
    177
  • Rank 215,985 (Top 5 %)
  • Language
    TypeScript
  • Created about 5 years ago
  • Updated over 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A jumping ball demo created by Cocos Creator 3D

弹弹乐 3D 游戏教程

1. 游戏介绍

弹弹乐游戏是一款通过操作屏幕点击移动控制小球运动的休闲弹跳类游戏,在游戏过程中可以通过跳中板心或者板边分别获得 2 分和 1 分,以及吃钻石等方式获得分数。本文主要分享的是一个游戏的最基础的完整流程,并不具备一款游戏的发布能力。因此,在后续的拓展里就需要大家发挥创造力来将一款很简单的小游戏变成一款很有趣的小游戏。

2. 结构说明

以下是游戏的草图以及整体设计思路

scheme

在出完整体思路了之后,就可以开始设计一下每个阶段应该完成的目标,以便于提高代码编写的效率。以下是我划分的每个阶段的完成任务。

  • 游戏初始化
    • 跳板初始化
    • 屏幕事件监听,小球与普通板块弹跳计算
    • 提供相机跟随接口
  • 游戏核心逻辑编写
    • 跳板复用逻辑编写
    • 小球与不同板块弹跳计算
    • 游戏开始与结束逻辑编写
  • 游戏丰富
    • 添加钻石以及吃砖石表现
    • 添加跳板表现
    • 增加小球粒子以及拖尾表现
    • 增加音效和音乐

初期设计完了后,我们开始整个游戏场景的搭建。整个游戏一共就一个场景,一个主程序 Game,负责管理所有分支管理的 Manager 以及负责事件的监听和派发;多个分支 Manager,负责管理跳板创建摆放或游戏页面等;一个全局配置模块,负责存储游戏中使用的配置;独立对象的运作脚本,负责自身行为运作。

scene

3. 编写游戏内容逻辑

由于最终呈现出来的详细步骤代码太多,我这里就不一一演示,主要针对每个流程的几个关键部分做个说明。在这里有一个不会多做说明的部分就是 Constants 全局数据类,只需要记得游戏中的所有配置数据都存放在此即可。

游戏初始化

  1. 跳板初始化

跳板初始化主要体现在 BoardManager 里的 initBoardgetNextPos 两个方法上。在整个游戏过程中,使用的板一共就只有 5 个,后续的跳板生成都是通过复用的方式,不断的去重新计算位置以及序号。跳板的生成也是严格根据上一个跳板的位置来计算,避免出现长距离位置偏移影响游戏进行。

getNextPos(board: Board, count: number, out ?: Vec3) {
    const pos: Vec3 = out ? out.set(board.node.position) : board.node.position.clone();
    const o = utils.getDiffCoeff(count, 1, 2);
    pos.x = (Math.random() - .5) * Constants.SCENE_MAX_OFFSET_X * o;
    if (board.type === Constants.BOARD_TYPE.SPRINT) {
        pos.y += Constants.BOARD_GAP_SPRINT;
        pos.x = board.node.position.x;
    }

    if (board.type === Constants.BOARD_TYPE.SPRING) {
        pos.y += Constants.BOARD_GAP_SPRING;
    } else {
        pos.y += Constants.BOARD_GAP;
    }
    return pos;
}

getDiffCoeff(e: number, t: number, a: number) {
    return (a * e + 1) / (1 * e + ((a + 1) / t - 1));
}
  1. 屏幕事件监听,小球与普通板块弹跳计算

初始化完跳板之后,要开始做小球的弹跳。整个游戏的入口函数都设定在 Game 类上,Game 又添加在 Canvas 节点上,因此,Game 类所挂载的节点就作为全局对象的事件监听节点来使用最合适不过。因为主要接受该事件的对象是小球,所以,我们在小球里做监听的回调。

start () {
    Constants.game.node.on(Node.EventType.TOUCH_START, this.onTouchStart, this);
    Constants.game.node.on(Node.EventType.TOUCH_END, this.onTouchEnd, this);
    Constants.game.node.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this);

    this.updateBall();
    this.reset();
}

onTouchStart(touch: Touch, event: EventTouch){
    this.isTouch = true;
    this.touchPosX = touch.getLocation().x;
    this.movePosX = this.touchPosX;
}

onTouchMove(touch: Touch, event: EventTouch){
    this.movePosX = touch.getLocation().x;
}

onTouchEnd(touch: Touch, event: EventTouch){
    this.isTouch = false;
}

然后,小球根据一定比例的换算来做实际移动距离的计算。在 update 里每帧根据者冲刺等状态对小球进行 setPosXsetPosY 调整。小球的上升与下降是通过模拟重力效果来实现。

// Constants
static BALL_JUMP_STEP = [0.8, 0.6, 0.5, 0.4, 0.3, 0.2, 0.15, 0.1, 0.05, 0.03]; // 正常跳跃步长
static BALL_JUMP_FRAMES = 20; // 正常跳跃帧数

//Ball
_tempPos.set(this.node.position);
_tempPos.y += Constants.BALL_JUMP_STEP[Math.floor(this._currJumpFrame / 2)];
this.node.setPosition(_tempPos);
  1. 提供相机跟随接口

相机的移动位置不是由自身来操控的,而是根据小球当前的位置来进行实时跟踪。因此,相机只需要调整好设置接口,按照一定脱离距离去跟随小球即可。

update() {
    _tempPos.set(this.node.position);
    if (_tempPos.x === this._originPos.x && _tempPos.y === this._originPos.y) {
        return;
    }

    // 横向位置误差纠正
    if (Math.abs(_tempPos.x - this._originPos.x) <= Constants.CAMERA_MOVE_MINI_ERR) {
        _tempPos.x = this._originPos.x;
        this.setPosition(_tempPos);
    } else {
        const x = this._originPos.x - _tempPos.x;
        _tempPos.x += x / Constants.CAMERA_MOVE_X_FRAMES;
        this.setPosition(_tempPos);
    }

    _tempPos.set(this.node.position);
    // 纵向位置误差纠正
    if (Math.abs(_tempPos.y - this._originPos.y) <= Constants.CAMERA_MOVE_MINI_ERR) {
        _tempPos.y = this._originPos.y;
        this.setPosition(_tempPos);
    } else {
        const y = this._originPos.y - _tempPos.y;
        if (this.preType === Constants.BOARD_TYPE.SPRING) {
            _tempPos.y += y / Constants.CAMERA_MOVE_Y_FRAMES_SPRING;
            this.setPosition(_tempPos);
        } else {
            _tempPos.y += y / Constants.CAMERA_MOVE_Y_FRAMES;
            this.setPosition(_tempPos);
        }
    }
}

游戏核心

整个游戏的节奏控制其实都是通过小球来的,小球通过弹跳位置决定什么时候开始新板的生成,小球在游戏过程中的得分决定了板子后续生成的丰富性(比如长板或者弹簧板),以及小球的死亡以及复活决定了游戏的状态等等。最后通过 UI 配合来完成游戏开始结束复活的界面引导交互操作。

  1. 跳板复用逻辑编写

在前面创建跳板的时候我们说过,游戏中的跳板是不会累增的,都是通过复用来是实现,所以需要提前度量好板块间的最小距离。那么,屏幕最下方的板块在什么时机开始复用到屏幕最上方呢?举个例子:假设当前场景的板上限是 5 块,在数组里的顺序就是 0 - 4,按前面说的所有板在全显示的情况下是会均匀分布的,因此,屏幕的分割板就是在中间板的 2 号板,因此只要超过了 2,就代表小球已经跳过的屏幕的一半,这个时候就要开始清理无用的板了。

for (let i = this.currBoardIdx + 1; i >= 0; i--) {
    const board = boardList[i];

    // 超过当前跳板应该弹跳高度,开始下降
    if (this.jumpState === Constants.BALL_JUMP_STATE.FALLDOWN) {
        if (this.currJumpFrame > Constants.PLAYER_MAX_DOWN_FRAMES || this.currBoard.node.position.y - this.node.position.y > Constants.BOARD_GAP + Constants.BOARD_HEIGTH) {
            Constants.game.gameDie();
            return;
        }

        // 是否在当前检测的板上
        if (this.isOnBoard(board)) {
            this.currBoard = board;
            this.currBoardIdx = i;
            this.activeCurrBoard();
            break;
        }
    }
}

// 当超过中间板就开始做板复用
for (let l = this.currBoardIdx - Constants.BOARD_NEW_INDEX; l > 0; l--) {
    this.newBoard();
}
  1. 小球与不同板块弹跳计算

从上面的制作过程中我们已经实现来在普通板上小球是一个乒乓球状态,那么遇到弹簧板或者冲刺板的时候,也可以用类似逻辑结构来继续补充不同板子的不同处理。这里的实现因为结构已定较为简单,就不再多做说明,只需要在全局数据类里加上相应的相同配置即可。

  1. 游戏开始与结束逻辑编写

游戏开始以及结束都是通过 UI 界面来实现。定义一个 UIManager 管理类来管理当前 UI 界面。所有的 UI 打开与关闭都通过此管理类来统一管理,点击事件的响应都直接回调给游戏主循环 Game 类。

以上部分就基本完成了整个游戏的逻辑部分。

游戏丰富

即使上面的游戏结束了也会觉得整体的表现显得有些单薄。因此,接下来丰富一下游戏的真实表现力。

  1. 添加钻石以及吃砖石表现

因为游戏内的跳板数量限制,因此,我们可以大方的给每个跳板配置 5 个钻石,通过随机概率决定钻石的显示

if (this.type === Constants.BOARD_TYPE.GIANT) {
    for (let i = 0; i < 5; i++) {
        this.diamondList[i].active = true;
        this.hasDiamond = true;
    }
} else if (this.type === Constants.BOARD_TYPE.NORMAL || this.type === Constants.BOARD_TYPE.DROP) {
    if (Math.random() > .7) {
        this.diamondList[2].active = true;
        this.hasDiamond = true;
    }
}

既然有了钻石,那吃钻石的时候,肯定也要有些表示。那就是掉落一些粒子来增加表现。由于游戏设计过程中如果有很多对频繁的创建和销毁的话,对性能其实是很不友好的。因此,提供一个对象池在一款游戏中是必不可少。在这里,我们就可以把散落的粒子存放在对象池里进行复用。在这款游戏的设计过程中,小球部分的计算量是很频繁的,特别是在每帧需要更新的地方,想要去做性能优化的同学可以根据对象池的概念对小球里的一些向量进行复用。

getNode(prefab: Prefab, parent: Node) {
    let name = prefab.data.name;
    this.dictPrefab[name] = prefab;
    let node: Node = null;
    if (this.dictPool.hasOwnProperty(name)) {
        //已有对应的对象池
        let pool = this.dictPool[name];
        if (pool.size() > 0) {
            node = pool.get();
        } else {
            node = instantiate(prefab);
        }
    } else {
        //没有对应对象池,创建他!
        let pool = new NodePool();
        this.dictPool[name] = pool;

        node = instantiate(prefab);
    }

    node.parent = parent;
    return node;
}

putNode(node: Node) {
    let name = node.name;
    let pool = null;
    if (this.dictPool.hasOwnProperty(name)) {
        //已有对应的对象池
        pool = this.dictPool[name];
    } else {
        //没有对应对象池,创建他!
        pool = new cc.NodePool();
        this.dictPool[name] = pool;
    }

    pool.put(node);
}
  1. 添加跳板表现和增加小球粒子以及拖尾表现

其实这两点功能都基本类似,都是增加一些波动,拖尾粒子等来丰富表现,在这里就不过多说明,具体的表现都写在了 Board 类和 Ball 类相对应关键字的方法里。

  1. 增加音效和音乐

因为是基础教程,游戏内的表现也不是很多,所以就选取了按钮被点击的音效和背景音乐来做效果。

playSound(play = true) {
    if (!play) {
        this.audioComp.stop();
        return;
    }

    this.audioComp.clip = this.bg;
    this.audioComp.play();
}

playClip() {
    this.audioComp.playOneShot(this.click);
}

以上就是本教程的全部内容。接下来看一下运行结果吧。

play

More Repositories

1

cocos-engine

Cocos simplifies game creation and distribution with Cocos Creator, a free, open-source, cross-platform game engine. Empowering millions of developers to create high-performance, engaging 2D/3D games and instant web entertainment.
C++
8,099
star
2

example-projects

Example project to guide users through how to create game with Cocos Creator
JavaScript
1,284
star
3

tutorial-blackjack-deprecated

Blackjack game made with Cocos Creator.
JavaScript
798
star
4

engine-native

Native engine for Cocos Creator v2.x
C++
696
star
5

cocos-awesome-tech-solutions

Cocos' Awesome Technical Solutions
TypeScript
625
star
6

cocos-example-projects

JavaScript
499
star
7

cocos-docs

Manual docs content for Cocos Creator
TypeScript
303
star
8

cocos-example-dark-slash

PLSQL
291
star
9

cocos-test-projects

TypeScript
238
star
10

cocos-tutorial-first-game

Quick start game for Fireball
JavaScript
236
star
11

cocomat

C
195
star
12

cocos-example-cartoon-vegetation

TypeScript
162
star
13

cocos-example-ui

TypeScript
148
star
14

cocos-tutorial-taxi-game

TypeScript
147
star
15

cocos-tutorial-duang-sheep

tutorial repo
JavaScript
120
star
16

creator-lua-deprecated

Cocos Creator 的 Lua 支持
Lua
117
star
17

awesome-cocos

⚡️ Delightful Cocos Creator show cases, utilities, documents and more!
114
star
18

cocos-tutorial-airplane

video tutorial airplane
TypeScript
114
star
19

cocos-tutorial-mind-your-step

Cocos Creator learning tutorial
TypeScript
74
star
20

firedoc-deprecated

API Doc generator based on YUIDoc
JavaScript
70
star
21

cocos-example-process-build-textures

A build plugin to help you process textures in Creator 2.x after build
JavaScript
66
star
22

FBX-glTF-conv

A FBX to glTF file format converter.
C++
63
star
23

example-camera-deprecated

Camera demo for Cocos Creator
JavaScript
48
star
24

cocos-engine-external

external 3rd party modules for cocos-engine
C++
47
star
25

cocos-creator-extensions

TypeScript
47
star
26

cocos-example-physics

Samples for 3d physics
TypeScript
28
star
27

cocos-example-marionette

This project is to demonstrate the use of Cocos Creator Marionette animation system.
TypeScript
25
star
28

example-instant-games-deprecated

JavaScript
20
star
29

example-combat-animation-deprecated

turn based action rpg
JavaScript
19
star
30

i18n-example-deprecated

showcase how to implement i18n to your game.
JavaScript
19
star
31

smart-merge-tool-deprecated

JavaScript
17
star
32

cocos-tutorial-storage

Cocos Creator Demo: Save and load user profile with encryption using sys.localStorage
JavaScript
17
star
33

example-team-build-ui-deprecated

Team build ui demo for Cocos Creator
JavaScript
16
star
34

creator-api-docs-2.x

Host generated Cocos Creator 2.x API
JavaScript
15
star
35

cocos-plugin-import-2.x

plugin for migrating cocos creator 2.x projects.
TypeScript
14
star
36

google-game-sdk

C++
14
star
37

helloworld-typescript-legacy

Typescript helloworld project template for Cocos Creator
TypeScript
13
star
38

gulp-fontello-import-deprecated

Import svg files to fontello icon font project, use svg filename as glyph name. Also provide task for auto download exported css and font files into desinated folder.
JavaScript
12
star
39

cocos-example-materials

TypeScript
11
star
40

example-responsive-ui-deprecated

Cocos Creator - Responsive UI demo
JavaScript
11
star
41

cocos-example-render-pipeline

TypeScript
9
star
42

divine-vanity-deprecated

JavaScript
9
star
43

cocos-benchmark

EJS
9
star
44

cocos-example-open-data-context

TypeScript
9
star
45

cocos-google-admob

Objective-C
9
star
46

cocos-template-taxi-game

TypeScript
8
star
47

cocos-effect

VSCode Syntax highlight support for Cocos Effect file
TypeScript
7
star
48

cocos-tutorial-video-src

JavaScript
6
star
49

ui-kit-deprecated

JavaScript
6
star
50

cocos-example-custom-pipeline

TypeScript
6
star
51

cocos-ccbuild

TypeScript
5
star
52

cocos-web-extension-detect-engine

A Chrome extension to detect the engine running in web.
JavaScript
4
star
53

example-packages-deprecated

HTML
4
star
54

overhead-benchmark-deprecated

Creator 架构层性能损耗 Benchmark
C++
4
star
55

cocomat-docs

3
star
56

firedoc-theme-notab-deprecated

A new theme without tab in navigation sidebar
HTML
3
star
57

devops-workflows

Workflows for creator team.
JavaScript
3
star
58

v8

C++
3
star
59

cocos-gameplay

cocos creator gameplay
TypeScript
3
star
60

gulp-download-fire-shell-deprecated

gulp plugin to download atom-shell
2
star
61

node-font-lib-deprecated

built for https://github.com/fireball-x/font-editor
JavaScript
2
star
62

hello-world-legacy

Hello world new project template.
JavaScript
2
star
63

fireball-engine-core-deprecated

The basic classes used in fireball engine and editors
JavaScript
2
star
64

runtime-pixi-deprecated

A pixi runtime for test basic fireball features
JavaScript
2
star
65

gulp-fb-deprecated

utilities for gulp
JavaScript
1
star
66

fire-watch-deprecated

A watch module based on node-pathwatcher, the module is implemented for syncing changes between file system and asset db in fireball
JavaScript
1
star
67

tests-3d-deprecated

engine-3d test framework
HTML
1
star
68

fireball-runtime-cocos-deprecated

JavaScript
1
star
69

firedoc-theme-default-deprecated

Optimized theme for firedoc
Handlebars
1
star
70

cocos-fire-path

node's path module with some helpful additions.
JavaScript
1
star
71

cocos-fire-fs

node's fs module with some helpful additions.
JavaScript
1
star
72

preview-template-deprecated

JavaScript
1
star
73

font-editor-angular-deprecated

Font Editor
JavaScript
1
star
74

atlas-editor-polymer-deprecated

Atlas Editor for 2D games
JavaScript
1
star
75

cocos-scripting

TypeScript
1
star
76

creator-types

TypeScript
1
star