ラベル unity の投稿を表示しています。 すべての投稿を表示
ラベル unity の投稿を表示しています。 すべての投稿を表示

2014年2月19日水曜日

[Unity][C#][ラムダ式]他オブジェクトに依存する処理のレシピ

こんにちは。開発の松本です。
しばらくObjective-Cをやっていましたが、久しぶりにUnityでゲーム開発をしています。
コンポーネント指向のUnityではMVCやオブジェクト指向がはまらず、勘を取り戻すのにやや苦労しました。

さて...
ゲームに限らず、他オブジェクトに依存する処理は、よくあると思います。
MVCだと「コントローラが、モデルなど他オブジェクトの状態を見て制御」ですが、Unityではしっくりこない気がします。(自分だけ?)
そこで今回、私が実装したレシピをご紹介します。(C#。見やすくするためnullチェックは省略)

例)魔法「イレカワール」

仕様

  • 自キャラと、仲間ターゲットキャラの位置が入れ替わる
  • 仲間ターゲットキャラが、攻撃中、魔法詠唱中、被ダメージ中、スタン中は、入れ替われない
  • 敵キャラとは入れ替われない

実装

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

using MagicParams = System.Collections.Generic.Dictionary<string, object>;
public delegate void MagicCallback (GameObject sender, MagicParams userParams);

public class Magic : MonoBehaviour {
 
 /** 入れ替わり */
 void Replace (GameObject him) {
  // コールバック
  MagicCallback replaceRes = (sender, resParams) => {
   // 返答OKの場合
   if ((bool)resParams ["result"]) {
    // 移動
    transform.position = (Vector3)resParams ["position"];
   }
   // 返答NG
   else {
    // 失敗メッセージ出力などを実装
   }
  };
  // 要求パラメータ
  MagicParams reqParams = new MagicParams () {
   {"sender", gameObject},
   {"position", transform.position},
   {"callback", replaceRes},
  };
  // 入れ替わり要求
  him.SendMessage ("ReplaceReq", reqParams);
 }
 
 /** 入れ替わり要求 */
 void ReplaceReq (MagicParams reqParams) {
  // 要求パラメータ
  GameObject sender = (GameObject)reqParams ["sender"];
  MagicCallback cb = (MagicCallback)reqParams ["callback"];
  // 返答パラメータ
  MagicParams resParams = new MagicParams () {
   {"sender", gameObject},
   {"position", transform.position},
  };
  // 入れ替わり可能かチェック
  if (!IsReplacable(sender)) {
   resParams ["result"] = false;
   if (null != cb) cb (gameObject, resParams);
   return;
  }
  // 移動
  transform.position = (Vector3)reqParams ["position"];
  resParams ["result"] = true;
  if (null != cb) cb (gameObject, resParams);
 }

 /** 入れ替わり可能か */
 bool IsReplacable (GameObject him) {
  // himが敵ならreturn false;
  // 自分が攻撃中、魔法詠唱中、被ダメージ中、スタン中ならreturn false;
  return true;
 }
}

以上のように、当事者同士でチェックし合って制御しています。ポイントは↓
  • コールバック処理をラムダ式で実装
  • パラメータはとりあえずDictionaryにぶち込んで渡す

Magic.csを、魔法を使う/受けるGameObjectに付けて使用します。
イレカワール詠唱時に、Replace(ターゲットGameObject)で呼べば、入れ替わります。

以上、他オブジェクトの状態などに依存する処理のレシピでした。 他にも色々方法はあると思います。ご指摘などあれば、コメントいただけると幸いですm(_ _)m





2013年10月8日火曜日

【Unity】NGUI3.0を使ってスクロールビューを作ってみよう

こんにちは、開発の半沢 匠です。

今回はUnityでUIを作るとしたら今や欠かせないアセットNGUIについてお話させていただきます。
つい先日NGUIが3.0にメジャーバージョンアップしました。作者さんいつもありがとうございますmm

そこで今回はこちらを用いてスクロールビューの作成方法を図入りで説明します。
っとは言うものの実は作者さんが既にわかりやすい動画をYoutubeにアップしてらっしゃるので
詳細はそちらを見ていただくのが良いかと思います。

動画はこちら https://www.youtube.com/watch?feature=player_embedded&v=OiA4o8KqBFI

こちらは簡単に日本語でまとめたメモとしてご活用いただければと思います。

======

①ベース作成










・メニュのNGUI→Open→UI Wizardを選択
・CameraがSimple 2Dになっていることを確認しCreate Your UI ボタンを押下


②ScrollView作成

















































・Anchorの子オブジェクトとして空のオブジェクトScrollViewを作成
・Add Componentボタンを押下しPanelを選択しUIPanelスクリプトをアタッチする
・UIPanelのClippingをAlphaClipにする
※もしくはSoftClip。こちらの場合は両端のぼやける範囲をSoftnessの値で指定可能
・表示したい場所/領域にCenter/Sizeの値を変更(Scene内の色枠を参考に)
・Add Componentボタンを押下しDraggable Panelを選択しUIDraggable Panelスクリプトをアタッチする
・Scaleの値を移動させたい方向のみに1を入力
※縦スクロール:X0、Y1、Z0
  横スクロール:X1、Y0、Z0


③Grid作成






















・ScrollViewの子オブジェクトとして空のオブジェクトGridを作成
・Add Componentボタンを押下しGridを選択しUIGridスクリプトをアタッチする
・UIGridのArrangementを移動させたい方向に選択
※縦スクロール:Vertical
  横スクロール:Horizontal
・アイテム(④で作成するオブジェクト)の1つの大きさをCellWidthとCellHeightで指定
・Gridを移動し②で作成したPanel内の表示させたい場所に移動させる


④アイテム作成























・ScrollViewの子オブジェクトとして空のオブジェクトItemを作成
・こちらのオブジェクトがスクロールさせるアイテム(画像等)になります
・表示させたいものを子オブジェクトとして追加する(複数可。画像やラベル等)
・Add Componentボタンを押下しDrag Panel Contentsを選択しUIDrag Panel Contentsスクリプトをアタッチする
・メニューのNGUI→Attach→ColliderでBox Colliderをアタッチする
・必要に応じてItemオブジェクトを複数作成する


⑤Outline作成

・Anchorの子オブジェクトとして空のオブジェクトOutlineを作成
・メニューのNGUI→Attach→ColliderでBox Colliderをアタッチする
・CenterとSizeを②で作成したScrollViewのUIPanelのCenter/Sizeの値にする
・Add Componentボタンを押下しDrag Panel Contentsを選択しUIDrag Panel Contentsスクリプトをアタッチする
・Draggable PanelにScrollViewをドラッグ&ドロップする
======

以上でスクロールビューの完成です!
説明すると少し長く思えますが、実際に作ると直ぐにできますので、
是非作ってみてください。





2013年3月11日月曜日

Unityでの音(サウンド)管理(SoundManager Ver.β)

こんにちは、開発の半沢 匠です。
今回もUnity(http://unity3d.com/)に関しての記事を書きたいと思います。


■音管理

ゲーム開発及び、アプリ開発において
音再生は欠かせないもの。

BGMやSEを導入するだけで、ぐっと雰囲気が出てきます。
今回はこれをなるべく簡単に管理できるような仕組みを紹介したいと思います。


■スクリプト
SoundManager.csという下記のスクリプト作成いたしましたので、
こちらを紹介させていただきます。

using UnityEngine;
using System;
using System.Collections;


// 音管理クラス
public class SoundManager : MonoBehaviour {
 
 protected static SoundManager instance;

 public static SoundManager Instance
 {
    get
    {
       if(instance == null)
       {
          instance = (SoundManager) FindObjectOfType(typeof(SoundManager));

          if (instance == null)
          {
             Debug.LogError("SoundManager Instance Error");
          }
       }

       return instance;
    }
 }
 
 // 音量
 public SoundVolume volume = new SoundVolume();
 
 // === AudioSource ===
 // BGM
 private AudioSource BGMsource;
 // SE
 private AudioSource[] SEsources = new AudioSource[16];
 // 音声
 private AudioSource[] VoiceSources = new AudioSource[16];
 
 // === AudioClip ===
 // BGM
 public AudioClip[] BGM;
 // SE
 public AudioClip[] SE;
 // 音声
 public AudioClip[] Voice;

 void Awake (){
  GameObject[] obj = GameObject.FindGameObjectsWithTag("SoundManager");
  if( obj.Length > 1 ){
   // 既に存在しているなら削除
   Destroy(gameObject);
  }else{
   // 音管理はシーン遷移では破棄させない
   DontDestroyOnLoad(gameObject);
  }
  
  // 全てのAudioSourceコンポーネントを追加する

  // BGM AudioSource
  BGMsource = gameObject.AddComponent<AudioSource>();
  // BGMはループを有効にする
  BGMsource.loop = true;
  
  // SE AudioSource
  for(int i = 0 ; i < SEsources.Length ; i++ ){
   SEsources[i] = gameObject.AddComponent<AudioSource>();
  }
  
  // 音声 AudioSource
  for(int i = 0 ; i < VoiceSources.Length ; i++ ){
   VoiceSources[i] = gameObject.AddComponent<AudioSource>();
  }
 }
 
 void Update () {
  // ミュート設定
  BGMsource.mute = volume.Mute;
  foreach(AudioSource source in SEsources ){
   source.mute = volume.Mute;
  }
  foreach(AudioSource source in VoiceSources ){
   source.mute = volume.Mute;
  }
  
  // ボリューム設定
  BGMsource.volume = volume.BGM;
  foreach(AudioSource source in SEsources ){
   source.volume = volume.SE;
  }
  foreach(AudioSource source in VoiceSources ){
   source.volume = volume.Voice;
  }
 }
 

 
 // ***** BGM再生 *****
 // BGM再生
 public void PlayBGM(int index){
  if( 0 > index || BGM.Length <= index ){
   return;
  }
  // 同じBGMの場合は何もしない
  if( BGMsource.clip == BGM[index] ){
   return;
  }
  BGMsource.Stop();
  BGMsource.clip = BGM[index];
  BGMsource.Play();
 }
 
 // BGM停止
 public void StopBGM(){
  BGMsource.Stop();
  BGMsource.clip = null;
 }

 
 // ***** SE再生 *****
 // SE再生
 public void PlaySE(int index){
  if( 0 > index || SE.Length <= index ){
   return;
  }
   
  // 再生中で無いAudioSouceで鳴らす
  foreach(AudioSource source in SEsources){
   if( false == source.isPlaying ){
    source.clip = SE[index];
    source.Play();
    return;
   }
  }  
 }
 
 // SE停止
 public void StopSE(){
  // 全てのSE用のAudioSouceを停止する
  foreach(AudioSource source in SEsources){
   source.Stop();
   source.clip = null;
  }  
 }
 
 
 // ***** 音声再生 *****
 // 音声再生
 public void PlayVoice(int index){
  if( 0 > index || Voice.Length <= index ){
   return;
  }
  // 再生中で無いAudioSouceで鳴らす
  foreach(AudioSource source in VoiceSources){
   if( false == source.isPlaying ){
    source.clip = Voice[index];
    source.Play();
    return;
   }
  } 
 }
 
 // 音声停止
 public void StopVoice(){
  // 全ての音声用のAudioSouceを停止する
  foreach(AudioSource source in VoiceSources){
   source.Stop();
   source.clip = null;
  }  
 }
}


更に音量クラスも作成する。

// 音量クラス
[Serializable]
public class SoundVolume{
 public float BGM = 1.0f;
 public float Voice = 1.0f;
 public float SE = 1.0f;
 public bool Mute = false;
 
 public void Init(){
  BGM = 1.0f;
  Voice = 1.0f;
  SE = 1.0f;
  Mute = false;
 }
}


■使用手順
1.上記のSoundManager.csを空のオブジェクトに張り付ける。
2.オブジェクトのタグ名を”SoundManager”とする。
3.追加したい音ファイルをGUI操作で追加する(下図参照)

4.音を再生したい箇所に下記ソースを、それぞれ追加する。
SoundManager.Instance.PlayBGM(0);
SoundManager.Instance.PlaySE(2);
SoundManager.Instance.PlayVoice(3);

以上で音を再生できるようになったかと思います。



■解説
・SoundManagerは、1シーンに1つのみ生成されるようになっており、
シーン遷移時でも破棄されないように設定されており、
シングルトン化してある為、どのソースからもアクセス可能になっております。
・エディタ上でドラッグ&ドロップだけで音ファイルを簡単に追加/削除できるため、
プログラマー以外の人でも簡単に使えるように想定(別途エクセル等でインデックス管理はした方が良い)
また、鳴らす際も1行のソースを追加するだけの簡単仕様にしました。
・new AudioSource[16];は16個まで同時再生可能という設定になっております。
こちらは状況に応じて色々と変更されると良いかもしれません。
・SoundVolumeに[Serializable]を追加することで、エディタ上に音量やミュート状態を表示するようにし、デバッグ時に使用できるように致しました。


■最後に
よくある手法ではございますが、こんな感じに私は使用しておりますという事でご紹介させていただきました。
結構便利な機能だと思いますので、是非ご活用頂ければと思います。
Ver.βということで今後も随時使いやすい形に改良していきたいと思います。







2012年6月1日金曜日

Jenkinsを使ってUnity開発をもっと楽にしよう


こんにちは、開発の半沢 匠です。
今回は、CI(継続的インテグレーション)ツール「Jenkins」を使ってUnity開発をもっと楽にしよう!(WebPlayer版 導入編)になります。※Macへの導入方法を解説いたします。
■Jenkins導入のメリット
・いつでも実行可能な最新版のモジュールが、定期的に自動で作成される
・ボタンをポチっと押すだけで、いつでも実行可能な最新版のモジュールが作成できる
・モジュール履歴を残せる→バグ早期発見に繋がる(どのバージョンでバグが出たのか確認できる)

■今回行うこと
1.SVNからの最新版取得
2.UnityでのビルドでWebPlayer作成
3.Webへのアップロード
4.1~3を15分毎に自動で行う

■準備
  • 以下を用意・構築しておく
    • Mac
    • SVN環境
    • Unity

■導入
▼Unity側の準備
  • UnityプロジェクトをSVNにコミット
  • そのプロジェクト内にビルドスクリプトを作成し、Editorフォルダ内に配置する
using UnityEngine;
using UnityEditor;
using System.Collections;
 
public class MyBuilder {
    
    [UnityEditor.MenuItem("Tools/Build Project AllScene")]
    public static void BuildProjectAllScene() {
        EditorUserBuildSettings.SwitchActiveBuildTarget( BuildTarget.WebPlayer );
        string[] allScene = new string[EditorBuildSettings.scenes.Length];
        int i = 0;
        foreach( EditorBuildSettingsScene scene in EditorBuildSettings.scenes ){
            allScene[i] = scene.path;
            i++;
        }
        
        BuildPipeline.BuildPlayer( allScene,
                                   "WebPlayer",
                                   BuildTarget.WebPlayer,
                                   BuildOptions.None
                                 );
    }
}

 ▼Jenkinsインストール
  • Javaのランタイムをインストール(Lionのみ必要になります)
  • http://jenkins-ci.org/ から MaC OS X パッケージをDL&展開
  • /Library/LaunchDeamons/org.jenkins-ci.plistの下記箇所を編集
    • JENKINS_HOME を任意のパスに変更(Jenkinsのワークスペースになります)省略可
    • GroupName をdeamonからログインアカウントの所属グループに変更
    • UserName をdeamonからログインアカウントの名前に変更
  • ワークスペースの権限を自分の所属グループ&自分のアカウントに変更
    • 初期値のワークスペースの場合は下記の通り
      • sudo chown -R ユーザー名 /Users/Shared/Jenkins/Home
      • sudo chgrp -R グループ名 /Users/Shared/Jenkins/Home
  • システム環境設定を起動し、共有→Web共有をオンに設定します
  • ブラウザで http://[PCのIPアドレス]:8080/ にアクセスでJenkinsが表示される

--- ここからは、Jenkins(ブラウザ)上での作業になります。 ---

 ▼プラグインの追加・設定
  • プラグインの追加
    • ホーム→Jenkinsの管理(左メニューリンク)→プラグインの管理(コンテンツリンク)→利用可能(タブ) を選択
      • Jenkins Unity3d plugin にチェック
      • SCP Plugin にチェック
      • 下の「ダウンロードして再起動後にインストール」ボタンを押下
      • 再起動されるまで待つ
  • プラグインの設定
    • ホーム→Jenkinsの管理 (左メニューリンク) →システムの設定 (コンテンツリンク) を選択
      • Unity 3d 
        • 名前:適当(例:Unity3.5.1f2)
        • インストールディレクトリ:/Applications/Unity/Unity.app/
      • SCPリポジトリホスト で設定
        • 出来上がったモジュールの公開先をこちらのプラグインで設定します
        • ホスト名、ポート、ルートリポジトリパス、ユーザー名、パスワード/パスフレーズ、秘密鍵を設定
    • 左下の「保存」ボタンを押す

 ▼ジョブ作成
  • 新規ジョブ作成 を選択
    • ジョブ名:適当(例:Unity-TestProject)
    • フリースタイル・プロジェクトビルド を選択
    • 「OK」ボタンを押す

▼ジョブ設定
  • ソースコード管理システム
    • Subversion を選択
    • リポジトリURL:プロジェクトのSVNパス
      • 例)svn://localhost/Unity/TestProject
    • ローカルモジュールディレクトリ:ローカルのプロジェクトのパス(※パスはワーキングスペースからなので注意)
      • 例)./Unity/TestProject
    • チェックアウト方式:svn revert'してから'svn update'を実行 を選択(任意のものでOK)
  • ビルド・トリガ
    • こちらで定期を指定する。今回は15分に1回リポジトリをチェックし、更新があれば実行するように設定
      • SCMポーリング を選択
      • スケジュール:下記を記述
        • */15 * * * *
  • ビルド
    • ビルド手順の追加:Invoke Unity3d Editor を選択
      • Unity 3d installation name:プラグインの設定で指定した名前を選択
      • Editor command line arguments:下記を入力
        • -quit -batchmode -executeMethod MyBuilder.BuildProjectAllScene
  • ビルド後の処理
    • 成果物をSCPリポジトリに公開 を選択
      • SCPサイト:プラグインの設定で指定したホスト名を選択
      • アップロードファイル:追加ボタンを押す
        • ソース:ローカルのアップロードファイル(出来上がったモジュールフォルダ)を選択
          • 例)Unity/TestProject/WebPlayer
        • 送信先:アップロード先を選択(フォルダ名にビルド番号・リビジョンなどを追記するとよい)
          • 例)No${BUILD_NUMBER}_Rev${SVN_REVISION}

