# ティミーの伝説

初めての3Dゲームを作ってみましょう

# プロジェクト設定

まず、新規プロジェクトを作成しましょう。2Dゲームであるため「Universal 3D」を選んでください。プロジェクト名は「legend」にしましょう。

![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/XfWimage.png)

### 素材を追加

- 3Dモデル 
    - 主人公（Timmy）とゾンビのモデル（[Mixamo](https://www.mixamo.com/)）
    - キャラクターのアニメーション
    - ステージ作成のパーツ（[Kenney](https://kenney.nl/))
- 画像、テクスチャ 
    - プロトタイプ用のテクスチャ
    - UIのスプライト
- フォント（[Google Fonts](https://fonts.google.com/))

すべての素材をプロジェクトに追加しましょう。

## プロトタイプ用のシーンを構築

本番のシーンを作る前、各部品を個別で開発しなければならない。キャラクターの移動やカメラの操作などを作業用のシーンで開発し、出来上がったら本番シーンに入れると快適に仕事ができる。

それでは、「Scenes」フォルダーを作成し「Prototype」の新しいシーンを追加してください。その後、床面になる「キューブ」を追加し、サイズは \[50 x 0.1 x 50\] で広い遊ぶ場所を準備しましょう

![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/ou9image.png)

ただし、このままだと、真っ白な床面で動きなどが把握できないので、単純なプロトタイプ用のテクスチャ（greybox\_grey\_grid）をドラッグドロップで追加しましょう。ただし、床面のサイズは50倍大きくなったので、テクスチャも50回をタイル化しましょう。床の「Material」（マテリアル）を選択し、「Tiling」を 50 x 50 にしてください。

![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/mTNimage.png)

また、坂を上るとジャンプのテストもやりたいので、ランプと箱を追加しましょう。ランプはキューブから作成し、斜めに倒せば良いでしょう：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/grdimage.png)

最後、ただのキューブを作成し、ランプとつながるようにする：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/oCnimage.png)

素材を作成する前に、ゲームの操作、面白さ、楽しさを確かめらためのプロトタイプシーンは「[Grayboxing](http://www.neilblevins.com/art_lessons/greybox/greybox.htm)」（グレーボクシング）という。ある程度コストを削減し、まずゲーム性を確かめるためのシンプルなレベルである。

# Timmyの移動を実装

## 主人公のゲームオブジェクトを作成

主人公のキャラクターの移動を実装しましょう。まず、空のゲームオブジェクトを作成し、「Player」という名前を付けてください。このゲームオブジェクトはプレーヤーの役割にする。

### 3Dモデルを追加

Characters/Timmy/Models から Timmy のモデルを追加してください。Player の子オブジェクトとして追加しましょう：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/27Gimage.png)

よく見ると、色が多少おかしい。これはノーマルマップ（※）の問題であるので、解決しましょう。Characters/Timmy/Textures の中にあるノーマルテクスチャ（青い画像）を選択し、ノーマルマップであることを指定し、最後に「Apply」を押してください：

<table id="bkmrk-%C2%A0-%EF%BC%88%E2%80%BB%EF%BC%89%E3%83%8E%E3%83%BC%E3%83%9E%E3%83%AB%E3%83%9E%E3%83%83%E3%83%97%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E3%81%AF%E3%80%81%E3%81%BE%E3%81%9F" style="border-collapse:collapse;width:100%;border-width:0px;"><colgroup><col style="width:60.0664%;"></col><col style="width:39.8919%;"></col></colgroup><tbody><tr><td style="border-width:0px;">![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/cHaimage.png)

（※）ノーマルマップについては、また「ゲームエンジンII」で勉強する予定

</td><td style="border-width:0px;">![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/Mgximage.png)

</td></tr></tbody></table>

### 物理処理の準備

キャラクターを物理的に移動するべきなので、コライダーとリジッドボディが必要。Player に Rigidbody と CapsuleCollider を追加してください。なお、コライダーの形がある程度 3Dモデルの体に合わせましょう：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/kLiimage.png)

