当前位置: 首页 > news >正文

【 Cocos Creator 项目实战】益智游戏《2048》(附带完整源码工程)

本文乃Siliphen原创,转载请注明出处

目录

游戏介绍

概述

游戏整体流程

游戏框架设计

主要流程控制类

本文项目的代码组织结构

构建游戏世界

数字方块

地图

 触摸手势识别

防触摸抖动

判断用户输入的方向

地图

任意大小的地图

初始化地图大小

地图绘制

合并和移动

合并和移动的逻辑

丝滑的合并和移动动画

本文的完整实现源码工程


游戏介绍

《2048》是一款曾经风靡全球的数字益智游戏。

目前(2023.08.14)在 App Store 的情况如下图:

关于这个游戏的更多情况可看看百度百科:百度百科-验证

概述

本文讲解用 Cocos Creator 实现经典《2048》的核心流程和算法。

Cocos Creator 版本:Cocos Creator 3.8.0

本文实现的游戏效果如下:

 

可以随意调整地图大小。可随意调整方块移动速度。

上图分别演示了 4 x 4 ,7 x 10 地图大小的效果。

可在这个地址运行体验下本文实现的版本:Cocos Creator | 2048

文本末尾给出完整实现的源码工程。

游戏整体流程

游戏执行一轮玩家操作的流程:等待玩家输入操作 -> 用户滑动屏幕 -> 移动数字方块 -> 合并方块 -> 空白地方随机出现一个数字方块 -> 等待玩家输入操作

以上流程是游戏玩家操作一次,游戏执行一轮的分解动作循环。

游戏通关条件:合成数字方块2048。

游戏失败条件:当整个棋盘都填满数字方块,且没有可以合并的方块。

游戏框架设计

主要流程控制类

从调用先后顺序开始依次如下:

类名

作用

UiTouch

处理用户触摸输入

Merge

处理移动、合并的逻辑和动画。

FlowRound.fillEntity()

在地图空白处随机生成一个数字方块。

FlowRound.judge()

判断输赢。

本文项目的代码组织结构

构建游戏世界

《2048》的游戏世界只有2个实体:数字方块、棋盘地图。

棋盘是数字方块的容器。后面的移动和合并算法,都是作用在棋盘上计算的。

数字方块

// 实体
export class Entity {// 实体所代表的数值public val : number ;// 表现public presentation = new EntityPresentation() ;}// 实体表现
export class EntityPresentation {public root : Node ;}

地图

地图数据本质是个二维数组。定义如下:

export class Map {// 单件public static ins : Map = null ;// 地图单元格public grid = new Array< Array< MapCell > >() ;// 格子宽高public size = new Size() ; // 表现public presentation = new MapPresentation() ;}// 地图表现
export class MapPresentation {// 根节点public root : Node ;}// 地图单元格
export class MapCell {// 单元格上的实体public entity : Entity = null ;// 单元格所在的局部空间的坐标public pos : Vec3 = null ;}

我们规定地图单元格(0,0)的位置在地图显示的左下角。x , y 的增长分别向右边和上边延伸。如下图:

