取り合えず動けば良いの精神で作るゲームチュートリアルの中編です。
ではさっそく続きを作っていきます。
プレイヤーを作成
前回プレイヤーにスクリプトをくっつける所まで行いましたので、ゴリゴリ書いていきます。
その前に、プレイヤーを動かすために必要な「Rigidbody」をPlayerオブジェクトに追加します。
HierarckyのPlayerオブジェクトを選択し、Inspectorの一番下にある「Add Component」をクリックします。
「physics」をクリック「Rigidbody」をクリックします。
無事にRigidbodyが追加されているかと思います。Rigidbodyがあると物理演算が行われ重力もかかります。
ここでミスに気づきました。ゴールを守る壁の高さが低かったので1大きくします。
「Wall」を選択し、InspectorのPositionのYを1に、ScaleのYを2に変更します。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerScript : MonoBehaviour { public float speed; public float jumpSpeed; public bool isFloor = true; Rigidbody rb; // Use this for initialization void Start () { rb = GetComponent<Rigidbody>(); } // Update is called once per frame void Update () { if (Input.GetKey(KeyCode.RightArrow)) { rb.MovePosition(transform.position + transform.right * speed * Time.deltaTime); } else if (Input.GetKey(KeyCode.LeftArrow)) { rb.MovePosition(transform.position + transform.right * speed * -Time.deltaTime); } else if (Input.GetKey(KeyCode.UpArrow)) { rb.MovePosition(transform.position + transform.forward * speed * Time.deltaTime); } else if (Input.GetKey(KeyCode.DownArrow)) { rb.MovePosition(transform.position + transform.forward * speed * -Time.deltaTime); } if (Input.GetKeyDown(KeyCode.Space)) { if (isFloor == true) { rb.velocity = new Vector3(0, jumpSpeed, 0); isFloor = false; } } } void OnCollisionEnter(Collision c) { if (c.gameObject.tag == "Floor") { isFloor = true; } } public float ForceGravity = 20.0f; void FixedUpdate() { rb.AddForce(Vector3.down * ForceGravity, ForceMode.Acceleration); } }
PlayerScriptを表示して上記のように記載し保存します。
ざっくり書きますが、必要な変数を用意して、Start関数でPlayerについているRigidbodyを取得します。
毎秒処理されるUpdate関数で矢印キーとスペースキーの入力を待っています。
Pleyerオブジェクトを選択し、Inspectorの「PlayerScript」を見るとさっきまでなかった「Speed」などの項目が増えてる事がわかるかと思いますのでSpeed=5、Jump Speed=10に設定します。
画面真ん中の上の方にあるプレイボタンを押すとゲームが開始されます。
十字キーの上下左右で移動、スペースキーを押すとジャンプしますが、ジャンプした後もうジャンプできないかと思います。
これはスペースキーが押された時にisFloorという変数にtrueが入ってた場合はジャンプをし、ジャンプをした時にisFloorをfalseにする処理が入っているからです。ジャンプが終わり床に接触するとisFloorをまたtrueにする処理が入っていますので、本来またジャンプ出来るようになるはずなんですが出来ません。
それはc.gameObject.tag == “Floor”で接触した相手にFloorというタグが入っている場合に処理が動くようになっているからです。
Playerにタグをつけた時と同じにタグをつけます。ただ、Floorというタグは用意されてませんので作成する必要があります。
HierarchyのFloorを選択し、InspectorのTagを選択、「Add tag」をクリックします。
プラスボタンを押して「Floor」というタグを作成します。
作成したらFloorのタグに「Floor」を選択します。
出来たら実行してみて下さい。ジャンプして床に着地すると、再度ジャンプできるようになっていると思います。
動かせる箱を作成
今度は押したり乗ったり出来る箱を準備します。Hierarchyのcreateから「Cube」を作成し、名前を「Box1」に変更します。
InspectorのTransformにあるPositionをX=-2、Y=0.5、Z=1に変更してください。
一度実行してみます。プレイヤーを動かして白い箱に体当たりをすると動かない事が確認できるかと思います。これだと移動出来ないし問題がありますので動くようにします。
Box1を選択した状態でInspectorの「Add Component」を押して、「physics」をクリック「Rigidbody」をクリックします。
Rigidbodyが追加されたら「Constraints」をクリックして開き、「Freeze Rotation」のXYZにチェックをいれます。ここにチェックを入れるとBox1が回転しなくなります。間違って「Freeze Position」にチェックを入れると動かなくなるのでダメです。
実行してプレイヤーをぶつけてみて下さい。箱が動くのを確認できると思います。
Box1は白いままでもいんですが、折角なのでまた色を付けておきます。Projectの「MaterialFolder」を選択して「Create」、「Material」とクリックします。出来たら「BoxMaterial」と名前を付けて下さい。「Albedo」から好きな色に変更して、BoxMaterialをBox1へドラッグドロップします。
Box1の「Tag」を「Floor」にします。これはジャンプでBoxに乗った時に再度ジャンプ出来るようにする為です。本当ならBoxタグを作ってスクリプトを修正した方が良いと思うのですが動けばいのでFloorにしてしまいます。
箱をもう一つ用意したいのでBox1を右クリックし「Duplicate」をクリックします。Duplicateは重複とか複写という意味があるようです。
新しくできたBoxの名前をBox2に変更し、PositionをX=3、Y=0.5、Z=-2に、ScaleのX=2に変更します。
実行してジャンプして壁を飛び越えて貰うとわかるかと思うんですがPlayerが回転してグラグラしたりこけて進めなくなったりします。のでPlayerも箱と同じように「Freeze Rotation」のXYZにチェックをいれて固定してしまいます。
ゴールを作成
ゴールを作成していきます。「Create」の「3D Object」「Plane」を作成して名前を「Goal」に変更します。
Cubeでも何でもいんですが薄いものを置きたいのでPlaneにしています。
GoalのTransformを下記に変更します。
PositionをX=3、Y=0.05、Z=4.3、ScaleをX=0.1、Y=1、Z=0.1
GoalにはRigidbodyはつけないので位置は浮いていても問題ありません。通り抜けます。
Goalに「Goal」というタグを追加します。
PlayerがGoalに接触したら次の面に進む仕組みを作る際に、タグをつけておくと便利です。
「ScriptFolder」を選択した状態でProjectの「create」、「C# Script」を選択て新しいスクリプトを作成し、名前を「GoalScript」に変更します。
名前を変更したらHierarchyのGoalにドラッグドロップします。
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; public class GoalScript : MonoBehaviour { public GameObject clearText; void OnCollisionEnter(Collision c) { if (c.gameObject.tag == "Player") { if (SceneManager.GetSceneByName("stage1").IsValid()) { SceneManager.LoadScene("stage2"); } else if (SceneManager.GetSceneByName("stage2").IsValid()) { clearText.SetActive(true); } } } }
GoalScriptをエディタで開き上記のように書き換えます。
4行目でSceneManagementを読み込んでいますがシーンを変更するのに必要なようです。
やっていることはPlayerというタグを持っているオブジェクトと接触したら現在のシーン名を確認し、次のシーン、またはクリアーを表示します。
次のステージを作成
既存のシーンの名前をSanpleSceneからstage1に変更します。
メニュー左上にある「File」を選択し、「New Scene」をクリックします。するとすぐ新しいシーンが作成されます。
「File」を選択し、「Save Scenes」を選択すると保存画面が出ると思いますので「stage2」として保存します。
空っぽのステージが出来たかと思います。Projectにある「stage1」をダブルクリックすると元のsceneが表示されると思いますので実行してみます。
壁を乗り越えてゴールまで行ってもエラーが出るだけでステージは切り替わりません。
「File」を選択し、「Build Settings」を選択し、出てきたダイアログにProjectの「stage2」をドラッグドロップします。
新しく作成したSceneを読み込めるようにするにはいちいちBuild SettingsにSceneを登録してあげる必要があります。面倒ですね。
登録したら改めて実行し、ゴールにたどり着くと次のステージに進めたかと思います。もうこれでほぼ完成ですね!
stage2を作成
次にstage2を作るのですが一から作るのは面倒なのでstage1で使用したオブジェクトたちをプレハブ化します。取り合えずProjectに「PrefabFolder」を作成し、HierarchyにあるオブジェクトをどんどんPrefabFolderにドラッグドロップします。すると上の画像の様に青い四角のプレハブが作られます。
プレハブを作成したら「stage2」をダブルクリックし、Sceneを切り替えます。Projectに作成したプレハブを今度はScene上にドラッグドロップします。ほぼstage1と同じような形になったかと思います。ついでにscene2をScenesフォルダーに移動しておきます。
Hierarchyの「Wall」を右クリックし「Duplicate」を選択します。複製出来たら位置をtransision「X=0,Y=1=-2」に変更します;
壁にプレイヤーが埋まってしまったので「Pleyer」の「Position」をX=2、Y=0.5、Z=-4に変更します。
「Box1」の「Position」をX=-2、Y=0.5、Z=-3に変更します。その後Box1をDuplicateで複製するか、プレハブからもう一つドラッグするかしてBox2を作成します。
「Box2」の「Position」をX=3、Y=0.5、Z=1に変更します。
最後にクリアー時に表示されるテキストを作成します。Hierarchyの「Create」から「UI」、「Text」を選択します。
「Text」のRect TransformのPosX=0、PosY=0、PosZ=0に変更し、Width=200、Height=80にします。
次に「Text」=CLEARに変更し、Font Size=30、Alignmentを中央よせにします。
文字を大きくした時に文字が画面に表示されなくなる時があります。その場合Textの枠が足りてないのでwidth、Heightを調整する必要があります。今回はwidth200、Height80で十分入るように設定しています。
Textの名前の横にあるチェックボックスを外して非表示にしておきます。
Goalを選択するとInspectorの「Goal Script」に「Clear Text」があると思います。ここにさっき作成した「Text」をドラッグドロップします。
落下処理
ステージから落下するとどこまでも落ちてしまいますので落下したらステージを読み込みなおす処理をつくります。
Hierarchyの「create」から「Plane」を作成して名前を「EndZone」にします。
EndZoneのPositionをX=0、Y=-3、Z=0にし、ScaleをX=5、Y=1、Z=5にします。
「MaterialFolder」に新しく「EndZoneMaterial」を作成し、「Rendering Mode」を「Fade」に変更し、透明度を0にします。設定したらEndZoneにドラッグドロップしてくっつけ、透明にします。
「ScriptFolder」に新しく「EndZoneScript」を作成し、EndZoneにドラッグドロップでくっつけます。
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; public class EndZoneScript : MonoBehaviour { void OnCollisionEnter(Collision c) { if (c.gameObject.tag == "Player") { if (SceneManager.GetSceneByName("stage1").IsValid()) { SceneManager.LoadScene("stage1"); } else if (SceneManager.GetSceneByName("stage2").IsValid()) { SceneManager.LoadScene("stage2"); } } } }
「EndZoneScript」の中身を上記に変更します。Goalの時と同じく「using UnityEngine.SceneManagement;」を追加してください。
EndZoneにぶつかったのがプレイヤーだった時、同じステージを読み込みなおします。
これで実行して試しに落下してみると、復活はするものの何故かライトが暗くなると思います。
なんで暗くなってしまうのか不明ですが、とりあえずメニューの「Window」、「Lighting」、「Settings」と進みます。
「Auto Generate」のチェックボックスを外して、「Generate Lighting」をクリックします。その後実行して落ちてみるとライトが赤いままなのがわかると思います。
EndZoneをPrefabFolderにドラッグドロップしてプレハブ化します。
Stage1に切り替えてEndZoneを同じように追加します。さっきと同じように「Generate Lighting」も設定します。
完成
ゲームを実行してゴールすると、CLEARのテキストが表示されると思います。クリアー後もプレイヤーは動けますが気にしませんw
画面左上のSceneビューは作成時に使用するビューでゲーム時は画面左下の「Game」ビューが使用されます。今のカメラ視点だとかなりやりにくいのでSceneをいい感じの視点に変更し、「Main Camera」を選択した状態で「Ctrl+Shift+F」キーを押して下さい。GameビューもSceneビューと同じ視点になると思います。
Stage1,2共に視点を調整したら完成です。お疲れ様でした!
コメント