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

为什么要建设政府网站/东莞做网络推广的公司

为什么要建设政府网站,东莞做网络推广的公司,做网站一般用什么语言,婚恋网站上认识人 带你做原油交易本文乃Siliphen原创,转载请注明出处 目录 概述 游戏整体流程 游戏框架设计 单一职责的类 主要流程控制类 核心玩法模块 UI: 游戏世界: 本文项目的代码组织结构 作者项目实践总结 场景只有一个入口脚本 尽量少在节点上挂载脚本 构…

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

目录

概述

游戏整体流程

游戏框架设计

单一职责的类

主要流程控制类

核心玩法模块

UI:

游戏世界:

本文项目的代码组织结构

作者项目实践总结

场景只有一个入口脚本

尽量少在节点上挂载脚本

构建游戏世界

ECS 设计

消除物

棋盘地图

逻辑计算和显示分离

消除的实现

查找联通分量

逐步由内向外扩张的消除动画

掉落的实现

合并的实现

道具的实现

本文的完整实现源码工程


概述

《消灭星星》是一个爆款休闲游戏,累计用户5亿+。

目前(2023.08.06)在 App Store 上39.6万个评分,评分4.6,益智解谜类第7名。

参考链接:‎‎App Store 上的“消灭星星全新版®”

本文讲解用 Cocos Creator 实现一款加强效果版的《消灭星星》的核心流程和算法。

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

 

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

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

游戏整体流程

游戏执行一轮玩家操作的流程:等待玩家点击操作 -> 用户点击 -> 消除-> 掉落 -> 合并 -> 等待玩家点击操作

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

整个游戏的组成:游戏由N关组成,一关由N轮玩家操作组成。

游戏框架设计

单一职责的类

可以把一轮中的每个动作都独立成一个控制类,每个控制类只负责一种动作,比如A类只负责消除控制,B类只负责掉落控制。

这是敏捷开发中的重要原则:单一职责。一个类的功能越是单一,它就越内聚、越和其他系统解耦合。

每种控制类在它负责的单一动作执行完成后,用回调通知其他系统,它已经完成,可以进行下一步操作。

比如:消除控制系统处理消除完成后,用一个 onComplete 回调通知外界,已经完成了消除这个动作。

掉落控制系统监听消除控制系统的 onComplete ,处理消除后下一步的掉落控制。

主要流程控制类

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

类名

作用

UiTouch

处理用户触摸输入

Eliminate

处理消除。消除上下左右连色的实体。

Fall

消除后会留下空位,控制消除实体掉落下来。

Merge

掉落后,如果有空的列,那么向左边靠拢合并起来。

核心玩法模块

核心玩法分成2大部分:UI、游戏世界

UI:

包括:按钮、弹窗、分数显示、玩家输入 等部分。是所有用户界面的集合。

这个部分的开发有点类似于做 APP。可以用 MVC 等 APP 常用开发模式。

游戏世界:

这是游戏开发独有的部分。处理游戏世界中游戏实体的行为、游戏实体之间的关系和交互、游戏世界的规则等。

游戏核心玩法的开发主要关注这部分。

因为游戏世界不可能简单分为几个层,比如,什么显示层,逻辑层,数据层等。

有可能实体之间的关系和交互很复杂,MVC 等传统 APP 开发模式并不适用。一般大型游戏会采用 ECS 等设计。

本文项目的代码组织结构

如下图:

作者项目实践总结

场景只有一个入口脚本

一个场景只挂一个入口脚本,各种节点的引用使用 find、node.getChildByPath 等去查找。

就像 C/C++ 语言有一个唯一入口函数 main 。

这样做的好处是:在代码中初始化各个系统,有明确的初始化顺序。

在多个节点上挂多个脚本,默认情况下有个问题,哪个脚本先执行哪个脚本后执行。有时候执行顺序是非常重要的。

编辑器可以指定节点上挂的脚本的执行顺序,但这是额外的维护负担。不如在代码中指定的维护性好。

尽量少在节点上挂载脚本

