第12講 ポインタを利用してテトリスからグローバル変数とグローバル配列を追放する!

第3話 グローバル変数mをローカル変数に変更
前話課題コード例(全文掲載)
#include "DxLib.h"
#include<stdlib.h> //乱数を発生させるrand()を使うために必要
#include<time.h> //time()使用するために必要
void hajimennogamen(
char *m); //ゲームの最初の画面を担当する社員
void gamemain(char *x,char *y
,char *m); //ゲーム画面を担当する社員
void make(char *m); //ゲームオーバー画面を担当する社員
void kachi(char *m); //ゲームクリア画面を担当する社員
//使用されていなかったint h ; //画像に付けられた出席番号を収納する箱を用意をカット
int siro = GetColor(255, 255, 255); //白色の出席番号収納する箱
int kiiro = GetColor(255, 255, 0); //黄色の出席番号収納する箱
int aka = GetColor(255, 0, 0); //赤色の出席番号を収納する箱int siro; //白色の出席番号収納する箱
int midori = GetColor(0, 255, 0); //白色の出席番号収納する箱
int ao = GetColor(0, 0, 255); //青色の出席番号収納する箱
int mizuiro = GetColor(0, 255, 255); //水色の出席番号を収納する箱int siro; //白色の出席番号収納する箱
int murasaki = GetColor(255, 0, 255); //紫色の出席番号収納する箱
char taisyoutonarukey_mae[100];
char jyu(int taisyoutonarukey);
void chizusakusei(); //地図作成(壁建設)社員
char chizu[12][27]; //配置担当
char chizumodosu[12][27]; //地図配置を元に戻す1:可0:不可
void block(char *x,char *y
,char *m); //ブロックを描く社員
char p[4][4][4] = { //x座標に加える数
-1,0,1,2,
0,0,0,0,
-2,-1,0,1,
0,0,0,0,

0,1,1,0,
0,1,1,0,
-1,0,0,-1,
-1,0,0,-1,

0,0,0,1,
0,0,1,2,
-1,0,0,0,
-2,-1,0,0,

0,0,1,1,
-1,0,0,1,
-1,-1,0,0,
-1,0,0,1
};
char q[4][4][4] = { //y座標に加える数
0,0,0,0,
-1,0,1,2,
0,0,0,0,
-2,-1,0,1,

-1,-1,0,0,
0,0,1,1,
0,0,1,1,
-1,-1,0,0,

-2,-1,0,0,
1,0,0,0,
0,0,1,2,
0,0,0,-1,

-1,0,0,1,
1,1,0,0,
-1,0,0,1,
0,0,-1,-1
};
//sはブロックの種類 0:長方形1:正方形2:L字3:N字
//kは回転状態0:元の状態1:90°回転2:180°回転3:270°回転
char s[2],k[2]; //[0]は現在 [1]は未来
char t; //0:作動1:作動停止
int hajimenojikoku; //始めの時刻を入れる整数型の箱
void syokika(); //初期化を行う社員
void kesu(char *m); //そろった正方形を消す社員
int tokuten; //ゲームスコアをカウントする変数
int iro[2]; //ブロックの色をランダムに選ぶ [0]は現在 [1]は未来
int irosentaku[7]={kiiro,mizuiro,aka,ao,siro,midori,murasaki};
void yokoku(); //次のブロックを予告する社員
int hayasa; //ブロックの速さ
char nanido; //ゲーム級 0:初級 1:中級 2:上級
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
  ChangeWindowMode(true); //ウィンドウモードの切り替え
  SetGraphMode(440, 540, 32); //ウインドウサイズの変更
  if (DxLib_Init() == -1)return(-1);  // DXライブラリ初期化処理
  SetDrawScreen(DX_SCREEN_BACK); //裏画像を描画対象に加える(2017/06/28追加) 画面のちらつき対策
  //整数型の箱*xと*yを用意とそれぞれの箱の住所を入れる箱xとyを用意
  char *x=(char *)malloc(sizeof(char)),*y=(char *)malloc(sizeof(char));
  
