質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

1901閲覧

テトリスで一次回転による回転でブロックの中心を原点にして回転する方法が知りたい。

退会済みユーザー

退会済みユーザー

総合スコア0

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2020/10/13 05:15

編集2020/10/13 05:16

ブロックを回転させると際に表示されているブロックの中心を原点にして回転させたいのですがどうすればいいのでしょうか?
4x4の配列にブロックがるので2x2を中心にブロックを回転させたいです。一次回転を使って最後にその分のCELを引いて原点をずらしているのですがどうすればいいのでしょうか?上手く実装出来ません。

参考サイト: http://www.geisya.or.jp/~mwm48961/kou2/linear_image3.html

cpp

1 2//描画アップデート 3void Game::GenerateOutput() 4{ 5 6 //画面クリア関係 7 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 8 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 9 10 11 //ステージ 12 for (int y = 0; y < 11; y++) 13 { 14 for (int x = 0; x < 10; x++) 15 { 16 if (stage[y][x] == 1) { 17 DrawMap->DrawGraph(stage_pos.x + (CELL * x) , stage_pos.y + (-CELL * y)); 18 } 19 } 20 } 21 22 23 24 //ブロック表示 25 for (int i = 0; i < 4; i++) { 26 for (int j = 0; j < 4; j++) { 27 28 if (block[1][i][j] == 1) { 29 30 glm::ivec2 v; 31 v.x = (CELL * j); 32 v.y = -(CELL * i); 33 34 Set_rotate(&v, r); 35 36 sprite->DrawGraph(move_sprite.x + v.x, move_sprite.y + (v.y)); 37 38 //sprite->DrawGraph(move_sprite.x + v.x , move_sprite.y - v.y ); 39 } 40 } 41 } 42 43 44 45 46 47 //デバッグログ 48 char str[1000]; 49 sprintf_s(str, sizeof(str),"block x %d , y %d", move_sprite.x, move_sprite.y); 50 font->RenderText(str, -CELL * 7, -CELL * 2, 0.5, glm::vec3(1.0, 1.0, 1.0)); 51 52 sprintf_s(str, sizeof(str), "stage x %d , y %d", stage_pos.x, stage_pos.y); 53 font->RenderText(str, -CELL * 7, -CELL * 3, 0.5, glm::vec3(1.0, 1.0, 1.0)); 54 55 56 57 sprintf_s(str, sizeof(str), "R: %d", r); 58 font->RenderText(str, -CELL * 7, -CELL * 1, 0.5, glm::vec3(1.0, 1.0, 1.0)); 59 60 61 glViewport(0, 0, WIDTH, HEIGHT); //ビューポート 62 glfwSwapBuffers(Window); //ダブルバッファリング 63 glfwPollEvents(); //イベント処理 64 65 66} 67 68 69void Game::Set_rotate(glm::ivec2 *pos,const int R) 70{ 71 float posX = (float)pos->x; 72 float posY = (float)pos->y; 73 74 float r[4] = { 0 }; 75 76 r[0] = cos((PI / 2.0f) * R); 77 r[1] = sin((PI / 2.0f) * R); 78 79 r[2] = -sin((PI / 2.0f) * R); 80 r[3] = cos((PI / 2.0f) * R); 81 82 glm::imat2 m = glm::make_mat2(r); 83 84 float p[2]; 85 p[0] = posX; 86 p[1] = posY; 87 88 glm::ivec2 m2 = glm::make_vec2(p); 89 90 glm::ivec2 t = (m * m2); 91 92 pos->x = (int)t.x - CELL * 2; 93 pos->y = (int)t.y + CELL * 2; 94 95 // printf(" あああ: %d \n",pos->x); 96 97}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

演算で回転後の座標を求めたい ということであれば,

回転前の座標 P に対して回転後の座標 q を求める手続きは以下のようになるでしょう.

  1. 回転中心Oを原点とする座標系に変換: P' = P - O
  2. 回転: P'' = Pを回転した結果
  3. 元の座標系に戻す:q = P'' + O

回転中心の位置次第では,回転後の座標が「4x4」の範囲に収まらないかもしれないことに注意してください.
(Pの座標を配列のindexとするような場合,qは元の配列の有効なindex範囲となる保証はないので.)


それはそれとして,"テトリス"とのことなので,回転は90度単位であろうと思います.
であれば,回転行列の要素は{0,1,-1}のいずれかですから,わざわざsinやcosを用いずとも良いでしょう.
(x,y) を(原点まわりに)90度回転した結果というのは,以下のいずれかです

  • (y,-x)
  • (-y,x)

(どっちが「右回転」でどっちが「左回転」かは座標軸の取り方によります.ご自身の世界の上で検算してみれば明確になるでしょう)


簡単なサンプルコード.
4x4配列の中身を90度回転する.

C++