 触摸手势识别

防触摸抖动

在触摸按下时记录按下的坐标,在触摸结束时用结束时的坐标减去按下时的坐标,得到一个向量。

判断这个向量的长度,大于某个数值后,就认为是有效的输入。

如果只是个很小的滑动,可能是抖动造成的,为了防止玩家误操作,可以丢弃这种输入。

判断用户输入的方向

用上一步减法得到的向量就可以判断用户操作的方向。

主要是用到 Math.atan2 这个系统函数。atan2 判断一个向量与 x 轴正方向的夹角,单位是弧度。

注意:atan2 的参数是 ( y , x ) , 不是( x , y ),y 是第一个参数。

不习惯使用弧度的话,可以转换成角度。

判断角度代码如下:

// 手势识别
export class GestureRecognition {// 返回:上左下右 1234 从上开始顺时针。0 无效方向public static exe( v : Vec2 ) : number {let rad = Math.atan2( v.y , v.x ) ;let degree = rad * ( 180 / Math.PI ) ;if( 45 < degree && degree < 135 ){return 1 ;} else if( -45 < degree && degree < 45  ){return 2 ;} else if( -135 < degree && degree < -45 ){return 3 ;}else if( 135 < degree && degree < 180 || -180 < degree && degree < -135 ){return 4 ;}// console.log( "度数:" + degree ) ;return 0 ;}}

地图

任意大小的地图

本文的实现,可设置任意地图大小。

如下图:

上图展示了这些尺寸的地图大小效果:3 x 3 , 5 x 5 , 6 x 4 , 7 x 10

不同地图尺寸对应不同的地图根结点缩放值。

要实现可指定任意大小的地图的前提是,动态绘制地图。

先初始化地图二维数组结构的大小,然后,地图绘制类再处理地图的绘制。

初始化地图大小

// 地图数据初始化
export class MapInit {public static exe( map : Map ) {map.presentation.root = find( "Canvas/Map" ) ;// 地图宽高let mapWidth = 3 ; let mapHeight = 3 ; map.size = new Size( mapWidth , mapHeight ) ;for( let y = 0 ; y < mapHeight ; ++y ) {let row = new Array< MapCell >( ) ;map.grid.push(row) ;for( let x = 0 ; x < mapWidth ; ++x ){let cell = new MapCell() ; row.push( cell ) ;} // end for} // end for}}

地图绘制

本文实现的中心对其的地图布局,地图的几何中心点与其父节点的原点重叠。

算法是,先算出整个地图的大小,然后宽高分别除以2,先算出 ( 0 , 0 ) 起始逻辑坐标单元格的位置。

先算出左下角的起始单元格的位置,后续可以统一处理其他单元格位置。仅仅是通过不断累加间隔就行。

地图绘制 具体实现查看源码工程的类 MapDraw

设置地图大小位置:MapInit.exe 函数

合并和移动

这个是2048的核心玩法实现,也是最难的部分。

合并和移动的逻辑

可以先算方块逻辑上的合并,后算方块逻辑上的移动。

也可以合并和移动合并在一起计算。

上下左右的合并和移动要分别处理。

这里列举用户向左( <- )滑动的处理算法,其他3个方向的以此类推。为了说明原理和简单起见以下为描述性伪代码。

// 一行行遍历地图。从左到右(->)
for( let y = 0 ; y < map.size.height ; ++y ) {for( let x = 0 ; x < map.size.width ; ++x ) {let cell = map.grid[ y ][ x ] ;// 如果单元格上没有实体,略过。因为我们只处理实体。不处理空格。if( cell.entity == null ) continue ;let cell2 = 向右(->)查找一个最近的实体所在的单元格。if( cell2 != null && cell2 和 cell 的实体数字相同 ){   // 这表示找到一个可以合并的实体2个实体合并,合并和的实体放在 cell 单元格的位置上。}以 cell 单元格为起点向左(<-)查找一个连续的空位的最右边的那个空位这个空位便是 cell 上的方块实体要移动到的位置。} // end for
} // end for

算法图示:

丝滑的合并和移动动画

如果只是按照上一步说的先在逻辑上计算合并和移动的结果,然后直接更新画面显示,会显得很生硬。

大部分的瞬间更新结果都会让画面显得生硬。好的做法是,有个滑动和合并的移动缓动动画。

加入动画后,流程就变成了:

所有的方块都会先移动到一边,然后进行合并,如果合并后留出了空位,需要再移动。保证移动后,中间不留空位。

这个流程需要对以上的逻辑处理进行改造。

具体实现查看源码工程的类 Merge

本文的完整实现源码工程

源码工程下载地址:Cocos Store

作者创作不易,您的支持让我创造出更多更好的作品。​:)

【 Cocos Creator 项目实战】系列文章链接:

【 Cocos Creator 项目实战】益智游戏《2048》

​​​​​​【Cocos Creator 项目实战 】消灭星星加强版

相关文章:

【 Cocos Creator 项目实战】益智游戏《2048》(附带完整源码工程)

本文乃Siliphen原创&#xff0c;转载请注明出处 目录 游戏介绍 概述 游戏整体流程 游戏框架设计 主要流程控制类 本文项目的代码组织结构 构建游戏世界 数字方块 地图 触摸手势识别 防触摸抖动 判断用户输入的方向 地图 任意大小的地图 初始化地图大小 地图绘制…...

剑指Offer68-II.二叉树的最近公共祖先 C++

1、题目描述 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个结点 p、q&#xff0c;最近公共祖先表示为一个结点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也可以…...

【JAVA】我们该如何规避代码中可能出现的错误?(一)

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️初识JAVA】 文章目录 前言三种类型的异常异常处理JAVA内置异常类Exception 类的层次 前言 异常是程序中的一些错误&#xff0c;但并不是所有的错误都是异常&#xff0c;并且错误有时候是可以避免的&…...

openLayers实战(八):坐标系及其转换

坐标系介绍 EPSG: 3857 --web地图&#xff0c;基于球体的、web墨卡托投影&#xff08;伪墨卡托投影Pseudo-Mercator&#xff09;的投影坐标系&#xff0c;范围为纬度85度以下&#xff0c;由于google地图最先使用而成为事实标准。至今&#xff0c;大多互联网地图都使用EPSG3857&…...

DAY06_SpringBoot—简介基础配置yaml多环境开发配置整合第三方技术

目录 一 SpringBoot简介1. 入门案例问题导入1.1 入门案例开发步骤1.2 基于SpringBoot官网创建项目1.3 SpringBoot项目快速启动 2. SpringBoot概述问题导入2.1 起步依赖2.2 辅助功能 二 基础配置1. 配置文件格式问题导入1.1 修改服务器端口1.2 自动提示功能消失解决方案1.3 Spri…...

无涯教程-Perl - setpwent函数

描述 此功能将枚举设置(或重置)到密码条目集的开头。应该在第一次调用getpwent之前调用此函数。 语法 以下是此函数的简单语法- setpwent返回值 此函数不返回任何值。 例 以下是显示其基本用法的示例代码- #!/usr/bin/perlwhile(($name, $passwd, $uid, $gid, $quota, …...

代码随想录-数组篇

2-二分查找 方法一&#xff1a; 左闭右闭&#xff0c;[left, right] class Solution { public:int search(vector<int>& nums, int target) {//[left, right]int left 0;int right nums.size() - 1 ;while(left < right){int middle left ((right - left)…...

vue3+element-plus表格默认排序default-sort失效问题

场景 在使用动态数据渲染的场景&#xff0c;el-table设置默认属性default-sort失效。 原因 el-table的default-sort属性是针对静态数据的&#xff0c;如果是动态数据&#xff0c;default-sort则无法监听到。 案例&#xff1a;静态数据 <template><el-table:data&…...

CH32V203 单片机 I2C 使用

CH32V203系列是基于32位RISC-V内核设计的工业级增强型低功耗通用微控制器&#xff0c;高性能&#xff0c;最高支持144MHz系统主频&#xff0c;低功耗&#xff0c;运行功耗低至45uA/MHz。CH32V203集成双路USB接口&#xff0c;支持USB Host主机及USB Device设备功能&#xff0c;具…...

链表OJ题

今天讲一些关于链表的Oj题&#xff0c;相信你看完对链表又提升一个档次。 题目一 思路一 遍历一遍链表是Val值得时候free这个&#xff0c;然后我们往后走&#xff0c;一直走到末尾空指针得时候&#xff0c;新链表就是我们得答案&#xff0c;那我们用代码来表示一下吧。 struct…...

Llama 2免费托管及API提供

Llama 2 是 Meta 最新的文本生成模型&#xff0c;目前其性能优于所有开源替代方案。 推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 1、强大的Llama 2 它击败了 Falcon-40B&#xff08;之前最好的开源基础模型&#xff09;&#xff0c;与 GPT-3.5 相当&#xff0c;仅低…...

回到未来:使用马尔可夫转移矩阵分析时间序列数据

一、说明 在本文中&#xff0c;我们将研究使用马尔可夫转移矩阵重构时间序列数据如何产生有趣的描述性见解以及用于预测、回溯和收敛分析的优雅方法。在时间上来回走动——就像科幻经典《回到未来》中 Doc 改装的 DeLorean 时间机器一样。 注意&#xff1a;以下各节中的所有方程…...

vue element 多图片组合预览

定义组件&#xff1a;preview-image <template><div><div class"imgbox"><divclass"preview-img":class"boxClass"v-if"Imageslist 3 ||Imageslist 5 ||Imageslist 7 ||Imageslist 8 ||Imageslist > 9"&…...

Vue2集成Echarts实现可视化图表

一、依赖配置 1、引入echarts相关依赖 也可以卸载原有的&#xff0c;重新安装 卸载&#xff1a;npm uninstall echarts --save 安装&#xff1a;npm install echarts4.8.0 --save 引入水球图形依赖 npm install echarts-liquidfill2.0.2 --save 水球图可参考文档&#xff1…...

3 Python的数据类型

概述 在上一节&#xff0c;我们介绍了Python的基础语法&#xff0c;包括&#xff1a;编码格式、标识符、关键字、注释、多行、空行、缩进、引号、输入输出、import、运算符、条件控制、循环等内容。Python是一种动态类型的编程语言&#xff0c;这意味着当你创建一个变量时&…...

new String()到底创建了几个对象

题目&#xff1a; new String&#xff08;"abc"&#xff09;会创建几个对象&#xff1f; 看字节码&#xff0c;就知道是两个。...

第五十五天

CSS3 ●背景 CSS3 中包含几个新的背景属性&#xff0c;提供更大背景元素控制&#xff1a; •background-image&#xff1a;添加背景图片。不同的背景图像和图像用逗号隔开&#xff0c;所有的图片中显示在最顶端的为第一张。 •background-size&#xff1a;指定背景图像的大…...

【推荐】深入浅出benan的生命周期

目录 1.spring 管理JavaBean的过程&#xff08;生命周期&#xff09; 2.spring的JavaBean管理中单例模式及原型&#xff08;多例&#xff09;模式 2.1 . 默认为单例&#xff0c;但是可以配置多例 2.2.举例论证 2.2.1 默认单例 2.2.2 设置多例 2.2.3单例与多例的初始化的时…...

mysql使用redis+canal实现缓存一致性

目录 一、开启binlog日志 1.首先查看是否开启了binlog 2、开启binlog日志&#xff0c;并重启mysql服务 二、授权 canal 链接 MySQL 账号具有作为 MySQL slave 的权限 三、下载配置canal 1、下载 canal, 访问 release 页面 , 选择需要的包下载, 如以 1.0.17 版本为例 2、 …...

9.利用matlab完成 泰勒级数展开 和 符号表达式傅里叶变换和反变换 (matlab程序)

1.简述 matlab之傅里叶变换和逆变换 首先生成一个方波&#xff08;或者其他组合波形&#xff09;&#xff0c;然后对这个信号做傅里叶变换&#xff0c;拆解到频域&#xff0c;可以看到这个信号是由哪些频率的信号叠加而来。 然后把频域信号&#xff0c;用傅里叶逆变换恢复到时…...

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表&#xff1f;1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...

深入理解Optional:处理空指针异常

1. 使用Optional处理可能为空的集合 在Java开发中&#xff0c;集合判空是一个常见但容易出错的场景。传统方式虽然可行&#xff0c;但存在一些潜在问题&#xff1a; // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...

6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础

第三周 Day 3 &#x1f3af; 今日目标 理解类&#xff08;class&#xff09;和对象&#xff08;object&#xff09;的关系学会定义类的属性、方法和构造函数&#xff08;init&#xff09;掌握对象的创建与使用初识封装、继承和多态的基本概念&#xff08;预告&#xff09; &a…...

Ray框架:分布式AI训练与调参实践

Ray框架&#xff1a;分布式AI训练与调参实践 系统化学习人工智能网站&#xff08;收藏&#xff09;&#xff1a;https://www.captainbed.cn/flu 文章目录 Ray框架&#xff1a;分布式AI训练与调参实践摘要引言框架架构解析1. 核心组件设计2. 关键技术实现2.1 动态资源调度2.2 …...

轻量安全的密码管理工具Vaultwarden

一、Vaultwarden概述 Vaultwarden主要作用是提供一个自托管的密码管理器服务。它是Bitwarden密码管理器的第三方轻量版&#xff0c;由国外开发者在Bitwarden的基础上&#xff0c;采用Rust语言重写而成。 &#xff08;一&#xff09;Vaultwarden镜像的作用及特点 轻量级与高性…...