【Unity】無謀にもunity1week(二回目)に挑戦【転がる】
第一回に引き続き、
先週の第二回unity1weekにチャレンジしてきた。
ころころフレンズ | 無料ゲーム投稿サイト unityroom - Unityのゲームをアップロードして公開しよう
なんだかんだでやっぱり血反吐を吐いたので、振り返って
次につなげたいと思う。
何作ろうか?
参加の目的としては「Unityの学習」にあり、
このチャレンジを通して一連の流れ(ゲームの遷移)について学びたいと思った。
ゲームの遷移とはゲーム内で行われる状態の遷移だ。
今回は
タイトル -> (ADV -> ゲーム待機 -> ゲーム) x n -> ゲームクリア
というシンプルな流れなんだけど、
ADVモードとゲームモードを行き来するのがよくある形なのかなと
思ってそこを勉強したかった。
後は
- 平日は時間がない、土日でやりきれるボリューム
- ADVがあるのでストーリーがある。
- 遅刻はしない。制限時間を守る。
このぐらいものがいいなぁ、と漠然と考えていた。
ゲームは何にするか?
正直全然思いつかなかった。
twitterで周りの人の発想を見て感嘆の吐息を漏らすばかり。
とはいえ諦めたら何にもならん。誰でも思いつくような
「高いところから低いところへ球を転がす」
恥ずかしいけど、簡単なゲームでいい、完成まで作ってUnity力を少しでも上げるのだ。
と自分に言い聞かせた。
後はなるべく自力でやる。絵も自力だ。
これもやんなきゃ身につかないだろうな…と。
画面構成
ノートの端っこにでも画面を書いておく。
Sceneはもちろん1枚。
UIの切り替えで画面遷移を実現させる。
ADVモード用のUIとGame用のUIと画面中央にドーンとロゴを出すUIと
とUIを3つのグループに分けておくことを考えた。
この時はフレンズなんて頭の片隅にもありませんでした。
血反吐ポイントその1
ここが血反吐ポイントその1。
必要なものを洗い出しておいて作業量を見極める。
- 雪山フィールド
- ADVアイコン画像
- ロゴ
- 立ちキャラ
- プログラム
この内最もヤバそうなのは雪山フィールドだ。
雪山フィールドエディタ
terrainやらAsset Storeやらも頭をよぎったけど、
そこまで高機能じゃなくていいので自分で作ってみる。
これは平日の夜にちまちまやってた。
やだ…。この人またmesh作ってる…。 #unity1week pic.twitter.com/iSG19goBTq
— ぐーるさん (@uuha_goul) 2017年5月24日
頂点を軽く動かすとそれに沿ったベゼルカーブが描かれて、そのベゼルカーブに沿った
Meshを構成してそれに沿ったオブジェクトを作ってあげればOKだ。
頂点を好きなだけ追加することが出来るが、位置は一つ一つマウスで動かしてあげる必要がある。
これをうねうねさせて作ったのが雪山フィールドだ。
デコレーションで木を乗せているが、これはMagical Voxelでささっと作って後乗せ。
これでフィールドは出来た。
フレンズ
金曜日のニコニコ一挙見てたらカッとなってしまった。
そしてやってしまった。後悔はしていない。
後は作っていくだけ
必要なUIを置いたり、オブジェクトを置いたりして
画面を組み立てていく。
その都度必要になるスクリプトを書いていく。
今回全体で作ったクラス関連図はこんな感じ。
ゲームって割り込みで制御したくなるものが多いんで
分けるとアクセス周りがガバッちゃうんですよねぇ。
1つにまとめちゃうと鬼クラスの出来上がりだし。
見直して今後に使いまわせるようなものにしたい。
UIやSnowBallとかは特化しているので流用しにくいかもしれないけど。
血反吐ポイントその2
素材だ。画像素材。
ここが一番時間かかった。慣れの問題もあるけど。
もうとにかく描く。下手でも描く。
下手の横好きだけど楽しかった。もっと描きたい。
ロゴはジェネレーターをお借りしました。
https://aratama.github.io/kemonogen/
日曜日
エロマンガ先生~ヤバい○○まで見てTVを消して集中する。
朝日が眩しくなり、大分日が昇ったなとTVを改めて付けたら
さまぁ~ずの神ギモンってTVがやってた。
一通り出来て、結合して動かして
最後にお礼一枚絵を描いてビルドして提出。
そして夕飯素材を買いに行き、餃子を包む。
感想と次回
プレイして下さった方、本当にありがとう。
自分は普段Unity使ってませんので、難しかったですがあっという間でとても楽しかった。
そして次はもっと良いものを作ろうと沸々と湧いてくるものがあります。
目的としていたゲーム遷移だけど、
敵に攻撃した時、敵が全て倒れていたら、次の状態へ進む
という遷移も割りと噛み砕けて書けたんじゃないかと思う。
UpdateとEventとStateにしっかり分けたのが良かったかもしれない。
ちょっとゲームの遷移についてググって調べてみたり本を読んでみよう。
次も頑張ります。次のお題は何だろうなぁ。
やっときたいこと
黒画面がフェードイン・フェードアウトをさっと出来るスクリプト用意しておきたい。
コールバック付与出来るやつで。ステージ切り替わりとかに使ってもっとゲームっぽくしたいところだ。
【Unity】メッシュカッターについて その4
は?(威圧)
なんだよ、前回で終わりじゃねーのかよ。
と悪態をつきたい所だが、まだ終わりじゃない。
前回までは流れを理解するためのkindar_codeに過ぎない。
しっかり実践寄りに調整し直してみよう。
ゴールはcubeを切るところまでだ。
いきなりだけど、まるっとproject化してgithubに公開しておいた。
ゲームにも応用が出来そうなRigidbody入りだ。
github.com
あんまり変わらない?
概ねの流れはここまでで把握できている。
後は複数のものに対応出来るように変えた。
meshのポリゴン数が増えるので、ちゃんと三角形ずつ計算
するようにしたり
for (var i = 0; i < meshTriangles.Length; i += 3) { var idx0 = meshTriangles[i]; var idx1 = meshTriangles[i + 1]; var idx2 = meshTriangles[i + 2];
渡されたmeshからちゃんと頂点位置を計算するようにしたり
// 頂点位置をscaleやpositionに合わせてしっかり計算しないとおかしくなる // あれ、もうmatrixで計算したほうがいい? var v1 = Vector3.Scale(meshVertices[idx0], meshScale) + meshPos; var v2 = Vector3.Scale(meshVertices[idx1], meshScale) + meshPos; var v3 = Vector3.Scale(meshVertices[idx2], meshScale) + meshPos; verts.Add(v1); verts.Add(v2); verts.Add(v3); // そのポリゴンの法線を計算しておく var normal = Vector3.Cross(meshVertices[idx2] - meshVertices[idx0], meshVertices[idx1] - meshVertices[idx0]);
後はゲームっぽさを出して複数のinputに対応出来るようにしたり
rigidbodyつけたり
と調整しただけ。
この後
ここまでの流れが把握できていれば、もうなんでも出来る。
SkinedMeshに対応してあげればunityちゃんだって切れるし、
切り口が気になるなら、そこを塞ぐmeshを作るプログラムを組むのもいい。
(交点を全部保持しておいて、そこを繋ぐようなmesh作るだけでいい?)
submeshがあるオブジェクトも同じように計算していくだけだ。
また、uvやMaterialsが入っていないのでそれを追加してもいい。
やってみて
MeshCutterなんて検索すればいくらでもあるし、
それを使えば機能は満たす事が出来るだろう。
だけど、理解しているか否かでは雲泥の差がある。
「ライブラリが対応していないので出来ません。」
が通用する世界では無いのだろうと思っている。
Unityはやっぱり楽しい。
仕事で使ってみたいですね。
【Unity】メッシュカッターについて その3
MeshとPlaneは作った
ghoul-life.hatenablog.com
ghoul-life.hatenablog.com
前回、前々回で必要なものは用意出来た。
ではこの2つを使ってMeshを実際に切ってみよう。
planeの交点の計算
とはいえ、いきなり切るわけにはいかない。
まずはPlaneと三角形の交点を計算しなければならない。
計算してみよう。
Cross Point Check source file
(このソースファイルは後述のCutter.csの一部分を切り出したもの。
プロジェクトには入れなくてもOK)
一点の頂点から別の二つの頂点に対してRayを作る。
そのRayがPlaneに当たるまでを計算してその距離を測れば
交点を計算出来る。
実行した結果はこんな感じだ。
わかりやすく頂点にSphereを表示するようにしている。
引いた線と三角形の辺の交点が求められているのがわかる。
後は切るだけ
ここまで来たらもう切るだけだ。
カットするということは2つのオブジェクトに分ける
という意味に捉えることが出来る。
片側のグループの頂点数は3、
もう片方のグループの頂点数は4になるように当てはめて、
個別にMeshを作ってあげれば完成だ。
気になるのは
cross = Vector3.Cross(pos[triIdx2] - pos[triIdx0], pos[triIdx1] - pos[triIdx0]); inner = Vector3.Dot(cross, Camera.main.transform.forward);
ここだろうか。
これは、頂点インデックスを計算したときに裏側を向いているかどうかをチェックしていて、
裏側を向いてしまっていたら反転させてあげているだけだ。
http://www.sousakuba.com/Programming/gs_polygon_inside_outside.html
頂点インデックスを元にしたベクトルABとBCの外積が0より大きければ表、小さければ裏になる。
ここではカメラの方を向いているかで調べてしまっているが、本来ならnormalの方向でチェックするのがいい。
実行した結果はこんな感じだ。カットできた。
切って生成したオブジェクトにRigidbodyを付ける、などすれば
ゲームに実用出来るだろう。
ここまでで基本的な流れは抑えられる。簡単でしょう?
が、そう思ったやつから死んでいく…。
【Unity】メッシュカッターについて その2
これを切断するわけだが
前回でメッシュの作り方は書いた。
メッシュカッターはズバッとこれを切断する。
ポリゴン数が増えても各ポリゴンで繰り返すだけだ。
どっちに頂点があるかチェックする
3つの頂点があり、これを2つのグループに分けた場合、以下になる
- 全部1グループに属し、2グループは0になる
- 全部2グループに属し、1グループは0になる
- 1つが1グループに属し、2グループが2つ
- 2つが1グループに属し、1グループが1つ
外を切る!という考えがあることに注意。
この内
- 全部1グループに属し、2グループは0になる
- 全部2グループに属し、1グループは0になる
この2つは切る必要がないため、後回しにする。
グループ分けの方法
正直なんでもいい。自作でもいいし。
有り物で行くならUnityではPlaneを使うのがシンプルだ。
3DオブジェクトにあるPlaneでは無く、
たった一つのpointとたった1つのnormalを持つ平面を表す構造体だ。
この
これを使う。
内部的には
その頂点が法線方向にあればtrue。
無ければfalseなだけだ。
これを使えば一定の位置からどっちのグループにあるか計算出来る。
まずは、Planeを作るスクリプトを組んでみよう
ここではLineRendererで引いた線に沿ってplaneを作る。
こんな感じにシンプルに。
OnDrawGizmosSelected()でPlaneの法線が見えるようにしている。
一歩踏み込んでマウスで自由に線を引けるようにして、
その都度Planeを作ってもいい。
「もうこれで頂点分けてメッシュ作るだけでええやん」
と思ったかと思います。そのとおりです。
次はいよいよ本当にカットします。
【Unity】メッシュカッターについて その1
最小構成で作ってくる
【Unity】ScreenToWorldPointが上手く取れない?
ScreenToWorldPointを使う
Unityを使用していて、クリックした位置にオブジェクトを移動させたりすることはよくある。
クリックした位置を取得するのは容易だ。流石Unity。
https://docs.unity3d.com/jp/540/ScriptReference/Input-mousePosition.html
これだけでいい。
しかし、これはあくまでScreen上のpointであって、Scene上のpositionに変換してあげなければ
思っていたような動きをしてくれない。
調べると
https://docs.unity3d.com/ja/540/ScriptReference/Camera.ScreenToWorldPoint.html
を使えばOK。
という記事が大量に見つかる。
「OK!やったるで!」
と素直な君はそのまま書くだろう。
が、
「上手く取れない…なんでだ、Unityやっぱクソだな。」
と絶望を感じるだろう。
逆に
「上手く取れた!やっぱUnity神だな。そして俺天才だな。」
と大いに自惚れるだろう。
UnityのCamera
Cameraの投影設定には2つのモードがある。
Perspective
Orthographic
誤解を恐れずに簡潔に言えば、立体投影と平面投影の違いだ。
なんでや
この
Perspective
に設定されている時はz値の設定が無いと
正しく ScreenToWorldPoint
で値を変換することが出来ない。
Input.mousePositon
はそのままだとxyだけで事足りるのでz値が無いのだ。
このz値を設定してあげれば正しく変換することが出来る。
ScreenToWorldPoint check source file
このソースを実行して画面の真ん中辺りをクリックすると
こんな値が取れる。
ちなみにtmpPos側は画面のどこをクリックしても同じこの値が返ってくる。
また、z値によって変換されて返ってくる値が変わってくるのに注意だ。
「そりゃそうだろ」って話なんだが、
Perspectiveで描画するがゲーム自体は2Dのように動かす、
なんて時には直面するかもしれない。
では
Orthographic
の時はどうだろう?
これはz値が必要ないので、直でもOKだ。
ちゃんと値が変換されて返ってくる。
また、逆にz値に何を入れてもxy値は変わらずに返ってくる。
これも「そりゃそうだろ」って話なんだが、
ハッキリわかっておくと安心出来る。
無謀にもunity1weekに挑戦 その2
考え直す
構成を考える
- 画面構成 シンプルに飛距離を右上に出すぐらい
- オブジェクト 背景とプレイヤーいれば良さそう
- 必要なリソース
ここまで
プロトタイプ制作
- 着地判定が必要
- 溜めるアニメーション
- 飛んでいるという感覚
作業を開始
(パーツ分解で身体とロール部分とロールをつなぐ部分を分けておく。
ロール無しでも悪くない?)