Upgrade to Pro — share decks privately, control downloads, hide ads and more …

1day_unity_breakout

au-niji
May 05, 2019
2.2k

 1day_unity_breakout

1日でunityを使用してブロック崩しを作った際にまとめた資料

au-niji

May 05, 2019
Tweet

Transcript

  1. 1 日で学ぶ Android 向け Unity 講座〜ブロック崩し〜 制作:au・神 注意 この文書制作者は Mac

    で制作しています。 UI などの違いがあるかもしれません。 使用したもの Unity 2019.1.1 VSCode Visual Studio 書き出しの際は以下のサイトを参照 https://qiita.com/taroyan/items/2ff7f6aadd640221bde5 この講座を受講すると、1 日で Unity を使った Android 向けの簡単な 2D ゲームを作成するや り方を学ぶことができます。 ゲームフロー案 なお今回は NextStage を作る時間がなかったため、ログで「NextStage」と出るだけです。 全体のフローが完成した後、追加システムの案を神さんに提案し、一部実装していただきまし た。その際に提案させていただいた資料はこちらです。
  2. 以上でプロジェクトの作成は終わりです。 3. Android 向けに画⾯を作成する このゲームは Android 向けのゲームなので、画⾯⽐を神の Android に合わせるように します。

    自動的に合わせるやり方は以下を参考にするといいと思う。 (https://0kara.tokyo/unity-android-screen-setting/) File→Build Setting を選択
  3. Hierarchy タブにある SampleScine を右クリックして GameObject→UI→Canvas を選 択して作成します。Canvas は、UI(User Interface: 設定ボタンやメニューなど)を載せ

    るための土台になります。 Canvas をクリックすると Inspector というタブができるので、以下のように設定します。 Canvas Scaler(Script)の UI Scale Mode を Scale With Screen Size に変更し、 Reference Resolution を X720, Y1280 に変更しました。
  4. Canvas コンポーネントで右クリックして、UI→Text を追加、test という文字を Font Size100 で作成しました。 文字がはみ出ていると表示されないので、Pragraph の設定を Overflow

    にすると出る ようになります。ただ、ちゃんと範囲設定をして Overflow しないようにするのが基本 なので、あまりしないほうがいいと思います。
  5. 画像を表示させようと思います。 今回使用するのは 256x256 の真っ白の画像です。 Project に Image とフォルダを作成し、開いた所に画像をドラッグ&ドロップで追加する ことができます。 画像を

    Scene にドラッグ&ドロップすることで追加することができます。そのあとは Scene で画像をクリックしてサイズなどを変更することもできます。 5. 背景の設定 背景を追加しようと思います。と⾔っても、720x1280 の画像を作って貼り付けるだけ でできました。 上の⽩い画像のようにドラッグ&ドロップで追加しましょう。後は座標 x0,y0 に設定す るだけです。
  6. Box Collider 2D が追加されたので、そこの Edit Collider の左側にあるマークをクリッ クすると⽩い画像が緑⾊の線で囲まれると思います。これがコリジョンです。 画⾯を囲むように配置します。Command +

    D キーで複製することができるので活⽤し ていきましょう。 画⾯に触れないように配置したので、 下のゲーム画⾯のプレビューでは⽩い画像は⾒え ません。
  7. 開こうと思ったけど VisualStudio のインストールしてくれとしか⾔われない…VS Code を使いたいのになって場合、Unity からファイルを開く場合のエディタの設定を 変える必要があります。 (VScode で Unity

    のスクリプトの⾃動補完をする場合は設定が 必要です。 正直めんどくさいと思うので Visual Studio for mac を⼊れたほうが早かった です) Unity→Perferences…をクリック External Tools の External Script Editor のマークダウンをクリックし、Browse…から VSCode を選択します。Application フォルダの中にあると思われます。
  8. [SerializeField] private float moveXMin = 0; [SerializeField] private float moveXMax

    = 0; // Start is called before the first frame update void Start() { mainCamera = FindObjectOfType<Camera>(); } // Update is called once per frame void Update() { /* float speed = 0.1f; if (Input.GetKey(KeyCode.LeftArrow)) { Vector3 pos = gameObject.transform.positio n; pos.x -= speed + Time.deltaTime; gameObject.transform.position = pos; } else if (Input.GetKey(KeyCode.RightArrow)) { Vector3 pos = gameObject.transform.positio n; pos.x += speed + Time.deltaTime; gameObject.transform.position = pos; } */ var touchInfoList = InputUtility.GetTouchInfoe s(); if (touchInfoList.Count > 0) { var touch = touchInfoList[0]; var worldPos = mainCamera.ScreenToWorldPoi nt(touch.touch.position);
  9. var objPos = gameObject.transform.position ; objPos.x = Mathf.Clamp( worldPos.x, moveXM

    in, moveXMax); gameObject.transform.position = objPos; } } } InputUtillity.cs . using System.Collections; using System.Collections.Generic; using UnityEngine; public enum MyTouchPhase { // UnityEngine.TouchPhase で定義された内容 Began = 0, Moved = 1, Stationary = 2, Ended = 3, Canceled = 4, // 追加 None = 100, } public struct TouchInfo { public MyTouchPhase phase; public bool isMousePointer; public Touch touch; public TouchInfo(Touch _touch)
  10. { phase = (MyTouchPhase)_touch.phase; isMousePointer = false; touch = _touch;

    } public TouchInfo(Vector2 mousePosition) { phase = (MyTouchPhase.None); isMousePointer = true; touch = new Touch(); if (Input.GetMouseButtonDown(0)) phase = MyTou chPhase.Began; else if (Input.GetMouseButtonUp(0)) phase = My TouchPhase.Ended; else if (Input.GetMouseButton(0)) phase = MyTo uchPhase.Moved; touch.position = mousePosition; } } public static class InputUtility { public static List<TouchInfo> GetTouchInfoes() { #if (UNITY_EDITOR || UNITY_STANDALONE || UNITY_WEBGL) // && false // マウスの位置からタッチ情報を作成しています TouchInfo touchInfo = new TouchInfo(Input.mous ePosition); return new List<TouchInfo>() { touchInfo }; #else // 実際のタッチ情報を var list = new List<TouchInfo>(); foreach( var touch in Input.touches) {
  11. var touchinfo = new TouchInfo(touch); list.Add(touchinfo); } return list; #endif

    } } InputUtillity でタッチの位置を取得し、 プレイヤーの動きになるように PlayerObject で 動きの処理にしています。 Player の動きは x 軸しか動きません。Player は左側(x 軸のマイナス⽅向)と、右側(x 軸 のプラス⽅向)に進みます。 次に、作成したファイルをゲームオブジェクト(Player)に適応します。 Player オブジェクトをクリックした後、適応させたいファイルをドラッグ&ドロップで 追加することができます。
  12. 8. ボールを動かす Player の操作が可能になったら、次はボールを動かしてみましょう。 今回使⽤した画像はこちらです。ただの円ですね。 Player と同じく、コンポーネントを追加していきます。 Add Component→Physics 2D→Circle

    Collider 2D と Add Component→Physics 2D→Rigidbody 2D を追加します。 Circle Collider は、円形のコリジョンを追加。Rigidbody は、物理シミュレーションを 追加するためのコンポーネントです。 物理シミュレーションで⼒を与えて、 コリジョン同⼠がぶつかると反対のベクトルに数 値を変えて反射させるイメージです。 ball の Inspector を以下のように設定します。 それぞれ重⼒の⼤きさや質量の⼤きさを設定します。
  13. Friction と Bounciness を以下のように設定。 ボールを動かしたり、反射を計算するためのスクリプトを書いていく。Player の記述と 同じように、Script にファイルを作成する。忘れないように ball のオブジェクトに適応

    しましょう。 計算部分の説明は省きますが、壁に当たった時と Player に当たった時で速さ(velocity) を変えています。Player のどこに当たったのかも判定していて、跳ね返る速度が変わっ てきます。 BallObject.cs using System.Collections; using System.Collections.Generic; using UnityEngine; public class BallObject : MonoBehaviour { /// <summary> /// 最高速度(x 成分または y 成分) /// </summary> public float MaxSpeed; //private float speed = 10; private Rigidbody2D rigidbody2d; private void Awake() { }
  14. // Start is called before the first frame update void

    Start() { // body = this.GetComponent<Rigidbody>().AddFo rce( // (transform.forward + transform.right) * speed, // ForceMode.VelocityChange); rigidbody2d = this.GetComponent<Rigidbody2D>() ; rigidbody2d.velocity = new Vector2(0.0f, - MaxSpeed); StageManager.Instance.OnCreateBall(this); } // Update is called once per frame void Update() { //float vs = body.velocity.magnitude; //if (vs > speed) //{ // body.velocity = body.velocity / vs * spe ed; //} Vector2 v = rigidbody2d.velocity; v.x = Mathf.Clamp(v.x, -MaxSpeed, MaxSpeed); v.y = Mathf.Clamp(v.y, -MaxSpeed, MaxSpeed); rigidbody2d.velocity = v; } private void OnCollisionEnter2D(Collision2D collis ion) { if (collision.gameObject.name == "under_wall") {
  15. Destroy(this.gameObject); //gameOverObj.Show(true); return; } if (collision.gameObject.tag == "Player") { Vector3

    posPlayerCenter = collision.collid er.bounds.center; Vector3 posPlayerRight = collision.collide r.bounds.max; Vector3 posHit = gameObject.transform.posi tion; float t = (posHit.x - posPlayerCenter.x) / (posPlayerRight.x - posPlayerCenter.x); t = Mathf.Clamp(t, -1.0f, 1.0f); var v1 = rigidbody2d.velocity; rigidbody2d.velocity = v1 + 100.0f * v1.no rmalized; if (t < -0.7f) { var v = rigidbody2d.velocity; rigidbody2d.velocity = new Vector2(v.x - MaxSpeed * 1.0f, v.y); } else if (t > 0.7f) { var v = rigidbody2d.velocity; rigidbody2d.velocity = new Vector2(v.x + MaxSpeed * 1.0f, v.y); } //Debug.Log("Hit:" + t); } } private void OnDestroy() {
  16. StageManager.Instance.OnDeleteBall(this); } public void Stop() { rigidbody2d.simulated = false; }

    public void Play() { rigidbody2d.simulated = true; } } Ball の object に Script を適応しましょう。 Max Speed を書き換えることでボールのスピードを変更することが可能です。これは、 public で書かれた変数の初期値になります。 Player の操作と同じく、▶ボタンを押して実⾏してみましょう。壁や Player にぶつか って跳ね返るはずです。
  17. 9. ゲームオーバー 次にゲームオーバーの機能を作っていきます。Player の下にある壁にぶつかったら Game Over と出て、Restart ボタンを押すと最初からスタートするようにします。 完成はこんな感じ。 先に表⽰する⽂字とボタンを作ります。

    Hierarchy の Canvas を選択し、Create Empty を「GameOverObject」と⾔う名前で作 成します。これは空のオブジェクトで、この中に⽂字とボタンを作成することで、 GameOverObject を⾮表⽰にすることで⼦階層のオブジェクトを⼀緒に⾮表⽰するこ とができます。逆に表⽰する場合も GameOverObject を表⽰するだけで⽂字とボタン を表⽰することができます。 つまり、スタートからゲームオーバー直前までは⽂字が隠れていて、ゲームオーバーに
  18. そしたら次に、下の壁を配置します。上の壁を Command + D キーで複製し、下に置き ました。 ついにスクリプトです。 Project の Assets→Script

    に GameOver という C#ファイルを作 成し、以下のように記述します。 GameOver.cs using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; public class GameOver : MonoBehaviour { private Rigidbody2D Ball; //public GameObject gameover; // Start is called before the first frame update void Start() { ////gameover.gameObject.SetActive(false); //GameObject.Find("Button").SetActive(false); //Debug.Log(gameObject.name); //GameObject.Find("GameOverText").SetActive(fa lse); } // Update is called once per frame
  19. void Update() { } public void Show(bool flag) // この関数は

    bool 型の引数 を受け取る { gameObject.SetActive(flag); // 受け取った引数によっ て表示・非表示をする } public void OnPushRetry() { SceneManager.LoadScene("SampleScene"); } } under_wall のコリジョンに当たったら関数名 Show に true を渡して⽂字列とボタンを 表⽰する。 Ballmove.cs の⻩⾊いラインの部分が、 ゲームオーバーの処理に当たります。 Ballmove.cs // 黄色を処理が GameOver で使用される処理の Script public GameOver gameOverObj; private void OnCollisionEnter2D(Collision2D collision) { if (collision.gameObject.name == "under_wall") { gameOverObj.Show(true); // GameOver.cs の関 数名 Show に true を渡す return; } Hierarchy の ball を選択し、Inspector の Ball Move(Script)の Game Over Obj に GameOverObject(GameOver)を適応させます。これは初期値を与える処理です。これ をしていないでエラーが起きると言うのが多々あったので忘れずにしましょう。
  20. 全体の状況を管理するファイルも作成します。同じく C#の Script で、StageManager と、GameSceneManager を作成します。 StageManager.cs using System.Collections; using

    System.Collections.Generic; using UnityEngine; using System.Linq; public class StageManager : MonoBehaviour { private static StageManager _Instance;
  21. public static StageManager Instance { get { if (_Instance ==

    null) // まだ未セット、もしくは Destroy されてた { _Instance = FindObjectOfType<StageManager >(); } return _Instance; } } private void OnDestroy() { _Instance = null; } /// <summary> /// ステージ中に配置されたブロック /// </summary> [System.NonSerialized] public List<BlockProc> blocks; // Start is called before the first frame update void Start() { blocks = FindObjectsOfType<BlockProc>().ToList(); // とりあえず } // Update is called once per frame void Update() {
  22. } public void CreateStage() { } public void OnDeleteBlock(BlockProc block)

    { blocks.Remove(block); if (blocks.Count == 0) { Debug.Log("!全部消しました!"); GameSceneManager.Instance.CallStageClear(); } } } GameSceneManager.cs using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameSceneManager : MonoBehaviour { private static GameSceneManager _Instance; public GameClear clear; public static GameSceneManager Instance { get { if (_Instance == null) // まだ未セット、もしくは Destroy されてた {
  23. _Instance = FindObjectOfType<GameSceneMan ager>(); } return _Instance; } } private

    void OnDestroy() { _Instance = null; } // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } public void CallGameOver() { } public void CallStageClear() { clear.Show(true); Debug.Log("!ステージクリアですです!"); } }
  24. 次に Script を作成します。Assets→Script のファイルに GameClear と名付けて保存 します。 GameClear.cs using System.Collections;

    using System.Collections.Generic; using UnityEngine; public class GameClear : MonoBehaviour { // Start is called before the first frame update void Start() { } public UnityEngine.UI.Text scoreLabel; // Update is called once per frame void Update() { } public void Show(bool flag) { gameObject.SetActive(flag); } public void OnPushRetry() { Debug.Log("Next Stage"); } } GameOver と同じく、Show と言う関数で表示を管理しました。 そして、GameClear の条件はオブジェクトの数が 0 になる場合かつ、ゲームが終わっ ていない場合です。
  25. StageManager.cs // 以下を追加 public void OnDeleteBlock(BlockObject block) { blocks.Remove(block); if

    (blocks.Count == 0 && !EndStage) { Debug.Log("!全部消しました!"); balls.ForEach(x => x.Stop()); // 全てのボール停 止 GameSceneManager.Instance.CallStageClear(); EndStage = true; } } GameSceneManager.cs // 以下を追加 public void CallStageClear() { clear.Show(true); Debug.Log("!ステージクリアですです!"); } 次のステージへ進む場合はここに処理を記述します。
  26. 次の画面に進むための NextButton には、Click をした際の処理を記述します。 GameOver の時と同じような処理になります。 11. 書き出しをする 書き出しをしたのは私ではなく神なので、 眺めるだけになってしまいましたごめんなさ

    い。参考にしていたサイトはこちらです。 https://qiita.com/taroyan/items/2ff7f6aadd640221bde5 Android 側の設定 Android 端末を開発者モードにする。 Android 端末とパソコンを USB 接続 Unity 側の設定 File→Build Settings を選択
  27. プロジェクト名やバージョンを記入 Build And Run をする。 神に学んだこと 他のプログラミングにも言えるが、ファイルの命名や階層の分け方を学んだ。 オブジェクト指向でのゲームプログラミングを学んだ。ステージの管理、Scene の管 理をするファイルを作成することで、ゲームクリアの判定やゲームオーバーと言った、

    状況に応じた処理を記述するやり方を学んだ。 Unity の基礎中の基礎的な扱い方。コリジョンの付け方やファイル作成、C#の扱い 方など。 UE4 と比べてみて感じたこと(au) UE4 を使ってドットイートゲームを作った au です。 Unity も UE4 も、初心者なりに使ってみて比べてみたので是非読んで見てください。 1 点目は、どちらも作業画面から得られる情報はほぼ同じと言うことです。下に画像 を載せたのですが、それぞれ特に違いはないのではないかと思います。 違いがあるとすれば、UE4 ではモードパネルから基本のアクタを配置することができ るのですが、Unity は右クリックで探して追加するくらいだと思います。
  28. 2 点目は、Unity は全く落ちる気配がないことです。UE4 の場合は、作業している間 ずっとパソコンが排気をしていて明らかに重い作業をしていたのですが、Unity はそれ がなかったです。単純に 2D のことをやっているからな可能性が高いですが・・・ 3

    点目は、ブループリントが存在しないことです。UE4 では、ブループリントにだいぶ お世話になりました。ほとんどエラーがなく、直感的に作成することができるのでとて も楽ができたのですが、Unity の場合は、C#でそれぞれの機能を記述する必要があ りました。神の指導のおかげでサクサクいったように見えましたが、「やりたいこと」と 「実際に書いていること」があっておらず、毎回相談しながらやったり、神のコードを見 て真似をしているということしかできませんでした。直感的にできるブループリントはや はりすごいと感じました。 どちらのゲームエンジンから入るのがやりやすいかと言う疑問があると思います が、私がやった段階では、初心者ならばどちらでも構わないと思いました。正直参考 書や教えてくれる講師のような人がいない限りどちらも難易度が同じくらいだと思いま す。プログラミング周りを必ず扱う Unity は、書き方に慣れていないとそれなりに苦労 するところだと思うので、それが嫌ならば UE4 のブループリントを選ぶのがいいと思 いました。