1namespace 2{//座標変換 3 4 //便宜上, 5 //4x4配列のindexの世界と,回転を考える世界の座標との対応を以下とした 6 // 7 // index ←→ 回転用 8 // 0 ←→ -2 9 // 1 ←→ -1 10 // 2 ←→ 1 11 // 3 ←→ 2 12 13 inline int Cvt2ForRotCoord( int ArrayIndexCoord ) 14 { 15 return ( ArrayIndexCoord<=1 ? ArrayIndexCoord-2 : ArrayIndexCoord-1 ); 16 } 17 18 inline int Cvt2IndexCoord( int ForRotCoord ) 19 { 20 return ( ForRotCoord<0 ? ForRotCoord+2 : ForRotCoord+1 ); 21 } 22 23 std::pair<int,int> Cvt2ForRotCoord( std::pair<int,int> ArrayIndexCoord ) 24 { 25 return std::pair<int,int>( 26 Cvt2ForRotCoord( ArrayIndexCoord.first ), 27 Cvt2ForRotCoord( ArrayIndexCoord.second ) 28 ); 29 } 30 31 std::pair<int,int> Cvt2IndexCoord( std::pair<int,int> ForRotCoord ) 32 { 33 return std::pair<int,int>( 34 Cvt2IndexCoord( ForRotCoord.first ), 35 Cvt2IndexCoord( ForRotCoord.second ) 36 ); 37 } 38} 39 40//座標値の90度回転 41std::pair<int,int> Rotate90Deg( std::pair<int,int> P ) 42{ //※どちらを用いるかで回転の向きが変わるよ 43#if 0 44 return std::pair<int,int>( P.second, -P.first ); 45#else 46 return std::pair<int,int>( -P.second, P.first ); 47#endif 48} 49 50//配列内容の表示 51void ShowArray( const char A[4][4] ) 52{ 53 for( int y=0; y<4; ++y ) 54 { 55 for( int x=0; x<4; ++x ) 56 { 57 std::cout << '[' << A[y][x] << ']'; 58 } 59 std::cout << std::endl; 60 } 61} 62 63int main() 64{ 65 char SrcArray[4][4] = 66 { 67 { '0','1','2','3' }, 68 { '4','5','6','7' }, 69 { '8','9','A','B' }, 70 { 'C','D','E','F' }, 71 }; 72 std::cout << "Src:" << std::endl; 73 ShowArray( SrcArray ); 74 75 //{90度,180度,270度,360度}まわしてみるテスト 76 for( int ith=1; ith<=4; ++ith ) 77 { 78 char ResultArray[4][4] = {0}; //SrcArrayの内容を回転した結果をここに格納する 79 80 for( int y=0; y<4; ++y ) 81 { 82 for( int x=0; x<4; ++x ) 83 { 84 std::pair<int,int> ArrayIndexCoord( x,y ); 85 auto ForRotCoord = Cvt2ForRotCoord( ArrayIndexCoord ); 86 87 for( int i=0; i<ith; ++i ) 88 { ForRotCoord = Rotate90Deg( ForRotCoord ); } 89 90 auto ResultIndexCoord = Cvt2IndexCoord( ForRotCoord ); 91 92 ResultArray[ ResultIndexCoord.second ][ ResultIndexCoord.first ] = SrcArray[y][x]; 93 } 94 } 95 96 std::cout << "Result:(" << ith*90 << "度回転)" << std::endl; 97 ShowArray( ResultArray ); 98 } 99 100 std::cin.ignore(); 101 return 0; 102}

投稿2020/10/13 09:18

編集2020/10/13 10:59
fana

総合スコア11632

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2020/10/13 09:22 編集

関係ないけど,「4x4の配列」の形で持つと何か実装的に便利なんですかね? (素人考えでは,ブロックが必ず4マスで構成されているならば,"座標4個"で表現すれば良いのではないか?とか……)
退会済みユーザー

退会済みユーザー

2020/10/13 10:55

どちらも出来ませんでした。どうすればいいのでしょうか?
fana

2020/10/13 11:05

4x4の配列内容を回すサンプルを追記しました. 座標を表す型は std::pair<int,int> にしました. 【配列indexな座標 ArrayIndexCoord を,一旦,「"中心周りの回転" を考えるのに都合の良い座標系」の値に変換し,それをその世界で回転し,最後に元の配列indexな座標の世界に戻す】 という処理を行っています.
退会済みユーザー

退会済みユーザー

2020/10/13 11:09

自分は初心者なのでちょっと難解なので諦めて配列にそれぞれの角度のデータをもたせるようにしました。
fana

2020/10/13 11:09

※このサンプルコードで用いている「"中心周りの回転" を考えるのに都合の良い座標系」は,回答記載の > 回転中心Oを原点とする座標系 とは異なっていますけれども, 「目的の処理を考えるのに都合がよい世界に飛ばしてそこで処理して元の世界に戻す」 という大枠は変わらないので,まぁどうでもいいところかと思います.
fana

2020/10/13 11:12

> 配列にそれぞれの角度のデータをもたせるようにしました ということであれば, > 最初から全回転分のパターンの配列を持っておく という話が書かれている回答を BA とすべきであるように思いますが,どうなんでしょう?
退会済みユーザー

退会済みユーザー

2020/10/13 11:18

すいませんでした、修正します
guest

0

ベストアンサー

テトリスの回転であれば小難しいことはせずに

  • 最初から全回転分のパターンの配列を持っておく
  • 配列の右回転、左回転の入れ替え処理を作る

のどちらかでいいと思います。

投稿2020/10/13 05:58

mah

総合スコア591

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/10/13 06:00

せっかくなので一次変換を使ったプログラムを作りたいのですがどうすればいいのでしょうか?
mah

2020/10/13 06:07

その方法で作ったことはないのでわかりません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問