- 投稿日:2019-02-09T18:57:56+09:00
ゲーム開発中に気をつけていること
はじめに
私は数年間ゲーム会社で働いています。
主にUnityやUE4での開発経験がありますので、
今回は開発中にあった気をつけることや、あるある話をメモしておこうと思います。命名
これはコードであっても使用するリソースであってもですが、
最初に命名したものは絶対後からマネされます。
継承したり複製されそうなものはしっかりした名前をつけていないと、
- 命名した英語の意味が誤っている
- 命名規則が間違っている
といったことが起きます。
後から変えるのは面倒なので最初が大事です。
プロジェクトが始まったときにある程度各セクションごとにしっかり決めれるとベストです。
(コードのお話は他の方の素晴らしい記事があるので割愛します)アセット
エンジンなどを使用するとアセットを扱うことになると思います。
適していない命名をしてしまうと
- 使う人が役割をアセット名から推測できない
- 大文字、小文字、アンダースコアなどの付け方が不統一になる
- 保存先が適していない
といったケースが発生します。
また、アセットまでのパスが長すぎると問題になったりしますので注意です。パラメーター
呼び方は色々ありますが、
ここではキャラクターの攻撃力を決めるデータなどのことを指します。
このデータを識別するIDや値が間違っているとゲームが正常に動かないことがあります。
特に多いのが誤字と多重に定義されることによるヒューマンエラーです。
データのインポート前にIDの誤字や多重定義などをチェックする仕組みを用意しておくと不具合が減って助かります。
また、アプリ側からデータが見つからないときのエラーメッセージも早期発見に繋がります。エラーメッセージ
プロジェクトによっては
全然あまり読んでないことがあります。
内容を分かりやすく、問題を気づきやすくすると効果が高かったです。
また、プログラマーは何を対処すれば直るかを意識して書いておくと、
そのエラーに対する質問が減って助かります。
(IDに誤りがあるので、◯◯というデータを修正してください など)
- クリティカルなものは画面に表示する
- ログを色で識別する
- 見る人が使う言語(日本語)で書く
- 問題となってるリソースを書く
など試してみてはどうでしょうか。チュートリアル
最近のゲームは最初にゲームのチュートリアルが表示されたり、
進行中に機能を説明してくれたりします。
この機能は後から作られることが多く、
不親切な実装をされているとチュートリアル実装の担当者が泣くことになります…。
- 割り込み処理ができない
- チュートリアル用操作ができない
- 最初の無料ガチャが引けない
など。
どうしても難しいところではありますが、
チュートリアルを出す場所は早めに設計できるとベストです。キーアサイン
コントローラーでの操作の割当です。
開発中にコロコロ変わりますので、
担当者が調整しやすいように設計してあるとベストです。
また、担当者はキーアサインが変わったときは全体告知しないと開発の進行に影響します。
- 移動の仕方がわからない
- チェックしたい技が出ない
- デバッグメニューの出し方が分からない
などの報告が多発します。テスト・デバッグ
なにか問題があったときに、
プログラマー以外の人はブレークポイントを置いて数値を見る…といったことはできません。
チームの人がデバッグしやすい環境を整えてあげれるとGoodです。
- パラメーターの表示
- 処理負荷や使用メモリの表示
- 操作の自動化
- バグ報告のフロー整備
など。
デバッガーの方に必要なものをヒアリングするのもいいと思います。バージョン管理
ある意味一番大事なことです。
チーム全体でしっかり習熟しておかないと開発に大きく影響します。
- 競合のマージ
- アップしたデータの取り消し
- データのロック
などがつまづきやすいポイントだと思います。
また、マニュアルの作成・更新は定期的に行ったほうがいいと思います。まとめ
ここで紹介させていただいた例は一部でしたが、
ゲーム開発以外の開発者にとってもあるある話だったのではないでしょうか。
開発では意識して気をつけないと後で大きな問題に繋がることもあります。
この記事が1ミリでも誰かのためになれたら幸いです。こうしたら効率よく開発できたなどのご意見ありましたら、
ご共有いただけますと嬉しいです
- 投稿日:2019-02-09T18:08:19+09:00
[Odin]Unityエディタ上でディクショナリーを設定する
ディクショナリーを設定する
Unityエディタ上で、Dictionaryを設定したい場合もあると思います。
但し、標準のUnityエディタでは、ListのみサポートされているようでDictionaryは設定できません。
有料アセットOdinのSerializedMonoBehaviourを使うと、良い感じにDictionaryが設定できるようになります。ケーススタディ
実際に、Dictionaryを設定したい場合を見てみます。
AddPointAccordingToは渡された数値に応じて、ポイントを付与する関数ですが、同じような記述が続いており無駄が多いです。動的にプロパティを呼ぶ方法もあると思いますが、可読性的にもパフォーマンス的に無駄な気がします。
ここでエディタ上でDictionaryを使えるようにしてみます。GameParameter.csusing UnityEngine; public class GameParameter : MonoBehaviour { public int ScorePoint1; public int ScorePoint2; public int ScorePoint3; public int ScorePoint4; /* ... */ }Score.csusing UniRx; using UnityEngine; using Zenject; public class Score : MonoBehaviour { [Inject] protected GameParameter _gameParameter; private readonly ReactiveProperty<int> _score = new ReactiveProperty<int>(0); private const int MaxScore = 999999; public void Add(int value) { _score.Value = value < MaxScore ? _score.Value + value : MaxScore; } public void AddPointAccordingTo(int count) { if (count == 1) { Add(_gameParameter.ScorePoint1); } if (count == 2) { Add(_gameParameter.ScorePoint2); } if (count == 3) { Add(_gameParameter.ScorePoint3); } if (count == 4) { Add(_gameParameter.ScorePoint4); } /* ... */ } }上記のコードをDictionaryを使ったコードに書き換えます。
SerializedMonoBehaviourを継承するようにすれば良いです。GameParameter.csusing UnityEngine; using System.Collections.Generic; using Sirenix.OdinInspector; public class GameParameter : SerializedMonoBehaviour { public Dictionary<int, int> ScorePoints; }Score.csusing UniRx; using UnityEngine; using Zenject; public class Score : MonoBehaviour { /* 省略 */ public void AddPointAccordingTo(int count) { Add(_gameParameter.ScorePoints[count]); } }注意
エディタ上で編集可能なフィールドをリネームすると、せっかく設定した値が吹っ飛びます。ディクショナリーなどの複数の値が設定できる場合は、なかなか辛いものがあります。
FormerlySerializedAsアトリビュートを利用することで値は引き継げるので必要に応じて利用しましょう。
Riderのリファクタ→リネームだと自動でFormerlySerializedAsを設定してくれたので、IDEのリファクタ機能を使うのが安全かもしれません。
- 投稿日:2019-02-09T17:13:45+09:00
VRoidモデルの髪をLeapMotionでさわる
できたもの
Leapmotionで髪をさらさらした#vroid#lookingglass pic.twitter.com/PIMS5fkHhn
— neku (@neku18) 2019年1月25日VRoidのモデルの読み込みやLeapMotionのセットアップについては割愛。
VRoidの段階で髪にボーンはついていることとします。ボーン設定の場所
VRoidのモデルを読み込むと以下の画像のようにsecondaryという場所がある。
そこを開くとVRM Spring Boneというスクリプトがひっついている。
これがおそらくボーンの動きを制御するスクリプト。
この中のCollider Groupに髪と衝突させたいものを追加することで,好きなものを髪に衝突させ物理演算ができる。
衝突範囲の設定
衝突させるものには,VRM Spring Bone Collider Groupというスクリプトを引っ付けて,衝突範囲を設定。
リープモーションの場合は,動かす手のモデルのどこかにこれを設定する。
よりリアルにしたい場合は,指一つ一つに設定する。
設定したものを先程のVRM Spring Boneの中のCollier Groupsに追加すれば完了。
- 投稿日:2019-02-09T14:00:42+09:00
Unityでゲーム作る時に考えるべきこと
概要
- Unityでゲーム作る時に考えるべきことを考えてみました。
- 作り方は様々な手段があるのである意味でひとつの解答ぐらいに受け取ってもらえると嬉しい。
- サーバー連携は軽く言及しますが、取りきりアプリ・ゲームを前提とします。
ゲームを作る時に必要な要素
- 設計
- データ
- アセット
ここから細分化してみます。
- 設計
- どういうゲーム作る?
- ゴールは?ユーザーに何を遊ばせたい?
- どういうデータが必要?
- どういう素材が必要?
- エンドコンテンツどうしようかな?
- そもそもいる?ユーザーに長く遊んでほしい?
- データ
- ユーザーデータ
- ユーザーのゲームの進行状態などを保持するデータ。
- マスターデータ
- ゲームの特定の機能に対してのデータ。
- アセット
- 参照
- シーンあるいはプレハブに含めてしまうケース。
- 読込対象がシーンあるいはプレハブに含まれる。
- それを前提に設計するのがベスト。
- AssetBundle
- 複数のアセットを束ねる機能。
- 読み込み・メモリ・汎用性に長けている。
- 生成・読込・管理の難易度が高い。
- Resources
- 手軽に配置・ロードがしたい場合に使用。
- あまり使用が好ましくない機能。
- 読込・管理の難易度が非常に低い。
- ただし使用することは非推奨とされているので注意。
- StreamingAssets
- ビルドにAssetBundleを含めたい場合に使用。
- 実はプラットフォーム毎に管理する機能など実装が必要で難易度が高め。
- Local
- ファイルとして保存した場合。
- ダウンロードコンテンツ(以降DLC)などで使用。
- 難易度が非常に高い。
設計
すごいザクッとした言葉使っていますが、何事も設計が基本です。
ゲームを作る時には要素の洗い出しは必要不可欠なのでまずこれを行います。
「ドラクエっぽいRPG」を作る、とまず家庭した場合は以下のようになります。
- イベント
- 会話
- 選択肢
- アイテムの獲得(宝箱)
- バトル
- コンテンツ
- アイテム
- モンスター
- レベル(ステータス)
- 報酬
- 経験値
- ゴールド
- アイテム
- プレイヤー(仲間)
- レベル(ステータス)
- 経験値
- 装備
- シンボル
- マップ
- マップ
- マップ切り替え
- エンカウント
- 調べる
etc...
大雑把ですが、上記のようなものがあるかなと思います。
カテゴリごとに必要な機能を洗い出し、洗い出したらそれを正規化していきます。
洗い出しそのものは大雑把でいいですが、思いつかなくなったら大雑把さをなくして綺麗にすることが大切です。データ
ゲームにはユーザーの進行状態などを保持するデータとゲームの特定の機能に対してのデータと大まかに2つのデータに分かれる。
前者をユーザーデータとし、後者をマスターデータと呼称します。
この部分を細分化すると以下のようなものを用意する必要があります。考えるべきこと
手段はある程度絞った方がいいため、自分が作りたいゲームにあった手法をとった方がいいと思います。
一方で複数の手段が必要に駆られる場合は管理も含めて適切な手段をしっかり考え、対応するのがいいです。
ユーザーデータ
- 保存手段
- PlayerPrefs
- 手軽にゲームを作りたいならこの手法。
- Json/File
- 買い切りアプリなどはこの手法。
- Server
- 多くのソーシャルゲームアプリはこの手法。
- データ形式
- Json
- 手軽にしたいならこの手法。
- バイナリ
- MessagePack、flatbuffers etc...
- PlayerPrefsにダイレクトに保存 [非推奨]
- この手段は手軽のようでまったく手軽ではないので注意。
- 管理など、結構綿密に設計する必要があります。
- Attributeなど、Editorを拡張することで精密かつ簡略化された開発でカバーするなど必要。
マスターデータ
- データ形式
- Json
- 一番お手軽。入力方法を考えると変換ツールなどは必要。
- バイナリ
- MessagePack、flatbuffers etc...
- ScriptableObject
- UnityEngine.Objectの要素ではあるのでUnityとの親和性が非常に高いです。
- CSV [非推奨]
- あえて採用する理由がない……。
- 入力形式
- エクセル
- 王道、NPOIなどを使ってコンバートするとよし。
- GoogleSpreadSheet
- 結構便利、エクセルの上位互換ではないので注意。
- GGSはバージョン管理がしづらい、Webに依存する、実装までのハードルなど様々な問題があります。
- プロジェクトに直接直結するものでもないのでこれを直接的なツールとして使うのは好ましくありません。
- 直編集 [非推奨]
- Json、CSVなど。
- マンパワー。
- Unity上で編集
- ScriptableObjectはこの手法でとられることがあります。
個人的にはScriptableObjectは非推奨です。
AssetBundleの対象は別にTextAssetでもできることであり、データの変換のしやすさを踏まえるとScriptableObjectは優れているとは言い難いところがあります。
またデータの編集難易度の高さや再利用性の低さも合わせて考えるのであればJsonあるいはバイナリ形式が望ましいと思います。アセット
Unityがアセットとして認識してくれるデータ郡などです。
Texture、Material、Prefab...など様々なものが対象となります。考えるべきこと
ディレクトリの配置
- 疎かになりがち、しっかり決めましょう。
- よくあるのは「とりあえずResourcesな放り込む」
- Resourcesはよいものではないので使うのはやめましょう。
- AssetBundle、あるいは予めシーンの参照に含めることを前提とした設計に落とし込む。
- AssetBundle前提は難しいものではあるのでここは切って考えるのもありです。
- その場合はResourcesに頼る方向へとなります。
ロード手段
- 専用のロード機能を実装するのが好ましい。
- Editor上ではAssetDatabase、ゲーム上ではAssetBundle化されたデータで読み込むをするなど。
- Resources/StreamingAssetsは本当に必要なケースに限定して対応します。
- Resourcesはマスターデータなど。
- マスターデータもAssetBundleに含めるべきなので、本来はResourcesフォルダは使わないこと。
- StreamingAssetsはAssetBundleなど。
Unity管轄にならないデータについてはライブラリに依存した機能を除いて極力頼らないようにする。
小言
RPGツクール
- 予め「データ・アセット」の両方の問題を解決しているスグレモノ。
- 設計部分もかなり補完しているため、よほど凝ったことをしない限りはディテール部分だけで解決する。
ゲームは設計さえしっかりすればそれぞれの要素を調べられたら作れる。
- そう考えるとゲーム作りってめちゃくちゃ簡単ではと思います。
- 一方で設計時の細分化ができなければ一気にゲーム作成のハードルは上がります。
最後に
- ゲームは形はどうあれ完成させよう!
- 投稿日:2019-02-09T13:31:22+09:00
Unity コールバックの受け取り方あれこれ
Unityゲーム開発に使えるコールバックの備忘録
System.Action
System.Action と System.Func を使えば、デリゲートの形式を宣言する必要がないので楽。
使用例
DoTweenと組み合わせて、フェードアウト・インしつつ何かを実行するメソッドを作ってみた。
引数でColorを渡してやるようにすれば、ブラックアウト、ホワイトアウト好きなものにカスタマイズもできる。FadeController.csusing System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.SceneManagement; using DG.Tweening; namespace Sample { public class FadeController : MonoBehaviour { [SerializeField] CanvasGroup fadeCanvas; [SerializeField] Image image; System.Action action; public void FadeAction(System.Action action) { image.color = Color.black; var sequence = DOTween.Sequence(); sequence.Append(fadeCanvas.DOFade(1.0f, 2.0f)) .AppendCallback(() => { action(); //画面が真っ暗になったら、引数のactionメソッドを実行 }) .Append(fadeCanvas.DOFade(0.0f, 2.0f)) .OnComplete(() => SetActive(false)); } } }System.Func
返り値がある場合には System.Func を使用します。
値を渡して、それが ~~ ならば、最後の引数を返す ...といった処理が書ける。使用例
Sample.csusing System; public class Sample { public void Calculate() { Func<bool,bool,string> callBack = (valueA,valueB) => { if (valueA && valueB) { return "TRUE!"; } return "FALSE!"; }; } }UnityEvent
シーンに保存することができる引数を持たない永続的なコールバック。
...スクリプリファレンス分からない。
UnityEventとSystem.Actionの違いについては下記で紹介されていた。
【Unity】UnityEventの用法と用量特徴
- 機能としての違いはない
- Unityのインスペクター上に表示されるため、直感的
- 複数のイベントを簡単に登録できる。
- Invoke()を使うと登録したメソッドが一斉に実行される。UnityEventSample.csusing UnityEngine; using UnityEngine.Events; using System.Collections; public class UnityEventSample: MonoBehaviour { [SerializeField]UnityEvent m_MyEvent; void Start() { if (m_MyEvent == null) m_MyEvent = new UnityEvent(); m_MyEvent.AddListener(Ping); //Ping()メソッドを登録 } void Update() { if (Input.anyKeyDown && m_MyEvent != null) { m_MyEvent.Invoke(); //イベントの実行 } } void Ping() { Debug.Log("Ping"); } }UnityのUGUIボタンにあるような奴がインスペクター上に現れる。
ExampleClass.csusing UnityEngine; using UnityEngine.Events; [System.Serializable] public class MyIntEvent : UnityEvent<int> //引数の型を指定しておく { } public class ExampleClass : MonoBehaviour { public MyIntEvent m_MyEvent; void Start() { if (m_MyEvent == null) m_MyEvent = new MyIntEvent(); m_MyEvent.AddListener(Ping); } void Update() { if (Input.anyKeyDown && m_MyEvent != null) { m_MyEvent.Invoke(5); } } void Ping(int i) //登録するメソッドにint型引数を持たす { Debug.Log("Ping" + i); } }UnityAction
UnityActionというコールバック関数もある。
UnityEvent で使用される引数なしのデリゲートです。
違いや使い分けについては、UnityEventコールバック関数の設定方法についてが参考になった。
参考
UnityEvent でコールバックする
C# 備忘録 コールバックについて
UnityEventコールバック関数の設定方法について
- 投稿日:2019-02-09T12:13:12+09:00
【Blender】UV展開とベイクをしてからUnityへ取り込む
環境メモ
⭐️Mac OS Mojave バージョン10.14
⭐️Blender v2.79.6
⭐️Unity 2018.2.15f1
⭐️Mac BookBlenderでUV展開とベイクをしてからUnityへ取り込む
画像が多いとQiitaの容量オーバーで画像が貼り付けられなくなるので
詳細は、「はてなブログ」に記載してます↓
かぴばらさんの覚書ブログ nonkapibara
- 投稿日:2019-02-09T02:07:55+09:00
Unityの教科書レベル2 メモ
fの必要性について
変数の初期化について小数を代入するときは後ろに"f"をつける
例) float height = 1.0f;
これをつけないとdouble型だと認識されてしまいエラーが出るため
(C#ではdouble型の値をfloat型に代入することは禁じられている)配列の準備(基礎中の基礎)
例だけ示す
例) int[] hairetsu = new int[10];メソッドは引数は複数個渡せるが返り値は1つだけ
クラスの扱いについて
クラスは関係のある変数とメソッドをひとまとめにできる。
作成したクラスはintやstringのように型として扱える
例) Player myplayer = new Player();
(ここでPlayerはクラス名、 myplayerは変数名の扱いである)magnitudeメンバ変数について
ベクトルの長さを求めることができる
例) float dir = new Vector2(3.0f, 4.0f).magnitude;
- 投稿日:2019-02-09T00:04:58+09:00
【Unity(C#)】VRカメラから3Dカメラに切り替え
この記事は『プログラミング完全未経験からUnityでの開発現場に迎え入れてもらえた世界一の幸せ者』
の記事です。そのつもりでお読みください。
HMDをトラッキングしてVR空間に!
まず最初にこれはVIVEというVR専用のWearable Deviceでのお話です。
会社でいつも何気なく使ってますが家庭用ゲーム機として手の届く値段じゃないですね...HMDとは Head Mounted Displayの略称で頭部装着ディスプレイのことらしいです。
三か月働いてて今日初めて知った略称です。早速見出しにぶち込んでやりました。☆下準備☆
①Asset Storeから
Steam VR Plugin
をDL+Import
②ImportしたSteam VR Plugin
のPrefabから[CameraRig]
をHierarchyにドラッグ&ドロップ
これでエディター画面の
▷
を押せばHMDにVRの世界が広がるはずです。なぜVRカメラから3Dカメラに切り替える必要があるのか?
今回私が陥った状況としては、
・VRのカメラをオフにして別アングル(別のカメラ)からVR空間内をゲームビューに映したい
です。さらにかみ砕いて言えば、
・VRコンテンツのPVを作りたいのにやり方がわからない!
です。VRのPVはあまり一人称視点を導入するべきではないという意見があり、
私もいろいろなPVを見ましたが、賛同しております。(まあまあ古い記事ですが...→MoguraVRさん)一人称視点のPVはあんまりおもしろそうじゃないです。
そのゲームをプレイしたことがない人からしたら、何をやっているかあまり伝わってきません。なので、VR空間内を一人称視点以外で撮影する方法が必要となります。
そこで、
①[CameraRig]
を非アクティブにする
②新しい3D用のカメラを別で用意しておいてアクティブにするで可能だと思い、試しましたが無理でした。
VRカメラから3Dカメラに切り替え
XRSettings
をオン、オフ切り替えでいけました。
今回はキーボードで切り替えにしてます。using UnityEngine.XR; void Update () { if (Input.GetKeyDown(KeyCode.F)) //VRのカメラオフ { XRSettings.enabled = false; } if (Input.GetKeyDown(KeyCode.T)) //VRのカメラオン { XRSettings.enabled = true; } }もっといい方法があれば教えてください。