少挂载脚本的好处是:

  • 降低脚本Missing情况的维护成本。
  • 节约性能。
  • 提高项目移植性。比如移植到其他引擎上。

想象一个情况,一个场景中有很多节点,很多节点都挂有脚本。出于某些原因,脚本和节点的挂载关系丢失了。

编辑器节点上要么不显示脚本,要么脚本显示为丢失(Missing)。

场景简单还好,重新手动拖脚本到节点上。场景复杂,那就很麻烦。绝大部分情况,只是知道节点Missing了脚本,但不知道Missing的是哪个脚本。

为什么有时候会Missing脚本?原因很多,可能有如下几种:

* 用户误操作。比如 破坏了 *.meta 文件。

* 多人协作 *.meta 文件冲突,导致脚本丢失。

* 引擎版本多次低级高级来回切换。

* 一些说不清楚的,莫名其妙的情况。

构建游戏世界

《消灭星星》的游戏世界只有2个实体:消除物、棋盘地图。

棋盘没有画面表现。棋盘是消除物的容器,棋盘限定了消除物计算规则和运动规则。

后面的查找消除算法和掉落控制,都是作用在棋盘上计算的。

ECS 设计

本项目使用类似 ESC 的设计,非严格意义上的 ECS ,是如下定义:

Entity 是 Componet 的容器。Component 只有数据,没有逻辑。System 没有数据,只有逻辑。

实体和游戏世界的交互实现,实体和实体之间的交互实现,都放在 System 中。

这种设计的好处是:高扩展性。高维护性。易于移植到其他引擎。易于引擎升级。

消除物

定义如下:

// 消除物实体
export class Elimination 
{// 类型IDpublic kindId = "" ;public presentaion = new EliminationPresentaion() ;}// 消除物表现组件
export class EliminationPresentaion
{// 根节点public node : Node  = null ;// 动画public amin : Animation = null ; }

Elimination 是消除物实体类。EliminationPresentaion 是消除物实体的表现组件类。

实体类只是组件类的容器。实体类和组件类都只有定义,没有逻辑。

棋盘地图

棋盘数据本质是个二维数组。定义如下:

// 地图数据
export class MapData 
{// 单件/*  */public static ins = new MapData() ;// 数据网格public grid = new Array< Array< Cell > >() ;// 地图大小public size = new Size();// 是否是有效地坐标public isValid(coord : Vec2) : boolean{if( coord.x < 0 || coord.y < 0 || coord.x >= this.size.x || coord.y >= this.size.y ) return false ;return true ;}}// 地图单元格
export class Cell 
{// 消除物public elimination : Elimination = null ;// 坐标public coord = new Vec2();// 在世界空间中单元格的位置。public pt = new Vec3(); }

二位数组对应的位置如下图:

左下角的索引是(0,0),右上角是(9,9)。

逻辑计算和显示分离

先计算好结果后再播放达到这个结果的过渡动画。逻辑计算和播放显示动画的分离可以让代码结构更清晰,维护性更高。

后面的处理都是先在内存中计算好地图状态:消除后地图哪些单元格为空,掉落后消除物实体都落在哪个单元格上 等。

计算好地图状态后再处理画面显示:播放消除动画,播放掉落动画等。

消除的实现

先看下文本实现的消除效果:

