解説

実際にゲームを作ってビルドしてみる【Unityの教科書#5】

この記事では、これまでに学んだことを生かして実際にゲームを製作していきます!

ミニゲーム製作

最終的に次のようなゲームを作成します。

ゲームオブジェクトの配置と設定

2Dで新規プロジェクトを作成し、以下の画像の通りにゲームオブジェクトを配置してください。

各ゲームオブジェクトの座標と大きさは以下のように設定します。

Position(x,y)Scale(x,y)
Square(1)(-8 , 3.5)(1 , 1)
Square(2)(0 , -5)(19 , 1)
Square(3)(0 , 5)(19 , 2)
Square(4)(-9 , 0)(1 , 10)
Square(5)(9 , 0)(1 , 10)
CircleとText(TMP)は初期のままでOK

ちょっとゲームオブジェクト同士の名前が同じで分かりにくいですね…
なので名前を変更しちゃいましょう!また、名称変更後は下の表のようにコンポーネントを追加してください。

元の名前変更後の名前追加するコンポーネント
CirclePlayerCircleCollider2D
Rigidbody2D
SquareEnemyBoxCollider2D
Square(1)GroundBoxCollider2D
Square(2)CeilingBoxCollider2D
Square(3)LeftWallBoxCollider2D
Square(4)RightWallBoxCollider2D
Text(TMP)ScoreText
ScoreTextにはコンポーネントを追加する必要がありません

次に、タグを追加します。インスペクター上部のTagからAddTagを選択し、「Ground」というタグを新規作成してください。

タグの作成が完了したら、次のようにタグを付けてください。

ゲームオブジェクト名タグ名
PlayerPlayer
GroundGround
今回はゲームオブジェクト名とタグ名が同じですが、作るゲームによっては一致しないこともあります。
こんな感じにPlayerタグが付いていればOK
Groundタグはこんな感じ

タグの詳しい説明はこちらの記事で解説しています。

C#スクリプトの記述とアタッチ

では次に、C#スクリプトを書いていきましょう「PlayerMovement」「EnemySystem」という2つのスクリプトを新規作成してください。
それぞれのスクリプトのコードは以下の通りです。(コピー&ペーストで大丈夫です!)

PlayerMovementのコード
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    private Rigidbody2D rb2d;
    [SerializeField] float moveVelocity;
    [SerializeField] float jumpPower;
    private bool isJumping = false;

    void Start()
    {
        rb2d = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        rb2d.velocity = new Vector2(0,rb2d.velocity.y);

        if(Input.GetKey(KeyCode.D))
        {
            rb2d.velocity = new Vector2(moveVelocity,rb2d.velocity.y);
        }

        if(Input.GetKey(KeyCode.A))
        {
            rb2d.velocity = new Vector2(-moveVelocity,rb2d.velocity.y);
        }

        if(Input.GetKeyDown(KeyCode.Space) && !isJumping)
        {
            rb2d.AddForce(Vector2.up * jumpPower,ForceMode2D.Impulse);
            isJumping = true;
        }
    }

    private void OnCollisionEnter2D(Collision2D collision) 
    {
        if(collision.gameObject.CompareTag("Ground"))
        {
            isJumping = false;
        }
    }
}
EnemySystemのコード
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

public class EnemySystem : MonoBehaviour
{
    [SerializeField] TextMeshProUGUI scoreText;
    private int score;

    void Start()
    {
        score = 0;
    }

    void OnCollisionEnter2D(Collision2D collision)
    {
        if(collision.gameObject.CompareTag("Player"))
        {
            score += 10;
            scoreText.text = "Score:" + score; 
            ChangeEnemyPosition();
        }
    }

    void ChangeEnemyPosition()
    {
        int enemyPositionIndex =  Random.Range(0,3);

        if(enemyPositionIndex == 0)
        {
            transform.position = new Vector2(-8f,3.5f);
        }
        else if(enemyPositionIndex == 1)
        {
            transform.position = new Vector2(0f,3.5f);
        }
        else if(enemyPositionIndex == 2)
        {
            transform.position = new Vector2(8f,3.5f);
        }
    }
}

