「メディア工学特別講義A」カテゴリーアーカイブ

TPSキャラクターをタップした先に移動(2)

AIThirPersonContorollerを配置

  1. 次にステージ上にAIThirPersonContoroller(Asset内のStandardAssets>Characters>ThirdPersonCharacter>Prefab内にある)をPlane上にドロップする
  2. 次にMultiPuroposeCameraRig(Asset内のStandardAssets>Camera>Prefabsにある)をHierarchyにドロップする
  3. Hierarchyに入れたMultiPuroposeCameraRigを選択し,InspectorのTargetにAIThirPersonContorollerをドロップする(TargetがAIThirPersonContorollerになる)
  4. HierarchyにあるMainCameraを削除する
  5. 再生して確認してみる

Assetのダウンロード

  1. タップ移動用のキットをここからダウンロードし,UnityのImportPackage>CustomPackageで読み込む

TapMoveAssetの使用

  1. Asset内にTapMoveフォルダが作成されているので,その中のredCircleを選択する
  2. (この項目は設定済みの場合は飛ばしてください)InspectorでAlpha Source(アルファ=透明チャンネルのこと)でInput Texture Alphaを選択し,Applyをクリックする.これでテクスチャの透明が使用できるようになる
  3. HierarchyのAIThirPersonContorollerにTapMoveフォルダ何のtapMove.csを追加する
  4. 追加されたスクリプトのCirclePreにCirclePrefabをアサインし,RayLengthに10〜50の数字を入力する
  5. ゲームを再生し確認すること

参考動画

もどる

TPSキャラクターをタップした先に移動(1)

ワークフロー

  1. AIの利用について
  2. TPSのAIつきキャラクターを配置する
  3. マルチカメラを配置し,ターゲットにTPSのキャラクターを設定する
  4. MainCameraを削除する
  5. カスタムパッケージを読み込む
  6. tapMoveスクリプトをAIThiredPersonControllerにアサインする

という流れです

AIの利用について

  • ここでいう「AI」は最近流行の機械学習などではなく,経路探索アルゴリズムA*を利用する
  • UnityではAIの歩行可能な場所とそうでない場所とを判定させるNavMeshという機能を用いる

NavMeshの設定について

  1. NavMeshの理解のために,テストシーンを作成し確認してみる
  2. まずPlaneを配置し,その上にCubeなどを障害物として置く
  3. Window>Navigation を選択しNavigationの設定を呼び出す
  4. Planeを選択し,次にNavigationのObjectタブを選択し,Navigation Staticにチェックを入れ,Navigation AreaはWalkableにする
  5. この設定は,このPlaneが歩行可能にするという意味
  6. 次に障害物(Cubeなど)を選択し,Navigation Staticにチェックを入れ,Navigation AreaはNot Walkableにする
  7. これでCubeは歩けないエリアとなる
  8. 全てのオブジェクトの設定が終わったら,NavigationBakeのタブを選択し,Bakeボタンをクリックする
  9. これでNavMeshの準備は完了

