(GLUT)複数の円を動かす

OpenGL
TVアニメ「グランクレスト戦記」PV | 2018年1月より放送開始

アニメ『グランクレスト戦記』(2018)

原作は「ロードス島戦記」の水野良氏らしく、いつか氏の小説を読んでみたいと思っていましたが、アニメが先になってしまいました。
1話~3話目あたりですが、見てると陣取りゲームをプレイしているような感覚になります。いや陣取りというか、http://agar.io/ (細胞増殖ゲーム?)に似てます。
各プレイヤーは丸を操り、小さな丸を食べて大きくなり、大きくなったプレイヤーは自分よりも小さい丸のプレイヤーを食べて、さらに丸を大きくなるゲームなんですが、中毒性があります。
まあそれだけでも面白いかもしれませんが、最高に面白いと思うのは主人公ピンチの時。どうやって乗り切るのか毎度ハラハラします。

弱肉強食agarのような円の動きをランダム実行してみます。
最初は200個の小さな円ですが、衝突する度に相手を取り込んで巨大化していく円のアニメーションです。


プログラムです。

takk@deb9:~$ cat -n maru.c
     1  #include <GL/glut.h>
     2  #include <math.h>
     3
     4  #define DEG2RAD(deg) deg * 2.0 * M_PI / 360.0
     5
     6  GLdouble colors[][4]={
     7  {0.0, 0.0, 0.0, 0.0},   //0 BLACK
     8  {0.6, 0.3, 0.2, 0.0},   //1 BROWN
     9  {1.0, 0.0, 0.0, 0.0},   //2 RED
    10  {1.0, 0.7, 0.3, 0.0},   //3 ORANGE
    11  {0.0, 1.0, 1.0, 0.0},   //4 YELLOW
    12  {0.0, 1.0, 0.0, 0.0},   //5 GREEN
    13  {0.0, 0.0, 1.0, 0.0},   //6 BLUE
    14  {1.0, 0.0, 1.0, 0.0},   //7 PURPLE
    15  {0.5, 0.5, 0.5, 0.0},   //8 GLAY
    16  {1.0, 1.0, 1.0, 0.0},   //9 WHITE
    17  };
    18
    19  #define BLACK 0
    20  #define BROWN 1
    21  #define RED 2
    22  #define ORANGE 3
    23  #define GREEN 2
    24  #define BLUE 3
    25  #define YELLOW 4
    26  #define WHITE 9
    27

白は背景に溶け込んでしまうので使いません、8色だけ使います。

    28  struct maru_struct
    29  {
    30      GLdouble rx;
    31      GLdouble ry;
    32      GLdouble rsize;
    33      int color;
    34      int dir;
    35      int status;
    36  };
    37
    38  #define MARU_NUM 200
    39  #define ALIVE 0
    40  #define MARGE 1
    41

メンバstatusは、動いている間はALIVEで、強者に取り込まれた後は、MARGEに変化します。
メンバrsizeが半径ですが、取り込んだ相手の半径の50%を加算するようにしています。

    42  struct maru_struct maru[MARU_NUM];
    43
    44  #define COLOR(n) colors[n][0],colors[n][1],colors[n][2],colors[n][3]
    45
    46  void maru_disp(GLdouble x, GLdouble y, GLdouble r, int color)
    47  {
    48      GLdouble xx, yy;
    49      int i,n=20;
    50      double deg,offs = 90.0;
    51
    52      glBegin(GL_POLYGON);
    53      glColor4f(COLOR(color));
    54
    55      for(i=0; i<n; i++){
    56          deg = (360.0/n) * i + offs;
    57          xx = x + r * cos(DEG2RAD(deg));
    58          yy = y + r * sin(DEG2RAD(deg));
    59          glVertex2d(xx, yy);
    60      }
    61
    62      glEnd();
    63  }
    64
    65  void callback_timer(int value)
    66  {
    67      glutPostRedisplay();
    68      glutTimerFunc(30,callback_timer,0);
    69  }
    70
    71  void maru_init()
    72  {
    73      struct maru_struct* p;
    74      int i;
    75      for(i=1;i<MARU_NUM;i++){
    76          p = &maru[i];
    77          if(p->status != MARGE){
    78              p->rx = ((rand()%200)-100)/100.0;
    79              p->ry = ((rand()%200)-100)/100.0;
    80              p->rsize = (rand()%50)/2000.0;
    81              p->dir = rand()%360;
    82              p->color = rand()%9;
    83              p->status = ALIVE;
    84          }
    85      }
    86  }
    87

初期化のときに、乱数で方向を決めます。以降は一切方向の変更はしません。

    88  void maru_collision_check(int my_index)
    89  {
    90      struct maru_struct *my;
    91      struct maru_struct *p;
    92      int i;
    93      my = &maru[my_index];
    94      for(i=1;i<MARU_NUM;i++){
    95          if(i!=my_index){
    96              p = &maru[i];
    97              if(p->status != MARGE)
    98                  if((my->rsize + p->rsize) >=
    99                  sqrt(pow(my->rx - p->rx,2)+pow(my->ry - p->ry,2))){
   100                      if(my->rsize > p->rsize){
   101                          my->rsize += p->rsize/2;
   102                          p->status = MARGE;
   103                      }else{
   104                          p->rsize += my->rsize/2;
   105                          my->status = MARGE;
   106                      }
   107                  }
   108          }
   109      }
   110  }
   111

99行目で衝突の判定をしています。100行目で両者の大きさを判定して、取り込み先を決めています。

   112  void maru_move()
   113  {
   114      struct maru_struct* p;
   115      int i;
   116
   117      for(i=1;i<MARU_NUM;i++){
   118          p = &maru[i];
   119          if(p->status != MARGE){
   120              p->rx += 0.005 * cos(p->dir*M_PI/180.0);
   121              if(p->rx < -1.0) p->rx = 1.0;
   122              if(p->rx >  1.0) p->rx = -1.0;
   123              p->ry += 0.005 * sin(p->dir*M_PI/180.0);
   124              if(p->ry < -1.0) p->ry = 1.0;
   125              if(p->ry >  1.0) p->ry = -1.0;
   126          }
   127      }
   128  }
   129

画面の端にきたら、反対方向から出現したように見せるため、座標を設定し直しています。

   130  void callback_display()
   131  {
   132      struct maru_struct* p;
   133      int i;
   134      glClearColor(COLOR(WHITE));
   135      glClear(GL_COLOR_BUFFER_BIT);
   136
   137      maru_move();
   138      for(i=1;i<MARU_NUM;i++){
   139          p = &maru[i];
   140          if(p->status != MARGE){
   141              maru_disp(p->rx, p->ry, p->rsize,p->color);
   142              maru_collision_check(i);
   143          }
   144      }
   145
   146      glFlush();
   147  }
   148

MARGE状態でなければ、円の表示をします。

   149  int main(int argc, char *argv[])
   150  {
   151      maru_init();
   152
   153      glutInit(&argc, argv);
   154      glutInitDisplayMode(GLUT_RGBA);
   155      glutCreateWindow("maru");
   156
   157      glutTimerFunc(30,callback_timer,0);
   158      glutDisplayFunc(callback_display);
   159
   160      glutMainLoop();
   161
   162      return 0;
   163  }
   164
takk@deb9:~$

main処理は、定型文です。

takk@deb9:~$ gcc -lglut -lGLU -lGL -lm maru.c
takk@deb9:~$ ./a.out

実行すると、毎回このように動きます。

コメント

タイトルとURLをコピーしました