スクリプトを書いたら、「PlayerMovement」をPlayerに、「EnemySystem」をEnemyにアタッチしましょう。(余力があればコードの内容を確認し、こんな感じで動いてるんだ~と思ってください。)
次にPlayerのインスペクターから「MoveVelocity」「JumpPower」を任意に設定し、EnemyのインスペクターにはScoreTextをドラッグ&ドロップします。

Playerゲームオブジェクトの設定
Enemyゲームオブジェクトの設定

UIの設定

次に、ScoreTextの設定を行います。シーンビューでズームアウトし、UIが見えるように調整します。

先程までよりもかなりズームアウトします。

Canvasのインスペクター>CanvasScaler>UIScaleMode をScaleWithScreenSizeに変更し、ReferenceResolutionをX:1280,Y:720にします。

UIScaleModeを変更するのは、解像度を変更したときにUIが崩れる(想定しない所にTextが移動する)事を防ぐために行います。
ScaleWithScreenSizeはReferenceResolutionで決められた解像度の時にどこに配置されるかを決める物であり、今回は1280×720の16:9で設定したためFHDなどでゲームをビルドした時に崩れないようになっています。

最後に、ScoreTextを左上に移動させて範囲を大きくし、サイズを調整します。

ScoreTextを左上に移動
範囲とサイズの変更
ゲームビューで見た時にこんな感じに表示されていればOK!

UI(TextMeshPro)についての詳しい説明はこちらの記事で解説しています。

お疲れ様でした!これでひとまずミニゲームの完成です!ゲームビュー上部の▷を押してプレイモードに入り、テストプレイをしてみましょう。

操作方法とゲームルール
A:左移動、 D:右移動、 Space:ジャンプ
赤色のオブジェクトにぶつかると得点が加算される

BGMの鳴らし方

ミニゲームは完成しましたが、BGMがないのが寂しいですね。なのでBGMを追加しましょう!

Unity内に音源ファイルを取り込む

以下のBGMをダウンロードしてください。

このBGMは本サークルのサウンドクリエイターのセイさんに制作していただきました!

ダウンロードが完了したら、Unity内にインポートして音源ファイルを取り込みます。

次に、PlayerゲームオブジェクトにAudioSourceコンポーネントを追加します。
コンポーネントを追加したら、AudioSource>AudioClipに先程インポートした「gosikku1」をアタッチします。ついでに、AudioSource>Loopにチェックを入れておきましょう。

はい。これでBGMの追加は完了です。早速テストプレイして確かめてみましょう!

SEの追加やBGMの詳しい説明などはこちらの記事で解説しています。

音量調整

…テストプレイした方はこう思ったでしょう。
「うわっ…BGMの音量、でかすぎ…?」と。
なので、音量調整の方法を解説していきます。
とは言っても、そこまで難しいものではありません。AudioSourceのVolumeを変更するだけです。

Volume右側「1」の数字を直接変更しても大丈夫です!0~1の間で調整することができます!

変更したら、テストプレイをして音量を確かめましょう!

想定通りに動かない時の対処法

ゲーム製作をしていると、想定している挙動どおりにはならない時が多々あります。
ですので、あえて間違ったスクリプトを修正してみて、原因の特定方法・スクリプトの修正方法を学んでいきましょう!

例題

事前に作成したEnemySystemスクリプトのコードを、次の通りに変更してください。

EnemySystemの誤ったコード0
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

public class EnemySystem : MonoBehaviour
{
    [SerializeField] TextMeshProUGUI scoreText;
    private int score;

    void Start()
    {
        score = 0;
    }

    void OnCollisionEnter2D(Collision2D collision)
    {
        if(collision.gameObject.CompareTag("Player"))
        {
            score += 10;
            scoreText.text = "Score:" + score; 
            ChangeEnemyPosition();
        }
    }

    void ChangeEnemyPosition()
    {
        int enemyPositionIndex =  Random.Range(0,3);

        if(enemyPositionIndex = 0)
        {
            transform.position = new Vector2(-8f,3.5f);
        }
        else if(enemyPositionIndex = 1)
        {
            transform.position = new Vector2(0f,3.5f);
        }
        else if(enemyPositionIndex = 2)
        {
            transform.position = new Vector2(8f,3.5f);
        }
    }
}

