12小时复刻《羊了个羊》,代码已开源!
The following article is from CSDN Author 开发游戏的老王
先说说玩法
实现概要
牌堆数据结构的实现
如何检测和更新可拾取的牌
先做个小定义,一个牌堆中可被拾取的牌以下将简称其为:“窗口牌”。
牌堆的结构及其数据结构
蓝圈圈出的牌堆模式A:上面1张牌只挡住下面1张牌;同时下面的牌仅被上面1张牌挡住。只要上面的1张牌被取走,下面的牌就成为窗口牌;
红圈圈出的牌堆模式C:上面1张牌可以挡住下面4张牌;同时下面的牌可能被上面4张牌挡住,一张牌只有它上面的4张牌都被取走,它自己才成为窗口牌。
上面1张牌可以挡住下面2张牌;同时下面的牌可能被上面2张牌挡住,一张牌只有它上面的2张牌都被取走,它自己才成为窗口牌。
逻辑上牌堆模式A的窗口牌也可能是2维的,如果用队列实现就限制了它的灵活性;
牌堆模式B和C都不好用队列实现,所以想追求数据结构的统一,还要另求他法。
#MContainerBase
extends Node2D
class_name MContainerBase
func _ready():
add_to_group(name)
add_to_group("game")
var Mask = FileReader.read(mask_file,null)
box.resize(size_x)
for i in range(size_x):
box[i] = []
box[i].resize(size_y)
for j in range(size_y):
box[i][j] = []
box[i][j].resize(size_z)
for k in range(size_z):
if Mask == null or Mask[i][j] == 1:
box[i][j][k] = add_tile(i,j,k,get_parent().distribute_face())
else:
box[i][j][k] = null
for x in range(size_x):
for y in range(size_y):
for z in range(size_z):
check_is_on_top(x,y,z)
# S形遮罩
[
[0,0,0,0,0],
[0,0,0,0,0],
[1,1,1,0,1],
[1,0,1,0,1],
[1,0,1,1,1],
]
如何检测和更新可拾取的牌
#1 Cover 1
extends MContainerBase
func check_is_on_top(x,y,z):
if has_tile(x,y,z):
if not has_tile(x,y,z + 1) :
(box[x][y][z] as MTile).set_is_on_top(true)
#1 Cover 2
extends MContainerBase
func check_is_on_top(x,y,z):
if has_tile(x,y,z):
if z%2 == 0:
if not has_tile(x,y,z + 1) and not has_tile(x - 1 ,y,z + 1):
(box[x][y][z] as MTile).set_is_on_top(true)
else:
if not has_tile(x,y,z + 1) and not has_tile(x + 1 ,y,z + 1):
(box[x][y][z] as MTile).set_is_on_top(true)
#1 Cover 4
extends MContainerBase
func check_is_on_top(x,y,z):
if has_tile(x,y,z):
if z%2 == 0:
if not has_tile(x,y,z + 1) and not has_tile(x - 1 ,y,z + 1) and not has_tile(x,y - 1 ,z + 1) and not has_tile(x - 1,y - 1,z + 1):
(box[x][y][z] as MTile).set_is_on_top(true)
else:
if not has_tile(x,y,z + 1) and not has_tile(x + 1 ,y,z + 1) and not has_tile(x,y + 1 ,z + 1) and not has_tile(x + 1,y + 1,z + 1):
(box[x][y][z] as MTile).set_is_on_top(true)
如何生成新关卡
var tiles = []
export var initial_tiles = {
0:10,
1:10,
2:10,
3:10,
4:10,
5:10,
6:10,
7:10,
8:10,
9:10,
10:10,
11:10,
12:10,
13:10,
14:10,
15:10
}
func _init():
for key in initial_tiles:
var num = initial_tiles[key]*3
for i in range(0,num):
tiles.append(key)
tiles.shuffle()
关于游戏中的坑
只有桌面牌堆中牌的数量和待发牌池牌数一致,所有的牌才能“落地”,而游戏中桌面牌堆到底有多少(层)本身就是个迷。并且如果没猜错的话,在每一局设计者先要确保牌堆形状好看,然后再使堆牌数和待发池的牌数一致。二者哪怕差1个,也会造成死局。
上文说了,桌面牌数和待发牌池的牌数一致只是过关的必要而非充分条件。即使该条件满足,如果相对于牌桌上的牌数以及图案数量,窗口牌数太少,也会造成死局。比如下面这个极端的例子:假设游戏共有 15种花色,而牌桌上只有这个模式A牌堆,它有90张牌。那么玩家只要在连续7次拾牌时没有遇到3个相同图案的牌,就“必死无疑”了。
洗牌道具的实现
func shuffle_tiles():
tiles.shuffle()
tiles_index = -1
func redistribute_face() -> int:
tiles_index += 1
return tiles[tiles_index]
遮罩文件的读取
class_name FileReader
static func read(path,default_data):
var data = default_data
var file = File.new()
file.open(path,File.READ)
var content :String = file.get_as_text()
if not content.empty():
data = str2var(content)
file.close()
return data
对象间的通信
总结
推荐阅读
你好,我是程序猿DD,10年开发老司机、阿里云MVP、腾讯云TVP、出过书创过业、国企4年互联网6年。从普通开发到架构师、再到合伙人。一路过来,给我最深的感受就是一定要不断学习并关注前沿。只要你能坚持下来,多思考、少抱怨、勤动手,就很容易实现弯道超车!所以,不要问我现在干什么是否来得及。如果你看好一个事情,一定是坚持了才能看到希望,而不是看到希望才去坚持。相信我,只要坚持下来,你一定比现在更好!如果你还没什么方向,可以先关注我,这里会经常分享一些前沿资讯,帮你积累弯道超车的资本。