20210301のUnityに関する記事は7件です。

Debug.Log()に複数の変数を入れる

 前置き

すごい技とかじゃないけど、上の方にこの記事がなかったから自分が普段やってるのを書く。
もしかしたらこの書き方がダメな理由があるか、もっと良い方法があるのかも。

 本題

$""の中の{}に変数を書いて使う。
やり方はこう↓

a
int price = 200;
int num = 3;
string name = "太郎";
int money = 1000;

Debug.Log($"{price}円のリンゴを{num}個買ったら、{name}の所持金は{money - price * num}円だよ。");
//=> 200円のリンゴを3個買ったら、太郎の所持金は400円だよ。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

スマートデバイスプログラミング④(上からランダムに降ってくるボールを拾ってスコアを競うゲーム)

UnityとGameCanvasのダウンロード

1:UnityHubをDLしてインストール。(一部のwindows環境では拡張子を.exeに変更する必要があります)

2:UnityHubを起動して、Unity2019.3.11f1をインストール。

3:GameCanvasのダウンロード
https://github.com/sfc-sdp/GameCanvas-Unity/
でGameCanvasをダウンロード

4:追加画像をダウンロードしてAssetsに追加
http://web.sfc.keio.ac.jp/~wadari/sdp/k04_res.zip

Game.csの編集

Assets内のGame.csを別エディタで編集

using Sequence = System.Collections.IEnumerator;

/// <summary>
/// ゲームクラス。
/// </summary>
public sealed class Game : GameBase
{
  const int BALL_NUM = 30;
  int[] ball_x = new int [BALL_NUM];
  int[] ball_y = new int [BALL_NUM];
  int[] ball_col = new int [BALL_NUM];
  int[] ball_speed = new int [BALL_NUM];
  int ball_w = 24;
  int ball_h = 24;
  int player_x = 304;
  int player_y = 448;
  int player_speed = 3;
  int player_w = 32;
  int player_h = 32;
  int player_img = 4;
  int score = 0;
  int time = 1800;
  int player_col = 4;
  int combo = 0;

    /// <summary>
    /// 初期化処理
    /// </summary>
    public override void InitGame()
    {
      gc.SetResolution(640, 480);
      for(int i =0 ; i < BALL_NUM ; i ++ )
      {
        resetBall(i);
      }
    }

    /// <summary>
    /// 動きなどの更新処理
    /// </summary>
    public override void UpdateGame()
    {
      time = time - 1;

      for(int i =0 ; i < BALL_NUM ; i ++ )
      {
        ball_y[i] = ball_y[i] + ball_speed[i];
        if(ball_y[i]> 480){
          resetBall(i);
        }

        if(gc.CheckHitRect(ball_x[i],ball_y[i],ball_w,ball_h,player_x,player_y,player_w,player_h)){
          if(time>=0){
            score=score+ball_col[i];//ballごとに違った点数を加える
            if(player_col == ball_col[i]) {
              combo++;
              score+= combo;
            }
            else {
              combo = 0;
            }

            player_col = ball_col[i];
          }
          resetBall(i);
        }

      }
      if(gc.GetPointerFrameCount(0) > 0 ){
        if(gc.GetPointerX(0) > 320) {
          player_x += player_speed;
          player_img = 4;
        }
        else {
          player_x -= player_speed;
          player_img = 5;
        }
      }
      if(player_x<=0){
        player_x=0;
      }
      if(player_x>=616){
        player_x=616;
      }


    }