変更したら、ゲームモードに入れなくなっていると思います。ですので、コードを修正しましょう。

ヒント

VS Code側でエラーが出ていますが、30,34,38行目に問題がありそうです。

答え

正誤それぞれのスクリプトの30~43行目を抜き出しました。
左が正しいスクリプト、右が誤ったスクリプトです。

if(enemyPositionIndex == 0)
{
    transform.position = new Vector2(-8f,3.5f);
}
else if(enemyPositionIndex == 1)
{
     transform.position = new Vector2(0f,3.5f);
}
else if(enemyPositionIndex == 2)
{
     transform.position = new Vector2(8f,3.5f);
}
if(enemyPositionIndex = 0)
{
    transform.position = new Vector2(-8f,3.5f);
}
else if(enemyPositionIndex = 1)
{
     transform.position = new Vector2(0f,3.5f);
}
else if(enemyPositionIndex = 2)
{
     transform.position = new Vector2(8f,3.5f);
}

…ちょっと分かりにくいですが、正しいスクリプトでは「==」となっている部分が誤ったスクリプトでは「=」となっています。
「==」は同値比較、「=」は代入であるためエラーが出てしまうんですね。

この様に、VS Codeがエラーを検知してくれる場合もありますが、エラーが出ていないのに予期せぬ挙動になることもあります。次からは、エラーが出ない場合のスクリプト修正をしていきましょう。

問題1

EnemySystemスクリプトのコードを、次の通りに変更してください。

EnemySystemの誤ったコード1
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

public class EnemySystem : MonoBehaviour
{
    [SerializeField] TextMeshProUGUI scoreText;
    private int score;

    void Start()
    {
        score = 0;
    }

    void OnCollisionEnter2D(Collision2D collision)
    {
        if(collision.gameObject.CompareTag("Player"))
        {
            score = 10;
            scoreText.text = "Score:" + score; 
            ChangeEnemyPosition();
        }
    }

    void ChangeEnemyPosition()
    {
        int enemyPositionIndex =  Random.Range(0,3);

        if(enemyPositionIndex == 0)
        {
            transform.position = new Vector2(-8f,3.5f);
        }
        else if(enemyPositionIndex == 1)
        {
            transform.position = new Vector2(0f,3.5f);
        }
        else if(enemyPositionIndex == 2)
        {
            transform.position = new Vector2(8f,3.5f);
        }
    }
}

変更したらゲームモードに入ってテストプレイを行い、どの挙動がおかしいのかを見つけてコードを修正しましょう。

ヒント1

ボールがぶつかっても二回目以降得点が増えていないように見えますね。この時、考えられる原因は大きく3つあります。
1、接触判定が取られていない
2、変数”score”自体が加算されていない
3、ScoreTextの表示変更がうまくいってない
上に書いてある処理をしているコードの部分を詳しく見てみましょう。

ヒント2

ボールが初めてぶつかった時得点のTextが変化しているので、ヒント1の1~3のうち1,3が原因であるとは考えにくく、変数”score”が加算されていない可能性が非常に高いです。こうした変数の値を調べるためにDebug.Log(); を使用しましょう。
少しスクリプトを変更します。EnemySystemの誤ったコード1の16~24行目を次のように書き換えてください。

void OnCollisionEnter2D(Collision2D collision)
{
      if(collision.gameObject.CompareTag("Player"))
      {
           score = 10;
           scoreText.text = "Score:" + score; 
           ChangeEnemyPosition();
           Debug.Log(score);
      }
}

Debug.Log(score); を追加しました。これによって変数scoreの値が(接触判定が行われたときに)コンソールウィンドウに表示されるようになります。

答え

正誤それぞれのスクリプトの18~26行目を抜き出しました。
左が正しいスクリプト、右が誤ったスクリプトです。

void OnCollisionEnter2D(Collision2D collision)
{
      if(collision.gameObject.CompareTag("Player"))
      {
           score += 10;
           scoreText.text = "Score:" + score; 
           ChangeEnemyPosition();
      }
}
void OnCollisionEnter2D(Collision2D collision)
{
      if(collision.gameObject.CompareTag("Player"))
      {
           score = 10;
           scoreText.text = "Score:" + score; 
           ChangeEnemyPosition();
      }
}

