Как сделать кеттери тетриса?
Я работаю над кодированием клона Tetris в XNA С# и не знаю, как лучше всего подойти к структуре структуры данных на высоком уровне.
Я полностью уверен в обнаружении столкновений, поворотах, анимации и т.д. Мне нужно знать, как лучше всего хранить "сброшенные блоки" - то есть блоки, которые больше не находятся под контролем игрока.
Я думаю, что каждый блок Tetromino должен храниться в своем собственном классе, который состоит из массива 4x4, так что блок можно легко повернуть. Проблема заключается в том, как я могу сохранить окончательное положение тетромино в сетку игры, а затем разрезать тетромино на отдельные блоки (для каждой ячейки), а затем установить основные положения сетки игры в соответствующие позиции, чтобы удерживать эти же блоки, а затем сразу же исчезнуть тетромино он достиг своей конечной позиции. Возможно, есть некоторый недостаток моего метода.
Должен ли я создать матрицу 10x20 для основной сетки игр, которую можно сохранить? или я должен использовать стеки или очереди, чтобы каким-то образом сохранить упавшие блоки. Или, может быть, есть какая-то лучшая структура методов/данных для хранения вещей?
Я уверен, что мой способ будет работать, но я обращаюсь к тому, чтобы узнать, знает ли кто-нибудь лучший способ или если мой способ достаточно хорош?
P.S. Не домашнее задание, это будет проект для моего портфолио. Спасибо.
Ответы
Ответ 1
Как только блок неподвижен, нет ничего, что отличает его от любого другого блока, который теперь неподвижен. В этом отношении, я думаю, что имеет смысл хранить всю сетку в виде матрицы, где каждый квадрат либо заполнен, либо нет (вместе с цветом блока, если он есть).
Мне кажется, что у матрицы много преимуществ. Это упростит обнаружение столкновений (не нужно сравнивать с несколькими объектами, только местами на матрице). Сохранение его как матрицы также облегчит определение, когда была создана полная строка. Кроме того, вам не нужно беспокоиться о сращивании неподвижного Tetromino, когда линия исчезает. И когда вы это делаете, вы можете просто сдвинуть всю матрицу одним махом.
Ответ 2
Это пахнет домашней работой, но мой объектно-ориентированный подход к тетрису будет заключаться в том, чтобы каждый отдельный квадрат был объектом, и оба "блока" (тетромино) и сама сетка были бы коллекциями из тех же квадратных объектов.
Объекты блока управляют поворотом и положением падающих квадратов, а ручки сетки отображают их и отделяют завершенные строки. Каждый блок будет иметь цвет или текстуру, связанные с ним, которые будут предоставлены исходным блочным объектом, из которого он пришел, но в противном случае квадраты у основания сетки не будут иметь другого указания, что они когда-либо были частью одного и того же исходного блока.
Чтобы разработать, когда вы создаете новый объект блока, он создает набор из 4 квадратов с тем же цветом/текстурой в сетке. Сетка управляет их отображением. Поэтому, когда блок попадает в нижнюю часть, вы просто забываете о блоке, и квадраты остаются ссылкой на сетку.
Вращения и отбрасывание - это операции, в которых требуется только блок, и только один из его четырех квадратов (хотя он должен будет иметь возможность запросить сетку, чтобы убедиться, что поворот может поместиться).
Ответ 3
Не делать блоки на самом деле похожими на автономные блоки - это, на мой взгляд, большая неудача многих клонов Tetris. Я приложил все усилия к тому, чтобы мой клон всегда выглядел правдоподобно, остается ли блок "в игре" или отброшен. Это означало, что вы немного отставали от простой структуры матричных данных и придумывали что-то, что поддерживало концепцию "соединения" между блочными частями.
У меня был класс под названием BlockGrid
, который используется как базовый класс для Block
и Board
. BlockGrid
имеет абстрактный (чистый виртуальный в С++) метод под названием AreBlockPartsSameBlock
, который должен переопределять подклассы, чтобы определить, принадлежат ли две разные части блоков одному и тому же блоку. Для реализации в Block
он просто возвращает true
, если в обоих местах есть части блоков. Для реализации в Board
он возвращает true
, если оба места содержат один и тот же Block
.
Класс BlockGrid
использует эту информацию для "заполнения" деталей в обработанных блоках, так что они действительно выглядят как блоки.
Ответ 4
Использование массивов было бы самым простым способом обработки тетриса. Существует прямая корреляция между тем, что вы видите на экране, и структурами, используемыми в памяти. Использование стека/очередей было бы излишним и излишне сложным.
У вас может быть 2 копии падающего блока. Один будет отображаться (Alpha), а другой будет двигаться (Beta).
Вам понадобится структура, например
class FallingBlock
{
int pos_grid_x;
int pos_grid_y;
int blocks_alpha[4][4];
int blocks_beta[4][4];
function movedDown();
function rotate(int direction();
function checkCollision();
function revertToAlpha();
function copyToBeta()
};
Массив _beta будет перемещаться или поворачиваться и проверяться на доске для столкновений. Если есть столкновение, верните его в _alpha, если нет, скопируйте _beta на _alpha.
И если на moveDown() произошло столкновение, срок службы блока закончится, и сетка _alpha будет скопирована на игровое поле, а объект FallingBlock будет удален.
Совет, конечно, должен быть другой структурой вроде:
class Board
{
int gameBoard[10][20];
//some functions go here
}
Я использовал int для представления блока, каждое значение (например, 1,2,3), представляющее другую текстуру или цвет (0 означает пустое место).
Как только блок является частью игрового поля, ему будет нужен только идентификатор текстуры/цвета.
Ответ 5
Я просто сделал это несколько дней назад, кроме WPF, а не XNA. Вот что я сделал:
Изменить:
Похоже, я определяю "Блок" иначе, чем другие люди. То, что я определяю как Блок, является одной из 4-х ячеек, которые составляют Tetromino, и фактического самого Tetromino как Piece.
Имейте блок как структуру с координатами X, Y и цветом. (Я позже добавил bool IsSet, чтобы указать, было ли это в плавающей части или на фактической доске, но это было только потому, что я хотел визуально различать их)
Как методы на блоке, у меня были Left, Right, Down и Rotate (Block center), которые вернули новый сдвинутый блок. Это позволило мне вращать или перемещать любую деталь, не зная формы или ориентации куска.
У меня был общий объект Piece, у которого был список всех блоков, которые он содержал, и индекс блока, который был центром, который используется как центр вращения.
Затем я сделал PieceFactory, который мог бы производить все разные части, а Piece не нуждался в том, чтобы узнать, какой кусок он был, я мог (и сделал) легко добавить вариации пьес, состоящих из более или менее 4 блоков без необходимости создавать новые классы
Совет состоял из Словаря, который был всеми блоками, которые были в данный момент на борту, а также размерами платы, которая была конфигурируемой. Вы можете использовать Матрицу точно так же, как и возможно, но с помощью Словаря мне нужно только перебирать блоки без пробелов.
Ответ 6
Мое решение (дизайн) с примерами в Python как хорошая замена псевдокода.
Используйте сетку 20 x 10, чтобы тетроминоны упали.
Тетроминоны состоят из блоков, которые имеют атрибуты координат (x, y) и цвета.
Итак, например, тетромино T-формы выглядит так:
. 4 5 6 7 8 .
.
19 # # #
20 #
.
Таким образом, T-форма представляет собой набор блоков с коордами (5,19), (6,19), (7,19), (6,20).
Перемещение фигуры - это вопрос применения простого преобразования ко всем коордам в группе. например для перемещения формы вниз добавьте (0,1), влево (-1,0) или вправо (1,0) ко всем коордам в коллекции, которые составляют форму.
Это также позволяет использовать простой триггер для поворота фигуры на 90 градусов. Правило состоит в том, что при повороте на 90 градусов относительно начала координат (x, y) становится равным (-y, x).
Вот пример, чтобы объяснить это. Приняв T-образ сверху, используйте (6,19) в качестве центрального блока, чтобы вращаться вокруг. Для простоты сделайте это первой координатой в коллекции, поэтому...
t_shape = [ [6,19], [5,19], [7,19], [6,20] ]
Тогда, вот простая функция для поворота этого набора координат на 90 градусов
def rotate( shape ):
X=0 # for selecting the X and Y coords
Y=1
# get the middle block
middle = shape[0]
# work out the coordinates of the other blocks relative to the
# middle block
rel = []
for coords in shape:
rel.append( [ coords[X]-middle[X], coords[Y]-middle[Y] ] )
# now rotate 90-degrees; x,y = -y, x
new_shape = []
for coords in rel:
new_shape.append( [ middle[X]-coords[Y], middle[Y]+coords[X] ] )
return new_shape
Теперь, если вы примените эту функцию к нашей коллекции координат для T-shape...
new_t_shape = rotate( t_shape )
new_t_shape
[[6, 19], [6, 18], [6, 20], [5, 19]]
Выделите это в системе координат, и это выглядит так...
. 4 5 6 7 8 .
.
18 #
19 # #
20 #
.
Это было самое сложное для меня, надеюсь, что это кому-то поможет.
Ответ 7
Имейте в виду, что предыдущий победитель Obfuscated C Code Contest реализовал довольно неплохую игру в тетрис (для терминалов VT100 на BSD unix) в менее чем 512 байтах запутанных C:
long h[4];t(){h[3]-=h[3]/3000;setitimer(0,h,0);}c,d,l,v[]={(int)t,0,2},w,s,I,K
=0,i=276,j,k,q[276],Q[276],*n=q,*m,x=17,f[]={7,-13,-12,1,8,-11,-12,-1,9,-1,1,
12,3,-13,-12,-1,12,-1,11,1,15,-1,13,1,18,-1,1,2,0,-12,-1,11,1,-12,1,13,10,-12,
1,12,11,-12,-1,1,2,-12,-1,12,13,-12,12,13,14,-11,-1,1,4,-13,-12,12,16,-11,-12,
12,17,-13,1,-1,5,-12,12,11,6,-12,12,24};u(){for(i=11;++i<264;)if((k=q[i])-Q[i]
){Q[i]=k;if(i-++I||i%12<1)printf("\033[%d;%dH",(I=i)/12,i%12*2+28);printf(
"\033[%dm "+(K-k?0:5),k);K=k;}Q[263]=c=getchar();}G(b){for(i=4;i--;)if(q[i?b+
n[i]:b])return 0;return 1;}g(b){for(i=4;i--;q[i?x+n[i]:x]=b);}main(C,V,a)char*
*V,*a;{h[3]=1000000/(l=C>1?atoi(V[1]):2);for(a=C>2?V[2]:"jkl pq";i;i--)*n++=i<
25||i%12<2?7:0;srand(getpid());system("stty cbreak -echo stop u");sigvec(14,v,
0);t();puts("\033[H\033[J");for(n=f+rand()%7*4;;g(7),u(),g(0)){if(c<0){if(G(x+
12))x+=12;else{g(7);++w;for(j=0;j<252;j=12*(j/12+1))for(;q[++j];)if(j%12==10){
for(;j%12;q[j--]=0);u();for(;--j;q[j+12]=q[j]);u();}n=f+rand()%7*4;G(x=17)||(c
=a[5]);}}if(c==*a)G(--x)||++x;if(c==a[1])n=f+4**(m=n),G(x)||(n=m);if(c==a[2])G
(++x)||--x;if(c==a[3])for(;G(x+12);++w)x+=12;if(c==a[4]||c==a[5]){s=sigblock(
8192);printf("\033[H\033[J\033[0m%d\n",w);if(c==a[5])break;for(j=264;j--;Q[j]=
0);while(getchar()-a[4]);puts("\033[H\033[J\033[7m");sigsetmask(s);}}d=popen(
"stty -cbreak echo stop \023;cat - HI|sort -rn|head -20>/tmp/$$;mv /tmp/$$ HI\
;cat HI","w");fprintf(d,"%4d on level %1d by %s\n",w,l,getlogin());pclose(d);}
http://www.ioccc.org/1989/tromp.hint
Ответ 8
Я никоим образом не эксперт Tetris, но, как вы описали, матрица размером 10x20 кажется мне естественным выбором.
Это очень упростит, когда придет время проверить, завершили ли вы линию или нет, и справиться с ней. Просто итерация над 2d-массивом, смотрящая на логические значения каждой позиции, чтобы увидеть, если они добавляют до 10 позиций блока.
Однако, если у вас есть завершенная строка, у вас будет ручная очистка. Нужно все переложить. Все, хотя это не так уж важно, когда дело доходит до него.
Ответ 9
Используя логику Симона Певерета, вот что я получил в С#
public class Tetromino
{
// Block is composed of a Point called Position and the color
public Block[] Blocks { get; protected internal set; }
// Constructors, etc.
// Rotate the tetromino by 90 degrees, clock-wise
public void Rotate()
{
Point middle = Blocks[0].Position;
List<Point> rel = new List<Point>();
foreach (Block b in Blocks)
rel.Add(new Point(b.Position.x - middle.x, b.Position.y - middle.y));
List<Block> shape = new List<Block>();
foreach (Point p in rel)
shape.Add(new Block(middle.x - p.y, middle.y + p.x));
Blocks = shape.ToArray();
}
public void Translate(Point p)
{
// Block Translation: Position+= p;
foreach (Block b in Blocks)
b.Translate(p);
}
}
Примечание. Использование XNA, структура Point
может быть заменена на Vector2D
Ответ 10
в моем примере (Java) - все цифры имеют списки блоков, которые можно удалить при необходимости. Также в моем классе Board у меня есть список цифр и полевая переменная, которая контролируется пользователем. Когда фигура "приземлилась" - она попадает в список других цифр, и новая фигура становится контролируемой пользователем.
Лучшее объяснение здесь: http://bordiani.wordpress.com/2014/10/20/tetris-in-java-part-i-overview/