基于Unity3D的“奋了个斗”算法设计与实现

2023-06-22 13:47张佳豪许向荣安沛沛
现代信息科技 2023年5期

张佳豪 许向荣 安沛沛

摘  要:随着时代的进步,很多在以往风靡一时的经典游戏逐渐退出人们的视线并被人们慢慢淡忘,对这些消除类游戏的游戏特性进行整合创新,从而创作出新的游戏,并带动整个消除类游戏的新热潮。在原本的消除类游戏的主要特性中,对游戏消除的类型与个数进行创新,加入原本的三个相同元素进行消除的思路,采用人生的阶段与游戏关卡一一对应,加入更多解密的难度,基于Unity3D引擎创作了一款新型的解谜烧脑三消类游戏:奋了个斗。

关键词:三消类游戏;Unity3D;游戏开发

中图分类号:TP311        文献标识码:A        文章编号:2096-4706(2023)05-0090-05

Design and Implement of“Finish a Struggle”Algorithm Based on Unity3D

ZHANG Jiahao, XU Xiangrong, AN Peipei

(School of Information Engineering, Zhengzhou University of Science and Technology,Zhengzhou  450064, China)

Abstract: With the progress of the times, many classic games which were popular in the past gradually drop out of people's sight and are slowly forgotten by them. This paper integrates and innovates the game characteristics of these elimination games to create new games, and promotes the whole new craze of elimination games. In the main characteristics of original elimination games, it innovates the type and number of elimination in games, adds the thought of eliminating original three same elements, uses the stage of life with the game level for one to one correspondence, and adds more difficulty of decryption, based on Unity3D engine, creates a new three-elimination game with solving puzzles to exercise brain: Finish a Struggle.

Keywords: three-elimination game; Unity3D; game development

0  引  言

在科技的快速发展下,手机成为很普遍的产物。从青少年到老年人几乎人手智能手机。因疫情原因,大大推动了游戏行业的发展[1]。随着生活节奏的加快,游戏成为人们首选的休闲娱乐方式[2]。人们的生活水平随着时代的发展不断提高,对于物质的追求也逐渐变得不那么热切,对于精神上的需求却更加强烈,因此对于游戏这方面的关注便不断增加,对于游戏的需求也越发强烈。

Unity是由丹麦Unity Technologies公司开发的综合性多平台的游戏开发工具。使用Unity可实现多平台一次性开发且兼容性较好。Unity支持Android,Windows,Linux,Web Player,PC,IOS,BlackBerry,Windows Phone 8,Xbox等多個平台发布[3,4]。作为现如今最为主流的游戏设计软件,其强大之处不必多说,对整个游戏的创作与开发也为我们提供了莫大的支持。整个游戏体系的创建是基于传统经典游戏的特色和原有游戏体系的结构想法上加以创新,创造出一个全新的游戏结构,将原本的消除类游戏加入新的创意,使其具有更加丰富的游戏趣味,适应现时代的时代游戏浪潮。

1  项目简介

此游戏主要基于Unity3D作为主要游戏开发平台,C#等程序语言作为程序设计基础设计出的一款解密烧脑类三消游戏,本文主要阐明其中的重要算法以及关键思路。以方块作为整个游戏的游戏特征元素并以三消作为整个游戏的类型,玩家需要思考并解决的问题是通过选取、换位或配对,将原本看起来混乱、随机排列的游戏元素进行调整,使三个相同的游戏元素进行匹配,最终达到消除的效果[5,6]。

2  主要设计思路与关键算法

本项目主要的基本结构在于项目的基本设计的基础上对项目的需要进行分类,将其分为:游戏分布设计、随机分组设计以及其他功能设计,如图1所示。这些结构都是整个项目结构的一部分,在下方将对这些结构分组进行说明讲解。

2.1  游戏分布设计