これまた少し分かりにくいですが、正しい方は「score += 10」となっているところが誤った方では「score = 10」となっています。
「+=」は右の値が加算されますが、「=」だけでは代入になってしまうため、scoreの値が10のままだったんですね。

この様に、変数が原因であると考えられるときはDebug.Log(); を使用することでその変数の値を知ることができます。“うまく動かないときはDebug.Log();”を覚えておきましょう!

Debug.Log(); はあくまで開発段階でデバッグを行うために使用するものです。デバッグが終わり次第コードを消してしまいましょう。

問題2

EnemySystemスクリプトのコードを、次の通りに変更してください。

EnemySystemの誤ったコード2
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

public class EnemySystem : MonoBehaviour
{
    [SerializeField] TextMeshProUGUI scoreText;
    private int score;

    void Start()
    {
        score = 0;
    }

    void OnCollisionEnter2D(Collision2D collision)
    {
        if(collision.gameObject.CompareTag("Player"))
        {
            score += 10;
            scoreText.text = "Score:" + score; 
            ChangeEnemyPosition();
        }
    }

    void ChangeEnemyPosition()
    {
        int enemyPositionIndex =  Random.Range(0,2);

        if(enemyPositionIndex == 0)
        {
            transform.position = new Vector2(-8f,3.5f);
        }
        else if(enemyPositionIndex == 1)
        {
            transform.position = new Vector2(0f,3.5f);
        }
        else if(enemyPositionIndex == 2)
        {
            transform.position = new Vector2(8f,3.5f);
        }
    }
}

変更したらゲームモードに入ってテストプレイを行い、どの挙動がおかしいのかを見つけてコードを修正しましょう。

ヒント1

一見すると上手く挙動しているように見えますが、よくよく見てみるとEnemyゲームオブジェクトが右上に移動することがありませんね。
Enemyゲームオブジェクトを動かしている処理の部分のコードを詳しく見てみましょう。

ヒント2

Enemyゲームオブジェクトが右上に行く処理は38~41行目に書いてあり、ここは動いていないようですが、30~37行目の左上・中央上に移動する処理は動作しています。このことから、変数”enemyPositionIndex”の値が2になっていない可能性が高いです。こういう時もやはりDebug.Log(); を使用しましょう。
少しスクリプトを変更します。EnemySystemの誤ったコード2の26~42行目を次のように書き換えてください。

    void ChangeEnemyPosition()
    {
        int enemyPositionIndex =  Random.Range(0,2);

        Debug.Log(enemyPositionIndex);

        if(enemyPositionIndex == 0)
        {
            transform.position = new Vector2(-8f,3.5f);
        }

        if(enemyPositionIndex == 1)
        {
            transform.position = new Vector2(0f,3.5f);
        }

        if(enemyPositionIndex == 2)
        {
            transform.position = new Vector2(8f,3.5f);
        }
    }

Debug.Log(enemyPosition); を追加しました。これによって変数”enemyPositionIndex”の値が(接触判定が行われた際に)コンソールウィンドウに表示されます。

答え

正誤それぞれのスクリプトの26~42行目を抜き出しました。
左が正しいスクリプト、右が誤ったスクリプトです。

    void ChangeEnemyPosition()
    {
        enemyPositionIndex =  Random.Range(0,3);

        if(enemyPositionIndex == 0)
        {
            transform.position = new Vector2(-8f,3.5f);
        }

        if(enemyPositionIndex == 1)
        {
            transform.position = new Vector2(0f,3.5f);
        }

        if(enemyPositionIndex == 2)
        {
            transform.position = new Vector2(8f,3.5f);
        }
    }
    void ChangeEnemyPosition()
    {
        enemyPositionIndex =  Random.Range(0,2);

        if(enemyPositionIndex == 0)
        {
            transform.position = new Vector2(-8f,3.5f);
        }

        if(enemyPositionIndex == 1)
        {
            transform.position = new Vector2(0f,3.5f);
        }

        if(enemyPositionIndex == 2)
        {
            transform.position = new Vector2(8f,3.5f);
        }
    }

