テトリス"ぐらい:レイアウトの授業
-
21-09-2019 - |
質問
長々と書きましたがワーキテトリス"ぐらいのクローンのなかはちます。ってくださいフィードバックを得ることが出来どのように再構築に私のクラスでの私の符号です。私に焦点を当てて自分のコードとして汎用しながら、よりエンジン用ゲームだけを用いてブロックとなります。
各ブロックが作成される為には、ゲームです。私のゲームは2BlockLists(リス):StaticBlocksとTetroid.StaticBlocksは、リストのすべての非可動ブロックtetroidは4ブロックの現在のtetroid.
主な世界が作成されます。
最初の新しいtetroid(4ブロックをリストにTetroid)で作成(NewTetroid)
衝突により検出され(***衝突)の機能比較のTetroidのStaticBlocks利用の場合*****)。
がtetroid停止(トの底/ブロック)、コピーされた(CopyTetroid)のStaticBlocksとTetroidが空の場合、試験のための完全なラインブロックは破壊/落下などによる検索StaticBlocks(SearchY).
新しい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.そのすべてのブロックblocklist.でなければならないと考えておTranslateBlocks
あります。もとは冗長となります。見てきたように、署名してくださいBlockList&
で動作ブロックとなります。そう呼んでいTranslate
. - をしないようにしてC-スタイルのコメント(
/*...*/
).C++スタイル(//..
)振る舞う少し綺麗に使用した場合はC-スタイルのコメントは、全体をブロックのコードで壊れるブロックも含まれるCスタイルのコメント.(単純な例として、/*/**/*/
などのコンパイラの最初の*/
としてのコメントなどの*/
なるコメントとなります。 - 何を(unnamed名前のない)
int
パラメータ?もの作りコードで読み出します。 - 関言語機能やコンベンションに出かけていく。のコピーオブジェクトはそのコピーコンストラクタです。むしろ、
CopyTetroid
機能、BlockList
コピーコンストラクタです。そのままコピーがかかったようにBlockList b1 = b0
. - よ
void SetX(Y)
やY GetX()
方法の冗長化の取得/設定先頭となっていvoid X(Y)
やY X()
.しかもゲッターでないパラメータを返します。すなわち、その他の一つはセッタでパラメータを返します。 BlockList
なも可能です。おいて非常に異なるニーズの現在のtetroid"と"のリストを静的ブロックを現在のグリッド".の静的ブロックで表すことができる簡単な配列のブロックとしてではのシーケンスを行い、または2次元配列があり便利だったのかもしれませんが、現在のアクティブ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))) { ... }
他のヒント
私は個人的には、静的なブロックを捨てると行としてそれらを扱うでしょう。あなたが必要とするよりも多くの情報を保持されます。
静的ブロックを有します世界は、単一の正方形のアレイである、行で構成されています。四角は空、または色のどちらか(あるいはあなたが特別なブロックを持っている場合は、それを拡張する)ことができます。
あなたが今持っているように、世界でも、単一のアクティブブロックを所有しています。クラスは、回転を持っているし、方法を翻訳する必要があります。ブロックは明らかにそれは既存のレンガやボードのエッジに衝突するかどうかを判断するために、世界への参照を維持する必要があります。
アクティブブロックは競技外になると、それはworld.updateのようなものを(呼び出します)など、適切な行にアクティブなブロックの部分を追加し、すべての完全な行をクリアし、あなたが失っているかどうかを判断し、最終的には必要に応じて、新しいアクティブブロックを作成します。