- 投稿日:2020-08-01T22:31:13+09:00
Hololens2でMapBox SDK (Maps for unity)を使う
はじめに
Hololens2で3Dマップを使用する際、BingMapやGoMAP等いくつかの選択肢がありますが、
今回はMapBoxのUnity用SDK(Maps for Unity)を使用する方法をご紹介したいと思います。尚、下記がMapBoxを採用した理由です
無料である程度の規模まで使える
https://www.mapbox.com/pricing/大きなサイズの地図を表示できる
BingMapも試しましたが、地図の大きさが最大3x3 UnityUnitに制限されているようです。
MapBoxは最大10x10UnityUnitまで表示可能ですタイルの品質
BingMapでは私の使用する拡大率(ZoomLevel 7~13)でのタイル(テクスチャ)が芳しくありませんでした。注意点
Maps for UnityはIL2CPPでのビルドに対応していません。
よってScriptingBackendに.Netを指定する必要がありますので、Unityのバージョンは2018.4系を使用する必要があります。MapBox アカウント作成
Maps for Unityを使用する為にはmapboxのアカウントが必要です。
https://account.mapbox.com/auth/signup/
からアカウントを作りログインしておきますMaps for Unityダウンロード
https://www.mapbox.com/install/unity/
よりSDK(Maps for Unity)をダウンロードしますMRTKダウンロード
https://github.com/Microsoft/MixedRealityToolkit-Unity/releases
よりMRTK(Unity.Foundation.2.4.0.unitypackage)をダウンロードします。プロジェクト作成 & MRTKインポート
適当なサンプルプロジェクトを作り、MRTKをインポートします。
前述していますがUnityのバージョンは2018.4系を使って下さい。
プロジェクトを作成後、Assets => Import Package => Custom Package から先ほどダウンロードしたMRTKをインポートします。
途中で表示されるインポートするファイルの指定では、全てのファイルをインポートします。インポートが終了するとUnityのメニューバーにMixed Reality Toolkitという項目が追加されますので、
ここからAdd Scene and Configure...を実行します。
この段階で、Hierarchyは画像のようになっているはずです。
MRTK 設定
Project ViewからMixedReality Toolkitを選択し、インスペクターでMixed Reality ToolKitコンポーネントの設定を編集します。
Configuration Profileを DefaultHololens2ConfigurationProfileに変更します。Buildセッティング
Hololens2向けにビルドセッティングを行います。
File => BuildSettingを選択してBuildSettingダイアログを表示し、PlatformからUniversal Windows Platformを選択後、Switch Platformボタンを押してプラットフォームを切り替えます。
途中MRTK Project Configuratorが表示される場合は、Enable MS Buildのチェックを外してApplyしてください。
Player設定
続いてBuildSettingダイアログでPlayerSettingボタンを押し、Player設定画面を表示します。
XR Settingsタブの設定および確認
- Virtual Reality Supportedがチェックされている
- Virtual Reality SDKsにWindows Mixed Realityが追加されている
- Depth Formatを16bit depthに変更
- Enable Depth Buffer Sharingがチェックされている
- Stereo Rendering ModeでSingle Pass Instancedが選択されている
Publish Settingsの設定および確認
- Packagingをプロジェクトにふさわしい内容で適当に書き換えてください。
Other Settingsの設定および確認
- Configuration内のScripting Runtime Versionが.Net 4.x Equivalentが選択されている
- Scripting Backendを.NETに変更
- Api Compatiblility Levelを.NET Standard2.0に変更
Maps for Unityのインストール及び設定
Assets => Import Package => Custom Package から先ほどダウンロードしたMaps for Unityをインポートします。
途中で表示されるインポートするファイルの指定では
・Mapbox
・ThirdpartyAssets
のみをインポートします。インポート中にMapbox Setupダイアログが表示されますので、Access Tokenの欄にアクセストークンを入力し、submitを押します。
アクセストークンは下記URLより取得して下さい。
https://www.mapbox.com/install/
またこのダイアログから各種サンプルプログラムにアクセスできます。
今回はサンプルプログラムは使わないので、アクセストークン設定後はそのままダイアログを消してください。
Project ViewからAssets\Mapbox\Core\Plugins\Mapbox\MapboxAccounts\net4xのMapboxAccountsUnity.dllを選択し、
インスペクターよりSelect Platforms for pluginセクションのWSAPlyerのチェックをONにします。地図の設置
Assets\Mapbox\Prefabs内のMapをHierarchyにドラッグした後に、インスペクターでMapを表示します。
インスペクターでAbstract Mapコンポーネントに対し設定を行う事で地図の表示内容を制御します。
表示する地点や拡大率、衛星写真・地図の切り替え等はここで行います。
今回は下記内容で設定してくださいGENERAL/Locationセクション
- Latitude Longitude : 35.3611236, 138.7266352
- Zoom : 14
GENERAL/Extent Optionsセクション
- Extent Options : Range Around Center
- West , North , East , South : 10
- Initialization On Start : ON
GENERAL/Othersセクション
- Unity Tile Size : 1
IMAGEセクション
- Data Source : Mapbox Satellite
- Use Retina : ON
- Use Compression : OFF
- Use Mip Map : ON
TERRAINセクション
- Data Source : Mapbox Terrain
- Elevation Layer Type : Terrain With Elevation
- Add Collider : OFF
- Exaggeration Factor : 1
また、MapオブジェクトのTransformのPosition:Yを-2程度にすると見やすくなるかもしれません。
上記設定後、UnityのPlayボタンを押してGameViewに富士山が表示されればOKです。
初期表示では視点位置により表示内容が分かり難い場合があります。
その場合はWASDキーと、右クリック+マウス操作で視点を変更して下さい。UnityでのBuild
File=>Build Settingsを選び、Build Settingsダイアログを表示します。
Add Open Sceneを押してビルド対象に現在のシーンを追加します。
Buildを押しビルド結果の出力ディレクトリを指定するとビルドが開始されます。
ビルド終了後、Console Viewでエラーが発生していないことを確認して下さい。VisualStudio2019でのBuild
Unityでのビルドが終了後、ビルド結果の.slnファイルをVisualStudio2019で開き下記設定を行います。
- 構成をDebugからReleaseまたはMasterに変更する
- 構成をARM またはARM64に変更する
- 構成をデバイスに変更する
ソリューションエクスプローラーからUniversal Windowsプロジェクトを選び、Nugetパッケージの管理を選びSQLite.Universalをインストールして下さい。
デバッグ => デバッグ無しで開始
を選ぶとビルド後に実機にデプロイされ、自動的に起動するはずです。
全てが正しく行われていれば、目の前に富士山が表れると思います。
- 投稿日:2020-08-01T21:09:48+09:00
【Windows版】Unityインストール方法 2020/08
概要
今回はWindowsのPCでUnityをインストールする際に、自分が経験した躓いたことや注意を踏まえた流れを書いたので是非参考にしてください。
この記事で、そのまま書いてある手順を踏めば、あら不思議。勝手に自分の手元にはUnityが扱えている状態になっていることでしょう。
所々の画像がMacになってたりしますが、やることは変わらないので気にせず進めてください。まずは公式HPへ
Unityをいれるために、まずはインストーラーを用意します。
こちらのUnity公式HPに飛んでください。UnityHubをいれよう
下の画像のようなページに飛ぶので、「Unity Hubをダウンロード」を押しましょう。
そしたら、何かいろいろ出てくると思うので、はいとか同意する的なニュアンスの方をポチポチしていってください。
無事Unity Hubがダウンロードできたら早速起動してください。すると環境設定の画面のライセンスという画面に飛びます。
もし画像の画面になっていなかったら右上の歯車マークを押すと行けると思います!
写真にはモザイクをかけてますが、入れたばかりの皆さんはまだ何も入ってないはずです。
UnityHubからUnityのアカウントを作成
次にライセンスを取得しましょう!!!…って言いたところですが、ライセンスを取得するためにはまずUnityにアカウントを作ってログインする必要があります。なので右下の『ログイン』というボタンからアカウントの作成を行いましょう。
下にボタンがない方は右上の歯車の隣にある人マークを押してアカウント作成まで進んでください。
そしたら下のような画像になるので『IDを作成』をポチります。
各項目のメールアドレス、パスワード、ユーザーネーム、フルネームを入力して利用規約のチェックボックスにチェックを入れてください。その下のチェックは付けなくていいです。(いわゆるメルマガを受け取るかどうかの話です。)
また、パスワードは頭文字が大文字なので注意してください!
無事入力が済んだら、『UnityIDアカウントを作成』をポチッ。
すると、登録しようとしてるメールアドレス宛てにUnityから本人確認のメールが届くと思うので、本人確認を行ってください。
無事確認が済んだら、早速ログインしてください。ログインが完了すると、『新規ライセンスの認証』のボタンがアクティブ状態になりますので、そこをクリックしてライセンスを獲得しましょう。
選択肢はUnityPersonalを選びます。するとまた選択肢が増えるので、個人利用の場合は下を、どこか団体に所属している場合は上を選択します。
Unityを入れよう
ここからやっとUnityを入れることができます。
UnityHubの最初の画面に戻りましょう。そしたら、左の欄に『インストール』があるのでそこに移り、右上の『インストール』というボタンを押してください。
するとこんな画面が出ると思うので、バージョンを選びます。最新正式リリースの中の『Unity 2019.4.6f1 LTS版』というものを選びましょう。こちらはバグが少ないのでおすすめです。迷ったらこれを選べば間違い無いです。
また、この記事は現在2020年の8月に更新していますが、LTSのバージョンが複数あったり、画像とはバージョンの値が全く異なるものが出てくることがあります。
特に理由がなければ、最も新しいバージョンのLTS版を選びましょう!(画像の場合、真ん中の項目を選択。)
ここで注意です!こんな画面に移ると思うので、モジュールというものを追加していきます。これが無いとビルドというものをするときに大変なことになってしまうのでしっかり確認しながら行ってください。(ぶっちゃけ後からでもできますが…)
いろんなモジュールがありますが、用途に合わせて選んでください、AppStoreやGooglePlayにリリースするのであれば、AndroidとiOSにチェックを入れましょう!
そして、一番上にある『Microsoft Visual Studio Community 2017』は必ずチェックを入れてください!そして次へを押すと、こんな画面が出ると思うので同意をして実行しましょう!早速Unityのダウンロードが始まります。
と同時にVisualStudioもダウンロードされます。途中でこんな画面が出ると思いますが、画像と同じものにチェックが入っていればOKです。(このダウンロードの時間の速さは人によって個人差があります。お菓子でも食べながら気長に待ちましょう。)
無事ダウンロードが終わるとこんな感じで表示れます。下のアイコンはiOSとAndroidのモジュールが入っている証拠になります。
早速Unityを起動してみよう!
ここまでお疲れ様でした。インストールの話はここまでですが、
一応おまけということでプロジェクトの作り方を軽く解説していきます。
下の画像のようにプロジェクトから『新規作成』を押します。
するとこんな画面になると思うので、作りたいゲームに合わせて2Dや3Dなどを選び、プロジェクト名を自分で入力します。保存先はわかりやすい場所やお好きな場所にどうぞ。
Visual Studio でエラー出たら。。。
Unity画面で上タブにある「Unity」の「Preference」をクリック
次に右タブの「External Tools」を選択して「External Script Editor」を『Visual Studio』にしましょう。
- 投稿日:2020-08-01T21:05:59+09:00
UnityのBoltをさわってみたので超簡単なUIサンプルを書いてみた【初心者向け】
はじめに
無料化&公式アセット化で最近話題のBoltをさっそくいじってみました!
まだいじって一日目ですが「あーBolt完全に理解した」という心意気で、とりあえず簡単なサンプルを記事にしてみようと思います。ちなみに、がっつりしたチュートリアルはちゃんと公式にあって一通りやってみましたが「もっと気軽にBoltってみたいなー」と思ったのでこの記事を書いてみました。
『Unity入門チュートリアル 「玉転がし」(Roll-a-Ball) ビジュアルスクリプティング版』
https://learn.unity.com/project/bolt-roll-a-ball-tutorial?uv=2019.4やりたいこと
・テキストとボタンがある
・ボタンを押すとテキストが「クリックしました」に変わる前提情報
Unity2020.1.0f1
Bolt Version 1.4.12新規プロジェクトの作成
2Dプロジェクトで「BoltTest01」という名前にしました。
Boltのインストール
公式のチュートリアルがあるので、こちらでお願いします。
https://learn.unity.com/tutorial/bolt-install?uv=2019.4&projectId=5f0e8e30edbc2a1549c23a2cオブジェクトの配置と設定
以下のオブジェクトを配置、設定します。
1.Textオブジェクト「TextMessage」
ヒエラルキービューの「+」からUI>Textでテキストオブジェクトを配置し、名前を「TextMessage」とします。
RectTransformを以下のように設定します。
Textコンポーネントを以下のように設定します。
・Font Sizeを「28」
・Colorを白(FFFFFF)に設定
2.Buttonオブジェクト「ButtonMessage」
ヒエラルキービューの「+」からUI>Buttonでボタンオブジェクトを配置し、名前を「ButtonMessage」とします。
RectTransformを以下のように設定します。
3.Emptyオブジェクト「GameManager」
ヒエラルキービューの「+」からCreate Emptyでオブジェクトを配置し、名前を「GameManager」とします。
AddComponentで「Bolt>Flow Machine」を選択してFlow Machineコンポーネントを追加します。
NEWボタンを押し、ファイル名を「GameManager」にしてGameManager.assetを保存します。
このコンポーネントにBoltスクリプトを作っていきます。ButtonのOnClick()設定
ButtonMessageのButtonコンポーネントにあるOnClick()部分を設定します。
このボタンを押した際にGameManagerにOnClickイベントが飛ぶように設定します。
「+」を押すと下の画像のようになります。
この「None(Object)」にヒエラルキービューのGameManagerをドラッグ&ドロップして設定します。
「No Function」をクリックして「FlowMachine>TriggerUnityEvent」を選択します。
その下の入力欄に「OnClick」と入力します。
これで、このボタンをクリックするとGameManagerに向けてOnClickイベントを発火させることができます。GameManagerに変数の設定
GameManagerにGameObject変数「TextMessage」を設定します。
GameManagerのVariableコンポーネントにある入力欄「(New Variable Name)」に「TextMessage」と入力します。
Typeを「GameObject」に設定します。
Value欄にヒエラルキービューの「TextMessage」をドラッグ&ドロップして設定します。
Boltでテキストを変更する際に、この変数を使います。Boltスクリプティング
いよいよBoltをいじります!
GameManagerのFlow Machineコンポーネントにある「Edit Graph」を押すと、Boltスクリプトの編集画面が開きます。
初期状態で「Start」ユニットと「Update」ユニットがあります。
UE4のBlueprintではノードと言いますが、Boltではユニットと言うそうです。
これはStart()とUpdate()と同じ働きをするもの、と直感的にわかると思います。
今回は、どちらのユニットも使いません。1.UnityEventユニット
グリッド上の背景を右クリックすると下の画像のようなユニットリストが出てきます。
虫メガネの検索欄に「UnityEvent」と入力して「UnityEvent (in Events)」を選択すると、画面上にUnityEventユニットが配置されます。
配置されたユニットの入力欄に「OnClick」と設定します。これでOnClickイベント発火時にこのユニットから処理が始まります。
2.GetVariableユニット
ヒエラルキービューにあるGameManagerのVariableコンポーネントで設定した変数「TextMessage」の「=」部分をBoltスクリプト編集画面にドラッグ&ドロップすると、自動的にGetVariableユニットが配置されます。
3.Textユニット
右クリックして検索欄に「Text.text」と入力して「Text.text(set)」ユニットを配置します。
オレンジの丸の入力欄に「クリックしました」と変更後のテキストを直接入力します。
4.各ユニットの接続
3つのユニットを下の画像のように接続します。
・UnityEventでOnClickを検知したらtext(set)を処理します
・text(set)を処理する際、入力値としてTextMessageを参照します
これでBoltスクリプトは完成です!実行してみましょう!実行してみる
実行すると最初は画面に「New Text」が表示されています。
「Button」をクリックすると、テキストが「クリックしました」に変わります。
追記:こんな方法もありました
「OnButtonClick」ユニットを使えば、OnClickイベントを設定しなくても同じことができました。
・GameManagerにGameObject変数「ButtonMassage」を宣言する
・ButtonMassageを入力値にOnButtonClickユニットをText.text(set)ユニットに接続する
後から追記しておいてなんですが、個人的にはこちらの方がわかりやすいかな、と…(お好みでどうぞ)おわりに
主に自分用に書いた記事ですが、参考になれば幸いです。
- 投稿日:2020-08-01T20:59:14+09:00
【Unity】Macでバージョン管理ができる? SnailSVNを解説
これまでWindowsで利用していたリポジトリをMacで利用する必要が出てきたので
今回はMacでSubversion利用するためのソフトの紹介や設定方法
Windowsリポジトリをチェックアウトするところまでを解説したいと思います。本文はこちらです。
https://tedenglish.site/how-to-manage-unity-project-svnmac/
- 投稿日:2020-08-01T19:39:33+09:00
Unity(C#)でWINAPIを使う
はじめに
Windowsのデスクトップアプリで枠(BORDER)とかタイトルバーを消したいというのはよくあることですが、WINAPIのドキュメントを引用しながら方法を教えてくれるページがそんなに無いようなので作ることにしました。つまりは下記あたりを見るんですね。
https://docs.microsoft.com/en-us/windows/win32/api/winuser/そもそもQiitaでWINAPI(WindowsAPI)のタグがない? みんな使っていると思うんですが興味ないんですかね……。
だらだら説明を書くので、方法だけを知りたい人は別のページをググると良いです。背景
「NICT インターネット時刻供給サービス」を利用するUnity(2018.4.24f1)製のデスクトップ時計を作成しました。
https://github.com/hakua-doublemoon/NetClock
なんでこんなものを作ったかといえば、私の富士通製タブレット型PCの時計が半年に一回ぐらい狂いまくる時期が来るからです。(RTCとかがおかしいのかな……)
ついでについなちゃんの声で時報してもらってます。環境
項目名 値 OS Windows10 Unity 2018.4.24f1 内容
基本的な考え方
Windowsのアプリケーションですので、WindowsのAPIを使ってWindow(=アプリの表示)を変えることができるようです。
このWINAPIは基本的にC/C++のコードですが、DLLImportすることでC#でも使えます。DLLImportについては他の記事が詳しいのではと思います。(これに関してはもう調べてない。)事前: Windowの取得
BorderやTitleの表示はWindowの属性として決まっています。なのでWindowを取得し、現在の属性を取得し、必要な部分だけ変更して設定しなおします。
Window(ハンドラー)の取得: FindWindow
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-findwindowa
とにかく第二引数に指定した名前のWindow(アプリ)を見つけたい場合は第一引数をNULLにするようです。WindowController.cs[DllImport("user32.dll", EntryPoint = "FindWindow")] public static extern IntPtr FindWindow(System.String className, System.String windowName); string windowName = "net_clock"; // Unityで設定するアプリ名とそろえる。 var window = FindWindow(null, windowName);Window属性の取得: GetWindowLong
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlonga
第一引数で指定したハンドラーのWindowの属性(情報)を取得します。ただしWindowの属性は何種類もあり、内部仕様があるのか知りませんが32bitで収まっていません。そこで第二引数で取得/変更したい属性のインデックスを指定します。
(すなわち、特定の属性を変更したい場合はそれに対応したIndexで取得、設定する必要があるということです)WindowController.cs[DllImport("user32.dll")] public static extern int GetWindowLong(IntPtr hWnd, int nIndex);ほかのAPIについてはそれぞれのユースケースに沿って述べていくことにします。
枠(BORDER)とタイトルバーを消す
BORDERの属性: WS_CAPTION
BORDERの属性があるのでそれを変更します。すなわち下記です:
https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles
WS_CAPTION 0x00C00000L
The window has a title bar (includes the WS_BORDER style).WS_BORDER と WS_DLGFRAME を合わせてこれにしているコードが散見されますが、あまり意味がないようです。
上のGetWindowLongで書きましたが、Windowの属性は多数あり、取得・設定する際はそれに対応したIndexを使用する必要がありますが、どれなのかはGetWindowLongなどの説明に書いてあります。GWL_STYLE -16
Retrieves the window styles.したがって下記のようなコードで取得ができます。
WindowsController.csconst int GWL_STYLE = -16; int style = GetWindowLong(window, GWL_STYLE);属性の変更: SetWindowLong
属性の変更は、現在の属性をGetWindowLongで取得し、SetWindowLongで一部変更したものを設定しなおします。
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlonga
説明のとおり、引数はGetWindowLongと同様で、第三引数に設定する値を渡します。
WindowsController.cs[DllImport("user32.dll")] public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); style &= ~WS_CAPTION; SetWindowLong(window, GWL_STYLE, style);Window位置の変更: SetWindowPos
BORDERを消すと容易にWindowの位置を変えられなくなります。そこでSetWindowPosで適当な位置に移動させてやります。
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowpos
第二引数はWindowの順序の操作のようです。こだわりがなければHWND_TOPでよいでしょう。前面に出てきます。
第七引数は(ざっくり言うと)細かい動きを制御できるようです。特にこだわりがなければ0でよさそうです。WindowsController.cs[DllImport("user32.dll", EntryPoint = "SetWindowPos")] private static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags); const int HWND_TOP = 0; SetWindowPos(window, HWND_TOP, x, y, width, height, 0);ここまででこういう感じなります。
背景を透明にする
せっかく枠を消したら背景も消したくなるでしょう。背景の削除は下記の手順で行います。
- Layered Window属性の設定
- 背景色へのAlpha値の適用
Layered Window属性: WS_EX_LAYERED
https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles
WS_EX_LAYERED 0x00080000
The window is a layered window. This style cannot be used if the window has a class style of either CS_OWNDC or CS_CLASSDC.Layered Windowについて:
https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#layered-windowsvisual effects for a window ... wishes to use alpha blending effects. The system automatically composes and repaints layered windows and the windows of underlying applications
Alpha Blending 効果を使用したいウィンドウの視覚効果。システムは自動的にLayered Windowと下にあるアプリケーションのウィンドウを構成し再描画します。これだって感じですね。WS_EX_LAYERED属性もSetWindowLongで設定できますが、Indexは-20になります。
GWL_EXSTYLE -20
Sets a new extended window style.WindowsController.csconst int GWL_EXSTYLE = -20; const int WS_EX_LAYERED = 0x80000; int style = GetWindowLong(window, GWL_EXSTYLE); SetWindowLong(window, GWL_EXSTYLE, style | WS_EX_LAYERED);Layered Windowの属性の設定: SetLayeredWindowAttributes
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setlayeredwindowattributes
第三引数が難しくて正直に言って理解できていません。
単純に背景色を透明にするだけなら、第四引数をLWA_COLORKEYにすれば、第二引数(crKey)を透明な色としてくれます。つまりUnityの方で背景色を適当な色にして、そのカラーコードを第二引数に書けばいいです。WindowsController.cs[DllImport("user32.dll", EntryPoint = "SetLayeredWindowAttributes")] private static extern Boolean SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags); const int LWA_COLORKEY = 1; SetLayeredWindowAttributes(window, 0x00000000, 0, LWA_COLORKEY);これでこうなります。
いじょ。
この記事を書きながら改めて各APIを調査し、コードを整理できました。やはり正しい知識が妥当なコードを書く鍵だと思います。
- 投稿日:2020-08-01T19:21:43+09:00
unity standard講座 50%OFFセール中です。
私の販売している unity3Dゲームstandard講座ですが
50%OFFセールにしました。こちらはNOTE限定のセールになります。
8月いっぱいまで
https://note.com/zazizuzezo22334/n/n90340339a00c
約6時間の講座なので、ボリュームたっぷりの内容となっています。
実際にゲームを作り、それをGoogleplayに登録してリリースするところまで
解説しています。更にコードのPDFもプレゼントしています。
そのまま コピペして やることもできるので 初心者でも大丈夫ですよ。PS
新しいunity講座ですが、 多分来月にはリリースします。
こちらの講座の姉妹講座になる感じですね。
普段の情報はNOTEに書いているので
よかったらフォローしてください。
- 投稿日:2020-08-01T17:44:18+09:00
Unityでゴエモンインパクト戦を作ってみる Part1
目的
- 設計の速さやそれを実装する速さを測定したい
- 良いアウトプットがしたい
- 良いフィードバックがほしい
- 人が楽しめるゲームを作りたい
動機
- 就職活動の強みとなるように、Qiitaに定期的に投稿したい
- 昔からの夢は3Dゲーム制作(ソシャゲ開発はしていますが、このままだと一生2Dソシャゲ制作になると思った
- コンシューマ開発したい(いずれはUEに以降したい
- とある知り合い実況者が復活したので自分も負けないよう行動する
結果
- Profilerで見たところ250fps以上出ている
- 割と思い通りに形に出来た
- 合計18時間で作成できた(約:3日
2020/07/23 16:00 ~ 23:00
Terrain(仮背景いずれは出現する敵によって動かしたりする)
カメラ
弾管理
弾方向2020/07/24 14:00 ~ 19:00
弾の衝突
敵の当たり判定
敵被弾後の無敵時間
敵の喰らいアニメーション
UI(仮)
MVPに変更2020/07/24 15:00 ~ 21:00
左パンチ(ジャブ)
右パンチ(ストレート)
百烈パンチコード
※GitHubは準備中
MVPは端折っています
ゴエモンインパクト
GoemonImpact.csusing System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Serialization; using Random = UnityEngine.Random; namespace Goemon.Impacts { /// <summary> Goemon Impact </summary> public class GoemonImpact : MonoBehaviour { /// <summary> インパクトのカメラ 操縦席の目 </summary> [SerializeField] private Camera _camera = default; /// <summary> 弾やパンチを出す場所 </summary> [SerializeField] private Transform actionTransform = default; /// <summary> 左手 パンチはカメラTransformの下から出すようにした </summary> [SerializeField] private ImpactHandLeft impactHandLeft = default; /// <summary> 右手 </summary> [SerializeField] private ImpactHandRight impactHandRight = default; /// <summary> 100烈出す場所 </summary> [SerializeField] private Transform oneHandredPunchTransform = default; /// <summary> 計算用差分カメラ回転値 </summary> private Vector3 _presentCamRotation; /// <summary> カメラ回転制限のためのカメラ初期値 </summary> private Quaternion initCameraRotation; /// <summary> 初期化 </summary> private bool initialze = false; /// <summary> モデル </summary> private GoemonImpactBattleModel model = null; /// <summary> 小判生成リスト </summary> private List<ImpactKoban> kobanList = new List<ImpactKoban>(); /// <summary> 100烈パンチ生成リスト </summary> private List<GameObject> oneHandredPunchList = new List<GameObject>(); /// <summary> 100烈パンチのカウント </summary> private int punchCounter = 0; /// <summary> 初期化 </summary> public void Initialize(GoemonImpactBattleModel battleModel) { this.model = battleModel; initialze = true; initCameraRotation = _camera.transform.rotation; kobanList = new List<ImpactKoban>(); oneHandredPunchList = new List<GameObject>(); impactHandLeft.onEndPunch += LeftPunchOnEnd; impactHandRight.onEndPunch += RightPunchOnEnd; punchCounter = 0; } /// <summary> /// 更新 /// SceneManagerでUpdateする /// </summary> /// <param name="deltaTime">進んだ時間</param> public void ManualUpdate(float deltaTime) { // 初期化済みなら処理開始 if (initialze) { // 小判生成リストがあれば小判の位置によって削除処理 if (kobanList.Count > 0) { var copyKobanList = new List<ImpactKoban>(kobanList); foreach (var koban in copyKobanList) { // 小判を消す範囲 if (koban.gameObject.transform.position.x > actionTransform.position.x + 50.0f || koban.gameObject.transform.position.x < actionTransform.position.x - 50.0f || koban.gameObject.transform.position.y > actionTransform.position.y + 50.0f || koban.gameObject.transform.position.y < actionTransform.position.y - 50.0f || koban.gameObject.transform.position.z > actionTransform.position.z + 50.0f) { kobanList.Remove(koban); Destroy(koban.gameObject); } } } // 百烈パンチのオブジェクトを削除 if (oneHandredPunchList.Count > 0) { var copyPunchList = new List<GameObject>(oneHandredPunchList); foreach (var punch in copyPunchList) { // パンチオブジェクトのAnimationが終わっていたら削除 if (!punch.GetComponent<Animation>().IsPlaying("100PunchParts")) { oneHandredPunchList.Remove(punch); Destroy(punch); } } } // 百烈パンチのオブジェクト生成 if (model.IsOneHundredPunch.Value) { var resource = Resources.Load<GameObject>("Impacts/100PunchParts"); var obj = Instantiate(resource, oneHandredPunchTransform); // 百烈を出す場所をランダムで決める float positionX = Random.Range(-2.0f, 2.0f); float positionY = Random.Range(-2.0f, 2.0f); obj.transform.localPosition = new Vector3(positionX, positionY, 0.0f); oneHandredPunchList.Add(obj); punchCounter++; if (punchCounter == 100) { punchCounter = 0; model.SetOneHandredPunch(false); } } // パンチしてないならカメラが動かせる if (model.ImpactLeftPunchCoolTime.Value == 0 && model.ImpactRightPunchCoolTime.Value == 0) { // カメラ回転計算 _presentCamRotation.x = _camera.transform.eulerAngles.x; _presentCamRotation.y = _camera.transform.eulerAngles.y; float rotateX = _presentCamRotation.x; float rotateY = _presentCamRotation.y; if (Input.GetKey(KeyCode.UpArrow)) { _presentCamRotation.x = _presentCamRotation.x - ConstImapct.CAMERA_X_MOVE; } if (Input.GetKey(KeyCode.DownArrow)) { _presentCamRotation.x = _presentCamRotation.x + ConstImapct.CAMERA_X_MOVE; } if (Input.GetKey(KeyCode.LeftArrow)) { _presentCamRotation.y = _presentCamRotation.y - ConstImapct.CAMERA_Y_MOVE; } if (Input.GetKey(KeyCode.RightArrow)) { _presentCamRotation.y = _presentCamRotation.y + ConstImapct.CAMERA_Y_MOVE; } rotateX = Mathf.Clamp(Mathf.DeltaAngle(initCameraRotation.eulerAngles.x, _presentCamRotation.x), -ConstImapct.ROTATE_X_LIMIT, ConstImapct.ROTATE_X_LIMIT); rotateY = Mathf.Clamp(Mathf.DeltaAngle(initCameraRotation.eulerAngles.y, _presentCamRotation.y), -ConstImapct.ROTATE_Y_LIMIT, ConstImapct.ROTATE_Y_LIMIT); // カメラが向いている方向をUpdate _camera.transform.rotation = Quaternion.Euler(rotateX, rotateY, 0); } } } /// <summary> 小判追加 </summary> /// <param name="addKoban">追加小判数</param> public void KobanAdd(int addKoban) { model.SetKobanCount(model.KobanCount.Value + addKoban); } /// <summary> 後更新処理 </summary> private void LateUpdate() { // パンチしてないならパンチと小判が打てる if (!model.IsOneHundredPunch.Value && model.ImpactLeftPunchCoolTime.Value == 0.0f && model.ImpactRightPunchCoolTime.Value == 0.0f) { // Xボタンで弱パンチ攻撃 if (Input.GetKeyDown(KeyCode.X)) { // 敵にあたったら手を戻すフレームを数フレーム早くする impactHandLeft.Punch(); //model.SetImpactLeftPunchCoolTime(impactHandLeft.PunchCoolTime); } // Zボタンで強パンチ攻撃 if (Input.GetKeyDown(KeyCode.Z)) { // 敵にあたったら手を戻すフレームを数フレーム早くする impactHandRight.Punch(); //model.SetImpactRightPunchCoolTime(impactHandRight.PunchCoolTime); } // Cボタンで弾発射 if (Input.GetKeyDown(KeyCode.C)) { if (model.KobanCount.Value > 0) { if (kobanList.Count < ConstImapct.KOBAN_MAX) { // 弾上限値を超えていなければ弾を生成(プール処理がいいかな) var kobanResource = Resources.Load<ImpactKoban>("Impacts/ImpactKoban"); var obj = Instantiate<ImpactKoban>(kobanResource, actionTransform); // ListからRemoveしたいので弾の削除処理をImpactで登録 obj.onDestroy += KobanOnDestroy; // カメラの向いている方向に弾を飛ばす為、カメラのTransformを渡す obj.SetMoveVector3(_camera.transform); // 生成リストに追加 kobanList.Add(obj); model.SetKobanCount(model.KobanCount.Value - 1); } else { Debug.Log("打ちすぎ " + ConstImapct.KOBAN_MAX + "個まで"); } } else { Debug.Log("小判ねーぞ"); } } if (Input.GetKeyDown(KeyCode.V)) { model.SetOneHandredPunch(true); } } } // アクションに入れる処理 /// <summary> 小判を削除するための登録用関数 </summary> /// <param name="koban">小判クラス</param> private void KobanOnDestroy(ImpactKoban koban) { kobanList.Remove(koban); Destroy(koban.gameObject); } /// <summary> 左パンチ終わり </summary> private void LeftPunchOnEnd() { model.SetImpactLeftPunchCoolTime(0.0f); } /// <summary> 右パンチ終わり </summary> private void RightPunchOnEnd() { model.SetImpactRightPunchCoolTime(0.0f); } /// <summary> インパクト(GameObject)削除 </summary> private void OnDestroy() { initialze = false; _camera = null; actionTransform = null; } } }巨大ボス
GiantEnemy.csusing UnityEngine; namespace Goemon.Impacts { /// <summary> 巨大ボスクラス </summary> public class GiantEnemy : MonoBehaviour { [SerializeField] private BoxCollider boxCollider = default; [SerializeField] private Animator animator = default; private GoemonImpactBattleModel model = null; /// <summary> /// 初期化 /// </summary> /// <param name="model"></param> public void Initialize(GoemonImpactBattleModel model) { this.model = model; } /// <summary> /// 更新処理(当たり判定などの物理演算用) /// </summary> /// <param name="deltaTime">frameの時間</param> public void ManualFixedUpdate(float deltaTime) { var isInvincibilityTime = false; if (model.EnemyInvincibilityTime.Value > 0) { isInvincibilityTime = true; model.SetEnemyInvincibilityTime(model.EnemyInvincibilityTime.Value - deltaTime); if (model.EnemyInvincibilityTime.Value < 0) { model.SetEnemyInvincibilityTime(0); } } Collider[] colliders = Physics.OverlapBox(boxCollider.transform.position, boxCollider.transform.localScale); foreach (var collider in colliders) { // 小判を食らった場合 if (collider.GetComponent<ImpactKoban>() != null) { var koban = collider.GetComponent<ImpactKoban>(); Debug.Log("ログ対決!これは「敵」だよ!!!!!"); koban.onDestroy.Invoke(koban); if (!isInvincibilityTime) { Damage(koban.Damage, 1.0f); } } // ジャブを食らった場合 if (collider.GetComponentInParent<ImpactHandLeft>() != null) { var impactLeftPunch = collider.GetComponentInParent<ImpactHandLeft>(); Debug.Log("ジャブヒット!"); impactLeftPunch.PunchEnd(); if (!isInvincibilityTime) { Damage(impactLeftPunch.Damage, 0.2f); } } // ストレートを食らった場合 if (collider.GetComponentInParent<ImpactHandRight>() != null) { var impactRightPunch = collider.GetComponentInParent<ImpactHandRight>(); Debug.Log("ストレートヒット!"); impactRightPunch.PunchEnd(); if (!isInvincibilityTime) { Damage(impactRightPunch.Damage, 1.0f); } } if (collider.GetComponentInParent<ImpactOneHundredPunchParts>() != null) { var OneHundredPunch = collider.GetComponentInParent<ImpactOneHundredPunchParts>(); Debug.Log("百烈ヒット!"); OneHundredPunch.OnEnd(); if (!isInvincibilityTime) { Damage(OneHundredPunch.Damage, 0.01f); } } } } /// <summary> ダメージを受けた際の処理 </summary> /// <param name="damage">ダメージ数</param> /// <param name="invincibilityTime">ダメージを受けた後の無敵時間</param> private void Damage(int damage, float invincibilityTime) { model.SetEnemyHP(model.EnemyHp.Value - damage); if (model.EnemyHp.Value <= 0) { // 死んだら移動させる transform.position = new Vector3(0,0,-100); } else { model.SetEnemyInvincibilityTime(invincibilityTime); animator.SetTrigger("Damage"); } } } }課題
- 当たり判定を敵だけに実装しているため、オブジェクト指向にしたい
- たまに弾は当たったのに、敵が当たっていない判定になるのが面倒くさいので敵にまとめてしまった
- これだと、インパクトの喰らい判定どうするのという問題が出てくるため、いずれ変更したい
- 百烈パンチを生成でやっているので、予め用意したオブジェクトのONOFFでやりたい
- コックピットをUIから3Dモデルにしたい
- 弾を打ち出すところをカメラの下にしたい
次回
敵AIを実装してみる(ワクワク)
参考文献
3Dゲームで壁抜けしないコリジョンを実装してみた
https://qiita.com/HiShiG/items/a49ea295254318625c35【Unity】”FPSみたいなカメラの実装”
https://qiita.com/Nekomasu/items/0a6aaf1f595cf05fbd0dUnityのTerrainで大地を創る
https://qiita.com/yando/items/ef76c200bb50005170d5進行方向に弾を発射する
https://qiita.com/Eureka/items/e0ca38903d7b56a29dcc動画
https://www.youtube.com/watch?v=HeuuuUme3Jc
※がんばれゴエモンシリーズは Konami Degital Entertainment の登録商品です
- 投稿日:2020-08-01T11:52:35+09:00