//*m ゲームモードを決定する整数 0:ゲームの最初の画面 1:ゲーム画面 2:ゲームオーバー 3:ゲームクリア画面
  char *m=(char *)malloc(sizeof(char));

  *m=0;
  t=0; //0:作動1:作動停止
  syokika(); //ゲームの最初の設定を行う

  while (ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0) {
    if(*m==0)hajimennogamen(
m);
    if(*m==1)gamemain(x,y
,m);
    if(*m==2)make(
m);
    if(*m==3)kachi(
m);
  }

  WaitKey();   // キー入力待ち
  DxLib_End();  // DXライブラリ終了処理
  return 0;
}
void syokika(){ //ゲームの最初の設定を行う
  srand((unsigned) time(NULL)); //乱数の系列決定するシード値を現在時刻から取得
  hajimenojikoku=GetNowCount(); //ゲーム起動時の時刻をゲット
  int i,j;
  for(i=0;i<12;i++){
    for(j=0;j<27;j++){
      chizumodosu[i][j]=1;
    }
  }
  tokuten=0;
}
void kesu(char *m){
  char h=1,i,j,ik=25;
  for(i=25;i>21;i--){
    for(j=1;j<11;j++){
      if(chizumodosu[j][i]==1){ //地図配置を元に戻す1:可0:不可
        ik=i;
        h=0;
        break;
      }
    }
    if(h==0){
      break;
    }
  }
  if(ik<25){
    for(i=ik;i>0;i--){
      for(j=1;j<11;j++){
        if(chizumodosu[j][i]==0){
          chizu[j][i+(25-ik)]=chizu[j][i];
        }
        chizumodosu[j][i+(25-ik)]=chizumodosu[j][i];
      }
    }
    tokuten+=(25-ik)*100;
    if(tokuten>=2000)
*m=3;
  }
}
void block(char *x,char *y
,char *m){
  int i,j,l;
  *y=(GetNowCount()-hajimenojikoku)/hayasa;
  char h;
  if (jyu(KEY_INPUT_LEFT) != 0){
    h=1;
    for(i=0;i<4;i++){
      if(chizumodosu[*x+p[s[0]][k[0]][i]-1][*y+q[s[0]][k[0]][i]]==0){
        h=0;
        break;
      }
    }
    if(h==1)(*x)--;
  }
  
  if (jyu(KEY_INPUT_RIGHT) != 0){
    h=1;
    for(i=0;i<4;i++){
      if(chizumodosu[*x+p[s[0]][k[0]][i]+1][*y+q[s[0]][k[0]][i]]==0){
        h=0;
        break;
      }
    }
    if(h==1)(*x)++;
  }

  if(jyu(KEY_INPUT_SPACE) != 0){
    h=1;
    for(i=0;i<4;i++){
      if(chizumodosu[*x+p[s[0]][(k[0]+1)%4][i]][*y+q[s[0]][(k[0]+1)%4][i]]==0){
        h=0;
        break;
      }
    }
    if(h==1)k[0]=(k[0]+1)%4;
  }
  
  for(i=0;i<4;i++){
    //sはブロックの種類 0:長方形1:正方形2:L字3:N字
    //kは回転状態0:元の状態1:90°回転2:180°回転3:270°回転
    if(chizumodosu[*x+p[s[0]][k[0]][i]][*y+q[s[0]][k[0]][i]]==1){
      if(*y+q[s[0]][k[0]][i]>0)chizu[*x+p[s[0]][k[0]][i]][*y+q[s[0]][k[0]][i]] = 2 + 7 * s[0] + iro[0];
    }
  }
  char a;
  for(a = 0;a < 4;a++){
    for(i = 0;i < 7;i++){
      for (j = 0; j < 12; j++) {
        for (l = 0; l < 27; l++) {
          if (chizu[j][l] == 2 + 7 * a + i) {
            DrawBox(20 * j, 20 * l, 20 * (j + 1) - 1, 20 * (l + 1) - 1, irosentaku[i], true);
          }
        }
      }
    }
  }

  h=1;
  for(i=0;i<4;i++){
    if(*y+q[s[0]][k[0]][i]+1>0 && chizumodosu[*x+p[s[0]][k[0]][i]][*y+q[s[0]][k[0]][i]+1]==0){
      h=0;
      break;
    }
  }
  if(h==0){
    for(i=0;i<4;i++)chizumodosu[*x+p[s[0]][k[0]][i]][*y+q[s[0]][k[0]][i]]=0;
    kesu(
m);
    t=0; //0:作動1:作動停止
    hajimenojikoku=GetNowCount();
  }
  for(i=0;i<4;i++){
    if(*y+q[s[0]][k[0]][i]==1 && chizumodosu[*x+p[s[0]][k[0]][i]][*y+q[s[0]][k[0]][i]]==0){
      
*m=2;
    }
  }
}
void chizusakusei() {
  int gazou = LoadGraph("画像/kb00.png");
  int i,j;
  for (i = 0; i < 12; i++) {
    for (j = 0; j < 27; j++) {
      if(chizumodosu[i][j]==1){
        chizu[i][j] = 0;
      }
    }
  }
  for (j = 0; j < 27; j++) {
    chizu[0][j] = 1;
    chizu[11][j] = 1;
    chizumodosu[0][j] = 0;
    chizumodosu[11][j] = 0;
  }
  for (i = 1; i < 11; i++) {
    chizu[i][0] = 1;
    chizu[i][26] = 1;
    chizumodosu[i][0] = 0;
    chizumodosu[i][26] = 0;
  }
  for (i = 0; i < 12; i++) {
    for (j = 0; j < 27; j++) {
      if (chizu[i][j] == 1) {
        DrawGraph(20 * i, 20 * j, gazou, true);
      }
    }
  }
  DeleteGraph(gazou);
}
void hajimennogamen(char *m){ //ゲームの最初の画面
  int gazou;
  gazou = LoadGraph("画像/05.png");
  DrawGraph(0, 0, gazou, true);
  if (jyu(KEY_INPUT_Q) != 0) {
    nanido=0; //ゲーム級 0:初級 1:中級 2:上級
    
*m=1;
    hayasa=500;
    char a;
    a=rand() % 8;
    if(a<3)s[0]=0;
    if(a>=3 && a<6)s[0]=1;
    if(a==6)s[0]=2;
    if(a==7)s[0]=3;
    k[0]=rand()%4; //kは回転状態0:元の状態1:90°回転2:180°回転3:270°回転
    iro[0]=rand()% 7; //ブロックの色をランダムに設定
    syokika();
  }
  if (jyu(KEY_INPUT_A) != 0) {
    nanido=1; //ゲーム級 0:初級 1:中級 2:上級
    *m=1;
    hayasa=200;
    s[0]=rand()%4; //sはブロックの種類 [0]:現在 [1]:未来
    k[0]=rand()%4; //kは回転状態0:元の状態1:90°回転2:180°回転3:270°回転 [0]:現在 [1]:未来
    iro[0]=rand()% 7; //ブロックの色をランダムに設定 [0]:現在 [1]:未来
    syokika();
  }
  if (jyu(KEY_INPUT_Z) != 0) {
    nanido=2; //ゲーム級 0:初級 1:中級 2:上級
    *m=1;
    hayasa=100;
    char a;
    a=rand() % 6;
    if(a==0)s[0]=0;
    if(a==1)s[0]=1;
    if(a==2 || a==3)s[0]=2;
    if(a==4 || a==5)s[0]=3;
    k[0]=rand()%4; //kは回転状態0:元の状態1:90°回転2:180°回転3:270°回転 [0]:現在 [1]:未来
    iro[0]=rand()% 7; //ブロックの色をランダムに設定 [0]:現在 [1]:未来
    syokika();
  }
}
void gamemain(char *x,char *y
,char *m){ //ゲーム画面
  chizusakusei(); //地図作成(壁建設)
  yokoku();
  if(t==0){
    char a;
    if(nanido==0){
      a=rand() % 6;
      if(a<2)s[1]=0;
      if(a>=2 && a<4)s[1]=1;
      if(a==4)s[1]=2;
      if(a==5)s[1]=3;
    }
    if(nanido==1){
      s[1]=rand()%4;
    }
    if(nanido==2){
      a=rand() % 6;
      if(a==0)s[1]=0;
      if(a==1)s[1]=1;
      if(a==2 || a==3)s[1]=2;
      if(a==4 || a==5)s[1]=3;
    }
    k[1]=rand()%4; //kは回転状態0:元の状態1:90°回転2:180°回転3:270°回転
    iro[1]=rand()% 7; //ブロックの色をランダムに設定
    if(s[0]==0){
      if(k[0]==0)*x=1+rand()%8;
      if(k[0]==1)*x=1+rand()%10;
      if(k[0]==2)*x=2+rand()%8;
      if(k[0]==3)*x=1+rand()%10;
    }
    
    if(s[0]==1){
      if(k[0]==0)*x=1+rand()%9;
      if(k[0]==1)*x=1+rand()%9;
      if(k[0]==2)*x=2+rand()%9;
      if(k[0]==3)*x=2+rand()%9;
    }

    if(s[0]==2){
      if(k[0]==0)*x=1+rand()%9;
      if(k[0]==1)*x=1+rand()%8;
      if(k[0]==2)*x=2+rand()%9;
      if(k[0]==3)*x=3+rand()%8;
    }

    if(s[0]==3){
      if(k[0]==0)*x=1+rand()%9;
      if(k[0]==1)*x=2+rand()%8;
      if(k[0]==2)*x=2+rand()%9;
      if(k[0]==3)*x=2+rand()%8;
    }
    t=1;
  }
  block(x,y
,m); //ブロックを描く
  if(t==0){
    s[0]=s[1];
    k[0]=k[1];
    iro[0]=iro[1];
  }
  SetFontSize(28); //フォントサイズを32に変更
  DrawFormatString(240, 300, siro, "ゲームスコア:"); // 文字を描画する
  DrawFormatString(300, 350, siro, "%d",tokuten); // 文字を描画する
}
void yokoku(){
  int i;
  for (i = 0; i < 4; i++) {
    DrawBox(300 + 20 * p[s[1]][k[1]][i], 60 + 20 * q[s[1]][k[1]][i], 299 + 20 * (p[s[1]][k[1]][i] + 1), 59 + 20 * (q[s[1]][k[1]][i] + 1), irosentaku[iro[1]], true);
  }
}
void make(
char *m){ //ゲームオーバー画面
  int gazou;
  gazou = LoadGraph("画像/03.png");
  DrawGraph(0, 0, gazou, true);
  if (jyu(KEY_INPUT_RETURN) != 0) {
    *m=1;
    syokika();
  }
  if (jyu(KEY_INPUT_Z) != 0) m=0;
}
void kachi(
char *m){ //ゲームクリア画面
  int gazou;
  gazou = LoadGraph("画像/04.png");
  DrawGraph(0, 0, gazou, true);
  if (jyu(KEY_INPUT_RETURN) != 0) {
    *m=1;
    syokika();
  }
  if (jyu(KEY_INPUT_Z) != 0) m=0;
}
char jyu(int taisyoutonarukey) { //taisyoutonarukey(たいしょうとなるキー)は、今対象としているキーの出席番号
  if (CheckHitKey(taisyoutonarukey) != 0) {
    if (taisyoutonarukey_mae[taisyoutonarukey] == 0) {
      taisyoutonarukey_mae[taisyoutonarukey] = 1;
      return 1;
    }
  }
  else {
    taisyoutonarukey_mae[taisyoutonarukey] = 0;
    return 0;
  }
  return 0;
}
コピペ用添付ファイル

