Unityで3Dスマホゲームを作ってみました

manabyのeラーニングで下記のようなゲームを作成しましたので、スマートフォン用に移植してみました。
https://ma510884.com/unity_chan4/

基本的な構造はそのまま利用できそうでしたが、スマホでキャラクターを動かすためにはタッチパネルに対応したコントローラーが必要になります。ちょうど「Joystick Pack」という無料のアセットがありましたので、そちらを利用することにしました。

使い方をネットで検索してみると、とても簡単にできそうに書いてありましたが、必要なアセットがもう入手できなかったり、Unityその他のバージョンが違うため上手くいかなかったりと難航しました。
「teratail」 https://teratail.com/というサイトで質問してみることも考えましたが、状況を詳しく説明できないと的確な回答が得られません。
そこで、参考にしたこちらのサイトで実際に作られた方に質問してみることにしました。

すると、懇切丁寧な解説をしていただけましたので、それを見ながら実装し完成させることができました。

それでは説明の方に移っていきます。使用したものは、

以上の4つです。

まずは地面を用意します。Hierarchyから3D Object → Terrainを作成します。
Positionをx=-100、z=-100とします。

次にキャラクターを配置します。こちらの「Unityちゃん公式ページ」 https://unity-chan.com/からSDユニティちゃん 3Dモデルデータをダウンロードします。

ダウンロードしたunitypackageは、Assets → Import Package → Custom Packageから該当ファイルを選択するとインポート出来ます。

ProjectからUnityChanフォルダ → SD_unitychan → Prefabs → SD_unitychan_humanoidを選択し、HierarchyまたはSceneビューの任意の場所へ配置します。

続いてWindow → Asset Storeで公式アセットストアを開き、Joystick PackとNature Starter Kit 2をダウンロード・インポートします。

ではまずはJoystickを配置します。JoystickはUI(User Interface)の種類になりますので、Hierarchy → UI → Canvasフォルダを作成し、Project → Joystick Pack → Prefabsから(今回は)Variable JoystickをCanvasフォルダ内に配置します。初期状態では色が白で見づらいですので、Hierarchy → Canvas → Variable Joystickの下のBackground、HandleにそれぞれInspectorのImageにColorという項目がありますので、そちらで色を変えることができます。背景によって変更してみてください。

次に地面を作りこむ前に、Joystickでキャラクターを動かせるようにします。先にAnimatorControllerの作成が必要なのですが、まずはこちらのScriptをご覧ください。(UnityChanController.cs)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(CharacterController))]
public class UnityChanController : MonoBehaviour
{
    [SerializeField] VariableJoystick m_VariableJoystick;
    [SerializeField] Animator m_Animator;
    [SerializeField] float m_Speed = 1;
    private CharacterController m_Controller;
    private Vector3 m_Direction;

    void Start()
    {
        m_Controller = GetComponent<CharacterController>();
    }

    void Update()
    {

        if (m_Direction != new Vector3(0, 0, 0))
        {
            transform.localRotation = Quaternion.LookRotation(m_Direction);
        }
        m_Animator.SetFloat("isMoving", Mathf.Max(Mathf.Abs(m_Direction.x), Mathf.Abs(m_Direction.z)));

        m_Controller.Move(m_Direction * m_Speed * Time.deltaTime);
    }

    public void FixedUpdate()
    {
        m_Direction = Vector3.forward * m_VariableJoystick.Vertical + Vector3.right * m_VariableJoystick.Horizontal;
    }
}

26行目、animator.SetFloatの引数に”isMoving”とありますが、AnimatorControllerでこれから作成する項目なのでUnityで警告などが出ても問題ありません。

ではAnimatorControllerを作成します。ProjectからAnimatorControllerを選択し、名前をUTC_Animatorとします。

次にAnimatorビューのParametersを選択し、+ボタンからfloat型のパラメーターを作成し、名前をisMovingとします。float型は実数(少数)を扱うことができ、コントローラーの傾き具合によって動きを制御できます。

次にAnimatorビューの何もない所で右クリックし、メニューからCreate State → From New Blend Treeと選択し、Blend Treeを作成します。

BrendTreeをダブルクリックすると編集画面が開きますので、もう一度BrendTreeをクリックして選択します。するとInspectorビューでParameterにMotionを追加できるようになりますので、+ボタンからフィールドを2つ追加します。

追加したMotionのフィールドには、「Standing@loop」(立っているモーション)と「Running@loop」(走っているモーション)をそれぞれ追加します。◎をクリックすると一覧が出てきますので、そこから選択してください。

ここまで出来たら、HierarchyからSD_unitychanを選択し、AnimatorのControllerをUTC_Animatorに変更します。

そして先ほどのUnityChanControllerをVisual Studioで作成し、SD_unitychanにアタッチします。

Variable Joystickの欄にはHierarchyからVariable Joystickを、Animatorの欄にはSD_unitychanのアバターをそれぞれドラッグ&ドロップします。

以上でUnityちゃんを動かす準備は完了ですので、一度動かしてみましょう。

Unityを実行してみたところ、エラーが出てしまいました。どうやらまばたきをさせるスクリプトに不具合があった模様です。警告文をダブルクリックしてVisual Studioを立ち上げ、該当する行を「//」でコメントアウトしました。

//
//AutoBlinkforSD.cs
//SDユニティちゃん用オート目パチスクリプト
//2014/12/10 N.Kobayashi
//
using UnityEngine;
using System.Collections;
// using System.Security.Policy;

namespace UnityChan
{
	public class AutoBlinkforSD : MonoBehaviour
	{

