专栏名称: 51CTO技术栈
有趣 | 有料 | 有内涵,为您提供最优质的内容,愿我们一起悦享技术,成就人生。
目录
相关文章推荐
码农翻身  ·  穷人需要投机,别迷信长期主义 ·  14 小时前  
程序员小灰  ·  蔚来汽车裁员约10%,20分钟完成裁员。。。 ·  4 天前  
OSC开源社区  ·  【直播预告】AiEditor:面向AI的下一 ... ·  4 天前  
51好读  ›  专栏  ›  51CTO技术栈

为了媳妇,熬夜撸了一个合成大西瓜!

51CTO技术栈  · 公众号  · 程序员  · 2021-02-02 18:05

正文

最近微博上曝出了很多瓜,"合成大西瓜"这个游戏也很火热,玩了一阵还挺有意思的。


图片来自 Pexels


研究了一下原理,发现目前流传的版本都是魔改编译后的版本,代码经过压缩不具备可读性,因此决定自己照着实现一个。


本项目主要用作 cocos creator 练手使用,所有美术素材和音频材料均来源于:
www.wesane.com/game/654/

感谢原作者,向每一位游戏开发者致敬!


本文所有代码及素材都放在 Github 上:

https://github.com/tangxiangmin/cocos-big-watermelon

也可以通过在线预览地址体验:
https://web-game-9gh6nrus14fec37e-1252170212.tcloudbaseapp.com/


游戏逻辑


整个游戏逻辑比较简单,结合了俄罗斯方块与消除游戏的核心玩法:

  • 在生成一个水果

  • 点击屏幕,水果移动到对应 x 轴位置并自由下落

  • 每个水果会与其他水果发生碰撞,两个相同的水果碰撞时会发生合并,升级成更高一级的水果


水果共有 11 种类型:

游戏目标是合成最高级的水果:大西瓜!当堆积的水果超过顶部红线时则游戏结束。


整理出需要实现的核心逻辑:

  • 生成水果

  • 水果下落与碰撞

  • 水果消除动画效果及升级逻辑


预备工作


cocos creator 基本概念


整个项目使用 cocos creator v2.4.3 实现,建议初次了解的同学可以先过一下官方文档,本文不会过多介绍 creator 的使用(主要是我也不太熟练 hah)。


官方文档链接:
https://docs.cocos.com/creator/2.3/manual/zh/

游戏素材


首先需要准备美术资源,本位所有美术素材和音频材料均来源于:

www.wesane.com/game/654/

首先访问游戏网站,打开 network 面板,可以看见游戏依赖的所有美术资源,我们下载自己所需的文件即可。

所需的图片资源包括:

  • 11 张水果贴图

  • 每种水果合成效果贴图,均包含:一张果粒图片,一张圆形水珠图片,一张爆炸贴图

  • 两个西瓜合成时有灯光和撒花的效果,时间有限暂不实现

  • 音频文件同理,可以在 Filter 栏选择 .mp3 后缀的请求快速筛选对应资源。水果消除时的爆炸声和水声,音频文件同理,可以在 Filter 栏选择 .mp3 后缀的请求快速筛选对应资源


创建游戏场景和背景


打开 cocos creator,新建一个项目(也可以直接导入从 github 下载的项目源码):
https://github.com/tangxiangmin/cocos-big-watermelon




    


然后记得将刚才下载的素材资源拖拽到右下角的资源管理器中。


创建 scene 和背景节点


项目初始化之后,在左下角资源管理器新建一个游戏 Scene,取名 game 作为游戏主场景。

创建完毕后就可以在资源管理器的 assets 中看见刚才创建的名为 game 的 scene。


选择 game 场景,在左上角的层级管理器中可以看见场景的 Canvas 画布根节点,cocos 默认画布是横屏的 960*640,可以选择根节点然后再右侧属性检查器中调整宽高为 640*960。

接下来创建背景层,我们在 Canvas 节点下面新建一个 background 节点,由于整个背景是纯色 #FBE79D 的,因此使用一个单色 Sprite 填充即可。

同样将 background 节点宽高调整为整个画布的大小,由于默认锚点均为 0.5*0.5,此时整个画布会被完全填充。


现在整个游戏场景大概是这个样子的:

接下来设计游戏的逻辑脚本部分。


场景脚本组件


在 assets 目录下新建一个 js 脚本,按照惯例命令成 Game.js,creator 会生成一个带基础 cc.Class 的模板文件。

先将脚本组件与节点关联起来,选择 Canvas 根节点,在右侧属性检查器中添加组件,然后选择刚才创建的这个 Game 组件。

然后编写具体的代码逻辑,打开 Game.js 文件(建议使用 vscode 或者 webstrom 打开整个项目的根目录进行编辑)。


里面的初始代码大概长这样:
// Game.js
cc.Class({
    extends: cc.Component,

    properties: {

    },
    onLoad(){

    },
    start(){ }
})

我们需要在这里维护整个游戏的逻辑,后面逐步添加代码内容。


创建水果


水果是整个游戏的核心元素,在游戏中被频繁创建和销毁。


生成单个水果预制资源


这种动态创建的节点可以通过预制资源 Prefab 来控制,制作 Prefab 最简单的方式就是将资源从资源管理器拖动到场景编辑器中,然后再将层级管理器中的节点拖回资源管理器。


这里以等级最低的水果“葡萄”为例:


然后将层级管理器中的节点删除,这样我们就得到了一个 fruit 的预制资源,在脚本组件中,就可以使用代码通过预制资源动态生成节点了。