このプログラムでは
int h ; //画像に付けられた出席番号を収納する箱を用意
はどこにも使われていませんでしたので、
今回からカットしました。

上から順番にグローバル変数を追放していますが、
int siro = GetColor(255, 255, 255); //白色の出席番号収納する箱
int kiiro = GetColor(255, 255, 0); //黄色の出席番号収納する箱
int aka = GetColor(255, 0, 0); //赤色の出席番号を収納する箱int siro; //白色の出席番号収納する箱
int midori = GetColor(0, 255, 0); //白色の出席番号収納する箱
int ao = GetColor(0, 0, 255); //青色の出席番号収納する箱
int mizuiro = GetColor(0, 255, 255); //水色の出席番号を収納する箱int siro; //白色の出席番号収納する箱
int murasaki = GetColor(255, 0, 255); //紫色の出席番号収納する箱
は追放すると、引数が多くなりすぎますので、
これはこのままにしておくことにします。
もちろん皆さんは、練習のために追放されても結構です。
最後には、添付ファイルを使って元に戻すので、
冒険をなさっても大丈夫です。
このサイトの該当ページを開いて、
添付ファイルからコピペすれば元に戻るからです。
冒険という言葉を使いましたが、
ビルドの成功したプログラムをいじる行為は、
いつでも冒険です。
なぜなら、変更したことによってビルドエラーをしてしまう可能性があるからです。
そして、原因が解明できずに、いらいらさせられることが多いからです。

