我已经写了一个工作方克隆,但它有一个相当混乱的布局。我能请得到的反馈,在如何重组我的课,让我更好的编码.我的重点是制作我的代码作为一般成为可能,试图使更多的发动机的游戏,只有使用区块。

每一块是创建单独的游戏。我的游戏有2个黑名单(联系清单):StaticBlocks和Tetroid.StaticBlocks显然是所列的所有非移动块,并tetroid是4块目前的tetroid.

  1. 在主要的世界被创建。

  2. 第一个新的tetroid(4块在一个列表Tetroid)创建(NewTetroid)

  3. 检测到碰撞通过的(***碰撞)的功能,通过比较每个Tetroid与所有StaticBlocks使用的(如果*****)的功能。

  4. 当tetroid停止(击的底部/块),它是复制(CopyTetroid)的StaticBlocks和Tetroid是空的,然后试验了为完成行,块被破坏/下降等,通过搜索StaticBlocks与(SearchY).

  5. 一个新的tetroid是创建。

(TranslateTetroid)和(RotateTetroid)执行操作上的每一块在Tetroid列表一通过一个( 我认为这是不好的练习).

(DrawBlockList)只是通过一个清单,运行绘制()功能对每个区块。

旋转控制,通过设置旋转轴的相对来的第一块在Tetroid时(NewTetroid)。我的旋转的功能(旋转)对每一块旋转,围绕的轴线,使用输入+-1左右旋转。RotationModes和国块,旋转2个或4个不同的方式,界定什么样的国家,他们目前在,以及他们是否应该将转左或右。 我不喜欢这些如何定义"的世界",但我不知道在哪里把他们,同时仍保持我的(旋转)通用功能的每个块.

我的类如下

class World
{
    public:
    /* Constructor/Destructor */
    World();
    ~World();

    /* Blocks Operations */
    void AppendBlock(int, int, BlockList&);
    void RemoveBlock(Block*, BlockList&);;

    /* Tetroid Operations */
    void NewTetroid(int, int, int, BlockList&);
    void TranslateTetroid(int, int, BlockList&);
    void RotateTetroid(int, BlockList&);
    void CopyTetroid(BlockList&, BlockList&);

    /* Draw */
    void DrawBlockList(BlockList&);
    void DrawWalls();

    /* Collisions */
    bool TranslateCollide(int, int, BlockList&, BlockList&);
    bool RotateCollide(int, BlockList&, BlockList&);
    bool OverlapCollide(BlockList&, BlockList&); // For end of game

    /* Game Mechanics */
    bool CompleteLine(BlockList&); // Test all line
    bool CompleteLine(int, BlockList&); // Test specific line
    void ColourLine(int, BlockList&);
    void DestroyLine(int, BlockList&);
    void DropLine(int, BlockList&); // Drops all blocks above line

    int rotationAxisX;
    int rotationAxisY;
    int rotationState; // Which rotation it is currently in
    int rotationModes; // How many diff rotations possible

    private:
    int wallX1;
    int wallX2;
    int wallY1;
    int wallY2;
};

class BlockList
{
    public:
    BlockList();
    ~BlockList();

    Block* GetFirst();
    Block* GetLast();

    /* List Operations */
    void Append(int, int);
    int  Remove(Block*);
    int  SearchY(int);

    private:
    Block *first;
    Block *last;
};

class Block
{
    public:
    Block(int, int);
    ~Block();

    int GetX();
    int GetY();

    void SetColour(int, int, int);

    void Translate(int, int);
    void Rotate(int, int, int);

    /* Return values simulating the operation (for collision purposes) */
    int IfTranslateX(int);
    int IfTranslateY(int);
    int IfRotateX(int, int, int);
    int IfRotateY(int, int, int);

    void Draw();

    Block *next;

    private:
    int pX; // position x
    int pY; // position y
    int colourR;
    int colourG;
    int colourB;
};

对不起,如果这一点不清楚或长篇大论,我只是在寻找一些帮助重组。

有帮助吗?