■動作確認
  • 先ほど作成したジョブを選択し、左メニューから「ビルド実行」をクリック
  • Unityビルドが実行され、モジュールがローカル上に作られた後、Webにアップロードされる

これで、15分に1回は最新版のモジュールが自動で作成され自動でWeb公開されるようになりました!
また、■動作確認で行った「ビルド実行」をポチッとするだけで、
いつでも最新版のモジュール作成&公開が行われるようになりました!


◎導入してみて
  • 「ビルド実行」をポチッと押すだけなのが本当に楽!
    • UnityのUI上でビルドを実行し、ビルドが完了するまで待った後に、出来上がったファイルを、SCPツールでWeb上にアップっといった作業が一切必要無くなった。
  • ビルド&公開作業が自動化された為、その間の時間を丸々他作業に当てれるようになった!
→手間と時間の大幅削減に成功!


Jenkinsは、他にも便利な機能がたくさんあり大変便利なツールなので是非導入してみてください。










2012年1月17日火曜日

Unityを用いたゲーム開発(タワーディフェンス編)Part.1

はじめまして、開発の半沢 匠です。

今話題のゲームエンジンUnity(http://unity3d.com/)を使ってゲーム開発をしてみよう!
という事で、まずは「タワーディフェンス」を作ってみました!

ここでは、作業報告も兼ねて作業経過や開発時に役に立った事等を連載していきたいと思います。


はじめに

Unityとは、簡単に表すと
「Windows及びMacで動作可能な3Dゲーム開発ツール」です。

簡単なGUI操作でコンテンツを構築し、
構築したコンテンツに対してプログラミング言語(JavaScript、C#)を使って動きを作成することができます。
プログラマでない方でも直感的に操作できるインターフェイスになっております。

さらに、Unityはマルチプラットフォームに対応しており、
下記の実行形式に書き出す事が出来ます。

・Windows、MacOSXのOS
・WEBブラウザ
・iPhone、Android
・PS3、Xbox、Wii

Proライセンスは有料ですが、無料版も用意されており、
無料版でも十分な機能が備わっています。

---------------

■プレイアブルデモ Part.1
※プレイするにはUnityのwebプレイヤーが必要になります

それなりに動いてはいますが、
ゲームとしてはまだまだまだですね。
ここからゲームらしくなるように調整していきたいと思います。


■スクリプト
Unityはスクリプト言語を使って"ゲームらしさ"を作成することができます。
ここでは、今回開発した際に役に立った事を紹介したいと思います。
今回は C# で開発しております。
こちらでは、ある程度Unityの知識を持った人向けの解説になっております。

・クリックした位置に銃をポップ
画面上でマウス左クリックを押下箇所に銃(GameObject)を生成させます。
- GunPopスクリプトを適当なGameObjectに指定する


public class GunPop: MonoBehaviour {
 public GameObject selectGun   // 生成する銃を指定

 void Update (){
  // マウス左クリック押下したか判定
  if( Input.GetMouseButtonUp(0) ){
   // マウス座標を取得
   Vector3 screenPoint = Input.mousePosition;
   // マウス座標はXYの2次元なのでZ座標にはカメラのZ座標を指定
   screenPoint.z = 17.0f;
   // カメラ座標からワールド座標に変換
   Vector3 worldPoint = Camera.main.ScreenToWorldPoint(screenPoint);
   // 変換されたワールド座標にselectGun(銃のGameObject)のインスタンスを生成
   Instantiate(selectGun, worldPoint, selectGun.transform.rotation);
  }
 }
}


・弾が敵に当たった場合、当たった敵のHPを減らす
- 弾のGameObjectにBulletスクリプトを指定する
- 敵のGameObjectにEnemyStatusスクリプトを指定する
- 弾・敵のGmaeObjectにColliderを設定し、「Is Trigger」にチェックを入れる

public class Bullet : MonoBehaviour {
    public float hitDamage = 1.0f; // 与えるダメージを指定
    void OnTriggerEnter(Collider collisionInfo){
        // 当たったGameObjectのタグが"Enemy"か判定
        if(collisionInfo.gameObject.tag == "Enemy"){
            // 当たったGameObjectに指定されているスクリプトの"ApplyDamege"関数を呼び出し
            // 引数、hitdamage を渡す
            collisionInfo.gameObject.SendMessage("ApplyDamage",hitDamage);
            // 自分自身を消滅させる
            Destroy(gameObject);
        }
    }
}

public class EnemyStatus : MonoBehaviour {
    public float hitPoint = 1.0f; // 自分自身のHPを指定
    void ApplyDamage(float damage){
        // 受けたダメージを自身のHPから減算
        hitPoint-=damage;
        // HPが0以下になったか
        if( hitPoint <= 0.0f ){
            // 自分自身を消滅させる
            Destroy(gameObject);
        }
    }
}


■次回予定
・もう少し銃の配置される場所を明示的にし、敵に弾を当てやすいようにする仕組みを導入
・スコア機能を実装