バックアップは、フォルダごとのコピペでできますが、
ゲームの場合かなりハードディスクの容量を食いますので、
バックアップを取り過ぎるとハードディスクの空き容量を圧迫することになります。
ビルドエラーして、どうしてもエラー原因がわからないときは、
慌てずに『Ctrl+Z』をやって下さい。
これは1つ手前に戻るです。
『Ctrl+Z』をやってはデバッグを続ければ、
成功した時点まで必ず戻ることができます。
ですから、こまめにデバッグをするようにしましょう。

本講義では、練習のためにファイルの分割は行いますが、
ゲーム開発はファイル1本で進める予定です。
理由は、1回のコピペでエラーしてしまったプログラムを復旧することができるからです。
姉妹編では、ファイルを20本程度に分割してしまったために、
フォルダごとのコピペをしないと、
1回のコピペでは復旧ができなくなってしまったことを反省したからです。

ファイル1本の場合は、フォルダごとコピペしなくても、
main.cppのコードをメモ帳にコピペしてファイル名を付けて保存しておけば、
いつでも復旧はコピペで簡単にできます。
ですから、ご自分でいろいろプログラムを開発していく際には、
メモ帳を利用してバックアップをして下さい。
保存する際には、ファイル名
.cppとしておくとよいでしょう。
.cppを付けずに普通に保存しておいてもコピペはできますが、
念のためにです。
前に一度説明していますが.以降を拡張子と言います。
拡張子は、ファイルの種類を示し、
cppの場合にはC++のファイルであることを示します。
基本的にはC言語の機能しか使っていませんので、
ファイル名
.cppでも大丈夫です。
これはファイルがC言語のファイルであることを示しています。

