戴培山,马凌尧,范敏
(1.中南大学计算学院,湖南长沙 410083;2.湖南女子学院社会发展与管理学院,湖南长沙 410004)
卡牌游戏是一种棋类游戏,自产生以来,就因其具有多样化的玩法、自由的空间、休闲娱乐、智力挑战和互动性强等特点而受到各个年龄段和地区人群的喜爱。扑克牌的传入在一定程度上统一了中国上一代人的卡牌游戏类型并影响至今。同时,扑克牌的玩法也因中国的地大物博而变得多种多样,越来越多极具地方性特色的扑克牌游戏都在其发源地广受欢迎[1]。
随着移动互联网技术的高速发展和智能移动设备的普及,社会进步得更快。人们将更多时间用于工作,导致闲暇时间变得零碎。这种现象促使移动在线游戏的出现,并逐渐取代传统的线下游戏。卡牌类游戏的线上运营也因此引来了热潮。
由于受众规模和开发效益等因素的影响,目前市场上绝大多数的卡牌类游戏都以全国性的卡牌游戏为主。而具有地方性特色的卡牌游戏很少有团队进行开发。许多在外求学或是工作的异乡人很难在休闲时间找到可以和自己一起娱乐的玩家。在线地方特色卡牌游戏可以解除空间上的限制,不论大家在哪里,都可以在线上体验到全国各地的卡牌玩法。空间、时间上的自由、玩法的多样性、娱乐与益智共存等特点将会吸引越来越多的受众人群。此类游戏的实现将为大家提供一个方便、丰富的交友娱乐平台。
20世纪以来,移动互联网的出现改变着新世纪人们的生活方式,在广大的游戏市场中异军突起的便是网络游戏[2]。自此之后,在短短的10 年里,国内涌现出了一大批游戏公司,从刚开始的盛大、金山到现在的腾讯、网易。国内主流的游戏基本上都诞生在这些大游戏公司。广大的游戏玩家为这些公司带来了不错的经济效益,也促使一大批计算机从业者开始从事游戏开发工作。作为新兴产业,游戏产业从萌芽走向成熟只用了短短15年的时间[3],并且已经成为网络经济不可或缺的组成部分。其中棋牌类游戏无疑是属于元老级的存在,并在历史的潮流中不停地发展演变[4]。除全国性游戏平台外,各种各样的地方性平台涌现出来,尤其在北方地区较为明显。随着地方性棋牌游戏火热程度的加深,游戏平台的目标受众也更加细化,出现了专门为温州市民研发的“温州牛牛”等游戏平台,游戏内的图片和方言聊天充满着浓郁的地方特色。在博大精深的中华文化孕育之下,全国各地诞生了不计其数的卡牌玩法,仍有许多地方游戏没有得到线上化,这也促进着棋牌游戏的进一步发展。
本文卡牌游戏采用Unity2019 游戏引擎开发,编程工具为Visual Studio 2019,编程语言为C#语言。
主要包括游戏项目的整体框架、游戏运行流程、游戏规则设计、游戏功能实现。
此卡牌游戏规则[5]如下:
①游戏共有52 张卡牌,数值为3、4、5、6、7、8、9、10、J、Q、K、A、2,每个数值共有4 张,分别为4 个不同花色:红心、黑桃、方片、梅花,没有大小王。
②游戏开始时,3名玩家各随机发放5张卡牌,系统随机设置首位出牌玩家,并多发给其1张牌。
③玩家出牌类型可包括:
单牌:单独1张卡牌,如:3、5、7等;
对子:两张数值相同的卡牌,如44、77、QQ等;
顺子:至少3张数值相连的卡牌,如:456、JQK,但KA2、A23、234不能出牌;
炸弹:至少3 张数值相同的卡牌,如:555、6666、JJJ等。
④所有玩家依次选择是否出牌,合法的出牌如下:
出牌类型应和场上出牌配型相同,且数值只能大于1,如:3只能被4压,不可被5压,对55只能被66压,456只能被567压;
单张2 可以压所有的单牌,对2 可以压所有的对子;
炸弹可以压所有的非炸弹牌,4 张牌的炸弹不论数值都可以压3张牌的炸弹,牌数相同的炸弹,数值高的炸弹可以压数值低的炸弹,场上每打出一套炸弹,场上的倍数就×2。
⑤当玩家出的牌使其余两位玩家都无法出牌管上时,该玩家摸一张牌,继续出牌。
⑥当场上有玩家将手牌出完时,该玩家获得胜利,其余玩家失败,胜利玩家获得100 经验和“1 000×倍数”的豆子,失败玩家获得10经验并失去“1 000×倍数”的豆子。
本游戏实现了可多人在线娱乐的唬牌游戏,主要功能如图1所示。
图1 唬牌游戏功能架构图
图2 客户端运行流程图
图4 UDP传输模型
图5 Socket抽象层
图6 C/S连接图
3.2.1 服务器功能
服务器端主要为本设计提供了网络通信、数据缓存和逻辑处理三大部分的功能。1)网络通信。多人在线的关键在于不同的客户端可以同时进入一个房间内进行游戏,玩家的操作可以在房间内广播,战斗的信息会实时地传递到房间内的每个客户端,玩家之间可以在房间内进行文字聊天,服务器端的网络通信[6]使得这一部分的功能顺利实现。2)数据缓存。在整个设计过程中,有许多信息需要服务器进行保存,以方便所有的客户端既可以访问到这些数据,又可以节省存储空间。本文中,服务器端存储了所有玩家的共享信息,如:账号密码库、卡牌库、房间库、用户信息库等,客户端可以通过相对应的操作码向服务器发起请求来访问这些数据。3)逻辑处理。逻辑模块是整个系统顺利运行的核心所在。
在服务器端,实现了账号密码的部分合法性判断、房间匹配机制、战斗回合制管理、出牌合法逻辑块、卡牌发放与回收、战斗结算等功能。这些算法的实现在逻辑和内存管理上为客户端的处理提供了方便的接口,统一了客户端的操作码。
客户端功能。客户端在为玩家提供了一个简洁友好的页面基础上,实现了卡牌游戏的各项基本功能,主要包括以下6 部分:1)账号注册和登录。用户可以自己注册游戏账号,设置账号登录密码,由服务器端进行密码是否合法的判断并给予用户相关提示;用户可以使用自己已注册的正确的账号密码登录游戏,体验唬牌游戏世界。2)用户信息初始化。当注册的账号初次登录时,用户可以自定义自己的游戏名称,名称确认之后,由系统进行用户信息的初始化,所有用户初始等级为1级,初始豆子数量为1 000。3)房间匹配机制。系统会设置匹配房间列表,为所有准备进入大厅的玩家分配房间,以供给玩家游戏使用;当房间内玩家全部退出房间之后,系统会回收该房间,给其他准备进入大厅的玩家使用。4)游戏战斗功能。当所有玩家进入房间准备之后,战斗场景开始,系统随机为玩家发牌,3 名玩家可以按照游戏规则顺利进行游戏;在游戏结束之后,系统会根据玩家的胜负情况为玩家增加经验值并奖惩一定数量的豆子。5)在线聊天功能。房间内的玩家可以通过快捷喊话按钮,选择自己想要发送消息,系统会将文字信息展示在房间内,并播放相关语音。6)设置功能。在设置面板,用户可以开启/关闭背景音乐,也可以自由调节背景音乐的声音大小。
在C/S 模式中,客户端和服务器之间需要进行频繁的信息传递。而这两者之间的通信方式一般为HTTP 通信和Socket 通信两种[7]。这两个在功能上都可以满足客户端与服务器之间的数据传输。但是不同于HTTP 通信中的“请求响应方式”,Socket 通信则更为直接,它会在双方连接成功的基础上直接进行信息传递,不需要请求和响应。所以本文用Socket通信实现客户端和服务器之间的通信。
1)TCP和UDP协议
在实现Socket 通信时,经常使用到运输层的两个协议TCP协议和UDP协议[8]。TCP指的是用户数据报协议,可以提供面向连接和可靠传输两个服务,但是没有时间保证和最小传输速率保证,常见的面向连接服务就是“三次握手”的过程。虽然可以随时进行数据传输,但是由于传输过程会消耗一定的时间,所以并不能确保做到实时传输。
UDP指的是传输控制协议,它不需要像TCP协议那样一定要双方连接起来,它可以直接发送,但是并不能确保信息是否被对方接收成功,也不能确保对方收到的报文是否出错。常用于实时消息的传输。
本设计实现的Socket 通信是基于TCP 协议[9]设计的。
2)Socket抽象层
3)C/S模式连接流程图
UI(User Interface) 是指提供给用户一个简洁、美观的页面。它包含视觉方面的文字、动画、图片,以及听觉上的音效和BGM,同时还要有常见的人机交互组件,如点击按钮和滑动滚动条等。随着时代的进步,人们对UI界面的需求量和质量要求都有所提高,这使得开发者需要更高的技术水平来满足用户需求。UI框架为开发者提供了一套完整的基础工具。开发者可以利用这些框架,进行基本的UI 设计。UI 可以帮助游戏开发者进行基本的UI 设计,优质的UI 框架可以在保证系统效率的同时,降低代码耦合度[10]。本文使用UI消息框架主要作为一个消息传递中心,各个逻辑单元发出来的消息处理都会经过MsgCenter 进行转发。
在玩扑克牌时,需要对一副扑克牌进行顺序打乱,保证所有玩家获取到每张卡牌的概率是相等的,从而增加整个游戏的随机性[11],常见的洗牌方法有两种:全局洗牌法和局部洗牌法[12]。
这2种洗牌法都是基于一个数组实现的。
全局洗牌法会按照数组的索引,从1~54 依次生成一个余数value,value=rand%54,其中rand为系统产生的随机数,将当前索引的值和余数对应索引的卡牌进行交换,直到遍历到最后一个结束,以此达到洗牌的目的。然而,这样实现的话就会导致前面洗过的牌可能会再次被遍历到,所以可以将余数value 的计算方式进行改进,令value = index + rand % (54 - index),这样就可以保证索引牌和之前没有交换过的牌进行交换,更高效地洗牌。
在本设计中,仍然采用数组的形式,每个数组元素都是一个卡牌模型,通过打乱索引实现了洗牌算法,具体实现在如下。
包含52张基本卡牌,并实现了对这些卡牌的基本算法。
1)创建卡牌库。
通过两层for循环,第一层循环花色,第二层循环点数,依次将52 张卡牌模型添加到CardQueue 队列中,对每张卡牌命名为“花色+点数”,如:“红桃3”为“Heart Three”。
2)洗牌算法
通过for 循环创建的卡牌队列一定是一个花色和点数成规律的队列,为了方便发牌时随机出队列的复杂,这里直接实现洗牌算法,发牌时选择洗过之后的卡牌库,依次出队列发牌得到的就是随机分发的卡牌了。
算法实现:创建一个新的卡牌队列newList,通过foreach方法依次读取CardQueue队列中的卡牌。每次都在0到newList.count+1中随机生成一个整数索引index,并在newList列表的index位置处插入读取到的卡牌,直到CardQueue中的卡牌全部被读取过。由于index索引的位置是通过Random随机生成的,所以即使读取的卡牌有顺序,但卡牌的插入位置是无序的,结果仍然可以得到一个无序的卡牌列表。清空原有的CardQueue 队列,将newList 列表中的卡牌依次拿出并添加到CardQueue队列中,这样就完成了对52张卡牌的洗牌。
房间传输对象是一个DTO(Data Transfer Objects,数据传输对象)模型。它包括了用户id与用户模型的映射列表、已准备的玩家列表、玩家进入房间的顺序列表UIdList。除了基本的进入、离开、准备的初始化外,还实现了对房间内玩家座次的定位。对于每个客户端而言,由于其进入房间的顺序不同,在UidList 列表中的位置也不一样。但是要求每个客户端都将自身放在最下方的位置,这就要求每个客户端根据自己在UidList 列表中的序号来显示自己左右两边分别是几号玩家。如图7所示。
图7 玩家座次图
图8 游戏的开始界面
面。包括注册账号和开始游戏的功能。点击注册账号,会进行一些合法性判断。
若两次输入密码不匹配或者注册账号已经存在就会导致注册失败。登录成功后可以创建玩家角色,如图9所示。开始游戏后,会首先寻找可进入的游戏房间,如图10 所示。游戏过程中还可以设置背景音乐,还设有玩家进入游戏玩家等待,准备开始游戏等功能。所有玩家准备好后,游戏开始,随机选出一名玩家作为第一个出牌者。出牌之后,在其他玩家的页面可以看到桌面卡牌信息。图10为三人在线玩游戏的画面。出牌结束后,显示胜利和失败,其中胜利结果如图12所示,退出游戏后,服务器收到反馈。
图9 创建游戏角色
图10 寻找可进入的房间
图11 三人在线玩游戏的画面
图12 胜利玩家视角