(次:AIキャラクターを配置

 

タップした位置に移動

動画のような動きを実現します

2017/11/02更新,次々タップしてゴールをどんどん変更できるようにした

ここからダウンロードできます

using UnityEngine;
using System.Collections;
using System;

public class tapMove : MonoBehaviour {

	//AIの変数用
	UnityEngine.AI.NavMeshAgent agent;

	//hit情報(タップ先)情報の格納用
	RaycastHit hit;

	//タップ用レイの準備
	Ray ray;

	[SerializeField, HeaderAttribute ("circlePrefabをここにアサイン")]
	public GameObject circlePre;

	//レイヤーマスクでタップを無視するレイヤー設定用
	LayerMask mylayerMask;

	//アニメーターの変数用
	Animator animator;


	//初期化
	void Start () {
		agent = GetComponent<UnityEngine.AI.NavMeshAgent>();//AIをこのスクリプトがあるゲームオブジェクトから探す
		animator = GetComponent<Animator> ();//このゲームオブジェクトからアニメーターを探す
		int layerMask = LayerMask.GetMask(new string[] {"Default"});//レイヤーマスクの設定
		mylayerMask = layerMask;//これ何だっけ?
	}


	//毎回処理します
	void Update () {

	
			// 左クリックしたときに、
			if (Input.GetMouseButtonDown (0)) {
				// マウスの位置からRayを発射して、
				ray = Camera.main.ScreenPointToRay (Input.mousePosition);
				// 物体にあたったら、
			if (Physics.Raycast (ray, out hit, 30f, mylayerMask)) {
					// その場所に、Nav Mesh Agentをアタッチしたオブジェクトを移動させる
					agent.SetDestination (hit.point);

					// "Run"アニメーションに遷移
					//	animator.SetBool ("Wak", true);
					spawnPrefab ();
				}       


		}
		// 目的地とプレイヤーとの距離が1以下になったら、
		if (Vector3.Distance(hit.point, transform.position) < 1.0f) {

		}
	}

	//ターゲットマーカーを表示
	void spawnPrefab(){

		string delPreviusGO = circlePre.name;//Prefabの名前を取得する
		try{
		GameObject deathObj =  GameObject.Find (delPreviusGO);//Prefab名のゲームオブジェクトがあったら
			Destroy(deathObj);//それを消す=つまり目的地に到着前にタップして行き先を変更した時用
		}catch(NullReferenceException e) {//見つからなかった時の処理(特に何もしていない)
			Debug.Log ("noGO");
		}

		Vector3 mypos ;//行き先の座標設定用
		float myPosy = hit.point.y + 0.1f;//若干高い位置にマーカー表示させる
		mypos = new Vector3 (hit.point.x, myPosy, hit.point.z);//Yだけ少し高くした座標作成
		GameObject circleGO = Instantiate(circlePre, mypos, circlePre.transform.rotation) as GameObject;//Prefabを生成する
		circleGO.name = circlePre.name;//Prefabでなくゲームオブジェクトにして名前を設定する(上で名前で検索して消すために設定いている)

	}


}

元ネタはこちらのスクリプトを参考にしています

また,Prefab側にはこのスクリプトをあてて,IsTriggerをONにしています

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

public class triggerDestroy : MonoBehaviour {
	Animation anim;
	bool animCheck;
	// Use this for initialization
	void Start () {
		anim = gameObject.GetComponent<Animation> ();
		animCheck = false;//noPlayning
	}
	
	// Update is called once per frame
	void Update () {
		if (animCheck) {
			if (!anim.isPlaying) {
				print ("end");
				animCheck = false;//noanimPlay
				Destroy(this.gameObject);
			}
		}
		
	}
	void OnTriggerEnter(Collider other) {
		//Debug.Log ("ON");
		if (!anim.isPlaying) {
		anim.Play ();
		animCheck = true;//Playing
		}

	}


}

ターゲットの赤い輪はPhotoshopで作成し,Planeにテクスチャで貼付

パッケージのダウンロード(ここ

Unity入門-15

条件分岐に応じてリザルトを変更する

 

復習

  • シーン遷移のスクリプトはどこ?
  • シーン遷移するためには,build setupにシーンを入れておく
  • invokeは「ちょいまち」のこと

条件分岐

  • 経由地を全て取得=endシーンへ(現在の設定)
  • タイムアップ=新たに作成するタイムアップシーンへ

概要

  1. endを複製し名前と画像を変更
  2. タイムアップをしたら,タイムアップシーンへと飛ばす
  3. 実行して確認

編集する

badendシーンを作成する
  1. edukit内のend選択
  2. Edit>Duplicate で複製する
    enddup
  3. ファイル名がend 1になるので,名前を badend に変更
    badend
画像を入れ替える
  1. badendをダブルクリックして開く
  2. Hiearchy>Canvas>Imageを選択する
    canvas
  3. インスペクタのSource Imageで画像を「ざんねんでした」と表示されている画像(ui_1)にする
    imageswap
  4. リザルトは不要なので,Hierarchy>Canvas>timemat を選択し,右クリック>Delete で削除する
    timedelete
ビルドシーンに加える
  1. このシーンをビルドする対象に含める(でないとシーン遷移できない)
  2. (下図の1)File>Build Settings… を開く
  3. (下図の2)Add Open Sceneをクリックする
  4. (下図の3)badendが追加されたか確認して閉じるbuildscn
スクリプトを変更する1
  1. edukit>myQuest をエディタで開く
  2. 以下のスクリプトを追加する.どこに追加するとよいか?(これは滑り台のように1回実行されるもの)
	void gobadEnd(){
		SceneManager.LoadScene ("badend");//go badend scene
	}
スクリプトを変更する2
  • 同じくmyQuestを開き,タイムアップした箇所で上記の関数を実行するにはどうすればよいか?(ヒントは上の復習コーナーに)
その他
  • 自習用のサンプルなどを例示する

Unity入門-14

 

0秒になったら終了させる

  • まずは時間がゼロ秒になったら終わらせるプログラムを入れる場所を探す
  • 探し方概要
    • 概要:いろいろな場所にDebug.Log(“koko”);を書いて,1秒毎に変化する場所を探す
    • 入力した行が実行された時にコンソールkokoと表示される
    • コンソールのCollapseはオフにする(Collapse同じ内容は1行だけ表示する機能をオフにする)
  • 実際の探し方
    • 各関数{}内の最初か終わり近くにDebug.Log(“koko”);を記入
    • 保存→ゲーム実行→Consoleを確認 を繰り返し,秒変化と同じタイミングで現れる箇所を探す
    • ヒント:秒の変化と音(Tick)の再生は同期しているので,そのあたり,,,?
  • その場所に以下のプログラムを入力する
			if (gameTime < 1) {
				//go end
				StopCoroutine("loop");
				Invoke("goEnd", 2f);
			}

変数 gameTimeが1未満になったら,時間制御をしているコルーチンloopを止めて,goEndの関数を2秒後に実行 の意味

こちらは2秒後に実行されるので,少し余裕が持てます.goEndを実行するタイミング(ここでは2秒)を変えて自分が最も良いと思う時間に変更してみましょう

なお2fのfはfloat(実数)の意味です.Unity上では実数の場合はfとつけるルールがあります

 

これでもいけます

if (gameTime < 1) {
goEnd();
			}

変数gameTimegが1未満になったらgoEnd関数を実行(こっちは2秒待たないのでせわしない)

 

シーンチェンジはどこ?

  • シーン「end」にジャンプする関数 void ○○○(){   } はどこか再度確認すること(上でに書いてありますが)

変数宣言をいろいろ試す

  • 変数宣言をする場所がある
    • グローバル編
      • プログラムのどこでも使えるけど,このプログラム外では使えない
      • publicをつけるとインスペクタで設定できる.インスペクタで設定した値は,プログラムで宣言した値よりも優先される(プログラム側で変更はできる)
      • privateをつけるか何もつけない変数宣言はインスペクタには表示されない
    • ローカル編
      • 関数内で使える(ちょいとメモ代わりや,変数の受け口に使える)
    • 神編
      • staticは神
      • public static int mikeneko; と宣言すると,スクリプト名.mikeneko でゲーム内のどこでも使える
      • ただし関数などによってはstaticを嫌うものがあるので,その際はnamespaceなどを使ってusingする

Unity入門-13 スクリプトmyQuestをざっくり対訳してみる

対訳してみました.

元のスクリプトは

using UnityEngine;
using System.Collections;
using UnityEngine.UI;//UIを使用するのに必要
using UnityEngine.SceneManagement;//シーン遷移をするのに必要

public class myQuest : MonoBehaviour {

	[SerializeField, HeaderAttribute ("経由地の数")]
	public int points;

	//sound
	[SerializeField, HeaderAttribute ("効果音をドロップ")]
	public AudioClip getSound;
	public AudioClip tickSE;


	//default time
	[SerializeField, HeaderAttribute ("カウントダウンの初期時間")]
	public int gameTime;
	[SerializeField, HeaderAttribute ("残時間のUItextをドロップ")]
	public Text timeText;
	[SerializeField, HeaderAttribute ("残りの経由地数表示UIをドロップ")]
	public Text nokoriText;

	[SerializeField, HeaderAttribute ("エンディングのシーン名を入力")]
	public string endSceneName;

	//audio source get from this script attached
	AudioSource myaudio;


	// Use this for initialization
	void Start () {

		myaudio =	GetComponent<AudioSource>();
		timeText.text = "残り時間" + gameTime.ToString () + "秒";
		nokoriText.text = "のこり" + points.ToString () + "個";
		//start after nSec
		Invoke("timeCount", 3.5f);
	}
		
	void Update () {
	
	}


	//if hit destroy obj
	void RedirectedOnTriggerEnter(Collider mycollider) {
		//Debug.Log (mycollider.gameObject.tag);
		if (mycollider.gameObject.tag == "pointObj") {
			points--;
			nokoriText.text = "のこり" + points.ToString () + "個";
			myaudio.PlayOneShot (getSound, 0.7F);
			Destroy (mycollider.gameObject);

			if (points < 1) {
				//go end
				StopCoroutine("loop");
				Invoke("goEnd", 2f);
			}
		}
	}

	void timeCount(){
		StartCoroutine("loop");
	}

 

対訳はこちら(画像:クリックで拡大)

%e5%af%be%e8%a8%b3%e3%81%97%e3%81%a6%e3%81%bf%e3%82%88%e3%81%86

次へ

Unity入門-11 ゲーム化の準備(2)

効果音をアサインする

  1. scriptへのパーツを登録を続けます
  2. edukitの中のseフォルダを開く
  3. lupse2を効果音のGetSound
  4. tickを効果音のTick SEsound
    (※画像では”script”となるべき部分がgamecontrollerになっています)
  5. 次に音再生の準備をする
  6. scriptを選択Component>Audio>Audio Source を選択する
  7. scriptAudio Sourceが追加されます
  8. Audio Sourceは音再生装置と考えてくださ
  9. これでscriptの準備は終わり

FPSControllerへスクリプトを追加する

  1. カメラのついているFPSControllerは,衝突判定にも使用できる
  2. edukitの中のhitObjSenderInspectorにアサインする
  3. hitinfo
  4. このスクリプトはプレーヤー(つまりFPSController)に,当たったColliderが何なのかを,scriptに送るプログラムである
  5. ゲームを再生し,チェックポイントへ向かい,音が出るか,時間表示は適切かなどを確認する

シーン遷移の準備

  1. File>Build Settings… を選択
  2. Add open sceneをクリックし,ビルドするシーンの一覧に現在のシーンを追加するbuidset1
  3. edukit内のopとendも追加し,edu/opのシーンが最上段にくるようにドラッグして移動させるbuild2
  4. BuildeSettingsのウインドウを閉じたあと,シーンを保存しておく

シーン遷移情報の設定

  1. edukit内opを開く
  2. opScriptを選択し,メインシーン名にゲームのメインシーン名を入力する(各自異なるので注意.画像例は02)opscne
  3. 保存し,ゲームを再生してみる
  4. ゲームスタートボタンが押せない場合、Canvas展開しsetsumeiPanelオフにするか(説明文が書けない),setsumeiPanelとその子のsetumeiのRaycast Targetをオフにする(パネルを残してマウスクリックを無視する設定)

レベル調整

  1. 何度かテストプレイし遊びやすく調整する
  2. また,プレイしたことのない人に遊んでもらい,難易度を調整すること
  3. 特に容赦のない他人や子供はバグ取りに最適です
  4. オープニング→ゲーム→エンディング と1周する状態になりバグも出ないようであればビルドを実行する
  5. File>Build SettingsでBuild Settingsの画面を開き,Buildをクリックする
  6. 保存先を設定すればビルドが開始される
  7. ビルドが成功したら,早速アプリを実行してバグがないか確認すること

提出について

  1. ビルド後,Mac版は1つのファイルが作成されるが,Windows版はアプリとそれが使用するファイルの入ったフォルダが作成される
  2. 提出時の際Windows版はアプリとそれが使うフォルダの2つを提出する必要があるので注意

おかしいとおもったら

  • なにかひっかかる感じがする>自分の位置を示すマーカー(白い球)が木などにひっかかっているかも.球のColliderのチェックを外すか,Yの値を上げてみる
  • 歩く音が出なくなった>マップビュー用のカメラにもAudioListnerがついているのが原因,歯車のアイコンでRemove Componentしましょう
  • 時間が0秒以下になってもゲームできてしまう>仕様です 時間に余裕をみてこのバグを取る回を設けたいと考えています

次へ

Unity入門-10 ゲーム化の準備(1)

概略

  • 複数シーンをまたぐゲーム化をおこなう
  • 今回は作成手順を理解するため,スクリプトは提供されたものを使用する

準備・パッケージの読み込み

  1. スクリプトなどが入ったedukit2.unitypackageをダウンロードする(ダウンロードはここから)
  2. Assets>ImportPackage>Custom Package… でダウンロードしたパッケージを読み込むimport
  3. Asset内にedukitというフォルダが作成される

タグの準備

  1. チェックポイント(今回はCube)を作成しフィールド内に配置する
  2. チェックポイントを選択し,InpsectorのタグからAdd Tagを選択
    addtag1
  3. タグとしてpointObjを追加するaddtag2

チェックポイントの準備

  1. 再度Isnpectorに戻り,作成されたタグpointObjを選択する
  2. BoxColliderIsTriggerにチェックを入れる(衝突しないですり抜けられるようになるり,トリガー=きっかけ を発生させることができる)
  3. マップビュー用のマーカーをチェックポイントの子にする
    cubeedit
  4. これを4回繰り返し,チェックポイント5個作成する (ただし以後は1個のみで説明しています)

スクリプトを空のオブジェクトに配置する

  1. Gameobject>Create Empty で空のオブジェクトを作成し名前をscriptにする
  2. Asset内にある(さきほど読み込んだ)Edukit>myQuest をscriptにアサインするaddscript
    (※画像ではscriptであるべき部分がgamecontrollerになっています)

解説

  1. GameObjectにどんなコンポーネントをアサインするかでそのオブジェクトの性質が変わる.カメラコンポーネントをアサインすればカメラに,Meshfilterをアサインすれば3Dオブジェクトに,今回はスクリプトだけをアサインしている
  2. この「空のオブジェクト」は便利な入れ物と考えてよい.CGソフトではNullや,ロケーターと呼ばれている

UIprefabを配置する

  1. Asset内にある(さきほど読み込んだ)Edukitの中のUIprefabをヒエラルキーにドロップします
  2. ゲーム画面にするとUI(ユーザインタフェイス)が表示されます
  3. このPrefabの中身は次の項目で使用します
  4. なおUIについてはこちらも参考に

スクリプトにパーツをアサインする

  1. 再びscriptを選択し,Inspectorに以下のような入力をする
  2. 経由地の数は5(半角)
  3. カウントダウンの初期時間は180(半角)
  4. UIprefabを展開し,対応するパーツ(下図参照)をアサイン
  5. エンディングのシーンにend(半角)と入力partsassign
    (※画像では”script”であるべき部分がgamecontrollerになっています)

つぎへ

 

 

Unity入門-9 ゲームフィールドの改善

コライダの設置

  1. 現在は地形の端に行くと世界の果てに落下するが,これを防ぐために衝突判定用のオブジェクト=コライダを設置し,それ以上先に進めないようにする
  2. まずCubeを作成し,Terrainを囲むように設置する
  3. 囲んだCubeのMeshRendererをオフにする。これで面が表示されなくなる
  4. この時,Box Colliderはそのままにする(これがコライダ)collider
  5. 緑の線(コライダ)だけが残るcollider2
  6. このコライダは衝突判定のための形状で,同様にカメラが搭載されているFPSControllerもColliderを持っているため互いにぶつかってこの壁を通れない
  7. これを応用すれば水の上も歩行可能になる
  8. 実際には地平線の先が見えないように,周囲は山を高くするなどしてユーザが没入できるような工夫が必要

カメラの視程を調整

  1. カメラの視程を調整し余計な部分が見えにくいようにする
  2. ゲーム画面のカメラはFirstPesonControllerにアタッチされている
  3. CameraのClipping PlanesのFarを100程度にする
  4. これで最短距離0.3m〜100mの範囲だけが描画されるようになるshitei

霧を出現させる

  1. Window>Lightingを選択
  2. Scenesを選択
  3. FogをONにする
  4. Fogを下図のように調整するkasumi

次へ

Unity入門-8 マテリアルなどの設定や応用

レイヤーで不要な表示を消す

  1. マップビュー用に作成したマテリアルが水面に反射してしまう
  2. これはCameraのレイヤーに関わりなく,(WaterProDaytimeを出していれば)WaterProDaytime全てのレイヤーを反射しているからである
  3. そこでWaterProDaytimeを選択し,Reflect Layersの中からmarkerのチェックを外すと水面にも表示されなくなりますrefrect

マテリアルその3 NormalMapをアサインする

  1. マテリアルにはノーマルマップ(法線マップ)をアサインできる
  2. GameObject>3D Object>Cube で立方体を作成するmatnormal
  3. Asset内に空のマテリアルを作成し(Asset右クリック>Create Material),分かりやすい名前に変更する
  4. 作成したマテリアルを選択し,InspectorのAlbedoの左にある○をクリック
  5. Select Texture画面が開くので,上段の検索フィールドにmudと入力すると,MudAlbedoSpeculraTextureが表示されるので,選択をする
  6. 次にNormal Mapの左の○をクリックし,同様に検索語にmudを入力し,MudRockyNormalが表示されるのでこれを選択する
    normal2
  7. 作成されたマテリアルをさきほど作成したCubeにアサインするnormal3
  8. 6ポリゴンしかないCubeが一見して超ハイポリゴンに見える
  9. 見た目の形状が変わっているため,光源が変化すると影も変化するnormalshado

Normal Mapとは

  1. 計算資源に限りのあるスマートホンや携帯ゲーム機といったデバイスには,距離などによってローポリ/ハイポリのモデルを切り替えるLOD( Level of Detail)を利用したり,Fogや視程を下げるなどして狭い範囲だけ描画するなどしている
  2. Normal Mapはポリゴンの表面にある法線を画像で指定する方法で,少ないポリゴンでも見た目を細かく見せることができるため,皮膚や服のシワ,壁や地面といった部分に使用される
  3. このNormal Mapは自分でも作成でき,画像などを加工してもそれなりなものを作成できるため,出かけた際は地面や壁や屋根の写真を撮影することをお勧めする

つぎへ