またまた少し分かりにくいですが、正しい方は「Random.Range(0,3)」となっているところが誤った方では「Random.Range(0,2)」となっています。
整数を生成するRandom.Range(a,b)はa以上b未満のためRandom.Range(0,2)では0,1しか生成されないんですね。

浮動小数点を生成するRandom.Range(a,b)はa以上b以下であるため、Random.Range(0f,3f)では0~3の実数が生成されます。
この様に、Random.Rangeという同じものでも整数か浮動小数点かで生成されるものが違うこともあります。

問題3

PlayerMovementスクリプトのコードを、次の通りに変更してください。

PlayerMovementの誤ったコード3
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    private Rigidbody2D rb2d;
    [SerializeField] float moveVelocity;
    [SerializeField] float jumpPower;
    private bool isJumping = false;

    void Start()
    {
        rb2d = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        if(Input.GetKey(KeyCode.D))
        {
            rb2d.velocity = new Vector2(moveVelocity,rb2d.velocity.y);
        }

        if(Input.GetKey(KeyCode.A))
        {
            rb2d.velocity = new Vector2(-moveVelocity,rb2d.velocity.y);
        }

        if(Input.GetKeyDown(KeyCode.Space) && !isJumping)
        {
            rb2d.AddForce(Vector2.up * jumpPower,ForceMode2D.Impulse);
            isJumping = true;
        }

        rb2d.velocity = new Vector2(0,rb2d.velocity.y);
    }

    private void OnCollisionEnter2D(Collision2D collision) 
    {
        if(collision.gameObject.CompareTag("Ground"))
        {
            isJumping = false;
        }
    }
}

変更したらゲームモードに入ってテストプレイを行い、どの挙動がおかしいのかを見つけてコードを修正しましょう。

ヒント1

x方向にPlayerオブジェクトが移動しませんね。
Playerオブジェクトの移動の処理をしている部分のコードを詳しく見てみましょう。

ヒント2

Playerの移動を処理するコードは17~36行目に書かれています。今回の移動はPlayerゲームオブジェクトのvelocityを直接変更して動くようにしているので、恐らくvelocityの値が誤っているのでしょう。こういう時もやはりDebug.Log(); を使用します。
少しスクリプトを変更します。PlayerMovementの誤ったコード3の18~37行目を次のように変更してください。

    void Update()
    {
        if(Input.GetKey(KeyCode.D))
        {
            rb2d.velocity = new Vector2(moveVelocity,rb2d.velocity.y);
        }

        if(Input.GetKey(KeyCode.A))
        {
            rb2d.velocity = new Vector2(-moveVelocity,rb2d.velocity.y);
        }

        if(Input.GetKeyDown(KeyCode.Space) && !isJamping)
        {
            rb2d.AddForce(Vector2.up * jampPower,ForceMode2D.Impulse);
            isJamping = true;
        }

        rb2d.velocity = new Vector2(0,rb2d.velocity.y);   
        Debug.Log(rb2d.velocity);
    }

Debug.Log(rb2d.velocity); が追加されました。これでPlayerゲームオブジェクトの速度が(毎フレーム)コンソールウィンドウに表示されます。

ヒント3

実は

if(Input.GetKey(KeyCode.D))
{
    rb2d.velocity = new Vector2(moveVelocity,rb2d.velocity.y);
}

if(Input.GetKey(KeyCode.A))
{
    rb2d.velocity = new Vector2(-moveVelocity,rb2d.velocity.y);
}

if(Input.GetKeyDown(KeyCode.Space) && !isJamping)
{
    rb2d.AddForce(Vector2.up * jampPower,ForceMode2D.Impulse);
    isJamping = true;
}

この部分は正常に動作しています。しかしながらDebug.Log(rb2d.velocity); から分かるようにx方向の速度は0のままです。キーが押されている間はx方向の速度が”moveVelocity”になるはずなのに0になってしまうのはなぜでしょうか。

答え

