夏休み・練習課題
義務ではないが、練習不足と思う学生にはおすすめ
目標
- 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」の連携を説明してください。
答え
26. UIを作るために、最も重要なコンポーネントを記述してください。
答え
Canvas。キャンバスの中に、ボタン、テキスト、画像などを配置する。27. あるRigidbodyの重力が効かないでほしいとき、どうすれば良いでしょうか?
答え
Unity の Inspector で「Use Gravity」のチェックを外す
28. ベタベタの壁を作りたくて、何か衝突したら、弾みなく、衝突したオブジェクトを壁に張り付くような現象を作りたい。どうすれば良いでしょうか?
答え
2つの解決策が考えられる。
最終的な効果が多少異なるが、べたべたの壁をエフェクトを作成できる。
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」の違いを説明してください。それぞれいつ使うと思う?
答え
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);
}
}