现有的为大众所喜爱的游戏中,很多游戏创作者对于游戏的整个游戏设计界面是下了极大的心思的,研究玩家最为接受的审美,以此为基础对整个游戏的功能布局以及游戏界面进行设计,使得游戏的界面设计更加贴合玩家习惯与审美,具有更良好的游戏体验。在设计游戏的整体布局时,主要考虑到玩家对于游戏的认知和对游戏布局的感知是比较敏感的,需要在设计整体的分布时更加注重大众的美感以及整体的协调性,因此在游戏创作中采用的是对称性设计,将整个游戏的分布设计成多个矩阵叠加的情况,并按照对称的性质将矩阵按照最为和谐的设计呈现在眼前,而这也是整个游戏的分布中最为主要的分布设计。

因为在最初设定的游戏类型是三消,也就是三元素消除的游戏类型,而要实现这一设计则是需要设计不同组的元素和相同的元素,而在其中在重要的是如何将相同元素进行选中、集合的操作。因为在最初的设想中是将各个元素以单独的方式呈现,让它能够作为一个能够被选中的、单独的物体存在,所以实现这一想法的最优解是将一个小的矩形作为元素的承载,使其成为元素的一个对应物体来代替元素作为被选中、集合的目标,也就是卡牌作为元素的对应物体,也是作为矩阵的最基础的组成单位。在设计整体布局时,矩阵作为主要的设计模型成为整个游戏的主要结构,最初的设计方案是将多个矩阵根据一定的规律进行叠加处理,在用矩阵叠加的层数与叠加的规律对游戏难度进行控制的同时,也带来更多设计其他模式的可能性,预想的矩形结构设计图如图2所示。

2.2  随机分组算法

三消类游戏最主要的特点是三元素消除,在卡牌的生成的设计是需要对卡牌数量进行一定的限制,整个面板里的卡牌数量需要是3的倍数,这样才能避免出现游戏到最后出现只余下1个或2个卡牌的情况,在这一前提条件下,卡牌数量需要针对性地去限制其数量。卡牌数量分为三种情况:卡牌数量除去3后没有余数、卡牌数量除去3后余1和卡牌数量除去3后余2,针对这三种情况,处理方法大致相同,即加一定的卡牌到其中使得卡牌的数量始终是3的倍数。

在确定卡牌的数量是3的倍数,避免出现多余卡牌不能消除的情况后,便是打乱卡牌的顺序并在卡牌上贴上对应元素的贴图。 在这一阶段的主要目的是将所有的卡牌整个卡牌的贴图顺序打乱并对这些卡牌进行贴图,其步骤是在统计所有卡牌的个数后,单独建立一个列表TempGroup用于存储卡牌的缓存数据,将所有panel中卡牌遍历进入列表中,对卡牌进行打乱操作,在将列表的卡牌顺序打乱后每三个卡牌贴上相同元素的贴图,之后再把列表内的卡牌按照原先顺序重新放回panel内,在此之后整个游戏内的卡牌便是一个较为无序的状态,也达成了原本的目的。

2.3  相关技能设定

在技能设定方面主要是依据最为常用的牌类游戏中的想法:重新洗牌、撤回卡牌以及推出卡牌。这三项功能主要针对卡牌本身位置、所处的panel还有卡牌上的贴图进行代码设计,而且这几项功能都是和卡牌所处的多项panel,卡牌在选中前后的位置以及随机分组的算法有关。洗牌是对随机分组算法的重新应用,但还要考虑各个pane中的卡牌数量以及剩余的还未消除的元素和在卡牌中的贴图个数,在此基础对卡牌的贴图进行更改、交换来达到洗牌的效果,实现效果如图3所示。

撤回卡牌则是对卡牌所处的位置以及所在panel的序列进行标记,然后在撤回进行时将卡牌放回原位,实现效果如图4所示;推出卡牌则是建立一个新的panel来存储pencilbox这一panel中最前面三个卡牌来实现中这一功能,如图5所示。

2.4  其他功能设计