正誤それぞれのスクリプトの17~36行目を抜き出しました。
左が正しいスクリプト、右が誤ったスクリプトです。

    void Update()
    {
        rb2d.velocity = new Vector2(0,rb2d.velocity.y);

        if(Input.GetKey(KeyCode.D))
        {
            rb2d.velocity = new Vector2(moveVelocity,rb2d.velocity.y);
        }

        if(Input.GetKey(KeyCode.A))
        {
            rb2d.velocity = new Vector2(-moveVelocity,rb2d.velocity.y);
        }

        if(Input.GetKeyDown(KeyCode.Space) && !isJamping)
        {
            rb2d.AddForce(Vector2.up * jumpPower,ForceMode2D.Impulse);
            isJumping = true;
        }
    }
    void Update()
    {
        if(Input.GetKey(KeyCode.D))
        {
            rb2d.velocity = new Vector2(moveVelocity,rb2d.velocity.y);
        }

        if(Input.GetKey(KeyCode.A))
        {
            rb2d.velocity = new Vector2(-moveVelocity,rb2d.velocity.y);
        }

        if(Input.GetKeyDown(KeyCode.Space) && !isJamping)
        {
            rb2d.AddForce(Vector2.up * jumpPower,ForceMode2D.Impulse);
            isJumping = true;
        }

        rb2d.velocity = new Vector2(0,rb2d.velocity.y);    
    }

今回はわかりやすいですね。rb2d.velocity = new Vector2(0,rb2d.velocity.y)の位置が違います。正しい方は上にありますが、誤った方は下のほうにあります。
これは関数の仕様ですが、関数は上から順番に処理されていきます。
例を提示しますね。

int a;
void Update()
{
    a = 1;
    a = 2;
    Debug.Log(a);
}

このような関数があった場合、aの値は一度1になりますが、そのすぐ後に2になり、結果として出力されるaの値は2となります。同じ変数を同じ関数内で変更した場合、後に変更された方の値が反映されます。

今回のように、順番を入れ替えるだけで意図しない挙動になることもあります。必要に応じて順番を確認するなどして対策していきましょう。

作ったゲームをビルドする

せっかく作ったゲームなので、自分だけでなく多くの人にプレイしてもらいたいですよね。ですのでゲームをビルドして遊べるようにしましょう!

Unity画面の上部のFile>BuildSettings… からビルド設定を開き出力することができます。ビルド設定などの詳しい説明はこちらの記事で説明しています。

これでゲームの完成です。お疲れ様でした!

やや発展的な内容

ゲーム製作をする上では最悪なくても完成するけれど、利用したほうが便利な機能などについて紹介してきます。時間がない場合は飛ばしてもらって構いません。

ColliderのisTrigger

isTriggerは、「接触判定を持つが物体が衝突しても通過する」という性質を持つようになります。実際にisTriggerを設定して実行してみましょう。

EnemyゲームオブジェクトのBoxCollider2D>isTriggerにチェックをいれてください。

この状況でテストプレイを行うと、次の画像のようになるはずです。

先程までとは異なり、PlayerがEnemyの中に入っていま。
…おや?ぶつかったはずなのに得点が加算されていませんね。

実は、isTriggerに設定してしまうと、Colliderを持つ者同士が衝突してもOnColloision○○2Dを呼び出すことが出来ません
じゃあisTriggerの意味ってなんだよ!と思った方、そんなに興奮しないでください。isTriggerを使用しているときはOnTrigger○○2Dを使いましょう!

スクリプトを変更します。EnemySystemを次のように変更してください。

EnemySystemのコード2
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

public class EnemySystem : MonoBehaviour
{
    [SerializeField] TextMeshProUGUI scoreText;
    private int score;

    void Start()
    {
        score = 0;
    }

    void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.CompareTag("Player"))
        {
            score += 10;
            scoreText.text = "Score:" + score; 
            ChangeEnemyPosition();
        }
    }

    void ChangeEnemyPosition()
    {
        int enemyPositionIndex =  Random.Range(0,3);

        if(enemyPositionIndex == 0)
        {
            transform.position = new Vector2(-8f,3.5f);
        }
        else if(enemyPositionIndex == 1)
        {
            transform.position = new Vector2(0f,3.5f);
        }
        else if(enemyPositionIndex == 2)
        {
            transform.position = new Vector2(8f,3.5f);
        }
    }
}

元々のスクリプトの18行目を変更しています。
元々は void OnCollisionEnter2D(Collision2D collision) だったのが void OnTriggerEnter2D(Collider2D collision) になっています

