Skip to main content

夏休み・練習課題

義務ではないが、練習不足と思う学生にはおすすめ

目標

  • Unityでの開発と調査能力を向上する
  • 先生が教えていない課題もあるので、少し調べる必要な場合もある
  • 迷わず、判断力、開発スピードをUPする
  • 自分の今のゆるいところを明確し、固める

ポイント:

  • 順番気にせず、答えれるのを先に答えて、難しい質問は最後に残す
  • 最後に回答があるが、課題により、いくつかの解決方法がある。
  • 下記の解決は、あくまで先生のやり方である。

C# / スクリプト(言語、およびUnityのの基礎)

次の質問をC#で回答してください(1行〜5行程度)

例:2つの整数を足してください:

int sum = 20 + 15;

1. ゲーム実行中に「enemyPrefab」プレハブから具体的なゲームオブジェクト「enemy」を作成してください。

答え
GameObject enemy = Instantiate(enemyPrefab);

2. 「ball」というゲームオブジェクトからRigidbodyのコンポーネントを取得てください。

答え
Rigidbody rb = ball.GetComponent<Rigidbody>();

3. 取得したRigidbodyの速度は、X軸で毎秒5単位、Z軸で毎秒-1単位にしてください。また、Y軸の速度を変えないでください(元の通りに保持する)

答え
Vector3 velocity = rb.linearVelocity;
velocity.x = 5;
velocity.z = -1;
rb.linearVelocity = velocity;

4. 取得したRigidbodyをY軸周りに90度回転してください。

答え

Rigidbodyで動かす場合、Transformをいじるのではなく、Rigidbodyのメソッドを使わないといけない。Rigidbodyを回転させるには「MoveRotation」を使用する。

rb.MoveRotation(Quaternion.Euler(0, 90, 0));

5. スペースキーを押したら、「ball」ゲームオブジェクトを削除する処理を書いてください。