最主要的结构设计结束之后便实现其他的必要实现功能,如遮挡算法、移除展示、生成位置等功能,这些功能是游戏中必要且必需的功能,主要用于卡牌的遮挡、卡牌的移除等方面,也是对游戏功能的完善。遮挡算法的设计是为避免上层卡牌在为消除时,下层卡牌就处于可选取的状态,从而导致原本需要消除上层卡牌才能解锁下层卡牌的情况变成了在上层卡牌未消除的状态下,下层卡牌就已经处于解锁状态,这将破坏了整个游戏设立的游戏机制。想要是实现这一算法主要依据于字典Dictionary,利用字典来对卡牌进行查找与覆盖,在此基础上统计每个卡牌的类型、个数并判定卡牌之间的遮挡关系。移除展示算法的世界是为了实现选中卡牌的移动并在移动后取消原有的遮挡关系,解锁在其下层的卡牌,并使得遮挡算法重新判定遮挡关系这一系列的功能,从另一方面实现卡牌的移动。生成位置算法则是为了panel面板的生成而设计出来的,因为游戏的设计因素需要panel面板不是一整个平面的平铺,而是多个面板的重叠、覆盖,这也就需要对面板的生成要有一定的把控,需要其出现在它该生成的位置,所以设定其生成的位置来把控面板的层级大小和覆盖的面积。

3  設计思路与关键算法的实现

3.1  矩阵设计算法

矩阵在游戏中是作为整个游戏的基础设计结构而存在的,也在一定程度上决定了整个游戏的难度,使得游戏的结构层次上有了更加广阔的丰富性,带给玩家更优秀的游戏体验。因而在对矩阵的设计中,更多的是需要对矩阵的多种变化,而要实现这些效果则是需要在程序中将其以代码的方式来裁剪矩阵,使得矩阵的基本形状能够被限制,矩阵中卡牌的位置也需要重新设定,因程序代码过于繁琐,在此只以上下对称算法为例,核心代码如下:

int maxX = g.GetComponent().cardArrays.GetLength(0);

int maxY = g.GetComponent().cardArrays.GetLength(1);

for (int i = 0; i < (maxY+1)/2; i++)

{

for (int j = 0; j < maxX ; j++)

{

int r = UnityEngine.Random.Range(0, 2);

if (r == 0)

{

Destroy(g.GetComponent().cardArrays[i, j]);

if (i != (maxY - 1 - i))

{

Destroy(g.GetComponent().cardArrays[maxY - 1 - i, j]);

}

}

else

{

g.GetComponent().currentCards.Add(g.GetComponent().cardArrays[i, j]);

if (i != (maxY - 1 - i))

{

g.GetComponent().currentCards.Add(g.GetComponent().cardArrays[maxY - 1 - i, j]);

}

}

}

}

因為Unity3D本身的设计中渲染是需要一定时间的,而对于矩形的裁剪就需要在Unity3D本身的渲染完毕后,在使用相对应的代码来实现相应的功能,也就是说在整个程序的渲染未完成的时候是不能对进行任何修改的,否则就会造成矩阵的错误,导致卡牌处于多重的叠加在同一位置上的BUG,具体的矩阵设计思路如图6所示。

3.2  随机分组算法

在对卡牌的三种情况的相关算法处理中,三种情况的处理都是十分类似的,使用的思路也是相同的,都是通过遍历卡牌的数量并除以3来对三种情况进行区分,而之后便是对各种情况的代码设计。在此只罗列一种情况,如下:

if (n == 0)

{

return;

}

elseif (n == 2)

