C ++中的俄罗斯方块对象模型

时间:2016-03-21 10:14:30

标签: c++

我正在尝试使用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]);
}

然后实际将传递的数组复制到新数组中,以便可以编辑(例如旋转)新数据而不影响蓝图数组。

我仍然遇到面向对象建模范式的问题(虽然我在其他语言方面有一些经验),所以任何建议都会非常感激!

2 个答案:

答案 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