我正在尝试使用C ++构建tetris对象模型。我从第一个名为Piece(有点伪代码)的类开始:
class Piece
{
public:
Piece(SomeShape passedShape):shape(passedShape);
~Piece();
private:
int shape[4][4];
}
每件都有一个形状(4x4阵列代表所有可能的俄罗斯方块形状之一)。
还有类PieceFactory:
class PieceFactory
{
public:
PieceFactory();
~PieceFactory();
Piece CreatePiece() const;
private:
const int squarePiece[4][4]=
{{0, 0, 0, 0},
{0, 1, 1, 0},
{0, 1, 1, 0},
{0, 0, 0, 0}};
const int linePiece[4][4]=
{{0, 1, 0, 0},
{0, 1, 0, 0},
{0, 1, 0, 0},
{0, 1, 0, 0}};
}
我希望有一个函数返回一块随机形状,我不知道最聪明的方法是什么。我的想法是创建一个包含所有形状的列表(那些是4x4数组),生成随机数,并在随机生成的索引处返回元素,即返回相应的"数组" (我的意思是指向它)。还有更优雅的方法吗?
Piece PieceFactory::CreatePiece() const
{
int radnomNumer = generateRandom();
return new Piece(someListContainingAllShapes[randomNumber]);
}
然后实际将传递的数组复制到新数组中,以便可以编辑(例如旋转)新数据而不影响蓝图数组。
我仍然遇到面向对象建模范式的问题(虽然我在其他语言方面有一些经验),所以任何建议都会非常感激!
答案 0 :(得分:2)
我首先会注意到,包含当前模型中所有可能形状的列表将包含2^16
个元素。要减少数量,可以建立一些等价标准,例如
{{0, 1, 0, 0},
{0, 1, 0, 0},
{0, 1, 0, 0},
{0, 1, 0, 0}}
和
{{0, 0, 0, 0},
{0, 0, 0, 0},
{0, 0, 0, 0},
{1, 1, 1, 1}}
具有相同的形状(具有不同的方向和位置)。但是你很容易看出这会导致一些非常复杂和繁琐的设计。
我的建议是首先确定在形状,位置和方向发挥作用时您真正需要的信息。我认为当你需要计算“碰撞”时会发生这种情况。俄罗斯方块之间和底部的当前形状之间。一旦分解了这个计算,你就可以更好地了解如何正确编码形状信息。
无论您如何决定处理低级处理, 我可能会寻求一个如下所述的类层次结构。
一个Pile
数据类,用于描述屏幕底部的一堆碎片。它可能是一个简单的位图。
基类Shape
类,用于存储作品的不可变几何形状及其可变状态(位置,方向);并使用两个与Pile
交互的虚拟方法来确定当前位置/方向是否表示与底部的碰撞,如果发生这种情况则更新Pile
。
LShape : public BaseShape {
public:
virtual bool inCollision(const Pile&) override; // true if this piece has hit the bottom
virtual void pileOnBottom(Pile&) override; // update the pile of pieces if the bottom was hit
}
通过这种方式,特定形状的责任是正确处理碰撞。
生成随机碎片的问题显得相当微不足道。具有额外generateRandom()
的工厂类在注册件列表中选择随机元素对我来说似乎完全没问题。
答案 1 :(得分:2)
这里的一个基本设计问题是,不同形状的tetrominos是否应该有不同的类别:
在你的设计中,这些作品都属于同一类,通过自己的“模板”网格副本来处理形状:
形状特定规则在标准tetris中不是问题。但如果您计划将来进行高级游戏改进,例如,如果不同的棋子可能具有不同的重量,不同的材料,以及形状可能会影响加速度(速度和运动模式或弹跳),那么这可能是一个限制。
必要的改进 :使用4x4网格,在相同方向上翻转两次会使条形移动,这对用户来说真的很烦人。我建议使用奇数,比如5x5,以确保固定的旋转中心点
由于你已经有一块factory,很难有不同类别的不同类型的作品:
class OShape : public Piece {
...
};
但为了使这个工作,你的工厂应该返回一个指向一块(或更好,一个shared_ptr<Piece>
)的指针,你的片断函数和析构函数应该是虚拟的,以享受多态行为。
这种方法的一大优点是,您可以直接在网格中绘制棋子,了解其位置和方向并检测碰撞,而不必强制使用微型网格表示。
然而,不方便的是你有更多的课程,每个课程都需要一些特殊的想法。此外,形状会受到代码的限制(即,您无法为用户提供形状编辑器以使用自己的形状扩展游戏。)
在这两个极端之间的许多其他变体中,我建议看一下decorator pattern:
Shape
类用于表示通用形状。它被实例化以创建基本形状对象(以及基于0和3之间的旋转因子的网格表示)。
Piece
类用作Shape的装饰器,添加工件的位置和方向并引用基础形状对象。
距离您的设计不远,但它的优势在于Piece
的管理与Shape
的操纵之间有明确的separation of concerns。