スマートデバイスプログラミング 第3回
Unityで開発してみよう(その2、入力編)

GameCanvasについて

・GameCanvasの機能:今週使う変数、メソッド

 ・gc.DrawImage(GcImage img,int x,int y);
      座標x,yにimg番の画像を表示します。(詳細は次週)
 ・gc.GetPointerFrameCount(int ID);
      タッチされているフレーム数を返します。
      IDは基本0を入れてください。
      1以上を入れると複数タッチ時の情報を取る際使います)
 ・gc.GetPointerX(int ID);
      タッチされている場所のx座標を返します。(GetPointerYもあります)
 ・gc.SetColor(int r,int g,int b);
   図形、文字表示の描画色を指定します。
   引数はそれぞれ0から255の間で指定します。
      RGBの順で、赤緑青の各成分を指定します。
 ・gc.FillRect(int x,int y,int w,int h);
      塗り潰し四角形を描きます。
     (他にも図形描画命令はあるので興味のある方は調べてみましょう)
 ・gc.CheckHitRect(x1,y1,w1,h1,x2,y2,w2,h2);
   物体1と物体2が接触しているかを判定するメソッドです。
   (仲は簡単なif文でできているので、自作してみても良いでしょう)

課題

(1)画面内にボールを出して、画面内を上下左右に弾むようにしてみよう。
(2)(↑がうまくいったら)画面の下の方に、ラケットを作って、タップで左右に動かせるようにしてみましょう。
(3)(↑がうまくいったら)ラケットとボールが接触したらボールが跳ね返るようにしてみましょう。また、画面下でボールを受け損なったらボールが無くなるようにしてみましょう。
(4)(↑がうまくいったら)画面内にブロックを表示させて、ブロック崩しにしてみましょう。ラケットが上下にも動くようにして、ブロックを全部崩すのにかかったフレーム数を競えるようにしましょう。
(5)(余力があったら)ゲームをより改良するか、学んだ技術を使って何か面白いものを作ってみましょう。
作ったC#のプログラムを添付してください。
(プロジェクトを保存したフォルダのasset以下にあると思います。
コメント欄には、どこまでやったかと、(4)までできた人はクリアタイムを書いてください。

課題の進め方

  1. GameCanvasをDLして解凍。
    https://github.com/sfc-sdp/GameCanvas-Unity/
  2. フォルダ名をk03に改名
  3. ドキュメント/Unity(Windowsの場合)以下に配置
  4. Unityを起動
  5. 「Open」を押してk03を選択し「フォルダを選択」を押す
  6. (Unityのバージョンによりアラートが出るが、出た場合は「Continue」を押す)
  7. →Unityが起動され、プロジェクトk03が開かれる。
  8. Project窓でSceneフォルダをダブルクリックして開き、中に入っているGame(.scene)をダブルクリックで開く。
  9. →シーンが開かれる(Hierarchy窓の表示が変わる)
  10. Project窓(の左側)でScriptsフォルダをクリックして開き、中に入っているGame(.cs)をダブルクリックで開く。
  11. →エディタが起動し、ソースを編集できるようになる
  12. プログラムを編集して保存
  13. Unityに戻って、再生ボタンを押して実行
  14. →プログラムに文法的な問題がなければ、プログラム通りに動くはず
  15. うまく動かない場合は、エディター上でbuildしてみましょう。(上のメニューからBuild→BuildAll)

課題1をやってみましょう

  1. プログラムを編集

    9行目(クラスに入った直後)に変数の宣言を記載

    
    int ball_x;
    int ball_y;
    int ball_speed_x;
    int ball_speed_y;
    

    19行目(InitGame()の中)に初期化処理を記載

    
    gc.SetResolution(640, 480);
    ball_x = 0;
    ball_y = 0;
    ball_speed_x = 3;
    ball_speed_y = 3;
    

    32行目(UpdateGame()の中)に下記を記載

    
    ball_x = ball_x + ball_speed_x;
    ball_y = ball_y + ball_speed_y;
    
    if( ball_x < 0 ) {
      ball_x = 0;
      ball_speed_x = -ball_speed_x;
    }
    
    if( ball_y < 0 ) {
      ball_y = 0;
      ball_speed_y = -ball_speed_y;
    }
    
    if( ball_x > 616 ) {
      ball_x = 616;
      ball_speed_x = -ball_speed_x;
    }
    
    if( ball_y > 456 ) {
      ball_y = 456;
      ball_speed_y = -ball_speed_y;
    }
    

    Draw()の中(ClearScreenの後)に下記を記載

    
            // 画面を白で塗りつぶします
            gc.ClearScreen();
    
            // 0番の画像を描画します
            gc.DrawImage(GcImage.BlueSky, 0, 0);
    
    	gc.DrawImage(GcImage.BallYellow,ball_x,ball_y);
    
  2. セーブして実行してみましょう。

課題2のヒント

//変数の宣言に下記を追加
int player_x;
int player_y;
int player_w;
int player_h;

//初期化処理に下記を追加
player_x = 270;
player_y = 460;
player_w = 100;
player_h = 20;

//updateの中に処理を追加
if(gc.GetPointerFrameCount(0) > 0 ){
  player_x = (int)gc.GetPointerX(0) - player_w/2;
}

//drawの中に処理を追加
gc.SetColor(0, 0, 255);
gc.FillRect(player_x,player_y,player_w,player_h);

課題3のヒント

跳ね返りの条件を考えてみましょう。
ballの幅、高さは24ドットです。
跳ね返りの判定はgc.CheckHitRectを使うと簡単です。
if( gc.CheckHitRect(ボールのx,y,w,h,ラケットのx,y,w,h) )
という感じで書きます。
また、画面下の跳ね返り判定をなくすと、ミスした時にボールが無くなるようになります。
if(ボールとラケットが接触していたら){
  if(ボールが下向に動いていたら){
    ボールの動く向きを反転;
  }
}

課題4のヒント

配列とfor文を駆使して、ブロックを沢山作ってみましょう。 また、ブロックの数を数えるメソッドcountBlock()を作ってみましょう。
//宣言
const int BLOCK_NUM = 50;
int[] block_x = new int [BLOCK_NUM];
int[] block_y = new int [BLOCK_NUM];
bool[] block_alive_flag = new bool [BLOCK_NUM];
int block_w = 64;
int block_h = 20;
int time ;

//初期化
for(int i =0 ; i < BLOCK_NUM ; i ++ )
{
  block_x[i] = (i % 10 ) * block_w;
  block_y[i] = (i / 10 ) * block_h;
  block_alive_flag[i] = true;
}
time = 0;


//処理
ブロックが全部消えていなければ(ブロックの数が0でなければ)timeを1増やす
for文で繰り返してボールとブロックの接触判定
接触していたらblock_alive_flagをfalseに。

バーが上下にも動くよう移動処理を追加
player_y = (int)gc.GetPointerY(0) - player_h/2;

//表示
for文で繰り返して各ブロックを表示
block_alive_flagがtrueの時のみ表示する

timeを表示
ブロックが全部消えていたらClear!と表示


//Drawの後、クラスを閉じる括弧の前にメソッドを追加
int countBlock(){
  int num = 0;
  for(int i =0 ; i < BLOCK_NUM ; i ++ ){
    if(block_alive_flag[i]){
      num++;
    }
  }
  return num;
}

補足

余力がある人の改良の案

・タップした座標が取れるようになったり、フレームごとに座標を変えることで物を動かしたりなど、できることが増えていますので、それらを駆使してみたり、何らか遊びやすくする改善を施してみるなどすると良いのではないかと思います。

Tips

エディタについて

 標準でついてくるエディタMonoDevelopは日本語入力に不備が多々あります。
 入力せずに済ませるのが現実的ですが、どうしても日本語を入力したい場合は
 他のエディタで打ち込んでコピー&ペーストで持ってくるのが確実です。

 日本語対応するプログラムなども配布されているので、
 検索して試してみると良いかもしれません。
 容量は食いますがVisualStudioCommunityを導入するのもオススメです。
(madの人向けの導入gifアニメがgameCanvasページのdoc以下に追加されています)

よくある質問

スクリプトを更新したのに、何も変わらない

プログラムが間違っていてビルドエラーが出ている可能性が高いです。

エディタ上でbuildしてみましょう。

{}の対応が間違っていないか、全角スペースが入っているかなど、確認してみましょう

「gc.GetPointerXが無い」というエラーが出る

最新のgameCanvasを使用してみてください

諸事情によりgameCanvasを更新できない場合は下記のように書き換えて試してみてください。

player_x = (int)gc.LastPointerX - player_w/2;
player_y = (int)gc.LastPointerY - player_h/2;

「gc.CheckHitRectが無い」というエラーが出る

最新のgameCanvasを使用してみてください

諸事情によりgameCanvasを更新できない場合は下記の対応を試してみてください。


//メソッドを一つ追加(drawGameの後、クラスが終わる括弧の前に)

bool CheckHitRect(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2)
{
    if (x1 - x2 >= -w1 && x1 - x2 <=  w2) {
        if (y1 - y2 >= -h1 && y1 - y2 <=  h2) {
            return true;
        }
    }
    return false;
}

//接触判定gc.CheckHitRectの「gc.」を外す

if(CheckHitRect(ball_x,ball_y,24,24,player_x,player_y,player_w,player_h){

if(CheckHitRect(ball_x,ball_y,24,24,blok_x[i],block_y[i],block_w,block_h){

//など