- 投稿日:2019-01-27T05:05:21+09:00
大学生が有料スマホゲームを作った全てを公開するよ(3)ゲーム画面・インゲーム(前編)
大学生が有料スマホゲームを作った全てを公開するよ(2)開発環境とゲームの構成
の続き。今回は、ゲームシーンについて。
いわゆる、インゲームと呼ばれる部分。
逆に、メニュー画面とかはアウトゲームって呼ばれる。
pertica(この記事で紹介するゲーム)の場合は、パズルとアクションの仕組みを中心に解説していくよ。ここからはUnity
ここから先は、基本的にUnityの中での話になる。
プログラミングも出てくるし、
初めて見る人には結構難しいかもしれない。
出来る限り分かるように書いてみるけど、
よく分からない部分は飛ばしてもらって構わないよ。
なんとなーくでも理解してもらえると嬉しい。
後でUnityを使うようになったらまた見に来ても良いよ!Unityを知っている人や、ゲームを作った事がある人は参考にしてみて。
ボクはこうやってゲームを作ったよ。
この記事だと、後になるほど技術的になるから記事の下の方を見ると面白いかも。
これは前編で、後編はもっと技術的な話をするつもり。
アドバイスも待ってます!主要なゲームオブジェクト
前回の記事でも触れたけど、ゲームシーンは大きく3つの機能を持っている。
- メニューで選択したステージを生成する
- ステージが遊べる
- クリアしたら保存する
この3つだ。
そして、これを実現するために、
このゲームは10個の「部品」で出来ている。
- プレイヤー [Player]
- ブロック [Blocks]
- 敵 [Enemies]
- アイテム [Items]
- その他のステージオブジェクト [Others]
- カメラ [Main Camera]
- ゲームマネージャー [GameController]
- デコードオブジェクト [DecodeObject]
- UI(リトライボタン、戻るボタン) [UI Canvas]
- BGM・SE [BGMSource, SESource]
これらの「部品」を今後は、「ゲームオブジェクト」と呼ぶことにするよ。
Unityの中でも同じ名前で呼ばれるんだ。
でも長いから短く「オブジェクト」って言う時があるかも。ちなみに、[ ]の中はUnityの中での名前だ。
それぞれ説明するね。プレイヤー
ゲームを遊ぶ人が操作するキャラクターだ。
- タップした場所に弾を飛ばす
- 弾が当たったオブジェクトと入れ替わる
- 敵に触れたら死んでしまう
この3つの機能を持っているよ。
ブロック
基本的に四角くて動かないもの。
壁のこと。
マリオのブロックみたいな、四角くて動かないオブジェクトだ。敵
プレイヤーは敵に当たると死んでしまう。
プレイヤーに対して攻撃をしてくるオブジェクトだ。
マリオのクリボーとかノコノコと同じだ。アイテム
壁を出したり消したりするスイッチとか、ワープホールとか。
プレイヤーが道具として使うもの。
壁と違って動く可能性があって、四角くない。
マリオでいうと、キノコとかスターのことだ。その他のステージオブジェクト
今説明した「プレイヤー、ブロック、敵、アイテム」の事を、ボクは「ステージオブジェクト」と呼ぶことにしたんだ。
でも背景の星とか、敵が出すレーザーなんかはその4つでは分類できない。
だからブロック、敵、アイテムに分けにくいものを、例外として分類する為に「その他」を作ったんだ。
これでステージオブジェクトは「プレイヤー、ブロック、敵、アイテム、その他」の5種類になった。カメラ
カメラはスマホの画面にゲームを映す機能をもってる。
テレビはビデオカメラで撮影した映像を表示してるよね。
あれと同じで、ゲームの中の出来事を撮影してスマホの画面に表示してくれるんだ。
ズームしたり、カメラを複数使うことも出来るよ。ゲームマネージャー
ゲーム全体の管理をするオブジェクトだ。
このゲーム(pertica)では、
- リセット
- メニューに戻る
- ステージオブジェクトを作る
- フェードイン
の4つの機能を担当している。
デコードオブジェクト
ゲームの最初にステージを設置する機能を持ってる。
perticaはパズルゲームだから、パズルのステージを用意しないといけない。
ステージはたくさん欲しいよね。
そしたらボクだけじゃなくて、友達にもステージを作ってもらった方が良さそうだ。
友達がステージを作れるように、ボクは友達に「ステージを作るツール」を渡す。
友達はステージを作ったら、「保存」ボタンを押す。
そうすると、作ったステージが「どこに何があるか」を文字にしたデータになるんだ。
でもデータでは遊ぶ事は出来ない。
遊ぶ時にはデータを実際にステージに変換しなきゃいけない。
それをするのが、このデコードオブジェクトなんだ。
ゲームが始まった瞬間に、「メニューで選んだステージのデータ」から「ステージ」を作ってくれる、とっても賢いヤツなんだ。UI
リトライボタンとか、メニューに戻るボタンの事だ。
UIっていうのは、人がゲームを操作するために必要な仲介人なんだ。
「ゲームをもう一度やり直したい」とか、「メニューに戻りたい」と思うよね。
UIに頼めば、それを実際にやってくれるんだ。
頼む方法はボタンを押すだけだよ。BGM・SE
音を流す機能を持っている。
BGMはゲーム中にずっとなってる音楽
SEは「弾を飛ばす音」「スイッチを押す音」みたいな効果音の事だ。実装方法
ゲームが何で出来てるかは何となく分かったかな?
部品が「10個」なのは多いと思った?
それとも少ないと感じたかな。
段々と技術的な話に入っていくよ。
面白くなかったら飛ばして、最後のコメントだけ見ても良いよ。
今回は、ちょっとした制作秘話も書いてみた。ここからは、Unityを少し知っている人向けになると思う。
でもUnityを知らない人にも楽しんでもらいたいから、
簡単にUnityでのゲームの考え方を書いてみるね。Unityの簡単な説明
Unityでは主要なゲームオブジェクトで書いたような部品の事をゲームオブジェクトと呼ぶって言ったね。
それぞれのゲームオブジェクトは、さらにコンポーネントと呼ばれるもっと「小さな部品」で出来てるんだ。例えば、ゲーム内のプレイヤーは
- 今いる座標を定める部品 [Transform]
- 見た目を決める部品 [Particle System]
- 物理挙動を制御する部品 [Rigidbody 2D]
- 当たり判定を調べる部品 [Circle Collider 2D]
- アニメーションを制御する部品 [Animator]
- プレイヤーの動作(弾を飛ばす、移動するなど)を制御する部品 [Player(Script)]
みたいな小さな部品(コンポーネント)で出来ている。
このうち、上の5つはUnityが用意してくれてるんだ。
だから、ボクが作るのは一番下の一つだけ。
このゲーム特有の動きを決めるんだ。
脳を作ると言っても良い。
そして、この部品を作るためにプログラミングを使うんだよ。[ ]の中はUnityの中での呼び方で、Player(Script)はボクが作ったんだ。
こういうプログラミングで、自分で作ったコンポーネントを今後は「スクリプト」と呼ぶよ。
Unityの考え方では、
「車の部品にタイヤがあって、タイヤの部品にネジがある」みたいに
「ゲームの部品にプレイヤーがあって、プレイヤーの部品にPlayer(Script)」があるんだ。少し退屈かもしれないけど、このゲームのUnityでの実装の仕方を書き残しておくね。
プロジェクトツリー
メインシーンの説明の前に、Unityのプロジェクトのファイルツリーを書いておくよ。
Assets ├─ Animation ├─ Data ├─ Image ├─ Material ├─ Music ├─ ParticleSystem ├─ Prefab ├─ Scene └─ Script主要なのはこんな感じ。
さらにResourcesって名前のフォルダを必要に応じてそれぞれのフォルダの下に作るんだ。ゲームの仕組みを構造的に見る
ここからゲームシーンの説明だ。
Unity内のメインのゲームシーンは、DecodeStageと呼ばれるシーン名で管理している。
このシーンが実行されると、まずDecodeObjectがデータフォルダに入っているデータをもとにステージを生成する。
次に、Main Cameraがゴールからプレイヤーに向かって移動。
移動が完了すると、ゲームが始まる。
プレイヤーは、キャラクターを操作してゴールを目指す。
ゴールにたどり着くとリザルトシーンに遷移してこのシーンの役目は終わり。
分かりにくいかもしれないけど、上の図を目で追って見て欲しい。ここからは、10個のゲームオブジェクトについて実装の概要を説明するよ。
Unityの簡単な説明で書いたようにUnityは1つのゲームオブジェクトがたくさんのコンポーネントから出来ている。
前編の今回は、そのたくさんのコンポーネントのうちとても大事な「脳」の部分「スクリプト」を中心に解説するよ。
「スクリプト」はプログラミングを使ってボクが作ったものだったね。プレイヤー
プレイヤーの構造はこんな感じだ。
そしてプレイヤーの持つスクリプトはPlayer.csvoid IdlingAnimation() 踊るアニメーションを行うよ プレイヤーが一定時間何もしなかったときに使う。 public void Shift(GameObject obj) 弾が当たったものと位置を入れ替える 飛ばした弾が当たった時に使う public void Die() プレイヤーが死ぬ時に使う ・死ぬ時のエフェクトを表示 ・死んだ回数を保存 ・プレイヤーのゲームオブジェクトを非アクティブにする みたいな事をするよ public void Fire() タップした位置に弾を発射する 画面をタップした時に使う IEnumerator TypeAheadCoroutin() 先行入力を行う 弾のクールタイム中に画面がタップされた時に使う クールタイムの終了を待って弾を発射する準備をするこんな感じ。
「void IdlingAnimation()」とか「public void Shift(GameObject obj)」とかはその下に書いてある機能の名前だと思ってくれたら良い。
プログラミングを触ったことが無い人は、分かりにくいよね。
プログラミングを触ったことがある人にとっては、関数の事だ。
日本語で書いてある説明を、UnityではC#っていう言葉を使って書くんだよ。基本的に、先頭にpublicが書いてある関数は他のスクリプトから呼び出される。
書いていないのは、自身のUpdate関数から呼ばれる。
Update関数っていうのは、毎フレーム呼び出される関数のこと。
フレームっていうのは、ゲームの世界での時間みたいな感じだ。
フレームは1秒に60回くらい更新されるんだ。
だから、Updateも1秒に60回くらい呼び出される。
Updateの中でif文で条件を書いて関数を呼び出すんだ。ブロック
ブロックは次の3種類だ。
通常の壁 [Wall]
四角いオブジェクトでどんなものがぶつかっても動かない。
プレイヤーの放つ弾が壁に当たると、弾が消滅する。反射壁 [Reflection Wall]
四角いオブジェクトでどんなものがぶつかっても動かない。
プレイヤーの放つ弾が壁に当たると、弾が跳ね返る。スイッチ壁 [Switch Wall]
レーザーのような見た目をしている。
後で紹介するスイッチボタンによって、壁が出現している状態と消えている状態の2つが切り替わる。
壁が出現している時の機能は通常の壁と一緒で、弾が当たるとその弾は消滅する。
壁が消えている状態では、壁の機能は発揮せずに弾はそのまま通過するよ。さて、それぞれが持つスクリプトを説明するよ。
まずは通常の壁 [Wall]から。Wall.cs何もない。
実際は少しはあるんだけど、エフェクトを出すとか名前をつけるくらいの簡単なものだけだ。
通常の壁[Wall]はほとんどUnityの機能だけで出来ているんだ。
次は反射壁 [Reflection Wall]。ReflectionWall.csも、何も無い。
反射するはずの壁が、反射するスクリプトを持っていない。
実は、弾が反射するかどうかは壁ではなく、弾の方で処理しているんだ。じゃあ最後、スイッチ壁 [Switch Wall]
SwitchWall.cspublic override void SetStats(int[] stats) 初期状態で表示か非表示かを設定する。 public void SwitchEnabled() 壁の表示非表示を切り替える。 スイッチボタンの状態が切り替わった時に使う。スイッチ壁はスイッチボタンが押されたときに、状態を変える。
でも壁の状態を切り替える処理自体は、スイッチ壁の機能として用意しておくんだ。敵
今のところ敵は2種類。
名前はドッグ[Dog]とピッグ[Pig]だ。
名前の由来は...まぁそのうち話すよ...。
ドッグは直線移動して、壁に当たったら反対向きになる。
ピッグもそれは同じだけど、2つのピッグの間にはレーザーが出るんだ。
ドッグが持つスクリプトは、Dog.cspublic override void SetStats(int[] stats) 最初の状態を決定する。 上下左右とその間の斜め、8種類のうち最初に移動する方向を設定する。 void OnCollisionEnter2D(Collision2D coll) 他のゲームオブジェクトとぶつかった時に使う。 ぶつかったゲームオブジェクトがプレイヤーだったらプレイヤーを倒す。そして、ピッグはこれに加えて
Pig.csvoid SetLinePosition() レーザーの端っこの位置を決める。壁に当たったら向きを変える処理は書いていないんだ。
そういうのは全部Unityにやってもらってる。アイテム
アイテムは5種類。
- キューブ [Cube]
- クリアーホール [Clear Hole]
- スイッチボタン [Switch Wall Button]
- ワープホール [Dimension Crack]
- シフト弾 [Shift Bullet]
キューブはその場で静止しているだけのゲームオブジェクト。
クリアーホールはその位置にプレイヤーが到着すると、ゲームクリアー。
要するに、ゴールの事だ。
ワープホールは2つ1組で使う。
プレイヤーの飛ばす弾や敵が片方のワープホールに入るともう片方のワープホールに瞬間移動するんだ。
シフト弾はプレイヤーが発射する弾の事だよ。順番に見ていこう。
キューブ [Cube] のスクリプトは、
Cube.cs何もない。
これもUnityのコンポーネントだけで出来てる。クリアーホール [Clear Hole]が持つスクリプトは
ClearHole.csvoid OnTriggerEnter2D(Collider2D coll) データの保存、ゲームシーンの遷移をする。 プレイヤーが同じ位置に来た時に使う。クリアーした時のデータの保存もこのクリアーホールで行う。
保存専用のゲームオブジェクトがあるわけでは無いんだ。スイッチボタン[Switch Wall Button]は、さっき紹介したスイッチ壁の状態を切り替える事が出来るんだ。
スイッチボタンに敵のドッグかピッグがぶつかると、そのスイッチボタンと繋がってる壁の状態が変わる。
表示されてる壁は消えて、表示されてない壁が出現するんだ。スイッチボタンはこんな感じのスクリプトを持ってる。
SwitchWallButton.csvoid Switch() 繋がってるスイッチ壁の状態を切り替える。 敵がぶつかった時に使う。次はワープホール[Dimension Crack]
DimensionCrack.csvoid OnTriggerEnter2D(Collider2D coll) ステージオブジェクトがこのゲームオブジェクトに重なった時に使う。 void SetVisible(Vector3 viewpos) このゲームオブジェクトが現在カメラに写っているかの情報を更新する。 void SetPairDC() 現在このゲームオブジェクトと接続しているDimensionCrackを更新する。ワープホールは開発中は、DimensionCrack(次元の亀裂)って呼んでる。
クリアーホールもシーンをワープする感覚だから、ワープホールって名前だと分かりにくかったんだ。
今回の説明では逆に、次元の亀裂って言ってもイメージ出来ないと思ったからワープホールって書いたよ。最後はシフト弾[Shift Bullet]だ。
シフト弾はプレイヤーが発射するエネルギーの玉の事だ。
タップした方向に飛んでいって、「入れ替われるオブジェクト」にぶつかるとプレイヤーとぶつかったオブジェクトの位置を入れ替える。
スクリプトを見てみよう。ShiftBullet.csvoid OnCollisionEnter2D(Collision2D coll) 何かとぶつかった時に使われる。 ぶつかったオブジェクトが入れ替われるならプレイヤーの入れ替わる処理をする。 IEnumerator DieCoroutine() 弾が発射されてからしばらくたつと弾を消滅させる。 public override Die() 弾を消滅させる。 弾が反射壁以外のものとぶつかった時に使う。反射壁は反射する処理を書かないで、逆に壁に当たった時に弾が消える処理を書くことで実現してる。
反射する機能はUnityが用意してくれてるからボクはそれを上手に使うだけで良いんだ。その他のステージオブジェクト
「プレイヤー, ブロック, 敵, アイテム, その他」をステージオブジェクトと呼ぶって言ったね。
その他のステージオブジェクトには、背景の星とかピッグのレーザー[PigLazer]とか予測線[Prediction Line]がある。
他にもあるけれど、それはこのゲームシーンでは登場しないから今度話すね。
背景の星は、Unityのコンポーネントだけで出来てる。
ピッグのレーザーは、PigLazer.csvoid OnTriggerEnter2D(Collider2D coll) プレイヤーにぶつかった時にプレイヤーを倒す。これだけのスクリプトを持ってる。
予測線は、画面を長押ししたときに弾が飛ぶ軌道を赤い線で教えてくれる。PredictionLine.csvoid SetLine() 予測線の軌道を設定する。 予測線は、プレイヤーからタップした方向にに直進する。 予測線はものにあたるとその先までは貫通して表示されない。こんな感じのスクリプトだけで出来てる。
カメラ [Main Camera]
perticaでのカメラは基本的にプレイヤーが画面の中心になるように追う事だ。
でもステージの始めは、ゴールを映して徐々にプレイヤーにカメラを動かす演出がいる。
プレイヤーが入れ替わるときにも、少し遅らせ気味にプレイヤーを追うように演出する。
これを実装するために、ボクはこんな感じにいくつかの状態(ステート)で考えたよ。
持ってるスクリプトはMoveCamera.csvoid SetState(CameraState state) 状態(ステート)を切り替える関数 void LookClearHole_Move() ゴール(ClearHole)からプレイヤーの位置にカメラを移動させる。 ゲームの開始時に使う。 LookClearHoleという状態(ステート)のときに使われる。 void TracePlayer_Move() プレイヤーのカメラの中心に捉えるように、プレイヤーを追いかけ続ける。 プレイヤーが静止しているときに使う。 TracePlayerという状態(ステート)のときに使われる。 void GoToTarget_Move(GameObject objTarget, float speed) プレイヤーの方向にゆっくりと移動する。 プレイヤーが入れ替わるときに使う。 GoToTargetという状態(ステート)のときに使われる。実はカメラの制御は結構難しい。
良い演出が思いついても、難しくて実現できてないものもあるんだ。
特に、ズームとかが入ってくると厄介で...。ゲームマネージャー
ゲーム全体の管理をすると言ったね。
このオブジェクトはボクが作ったスクリプトだけで出来てる。Main.cspublic void Reset(float WaitTime = 1) ステージをリセットする。 ゲームシーンを最初から読み込み直す。 リトライボタンを押したり、死んでしまったときに使うよ。 public void LoadMenu() ゲームシーンを終了して、メニューシーンに移動する。 メニューボタンを押したときに使うよ。 public void FadeIn() ゲームシーンを終わるときに、画面全体を徐々に暗くするよ。 ゲームをクリアーしたり、メニューに戻ったりするときに使う。と、
Tutorial.csvoid Update() ゲームを初めてプレイする場合、チュートリアルを表示する。主にこの2つのスクリプトだ。
初めてスクリプトを2つもったゲームオブジェクトが出てきたね。
ゲームオブジェクトは2つどころか、スクリプトをいくつでも持てるんだ。ゲーム全体を管理すると言ったけど、
このゲームでは、ゲームマネージャーの仕事は少なめだね。デコードオブジェクト
Dataフォルダに入っている、StageCodeDataという名前のステージのデータを元に実際にステージを生成するよ。
データの仕組みや構造については別の回で説明するね。
デコードオブジェクトはスクリプトだけで出来ているよ。
ちなみに、これは先輩が作ってくれたんだ。DecodeStageScript.csvoid Awake() メニュー画面で選択したステージのデータをロードする。 データを解読(デコード)して、対応するゲームオブジェクトを次々に生成していく。 生成したオブジェクトによっては、初期状態を指定する。UI
UIは、ボタンの事だったね。
UIは基本的にスクリプトを持っていないんだ。
これは全部Unityが用意してくれているコンポーネントで出来ている。
ボタンを押すと、ゲームマネージャーが持ってるスクリプトのMain.cspublic void Reset(float WaitTime = 1)とか
Main.cspublic void LoadMenu()を使うように設定できるんだ。
BGM・SE
音を鳴らす機能を持っていたね。
これは、スクリプトを持っているよ。
まず、SEはSEPlayer.cspublic static void PlaySE(string keyname) 指定したSEを再生するよ。 流すSEは、事前に曲のファイルとキーワードを紐付ける必要がある。そして、BGMは
BGMPlayer.cspublic static void FadeinBGM(string keyname,float pertime=0.008f) 指定したBGMをフェードインしながら再生するよ。 曲が変わるときの違和感をなくすために、曲の入りで徐々に音を大きくするんだ。 流すBGMは、事前に曲のファイルとキーワードを紐付ける必要がある。 public static void FadeoutBGM(float pertime=0.009f) 指定したBGMをフェードアウトしながら終了するよ。 曲が変わるときの違和感をなくすために、曲の終わりで徐々に音を小さくするんだ。大事なのはこんな感じだ。
例えばプレイヤーが球を発射するときは音が鳴る。
この時、SEPlayerのPlaySEを使うんだよ。他にも、音を出したいときにはこのスクリプトを使って音を鳴らすんだ。
ちなみに、これも先輩が作ってくれたよ。まとめ
ゲームは「10個の部品」で出来ていた。
さらに「10個の部品」はもっと「小さな部品」で出来ている。
Unityではこの「10個の部品」を「ゲームオブジェクト」、
「小さな部品」を「コンポーネント」と呼ぶ。「コンポーネント」はUnityが用意してくれているものと、自分で作るものがある。
自分で作るコンポーネントの事を「スクリプト」と呼ぶことにしたね。「スクリプト」は大事な機能を持っていて「脳」みたいなものなんだ。
だから今回は、そんなスクリプトについて説明したよ。
実際には、パソコンが理解できるようにC#という言葉を使って書くんだ。
その言葉を書く作業がプログラミングだよ。なんとなく分かったかな?
制作秘話
perticaの制作を初めて2ヶ月くらいたったある日。
帰り道でボクが唐突に言った。「先輩、重力を消しましょう」
先輩「?????」
ボクが真顔で言うもんだから、ついに頭がおかしくなったかと思われた。
今のperticaは宇宙感のあるゲームで無重力だ。
でも昔は重力があったんだよ。昔のperticaは入れ替わった直後に重力で下に落ちたんだ。
でもそれだと、画面の動きが目まぐるしくて落ち着いて遊べなかった。
そこで、perticaでは重力をなくした方が良いんじゃないかと思ったんだ。先輩はオーケーしてくれて、それから「無重力計画」が始まった。
結構な変更だったから大変だったよ。
先輩もよく付き合ってくれた、本当に感謝してる。無重力にしたのが良かったのか悪かったのかは分からない。
でも面白いゲームを作るために思い切って変えたことに後悔はしていないよ。他にこんな話もある
メインシーンは「ゲームを作ろう」と思って最初に作り始めた部分だった。
「プレイヤー」「Dog」「通常の壁」「反射壁」「ClearHole」「ワープホール」「キューブ」「スイッチ壁」
これは機能だけなら最初の1ヶ月くらいで出来たんだ。
見た目とかは、適当な画像で代用してたけどね...。実は最初の頃は「Cat」とか「Ojisan」とかもいたんだよ。
その辺は今後「デザインについて」の記事も書くからその時に話そうかな。
多分その記事は神回になるよ。コメント
ここまで読んでくれてありがとう。
今回は、メインシーンについて触れた。
少し文量が多かったね...。Unityを知らない人にも、ゲームの仕組みが分かるように書いてみたんだけど、どうだったかな?
今このシリーズ記事の全体の構成をぼんやりと考えてるんだけど、多分全部で10回くらいになると思う。
なるべくこのゲームを全部公開したいんだけど、どうすると上手く伝えられるかな。
もし、気になる事とか書いてほしい事があったらコメントしてね。そろそろ実物があった方が分かりやすいんじゃないかな?
もし興味をもってくれたら、よろしくお願いします。
perticaは下のリンクからインストール出来るよ。大学生が有料スマホゲームを作った全てを公開するよ(1)イントロダクション
大学生が有料スマホゲームを作った全てを公開するよ(2)開発環境とゲームの構成
大学生が有料スマホゲームを作った全てを公開するよ(3)ゲーム画面・インゲーム(前編)
- 投稿日:2019-01-26T16:46:44+09:00
頂点シェーダで曲面を作る
やりたいこと
画像を任意の曲率で円柱型に曲げたときの画像を作る必要がありました。
GIFでは曲率半径を1~3unitに変動させています。幾何的な話
点Pの座標とrが分かっていて、P'の座標を求めたい訳です。円の全周が2πr(2π)でそのうちのθだけなので、l=rθです。これでθが求まりました。あとはxはcos、yはsinで座標が求まります。
実装
C#で頂点バッファをいじってもいいのですが、シェーダでいじった方が手っ取り早そうなので今回はSurfaceShaderで書きます。モデルの方は適度に細かい網目状の長方形を用意しておきます。(横幅を1unitに)
C#から曲率半径を受け取るために、
_Radius
を宣言しますProperties { _MainTex ("Albedo (RGB)", 2D) = "white" {} _Radius ("Radius", Range(1, 20)) = 1.0 }頂点シェーダを明示します。
#pragma surface surf Lambert vertex:vert
頂点シェーダ本体です。簡便にするため、横幅1unitだったモデルをπunitに引き伸ばしてから処理しています。
void vert(inout appdata_full v, out Input o ) { UNITY_INITIALIZE_OUTPUT(Input, o); float pi = 3.1415926; float l = v.vertex.z * pi / 2; float r = _Radius; float th = l/r; v.vertex.xyz = float3(v.vertex.x *pi/2, r*cos(th)-r, r*sin(th)); }コード全体
arch.shaderShader "Custom/arch" { Properties { _MainTex ("Albedo (RGB)", 2D) = "white" {} _Radius ("Radius", Range(1, 20)) = 1.0 } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Cull off CGPROGRAM #pragma surface surf Lambert vertex:vert #pragma target 3.0 sampler2D _MainTex; float _Radius; struct Input { float2 uv_MainTex; }; void vert(inout appdata_full v, out Input o ) { UNITY_INITIALIZE_OUTPUT(Input, o); float pi = 3.1415926; float l = v.vertex.z * pi / 2; float r = _Radius; float th = l/r; v.vertex.xyz = float3(v.vertex.x *pi/2, r*cos(th)-r, r*sin(th)); } void surf (Input IN, inout SurfaceOutput o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
- 投稿日:2019-01-26T16:23:06+09:00
【Unity(C#)】カメラに映った世界をGIF画像にする方法
この記事は『プログラミング完全未経験からUnityでの開発現場に迎え入れてもらえた世界一の幸せ者』
の記事です。そのつもりでお読みください。
GIF画像を作る
Unityで動画やGIF画像無しに説明するのしんどいので調べました。
ありました→https://github.com/Chman/Moments.git
ダウンロードして解凍してUnityのProjectにぶち込みます。
私がぶち込んだ際にはエラーが出ました。でも慌てないでください。
深呼吸してエラーメッセージを読みましょう。私のエラーは
MinAttribute
というクラス名を違う適当な名前に変更したら消えました。使い方を書きます。
使い方
①既存のScriptに追加
Recorder
というScriptを開きます。
Recorderクラスのフィールドにfloat returnBufferSize;
を追加し、
下記メソッドを追加します。public float ReturnBufferSize() { return returnBufferSize = m_BufferSize; }
m_BufferSize
というのは録画時間のことを指します。
次の工程で録画時間を他クラスに渡す必要があります。
そこで、録画時間を返すメソッドを用意しました。②新しいScriptを作る
便利なメソッドがたくさん用意されていたので助かりました。
ところどころよくわかりませんが動いたので良しとしましょう。using UnityEngine; using System; using Moments; using System.Collections; public class Rec : MonoBehaviour { bool isRec; bool isSave; int countTryRec; Recorder rec; Coroutine runCoroutine; void Start() { print("Rで録画開始"); rec = GetComponent<Recorder>(); rec.OnPreProcessingDone = OnProcessingDone; rec.OnFileSaveProgress = OnFileSaveProgress; rec.OnFileSaved = OnFileSaved; //BufferSizeが自然数でないとめんどうなのでエラーメッセージで忠告する if (rec.ReturnBufferSize() - (int)rec.ReturnBufferSize() != 0) { Debug.LogError("BufferSizeを自然数にしろ"); } } //保存開始時 void OnProcessingDone() { print("保存開始"); } //保存中 idという引数はよくわからん void OnFileSaveProgress(int id, float percent) { print("保存中:" + Math.Truncate(percent * 100.0) + "/" + "100%"); } //保存終了時 idという引数はよくわからん void OnFileSaved(int id, string filepath) { print("保存完了:" + "100" + "/" + "100%" + filepath); isSave = false; } void Update() { //録画 -> 録画中、セーブ中は押せない if (Input.GetKeyDown(KeyCode.R) && isRec == false && isSave ==false) { runCoroutine = StartCoroutine(RecCountDown()); } //保存 -> 録画中、セーブ中は押せない if (Input.GetKeyDown(KeyCode.S) && isRec == false) { rec.Save(); isSave = true; } } //録画開始~終了まで IEnumerator RecCountDown() { isRec = true; float second = 1f; int count = 0; //録画カウントダウン print("録画開始まで"); yield return new WaitForSeconds(second); print("3"); yield return new WaitForSeconds(second); print("2"); yield return new WaitForSeconds(second); print("1"); yield return new WaitForSeconds(second); print("開始"); rec.Record(); //残り秒数の表示 for (int i = 0; i < rec.ReturnBufferSize(); i++) { yield return new WaitForSeconds(second); count++; print("録画時間" + ":" + count + "/" + rec.ReturnBufferSize()+"秒"); } //録画終了 print("録画終了"); yield return new WaitForSeconds(second); isRec = false; print("保存するならS,上書き録画はもう一度R"); StopCoroutine(runCoroutine); } }③カメラに①、②をAdd Componentする
使いどころとしては以下3つくらいだと思います。
パラメーター名 機能 Frame Per Second 小さいとカクカク、大きいとヌルヌル Repeat 画像の繰り返し回数 Buffer Size 再生時間(録画時間) 実際に動かしてみた
キーボードのR
で録画のカウントダウンが開始します。
3秒後(正確には4秒後)に録画が開始されます。
録画開始後は録画終了までの秒数が表示されます。
もう一度録画をやり直したい場合はR
を、保存したい場合はS
を押します。
保存開始後は保存終了までのパーセンテージが表示されます。
保存完了後はファイルの保存場所が表示されます。
Editorウインドウを一度たたんで開くと保存されています。BufferSizeは自然数で
これのおかげでいろんな記事が書けるようになった気がします。
もっとこうした方がいいとか、他の簡単な方法とかあれば教えてください。
個人的にはシーンビューをGIFにできたら便利だなーと思いました。
- 投稿日:2019-01-26T05:06:48+09:00
Unity:GPU インスタンシング(Instancing) を WebGL がおかしい
はじめに
Unityの高速化の一般的な手法、GPU インスタンシングを使ってパフォーマンスを改善するようなデモを作成したので動作してみてください。 また、WebGLで出力した場合も有効かどうか確認し有効でしたので合わせてデモページもありますので各自の環境で試してみてください。
Link
- デモ : GPU Instancing無効
- デモ :GPU Instancing有効
- Github:https://github.com/fastsystem/unity-gpu-instancing-example
実装手順
こちらを参照してください
Unity:GPU インスタンシング(Instancing) を WebGL で実行する何がおかしいのか
- デモ
- 操作方法
- ホイールでドラッグ
- ホイールでズーム
- 右クリックでカメラの向き変更
初期表示が遅い
GPU Instancing無効:Unityロゴが表示後にすぐに表示される
GPU Instancing有効:Unityロゴが表示後に10秒くらいかかる。操作ができない
GPU Instancing無効:マウスでの操作ができる。
GPU Instancing有効:マウスでの操作が鈍すぎて実用に耐えない。FPSはでている
GPU Instancing無効:20fps くらい
GPU Instancing有効:50fps くらい若干の解決(2019/01/28追記)
「Quality」の設定からレベルを下げる事により本現象が若干和らいだので追記しておきます。
どちらがいいの?
あまり検証していないが、GPU Instancing を有効にしたオブジェクトを使いすぎると初期表示の遅さ、操作時の引っ掛かりがあり実用するのであれば無効も考えた方がよさそう。
バランスを見て有効・無効の割合をチューニングする必要がありそう。
- 投稿日:2019-01-26T01:39:29+09:00
【Unity】チェリーを取るとパーティクル飛ばすOnTriggerEnterイベント
環境メモ
⭐️Mac OS Mojave バージョン10.14
⭐️Unity 2018.2.15f1実際に動かした動画はこちら↓↓
https://twitter.com/nonnonkapibara/status/1088830093135237122?チェリー?を取ると✨パーティクル✨飛ばしてみたよぉ?
— non (@nonnonkapibara) 2019年1月25日
✨キラキラキラ✨
(●^o^●)v
「CharacterController」と「BoxCollider」
?Is Trigger チェックON
?Use Gravity チェックOFF
?Is Kinematic チェックOFF
OnTriggerEnterイベントが呼ばれる#Unity #Unity3d pic.twitter.com/lDbPLzwyyQQiitaの容量オーバーで画像が貼り付けられなくなりました。(errorとなる)
続きは、「はてなブログ」に記載してます↓
かぴばらさんの覚書ブログ nonkapibara