    /// <summary>
    /// 描画の処理
    /// </summary>
    public override void DrawGame()
    {
      gc.ClearScreen();

      for(int i =0 ; i < BALL_NUM ; i ++ ){
        gc.DrawImage(ball_col[i],ball_x[i],ball_y[i]);
      }

      gc.SetColor(0,0,0);
      if(time>=0){
        gc.DrawString("time:"+time,0,0);
      }
      else {
        gc.DrawString("finished!!",0,0);
      }
      gc.DrawString("score:"+score,0,24);
      gc.DrawString("combo:"+combo,0,48);

      //gc.DrawClipImage(player_img,player_x,player_y,0,64,32,32);
      if(time>=0){
        int u = 32+ ((time%60)/30)*32;//0.5秒ごとにユーザー画像を切り替えx
        int v = (player_col - 1) *32;//最初4 色に応じてユーザーの色の画像を指定y
        gc.DrawClipImage(player_img,player_x,player_y, u,v,32,32);
      }
      else {
        gc.DrawClipImage(player_img,player_x,player_y, 96,(player_col - 1) *32,32,32);
      }


    }
    void resetBall(int id){
      ball_x[id] = gc.Random(0,616);
      ball_y[id] = -gc.Random(24,480);
      ball_speed[id] = gc.Random(3,6);
      ball_col[id] = gc.Random(1,3);
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

スマートデバイスプログラミング③(制限時間内にボールを反射させてブロックを壊すゲーム)

UnityとGameCanvasのダウンロード

1:UnityHubをDLしてインストール。(一部のwindows環境では拡張子を.exeに変更する必要があります)

2:UnityHubを起動して、Unity2019.3.11f1をインストール。

3:GameCanvasのダウンロード
https://github.com/sfc-sdp/GameCanvas-Unity/
でGameCanvasをダウンロード

Game.csの編集

Assets内のGame.csを別エディタで編集

using Sequence = System.Collections.IEnumerator;

/// <summary>
/// ゲームクラス。
/// </summary>
public sealed class Game : GameBase
{
  int ball_x;
  int ball_y;
  int ball_speed_x;
  int ball_speed_y;
  int player_x;
  int player_y;
  int player_w;
  int player_h;

  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 ;

    /// <summary>
    /// 初期化処理
    /// </summary>
    public override void InitGame()
    {
      gc.SetResolution(640, 480);
      ball_x = 0;
      ball_y = 0;
      ball_speed_x = 3;
      ball_speed_y = 3;
      player_x = 270;
      player_y = 460;
      player_w = 100;
      player_h = 20;

      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;
    }

    /// <summary>
    /// 動きなどの更新処理
    /// </summary>
    public override void UpdateGame()
    {
      if(countBlock()!=0){
        time++;
      }
      player_y = gc.GetPointerY(0) - player_h/2;

      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;
      }
      if(gc.GetPointerFrameCount(0) > 0 ){
        player_x = gc.GetPointerX(0) - player_w/2;
      }

      if(gc.CheckHitRect(ball_x,ball_y,24,24,player_x,player_y,player_w,player_h)){
        if(ball_speed_y>0){
          ball_speed_y = -ball_speed_y;
        }
      }

      for(int i = 0; i<BLOCK_NUM; i++){
        if(gc.CheckHitRect(ball_x,ball_y,24,24,block_x[i],block_y[i],block_w,block_h)){
          block_alive_flag[i]=false;
        }
      }
    }

    /// <summary>
    /// 描画の処理
    /// </summary>
    public override void DrawGame()
    {
      // 画面を白で塗りつぶします
        gc.ClearScreen();

        // 0番の画像を描画します
        gc.DrawImage(0, 0, 0);

          gc.DrawImage(1,ball_x,ball_y);

        gc.SetColor(0, 0, 255);
        gc.FillRect(player_x,player_y,player_w,player_h);



        for(int i = 0; i<BLOCK_NUM; i++){
          if(block_alive_flag[i]){
            gc.FillRect(block_x[i],block_y[i],block_w,block_h);
          }
        }


        gc.DrawString("time:"+time,60,0);
        if(countBlock()==0){
          gc.DrawString("clear",60,30);
        }


    }
    int countBlock(){
      int num = 0;
      for(int i =0 ; i < BLOCK_NUM ; i ++ ){
        if(block_alive_flag[i]){
          num++;
        }
      }
      return num;
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

スマートデバイスプログラミング②(ランダムにカードを出して得点を競うゲーム)

UnityとGameCanvasのダウンロード

1:UnityHubをDLしてインストール。(一部のwindows環境では拡張子を.exeに変更する必要があります)

2:UnityHubを起動して、Unity2019.3.11f1をインストール。

3:GameCanvasのダウンロード
https://github.com/sfc-sdp/GameCanvas-Unity/
でGameCanvasをダウンロード

Game.csの編集

Assets内のGame.csを別エディタで編集

using Sequence = System.Collections.IEnumerator;

/// <summary>
/// ゲームクラス。
/// </summary>
public sealed class Game : GameBase
{
    // 変数の宣言
    int money;
    const int CARD_TYPE = 10;
    int[] card_count = new int [CARD_TYPE];
    string[] card_name =
         {"A","B","C","D","E","F","G","H","I","J"};
    bool isComplete;
    int new_card ;

    /// <summary>
    /// 初期化処理
    /// </summary>

    ///起動時に一回だけ呼ばれる・・初期化設定用
    public override void InitGame()
    {
      resetValue();
    }

    /// <summary>
    /// 動きなどの更新処理
    /// </summary>

    //1フレームごとに呼ばれる・動きの処理を入れる
    public override void UpdateGame()
    {
        //タップした時の処理
        if (gc.GetPointerFrameCount(0)==1 && ! isComplete) {
             money -= 100;
             if (gc.Random(0,3)==0){
             new_card = gc.Random(0,4);
           }else{
             new_card = gc.Random(5,9);
           }
             card_count[new_card]++;


           for (int i = 0; i < 5; i++) {
             if (card_count[i] > 4){
               isComplete = true;
             }
           }
        }
        //長押しした時の処理
        if(gc.GetPointerFrameCount(0) >= 120){
          resetValue();
        }
    }

    /// <summary>
    /// 描画の処理
    /// </summary>

    //1フレームごとに呼ばれる・描画の処理
    public override void DrawGame()
    {
      gc.ClearScreen();
      gc.SetColor(255,0,0);
      gc.SetFontSize(36);
      gc.DrawString("money:"+money,60, 40);

      if(new_card >= 0){
        gc.DrawString("new:"+card_name[new_card],60, 80);
      }

      for(int i=0 ; i< CARD_TYPE ; i++){
        gc.DrawString(card_name[i] + ":" + card_count[i],60, 120+i*80);
      }

      if(isComplete ){
        gc.DrawString("complete!!",60, 920);
      }

    }
    void resetValue(){
      money = 10000;
      for (int i = 0; i < CARD_TYPE; i++) {
        card_count[i] = 0;
      }
      isComplete = false;
      new_card = -1;
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

photon ボイスチャット

ボイスチャット 環境 OS : windows10 pro Unity 2020.2.1f1 Photon2 ver 2.28.1 Device : ASUS,Galaxys7 Phothon Voice ver 2.23.1  音声サンプル PhotonVoice/Demos以下の*.unityをひらく DemoVoiceMinimal 最小サンプル ProximityVoiceChat 近づいたときに通話ができる DemoVoicePun-Scene モデル表示、通話状態アイコンの表示 設定 通常のPUN2設定に加えてVoice用のIDも設定しておく Environmentの下に赤字のmissingが複数あるので削除しておく。 有効になれば[DemoVoicePun-Scene]において、PUNが音を認識したときキャラの上に吹き出しアイコンが表示される。 遅延時間、音量 通話中の制御 [RequireComponent(typeof(PhotonVoiceView))] public class PointersController : MonoBehaviour { //[SerializeField] 属性をつけていると、CS0649警告が出るようになった。消したい。 #pragma warning disable 649 [SerializeField] private GameObject pointerDown; [SerializeField] private GameObject pointerUp; #pragma warning restore 649 private PhotonVoiceView photonVoiceView; private void Start() { this.photonVoiceView = this.GetComponent<PhotonVoiceView>(); } private void Update() { this.SetActiveSafe(this.pointerDown, this.photonVoiceView.IsSpeaking); this.SetActiveSafe(this.pointerUp, this.photonVoiceView.IsRecording); } private void SetActiveSafe(GameObject go, bool active) { if (go != null && go.activeSelf != active) { go.SetActive(active); } } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

スマートデバイスプログラミング①(制限時間内にクリックした回数を競争するゲーム)

UnityとGameCanvasのダウンロード

1:UnityHubをDLしてインストール。(一部のwindows環境では拡張子を.exeに変更する必要があります)

2:UnityHubを起動して、Unity2019.3.11f1をインストール。

3:GameCanvasのダウンロード
https://github.com/sfc-sdp/GameCanvas-Unity/
でGameCanvasをダウンロード

Game.csの編集

Assets内のGame.csを別エディタで編集


using Sequence = System.Collections.IEnumerator;

/// <summary>
/// ゲームクラス。
/// </summary>
public sealed class Game : GameBase
{
    // 変数の宣言
    int sec = 0;

    int time = 600;
    int score = 0;

    /// <summary>
    /// 初期化処理
    /// </summary>
    public override void InitGame()
    {
        // キャンバスの大きさを設定します
        gc.SetResolution(720, 1280);
    }

    /// <summary>
    /// 動きなどの更新処理
    /// </summary>
    public override void UpdateGame()
    {
        // 起動からの経過時間を取得します
        sec = (int)gc.TimeSinceStartup;

        time = time - 1;
        if(gc.GetPointerFrameCount(0)==1){
          if(time >= 0){
            score = score + 1;
          }
        }
        if(gc.GetPointerDuration(0) >= 2.0f){
          time =600;
          score =0;
        }
    }

    /// <summary>
    /// 描画の処理
    /// </summary>
    public override void DrawGame()
    {
        // 画面を白で塗りつぶします
        gc.ClearScreen();

        // 0番の画像を描画します
        gc.DrawImage(0, 0, 0);

        // 黒の文字を描画します
        gc.SetColor(0, 0, 0);
        gc.SetFontSize(48);
        gc.DrawString("この文字と青空の画像が", 40, 160);
        gc.DrawString("見えていれば成功です", 40, 270);
        gc.DrawRightString($"{sec}s", 630, 10);

        if(time >= 0 ){
          gc.DrawString("time:"+time,60,0);
        }
        else {
          gc.DrawString("finished!!",60,0);
        }
        gc.DrawString("score:"+score,60,60);
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ゲーム開発におけるボイスデータの効率的な管理 with CRI ADX2 (後編)

前編記事について

本記事は下記記事の後編となります。
ゲーム向け統合型サウンドミドルウェア「ADX2」についての概要や、waveファイルをツールに登録する手順については、前編をご確認ください。

また、ゲームエンジンへの組み込みについては以下の記事にて解説しています。

Atom Craftで設定できる便利なボイス設定

ADX2とそのオーサリングツールであるAtom Craftは、ゲームの開発環境に導入することで、ボイスデータ側に様々な再生制御情報を埋め込むことができます。
本記事では、逆引き的にボイスにまつわる再生制御について紹介します。

同一キャラのボイスを複数同時に再生させない設定

ADX2のシステムでは、音データにタグ付けできます。カテゴリと呼んでいます。カテゴリごとに同時再生できる数の上限ルールを設定することで、音の鳴り方を制御できます。

たとえば、ゲームの会話シーンなどでユーザーのタップによりセリフが飛ばせる仕様を考えてみましょう。
セリフをスキップしたとき、前のセリフのボイスが鳴りっぱなしでは聞きづらくなります。プログラムで制御しようとした場合、「ボイス再生中にタップされたら、いったん再生中のボイスを止めて、そして次のボイスを再生する」という処理になると思います。

ADX2では、カテゴリに対して「最大再生数は1、これを超えたら今鳴っている同カテゴリの音は止める」という設定ができます。ボイスデータがプログラムから再生をリクエストされたとき、すでにセリフが再生されていたらその音をキャンセルして、音声を再生します。これによりプログラムは単に次のセリフを再生するだけで済みます。

これを「カテゴリキューリミット」といいます。Atom Craftでは、プロジェクトツリーでカテゴリを選択したときのインスペクタービューで内容が確認できます。

カテゴリキューリミット.png

「カテゴリ」をゲームに登場するキャラクターごとに用意しておき、それぞれの「カテゴリキューリミット数」を「1」にしておけば、同じキャラクターのボイスが同時に鳴ることがなくなります。

また、単に再生数上限にリミットをかけるだけではなく、指定した時間内は再生リクエストをキャンセルする「多重再生禁止時間」を指定することもできます。

BGMが鳴っているときにボイスを再生すると、BGMの音量を落とす設定

ゲームの演出には、キャラクターのボイスが再生されている間はBGMの音量を一時的に下げる「ダッキング」というものがあります。
プログラムで制御しようとした場合は、キャラクターボイスの再生状況を監視して、一度BGMの音量を下げ、ボイスの再生が終わったら戻し、という処理になります。

ADX2の場合は、カテゴリに対してフラグ立てをして、どのような遷移で音量が変化するかをツール側で設定しておくだけで、この演出を導入できます。

先ほどの例では、カテゴリをキャラクターごとに作成していました。今回はもっと大きな枠組み「BGM」と「Voice」というカテゴリを作って制御します。

カテゴリ.png

なお、ひとつのキューに複数のカテゴリ設定が可能です。先ほどのキャラクターボイスごとの制御と組み合わせる場合は、ボイスのキューはキャラクターのカテゴリと、全体的な音の種類のカテゴリ2つを持つ形になります。

カテゴリ分けされた音同士の相互作用を作るために「REACT」機能を使います。
プロジェクトツリービューの「REACT」で設定します。

REACTビューでは、パラメータの変化の仕方を表したグラフ、変化するカテゴリ対象、変化をトリガするカテゴリが表示されます。

React設定.png

この図の例では、「BGM」カテゴリに属するキューに対し、「Voice」カテゴリの音が鳴った時にボリュームを一時的に下げ、「Voice」カテゴリの再生が終わったらボリュームを戻します。

カテゴリとREACTを組み合わせて設定することで、プログラム側で何もせずに音を再生するだけで「ダッキング」が実現します。

複数のボイスからランダムに選んで再生する設定

ゲーム中の「やられボイス」など、複数のバリエーションがあるボイスデータについて、ランダムに再生することを考えます。
プログラムで制御しようとした場合は、あらかじめ複数のボイスデータを再生準備状態にしておき、リクエストが来たら乱数でどれを鳴らすか決める、というやり方になると思います。

ADX2の場合は、そういった「複数ファイルからのランダム再生」はランタイム側に内蔵しています。

複数のボイスデータを内包する形でキューを作り、トラックの再生設定を「シーケンシャル(全部同時に再生)」から「ランダム」や「ランダムノーリピート」に変更するだけで実現できます。

複数ランダム.png

上記の例では、4つの音声データが含まれたキューとなっています。トラックの再生設定が「ランダムノーリピート」になっているため、このキューに再生リクエストをするたびにランダム、かつ前回鳴らしたものは繰り返さない(ノーリピート)挙動になります。

また、ランダム設定は再生の出現率を変更することもできます。
インスペクターの「ランダム」欄で乱数重みの数値を設定することで、ランダムの中でも再生されやすいボイスデータと、あまり再生されないボイスデータの演出ができます。

乱数重み.png

一定間隔で連続再生されたときだけ特別なボイスを再生する(パズルゲーコンボ演出)

ひと言で説明するのが難しい演出なのですが、

  • 格闘ゲームの連続ヒットで、短い間に攻撃を連続ヒットしているときだけ食らったときのボイスが変わっていく
  • プレイヤーが武器を連射をしたいるときだけ別のボイスが流れる
  • 落ちものパズルでコンボがつながった時だけボイスが変わっていく

などの、シチュエーションもAtom Craftで作ることができます。
「コンボシーケンシャル」という機能を使うのですが、以前の記事で紹介しています。

セリフの文字情報を音声データに埋め込む

音響制作会社からセリフのデータが納品されてくるとき、たいては連番のwaveファイルでやってきます。どの番号のファイルがどのセリフなのかについては、Excelで管理したり、ゲーム開発環境側で読めるデータ(UnityならScriptable Object)などで管理すると思います。

ADX2の場合は、音データそのものに文字列データを埋め込むことができ、これをセリフの管理に転用できます。
キューの中の設定に「ユーザーデータ」という、任意の文字列を含むことができる機能があります。

ユーザーデータ.png

この文字列情報はビルドデータに含まれますので、どのデータがどんなセリフなのか、すぐに確かめることができます。
また、ゲームの実行時にも読み取ることができますので、そのままセリフ文字に使うこともできます。
ユーザーデータは、Atom Craftの「リストエディタビュー」で一覧表示できます。

ユーザーデータの一覧表示.png

埋め込まれた文字は、ゲームの開発環境側で読みだして表示できます。
Atom Craftで作業しない人でも、セリフの内容をいちいちプレビュー再生せずに確かめることができます。
Unityの場合は、ADX2 for Unity SDKのAtom Browserでユーザーデータの内容が確認できます。また、ウィンドウを改造することで、キューの横にセリフ文字を表示することも可能です。

ユーザーデータの表示.png

拡張方法は、以下の記事を参考にしてください。

Atom Craft上の別の機能で「コメント」というものがありますが、これはAtom Craftのプロジェクトファイル上のみの表示となり、ビルドされたデータには含まれません。

ADX2の機能の活用でコード量を減らそう

ゲームにおけるセリフデータの制御やデータ管理として、ADX2は幅広い応用が可能です。
フルボイスゲームであったり、バトルものでセリフが多いなどのケースでは特に活躍しまず。ぜひ触ってみてくださいね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む