{

foreach (GameObject g in smallCardsPanesls)

{

if (g.GetComponent().panelOrder == 1)

{

g.GetComponent().AddCard(1, false);

break;

}

}

currentCardNumber += 1;

在卡牌数量的修正后,我们便可对卡牌进行打乱处理,然后进行贴图来事的所有的卡牌的贴图是混乱的,而不是井然有序。其主要的设计思路是创建一个list表将所有卡牌遍历进入列表中,再使用打乱处理将卡牌的顺序打乱,核心代码如下:

List tempGroups = new List

();

foreach (var v in bigCardsPanels)

{

tempGroups.AddRange(v.GetComponent

().currentCards);

}

foreach (var v in smallCardsPanesls)

{

tempGroups.AddRange(v.GetComponent

().cards);

}

while (tempGroups.Count != 0)

{

int r = UnityEngine.Random.Range(0, tempGroups.Count);

currentCards.Add(tempGroups[r]);

tempGroups.RemoveAt(r);

}

在将卡牌在变后的列表中被打乱后,将每3个卡牌贴上3个相同的贴图,在贴图全部被贴图后将卡牌送回其原本在panel中的位置,核心代码如下:

foreach (var g in currentCards)

{

if (count % 3 == 0)

{

randomType = UnityEngine.Random.Range(0, cardTypes.Count);

count = 0;

}

g.GetComponent().cardTypeImage.sprite = cardTypes[randomType];

g.GetComponent().cardType = (CardTypeEnum)Enum.Parse(typeof(CardTypeEnum), cardTypes[randomType].name);

count++;

}

3.3  技能算法

在洗牌、推出卡牌、撤回三项最基础的技能中, 洗牌是其中最复杂的代码,其思路也是相对简单的,但实现其的难度相比于推出卡牌的操作实现还是比较困难的。推出卡牌的操作实现是建立新的panel用于容纳推出的卡牌,而只要将pencilbox中的卡牌选取前三个将其传入新建的panel即可,撤回则是对卡牌之前所在位置进行标记,寻求其父亲的存在并以此为撤回的标准。因此在此只对洗牌进行代码实现进行说明,洗牌的核心代码如下:

for (int i = 0; i < currentCards.Count / 2; i++)

{

Sprite tempSprite = currentCards[i].GetComponent

().cardTypeImage.sprite;

CardTypeEnum tempCardType = currentCards[i].GetComponent().cardType;

currentCards[i].GetComponent().cardTypeImage.sprite = currentCards[currentCards.Count - 1 - i].GetComponent

().cardTypeImage.sprite;

currentCards[i].GetComponent().cardType = currentCards

[currentCards.Count - 1 - i].GetComponent().cardType;

currentCards[currentCards.Count - 1 - i].GetComponent

().cardTypeImage.sprite = tempSprite;

currentCards[currentCards.Count - 1 - i].GetComponent

().cardType = tempCardType;

}

3.4  其他功能性算法

該算法是整个项目中较为耗费思考的算法,其中要考虑到卡牌的整体堆砌、卡牌的数量、相邻卡牌的距离、卡牌之间的联系等方面,需要从多个方面来对遮盖算法进行设计,方方面面都要考虑到,不然到调试运行的时候,bug和错误将会层出不穷,下面的算法是整个遮盖算法最为主要的地方,也就是这一部分的核心算法,如下:

if (target.position.x + target.rect.xMax <= b.position.x + b.rect.xMin ||

target.position.x + target.rect.xMin >= b.position.x + b.rect.xMax ||

target.position.y + target.rect.yMax <= b.position.y + b.rect.yMin ||

target.position.y + target.rect.yMin >= b.position.y + b.rect.yMax

{

continue;

}

移除算法与移动算法不同,移动算法是位置的改变,而一处算法则是在寻找到这一卡牌后,将这一卡牌从其中所有卡牌中消除,其对应的各项关系并在移除后进行重新判定,并从字典中移除这一卡牌,更新其周围卡牌的遮挡关系,核心代码如下:

if (CoverToOthers.ContainsKey(g))

{

returnCardCovers = CoverToOthers[g];

foreach (var v in CoverToOthers[g])

{

v.GetComponent().coveredNumber--;

if (v.GetComponent().coveredNumber == 0)

{

v.GetComponent