では、次はグローバル配列
char s[2],k[2]; //[0]は現在 [1]は未来
ローカル配列に変更しましょう。
今回は、第11講第8話の
#include<stdio.h>
#include<stdlib.h>
void f(int
x[]); //データを作成する社員
void g(int x[]); //データを表示する社員
int main(){
  int x[5];
  f(
x);
  g(
x);
  return(0);
}
void f(int x[]){
  int i;
  for(i=0;i<5;i++)x[i]=rand()%10;
}
void g(int x[]){
  int i;
  for(i=0;i<5;i++)printf("%d ",x[i]);
  printf("\n");
}
コピペ用添付ファイル
を参考にして下さい。配列を引数にできるのでしたね。


第2話へ   第4話へ

第2部目次に戻る  第1部目次に戻る

初心者のための excel 2016 マクロ VBA 入門講義 基礎から応用まで
vc++ c言語 c++ 入門 初心者 基礎から応用まで
ecliqse c++ 入門
魔方陣 数独で学ぶ VBA 入門

数独のシンプルな解き方・簡単な解法の研究
VB講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座
初心者のための世界で一番わかりやすいVisual Basic入門基礎講座
初心者のための世界で一番わかりやすいVBA入門講義(基礎から応用まで)
初心者のための VC++による C言語 C++ 入門 基礎から応用まで第1部
ecliqse java 入門
java 入門 サイト 基礎から応用まで
本サイトトップへ