修改 Game.js,添加一个属性 fruitPrefab,其类型为 cc.Prefab:
// Game.js
properties: {
    fruitPrefab: {
        defaultnull,
        type: cc.Prefab
    },
}

回到 creator,选择 Canvas 节点,可以在属性检查器中的 Game 组件栏目看见和修改该属性了。


我们将刚才制作的 prefab 资源从资源管理器拖动到这里,在初始化的时候,有 cocos 负责初始化对应的属性数据:

创建单个水果


回到 Game.js,开始编写真正的逻辑:创建一个葡萄。
// Game.js




    

onLoad(){
    let fruit = cc.instantiate(this.fruitPrefab);
    fruit.setPosition(cc.v2(0400));

    this.node.addChild(fruit);
}

预览模式下就可以看见屏幕正上方有一个葡萄了:

Nice,非常好的开始!


此外,由于水果还包含一些特定的逻辑,我们可以向它添加一个 Fruit 脚本组件,虽然目前看起来还没有什么用!


创建 Fruit 脚本组件与上面创建 Game 组件类似,然后选择刚才制作的 prefab 重新编辑,关联上 Fruit 用户脚本组件即可。


动态维护多种水果


整个游戏共 11 种水果(当然也可以添加或者改成其他的东西),如果每种水果都像上面去手动生成预制资源然后分别初始化,那也太繁琐了,我们需要解决动态渲染多种水果的方式。


我们需要获得每种水果的贴图信息,然后在实例化水果时选择对应贴图即可,最简单的方式就是维护一个配置表,每行的数据字段包括 id 和 iconSF。
const FruitItem = cc.Class({
    name: 'FruitItem',
    properties: {
        id: 0// 水果的类型
        iconSF: cc.SpriteFrame // 贴图资源
    }
});

然后为 Game 脚本组件新增一个 fruits 属性,用于保存每种水果的配置信息,其类型是数组,数组内元素类型为刚才创建的 FruitItem。
// Game.js
properties: {
    fruits: {
        default: [],
        type: FruitItem
    },
}


回到编辑器,这时候可以发现 Game 组件的属性下面多了一个 Fruits 属性,将其长度修改为 11,然后依次编写每个水果的 id,同时将其贴图资源从资源编辑器贴过来(体力活)。

这样我们只需要传入想要制作的水果 id,就可以获取到对应的配置信息,并动态修改贴图了。


这种初始化的逻辑应该由水果自己维护,因此放在刚才创建的 Fruit 组件中,我们暴露一个 init 接口出来。
// Fruit.js
properties: {
    id: 0,
},
// 实例放在可以在其他组件中调用
init(data) {
    this.id = data.id
    // 根据传入的参数修改贴图资源
    const sp = this.node.getComponent(cc.Sprite)
    sp.spriteFrame = data.iconSF
},

然后修改一下上面的初始化水果的代码:
// Game.js
createOneFruit(num) {
    let fruit = cc.instantiate(this.fruitPrefab);
    // 获取到配置信息
    const config = this.fruits[num - 1]

    // 获取到节点的Fruit组件并调用实例方法
    fruit.getComponent('Fruit').init({
        id: config.id,
        iconSF: config.iconSF
    });
}

这样就可以愉快的创建各种水果了。


监听点击事件


cocos 提供了各种事件监听,前端和客户端同学一定不会陌生。


整个游戏会在点击屏幕时创建一个水果,这只要监听一下全局点击事件即可,这个逻辑同样放在 Game 脚本组件中。
onLoad() {
    // 监听点击事件
    this.node.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this)
},
onTouchStart(){
    this.createOneFruit(1// 生成水果
}

实际游戏中还需要处理随机生成水果、上一个水果在点击的 x 轴下落等细节逻辑,这里不再赘述。


物理系统:自由落体与刚体碰撞


上面处理了水果创建的逻辑,在整个游戏中,水果是可以产生下落及弹性碰撞等物理效果的,利用 cocos 内置的物理引擎,可以很方便的实现。


对 cocos 引擎不熟悉的同学可以先看看这个官方 demo,里面展示的比较详细(起码比文档要更容易理解)。


开启物理引擎与碰撞检测


首先是开启物理引擎,以及设置重力大小:
const instance = cc.director.getPhysicsManager()
instance.enabled = true
// instance.debugDrawFlags = 4
instance.gravity = cc.v2(0-960);

然后需要开启碰撞检测,默认是关闭的:
const collisionManager = cc.director.getCollisionManager();
collisionManager.enabled = true

然后设置四周的墙壁用于碰撞,这样水果就不会无限制往下面掉落了:
 // 设置四周的碰撞区域
let width = this.node.width;
let height = this.node.height;

let node = new cc.Node();

let body = node.addComponent(cc.RigidBody);
body.type = cc.RigidBodyType.Static;

const _addBound = (node, x, y, width, height) => {
    let collider = node.addComponent(cc.PhysicsBoxCollider);
    collider.offset.x = x;
    collider.offset.y = y;
    collider.size.width = width;
    collider.size.height = height;
}

_addBound(node, 0, -height / 2, width, 1);
_addBound(node, 0, height / 2, width, 1);
_addBound(node, -width / 201, height);
_addBound(node, width / 201, height);

node.parent = this.node;

现在我们就开启了游戏世界的物理引擎,然后还需要配置需要受引擎影响的节点,也就是我们的水果。








请到「今天看啥」查看全文