OnCollision○○2DとOnTrigger○○2Dでは引数の型が異なります。

OnCollision○○2D(Collision2D <変数名>){} に対して
OnTrigger○○2D(Collider2D <変数名>){} となります。

誤った型を使用するとエラーが出てしまうので注意しましょう。

シーンの遷移

大きめのゲームを制作していると、ステージが多くなったりして管理が大変になりますよね。そんな時に役立つのがシーンです。(シーンの詳しい説明はこちらの記事で)

シーンの名付け&新規作成していきます。プロジェクトウィンドウ>Assets>Scenesを開き、すでにあるSampleSceneを「Game」と名前を変更し、右クリック>Create>Scenesでシーンを新規作成してください。新規作成したシーン名は「Title」とします。

最終的にこうなればOK!

作成したシーンは、ダブルクリックをする事で自由に移動することができます。試しにTitleシーンを開くと何もない空間が広がっていると思います。シーンの名称通り、タイトルを表示する機能を持たせるといいでしょう。

では、ここからシーンの遷移方法について解説していきます。Titleシーンに移行し、ヒエラルキー>右クリック>CreateEmptyでGameObjectを作成し、プロジェクトウィンドウ>右クリック>Create>C# ScriptでC#スクリプトを作成します。それぞれ名前を変更しておいてください。

元の名前変更後の名前
GameObjectTitleSystemObject
NewBehaviourScriptTitleSystem

ついでに、TitleSystemObjectにTitleSystemをアタッチしておいてください。

こうなればOK!

次に、TitleSystemのコードを記述していきます。以下のコードを書いてください。

TitleSystemのコード
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class TitleSystem : MonoBehaviour
{
    void Update()
    {
        if(Input.GetKeyDown(KeyCode.N))
        {
            SceneManager.LoadScene("Game");
        }
    }
}

SceneManager.LoadScene("Game"); は、「Game」という名前のシーンを呼び出すという処理です。

シーンをスクリプトから呼び出すときは「using UnityEngine.SceneManagement;」を忘れないようにしましょう。これがないと SceneManager.LoadScene(“Game”); の処理が行われません。

最後に、シーンを呼び出す上で最も忘れがちな設定を解説します。

Unity上部のFile>BuildSettings…でBuildSettingsを開き、AddOpenScenesを押してください。ゲーム製作に使用した全てのシーン(今回はGameとTitle)が表示されればOKです。

これで設定は完了です。Titleシーンからプレイモードに入り、Nキーを押すとGameシーンに移行します。GameシーンからTitleシーンに戻るコードも同じように作成できるので余力がある方は制作してみてくださいね。

シーンを移行すると、基本的に元々のシーンにあったゲームオブジェクトは全て破棄されます。GameシーンからTitleシーンに戻るコードをTitleSystemに記述してしまうと動かないので注意してください。

異なるスクリプトのメソッド(関数)を呼び出す

異なるスクリプトのメソッドを呼び出す方法はいくつかありますが、今回は[SerializeField]を利用してインスペクター上で指定する方法を解説します。

SerializeFieldを利用する方法

EnemySystemとPlayerMovementスクリプトを変更します。下のコードを書いてください。

EnemySystemのコード
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

public class EnemySystem : MonoBehaviour
{
    [SerializeField] TextMeshProUGUI scoreText;
    private int score;
    SpriteRenderer spriteRenderer;

    void Start()
    {
        score = 0;
        spriteRenderer = GetComponent<SpriteRenderer>();
    }

    void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.CompareTag("Player"))
        {
            score += 10;
            scoreText.text = "Score:" + score; 
            ChangeEnemyPosition();
            spriteRenderer.color = new Color32(0,0,255,255);
        }
    }

    void ChangeEnemyPosition()
    {
        int enemyPositionIndex =  Random.Range(0,3);

        if(enemyPositionIndex == 0)
        {
            transform.position = new Vector2(-8f,3.5f);
        }
        else if(enemyPositionIndex == 1)
        {
            transform.position = new Vector2(0f,3.5f);
        }
        else if(enemyPositionIndex == 2)
        {
            transform.position = new Vector2(8f,3.5f);
        }
    }

    public void ChangeEnemyColorRed()
    {
        spriteRenderer.color = new Color32(255,116,73,255);
    }
}