## スクリプト

Inputクラスを使用し、キャラクターを動かしましょう。後で改善するべきですが、とりあえず、WASD のキーで前後左右を移動しましょう：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/MKBimage.png)

ＡとＤでＸ軸（赤い軸）で移動し、Ｗ とＳでＺ軸（青い軸）で移動する。なお、各入力は：

- `Input.GetAxis("Horizontal")`: 入力の「水平軸」（つまりAとD）の入力をの取得ができる 
    - Ａの場合は -１ を返す
    - Ｄの場合は１を返す
    - 入力のない場合は０を返す
- `Input.GetAxis("Vertical")`: 入力の「垂直軸」（つまりＷとＳ）の入力をの取得ができる 
    - Ｗの場合は１ を返す
    - Ｄの場合は-１を返す
    - 入力のない場合は０を返す

また、移動速度をUnityで変えられるようにしましょう。この知識を活用し、移動のスクリプト「PlayerMove」を実装：

```c#
// プレーヤーの移動を処理する
public class PlayerMove : MonoBehaviour
{
    // 速度をInspectorで設定
    [SerializeField]
    private float speed = 5.0f;
    
    // 物理的に移動するので、リジッドボディが必要
    private Rigidbody rbody;

    // 3D空間の速度ベクトル
    private Vector3 velocity;

    private void Start()
    {
        // 参照を取っておく
        rbody = GetComponent<Rigidbody>();
    }

    private void Update()
    {
        // 何も入力がない前提で、速度をゼロにする
        velocity = Vector3.zero;
        
        // A/S（水平入力）を取得し、3D空間のX軸に割り当てる
        velocity.x = Input.GetAxis("Horizontal");
        
        // W/D (垂直入力)を取得し、3D空間のZ軸に割り当てる
        velocity.z = Input.GetAxis("Vertical");
        
        // 単位ベクトルにする
        velocity.Normalize();
        
        // 速度を乗算する
        velocity *= speed;
    }

    private void FixedUpdate()
    {
        // 速度を設定
        rbody.linearVelocity = velocity;
    }
}
```

##### `Normalize()`とは

`Normalize()`はあるベクトルを長さ１（単位ベクトル）にする。今回の処理は以下の図で解析してある：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/ZmOimage.png)

上下、または左右 **<span style="text-decoration:underline;">のみ</span>** で移動する場合は、`GetAxis` が -1～1 の数値を返すので、移動速度「speed」を掛けると、問題なく、長さ５の速度ベクトルができる。一方、同時に入力すると（斜め移動）

![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/Uxvimage.png)

掛け算の前に単位ベクトルにすると：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/EYDimage.png)

#### 実行確認

このスクリプトを「Player」にアタッチし、実行してみてください。移動し、ランプを上り、ジャンプしてみてください。なお、カメラの処理まだ行っていないので、Game ビューと Scene ビューを同時に表示するのはおすすめ。

#### 問題：倒れたり、回転したりする

すべての動きをリジッドボディに任せているため、移動だけではなく、回転も自由になっている。今後、回転はスクリプトで実現するので、リジッドボディが回転ができないようにすれば良いでしょう。制約設定で回転を固定しましょう：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/VhNimage.png)

#### 問題：ゆっくり落ちる

回転の問題を解決できたが、ランプから飛び出すと、ゆっくりに落ちる。なぜなら、Updateごとに<span style="color:rgb(186,55,42);">**速度を上書きしている**</span>からです！重力による落下をリジッドボディに任せても、縦軸（Y軸）の速度をゼロにしているので、なかなか落ちない！

解決としては、Y軸の速度を保守し、XとZだけを上書きすれば良い。`FixedUpdate` を以下のように直しましょう：