		public bool isActive = true;		//オート目パチ有効
		public SkinnedMeshRenderer ref_face;	//_faceへの参照
		public float ratio_Close = 85.0f;	//閉じ目ブレンドシェイプ比率
		public float ratio_HalfClose = 20.0f;	//半閉じ目ブレンドシェイプ比率
		public int index_EYE_blk = 0;	//目パチ用モーフのindex
		public int index_EYE_sml = 1;	//目パチさせたくないモーフのindex
		public int index_EYE_dmg = 15;	//目パチさせたくないモーフのindex
(以下略)

無事、動かすことができました。続いては地面を作成していきます。

NatureStarterKit2のSceneからNew TerrainをHierarchyにドラッグ&ドロップし、名前をGroundとします。New Terrainにはすでに道がありますので、それを利用したいと思います。

カメラの向きの都合上、道の一番奥へ配置する必要がありますのでUnityちゃんとカメラの位置を調整します。Unityちゃんはx=75、y=0.1、z=70、Main Cameraをx=75、y=0、z=68としました。

ではゲームオブジェクトを追加していきます。Hierarchy → 3D Object → Capsuleと選択し、Unityちゃんの近くに配置します。少し大きいようですので、Scaleをx、y、zそれぞれ0.5としました。続いてProject → Materialと選択し、名前をCapsuleMaterialとします。そして好きな色に変更し、先ほど配置したCapsuleにアタッチします。

色を変えたCapsuleはProjectウィンドウへドラッグ&ドロップし、Prefabにします。そして、Hierarchyに空のフォルダを用意し、名前をCapsulesとします。この中へPrefabのCapsuleを入れ、複製してどんどん並べていきます。合計で約30個ほど並べました。また、Unityちゃんが接触したかどうかの判定のため、InspecterのCapsule ColliderのIs Triggerにチェックを入れます。

同じ要領で、NatureStarterKit2 → Natureからbush(低木)を植えていきます。1本ずつ植えていくと大変ですので、数本をまとめて空のフォルダに入れてPrefab化し、まとめたものを地面に配置していきます。主に行き止まりなど進んでほしくないところに配置しました。

次にゴールとなるオブジェクトを配置します。場所は画面右下の行き止まりの所にしました。Hierarchy → 3D Object → Cubeと選択します。このオブジェクトに触れるとゴールとなるように、InspecterのBox ColliderのIs Triggerにチェックを入れます。

オブジェクトの配置が一通り終わりましたので、スクリプトの方に移っていきます。

まずは配置したオブジェクトの雰囲気を確認するために、カメラをキャラクターに追従出来るようにしておきます。こちらの記事を参考にしました。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraController : MonoBehaviour
{
    Vector3 diff;
    public GameObject target;

    // Start is called before the first frame update
    void Start()
    {
        diff = target.transform.position - transform.position;
    }

    // Update is called once per frame
    void LateUpdate()
    {
        transform.position = Vector3.Lerp(transform.position, target.transform.position - diff, Time.deltaTime * 5.0f);
    }
}

上記のスクリプトをMain Cameraにアタッチして、targetにUnityちゃんを指定します。Unityを実行してみて、各オブジェクトの配置を確認し、おかしな所があれば修正しましょう。

次にゲーム部分の基礎となるスクリプトを作成します。名前をPlayerControllerとし、Unityちゃんにアタッチします。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public AudioClip sound1;
    public AudioClip sound2;
    private AudioSource audioSource;

    // Start is called before the first frame update
    void Start()
    {
        audioSource = GetComponent<AudioSource>();
    }

    // Update is called once per frame
    void Update()
    {

    }

    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.CompareTag("Item"))
        {
            other.gameObject.SetActive(false);
            audioSource.PlayOneShot(sound1, 1.0f);

        }
        else if (other.gameObject.CompareTag("Goal"))
        {
            this.GetComponent<Rigidbody>().isKinematic = true;
            audioSource.Stop();
            audioSource.PlayOneShot(sound2, 1.0f);
        }
    }

}

sound1にはカプセルを取った時の効果音を、sound2にはゴールした時の効果音を指定します。BGMはUnityちゃんにアタッチし、Play On AwakeとLoopにチェックを入れておきます。

続いてゴールオブジェクトに触れた時のスクリプトを作成します。名前をGoalCheckerとし、ゴールオブジェクトにアタッチします。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class GoalChecker : MonoBehaviour
{
    public GameObject retryButton;
    public GameObject mainCamera;
    public GameObject player;

    // Start is called before the first frame update
    void Start()
    {
        retryButton.SetActive(false);
    }

    // Update is called once per frame
    void Update()
    {

    }

    public void OnRetry()
    {
        SceneManager.LoadScene(SceneManager.GetActiveScene().name);
    }

    private void OnTriggerEnter(Collider other)
    {
        retryButton.SetActive(true);
    }
}

他にリトライボタンが必要になりますが、こちらの記事を参考にしてください。

リトライボタンの配置が完了しましたら、InspecterのOn Clickにゴールオブジェクトをドラッグ&ドロップし、Functionの部分をGoalcheckerのOn Retryと選択します。

以上でゲーム部分は完成しましたので、Android用にビルドします。こちらの記事を参考にしてください。

実機で動作している様子はこちらです。

以上、最後までお読みいただきありがとうございました。次はもう少し本格的なゲームを制作してみたいと思います。

  • 5
  • 0
  • 2

おすすめのタグ

Laffey(ラフェィ)

色々な物・もの・モノを作って記事にしています

作者のページを見る

関連記事

寄付について

「novalue」は、‟一人ひとりが自分らしく働ける社会”の実現を目指す、
就労継続支援B型事業所manabyCREATORSが運営するWebメディアです。

当メディアの運営は、活動に賛同してくださる寄付者様の協賛によって成り立っており、
広告記事の掲載先をお探しの企業様や寄付者様を随時、募集しております。

寄付についてのご案内