99
UnityとBlenderハンズオン 静岡Developers勉強会 7UnityBlenderの学び直し

UnityとBlenderハンズオン第7章

  • Upload
    yaju88

  • View
    4.677

  • Download
    0

Embed Size (px)

Citation preview

UnityとBlenderハンズオン

静岡Developers勉強会

第7章 UnityとBlenderの学び直し

はじめに

静岡Developers勉強会では、過去に下記の勉強会を行いました。2010年:「Programming in Haskell」2011年:「JavaScript: The Good Parts」2012年:「HTML5&CSS3実践入門」、「GitHubハンズオン」、

「node.jsハンズオン」2013年:「入門 機械学習」2014年:「実践 コンピュータビジョン」

2015年は上記の勉強会で学んだことを実践し、Shizudev名義のスマホアプリを開発します。

作成後は公開用のサイトや動画も作成し、機械学習を使ってダウンロード状況などを分析していきます。スマホの広告費は会費にあて、万が一予定より多く収入が入った場合には寄付を考えています。

自己紹介

やじゅ@静岡・・・漢字名は「八寿」

平口八寿人(https://www.facebook.com/yasuhito.hiraguchi)

アラフォーエンジニア、元MSMVP(VisualBasic)

静岡県島田市のSL(大井川鉄道)が走っている所に在住

Twitter:yaju はてなID:Yaju3D

http://blogs.wankuma.com/yaju/

http://yaju3d.hatenablog.jp/ 数学と物理を基礎からやり直す)

http://www.slideshare.net/yaju88/presentations (スライド)

概要

2015年の静岡Developers勉強会は「スマホアプリの開発」を題材とします。

スマホアプリを開発する上で、汎用的なツールの使い方を学びます。

・Unity統合開発環境を内蔵し、複数のプラットホームに対応するユニティ・テクノロジーズが開発したゲームエンジンである。ゲームとインタラクティブな3Dコンテンツ制作のための常識にとらわれない強力な機能を提供します。

・Blenderオープンソースの3DCG制作ツールです。

3Dモデルの作成、レンダリングのほかアニメーション、コンポジット機能も備える。

セッション内容

Unityの学び直し今までチュートリアルをこなすことで精一杯になってしまい、理解度が弱かったため、

基礎部分を学び直していく。

パンダのモデリング初心者のための!作って学ぶBlenderの基礎:②モデリング - 日本VTR実験室

http://nvtrlab.jp/column/2-2

スマホアプリのアイディア今年は、Shizudev名義でスマホアプリを作成します。

プロトタイプ版を作成する予定です。※今回は間に合わなかったけど

Unityの学び直し

はじめに

前回、ミニゲーム発表会を行ったが、作成する上でまだちゃんと理解できていない部分があった。

今までチュートリアルをこなすことで精一杯になってしまい、理解度が弱かったため、

基礎部分を学び直していく。

資料作成の都合上、間に合わないのは持ち越します。資料作成が手一杯で間に合わないんだもん。

基礎部分を学ぶ上で、下記の資料を一部使わせて頂いた。感謝します。

シューティングゲーム用ドット絵フリー素材

http://stg.iaigiri.com/material/

Unityの学び直し

アジェンダ

・CameraのSizeについて

・GUI(テキスト)表示について

・MonoBehaviorクラスの役割

・プラットフォーム判別

・開発用ビルドと本番用の切り分け

・public変数とInspectorの関係

・ゲームオブジェクトの取得、表示/非表示(Active)、動的生成、削除

・コンポーネントの取得

・他スクリプトのアクセス(値参照/メソッド呼出)

・キー入力(キーボード、複数の入力端末:方向、複数の入力端末:ボタン)

・マウス入力とマウス位置

・タッチ入力とタッチ位置

衝突、フォントやアニメーション

BGM、効果音などは次回に持ち越します。

Unity:フォルダの基本ルール

フォルダ名 説明

Scenes ゲームのシーン(*.scn)を保存するフォルダ

Prefabs ゲーム内で繰り返し使用するObjectを保存するフォルダ

Scripts ゲーム内で使うスクリプト(.csや.js)を保存するフォルダ

Animations ゲーム内で使うアニメーションを保存するフォルダ

Materials ゲーム内のObjectに設定するマテリアルデータ(色とか光沢とか)を保存するフォルダ

Physics

Materialsゲーム内の物理エンジンで使用するフィジカルマテリアルを保存するフォルダ

Fonts ゲーム内のフォントデータ(画像)を保存するフォル

Audio ゲーム内のBGMやSEなどのデータを保存するフォルダ

Resources ゲームプログラム内でなく、ゲームプログラムから別ファイルとしてデータをロードして扱うフォルダ

Editor Unityのエディタ機能を拡張するためのスクリプトを保存するフォルダ(ゲームを作成するだけなら不要)

PluginsUnityで作成したゲームを、iPhoneやAndroidなどのプラットフォームで動作させるためのネイティブプラグインを保存するフォルダ

Unity:座標について

ワールド座標

すべてのオブジェクト、カメラで共通に利用する座標で空間の原点を中心としている。

ローカル座標

親⼦関係のオブジェクトがあった場合、親と⼦の相対的な座標。

親オブジェクトを移動させる⼦オブジェクトも同じように移動する。

positionプロパティはワールド座標、 localPositionプロパティはローカル座標となる。

スクリーン座標

ワールド座標の中から、画面上の左上を原点として距離を測った座標。

マウスやタップ座標はスクリーン座標(ピクセル単位)なので、ワールド座標に変換して使用する。

それ用にCamera.main.ScreenToWorldメソッドが用意されている。OnGUIはこの値が基本値です。

Unity:座標について

ビューポート座標

カメラが映る範囲の座標。左下を原点として0から1の間に正規化されています。

例えば、viewport RectのXを0.5にすると、右半分しか表示されなくなります。

2人プレイヤーの場合、カメラオブジェクトを2つ用意して上側と下側に

分けて表示させたり出来ます。

また、プレイヤーの移動制限などに使用する。

第09回 プレイヤーの移動制限と様々な修正

この中のClampメソッドにて、カメラのビューポートと

Mathf.Clampを使用して移動の制限させています。

■Mathf.Clampの使い方

オブジェクトの y座標を-10以上20以下に設定したい場合

transform.position.y = Mathf.Clamp(transform.position.y, -10, 20)

Unity:Pixels Per Unitについて

Unityでは、1メートルを1Unitとしている。

Pixels Per Unitがデフォルトの100に設定されている場合、1メートルに100pxが入るように扱います。

例えば画像が48px × 48pxの場合、Pixels Per Unitが100なら、48cm × 48cmの大きさとなります。

Pixels Per Unitを48にすると画像は1m × 1mの大きさに変更されます。

※Pixels Per Unit を変更しても、画像の表示サイズが変わるだけで

transform.position.y = transform.position.y – 1 で下に1マス分移動するのは変わらない。

白い四角形が1マス分のスケール(1×1)のQuad Pixels Per Unit = 100→48に変更

Unity:CameraのSizeについて

CameraのSizeはAPIの説明では、 orthographic モードの場合は垂直サイズの視野量の半分となる。

円を描く場合、直径ではなく半径を指定する感じですね。

例として画面サイズを512x256を作成、アスペクト比は2:1となる。

カメラのサイズ「1」にすると、アスペクト比は2:1 の場合4x2マスが表示対象となる。

1 1

1

1 1 1 1

Unity:CameraのSizeについて

要件定義 例

実際の解像度は 640×480 とします。

16×16 のスプライトを 320×240 の解像度に見立てたスクリーンに描画させます。

横 : 320px(画面サイズ) / 16px(スプライトサイズ) = 20個

縦 : 240px(画面サイズ) / 16px(スプライトサイズ) = 15個

16×16 のスプライトが横に 20 個、縦に 15 個描画できるようになる。

スプライトのPixels To Units は 「16」に設定しておく。

Size プロパティに縦にスプライトを敷き詰めたい個数を 2 で割った値 「7.5」を設定します。

すると、Unity では縦に 15 メートルの長さで描画してくれるようカメラを調整してくれます。

参考:Unity3D で画面の1ピクセルとテクスチャの1ピクセルを合わせる

Unity:GUI(テキスト)表示について

CanvasのRenderMode「Screen Space - Overlay」(デフォルト)

「Screen Space - Overlay」は、GUIが絶対に最前面にくる表示方法となります。

[GameObject]→[UI] →[Text]にてテキストを追加すると、右上に矩形の片鱗が見えるだけになります。

Sceneビュー上でマウスホイールにてかなり縮小すると、やっとテキスト文字が見える状態になります。

それでもGameビュー上では文字が一緒に表示されます。

この点が縮小された表示内容

UI

Camera

UIとカメラを分けて管理している。

Unity:GUI(テキスト)表示について

CanvasのRenderMode「Screen Space - Camera」(お勧め)

「Screen Space - Camera」は、特定のカメラにGUIを表示させる表示方法となります。

そのため、「Render Camera」にカメラ(Main Camera)を指定する必要があります。

UIの表示がカメラと同じ位置になるので、テキストの配置が分かりやすくなります。

UI

Camera

Unity:GUI(テキスト)表示について

CanvasのRenderMode「World Space」

「World Space」は、他のオブジェクトと全く同じ座標でGUIを配置する表示方法となります。

他のオブジェクト同様にPositionで指定ができる。

Rect TransformのScaleをPixcel Per Unitがデフォルトが「100」

なので、XYZとも「0.01」にセットする。

PosのXYZとも「0」にセットする。

WidthとHeightは、カメラ表示サイズより大きくしておく。

UI

Camera

Unity:GUI(テキスト)表示について

Pivotの設定

オブジェクトの基準点です。このPivotの位置が、画像の中心点としてみなされます。

座標は(0,0)~(1,1)でデフォルトの中心点は(0.5,0.5)です。

中心点を移動して回転させると分かりやすい。

(0,0)

(1,1) この丸が中心点で移動可能

中心点を移動し回転

Unity:GUI(テキスト)表示について

Anchorの設定

親要素(Canvas)との相対位置を調整します。参照:UnityのuGUIのレイアウト調整機能について解説してみる

親であるCanvasのサイズが変更された際、Textの位置が変わります。

アンカーポイント(4つ葉マーク)がcenter-middleのままだと、Textの位置はずれる。

アンカーポイント(4つ葉マーク)をleft-topに設定した場合、Textの位置は左上に固定される。

アンカーのテンプレート

Unity:MonoBehaviorクラスの役割Unityエンジンからゲームの実行状況に応じてメッセージが送信されると、

実装クラス内にあるメッセージと同じ名前の関数を実行します。

MonoBehaviorクラス

Awake

Start

FixedUpdate

Update

LastUpdate

OnGui

OnColisionXXXX2D

OnTriggerXXXX2D

OnDisenable

継承先クラス

Start

FixedUpdate

OnGui

シーン開始時に呼ばれる

Unity GUI描画時に呼ばれるOnColisionEnter2D

コライダーヒット時に呼ばれる

Update

描画時(物理シミュレータの進行で使用)に呼ばれる

描画時(フレームの進行)に呼ばれる

Awake

シーンロード時に呼ばれる

Unity: MonoBehaviorクラスの役割

AwakeメッセージとStartメッセージの違い

Awakeは、基本的にクラスが生成されるときに呼ばれるコンストラクタのようなもの。

Startは、UpdateかFixedUpdateの最初の呼び出し直前に実行される。

※Awakeは常にStartより前にコールされる。また、Startはコルーチン化可能でAwakeは不可

例として、プレハブをインスタンス化した場合

void Update() {

//この時点で生成したhogeゲームオブジェクトのAwakeは常に呼ばれるがStartは呼ばれないことがある

Instantiate(hoge);

} ※動的にオブジェクト生成ならAwakeを使う。

Awakeは、自身のスクリプトの有効無効に関わらず常に呼ばれるが、Startはスクリプトが有効化された時しか

コールされない。よって、初期化が必要なタイミングになるまで意図的に遅らせることが出来る。

Awakeは、他Componentを参照時、実行順序によって他Componentがまだ初期化されていない場合がある。

Unity: MonoBehaviorクラスの役割

UpdateメッセージとFixedUpdateメッセージの違い

Unityは、描画処理と物理演算処理が別々に駆動している。

そのため、描画処理はUpdateメッセージで行い、物理演算処理はFixedUpdateメッセージで行う。

端末の処理速度が速い場合、Updateの方がFixedUpdateよりも頻繁に呼び出されたりする。

端末の処理速度が遅い場合、描画処理が抑制されUpdate回数が減りFixedUpdateの方が頻繁に呼び出される。

Unityの基本的なプログラムスタイルは、Updateで入力やキャラの表示切り替えなどの処理を行い、

FixedUpdateで物理エンジンを利用したキャラの移動計算を行うのが一般的です。

Unity: MonoBehaviorクラスの役割

コンポーネント指向

http://www.slideshare.net/lucifuges/unityc オブジェクトコンポジション

Unity:プラットフォーム判別

Platform Dependent Compilation

コンパイル時にプラットフォームに応じてコードを分けることが出来ます。

ソースコード上に #If 等(ifディレクティブ)で分ける

#if UNITY_EDITOR

Debug.Log("Unity Editor");

#elif UNITY_IPHONE

Debug.Log("Unity iPhone");

#else

Debug.Log("Any other platform");

#endif

その他のプラットフォーム定義

プラットフォーム 条件文(一部)

Unityエディタ UNITY_EDITOR

Windows UNITY_STANDALONE_WIN

MacOS UNITY_STANDALONE_OSX

WebPlayer UNITY_WEBPLAYER

iPhone UNITY_IPHONE

Android UNITY_ANDROID

Unity 4.6 UNITY_4_6

Unity:プラットフォーム判別

Application.platform

実行時のプラットフォームを判別することが出来ます。

if (Application.platform == RuntimePlatform.IPhonePlayer) // iOS プレイヤー

その他のプレイヤー(RuntimePlatform)定義

プレイヤー プラットフォーム

iOS プレイヤー RuntimePlatform.IPhonePlayer

Android プレイヤー RuntimePlatform.Android

Windows プレイヤー RuntimePlatform.WindowsPlayer

Mac OS X プレイヤー RuntimePlatform.OSXPlayer

Windows 上の Web Player RuntimePlatform.WindowsWebPlayer

Mac OS X 上の Web Player RuntimePlatform.OSXWebPlayer

Unity:開発用ビルドと本番用の切り分け

Debug.isDebugBuild

Unity ではビルド時に Development Build のチェックをつける事で開発用のビルドを行う事ができます。

[File]→[Build Settings]にて対象のPlatformを選択し、「Development Build」にチェックをオンにする。

if (Debug.isDebugBuild) {

Debug.Log(“debug build”);

}

但し、コードには両方含まれてしまう。

Debugログの表示、非表示切り替えに使う。

Unity: public変数とInspectorの関係

スクリプト内でpublicにした型はInspectorに表示されます。

またゲームオブジェクトで設定した値が初期化値に使われます。

Unityではゲームオブジェクトをメタ情報に変換してファイルができます。例 Enemy.cs.meta

MonoBehaviorを継承して作成したクラスでは、public変数が自動的にシリアライズされ、

Inspector上で表示や編集が可能になります。

// スコアを表示するGUIText public GUIText scoreGUIText;

// ハイスコアを表示するGUIText public GUIText highScoreGUIText;

Inspectorで書き換えた値がpublicの初期値より優先して反映される。

Public変数が先頭が英小文字でもInspector上では英大文字に変換され、また単語間に空白が入る

Unity: public変数とInspectorの関係

クラスや構造体は、 [System.Serializable]属性を付ければ表示させる事ができます。

[System.Serializable]public class hogefuga {

public float hoge = 0;}

public class PacmanMove : MonoBehaviour {public float speed = 0.4f;public hogefuga hoge;

Unity:ゲームオブジェクトの取得

名前指定による取得(アクティブ時)

GameObject.Find()を使用する。

GameObject enemy = GameObject.Find("Enemy");

Debug.Log(enemy.name);

※複数ある場合、最初にヒットしたオブジェクトを取得

⼦要素を取得する。

GameObject tank = GameObject.Find("Tank");

Debug.Log(tank.name);

孫要素を取得する。※Tankを付けないと検索に時間がかかる

GameObject battery = GameObject.Find("Tank/Battery");

Debug.Log(battery.name);

※非アクティブ(非表示)の場合、GameObject.Findでは検索対象とならない。

チェックオン時は表示かつ検索対象

Unity:ゲームオブジェクトの取得 名前指定による取得(非アクティブ時 or アクティブ時)

他GameObjectから下記のように取得しようとしてもnullになる。

GameObject enemy = transform.Find("Enemy").gameObject;

他GameObjectから取得するならスクリプトにpublic変数を用意し

Element[0] にEnemyのGameObjectをセットするなど予め取得しておく。

public GameObject[] element;

非アクティブ(非表示)の場合、 GameObject.Findでは検索対象とならない

その場合はTransform.Findとすることで取得できる。・⼦要素の取得

GameObject tank = element[0].transform.Find("Tank").gameObject;

Debug.Log(tank.name);

・孫要素の取得GameObject battery = element[0]. transform.Find("Tank/Battery").gameObject;

Debug.Log(battery.name);

チェックオフ時は非表示かつ検索対象外

Unity:ゲームオブジェクトの取得

全ての⼦要素取得

GameObject enemy = GameObject.Find("Enemy");

SearchAll(enemy.transform); //再帰で全て取得する

private void SearchAll(Transform trans) {

foreach (Transform child in trans) {

Debug.Log(child.name);

SearchAll(child);

}

}

HelicopterとTankとBatteryが出力される。

Unity:ゲームオブジェクトの取得

⼦要素から親要素取得

GameObject battery = GameObject.Find("Battery");

GameObject parent = battery.transform.parent.gameObject;

Debug.Log(parent.name);

Tankが出力される。

孫要素(or ⼦要素)から最上位(Root)要素取得

GameObject battery = GameObject.Find("Battery");

GameObject root = battery.transform.root.gameObject;

Debug.Log(root.name);

Enemyが出力される。

Unity:ゲームオブジェクトの取得

タグ指定による取得

親⼦階層のEnemyをやめて、HelicopterとTankをRootに持ってくる。

HelicopterとTankに、Tag「Enemy」をセットする。

GameObject enemy = GameObject.FindWithTag("Enemy");

Debug.Log(enemy.name);

Tankが出力される。(複数ある場合、最初にヒットしたオブジェクトを取得)

GameObject[] enemys = GameObject.FindGameObjectsWithTag("Enemy");

foreach(GameObject obj in enemys) {

Debug.Log(obj.name);

}

TankとHelicopterが出力される。(配列に全てのオブジェクトをセット)

コガネブログ【Unity】GameObject.Find 系関数の処理速度の検証結果

Unity :ゲームオブジェクトの表示/非表示(Active)

表示/非表示

ゲームオブジェクトをアクティブ(true)/非アクティブ(false)にします。

gameObject. SetActive(value: bool)

※非アクティブにした場合、 GameObject.Find()の検索対象外となります。

また、 Update()関数も呼び出されなくなります。

アクティブ時はカウントされるが、非アクティブだとカウントされない

private int count = 0;

void Update () {

count++;

Debug.Log("count = " + count);

}

チェックオフ時は非表示かつ検索対象外

Unity :ゲームオブジェクトの表示/非表示(Enabled)

表示/非表示

ゲームオブジェクトの描画を有効(true)/無効(false)にします。

※GameObject.Find()の検索対象でUpdate()関数も呼び出されます。点滅させる等に使えます。

アクティブ時はカウントされるが、非アクティブだとカウントされない

private int count = 0;

void Start () {

//renderer.enabled = false; //Unity4.xまで、Unity5からは下記方法でないとコンパイルエラー

Renderer ren = gameObject.GetComponent<Renderer>();

ren.enabled = false;

}

void Update () {

count++;

Debug.Log("count = " + count);

}

Unity:ゲームオブジェクトの動的生成

スクリプトにpublic変数を追加してプレハブのオブジェクト(Helicopter)をセットする

public GameObject[] element;

//動的生成する(位置(Vector3.zero) および 角度(Quaternion.identity) がデフォルト値)

GameObject obj = (GameObject)Instantiate(element[0]);

または

GameObject obj = (GameObject)Instantiate(element[0], Vector3.zero, Quaternion.identity);

obj.name = element[0].name;

※「(Clone)」という文字列を付加されたくない場合は生成されたインスタンスの名前をプレハブ名に

強制的に書換える。

名前に(Clone)

自動的に付く

Unity :ゲームオブジェクトの動的生成

特定のGameObjectの⼦要素にする

先に特定のGameObjectとして「Enemy」を作成しておく。

GameObject enemy = GameObject.Find("Enemy");

GameObject obj = (GameObject)Instantiate(element[0]);

obj.name = element[0].name;

obj.transform.SetParent(enemy.transform, false);

※親のGameObjectを取得して、親要素にセットする。

SetParent(parent: Transform, worldPositionStays: bool)の第二引数: worldPositionStays

親要素が X:-2、Y:1 で⼦要素が X:0、Y:0 の場合

true:ワールド座標系の位置情報を保持するため、⼦要素は親要素の位置を無視しX:0、Y:0に表示される。

false:親要素のローカル座標系に変換するため、⼦要素はX:(-2+0)、Y:(1+0)に表示される。

⼦要素に移動

Unity :ゲームオブジェクトの削除

オブジェクトの削除Destroy(gameObject); //指定したGameObjectを削除することが出来ます。

自分自身を削除Destroy(this); //コンポーネントが削除される(this=Destroyを呼び出したコンポーネント)

一定時間後に削除Destroy(this, 5.0f); //第二引数にfloat型の秒数後にオブジェクトが削除されます。

全ての⼦オブジェクトを削除 (孫オブジェクトまで削除なら再帰させる)

foreach ( Transform child in gameobject.transform ) {

Destroy(child.gameObject);

}

Unity:コンポーネントの取得

GetComponent<コンポーネント名>()

GameObject heri = GameObject.Find("Helicopter");

Rigidbody2D rb = heri.GetComponent<Rigidbody2D>();

Debug.Log(rb.mass); //1が出力される。

コンポーネントが複数ある場合(例 Box Collider 2D)

BoxCollider2D[] colliders = heri.GetComponents<BoxCollider2D>();

foreach(BoxCollider2D bc in colliders) {

Debug.Log(string.Format("X:{0}, Y:{1}", bc.size.x , bc.size.y));

}

出力結果X:1.12, Y:0.7637944

X:0.2860652, Y:0.3159404

Unity:コンポーネントの追加

先ほどのBoxCollider2Dを一旦削除して、動的に追加してみます。

gameObject.AddComponent<コンポーネント名>()

GameObject heri = GameObject.Find("Helicopter");

BoxCollider2D bc = heri.AddComponent<BoxCollider2D>();

Rect rt = new Rect(0f, -0.108f, 1.12f, 0.763f);

bc.center = new Vector2(rt.x, rt.y);

bc.size = new Vector2(rt.width, rt.height);

BoxCollider2D bc2 = heri.AddComponent<BoxCollider2D>();

Rect rt2 = new Rect(0f, 0.424f, 0.286f, 0.316f);

bc2.center = new Vector2(rt2.x, rt2.y);

bc2.size = new Vector2(rt2.width, rt2.height);

Unity:コンポーネントの削除

コンポーネントの削除はRemoveComponentではなく、 Destroyとなります。

Destroy(コンポーネント)

GameObject heri = GameObject.Find("Helicopter");

BoxCollider2D[] colliders = heri.transform.GetComponents<BoxCollider2D>();

foreach(BoxCollider2D bc in colliders) {

Destroy(bc);

}

通常は、対象のゲームオブジェクトごと削除するだろうから、コンポーネントを単体で削除するのは

あまり無いと思われる。

Unity:他スクリプトのアクセス(値参照/メソッド呼出)

FindObjectOfType<スクリプト>

FindObjectOfType関数は、対象コンポーネントが1つしか使用されないことが確定している場合に

使用する。

// スコアコンポーネントを取得してポイントを追加

FindObjectOfType<Score>().AddPoint(point);

GetComponent<スクリプト>

GetComponent関数は、特定のGameObject上のコンポーネント(スクリプト)にアクセスする場合に

使用する。※Find系関数は遅いため、Startメソッド内で予め変数にセットして使用するのが良い

static Enemy _enemy;

_enemy = GameObject.Find("Helicopter").GetComponent<Enemy>(); //Startメソッド内に記述

_enemy.AddPoint(point); //Updateメソッド内に記述

Unity:他スクリプトのアクセス(メソッド呼出)

SendMessage(“メソッド名”, 引数, SendMessageOptions)

※引数とSendMessageOptionsは省略可能

GameObjectに対して名前指定で処理を呼ぶ。引数を一つだけ持たせられる。

• 名前指定なので、ソースコードを変えずに呼び出し先を変えるのに使える。

但し、名前指定で渡すのでミスしててもコンパイルエラーにはならない。

• 返り値がもてない。

• SendMessageOptions.DontRequireReceiver を付けると、メソッド先が無くてもエラーにしない

• Privateなメソッドも実行出来る

【参照】

「宴」実装時に得られたUnityプログラムノウハウ

SendMessageに変わるExecuteEvents.Execute

Unity:他スクリプトのアクセス(メソッド呼出)

BroadcastMessage(“メソッド名”, 引数, SendMessageOptions)

GameObjectまたは全ての⼦GameObjectに対して名前指定で処理を呼ぶ。

引数を一つだけ持たせられる。

• オブジェクトのグループを作って、それらに対してまとめて実行させれる。

親要素のEnemyのBroadcastMessageでメソッドを呼ぶと

⼦要素のHelicopterやTankの同じメソッドが呼ばれる。

• 返り値がもてない。

• SendMessageOptions.DontRequireReceiver を付けると、メソッド先が無くてもエラーにしない

Invoke(“メソッド名”, 秒数)

GameObjectに対して指定した秒数後に名前指定で処理を呼ぶ。

• 引数を渡せない、返り値がもてない。

Unity:キー入力(キーボード)

キーが押されたかどうかを確認する(押している間は常に連射状態)

bool 変数 = Input.GetKey( キーの指定 ); 参照:KeyCode一覧

if (Input.GetKey(KeyCode.Space)) //スペースキー

if (Input.anyKey) //任意キー

キーが押されたか瞬間を確認する

bool 変数 = Input.GetKeyDown( キーの指定 );

キーが離された瞬間を確認する

bool 変数 = Input.GetKeyUp( キーの指定 );

Unity:キー入力(複数の入力端末:方向)

入力と言ってもキーボード、ジョイスティック、ゲームパッドなど複数あるため、共通化されてます。

参照: 2.3 プレイヤーを動かす準備 Rigidbody2Dと組み合わせて操作する。

Input.GetAxisRaw (一定の間隔で移動する場合)

// 水平方向の入力量(-1、0、1) 、垂直方向の入力量(-1、0、1)

float horizontal = Input.GetAxisRaw("Horizontal");

float vertical = Input.GetAxisRaw("Vertical");

Input.GetAxis (徐々に加速をしていく場合)

// 水平方向の入力量(-1~1)、垂直方向の入力量(-1~1)

float horizontal = Input.GetAxis("Horizontal");

float vertical = Input.GetAxis("Vertical");

対応するキーを押してもすぐには「-1」や「1」にはならず、徐々にその値に近づけるようになっている。

値 内容

0 左右どちらも入力されていない

-1 左が入力されている

1 右が入力されている

Unity:キー入力(複数の入力端末:方向)

Input.GetAxis("Horizontal"); など "Horizontal" は、[Edit]→ [ProjectSetting] →[Input]で確認ができます。

「Input.GetAxis」の場合、ボタンを押された際にスムージング計算された力が働きます。

スムージング計算

Gravity 何もボタンを押していない時の0に戻る力

Dead デッド・ゾーン。この部分は値が0

Sensitivity ボタンを押した時の加速度(「-1」「1」に向かっていく速度)

Snap 反対方向のボタンを押した時に、値を0にするかどうか

Unity:キー入力(複数の入力端末:ボタン)

ボタンが押されたかどうかを確認する(押している間は常に連射状態)

Input.GetButton( ボタン名の指定 );

例 Input.GetButton("Jump")

ボタンが押されたか瞬間を確認する

Input.GetButtonDown( ボタン名の指定 );

ボタンが離された瞬間を確認する

Input.GetButtonUp( ボタン名の指定 );

※ボタン名は大文字小文字を区別するので注意

不明な場合、[Edit]→ [ProjectSetting] →[Input]で内容を確認する。

ボタン名 内容

Jump スペースキー またはジョイスティックのボタン3

Fire1 左Ctrl または 左クリック またはジョイスティックのボタン0

Fire2 左Alt または 右クリック またはジョイスティックのボタン1

Fire3 左Cmd または 中央クリック またはジョイスティックのボタン2

Unity:マウス入力とマウス位置

ボタンが押されたかどうかを確認する(押している間は常に連射状態)

Input.GetMouseButton( ボタン番号 );

ボタンが押されたか瞬間を確認するInput.GetMouseButtonDown( ボタン番号 );

ボタンが離された瞬間を確認するInput.GetMouseButtonUp( ボタン番号 );

マウス位置を取得する//マウス位置、スクリーン座標(左上原点)を返す

Vector3 mousePosition = Input.mousePosition;

//スクリーン座標からワールド座標に変換する

Vector3 position = Camera.main.ScreenToWorldPoint (mousePosition);

ボタン番号 内容

0 左ボタン

1 右ボタン

2 中ボタン

Unity:マウス入力とマウス位置

マウス位置のオブジェクトを取得する。

オブジェクトにはコライダーを付けておく必要がある。参照:Physics2D.OverlapPoint

void Update()

{

if (Input.GetMouseButtonDown(0)) {

Vector3 tapPoint = Camera.main.ScreenToWorldPoint(Input.mousePosition);

Collider2D collider = Physics2D.OverlapPoint(tapPoint); //コライダーが空間上の点を含むか判定

if (collider) {

GameObject obj = collider.transform.gameObject;

Debug.Log(obj.name);

}

}

Unity:タッチ入力とタッチ位置 タッチ数を検知

Input.touchCount

現在、同時にタッチされている数を返す。

タッチ情報の取得

Input.GetTouch(index) indexは、0~Input.touchCount-1

タッチした時の状態を扱う構造体を返す。(Touch構造体)

タッチ状態

Input.GetTouch(0).phase

タッチID(指:ポインター)

Input.GetTouch(0). fingerId

タッチ座標(スクリーン座標)

Input.GetTouch(0).position

タッチの状態 内容

TouchPhase.Began タッチした瞬間

TouchPhase.Moved タッチしたまま移動中

TouchPhase.Stationary タッチしているが移動していない

TouchPhase.Ended タッチから離れた

TouchPhase.Canceled タッチのトラッキングがキャンセル

1本指操作ならタッチ入力を使わなくても、マウス入力(左ボタン)で対応可能

タッチパネルPCでも、Input.touchCountは「0」を返す

Blender:パンダのモデリング

初心者のための!作って学ぶBlenderの基礎:②モデリング - 日本VTR実験室

http://nvtrlab.jp/column/2-2

テクスチャは次回予定なので、今回はモデリングまで。

セッション時間内(90分)でモデリングを完成させるのは無理と判断、ポイントだけ覚えてね。

今回の学ぶポイント

・画面の3分割による操作・ガイド絵による操作・オブジェクトごとレイヤーを分ける・ミラー、ループカット・押し出し・移動、拡大縮小による調整・面を閉じる

Blender:パンダのモデリングパンダのガイド絵を下記からダウンロードします。

http://nvtrlab.jp/wp-content/uploads/2013/10/panda_img.zip

パンダモデリング資料

https://github.com/yaju/ShizuDev_UnityAndBlender/tree/master/Session7

panda2.blend 囲み後(四角)

panda3.blend 囲み後(丸く)

panda4.blend パンダフォルム肩まで作成panda5.blend パンダフォルム肩まで作成(上側選択)

panda6.blend パンダフォルム頭まで作成panda7.blend 上側のみパンダフォルム調整panda8.blend パンダフォルム下側作成panda9.blend ポリゴン表示を滑らかpanda10.blend 股追加panda11.blend 股完成panda12.blend 腕作成準備(穴あけ前)

panda13.blend 腕作成

panda14.blend 腕作成(肘まで)

panda15.blend 腕完成panda16.blend 足作成前panda17.blend 足作成panda18.blend 足完成panda19.blend パンダフォルム調整panda20.blend 耳作成準備(穴あけ前)

panda21.blend 耳作成panda22.blend 耳完成panda23.blend 細分化panda24.blend 尻尾作成準備(穴あけ前)

panda25.blend 尻尾作成panda26.blend パンダ完成

Blender:基本操作 拡大・縮小

マウスのホイールで3Dモデルの拡大・縮小

視点の向きの変更

マウスのホイールを押しながらマウス移動

視点の移動

SHIFTキーとマウスのホイールを押しながらマウス移動

前後不覚になった場合

Homeキーを押す、または下側にある「ビュー」メニューから「全てを表示」を

選択すると3Dビューウィンドウに丁度すべてが収まるように視点が移動される。

履歴の参照とアンドゥで元に戻す

左側パネルの履歴にて

元に戻す(Ctrl + Z)

やり直す(Shift Ctrl + Z)

Blender:モデリングの下準備

画面の分割

3分割にします。3DViewの右上または左下にある三角っぽいタブにカーソルを近づけると、

カーソルが+に変わります。そのままドラッグすれば分割できます。

Blender:モデリングの下準備

画面の結合

結合するには、くっつけたい方向にドラッグすると色も変わり大きい矢印が出てきたらボタンを離します。

※大きいウインドウから小さいウインドウへは結合できません。

大きい矢印が薄く表示される。

Blender:モデリングの下準備

ガイド絵を置こう

パンダのガイド絵を下記からダウンロードします。

http://nvtrlab.jp/wp-content/uploads/2013/10/panda_img.zip

3DView上で「N」キーを押すと「プロパティパネル」が出てくるので、

下絵にチェックを入れて、画像を追加ボタンを押します。正面と横それぞれに対して行います。

Blender:モデリングの下準備

ガイド絵(正面)に下絵として「panda_img/panda_front.png」を指定します。

ガイド絵(横)に下絵として「panda_img/panda_side.png 」を指定します。

ビュー 前フロント・平行投影

ビュー 右ライト・平行投影

ビュー 上トップ・透視投影

視点が「平行投影」の時のみ下絵が表示される。

Blender:モデリングの下準備

オブジェクトごとにレイヤーを分ける

カメラとライトを別レイヤーに分けます。

別レイヤーに移動させたいオブジェクトを右クリックで選択。

「M」キーを押すと下図が表示されるので、現在のレイヤー以外をクリックすれば移動は完了です。

参考:ライトとカメラの移動

画面下部にレイヤーが分かれた物体が表示される。今回は、ライトを2番目、カメラを3番目とした。

Blender:モデリングの下準備

Mirror Modifierの設定

パンダが左右対称なので半分だけ作成して鏡像は自動作成させる「ミラー」を使います。

1.最初の立方体オブジェクトを削除(「X」キーを押す)します。

2.「Shift + C」キーで3Dカーソルを中心に持ってくる。

3.「Shift + A」で新たなオブジェクトを追加、メッシュの平面を選択します。

Blender:モデリングの下準備

回転を使います。「R」→「X」→「90」を入力→Enterを押す。

回転(Rotate)→ X軸方向に固定→回転する角度を入力で確定。

Blender:モデリングの下準備

ループカットを使います。「Tab」キーで「オブジェクトモード」から「編集モード」にしておきます。

「ctrl + R」を押すとピンクのラインが出てくるので、縦方向のラインになったらEnterを押します。

さらに何も動かさないで、もう一度Enterを押して下さい。

Blender:モデリングの下準備

左半分を削除します。

選択モードが「点選択モード」であることを確認します。

右クリックで左側の頂点を選択。「X」キーで消去します。消去対象は「頂点」を選んで下さい。

いっぺんに消えない人は残りの頂点を選択して、ともかく左半分を削除してください。

選択モードが点選択c

左半分を削除する。

Blender:モデリングの下準備

「N」キーでプロパティパネルが出てくるので、トランスフォームの中点 Xを「0.5」に正しておく。

「A」キーでオブジェクトを全選択します。

プロパティウインドウのヘッダーでレンチのマークを選択し、追加でミラーを選択します。

ミラー接合面が裂けないように、クリッピングにチェックを付けます。

Blender:モデリング(胴体の元)

右向き画面にて、「A」キーでオブジェクトを全選択して、おなかの前まで動かします。

動かすには「G」キーで任意の場所までマウスを動かし、左クリックで確定です。

ちなみに「G」キーを押した後に「Y」キーを押すと方向が固定されます。

Blender:モデリング(胴体の元)

「B」キーで矩形範囲選択になるので、右側の頂点を2つマウスで囲って選択する。

ここらへんで一旦、ファイル保存しておきましょう。※アンドゥで戻せないなら保存ファイルから戻す。

Blender:モデリング(胴体の元)

押し出しで面を増やしていきます。※操作を元に戻したい場合、「Ctrl + Z」を押します。

先に辺選択にします。押し出しは「E」キー。頂点か辺のみ、押し出すことができます。

マウスを動かした方向に押し出されて、左クリックで確定です。

ちなみに「E」キーを押した後に「X」キーを押すと方向が固定されます。

ガイド絵の端っこまで、三回ほど押し出します。間隔の幅を変更したい場合、「G」キーで移動。

Blender:モデリング(胴体の元)

囲みを作成します。

右向きの画面(ライト・平行投影)にて「E」キーで押し出します。

その際に方向固定するために「Y」キーを押します。

4回押し出して、パンダの右端まで作成してください。間隔の幅を変更したい場合、「G」キーで移動。

最初から丸く囲みたいところですが、慣れてないと難しい。

上からのビュー画面

Blender:モデリング(胴体の元)

ビューの「フロント・平行投影」を、「バック・平行投影」に変更してください。

前方向で作成した面が表示されているので、同じ間隔で「E」キーで押し出しを3回行います。

その際に方向固定するために「X」キーを押します。

囲みが作成できました。一旦、ファイル保存しましょう。

上からのビュー画面

Blender:モデリング(胴体の元)

囲みを丸くしていきます。

ビューの「バック・平行投影」を、「フロント・平行投影」に戻します。

ビューの「ライト・平行投影」を、マウスホイールを押しながら視点をクォータービューにします。

右側の右から2番目を選択します。「G」キーで押した後に方向固定で「X」キーを押します。

3辺に対して同じことを繰り返します。※方向固定をしないと、変な方向に歪んで作成されてしまいます。

上からのビュー画面

Blender:モデリング(胴体の元)

囲みを丸くしていきます。

ビューをやりやすい方向に変更して、前と後に対しても丸く調整していきます。

「G」キーで押した後に方向固定で「Y」キーを押します。

※方向固定をしないと、変な方向に歪んで作成されてしまいます。

上からのビュー画面理想な形にするには訓練が必要ですな。幅を等間隔にするとか

Blender:モデリング(ひょうたん的なフォルム)

胴体の元ができたところで、パンダのひょうたん的なフォルムに近づけていきます。

とりあえず直線すぎるので「Ctrl + R」キーでループカットを追加します。横のループカット。

Blender:モデリング(ひょうたん的なフォルム)

拡大/縮小(Scale)を使ってポリゴンを体に沿わせていきたいのですが、デフォルトで拡大縮小すると

選択範囲の中心に向かって縮小されるため、基準点(ピボット)を任意に指定できるようにします。

ヘッダーでPivot Pointを3Dカーソルに設定。3Dカーソルを上から見てパンダの中心、横から見て選択している辺に合わせます。「Shift + C」キーで3Dカーソルが中心に設定されます。(また、左クリックするとその場所に

3Dカーソルが設定されます)

Blender:モデリング(ひょうたん的なフォルム)

トップ・透視投影にて、「A」キーで全選択します。

フロントのパンダを見ながら、「S」キーで拡大/縮小(Scale)します。

Blender:モデリング(ひょうたん的なフォルム)

拡大/縮小した際に、穴が空いてしまいました。ミラー時の中点が「0」になってなかったようです。

フロント側で「B」キーで先端部分を選択します。「G」キーでX軸方向に移動させてくっつけてください。

再度、「A」キーで全選択し「S」キーで拡大/縮小してくっついているのを確認するといいです。

穴が空いてる

Blender:モデリング(ひょうたん的なフォルム)

「A」キーで全選択を解除してください。

「隠面処理」ボタンをオンにして置いてください。オン:白っぽくなる / オフ:暗くなる

「B」キーで箱選択になります。マウスで範囲をドラッグして、一番上側の辺を選択してください。

隠面処理ボタンがオフだと半分くらいしか選択されない。

Blender:モデリング(ひょうたん的なフォルム)

フロント・平行投影にて、「E」キーで押し出します。この際パンダの形に合わせて、斜め上側にします。

だたし、このままだとライト・平行投影では、垂直になっているので、右側もパンダの形に合わせます。

ピボットポイントは、3Dカーソルから中点に戻してください。

フロント・平行投影にて、「S」キーで拡大縮小します。

この際に、ライト・平行投影を見ながら調整してください。

Blender:モデリング(ひょうたん的なフォルム)

これを繰り返して、パンダのフォルムに合わせてください。

Blender:モデリング(ひょうたん的なフォルム)

頭の天辺までやったところで、「Z」キーを押してください。

「Z」キーで、ワイヤーフレームモードとソリッドモードを交互に切り替えれます。

そうすると、パンダのフォルムに合っていない部分が見えてきます。

※頭の天辺は後で閉じるので穴が空いたままにしてください。

Blender:モデリング(ひょうたん的なフォルム)

パンダのフォルムに合っていない部分を「B」キーで範囲選択します。

「G」キーでパンダのフォルムに合わせていきます。これを繰り返します。

※余分な部分が範囲選択されていたら「A」キーで選択解除して、再度「B」キーで範囲選択します。

Blender:モデリング(ひょうたん的なフォルム)

フロント・平行投影では、パンダのフォルムに合うのですが、ライト・平行投影では変化がありません。

今度は、ライト・平行投影にて「Z」キーでワイヤーフレームに切り替えます。

パンダのフォルムに合っていない部分を「B」キーで範囲選択します。

拡大縮小の「S」キーと移動の「G」キーでパンダのフォルムに合わせていきます。これを繰り返します。

※余分な部分が範囲選択されていたら「A」キーで選択解除して、再度「B」キーで範囲選択します。

※フロント・平行投影側にも変化が起きるので、移動の「G」キーで双方を見ながら調整してください。

Blender:モデリング(ひょうたん的なフォルム)

下側まで作成かつ調整した段階

Blender:モデリング(ひょうたん的なフォルム)

ツールシェルフの「シェーディング/UV」の面を「スムーズ」でポリゴン表示が若干滑らかになります。

Blender:モデリング(股の作成)

まずは足まわりから。股を前と後ろでつなぐため、「E」キーで押し出しで面を作っていきます。

Y軸方向に固定して2つ面押し出し、少しだけ丸みがかかるように「G」キーで移動させて調整します。

そのまま押し出して前と後ろがくっつけばいいのですが、面一つ分くらい残してストップします。

押し出し面と面にしたい部分の頂点を選択、選択する際には「Shift + 右クリック」する。

選択後に「F」キーを押すと、面を張ってくれます。

Blender:モデリング(腕の作成)

面選択モードにして、腕が生えてきそうな場所を選択して「X」キーで面を削除します。

Blender:モデリング(腕の作成)

辺選択にして、消去した部分の周辺を選択(Shift + 右クリック)します。

「E」キーで押し出し、腕を作ります。

関節になりそうな部分は、後々動きの設定がしやすいように分割しておきます。

関節ごとに拡大縮小の「S」キーと移動の「G」キーと回転の「R」キーでガイド絵に合わせて調整します。

先端は周辺を選択(Shift + 右クリック)した後に「F」キーで押すことで閉じます。

Blender:モデリング(足の作成)

足を作成する周辺を選択(Shift + 右クリック)します。

足も腕と同様に「E」キーで押し出し。「F」キーで足裏を閉じます。

関節ごとに拡大縮小の「S」キーと移動の「G」キーと回転の「R」キーでガイド絵に合わせて調整します。

Blender:モデリング(足の作成)

パンダの足のフォルムに合っていない部分を選択して、拡大縮小の「S」キーと移動の「G」キーと

回転の「R」キーでガイド絵に合わせて調整します。

ライト・平行投影とフロント・平行投影側ともにガイド絵に合わせていきます。

Blender:モデリング(顔の凸凹調整)

顔は凹凸を作るためにEdge loopを追加。横や上から見て違和感がないように動かしていきます。

「Ctrl + R」キーでループカットして調整していく。

Edge loopの選択は、「Shift + Alt + 右クリック」またはメニューの選択→ループ辺で行える。

Blender:モデリング(顔の凸凹調整)

頂点選択にします。

ライト・平行投影にて、「B」キーで箱選択で顔の先端を選択する。

「G」キーで移動して、顔の形に合わせる。

Blender:モデリング(顔の凸凹調整)

ライト・平行投影にて、「G」キーで鼻まで移動する。

トップ・透視投影にて、「R」キーで回転させて右上斜めにして出すぎた部分を「G」キー移動させる。

ライト・平行投影にて、「B」キーで箱選択で首部分を選択し「G」キー移動後、1頂点ずつ更に移動。

Blender:モデリング(顔の凸凹調整)

ライト・平行投影にて、「Ctrl + R」キーでループカットして鼻の付け根に移動させる。

「B」キーの箱選択にて鼻の付け根部分のみを選択して「G」キーで移動させる。

調整したい各頂点を「G」キーで移動させて、パンダの顔の凸凹を調整する。

Blender:モデリング(耳の追加)

耳も腕と同じように、不要な面を「X」キー削除し「E」キー押し出します。「F」キーで面を閉じる。

パンダの耳のフォルムに合っていない部分を選択して、移動の「G」キーや回転の「R」キーで

各画面を見ながらガイド絵に合わせて調整します。

Blender:モデリング(細分割曲面 Subdivision Surface)

オブジェクトを「A」キーで全選択して、プロパティパネルで追加→細分割曲面を選びます。

このモディファイアは、面を自動計算して分割してくれます。

Subdivisionsの値が大きいほど分割されて滑らかになります。今回はビューもレンダーも「2」にします。

Blender:モデリング(しっぽの作成)

作り方は、腕や耳と一緒です。

「Ctrl + R」キーでループカットして面を細かくしてから「E」キーで押し出します。

さらに、胴体との境目をシャープにしたいので、エッジループを追加。

境目にしたい辺に限りなく近づけると、辺がくっきりします。

Blender:モデリング(頭を閉じる)

頭を閉じます。

なるべく四角形の面で構成できるように、頂点を押し出したりして面を張っていきます。

※もはや、見本のように出来ませんでした。というか全然違うんだよな、むずかしいな。

Blender:モデリング(仕上げ)

適宜ループを追加したり移動したりして、不自然なところや細かい部分を調整していきます。

資料元サイト(http://nvtrlab.jp/column/2-2)のようにするには、何回か作るなど慣れが必要と思いました。

こうして見比べると、体の丸みが見本と違います。最初の段階でもっと丸く作成する必要がありそうです。

ご清聴ありがとうございました!