```c#
private void FixedUpdate()
{
    // 現在の速度を取得
    Vector3 v = rbody.linearVelocity;
    
    // XとZ を上書き
    v.x = velocity.x;
    v.z = velocity.z;
    
    // 速度を設定
    rbody.linearVelocity = v;
}
```

これで確認しましょう。

# 主人公を追いかけるカメラ

Timmyの移動できたとしても、操作が確認しにくいので、プレーヤーを追いかけるカメラ（<span class="HwtZe" lang="ja"><span class="jCAhz ChMk0b"><span class="ryNqvb">三人称視点</span></span></span>）を作りましょう

## 単純なアプローチ

一番簡単なのは「カメラの位置を主人公の位置にしろ！」の仕組みである。これをすぐ試せるので、「CameraController」スクリプトを作成してみましょう：

### スクリプト

```c#
// 主人公を追いかけるカメラ
public class CameraController : MonoBehaviour
{
    // 追いかけるオブジェクトのTransform
    [SerializeField] 
    private Transform target;

    private void Update()
    {
        // 自分の位置をターゲットの位置にするだけ
        transform.position = target.position;
    }
}
```

このスクリプトをカメラにアタッチし、確認しましょう。なお、Unity でカメラの「target」を「Player」にしてください。

![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/Hu0image.png)

## 改善：セルフィスティック

今の処理はとても単純で、カメラはプレーヤーの足元に行ってしまいました。もう少し賢い作戦を考えてみましょう。「三人称」といえば：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/ULoimage.png)

セルフィスティックは良いでしょう！シーンにあるカメラの親子関係を変えて、同じ仕組みになるようにしましょう。具体的に：

<span style="color:rgb(45,194,107);">INSERT ILLUSTRATION OF Base-&gt;Height-&gt;Stick-&gt;Camera</span>

- Base（基礎） ：主人公を追いかける根。セルフィスティックなら、「人間が立っている場所」
- Height（高さ） ：地面からの高さ。だいたいTimmyの頭までにすれば良いでしょう。
- Pole（ポール） ：スティックのそのもの。上がるか下がることにより、撮影角度を変えられる
- Camera（カメラ）：スティックの端にあるカメラ。適切な距離にし、Timmyが全身が見えるようにしましょう。

これはヒエラルキー（とその位置）はこうなる：

<table class="align-center" id="bkmrk--2" style="border-collapse:collapse;border-width:0px;"><colgroup><col style="width:50%;"></col><col style="width:50%;"></col></colgroup><tbody><tr><td colspan="2" style="border-width:0px;">![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/79Zimage.png)

</td></tr><tr><td style="border-width:0px;">![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/kdEimage.png)

</td><td style="border-width:0px;">![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/emYimage.png)