 大部分《消灭星星》的实现都是点击后瞬间一起消除。

本文做了不一样的效果,从点击的消除物开始逐步由内向外扩张的消除。

不管是瞬间消除,还是某种控制动画消除,第一步都是“查找相邻的同类消除物”。

查找联通分量

术语“查找联通分量”很多《数据结构》的书都会有介绍。此处,我们用来查找相邻的同类消除物。

使用深度优先搜索(DFS)实现,输出一颗树。树的根结点是玩家点击的那个消除物。

为什么要输出一棵树?因为要按照树的层次进行消除才能实现逐步由内向外扩张的动画。

具体实现可查看工程源码的 ConnectionFind.find 函数。

这里为了讲解算法原理,用伪代码说明算法的核心思想。

// start 是点击的消除物
dfs( start )
{// 结果数据结构,用 Map 表示一棵树。key 是一个被发现的消除物,value 是这个消除物的父节点。let ret = new Map< Elimination , Elimination >() ; // 创建一个栈 stack q ;let q = new Stack() ; // 访问记录。该数据结构是为了防止重复访问那些已经访问过的消除物let visit = Set< Elimination >() ;q.push( start ) ; // 起始点入栈ret.set( start , null ) ; // 点击的消除物是根结点,根结点没有父节点。for( ; q.count > 0 ; ) // 栈不为空就一直循环{let t = q.pop() ; // 出栈一个节点let list = expand( t ) ; // 查找出栈节点上下左右4个方向相邻的同样的节点foreach( let t2 in list ) // 所有查找出来的节点入栈{if( visit.has( t2 ) ) continue ; // 跳过访问过的消除物q.push( t2 ) ;        ret.add( t2 , t ) ; // 发现一个行节点t2,它的父节点是t。visit.add( t2 ) ;} // end for} // end forreturn ret ;                                        
}

《消灭星星》最难的算法就是这个“查找联通分量”了。

如果一下子不理解也没关系,可以反复琢磨下本文作者的伪代码和具体实现。

或者是查阅数据结构或算法的书籍,深入、详细的学习下。加油!:)

逐步由内向外扩张的消除动画

在上一步中,我们获得了一颗消除物节点树。是一个键值对数据结构,key 表示发现的节点,value 表示发现的节点的父节点。

这里,我们处理这棵树结构为按照树的层次划分的数据结构:let levels = new Array< Array< Elimination > >()

levels[ 0 ] 表示树第 1 层的节点集合。树根只有一个起始节点。

levels[ 1 ] 表示树第 2 层的节点集合。

... 以此类推

间隔一层层的整体消除即可。

如何把 Map< Elimination , Elimination > 处理成 Array< Array< Elimination > > 的层次结构呢?

遍历这个 Map,对每个 key 向上查找,直到查到 null 遇到根结点为止。就可以得知当前 key 所在的层次。

按照层次放入对应的 Array 数组容器中即可。

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

掉落的实现

消除后,棋盘地图的一些被消除的消除物所在的单元格会被设为空。上面的消除物会掉落下来。

从棋盘底部向上一行行遍历,遇到一个消除物后,向下查找一个空位,如果能找到一个空位,就把这个消除物设置到那个空位上。

先设置棋盘的逻辑状态。后计算被移动的消除物的新的显示位置,做一个移动动画即可。

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

合并的实现

本文实现的合并效果如下图:

 

合并的处理在掉落之后。

遍历棋盘最底部的那一行,遍历顺序从左到右。

因为之前已经执行了掉落,最底部的一行有空位的话,就说明有棋盘地图有一列为空。

如果发现了一个空位,就说明需要合并,向后查找一个非空列,整体移动那一列的消除物到空位即可。

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

道具的实现

经典的消灭星星有3个道具:指定一个消除物替换为另一个指定的消除物、九宫格炸弹,全体消除物随机变换。

九宫格炸弹

具体实现查看源码工程的类 PropBombNine、TouchPropBombNine

全体消除物随机变换

遍历整个棋盘地图,随机替换消除物即可。

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

单点替换

这个道具的实现相对以上2个比较特殊,耦合了点击操作。

先要设置触摸模式为使用道具,然后玩家点击后,如果点击的是一个消除物,

就在这个消除物的上方显示替换UI,供玩家选择变换后的消除物。

具体实现查看源码工程的类 PropChangeOne、TouchPropChangeOne

本文的完整实现源码工程

源码工程下载地址:Cocos Store

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

相关文章:

【Cocos Creator 项目实战 】消灭星星加强版(附带完整源码工程)

本文乃Siliphen原创&#xff0c;转载请注明出处 目录 概述 游戏整体流程 游戏框架设计 单一职责的类 主要流程控制类 核心玩法模块 UI&#xff1a; 游戏世界&#xff1a; 本文项目的代码组织结构 作者项目实践总结 场景只有一个入口脚本 尽量少在节点上挂载脚本 构…...

2023软件测试岗必问的100个面试题【含答案】

一、测试理论 1.什么是软件测试&#xff1f; 答&#xff1a;软件测试是通过执行预定的步骤和使用指定的数据&#xff0c;以确定软件系统在特定条件下是否满足预期的行为。 2.测试驱动开发&#xff08;TDD&#xff09;是什么&#xff1f; 答&#xff1a;测试驱动开发是一种开…...

MediaExtractor MediaCodec手动解码播放音乐

MediaExtractor MediaCodec手动解码播放音乐,笔记 private class DecodeAudio implements Runnable {Overridepublic void run() {//开始播放pcmaudioTrack.play();MediaExtractor extractor null;MediaCodec codec null;Log.i(TAG, "run: init");FileOutputStrea…...

element表格+表单+表单验证结合运用

目录​​​​​​​ 一、结果展示 二、实现代码 一、结果展示 1、图片 2、描述 table中放form表单&#xff0c;放输入框或下拉框或多选框等&#xff1b; 点击添加按钮&#xff0c;首先验证表单&#xff0c;如果存在没填的就验证提醒&#xff0c;都填了就向下添加一行表单表…...

亚马逊云科技发布Amazon HealthScribe,使用生成式AI技术实现临床文档的自动生成

近日&#xff0c;亚马逊云科技在纽约峰会上推出了Amazon HealthScribe&#xff0c;该服务符合HIPAA&#xff08;《健康保险流通与责任法案》&#xff09;的相关要求&#xff0c;可为医疗软件供应商提供一种基于语音和文字识别的生成式AI技术&#xff0c;帮助其创建能够自动生成…...

Windows11安装Linux子系统,并实现服务自启动,局域网访问,磁盘挂载

Windows11安装Linux子系统&#xff0c;并实现服务自启动&#xff0c;局域网访问&#xff0c;磁盘挂载 一、准备工作二、安装Linux子系统(wsl2)三、为Linux子系统设置桥接网络检查wsl版本在 Hyper-V 管理器中创建虚拟交换机创建 WSL 配置文件启动wsl 四、设置Windows开机自启动L…...

【Git】保姆级详解:Git配置SSH Key(密钥和公钥)到github

博主简介&#xff1a;22级计算机科学与技术本科生一枚&#x1f338;博主主页&#xff1a;是瑶瑶子啦每日一言&#x1f33c;: “当人们做不到一些事情的时候&#xff0c;他们会对你说你也同样不能。”——《当幸福来敲门》 克里斯加德纳 Git配置SSH Key 一、什么是Git?二、什么…...

离线环境conda虚拟环境备份迁移--conda pack问题

1.第一步&#xff1a;创建虚拟环境 conda create -n pyenv --clone base 或者 conda create -n pyenv python3.8.5 --offline 命令执行结束&#xff0c;在路径/xxxx/anaconda/envs 下看到pyenv 或者 conda info --envs 查看罗列虚拟环境 2.第二步&#xff1a;打包环境 conda …...

挂载 IK 分词器至 Elasticsearch Docker 容器 - Docker Docker Compose 教程

简介 本博客将讲解如何在 Docker 和 Docker-Compose 中运行 Elasticsearch&#xff0c;并挂载 IK 分词器。 步骤 一、快速运行Elasticsearch:8.1.3 1.首先&#xff0c;我们需要创建一个新的 Docker 网络&#xff1a;"elastic"。这个网络会提供给我们接下来所要创…...

7.6 通俗易懂解读残差网络ResNet 手撕ResNet

一.举例通俗解释ResNet思想 假设你正在学习如何骑自行车&#xff0c;并且想要骑到一个遥远的目的地。你可以选择直接骑到目的地&#xff0c;也可以选择在途中设置几个“中转站”&#xff0c;每个中转站都会告诉你如何朝着目的地前进。 在传统的神经网络中&#xff0c;就好比只…...

robotframework+selenium 进行webui页面自动化测试

robotframework其实就是一个自动化的框架&#xff0c;想要进行什么样的自动化测试&#xff0c;就需要在这框架上添加相应的库文件&#xff0c;而用于webui页面自动化测试的就是selenium库. 关于robotframework框架的搭建我这里就不说了&#xff0c;今天就给大家根据一个登录的实…...

手机突然无法获取ip地址

在日常生活中&#xff0c;我们对手机的依赖越来越大&#xff0c;尤其是在联网方面。然而&#xff0c;有时候我们可能会遇到手机无法获取IP地址的问题&#xff0c;这给我们的正常使用带来了很多不便。当我们的手机无法获得IP地址时&#xff0c;我们将无法连接到互联网或局域网&a…...

C++——关于命名空间

写c项目时&#xff0c;大家常用到的一句话就是&#xff1a; using namespace std; 怎么具体解析这句话呢&#xff1f; 命名冲突&#xff1a; 在c语言中&#xff0c;我们有变量的命名规范&#xff0c;如果一个变量名或者函数名和某个库里面自带的库函数或者某个关键字重名&…...

怎么进行流程图制作?用这个工具制作很方便

怎么进行流程图制作&#xff1f;流程图是一种非常有用的工具&#xff0c;可以帮助我们更好地理解和展示各种复杂的业务流程和工作流程。它可以将复杂的过程简化为易于理解的图形和文本&#xff0c;使得人们更容易理解和跟踪整个流程。因此&#xff0c;制作流程图是在日常工作中…...

【ChatGPT 指令大全】怎么使用ChatGPT来辅助学习英语

在当今全球化的社会中&#xff0c;英语已成为一门世界性的语言&#xff0c;掌握良好的英语技能对个人和职业发展至关重要。而借助人工智能的力量&#xff0c;ChatGPT为学习者提供了一个有价值的工具&#xff0c;可以在学习过程中提供即时的帮助和反馈。在本文中&#xff0c;我们…...

Ubuntu20配置仅主机网络

Ubuntu20配置仅主机网络&#xff0c;使虚拟机与物理机网络联通且配置固定IP 进入终端&#xff1a;vim /etc/netplan/01-network-manager-all.yaml 修改为&#xff1a; network:ethernets:enp0s8:addresses: [192.168.138.108/24]dhcp4: false optional: truegateway4: 192.…...

调整奇数偶数顺序

调整数组使奇数全部都位于偶数前面。 题目&#xff1a; 输入一个整数数组&#xff0c;实现一个函数&#xff0c;来调整该数组中数字的顺序使得数组中所有的奇数位于数组的前半部分&#xff0c;所有偶数位于数组的后半部分。 思路&#xff1a; 1. 给定两个下标left和right&#…...

日志的规范

确定日志级别&#xff1a; 确保你的系统有一个明确的日志级别策略。通常&#xff0c;日志级别包括DEBUG&#xff0c;INFO&#xff0c;WARN&#xff0c;ERROR和FATAL。DEBUG级别的日志记录所有详细信息&#xff0c;适用于开发和调试环境。INFO级别的日志记录常规操作信息&#x…...

Spring AOP(AOP概念,组成成分,实现,原理)

目录 1. 什么是Spring AOP&#xff1f; 2. 为什么要用AOP&#xff1f; 3. AOP该怎么学习&#xff1f; 3.1 AOP的组成 &#xff08;1&#xff09;切面&#xff08;Aspect&#xff09; &#xff08;2&#xff09;连接点&#xff08;join point&#xff09; &#xff08;3&a…...

Android WebView简单应用:构建内嵌网页浏览功能

在现代移动应用开发中&#xff0c;内嵌网页浏览功能是许多应用程序的常见需求。Android平台提供了WebView组件&#xff0c;它允许开发者将网页内容嵌入到应用中&#xff0c;并提供了丰富的功能和定制选项。本文将介绍如何在Android应用中使用WebView组件&#xff0c;帮助您快速…...

并发——乐观锁常见的两种实现方式,乐观锁的缺点

文章目录 乐观锁常见的两种实现方式1. 版本号机制2. CAS算法 乐观锁的缺点1 ABA 问题2 循环时间长开销大3 只能保证一个共享变量的原子操作 乐观锁常见的两种实现方式 乐观锁一般会使用版本号机制或CAS算法实现。 1. 版本号机制 一般是在数据表中加上一个数据版本号version字段…...

Spring 事务管理

目录 1. 事务管理 1.1. Spring框架的事务支持模型的优势 1.1.1. 全局事务 1.1.2. 本地事务 1.1.3. Spring框架的一致化编程模型 1.2. 了解Spring框架的事务抽象&#xff08;Transaction Abstraction&#xff09; 1.2.1. Hibernate 事务设置 1.3. 用事务同步资源 1.3.1…...

unity修改单个3D物体的重力的大小该怎么处理呢?

在Unity中修改单个3D物体的重力大小可以通过以下步骤实现&#xff1a; 创建一个新的C#脚本来控制重力&#xff1a; 首先&#xff0c;创建一个新的C#脚本&#xff08;例如&#xff1a;GravityModifier.cs&#xff09;并将其附加到需要修改重力的3D物体上。在脚本中&#xff0c…...

[Qt]FrameLessWindow实现调整大小、移动弹窗并具有Aero效果

说明 我们知道QWidget等设置了this->setWindowFlags(Qt::FramelessWindowHint);后无法移动和调整大小&#xff0c;但实际项目中是需要窗口能够调整大小的。所以以实现FrameLess弹窗调整大小及移动弹窗需求&#xff0c;并且在Windows 10上有Aero效果。 先看一下效果&#xf…...

【API生命周期看护】API日落

一、基本概念 在API的整个生命周期中&#xff0c;不可能是永远不变的。功能可能有变动、服务也可能有升级迭代&#xff0c;这个时候对外的能力入口&#xff1a;API自然也需要改变。 一般来说&#xff0c;API的变动是不可以引入兼容性问题的&#xff0c;也即不管做什么变动&am…...

PHP 使用ThinkPHP实现电子邮件发送示例

文章目录 首先我们需要设置我们的邮箱客户端授权&#xff0c;获取到授权码找到我们的邮箱设置去账号中找到这一堆服务&#xff0c;找到后开启smtp服务开启服务后管理服务 接下来需要去下载相应的第三方类库(我这里使用的是PHPMailer)在thinkPHP中封装一下邮件服务类实际调用效果…...

Leetcode-每日一题【剑指 Offer 18. 删除链表的节点】

题目 给定单向链表的头指针和一个要删除的节点的值&#xff0c;定义一个函数删除该节点。 返回删除后的链表的头节点。 注意&#xff1a;此题对比原题有改动 示例 1: 输入: head [4,5,1,9], val 5输出: [4,1,9]解释: 给定你链表中值为 5 的第二个节点&#xff0c;那么在调…...

[LINUX使用] top 命令的使用

COLUMNS150 LINES100 top 序号 是否为启动命令 命令模板 详解 1 no vh 帮助 2 yes -d 0.01 0.01秒的间隔刷新top输出 3 no c COMMAND列切换 4 yes -e [k | m | g | t | p] 以何种计量单位显示内存列 k-kb&#xff0c;m-mb&#xff0c;g-gb&#xff0c;t-t…...

通过redis进行缓存分页,通过SCAN扫描进行缓存更新

问题&#xff1a;当我们要添加缓存时&#xff0c;如果我们用了PageHelper时&#xff0c;PageHelper只会对查询语句有效&#xff08;使用到sql的查询&#xff09;&#xff0c;那么如果我们把查询到的数据都添加到缓存时&#xff0c;就会无法进行分页&#xff1b; 此时我们选择将…...

C#小轮子 Debug,Release,发布模式如何运行不同的代码

文章目录 前言C#运行模式运行模式介绍三种模式区分代码 前言 编译模式和发布模式的代码不一样是非常正常的。比较常见的是数据库不一样。编译测试数据库和发布真实的数据库地址不一样。 C#运行模式 运行模式介绍 运行模式有三种&#xff1a; Debug 不进行优化&#xff0c;…...