テトリス当たり判定について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら

トピックに返信する


答えを正確にご入力ください。答えられるかどうかでスパムボットか否かを判定します。

BBCode: ON
[img]: ON
[flash]: OFF
[url]: ON
スマイリー: OFF

トピックのレビュー
   

展開ビュー トピックのレビュー: テトリス当たり判定について

Re: テトリス当たり判定について

#4

by かずま » 8ヶ月前

みけCATさんが的確なアドバイスをしているのに、
質問者は応答しませんね。
具体的な修正方法が分からないのでしょうか?
次のように 8行だけ修正すると、問題なく動くように思うのですが。

コード: 全て選択

82行目:  j = 2; を j |= 1; に。
84行目:  j = 3; を j |= 2; に。
82行目:  j = 4; を j |= 4; に。
382行目:
   if(collision(blocY, blocX, LINES-1, pattern, spin, buffer) != 3){ を
   if((collision(blocY, blocX, LINES-1, pattern, spin, buffer) & 2) == 0){ に。
387行目:
   if(collision(blocY, blocX, LINES-1, pattern, spin, buffer) != 4){ を
   if((collision(blocY, blocX, LINES-1, pattern, spin, buffer) & 4) == 0){ に。
394行目:
   if(collision(blocY, blocX, LINES-1, pattern, spin, buffer) == 2){ を
   if(collision(blocY, blocX, LINES-1, pattern, spin, buffer) & 1){ に。
413行目:
   if(collision(blocY, blocX, LINES-1, pattern, spin, buffer) <= 2){ を
   if(collision(blocY, blocX, LINES-1, pattern, spin, buffer) <= 1){ に
437行目:
   if( collision(blocY, blocX, LINES-1, pattern, spin, buffer) == 2){ を
   if( collision(blocY, blocX, LINES-1, pattern, spin, buffer) & 1){ に
話は変わりますが、テトリスなら、
### ではなく、#### にしないといけないのでは?
あと、# が正方形ではなく、縦長なので、回転させたときに
形が大きく変わることに違和感があります。

Linux や cygwin で動くテトリスにこんなものがあります。

コード: 全て選択

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <signal.h>
#include <unistd.h>

struct itimerval h;

void t()
{
    h.it_value.tv_usec -= h.it_value.tv_usec / 3000;
    setitimer(0, &h, 0);
}

FILE *d;
struct sigaction v;
int c, l, s, I, K = 0, i = 276, j, k, q[276], Q[276], *n = q, *m, x = 17;
int 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
};

void u(void)
{
    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();
}

int G(int b)
{
    for (i = 4; i--; )
        if (q[i ? b + n[i] :b]) return 0;
    return 1;
}

void g(int b) { for (i = 4; i--; q[i ? x + n[i] : x] = b) ; }

int main(int C, char **V)
{
    char *a;
    h.it_value.tv_usec = 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");
    v.sa_handler = t;
    sigaction(SIGALRM, &v, NULL);
    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 {
                for (g(7), 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]) while (G(x+12)) x += 12;
        if (c == a[4] || c == a[5]) {
            printf("\033[H\033[J\033[0m\n");
            if (c == a[5]) break;
            for (j = 264; j--; Q[j] = 0) ;
            while (getchar() - a[4]) ;
            puts("\033[H\033[J\033[7m");
        }
    }
    system("stty -cbreak echo stop \023");
    return 0;
}
第1引数は、スピード[大きいほど速い](デフォルトは 2)
第2引数は、キー配置[左、回転、右、落下、ポーズ、終了](デフォルトは "jkl pq")
例えば、./a.out 2 hkljpq などのように起動します。

Re: テトリス当たり判定について

#3

by みけCAT » 8ヶ月前

やまんちゅ さんが書きました:どのように判定をしていけばいいでしょうか?
実際には例えば左と下に同時に接することもあるはずなのに、collision関数が下、左、右のどれか1個の接触判定しか返さない仕様なのがまずいと思います。
例えば
  • 下、左、右を2、3、4ではなく1、2、4で表す
  • 接触を検出したら、その方向を表す数値を代入するのではなくjとビットORする
  • collision関数の戻り値を用いた接触しているかの判定は、等価ではなく判定したい方向を表す数値とのビットANDで行う
とすると良さそうだと思います。

Re: テトリス当たり判定について

#2

by みけCAT » 8ヶ月前

オフトピック
インデントが乱れて見えます。
これは、デフォルトでスペースとタブが混ざったクソインデントを出力するemacsの糞仕様によるものであると思われます。
この糞仕様を回避するために、c-basic-offsetとtab-widthを同じ値に設定することをおすすめします。

テトリス当たり判定について

#1

by やまんちゅ » 8ヶ月前

今lunux環境の端末(emacs)でテトリスをある程度作ったのですが、横の当たり判定はうまく実装できているのですが、ブロックがあるないに関わらず、した判定がうまく実装出来ません。うまく着地する時もあれば、そのまましたの壁を透過してセグメントエラーが出てしまいます。どのように判定をしていけばいいでしょうか?
c言語はまだ習って一年ほどなので分からないことが多々ありますがよろしくお願いします。

コード: 全て選択

 
#include <curses.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

typedef struct{
  char name[20];
  char score[20];
}rank;


rank r[5];//ランキングを管理する構造体
int Time, s = 0;//時間、スコア
char str[256], name[20]; //文字列汎用変数,プレイヤー名
time_t current, start;//経過時間
FILE *fp;
int ch;//キー入力
int level;//レベル
int next, nextnext, d_lines = 0;//次と次の次のブロック、消した行

void go();


#define PATNUM 7
#define PATSIZEW 3
#define PATSIZEH 3
#define PATSPIN 4

static char blockPattern[PATNUM][PATSPIN][PATSIZEH][PATSIZEW] =
  { 
    {
      {{'#','#',' '}, {'#','#',' '}, {' ',' ',' '}},//pt ##
      {{'#','#',' '}, {'#','#',' '}, {' ',' ',' '}},//   ##
      {{'#','#',' '}, {'#','#',' '}, {' ',' ',' '}},//   
      {{'#','#',' '}, {'#','#',' '}, {' ',' ',' '}},//
    },{
      {{' ','#',' '}, {' ','#',' '}, {' ','#',' '}},//pt 
      {{' ',' ',' '}, {'#','#','#'}, {' ',' ',' '}},//   ###
      {{' ','#',' '}, {' ','#',' '}, {' ','#',' '}},//
      {{' ',' ',' '}, {'#','#','#'}, {' ',' ',' '}},//
    },{
      {{' ',' ',' '}, {' ','#',' '}, {'#','#','#'}},//pt 
      {{' ',' ','#'}, {' ','#','#'}, {' ',' ','#'}},//    #
      {{'#','#','#'}, {' ','#',' '}, {' ',' ',' '}},//   ###
      {{'#',' ',' '}, {'#','#',' '}, {'#',' ',' '}},//
    },{
      {{'#',' ',' '}, {'#','#','#'}, {' ',' ',' '}},//pt
      {{' ','#',' '}, {' ','#',' '}, {'#','#',' '}},//   #
      {{' ',' ',' '}, {'#','#','#'}, {' ',' ','#'}},//   ###
      {{' ','#','#'}, {' ','#',' '}, {' ','#',' '}},//
    },{
      {{' ',' ','#'}, {'#','#','#'}, {' ',' ',' '}},//pt
      {{'#','#',' '}, {' ','#',' '}, {' ','#',' '}},//     #
      {{' ',' ',' '}, {'#','#','#'}, {'#',' ',' '}},//   ###
      {{' ','#',' '}, {' ','#',' '}, {' ','#','#'}},//
    },{
      {{' ',' ',' '}, {' ','#','#'}, {'#','#',' '}},//pt 
      {{' ','#',' '}, {' ','#','#'}, {' ',' ','#'}},//    ##
      {{' ',' ',' '}, {' ','#','#'}, {'#','#',' '}},//   ##
      {{' ','#',' '}, {' ','#','#'}, {' ',' ','#'}},//
    },{
      {{' ',' ',' '}, {'#','#',' '}, {' ','#','#'}},//pt 回転軸は中央ではない(pt6と一致してしまう)
      {{' ',' ','#'}, {' ','#','#'}, {' ','#',' '}},//    ##
      {{' ',' ',' '}, {'#','#',' '}, {' ','#','#'}},//     ##
      {{' ',' ','#'}, {' ','#','#'}, {' ','#',' '}},//
    }
  };


//下+左右の当たり判定を調べる関数(下、左、右それぞれにブロック、壁がある場合に2、3、4を返すように設定)
int collision(int blocY, int blocX, int bottom, int pattern, int spin, char buffer[LINES][COLS/2] )
{
  int ii, jj;
  int j = 0;

  for( jj=0; jj<3; jj++){
    for( ii=0; ii<3; ii++){
      if(blockPattern[pattern][spin][jj][ii] == '#'){
	if((buffer[blocY + jj + 1][blocX + ii] == '#') || (blocY + jj + 1 == bottom))//下の当たり判定
	  j = 2;
	if((buffer[blocY + jj][blocX + ii - 1] == '#') || (blocX + ii - 1 == COLS/4))//左の当たり判定
	  j = 3;
	if((buffer[blocY + jj][blocX + ii + 1] == '#') || (blocX + ii + 1 == COLS/2))//右の当たり判定
	  j = 4;
      }
    }
  }
  return j;
}


//ランキングを書き込むか、新たにランキングのファイルを作成する関数
void ranking(int n)
{
  int i;
  
  //nが1ならランキングをファイルに書き込む
  if(n == 1){
    if((fp = fopen("highscore.txt", "w+")) == NULL){
      printf("ランキングファイルの作成に失敗しました。\n");
      exit(1);
    }
    for( i=0; i<5; i++){
      sprintf(str, "%s\n", r[i].name);
      if(fputs(str, fp) == EOF){
	printf("書き込みにに失敗しました。\n");
	exit(1);
      }
      sprintf(str, "%s\n", r[i].score);
      if(fputs(str, fp) == EOF){
	printf("書き込みに失敗しました\n");
	exit(1);
      }
    }
  }
  
  //nが0ならランキングを読み込めないとき
  if(n == 0){
    printf("ランキングを読み込めません。新しいファイルを作成します。\n");
    if((fp = fopen("highscore.txt", "w+")) == NULL){
      printf("ランキングファイルの作成に失敗しました。\n");
      exit(1);
    }
    for(i=0; i<5; i++){
      if(fputs("No name\n", fp) == EOF){
	printf("書き込みに失敗しました。\n");
	exit(1);
      }
      if(fputs("0\n", fp) == EOF){
	printf("書き込みに失敗しました。\n");
	exit(1);
      }
    }
  }
  return;
}


void end(int n)//ゲーム終了時の関数
{
  int i,j;
  
  mvaddstr(LINES / 2 - 7, ((COLS - 20) / 2) - 16, "                                      ");
  mvaddstr((LINES / 2) - 6, ((COLS - 20) / 2) - 16, "                                     ");
  mvaddstr((LINES / 2) - 5, ((COLS - 20) / 2) - 16, "                                     ");
  mvaddstr((LINES / 2) - 4, ((COLS - 20) / 2) - 16, "                                     "); 
  mvaddstr((LINES / 2) - 3, ((COLS - 20) / 2) - 16, "                                     "); 
  mvaddstr((LINES / 2) - 2, ((COLS - 20) / 2) - 16, "                                     "); 
  mvaddstr((LINES / 2) - 1, ((COLS - 20) / 2) - 16, "                                     "); 
  mvaddstr((LINES / 2) , ((COLS - 20) / 2) - 16, "                                     "); 
  mvaddstr((LINES / 2) + 1, ((COLS - 20) / 2) - 16, "                                     "); 
  mvaddstr((LINES / 2) + 2, ((COLS - 20) / 2) - 16, "                                     ");
  mvaddstr((LINES / 2) + 3, ((COLS - 20) / 2) - 16, "                                     ");
  
  mvaddstr(LINES / 2 - 7, ((COLS - 20) / 2) - 3, "GameOver...");
  mvprintw((LINES / 2) - 6, ((COLS - 20) / 2) - 7, "Your Score %7d !!", s);
  if (s > atoi(r[4].score)){
    mvaddstr((LINES / 2) - 5, ((COLS - 20) / 2) - 4, "High Score !!");
    mvaddstr((LINES / 2) - 4, ((COLS - 20) / 2) - 8, "Your Name -> ");
    
    
    curs_set(1);
    nocbreak();
    echo();
    timeout(-1);
    getnstr(name, 15);
    curs_set(0);
    cbreak();
    noecho();
    timeout(0);
    
    //ランキングに追加
    for (i = 4; i >= 0; i--){
      if (atoi(r[i].score) < s){
	if (i == 0){
	  strcpy(r[i].name,name);
	  sprintf(r[i].score, "%d", s);
	  break;
	}
	if (atoi(r[i - 1].score) >= s){
	  strcpy(r[i].name,name);
	  sprintf(r[i].score, "%d", s);
	  break;
	}else{
	  strcpy(r[i].name,r[i - 1].name);
	  strcpy(r[i].score,r[i - 1].score);
	}
      }
    }
    mvaddstr((LINES / 2) - 3 + i, ((COLS - 20) / 2) - 16, "You! -> "); 
    //ファイルに書き込み
    ranking(1);
    fclose(fp);
  }
  mvaddstr((LINES / 2) - 4, ((COLS - 20) / 2) - 8, "        Ranking              "); 
  for (i = 0; i < 5; i++){  
    mvprintw((LINES / 2) - 3 + i, ((COLS - 20) / 2) - 8,"%d:", i + 1);
    j = strlen(r[i].name);
    mvprintw((LINES / 2) - 3 + i, ((COLS - 20) / 2) + 6 - j,"%s", r[i].name);
    j = strlen(r[i].score);
    mvprintw((LINES / 2) - 3 + i, ((COLS - 20) / 2) + 14 - j,"%s", r[i].score);
  }
  
  mvaddstr((LINES / 2) + 3, ((COLS - 20) / 2) - 2, "q : Quit");
  
  while(1){    
    ch = getch();
    if (ch == 'q'){
      endwin();
      exit(1);
    }
  }
  return;
}


void deleteLine(char buffer[LINES][COLS/2])//揃った行を消す関数
{
  int i, j, q;
  int hantei;

  for(j = 0; j < LINES ; j++){
    hantei = 1;
    for(i = COLS/4 + 1; i < COLS/2; i++){
      if(buffer[j][i] == ' ')
	hantei = 0;
    }
    if(hantei){
      for(q = j; q>0; q--){
	for(i = COLS/4 + 1; i < COLS/2; i++){
	  buffer[q][i] = buffer[q-1][i];
	  //1段下げる
	}
      }
      s += 100 * level;
      d_lines++;
    }
  }
}


//画面横に情報を表示する関数
void info()
{
  int i,j;
  //経過時間の表示
  time(&current);
  current -= start;
  j = COLS/4 * 3;
  mvaddstr(5, j, "Time:");
  mvprintw(6, j, "              %5d", current + Time);
  //現在のレベルの表示
  mvaddstr(3, j, "Level:");
  mvprintw(4, j, "                  %d", level);
  //消した行数の表示
  mvaddstr(7, j, "deleted_lines:");
  mvprintw(8,j, "            %7d", d_lines);
  //スコアの表示
  mvaddstr(9, j, "Score:");
  mvprintw(10,j, "            %7d", s);
  //ハイスコアの表示
  mvaddstr(11,j, "High Score:");
  i = strlen(r[0].score);
  mvprintw(12,j + 19 - i, "%s", r[0].score);
  //ハイスコアプレイヤ―の表示 
  mvaddstr(13,j, "High Score Player:");
  i = strlen(r[0].name);
  mvprintw(14,j + 19 - i, "%s", r[0].name);
  //次と次の次ののブロックの表示
  mvaddstr(15,j, "Next Blocks");
  mvaddstr(16,j, "First    Second");
  mvaddstr(17,j, "+---+    +---+");
  sprintf(str, "|%c%c%c|    |%c%c%c|",blockPattern[next][0][0][0],blockPattern[next][0][0][1],blockPattern[next][0][0][2],blockPattern[nextnext][0][0][0],blockPattern[nextnext][0][0][1],blockPattern[nextnext][0][0][2] );
  mvaddstr(18,j, str);
  sprintf(str, "|%c%c%c|    |%c%c%c|",blockPattern[next][0][1][0],blockPattern[next][0][1][1],blockPattern[next][0][1][2],blockPattern[nextnext][0][1][0],blockPattern[nextnext][0][1][1],blockPattern[nextnext][0][1][2] );
  mvaddstr(19,j, str);
  sprintf(str, "|%c%c%c|    |%c%c%c|",blockPattern[next][0][2][0],blockPattern[next][0][2][1],blockPattern[next][0][2][2],blockPattern[nextnext][0][2][0],blockPattern[nextnext][0][2][1],blockPattern[nextnext][0][2][2] );
  mvaddstr(20,j,  str);
  mvaddstr(21,j, "+---+    +---+");
  
  return; 
}

int main(int argc, char **argv){
  
  int i, j;
  //ランキングファイルを読み込む
  for (;(fp = fopen("highscore.txt", "r")) == NULL;){
    ranking(0);
    fclose(fp);
  }
  for (i = 0; i < 5; i++){
    if (fgets(r[i].name, 20, fp) == NULL){
      ranking(0);
      printf("もう一度プログラムを実行しなおしてください\n");
      exit(1);
    }
    for (j = 0; r[i].name[j] != '\0'; j++);
    if (r[i].name[j - 1] == '\n')
      r[i].name[j - 1] = '\0';
    if (fgets(r[i].score, 20, fp) == NULL){
      ranking(0);
      printf("もう一度プログラムを実行しなおしてください\n");
      exit(1);
    }
    for (j = 0; r[i].score[j] != '\0'; j++);
    if (r[i].score[j - 1] == '\n')
      r[i].score[j - 1] = '\0';
  }
  fclose(fp);
  initscr();
  start_color();
  noecho();
  cbreak();
  curs_set(0);
  keypad(stdscr, TRUE);
  go();
  endwin();
  
  return 0;
}

void go()
{
  int blocX, blocY;
  
  int delay=0;
  int waitCount = 20000;
  int pattern;
  int spin = 0;
  char buffer[LINES][COLS/2];
  int i, j;
  int ii, jj;
  int a = 1;
  int t;
  
  level = 1;
  s = 0;
  Time = 0;
  timeout(0);  
  time(&start);  
  srand((unsigned) time(NULL));
  pattern = rand() % 7;
  next = rand() % 7;
  nextnext = rand() % 7;

  blocX = (COLS/8) * 3;
  blocY = 1;
  

  for( j=0; j<LINES; j++){
    for( i=0; i<COLS/2; i++){
      buffer[j][i]=' ';
    }
  }//buffer内を初期化
  
  timeout(0);
  
  while((ch=getch())!='q'){
    mvaddstr(blocY,blocX,"   ");
    mvaddstr(blocY+1,blocX,"   ");
    mvaddstr(blocY+2,blocX,"   ");
    
    for(a=0; a<LINES; a++){
      mvaddch( a, COLS/2, '|');
      mvaddch( a, COLS/4, '|');
    }
    for(a = COLS/4 + 1; a < COLS/2; a++){
      mvaddch(LINES-1, a, '-' );
    }
    
    if(delay%waitCount == 0){
      blocY += 1;
    }
    
    delay++;

    switch(ch){
    case KEY_LEFT://左に1マス移動
      if(collision(blocY, blocX, LINES-1, pattern, spin, buffer) != 3){
	blocX -=1;
      }
      break;
    case KEY_RIGHT://右に1マス移動
      if(collision(blocY, blocX, LINES-1, pattern, spin, buffer) != 4){
	blocX +=1;
      }
      break;
    case KEY_DOWN://真下に即落下させる
      while(1){
	blocY++;
	if(collision(blocY, blocX, LINES-1, pattern, spin, buffer) == 2){
	  for(jj=0; jj<3; jj++){
	    for(ii=0; ii<3; ii++){
	      if(blockPattern[pattern][spin][jj][ii] == '#'){
		buffer[blocY+jj][blocX+ii] = blockPattern[pattern][spin][jj][ii];
	      }
	    }
	  }
	  pattern = next;
	  next = nextnext;
	  nextnext = rand() % 7;
	  blocY = 1;
	  blocX = (COLS/8) * 3;
	  beep();
	  break;
	}
      }
      break;
    case ' ':
      if(collision(blocY, blocX, LINES-1, pattern, spin, buffer) <= 2){
	spin = (spin + 1) % PATSPIN;
      }
      break;
    default:
      break;
    }
    
    //buffer内のブロック
    for(j=0; j<LINES-1; j++){
      for(i= COLS/4 + 1; i<COLS/2; i++){
	mvaddch(j, i, buffer[j][i] );
      }
    }
    //操作中のブロック
    for(jj=0; jj<3; jj++){
      for(ii=0; ii<3; ii++){
	if(blockPattern[pattern][spin][jj][ii] == '#')
	mvaddch(blocY+jj, blocX+ii, blockPattern[pattern][spin][jj][ii]);
      }
    }
    
    
    //ブロックが着地した時にbufferに追加
    if( collision(blocY, blocX, LINES-1, pattern, spin, buffer) == 2){
      for(jj=0; jj<3; jj++){
	for(ii=0; ii<3; ii++){
	  if(blockPattern[pattern][spin][jj][ii] == '#'){
	    buffer[blocY+jj][blocX+ii] = blockPattern[pattern][spin][jj][ii];
	  }
	}
      }
      pattern = next;
      next = nextnext;
      nextnext = rand() % 7;
      blocX = (COLS/8) * 3;
      blocY = 1;
      beep();
      
    }
    if(s >= 500 * level){
      level++;
      waitCount = waitCount / 4 * 3;
    }
    
    deleteLine(buffer);
    
    t = 0;
    for(ii = 0; ii < 256; ii++)//ゲームオーバーの条件 
      if(buffer[1][ii] == '#')
	t = 1;
    if(t)break;
    
    info();

  }
  if(t)
    end(0);
  return;
}


 

ページトップ