</td></tr><tr><td style="border-width:0px;">[![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/3zVimage.png)](https://class.illogic.games/uploads/images/gallery/2026-05/3zVimage.png)

</td><td style="border-width:0px;">[![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/Kapimage.png)](https://class.illogic.games/uploads/images/gallery/2026-05/Kapimage.png)

</td></tr></tbody></table>

なお、「CameraController」のスクリプトは、カメラではなく、実際に動く基礎（Base）アタッチすべき。Main Camera からスクリプトを削除するのは、右側にある「**⋮**」を押し、「Remove Component」（コンポーネントを削除）でできる。

![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/gLwimage.png)

では、「CameraController」をBaseにアタッチし、もう一度確認しましょう。

## カメラをマウスで回転

カメラは主人公に追いかけても、周りを自由に見たいので、マウスでカメラを回転させたい。今のカメラの作りだと簡単に責任を分けることができる！

- Height オブジェクトをY軸まわりに回転させると、360度の回転ができる。
- Pole オブジェクトをX軸まわりに回転させると、上下の角度を変えることができる。

IMAGE: Height rotating and Pole pivoting

そして、WASDの入力は `Input.GetAxis("Horizontal");` と `Input.GetAxis("Vertical");` で取得できたが、同じようにカメラの回転 `Input.GetAxis("CamRotY");` とカメラの上下角度 `Input.GetAxis("CamRotX");` を取れれば良いけど、この入力軸が定義されていない。

### 新しい入力を定義する

#### 入力マネージャーの紹介

*※以下は「入力マネージャー（旧）」に対する話しである。２年生になったら「入力システム（新）」に切り替える予定。*

`"Horizontal"` （水平入力）と `"Vertical"` （垂直入力）が魔法の言葉ではなく、プロジェクト設定として定義されている入力である。これをトップメニューの「Edit / Project Settings」（編集 / プロジェクト設定）の中にある「Input Manager」（入力マネージャー）から確認ができる。

![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/Xb5image.png)

ここで「Horizontal」という入力名は「Positive」（正の数）と「Negative」（負の数）があるので、‐１～１の間の「軸」であることを確認できる。つまり、Negative Buttonの「a」を押すと、`Input.GetAxis("Horizontal")` が -1 を返す、Positive Button の「d」を押すと`Input.GetAxis("Horizontal")` が１を返す。この行動がすでに使用し、Timmyを動かしました。

その他には、Fire1～Fire3 や Jump の入力名がある。これはデフォルトとして定義されている入力であるが、変えたり、消したり、増やしたりすることが可能である。

「Jump」を確認してみましょう：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/CUcimage.png)

この入力は「Positive」のみで、「Negative」がない。つまり、この入力は「軸」ではなく「ボタン」である。ボタンは「`Input.GetButtonXXX("入力名")`」で取得でき、「true」または「false」を返す。`GetMouseButtonXXX` と同様に３パターンがある：

- `GetButtonDown("入力名")`： 押された瞬間に「true」を返す。
- `GetButton("入力名")`：押し続けるときに「true」を返す。
- `GetButtonUp("入力名")`：放した瞬間に「true」を返す。

デフォルト設定としては「Jump」という入力は「space」に設置されているので、スペースキーを押したら `GetButtonDown("Jump")` は true になる！

もちろん、これらあくまで初期設定なので、キーを変えて良いし、名前も変えても大丈夫（例えば、「Kick」「Punch」「Attack」など）

もうちょっと見ると、多くの入力は２回が現れる！もう一つの "Horizontal" を確認すると：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-05/scaled-1680-/aFlimage.png)

あれ？「Positive」も「Negative」もない？なにこれ？

これは、"Horizontal" に対するもう一つの入力処理である。Typeを詳しくみると、「Joystick」というキーワードが書かれている。

そう！これはコントローラーに対応する入力。A/Dを使えば、"Horizontal" が発動するし、コントローラーの「X Axis」を使うと、同じ "Horizontal" が発動する。

<span style="color:rgb(35,111,161);">**つまり、入力マネージャーが入力ハードウェアを抽象化してくれる機能である！**</span>

キーワード＆マウス、コントローラーを関係なく、C#で「Horizontal」を処理すれば十分。Unity が裏側で入力を通訳してくれる。

<span style="color:rgb(45,194,107);">IMAGE: Draw keyboard, mouse moving a/d, stick sidways -&gt; pass input manager -&gt; c# is "Horizontal"</span>

#### カメラ操作の入力を作成

すでに「MouseX」と「MouseY」の入力を定義されているので、これを再利用しましょう。MouseX はマウスの横の動きなので、これを「CamRotY」にしましょう。同様に MouseY を「CamRotX」に書き換えましょう。

Image; Visualize how MouseX rotates around Y and mouseY rotates around X

これで、後ほどコントローラーの対応を追加したいなら、同じ名前の新しい入力を追加し、適切なボタンやスティックを割り当てることが可能。

### スクリプト

CameraController を更新しましょう。まず、Y軸まわり（360度回転）を実装するには先ほど定義した「CamRotY」の入力を活用し、Height を回転する。