解决方案

  • 什么是的 责任 World 课吗?这只是一个blob包含的几乎每一种功能。这是不好的设计。一个明显的责任是"代表格上这块放".但是,这没有创造tetroids或操纵的框列出或绘图。事实上,大多数,可能不需要在一类。我期望的 World 对象包含的 BlockList 你叫StaticBlocks,因此它可以界定格栅上你玩。
  • 为什么你定义自己的 Blocklist?你说你想你的代码是通用的,那么为什么不允许 任何 容器可以用?为什么我不能使用 std::vector<Block> 如果我想要吗?或 std::set<Block>, 或者一些家庭酿造的容器?
  • 使用简单的名字不重复的信息或者违背自己。 TranslateTetroid 不翻译tetroid.它意味着所有块在一个黑名单.因此,它应该是 TranslateBlocks 或者什么的。但是,即使是多余的。我们可以看到,从签名(它需要一个 BlockList&),它适用于区块。所以就叫它 Translate.
  • 尽量避免C式的评论意见(/*...*/).C++style(//..)的行为方式一点好,如果你使用了C-style comment出一整块代码,它将打破,如果这块也包含C式的意见。(作为一个简单的例子, /*/**/*/ 不会的工作,如编译器将看到第一 */ 作为结尾的评论,因此最后一个 */ 不会被认为是一个意见。
  • 什么是所有的(名) int 参数?它让你的代码,无法阅读。
  • 尊重语言特征和公约。该方法复制目的使用其副本构造。因此,而不是一个 CopyTetroid 功能,得到 BlockList 一个复制的构造。然后如果我需要复制一个,我可以简单地做 BlockList b1 = b0.
  • 而不是 void SetX(Y)Y GetX() 方法,删除多余的Get/Set前缀和简单有 void X(Y)Y X().我们知道这是一个吸气,因为它没有参数和返回的价值。我们知道另一个是设置器,因为它需要参数和返回无效。
  • BlockList 不是一个非常好的抽象概念。你有非常不同的需求"的前tetroid"和"名单的静态块目前在网"。静块可以表示一个简单的顺序块你有(虽然序列的行或2D阵列,可以更方便),但当前活动的tetroid需要的额外信息,诸如旋转中心(它不属于在 World).
    • 一个简单的方式表示tetroid,以方便轮换,可能会有成员块储存的一个简单的偏离心旋转。这使得轮换更容易计算的,并意味着会员块不必要更新,在所有期间的翻译。只是旋转的中心必须移动。
    • 在静态表,它甚至没有效块知道他们的位置。相反,网格应地图上的位置,以块(如果我问问格"这块存在的细胞 (5,8), 它应该能够返回。但是这块本身并不需要存储的协调。如果是这样,它可以成为一个维护头痛的问题。什么如果,由于一些细微的错误,两个区块结了相同的坐标?这可能发生如果块储存他们自己的协调,但是没有如果格持有一个列表中的这块是在哪里。)
    • 这告诉我们,我们需要一个表示对"静态的方框",以及另一个"动态的框"(它需要储存偏离的tetroid的中心)。事实上,"静态"的方框,可以归结为基础:一个小网格中包含的一个方框,这块的颜色,或者它不含有一块。没有进一步相关的行为与这些区块,因此,也许这是电池到其它安置,应仿效代替。
    • 我们需要一类代表可动/态tetroid.
  • 由于很多你的碰撞探测是"预测",它涉及"如果我移动的对象在这里",可能简单以实现非改变翻译/转功能。这些应该离开的原未经修改的对象,并转动/翻译副本返回。

因此,这里是第一个通过你的代码,只需重新命名、评论和消除码不结构的改变太多了。

class World
{
public:
    // Constructor/Destructor
    // the constructor should bring the object into a useful state. 
    // For that, it needs to know the dimensions of the grid it is creating, does it not?
    World(int width, int height);
    ~World();

    // none of thes have anything to do with the world
    ///* Blocks Operations */
    //void AppendBlock(int, int, BlockList&);
    //void RemoveBlock(Block*, BlockList&);;

    // Tetroid Operations
    // What's wrong with using BlockList's constructor for, well, constructing BlockLists? Why do you need NewTetroid?
    //void NewTetroid(int, int, int, BlockList&);

    // none of these belong in the World class. They deal with BlockLists, not the entire world.
    //void TranslateTetroid(int, int, BlockList&);
    //void RotateTetroid(int, BlockList&);
    //void CopyTetroid(BlockList&, BlockList&);

    // Drawing isn't the responsibility of the world
    ///* Draw */
    //void DrawBlockList(BlockList&);
    //void DrawWalls();

    // these are generic functions used to test for collisions between any two blocklists. So don't place them in the grid/world class.
    ///* Collisions */
    //bool TranslateCollide(int, int, BlockList&, BlockList&);
    //bool RotateCollide(int, BlockList&, BlockList&);
    //bool OverlapCollide(BlockList&, BlockList&); // For end of game

    // given that these functions take the blocklist on which they're operating as an argument, why do they need to be members of this, or any, class?
    // Game Mechanics 
    bool AnyCompleteLines(BlockList&); // Renamed. I assume that it returns true if *any* line is complete?
    bool IsLineComplete(int line, BlockList&); // Renamed. Avoid ambiguous names like "CompleteLine". is that a command? (complete this line) or a question (is this line complete)?
    void ColourLine(int line, BlockList&); // how is the line supposed to be coloured? Which colour?
    void DestroyLine(int line, BlockList&); 
    void DropLine(int, BlockList&); // Drops all blocks above line

    // bad terminology. The objects are rotated about the Z axis. The x/y coordinates around which it is rotated are not axes, just a point.
    int rotationAxisX;
    int rotationAxisY;
    // what's this for? How many rotation states exist? what are they?
    int rotationState; // Which rotation it is currently in
    // same as above. What is this, what is it for?
    int rotationModes; // How many diff rotations possible

private:
    int wallX1;
    int wallX2;
    int wallY1;
    int wallY2;
};

// The language already has perfectly well defined containers. No need to reinvent the wheel
//class BlockList
//{
//public:
//  BlockList();
//  ~BlockList();
//
//  Block* GetFirst();
//  Block* GetLast();
//
//  /* List Operations */
//  void Append(int, int);
//  int  Remove(Block*);
//  int  SearchY(int);
//
//private:
//  Block *first;
//  Block *last;
//};

struct Colour {
    int r, g, b;
};

class Block
{
public:
    Block(int x, int y);
    ~Block();

    int X();
    int Y();

    void Colour(const Colour& col);

    void Translate(int down, int left); // add parameter names so we know the direction in which it is being translated
    // what were the three original parameters for? Surely we just need to know how many 90-degree rotations in a fixed direction (clockwise, for example) are desired?
    void Rotate(int cwSteps); 

    // If rotate/translate is non-mutating and instead create new objects, we don't need these predictive collision functions.x ½
    //// Return values simulating the operation (for collision purposes) 
    //int IfTranslateX(int);
    //int IfTranslateY(int);
    //int IfRotateX(int, int, int);
    //int IfRotateY(int, int, int);

    // the object shouldn't know how to draw itself. That's building an awful lot of complexity into the class
    //void Draw();

    //Block *next; // is there a next? How come? What does it mean? In which context? 

private:
    int x; // position x
    int y; // position y
    Colour col;
    //int colourR;
    //int colourG;
    //int colourB;
};

// Because the argument block is passed by value it is implicitly copied, so we can modify that and return it
Block Translate(Block bl, int down, int left) {
    return bl.Translate(down, left);
}
Block Rotate(Block bl, cwSteps) {
    return bl.Rotate(cwSteps);
}

现在,让我们添加一些缺失的部分:

第一,我们需要代表的"动态"块,tetroid拥有他们,和静态块或细胞中的一个网格。(我们还会添加一个简单的"碰撞"的方法向世界/格类)

class Grid
{
public:
    // Constructor/Destructor
    Grid(int width, int height);
    ~Grid();

    // perhaps these should be moved out into a separate "game mechanics" object
    bool AnyCompleteLines();
    bool IsLineComplete(int line);
    void ColourLine(int line, Colour col);Which colour?
    void DestroyLine(int line); 
    void DropLine(int);

    int findFirstInColumn(int x, int y); // Starting from cell (x,y), find the first non-empty cell directly below it. This corresponds to the SearchY function in the old BlockList class
    // To find the contents of cell (x,y) we can do cells[x + width*y]. Write a wrapper for this:
    Cell& operator()(int x, int y) { return cells[x + width*y]; }
    bool Collides(Tetroid& tet); // test if a tetroid collides with the blocks currently in the grid

private:
    // we can compute the wall positions on demand from the grid dimensions
    int leftWallX() { return 0; }
    int rightWallX() { return width; }
    int topWallY() { return 0; }
    int bottomWallY { return height; }

    int width;
    int height;

    // let this contain all the cells in the grid. 
    std::vector<Cell> cells; 

};

// represents a cell in the game board grid
class Cell {
public:
    bool hasBlock();
    Colour Colour();
};

struct Colour {
    int r, g, b;
};

class Block
{
public:
    Block(int x, int y, Colour col);
    ~Block();

    int X();
    int Y();
void X(int);
void Y(int);

    void Colour(const Colour& col);

private:
    int x; // x-offset from center
    int y; // y-offset from center
    Colour col; // this could be moved to the Tetroid class, if you assume that tetroids are always single-coloured
};

class Tetroid { // since you want this generalized for more than just Tetris, perhaps this is a bad name
public:
    template <typename BlockIter>
    Tetroid(BlockIter first, BlockIter last); // given a range of blocks, as represented by an iterator pair, store the blocks in the tetroid

    void Translate(int down, int left) { 
        centerX += left; 
        centerY += down;
    }
    void Rotate(int cwSteps) {
        typedef std::vector<Block>::iterator iter;
        for (iter cur = blocks.begin(); cur != blocks.end(); ++cur){
            // rotate the block (*cur) cwSteps times 90 degrees clockwise.
                    // a naive (but inefficient, especially for large rotations) solution could be this:
        // while there is clockwise rotation left to perform
        for (; cwSteps > 0; --cwSteps){
            int x = -cur->Y(); // assuming the Y axis points downwards, the new X offset is simply the old Y offset negated
            int y = cur->X(); // and the new Y offset is the old X offset unmodified
            cur->X(x);
            cur->Y(y);
        }
        // if there is any counter-clockwise rotation to perform (if cwSteps was negative)
        for (; cwSteps < 0; --cwSteps){
            int x = cur->Y();
            int y = -cur->X();
            cur->X(x);
            cur->Y(y);
        }
        }
    }

private:
    int centerX, centerY;
    std::vector<Block> blocks;
};

Tetroid Translate(Tetroid tet, int down, int left) {
    return tet.Translate(down, left);
}
Tetroid Rotate(Tetroid tet, cwSteps) {
    return tet.Rotate(cwSteps);
}

我们将需要重新实现了投机性碰撞的检查。鉴于非改变翻译/旋转方法,这就是简单的:我们只是创建旋转/翻译副本,以及测试这些碰撞:

// test if a tetroid t would collide with the grid g if it was translated (x,y) units
if (g.Collides(Translate(t, x, y))) { ... }

// test if a tetroid t would collide with the grid g if it was rotated x times clockwise
if (g.Collides(Rotate(t, x))) { ... }

其他提示

我会亲自沟静块和处理它们作为行。有一个静态块你保持更多的信息比你所需要的。

一个世界是由行,这是一系列单方块。方块既可以是空的,或颜色(或延长,如果你有特殊块)。

世界还拥有一个单一的活动框,因为你现在有。该类应该有一个旋转和平的方法。本框显然将需要维持一个参考世界,以确定它是否会发生碰撞与现有的砖或边缘板。

当活动块出去玩,就会呼叫什么样的世界。更新()将增加的碎片的活动块的适当行,清除所有全行,决定如果你已经失去了等,并最终创建一个新的活动框如果需要的话。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top