前置き
今回からちゃんとUnityを触っていきます。
前記事
https://qiita.com/VRCjonmyon34/items/ba87f43830b9d0551001
Unityのバージョンは 2018.4.9f です。
これは近い将来にVRCHATが対応するバージョンだそうなので、それに合わせておけばまあ皆入れやすいでしょ、とのことで選びました。ほかに特別な理由はないです。
テキストエディタ(プログラムを書く際のソフト)はUnityのデフォルトのやつとか好きなもの何でも使ってもいいと思います。私個人はVSCode使ってます。
今回やること
- ・オブジェクトを自動で動かす
- 適当にオブジェクトを出してプログラムを書いて自動で動かしてみます。
- その際のプログラムの内容についてはなるべくわかりやすく解説するので雑に見てもらえば。
画面の解説
Unityの画面の基本的な解説をします。わかるよって場合は飛ばしてください。
アバターをアップロードする際にやる方法と同じようにプロジェクトを作ってください。名前は好きなようにでいいです。
そうすると細かな違いはあれど上の画像のようなレイアウトが表示されるはずです。この画像の中の5つについて書きます。
- 1.Project
- Projectは素材置き場のようなものです。アバターをアップロードする際もまずはここに入れてから真ん中の画面や2のHierarchyに入れてますよね?つまりそういうことです。
- ここは素材が増えてくると散らかりがちなので、フォルダを作って細かくまとめておくのもいいかもしれません。というか推奨です。
- 2.Hierarchy
- Hierarchyは現在のゲームシーンに何があるかを示しています。画像の場合はカメラとライトがありますね。
- ここに配置した素材をこねくり回してゲームを作っていきます。
- 3.Inspector
- ここにはHierarchyに置いた素材の情報が表示されます。例えば位置だったり、角度だったり、適用されているスクリプト(プログラム)だったり……
- 4.Scene,Game
- Sceneはゲームをいじる画面、Gameは実際のゲーム画面だと思ってもらっていいです。
- Sceneでゲームを作って、Gameでテストプレイする…といった感じに。
- 5.Console
- 多分ここが一番なじみがないんじゃないでしょうか。ここには実行時のログとかエラーとかが表示されます。
- 表示されたエラーを直すのはもちろん、好きなタイミングでログを出したりすることもできるので、デバッグ作業によく使います。仲良くしましょう。
とりあえずこの5つがあるんだなー、程度に思っておけば大丈夫です、多分。
デフォルトで書かれてる中身について
じゃあ早速作っちゃいましょう。
まずはHierarchyのCreate→3D Object→Cubeをクリックして、HierarchyにCubeを生成します。
生成されたのを確認したら、こんどはProjectの中で右クリックして、Create→C# Scriptをクリック。名前は CubeMover とでもしておきましょうか。
名前を決定したら、つくったスクリプトを開きましょう。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CubeMover : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
開くとおおよそ上記のようになってると思います。
この中で一番最初に覚えるべき事項は
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
ここですね。
これらを一つずつ解説していきます。ここ以外の部分もそのうち解説します。
// Start is called before the first frame update
コードの中に//を入れると、その行の//から後ろはプログラム上は処理をしないものになります。
これをコメントといい、プログラムのメモ書きや、処理から外したいコードが一部あった時などに使います。今回の用法はメモ書きですね。
内容は
『Startは最初のフレームの前に呼ばれるよ!』
みたいな感じです。
要するに、ゲームが回りだす直前にStartの中身を処理するよ、っていうことです。
void Start()
{
}
これは関数です。関数って書くと難しく聞こえるかもしれませんが、呼んだら何かを処理してそのあとに何かしらを渡してくれる便利な奴くらいに思っておけば多分大丈夫です。
プログラム上での書き方は
型名 関数名(引数) {処理}
という風に書きます。今回は引数は書かれていないので無視してください。
この書き方の中の『型名』が渡してくれるモノの種類にあたります。今回だと『void』なので『何もない』を渡してくれますね。は?
……つまり、この関数に期待しているのは、渡してくれるモノの方ではなくて、処理をしてくれるということの方だ、ということです。
そして上記の英文と合わせると
void Start()
{
//ゲームの最初に呼ばれる処理を書く場所
}
ということになります。
// Update is called once per frame
void Update()
{
}
コメントには『Updateは1フレームごとに呼ばれるよ!』みたいなことが書かれてます。
そしてvoid Update(){}は関数です。
なので
void Update()
{
//1フレームごとにやってほしい処理を書く場所
}
ということになります。
オブジェクトを動かすコードとその中身
今回は
オブジェクトが一定のスピードで進み続け、x座標が5.0を越えたら止まる
という処理を書いてみたいと思います。
ちなみに、Unityでの位置関係での『1.0』というのはおおよそ『1.0m』だと思ってもいいと思います。まあ長さの単位は考えないのが正直1番いいかもしれませんが。
あと座標とかの話に関しては書いてって言われたら書くかもしれません、多分。
あとプログラムの記述には基本的に半角英数字のみを使うようにしましょう。全角を使うのはバグを引き起こす要因になりかねません。
それでも使ってしまうのはまあだいたいはウッカリか、あるいは一部の極まった変態のやることです。
コメントに関しては日本語で書いてもオッケーです。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CubeMover : MonoBehaviour
{
private float speed;
private Vector3 pos;
void Start()
{
speed = 0.05f;
this.transform.position = Vector3.zero;
pos = this.transform.position;
}
void Update()
{
pos.x = pos.x + speed;
this.transform.position = pos;
if(pos.x > 5.0f)
{
speed = 0.0f;
}
}
}
こんなとこですかね。
これをとりあえずコピペでもして上書き保存したあと、ProjectのCubeMoverをHierarchyのCubeにドラッグ&ドロップします。その結果CubeのInspectorの下のあたりにCubeMoverが追加されているのを確認してください。
確認したら画面真ん中上部の再生ボタンを押しましょう。
そうするとスィーっとキューブが動いたと思います。
動いたのを確認したら、コードの解説に入っていきましょう。
まずは一回コメントでやっていることをコードに書いてみます。
public class CubeMover : MonoBehaviour
{
private float speed; //float型の変数speedを宣言
private Vector3 pos; //Vector3型の変数posを宣言
void Start()
{
speed = 0.05f; //speedを0.05fに初期化
this.transform.position = Vector3.zero; //自分の位置を(0,0,0)の位置に持っていく
pos = this.transform.position; //posを自分の位置の値に初期化
}
void Update()
{
pos.x = pos.x + speed; //posの中のxの値を、自身にspeedを足したものにする
this.transform.position = pos; //自分の位置をposの値にする
if(pos.x > 5.0f) //もしもposの中のxの値が5.0fを越えたら
{ // ↓
speed = 0.0f; //speedを0.0fにする
}
}
}
わざとらしいくらいにしつこくコメントを書いたところで、ここもいくつかに分けて解説していきます。
ここからめちゃくちゃ長いので、コメント見てふんわり理解できた気がする、って人は下まで一気に飛ばしてもらってもいいかもしれません。
というか理解できてないかな、って人でもここはいったん飛ばしてください。
飛ばしてから戻ってくるほうが多分理解度が高いです。
private float speed; //float型の変数speedを宣言
変数を宣言しています。
変数というのは、中のデータを弄れる形の決まった箱みたいなものです。こいつらを足したり引いたり渡したり受け取ったりする処理を書いてプログラムが組みあがっていきます。
書き方は
修飾子 型名 変数名;
です。
修飾子は、変数の公開範囲を決めるモノです。今回の場合だとprivateが修飾子で、これは変数を外に見せません。
これだけではどういうことかわかりづらいと思うので、privateをpublicに書き換えて上書き保存したあと、CubeのInspectorを見てみましょう。多分それでわかると思います。
ちなみにこの修飾子、書かなくてもエラーにはなりません。その場合、自動的にprivateとして処理されます。
私がわざわざ書いてるのは趣味と、個人的にそっちのほうが見やすいって思ってるからです。
型名は、関数のときの型名と同様で、中のデータの種類を決定します。今回の場合だと、floatなのでこの変数はfloat型の変数になります。
floatは少数を扱える型で、ゲームの開発だとよく使います。
他にもいろいろな種類の型がありますが、ここに列挙するのはちょっと無理があるので、興味があるならググってください。
変数名は数字から始まらない限りは好きなようにつけてもらっても構いません。極論、a一文字とかでもエラーは出ません。
でもやっぱり意味のある名前にした方が当然分かりやすいですし、もし直前まで何を書いていたか忘れてしまっても(そういうことは多々あります)思い出しやすいです。
例えば今回ならspeedなのでそのままスピードなんだろうな、という予測がつきますね。
あるいは、厳密には『1フレームごとのスピード』なので『speedPerFrame』とかでもいいかもしれません。
private Vector3 pos; //Vector3型の変数posを宣言
Vector3は構造体です。構造体というのは、いくつかの変数をまとめたものを型として扱うようなものですね。
なのでこの場合、Vector3型の変数posの中に、さらに別の型の変数がいくつか入っている、と思っておけばだいたい大丈夫です。
その中の変数にアクセスするには、今回の場合だと
pos.変数名
と書けばいいです。
ちなみにVector3というのはベクトルが3つ、つまりX,Y,Zで三次元のことだと思っておけばいいです。
posはpositionの略です。
speed = 0.05f; //speedを0.05fに初期化
speedの中のデータの内容を0.05fという数値にしています。
fというのはfloatのfです。なんでわざわざつけなきゃいけないかというと、floatの他にも小数点を扱える型があるからですね。
で、変数というのは型にあった値しか=で入れられません。float型の変数ならfloat型の数を入れなければならないわけです。
なので私はfloatに使えるんですよ、って自己主張してあげなきゃいけないんです。だいたいそんな感じ。
ちなみにspeedの中に値を入れずに実行した場合、speedの中には0が入ってるのではなくて、謎の値(通称ゴミ)が入っています。(自分で何回か試した結果speedが0っぽい処理を返されたので、どうもUnityの場合察して0を入れてくれるのかもしれません)
これはメモリとかそういうあたりの問題なので説明は省きます。詳しくはググってね。
this.transform.position = Vector3.zero; //自分の位置を(0,0,0)の位置に持っていく
this.transform.positionは、thisの中のtransformの中のpositionです。
thisはこれ、つまり自分自身を指します。
transformは、CubeのInspectorの上部を見てもらったらあります。
オブジェクトの空間的な情報がまとめられている、と思っていたらだいたいあってます。
ちなみに親子関係もtransformの中に存在しています。この辺りはまた出てきたときに。
positionは位置です。上記のtransformの中にありますね。
Vector3.zeroは、Vector3型ですべて中身が0、つまりXもYもZも0の値を渡してくれます。
なのでこの部分は自分の位置をX=0,Y=0,Z=0の場所に持っていく、という意味になりますね。
pos = this.transform.position; //posを自分の位置の値に初期化
this.transform.positionは三次元空間における位置の情報なのでVector3型です。そしてposもVector3型なので、posの中にthis.transform.positionの値を入れることが出来ます。
pos.x = pos.x + speed; //posの中のxの値を、自身にspeedを足したものにする
this.transform.position = pos; //自分の位置をposの値にする
ここまで来て気が付いている人もいるでしょうが、プログラムにおける=は、数学の=とは違います。
数学の=は、左辺と右辺が等しいというものを示すものです。
一方プログラムの=は、左辺に右辺を代入するという処理を示しています。
posは3本のベクトルが合わさってVector3型ですが、その中のX,Y,Zそれぞれはfloat型です。
なのでfloat型のspeedと計算した結果を、=を使って自分自身に入れることが出来ます。
if(pos.x > 5.0f) //もしもposの中のxの値が5.0fを越えたら
{ // ↓
speed = 0.0f; //speedを0.0fにする
}
if文です。
書き方は
if(条件式){処理}
です。
条件式の内容が正しければ{}の中の処理をして、正しくなければ処理を無視して先に進みます。
今回の場合だと、最初のうちは0からスタートしてpos.xは当然5.0fより小さいのでif文の中身を通りません。
しかし、ゲームが続くにつれてspeedが足され続けて、ついには5.0fを越えてしまいます。
そのとき、条件式が正しいと判断され、if文の中身の、ここではspeedを0.0fにするという処理が行われます。
その結果、speedが0.0fになったCubeはピタッと止まることになります。
このif文、ゲームを作る際にはめちゃくちゃ重要で、極論を言ってしまえばこれだけあればゲームの中身は作れちゃいます。
この条件の時はコレする、この条件ならアレする、この条件なら……といった風にしていけば最終的にはゲームになってるんですよね。
だからこの時点で、これを読んでいる皆さんはもうゲームが作れちゃいます。
まあ実際は当然そううまく行かなかったりするんですけども。
解説なんかより
上に長々と書いてますけど、解説読むなんかよりも実際にコードの中身弄ったほうが絶対に分かりやすいです。
コードの中の数値が入ってる部分を別の数値に変えてみてください。
Cubeが爆速でぶっ飛んで行ったり、無限のかなたまで進み続けたり、そもそも初期位置から動かなかったり。
それだけで何が起きてるかはだいたいわかると思いますし、なによりそっちでやった方が楽しいです。
ひとしきり数値弄って楽しんだ後に、もしもヒマがあるなら、上に戻って長々とした解説を読んでみてください。
今回はこんなところで止めておきます。次の記事はプレイヤーが動かせるキューブをつくるとこからですね。
次の記事……の前に少しだけ
https://qiita.com/VRCjonmyon34/items/cfdb72596d468a377ad6