7. 特殊方块与道具
2026/4/14大约 9 分钟
开心消消乐——特殊方块与道具
什么是特殊方块?
到目前为止,我们的游戏只支持普通的三消。但经典消消乐(如 Candy Crush)之所以让人上瘾,很大程度上是因为特殊方块——消除4个或5个方块时,会产生有超能力的方块,能炸掉一整行、一整列甚至全屏。
| 特殊方块 | 触发条件 | 效果 |
|---|---|---|
| 条纹方块 | 消除4个同色方块 | 消除一整行或一整列 |
| 炸弹方块 | 消除5个L/T形方块 | 炸掉周围3x3区域 |
| 彩虹球 | 消除5个直线方块 | 消除棋盘上所有同色方块 |
你可以把特殊方块想象成"超级武器"——普通的方块只是"普通子弹",而特殊方块是"火箭弹"、"手榴弹"和"核弹"。
特殊方块类型
C
/// <summary>
/// 特殊方块类型
/// </summary>
public enum SpecialType
{
/// <summary>普通方块(无特殊效果)</summary>
None,
/// <summary>条纹方块 - 水平(消除一整行)</summary>
StripedHorizontal,
/// <summary>条纹方块 - 垂直(消除一整列)</summary>
StripedVertical,
/// <summary>炸弹方块(消除周围3x3区域)</summary>
Bomb,
/// <summary>彩虹球(消除所有同色方块)</summary>
RainbowBall
}GDScript
## 特殊方块类型
enum SpecialType {
NONE, ## 普通方块(无特殊效果)
STRIPED_HORIZONTAL, ## 条纹方块 - 水平(消除一整行)
STRIPED_VERTICAL, ## 条纹方块 - 垂直(消除一整列)
BOMB, ## 炸弹方块(消除周围3x3区域)
RAINBOW_BALL ## 彩虹球(消除所有同色方块)
}扩展 Piece 类
给方块增加特殊类型属性,以及相应的视觉效果。
C
using Godot;
public partial class Piece : Area2D
{
// ... 之前的代码保持不变 ...
/// <summary>特殊类型</summary>
public SpecialType Special { get; set; } = SpecialType.None;
/// <summary>条纹指示器(水平条纹的Sprite)</summary>
private Sprite2D _stripeHIndicator;
/// <summary>条纹指示器(垂直条纹的Sprite)</summary>
private Sprite2D _stripeVIndicator;
/// <summary>炸弹指示器</summary>
private Sprite2D _bombIndicator;
/// <summary>彩虹球指示器</summary>
private Sprite2D _rainbowIndicator;
/// <summary>
/// 设置特殊类型
/// </summary>
public void SetSpecial(SpecialType special)
{
Special = special;
// 隐藏所有特殊指示器
_stripeHIndicator.Visible = false;
_stripeVIndicator.Visible = false;
_bombIndicator.Visible = false;
_rainbowIndicator.Visible = false;
// 显示对应的特殊指示器
switch (special)
{
case SpecialType.StripedHorizontal:
_stripeHIndicator.Visible = true;
break;
case SpecialType.StripedVertical:
_stripeVIndicator.Visible = true;
break;
case SpecialType.Bomb:
_bombIndicator.Visible = true;
break;
case SpecialType.RainbowBall:
_rainbowIndicator.Visible = true;
break;
}
}
/// <summary>
/// 判断是否为特殊方块
/// </summary>
public bool IsSpecial() => Special != SpecialType.None;
}GDScript
extends Area2D
# ... 之前的代码保持不变 ...
## 特殊类型
var special: int = SpecialType.NONE
## 条纹指示器(水平)
@onready var _stripe_h_indicator: Sprite2D = $StripeH
## 条纹指示器(垂直)
@onready var _stripe_v_indicator: Sprite2D = $StripeV
## 炸弹指示器
@onready var _bomb_indicator: Sprite2D = $Bomb
## 彩虹球指示器
@onready var _rainbow_indicator: Sprite2D = $Rainbow
## 设置特殊类型
func set_special(special_type: int) -> void:
special = special_type
# 隐藏所有特殊指示器
_stripe_h_indicator.visible = false
_stripe_v_indicator.visible = false
_bomb_indicator.visible = false
_rainbow_indicator.visible = false
# 显示对应的特殊指示器
match special_type:
SpecialType.STRIPED_HORIZONTAL:
_stripe_h_indicator.visible = true
SpecialType.STRIPED_VERTICAL:
_stripe_v_indicator.visible = true
SpecialType.BOMB:
_bomb_indicator.visible = true
SpecialType.RAINBOW_BALL:
_rainbow_indicator.visible = true
## 判断是否为特殊方块
func is_special() -> bool:
return special != SpecialType.NONE特殊方块生成逻辑
当匹配消除时,需要判断是否应该生成特殊方块。
生成规则
| 匹配形状 | 匹配长度 | 生成结果 |
|---|---|---|
| 直线 | 4个 | 条纹方块(方向与匹配方向垂直) |
| L/T形 | 5个 | 炸弹方块 |
| 直线 | 5个 | 彩虹球 |
检测匹配形状
C
/// <summary>
/// 分析匹配列表,确定是否生成特殊方块
/// 返回 (生成位置, 特殊类型) 或 null
/// </summary>
public (Vector2I position, SpecialType type)? AnalyzeMatches(
List<MatchInfo> matches)
{
// 收集所有匹配的位置
var allCells = new HashSet<Vector2I>();
foreach (var match in matches)
{
foreach (var cell in match.Cells)
{
allCells.Add(cell);
}
}
int totalMatched = allCells.Count;
// 检查5消直线 → 彩虹球
foreach (var match in matches)
{
if (match.Length >= 5)
{
// 生成在匹配的中间位置
Vector2I center = match.Cells[match.Cells.Count / 2];
return (center, SpecialType.RainbowBall);
}
}
// 检查L/T形(两个匹配交叉)→ 炸弹
if (matches.Count >= 2)
{
for (int i = 0; i < matches.Count; i++)
{
for (int j = i + 1; j < matches.Count; j++)
{
// 找两个匹配的交叉点
var intersection = FindIntersection(matches[i], matches[j]);
if (intersection.HasValue)
{
// L/T形匹配的总消除数 >= 5
int combined = matches[i].Length + matches[j].Length - 1;
if (combined >= 5)
{
return (intersection.Value, SpecialType.Bomb);
}
}
}
}
}
// 检查4消 → 条纹方块
foreach (var match in matches)
{
if (match.Length == 4)
{
// 生成在交换方块的位置(更直觉)
Vector2I pos = match.Cells[1]; // 第二个位置
SpecialType stripeType = match.MatchDirection == MatchInfo.Direction.Horizontal
? SpecialType.StripedVertical // 水平匹配 → 垂直条纹
: SpecialType.StripedHorizontal; // 垂直匹配 → 水平条纹
return (pos, stripeType);
}
}
return null; // 普通匹配,不生成特殊方块
}
/// <summary>
/// 找两个匹配的交叉点
/// </summary>
private Vector2I? FindIntersection(MatchInfo a, MatchInfo b)
{
foreach (var cellA in a.Cells)
{
foreach (var cellB in b.Cells)
{
if (cellA == cellB)
{
return cellA;
}
}
}
return null;
}GDScript
## 分析匹配列表,确定是否生成特殊方块
## 返回字典 {"position": Vector2i, "type": int} 或 null
func _analyze_matches(matches: Array) -> Variant:
# 收集所有匹配的位置
var all_cells: Dictionary = {}
for match_info in matches:
for cell in match_info.cells:
all_cells[cell] = true
# 检查5消直线 → 彩虹球
for match_info in matches:
if match_info.length >= 5:
# 生成在匹配的中间位置
var center: Vector2i = match_info.cells[match_info.cells.size() / 2]
return {"position": center, "type": SpecialType.RAINBOW_BALL}
# 检查L/T形(两个匹配交叉)→ 炸弹
if matches.size() >= 2:
for i in range(matches.size()):
for j in range(i + 1, matches.size()):
var intersection = _find_intersection(matches[i], matches[j])
if intersection != null:
var combined: int = matches[i].length + matches[j].length - 1
if combined >= 5:
return {"position": intersection, "type": SpecialType.BOMB}
# 检查4消 → 条纹方块
for match_info in matches:
if match_info.length == 4:
var pos: Vector2i = match_info.cells[1]
var stripe_type: int
if match_info.match_direction == MatchInfo.Direction.HORIZONTAL:
stripe_type = SpecialType.STRIPED_VERTICAL
else:
stripe_type = SpecialType.STRIPED_HORIZONTAL
return {"position": pos, "type": stripe_type}
return null # 普通匹配,不生成特殊方块
## 找两个匹配的交叉点
func _find_intersection(a: MatchInfo, b: MatchInfo) -> Variant:
for cell_a in a.cells:
for cell_b in b.cells:
if cell_a == cell_b:
return cell_a
return null特殊方块触发效果
当特殊方块被消除时(不管是被匹配消除还是被其他特殊方块触发),会触发其特殊效果。
条纹方块效果
C
/// <summary>
/// 触发条纹方块效果
/// </summary>
/// <param name="piece">条纹方块</param>
/// <returns>被消除的所有位置</returns>
public List<Vector2I> TriggerStriped(Piece piece)
{
var cells = new List<Vector2I>();
if (piece.Special == SpecialType.StripedHorizontal)
{
// 消除整行
GD.Print($"[GameBoard] 条纹方块(水平)!消除第{piece.Row}行");
for (int col = 0; col < GameManager.COLS; col++)
{
cells.Add(new Vector2I(piece.Row, col));
}
}
else if (piece.Special == SpecialType.StripedVertical)
{
// 消除整列
GD.Print($"[GameBoard] 条纹方块(垂直)!消除第{piece.Col}列");
for (int row = 0; row < GameManager.ROWS; row++)
{
cells.Add(new Vector2I(row, piece.Col));
}
}
return cells;
}GDScript
## 触发条纹方块效果
## 返回被消除的所有位置
func _trigger_striped(piece: Piece) -> Array[Vector2i]:
var cells: Array[Vector2i] = []
if piece.special == SpecialType.STRIPED_HORIZONTAL:
# 消除整行
print("[GameBoard] 条纹方块(水平)!消除第%d行" % piece.row)
for col in range(GameManager.COLS):
cells.append(Vector2i(piece.row, col))
elif piece.special == SpecialType.STRIPED_VERTICAL:
# 消除整列
print("[GameBoard] 条纹方块(垂直)!消除第%d列" % piece.col)
for row in range(GameManager.ROWS):
cells.append(Vector2i(row, piece.col))
return cells炸弹方块效果
C
/// <summary>
/// 触发炸弹方块效果
/// 消除以炸弹为中心的3x3区域
/// </summary>
public List<Vector2I> TriggerBomb(Piece piece)
{
var cells = new List<Vector2I>();
GD.Print($"[GameBoard] 炸弹爆炸!位置: ({piece.Row}, {piece.Col})");
for (int row = piece.Row - 1; row <= piece.Row + 1; row++)
{
for (int col = piece.Col - 1; col <= piece.Col + 1; col++)
{
if (IsInBounds(row, col))
{
cells.Add(new Vector2I(row, col));
}
}
}
return cells;
}GDScript
## 触发炸弹方块效果
## 消除以炸弹为中心的3x3区域
func _trigger_bomb(piece: Piece) -> Array[Vector2i]:
var cells: Array[Vector2i] = []
print("[GameBoard] 炸弹爆炸!位置: (%d, %d)" % [piece.row, piece.col])
for row in range(piece.row - 1, piece.row + 2):
for col in range(piece.col - 1, piece.col + 2):
if is_in_bounds(row, col):
cells.append(Vector2i(row, col))
return cells彩虹球效果
C
/// <summary>
/// 触发彩虹球效果
/// 消除棋盘上所有指定颜色的方块
/// </summary>
/// <param name="targetType">要消除的方块类型</param>
public List<Vector2I> TriggerRainbowBall(PieceType targetType)
{
var cells = new List<Vector2I>();
GD.Print($"[GameBoard] 彩虹球!消除所有 {targetType} 方块");
for (int row = 0; row < GameManager.ROWS; row++)
{
for (int col = 0; col < GameManager.COLS; col++)
{
if (_grid[row, col] == targetType)
{
cells.Add(new Vector2I(row, col));
}
}
}
return cells;
}GDScript
## 触发彩虹球效果
## 消除棋盘上所有指定颜色的方块
func _trigger_rainbow_ball(target_type: int) -> Array[Vector2i]:
var cells: Array[Vector2i] = []
print("[GameBoard] 彩虹球!消除所有类型 %d 方块" % target_type)
for row in range(GameManager.ROWS):
for col in range(GameManager.COLS):
if grid[row][col] == target_type:
cells.append(Vector2i(row, col))
return cells组合效果
当两个特殊方块被交换时,会产生比各自单独触发更强的组合效果。
| 组合 | 效果 |
|---|---|
| 条纹 + 条纹 | 十字消除(一行+一列) |
| 条纹 + 炸弹 | 3行+3列交叉消除 |
| 条纹 + 彩虹球 | 消除所有该颜色方块,每个变成条纹方块 |
| 炸弹 + 炸弹 | 5x5大范围爆炸 |
| 炸弹 + 彩虹球 | 消除所有该颜色方块,每个变成炸弹 |
| 彩虹球 + 彩虹球 | 消除全部方块! |
C
/// <summary>
/// 处理两个特殊方块的组合效果
/// </summary>
public List<Vector2I> HandleSpecialCombo(Piece piece1, Piece piece2)
{
var cells = new List<Vector2I>();
bool p1Special = piece1.IsSpecial();
bool p2Special = piece2.IsSpecial();
if (!p1Special || !p2Special)
{
// 至少有一个不是特殊方块,不触发组合
return cells;
}
// 彩虹球 + 彩虹球 → 消除全部
if (piece1.Special == SpecialType.RainbowBall
&& piece2.Special == SpecialType.RainbowBall)
{
GD.Print("[GameBoard] 彩虹球组合!消除全部方块!");
for (int row = 0; row < GameManager.ROWS; row++)
{
for (int col = 0; col < GameManager.COLS; col++)
{
cells.Add(new Vector2I(row, col));
}
}
return cells;
}
// 彩虹球 + 条纹 → 消除指定颜色并全部变成条纹
if (piece1.Special == SpecialType.RainbowBall)
{
cells.AddRange(TriggerRainbowBall(piece2.Type));
}
else if (piece2.Special == SpecialType.RainbowBall)
{
cells.AddRange(TriggerRainbowBall(piece1.Type));
}
// 条纹 + 条纹 → 十字消除
if ((piece1.Special == SpecialType.StripedHorizontal
|| piece1.Special == SpecialType.StripedVertical)
&& (piece2.Special == SpecialType.StripedHorizontal
|| piece2.Special == SpecialType.StripedVertical))
{
GD.Print("[GameBoard] 条纹组合!十字消除!");
// 消除两个方块所在行和列
cells.AddRange(TriggerStriped(piece1));
cells.AddRange(TriggerStriped(piece2));
}
// 炸弹 + 任何 → 大范围爆炸
if (piece1.Special == SpecialType.Bomb
|| piece2.Special == SpecialType.Bomb)
{
GD.Print("[GameBoard] 炸弹组合!大范围爆炸!");
// 5x5范围
Piece bombPiece = piece1.Special == SpecialType.Bomb ? piece1 : piece2;
for (int row = bombPiece.Row - 2; row <= bombPiece.Row + 2; row++)
{
for (int col = bombPiece.Col - 2; col <= bombPiece.Col + 2; col++)
{
if (IsInBounds(row, col))
{
cells.Add(new Vector2I(row, col));
}
}
}
}
// 去重
return cells.Distinct().ToList();
}GDScript
## 处理两个特殊方块的组合效果
func _handle_special_combo(piece1: Piece, piece2: Piece) -> Array[Vector2i]:
var cells: Array[Vector2i] = []
var p1_special: bool = piece1.is_special()
var p2_special: bool = piece2.is_special()
if not p1_special or not p2_special:
# 至少有一个不是特殊方块,不触发组合
return cells
# 彩虹球 + 彩虹球 → 消除全部
if piece1.special == SpecialType.RAINBOW_BALL \
and piece2.special == SpecialType.RAINBOW_BALL:
print("[GameBoard] 彩虹球组合!消除全部方块!")
for row in range(GameManager.ROWS):
for col in range(GameManager.COLS):
cells.append(Vector2i(row, col))
return cells
# 彩虹球 + 条纹 → 消除指定颜色
if piece1.special == SpecialType.RAINBOW_BALL:
cells.append_array(_trigger_rainbow_ball(piece2.type))
elif piece2.special == SpecialType.RAINBOW_BALL:
cells.append_array(_trigger_rainbow_ball(piece1.type))
# 条纹 + 条纹 → 十字消除
var is_stripe1 := piece1.special == SpecialType.STRIPED_HORIZONTAL \
or piece1.special == SpecialType.STRIPED_VERTICAL
var is_stripe2 := piece2.special == SpecialType.STRIPED_HORIZONTAL \
or piece2.special == SpecialType.STRIPED_VERTICAL
if is_stripe1 and is_stripe2:
print("[GameBoard] 条纹组合!十字消除!")
cells.append_array(_trigger_striped(piece1))
cells.append_array(_trigger_striped(piece2))
# 炸弹 + 任何 → 大范围爆炸
if piece1.special == SpecialType.BOMB or piece2.special == SpecialType.BOMB:
print("[GameBoard] 炸弹组合!大范围爆炸!")
var bomb_piece: Piece = piece1 if piece1.special == SpecialType.BOMB else piece2
for row in range(bomb_piece.row - 2, bomb_piece.row + 3):
for col in range(bomb_piece.col - 2, bomb_piece.col + 3):
if is_in_bounds(row, col):
cells.append(Vector2i(row, col))
# 去重
var unique_cells: Array[Vector2i] = []
for cell in cells:
if not cell in unique_cells:
unique_cells.append(cell)
return unique_cells本章小结
| 完成项 | 说明 |
|---|---|
| 特殊类型 | 条纹(水平/垂直)、炸弹、彩虹球 |
| 生成规则 | 4消→条纹,5消L/T→炸弹,5消直线→彩虹球 |
| 条纹效果 | 消除一整行或一整列 |
| 炸弹效果 | 消除3x3区域 |
| 彩虹球效果 | 消除所有同色方块 |
| 组合效果 | 两个特殊方块交换产生更强效果 |
特殊方块大大增加了游戏的策略深度和爽快感。下一章,我们将完善游戏界面——HUD、过关弹窗、失败弹窗和关卡选择界面。