答え
if(Input.GetKeyDown(KeyCode.Space)
{
    Destroy(ball);
}

6.「box」というゲームオブジェクトの位置を「x=1, y=0.5, z=0」にしてください。

答え
box.transform.position = new Vector3(1, 0.5f, 0);

7. 「box」というゲームオブジェクトの位置を「x=5」にし、「y」と「z」は元の数値のままにしてください。

答え
Vector3 pos = box.transform.position;
pos.x = 5;
box.transform.position = pos;

8.「box」というゲームオブジェクトの位置を現在の位置よりも、左の方に1単位に動かしてください。

答え

少なくても、2つのやり方がある

Vector3 pos = box.transform.position;
pos.x -= 1;
box.transform.position = pos;

 または 

box.transform.Translate(-1, 0, 0);

9. Unityで「1〜100」の間に乱数10個を生成してください。

答え
for (int i = 0; i < 10; i++)
{
  // 整数の場合は最大値を除く(1~100)
  int rnd_i = Random.Range(1, 101); 

  // 小数型の場合は、最大値を含む
  float rnd_f = Random.Range(1.0f, 100.0f);
}  

10. 「cat」と「mouse」の2つのゲームオブジェクトの間の距離を求めてください。

答え
Vector3 catPos = cat.transform.position;
Vector3 mousePos = mouse.transform.position;
Vector3 direction = mousePos - catPos

float distance = direction.magnitude;

11. 「cat」と「mouse」の距離が「1.5」単位よりも小さければ、「mouse」は「cat」から「3」単位を離れる。

答え
if (distance < 1.5f)
{
    mousePos = catPos + direction.normalized * 3.0f;
    mouse.transform.position = mousePos;
}

12. 4種類のTシャツのサイズ(Small、 Medium、 Large、 ExtraLarge)を表す列挙型「ShirtSize」を宣言してください。

答え
enum ShirtSize
{
    Small,
    Medium,
    Large,
    ExtraLarge
}

13. ShirtSizeの列挙から「myShirt」の変数を宣言し、ラージサイズにする

答え
ShirtSize myShirt = ShirtSize.Large;

14. Debug.Logを使い、Tシャツのサイズにより、日本語でサイズを表示してください:
      Small:“小”、Medium:”並”、Large:”大”、ExtraLarge:”特大”

答え
switch (myShirt)
{
    case ShirtSize.Small:
        Debug.Log("小");
        break;
    case ShirtSize.Medium:
        Debug.Log("中");
        break;
    case ShirtSize.Large:
        Debug.Log("大");
        break;
    case ShirtSize.ExtraLarge:
        Debug.Log("特大");
        break;
}

15. 入力クラス(Input)を使い、上の矢印キー(↑)を放せば、Debug.Logで”JUMP!”というメッセージを表示してください。

答え
if (Input.GetKeyUp(KeyCode.UpArrow))
{
    Debug.Log("JUMP!");
}

16. 入力クラス(Input)を使い、InputManager で定義した ”Attack” アクションが行った場合、Debug.Logで”攻撃”を表示してください。

答え
if (Input.GetButtonDown("Attack"))
{
    Debug.Log("攻撃");
}

シーン、ゲームオブジェクト、コンポーネント

 次の質問を文章で回答してください:

17. ゲームオブジェクトとコンポーネントの違いを説明してください。

答え

ゲームオブジェクト:シーン(Hierarchy)の中にある1つのもの(例:プレーヤー、敵対、木、壁、スコアの表示など)

コンポーネント:ゲームオブジェクトにアタッチするものであり、ゲームオブジェクトの機能を増やす(例:物理演算:Rigidbody、当たり判定:Collider、サウンド再生:AudioSource、カスタム機能:スクリプト)

18. Rigidbodyというコンポーネントはいつ使う?

答え あるゲームオブジェクトを物理的に移動させたいとき(重力、速度、加速度などで移動させる)

19. Colliderというコンポーネントはいつ使う?

答え

 当たり判定の処理のために使う。衝突モードとトリガーモード2種類がある

20. コライダーをトリガーモード(isTrigger)をチェックするとなにが変わる?

答え

 ゲームオブジェクトを衝突せず、コライダーをすり抜けるが、その範囲の中に他のゲームオブジェクトが入っているかどうか「IsTriggerXXX」で確認できる。

21. RigidbodyがないColliderを使える?なぜ?

答え

 使える。当たり判定(衝突)のみが発生し、動かないので、壁、床面などに最適

22. RigidbodyのlinearVelocityはなんですか?

答え リジッドボディの移動速度である。

23. RigidbodyのAddForceで何ができる?linearVelocityと何が違う?

答え

力与えて移動させる(ゲームオブジェクトを押したら、引っ張ったりする)。linearVelocityと異なって、Rigidbodyの重さ(質量)の影響が発生する。つまり、AddForceを使う場合、Rigidbodyが重ければ重いほど、動き辛くなるが、linearVelocityを変えると直接に速度が変わるので、重さが関係ない。

24.「プレハブ」とは何ですか?はいつ使う?

答え

スクリプトやコンポーネントをアタッチし、独立に使えるゲームオブジェクトのかたまりである。プレハブは「図形」として使えることができ、実行中、またはUnityエディタで何度重複ができる。

25. 「Animation Clip」「Animator」と「Animation Controller」の連携を説明してください。

答え
  • Animation Clipは1つのアニメーションである(例:ジャンプ、走る、歩く、攻撃する)
  • Animation Controllerは遷移を管理する(遷移グラフ)。条件(パラメータ)により、Animation Clip の切り替えができる。
  • Animatorはゲームオブジェクトにアタッチするコンポーネントで、Animation Controller を設定できる。

26. UIを作るために、最も重要なコンポーネントを記述してください。

答え Canvas。キャンバスの中に、ボタン、テキスト、画像などを配置する。

27. あるRigidbodyの重力が効かないでほしいとき、どうすれば良いでしょうか?

答え

Unity の Inspector で「Use Gravity」のチェックを外す

28. ベタベタの壁を作りたくて、何か衝突したら、弾みなく、衝突したオブジェクトを壁に張り付くような現象を作りたい。どうすれば良いでしょうか?

答え

2つの解決策が考えられる。

  • スクリプトで衝突したときに、Rigidbodyが効かなくなる(isKinematic = true)
  • PhysicsMaterialで「Bounciness」(弾み)を「0」にする、「Friction」(魔雑)「1」にする

最終的な効果が多少異なるが、べたべたの壁をエフェクトを作成できる。

29. SpriteRendererとは何でしょうか?

答え 2Dの画像を表示する(2Dゲーム専用のコンポーネント)

30. InspectorでスプライトをY軸で逆さまにしたいなら、どうすれば良い?

答え

Inspectorで「Sprite Renderer」の「Flip Y」をチェックする

Transform の Scale Y を -1 にする手もあるが、コライダーに悪影響がある。

31. スプライトのモード「Single」と「Multiple」の違いは何でしょうか?

答え Singleは「単体」という意味で、画像の中にスクリプトが1つしかない。Multipleは「複数」という意味であり、同じ画像に中ではたくさんのスプライトが入っている。この場合は、Sprite Editorで切り分けないといけない。

32. シーンを切り替えるために、LoadSceneを呼び出す前に、どこに追加すれば良いでしょうか?

答え

File > Build Profiles(ファイル > ビルドプロフィール)画面で、「Scene List」の中に切り替えたいシーンを追加しないといけない。

33. 「タグ」は何に使える?

答え

ゲームオブジェクトの特定に使う。「xxxx.CompareTag("タグ")」を使うと、同じタグかどうかを確認できる。

34. TextMeshProというコンポーネントをなんのために使うのでしょうか?

答え

UIでテキストを表示するためのコンポーネントである。

35. UI の「Anchor」(アンカー)の使い方について説明してください。

答え

 UIの要素(画像、テキスト、ボタンなど)を画面の角や中央から相対的に配置するためのものである。いろんな画面サイズを対応するため、UIの要素を絶対的ではなく、アンカーを使って、相対的に配置するべき。

36.「Ray」(レイ)というものはなんでしょうか?使い方を説明してください。

答え

Rayは「ビーム」のようなもので、スクリプトで当たり判定を確認できるためにつかう。Rayは「原点」(origin)と「向き」(direction)をもち、Physics.Raycastで何か当たるかどうかを確認できる。

使い道がいっぱいある。例えば、シューティングゲームで、弾が当たったかどうかを確認できるし、画面をクリックすると、3D空間で何を選択したのかを確認できる。

C# / スクリプト

次の質問を文章で回答してください:

37. 「FixedUpdate」「Update」「LateUpdate」の違いを説明してください。それぞれいつ使うと思う?

答え
  • Update:ゲームのフレーム毎に実行する。普通の処理を行うところ。
  • FixedUpdate:一定間隔の時間で実行する。物理にかかわる処理(Rigidbodyなど)にはおすすめ。
  • LateUpdate:Updateの後に実行する。少し遅れて実行させたい処理はここに書くべき。

38. ゲームオブジェクトの「Destroy」と「SetActive(false)」の違いを説明してください。

答え
  • Destroyはゲームオブジェクトを完全に削除する。ゲームから抜けて、2度も使えなくなる。
  • SetActive(false)は一時的に無効にする。ゲームに残り、もう一度使いたいときには SetActive(true)で有効ができる。

39. 「Time.deltaTime」とは何でしょうか?

答え

前フレームから過ごした時間である。タイマーや速度に依存する移動量などに使う数値。

40. 「Time.timeScale = 0」にすれば、ゲームはどうなる?

答え

時間に依存する処理が止まる。単純なポーズ(Pause)に使える。

41. 「public」と「private」の違いについて説明してください。

答え

 「public」と「private」はアクセス修飾子である。「public」のメソッドや変数が他のクラス(スクリプト)からアクセス(読み込みこと、書き込むこと)ができる。一方「private」のメソッドと変数は、宣言されたクラスのみでアクセスができ、他のクラスからアクセスができない。

42. 「OnCollisionEnter」と「OnTriggerEnter」の違いを説明してください。

答え
  • OnCollisionEnterは衝突が始まるときに呼び出される。(衝突モードのコライダー)
  • OnTriggerEnterは「Is Trigger」のチェックしたコライダーの中に何か入ったときに呼び出される。(トリガーモードのコライダー)

43. 「OnTriggerEnter」「OnTriggerStay」「OnTriggerExit」の違いを説明してください。

答え

すべてのメソッドはトリガーモードになっているコライダーの時に発生する

  • OnTriggerEnter:トリガーは他のコライダーの中に入った瞬間
  • OnTriggerStay:トリガーは他のコライダーの中にあるとき
  • OnTriggerExit:トリガーは他のコライダーの中から出た瞬間

44. 「OnCollisionEnter」の引数(Collision)は何に使えるでしょうか?

答え

 OnCollisionXXXXの「Collision」には衝突に関する情報が入っている。

たとえば、衝突してきたオブザーバーは「Collision.gameObject」であり、衝突した3D位置は「Collision.point」である。

詳細:https://docs.unity3d.com/6000.4/Documentation/ScriptReference/Collision.html

45. 「SerializeField」どこで使う?なんのため?

答え 非公開(private)のメンバー変数を Unity の Inspector で変えられるようにする。

46. 「event」というキーワードは何に使うのでしょうか?

答え

C#のイベントを宣言するときに使う。イベントを作るには、「発生する側」と「受け取る側」の2つが必要。

発生する側:

  • 宣言:public event System.Action<引数1,引数2…> イベント名
  • 発生:イベント名?.Invoke(数値1, 数値2…);

受け取る側:

  • 登録:オブジェクト.イベント名 += 受け取るメソッド名
  • 受け取る:void 受け取るメソッド名(引数1, 引数2, …)

スクリプト作成

47. 「Countdown」というスクリプトをを作成し、ゲームが起動してから3秒後にConsoleで「時間切れ」を表示してください(1回だけ)

答え
using UnityEngine;

public class Countdown : MonoBehaviour
{
    // 残りの時間
    private float timer = 3.0f;
    
    // 時間が切れたか?
    private bool isTimeout = false;
    
    private void Update()
    {
        // 時間が切れたか? → 何もしない
        if (isTimeout)
            return;
        
        // 時間を減らす
        timer -= Time.deltaTime;
        
        // まだ時間ある? → 何もしない
        if (timer > 0)
            return;
            
        // 時間切れにする
        isTimeout = true;

        // コンソールで表示
        Debug.Log("時間切れ");
    }
}

48. 「Countdown」を編集し、「Timeout」というイベントを追加してください。時間が切れたら、イベントを発生してください。

答え
using UnityEngine;

public class Countdown : MonoBehaviour
{
    // タイムアウトイベントの宣言
    public event System.Action Timeout

    // 残りの時間
    private float timer = 3.0f;
    
    // 時間が切れたか?
    private bool isTimeout = false;
    
    private void Update()
    {
        // 時間が切れたか? → 何もしない
        if (isTimeout)
            return;
        
        // 時間を減らす
        timer -= Time.deltaTime;
        
        // まだ時間ある? → 何もしない
        if (timer > 0)
            return;
            
        // 時間切れにする
        isTimeout = true;
        
        // コンソールで表示
        Debug.Log("時間切れ");

        // イベント発生
        Timeout?.Invoke();
    }
}

49.「CountdownWait」の新しいスクリプトを作成し、Unity上でCountdownとの紐づけできるようにしてください。そして、CountdownのTimeoutイベントを登録し、イベントを受け取ったら、「イベントを受け取った」を表示してください。

答え
using UnityEngine;

public class CountdownWait : MonoBehaviour
{
    // Unityで紐づけ
    [SerializeField]
    private Countdown countdown;

    private void Start()
    {
        countdown.Timeout += OnTimeout;
    }

    private void OnTimeout()
    {
        Debug.Log("イベントを受け取った");
    }
}

スクリプト解析

スクリプト A

以下のスクリプトを解析し、下にある質問を回答してください

public class ObjectPusher : MonoBehaviour
{
    public float speed = 10;

    void OnCollisionEnter(Collision other)
    {
       var rbody = GetComponent<Rigidbody>();
       var direction = other.transform.position - transform.position;
       rbody.linearVelocity = direction.normalized * speed;
    }
}

自分(スクリプトを持っているゲームオブジェクト)と相手(衝突してきたゲームオブジェクト)が衝突した場合は9行目で「linearVelocity」を変えているが…

50. どちらのlinearVelocityが変わるでしょうか?(自分 or 相手)

答え 「自分」 - 取得したRigidbodyは、本スクリプトと同じゲームオブジェクトにあるので(7行目)

51. どの方向にlinearVelocityが変わる?(自分の方 or 相手の方)

答え

「相手」 - directionベクトルは相手の方に向いている(方位 = 相手 - 自分)

52. 全体的に、このスクリプトは何をするのかを説明してください。

答え

2つのゲームオブジェクトを衝突したら、自分が相手に向かって速度を設定する。

スクリプト B

以下のスクリプトを解析し、下にある質問を回答してください

public class InstanceMaker : MonoBehaviour
{
    [SerializeField]
    private GameObject original;

    private GameObject [] copies = new GameObject[10];
    private int lastCopy = 0;

    void Update()
    {
        if (Input.GetKeyUp(KeyCode.Space))
        {
            if (lastCopy < 10)
            {
                copies[lastCopy] = Instantiate(original);
                lastCopy++;
            }
            else
            {
                for(int i = 0; i < 10; i++)
                {
                    Destroy(copies[i]);
                }
                lastCopy = 0;
            }
        }
    }
}

53. このスクリプトを実行し、スペースキーを押したらどうなるのか説明してください。

答え

あるプレハブからオブジェクトをコピーし、シーンに追加する。ただし、11個目を作成しょうとしたら、作成したゲームオブジェクトをすべて削除する。

スクリプト C

以下のスクリプトを解析し、下にある質問を回答してください

public class Player : MonoBehaviour
{
    private int HP = 100;   // ヒットポイント
    [SerializeField]
    private int MP = 100;   // マジックポイント

    void Start()
    {
       HP = 50;
    }

    void OnTriggerEnter2D(Collider2D other)
    {
        if (other.CompareTag("fire"))
        {
            HP -= 5;
        }
    }

    void Update()
    {
        if (Input.GetKey(KeyCode.Q))
        {
            HP += 10 * Time.deltaTime;
            MP -=  5 * Time.deltaTime;
        }
    }
}

54. ゲームが開始のとき、プレーヤーはHPとMPはそれぞれ何ポイントでしょうか?

答え

MPは100点、HPは50点

55. 11行目〜17行目でどんなことが行われていると思う?

答え  トリガーに入ったとき、入ってきたゲームオブジェクトのタグは「fire」だったら、HPが5点減る

56. HPの回復処理を説明してください。

答え

「Q」キーをおっしぱなしで、1秒当たりにMPの5点を消費し、HPの10点を回復する

57. "Q”キーを5秒押すと、MPはどうなる?

答え

MPは25点が減る

58. Inspectorで編集できる変数を指定してください。

答え

MPはInspectorで編集ができる

59. HPとMPは「ゼロ」以下にならないように、スクリプトの適切なところに編集してください。

答え
void OnTriggerEnter2D(Collider2D other)
{
    if (other.CompareTag("fire"))
    {
        HP -= 5;
        
        // 0以下にならないように
        HP = Mathf.Max(HP, 0); 
    }
}

void Update()
{
    if (Input.GetKey(KeyCode.Q))
    {
        HP += 10 * Time.deltaTime;
        MP -=  5 * Time.deltaTime;
        
        // 0以下にならないように
        MP = Mathf.Max(MP, 0); 
    }
}

60. “Q”を固定せず、Inspectorでキーを設定できるようにしてください。

答え

まず、スクリプトの頭に:

// 回復のキーをUnityで設定
[SerializeField]
private KeyCode restoreKey;

そして、Updateでif文の条件を変える:

if (Input.GetKey(restoreKey))

61. HPが「ゼロ」になったら、ゲームオブジェクトを削除してください。

答え
void OnTriggerEnter2D(Collider2D other)
{
    if (other.CompareTag("fire"))
    {
        HP -= 5;
        if (HP <= 0)
            Destroy(gameObject);
    }
}