```c#
// 主人公を追いかけるカメラ
public class CameraController : MonoBehaviour
{
    // 追いかけるオブジェクトのTransform
    [SerializeField] 
    private Transform target;

    // 360度回転に使うTransform
    [SerializeField] 
    private Transform height;
    
    private void Update()
    {
        // 自分の位置をターゲットの位置
        transform.position = target.position;
        
        // 回転処理
        Rotate360();
    }

    // 360度の回転する
    private void Rotate360()
    {
        // トランスフォームの回転を取得
        // Quaternion（クオーターニオン・四元数）は回転を表すクラスである
        // Quaternionについては、「ゲーム数学」でまた勉強する
        Quaternion rot = transform.localRotation;
        
        // このまま使えないので、XYZの回転角度（オイラー角度）にする
        Vector3 angles = rot.eulerAngles;
        
        // Y軸だけを更新
        angles.y += Input.GetAxis("CamRotY");
        
        // 回転を更新（オイラー角度 → クオーターニオンに戻す）
        transform.localRotation = Quaternion.Euler(angles);
    }
}
```

一応、回転するが、速度が多少遅く感じるので、回転速度を「度 / 秒」単位で調整できるようにしましょう：

# マウスでカメラを回転



# Timmyをカメラに相対的に移動する



# ジャンプを実装



# アニメーションを追加



# アイテムを拾う



# 宝箱を作成



# UIを作成



# ゾンビを実装



# 課題

Timmyの伝説に新たな機能を追加し、よりも面白くする！

例えば…

- HP回復アイテムを実装
- 罠を作成（消えるプラットフォーム→穴に落ちる）
- ドアを実装 
    - 定めたコインの数がないと開かない
    - また、レバーを引くと開く
- HPゼロのときに、倒れるアニメーションを再生し、ゲームオーバーする
- 宝箱を開くと、コイン10個が飛んで出てくる
- 移動プラットフォームを実装
- ボールを投げ、ゾンビを倒す
- その他の好きな機能（自由）

#### ルール

- １つの機能を実装すると：合格（最低限） 
    - <span style="text-decoration:underline;"><span style="color:rgb(224,62,45);text-decoration:underline;">難しいことを頼んでいない！</span></span>
    - <span style="color:rgb(185,106,217);">自分のスキルレベル</span>に合わせて<span style="color:rgb(53,152,219);">適切な機能</span>を選択し、<span style="color:rgb(22,145,121);">実装</span>してください。
- ２つ以上の機能、または複雑な機能を実装すると：点数向上
- 基本として、授業の時間で実装するべきが、自宅で完成度を高めたいなら問題ない
- 一人でやるべき（お友達からコピーするのはNG） 
    - ネット、教科書、資料、今まで作ってきたプログラムを参照してもOK
    - <span style="text-decoration:underline;"><span style="color:rgb(224,62,45);text-decoration:underline;">ただし、AI（ChatGPT、Geminiなど）はNG</span></span>
- ネットで見つけたスクリプトのコピーぺーについて 
    - 当然、<span style="text-decoration:underline;"><span style="color:rgb(224,62,45);text-decoration:underline;">把握せずにコピーしないでください</span></span>
    - プログラムの動きを解析し、分かれば、使ってもOK
    - 説明してほしいなら、先生を呼んでください。
- それでも進まないなら、先生に聞いてもOK（ヒントを出す）

#### 提出

提出するのは、プロジェクトの<span style="text-decoration:underline;"><span style="color:rgb(224,62,45);text-decoration:underline;">以下のフォルダのみ</span></span>：

- Assets
- Packages
- ProjectSettings

また、「変更点.txt」を作成し、何を変えたのかを説明してください（速く見つけるため）

- 例：Timmyが玉を打ち、攻撃できるようになった（スクリプト：○○.cs）

この３つのフォルダと「変更点.txt」をZIPファイルに圧縮し、提出フォルダにコピーしてください。