追加したものと処理の簡単な説明

  • SpriteRenderer spriteRenderer; spriteRendererという変数がSpriteRendererであると宣言。
  • spriteRenderer = GetComponent<SpriteRenderer>();
  • spriteRenderer.color = new Color32(0,0,255,255); Enemyの色を青色に変更。
  • public void ChangeEnemyColorRed()
    {
    spriteRenderer.color = new Color32(255,116,73,255);
    }

    Enemyの色を元の赤色に変更する関数。後でPlayerMovementから呼び出される関数。
PlayerMovementのコード
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    private Rigidbody2D rb2d;
    [SerializeField] float moveVelocity;
    [SerializeField] float jumpPower;
    private bool isJumping = false;
    [SerializeField] EnemySystem enemySystemContent;

    void Start()
    {
        rb2d = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        rb2d.velocity = new Vector2(0,rb2d.velocity.y);

        if(Input.GetKey(KeyCode.D))
        {
            rb2d.velocity = new Vector2(moveVelocity,rb2d.velocity.y);
        }

        if(Input.GetKey(KeyCode.A))
        {
            rb2d.velocity = new Vector2(-moveVelocity,rb2d.velocity.y);
        }

        if(Input.GetKeyDown(KeyCode.Space) && !isJumping)
        {
            rb2d.AddForce(Vector2.up * jumpPower,ForceMode2D.Impulse);
            isJumping = true;
        }
    }

    private void OnCollisionEnter2D(Collision2D collision) 
    {
        if(collision.gameObject.CompareTag("Ground"))
        {
            isJumping = false;
            enemySystemContent.ChangeEnemyColorRed();
        }
    }    
}

追加したものと簡単な説明

  • [SerializeField] EnemySystem enemySystemContent; enemySystem変数がEnemySystemであると宣言。内容はインスペクター上で指定する。
  • enemySystemContent.ChangeEnemyColorRed(); EnemySystemのChangeEnemyColorRed()関数を呼び出す。

そうしたら、PlayerのインスペクターのPlayerMovement>EnemySystemContentにEnemyゲームオブジェクトをアタッチします。

これで完了です。プレイモードに入ってテストプレイを行いましょう。
EnemyにPlayerが接触するとEnemyが青色になり、地面にPlayerが接触すると元の赤色に戻っていると思います。

この様に、スクリプトで別スクリプトの関数を呼び出すことができます。
他の方法も調べてみてください。

コラム:クレジット表記と著作権

ゲーム製作において、クレジット表記と著作権は極めて重要です。法的な問題を回避するためにも、その役割とゲーム製作上での注意を覚えておきましょう。

クレジット表記

クレジット表記とは、ゲーム製作に関わった人の役割や名前を示し感謝を表明するためのものです。書き方は人によりますが、筆者が書く時の例を下に記します。

Programmer : A
SoundCreator : B
Illustrator : C
ScenarioWriter : D
Image : E 様
BGM : F 様

(A~Dは共同制作者、E,Fは外部サイト等を想定しています)

また、利用する素材によってはクレジット表記を条件としてしている物もあります。その場合にはクレジット表記を怠らないように気をつけてください。

ライセンス表記

Unityの拡張機能や一部フォントなどにはライセンス表記が必要なものがあります。ライセンス表記の例はこちら(SQUARE ENIXの公式ホームページに移行します)
クレジット表記と違う点として、法的責任の免責などを述べている点が挙げられます。

著作権

創作物は全て著作権で保護されており、原則として無断利用は禁止です。ですが、創作者が認めた範囲内であれば利用することが可能です。その認めた範囲というのは基本的に利用規約に書かれてあります。特に次の部分に気を付けましょう。

  • 利用可能か
  • クレジット表記が必要か
  • 改変が可能か
  • 商用利用が可能か
  • 制作するゲームジャンル的に利用可能か

これらを確認せずに利用すると訴訟問題に発展することもあります。必ず利用規約を確認し、遵守しましょう。

終わりに

次は「よくある間違い」としてUnityでのゲーム製作上で誰もがする間違いの事例と対策を載せています!
それでは!

タイトルとURLをコピーしました