20200426のUnityに関する記事は17件です。

Unityのモバイルゲーム向けクラッキングが行われるポイントを整理してみた

免責事項

この記事に記載されている内容を、実際に試して発生した損害に対していかなる責任も負いません(補償しません)。
すべて自己責任のもとで行ってください。

リリースされているアプリやゲーム、ソフトウェア利用許諾契約(EULA)やアプリケーション利用規約などでリバースエンジニアリングは禁止されています。
実際に試す場合は、自分で開発しているアプリやゲームや脆弱性確認用でリリースされているアプリやゲームを使いましょう。

勘違いして理解しており、誤ったことを記載しているところもあるかもしれません。
実際に対策を行うときは、専門家に相談してください。

はじめに

目的

Unityのモバイルゲーム向けセキュリティ関連覚書 - Qiita」の資料を全部読むのは大変です。 1
理解が進みやすいように、クラッキングが行われる目的ポイント対策を整理してみました。
なるべく一般的な名称を使っているつもりです。長くなるため細かく解説をつけていません。
必要に応じて、覚書で調べるか、ググっていただけると助かります。(各種対応方法の詳細も覚書を確認してください)

ここ間違っている、ここもう少し詳しくなどコメントもらえると助かります。

対応方法

この後に出てくる対応方法は、書かれている対応を行えば絶対に安全というものではありません。
基本、無限のコスト(人と時間)をかけられる攻撃者の方が有利です。
どこまで対応する必要があるかは、守りたい情報やアセットの重要度と、実装(対応)コストを、勘案する必要があります。

暗号化

エンコーディング(base64など)は、暗号化ではありません。
暗号化は、System.Security.CryptographyなどでAESやRAS使用したもののことを指しています。

暗号化にも強度があります。現在のPCでは暗号強度が弱いものだと解析されてしまいます。
適切な暗号強度のアルゴリズムと暗号化キーをを設定する必要があります。
(ハッシュアルゴリズムにも強度があります。適切なアルゴリズムを選択する必要があります。)

その他

Unityのモバイルゲームと銘打っていますが。
「WindowsやmacOSのPC環境」、「AndroidやiOS環境ネイティブアプリやゲーム」、「PlayStation 4、Xbox One、Switchなどコンソール機」、「Webサービスやゲーム」でもクラッキングのターゲットになり対策が必要です。

クラッキングの目的

私見になりますが。 2
ゲームをクラッキングする目的としては、「人より有利に進めたい」、「技術的興味」、「営利目的」などが考えられます。

  • 人より有利に進めたいは、「他の人に(お金を使わないで)簡単勝ちたい」、「時間短縮」、「欲しいユニットやアイテムを簡単に手に入れたい」などが考えられます。有利とは別に、元画像、3Dモデル、音楽データが欲しいなども考えられます。
  • 技術的興味は、対処のゲームがどのような構造で作られているか、セキュリティーを破りたいなど自分の欲求を満たすため。また、技術力を見せるためにクラッキングの方法やツールを公開することもあります。3
  • 営利目的は、「人より有利に進めたい」人でクラッキング能力がない人向けに、簡単に使えるクラッキングツール、改造済みのゲームアプリ、アカウントを提供して利益をえる。また、サービスを攻撃して停止に追い込み要求をだしたり。ユーザーデータなど価値あるものを手に入れて販売したりすることが考えられます。

対策が必要な条件は、上記の目的を持つ人が現れる場合に行う必要があります。
対策は守りたいポイントを明確にして、必要処置を行いましょう。
セキュリティ対策を後から入れる場合は、最初から対策よりもコストが高くなります。4

全体像

Unityのモバイルゲーム向けクラッキングが行われるポイントを整理してみた.png

大きく分けて、クライアント、ネットワーク、サーバに対するクラッキングのポイントを記載していきます。

  • クライアントは、クライアント内にある情報に対する攻撃
  • ネットワークは、クライアントとサーバ間で行う通信に対する攻撃
  • サーバは、サーバ内にある情報に対する攻撃

クライアント

スマートフォン(Android、iOS)端末内で行われる、クラッキングの内容と対策になります。

静的解析

プログラムが実行されていない状態で解析を行うことです。

パッケージ

Androidはapkファイル。iOSはipaファイル。
root(Android)、jailbreak(iOS)状態の端末から環境で取得したり、専用サイトを使用してダウンロードできる。(Androidはroot化してなくても端末から取り出せるかも)

apk、ipa共にzipファイルのため解凍するだけで中身をみることができます。
パッケージ情報の一部ファイルはプレーンテキストではなく、バイナリファイルに変換されています。
変換ツールを使ってプレーンテキストに戻すことができます。(apktoolツールなど)

改変したものをインストールするためには、apk、ipa共にパッケケージを再署名する必要があります。

実行ファイル

Windowsでいうと.exe。macだと.app(正確には実行許可ビットが立っているファイル)。

逆コンンパイル(逆アセンブル)を行われた場合に、可読性の高い状態でコードが復元されてしまいます。

  • ソースコードに実際使われないなコードやメソッドを挿入する
  • ソースコードを難読化(関数名や変数名を推測しにく名前に変えるなど)し、逆コンンパイルをされても解析しにくいようにする

セーブデータや通信時の暗号化キー、開発用サーバのエントリーポイントなどをconst、static
readonlyなどで文字列定義すると。
実行ファイル内にそのまま文字列として含まれる。逆コンパイルや「strings」コマンドなどで文字列を発見されてしまいます。

  • 不要な情報は含めないようにする
  • 暗号化や難読化する
  • 常にサーバから取得する(ローカルには保存しないようにする)

アセット

Unityだと、シーンファイル、シーンから参照しているリソースファイル、Resourcesフォルダ内のファイル、StreamingAssetsフォルダ内のファイルなどが対象になります。

シーンファイル、Prefab、画像、3Dモデル、音声データ、フォントなどが同梱されます。対処方法は「静的解析→ストレージ→アセット」の項目を参照してください。
マスターデータが含まれる場合もあります。対処方法は「静的解析→ストレージ→マスターデータ」の項目を参照してください。

ストレージ

ストレージは、ROMと表現される内蔵ストレージ。AndroidはSDカードで拡張した領域も含みます。
root(Android)、jailbreak(iOS)端末や、デバッグが許可されているアプリケーションの場合ストレージのデータにアクセスして取得できます。

AndroidのSDカードに書き込んだデータは、PCに接続してマウントしたり、SDカードリーダで簡単に読み出すことができます。

アカウント

ユーザーを識別する必要がある場合には、どのユーザーか認識するためにUUIDや専用アカウントを準備すると思います。
独自で実装する場合して、PlayerPrefs、テキストファイル、バイナリファイルに保存する場合は注意する必要があります。

プレーンテキスト、PlayerPrefs、バイナリファイルに保存すると内容がテキストエディターやバイナリエディターで読むことができます。

  • 暗号化や難読化する
  • OSのセキュアに保存する機能で保護して保存する(AndroidはAccountManager、iOSはKeychainなど)
  • idとパスワードではなく、セッショントークンを保存しておく。一定時間でセッショントークンは更新されるようにしておきクラッカーに渡っても永続的に使用できないようにする

マスターデータ

マスターデータはキャラクターデータ(HPやSTRなど)、敵キャラクターデータ、ステージ情報など、ゲーム初期や動作を設定するための情報をさしています。

敵キャラクターのHPをすべて1に変更して、バトルを有利に進めるられると困ります。

Sqliteや専用のバイナリフォーマットなどを、ストレージに保存している場合は解析が可能です。

マスターデータを取り出し。解析をしてパラメータ変更が行えます。

  • 改変されても問題のないデータのみ格納する
  • 暗号化や難読化する
  • 常にサーバから取得する(ローカルには保存しないようにする)
  • 重要な情報は、複数の場所にデータを保存しておき、比較して改変チェックを行う
  • ハッシュ値も合わせて保存しておき改変チェックを行う

開発用のデータなど、リリース版のアプリに不要な情報は含めないようにしましょう。

ユーザーデータ

ユーザーデータは、ゲームの設定(ユーザー名、BGMのボリュームなど)、フレンド情報、ゲームの中断データなどがあります。

BGMのボリュームは書き換えられても問題ありません。ですが、ゲームの中断データを改変されクリアしたことになる、勝利したことになると問題になります。

PlayerPrefs、テキストファイル、バイナリファイルでストレージに保存した場合はファイルを取り出し。
ファイルを解析をしてパラメータ変更が行えます。

  • 改変されても問題のないデータのみ格納する
  • 暗号化や難読化する
  • 常にサーバから取得する(ローカルには保存しないようにする)
  • 重要な情報は、複数の場所にデータを保存しておき、比較して改変チェックを行う
  • ハッシュ値も合わせて保存しておき改変チェックを行う

アセット

パッケージに含まれているものや、クラウドストレージ(AmazonのS3など)からダウンロードしてくるデータをさします。
UnityだとAssetbundleファイルのこと。
Assetbundleではなく、専用のフォーマットで保持する場合もあるかもしれません。

シーンファイル、Prefabなどにテキストで入力してあるセーブデータ、通信時の暗号化キー、開発用サーバのエントリーポイントなどは文字列として保存されてしまいます

  • 不要な情報は含めないようにする
  • 暗号化や難読化する
  • 常にサーバから取得する(ローカルには保存しないようにする)

画像、3Dモデルな、音声データ、フォントなどを変換してPCで閲覧できます。

  • 暗号化や難読化する
  • 専用フォーマットにして、解析しにくようにする
  • 常にサーバから取得する(ローカルには保存しないようにする)

動的解析

プログラムが実行いる状態で解析を行うことです。

攻撃方法

デバッガー

実行中のプログラムを動作を確認するための方法。
通常は開発中のアプリケーションの動作を見るために使用します。

  • 実行中のプログラムにアタッチして、実行中のプログラム動作を確認する
  • プログラムの動作からチェックロジックなどを探し出す
  • 回避ロジックを、実行ファイルにパッチを当て改造アプリを作成する
  • アプリケーションを再署名する

メモリ改変

プログラムはメモリ内にロードされて実行されます。
プログラムがロードされたメモリを書き換えて目的の状態に変更する方法です。

  • メモリ調査用のアプリを動作させて、アプリケーションを動作させる
  • HPなど変化を確認したい箇所をプレイする
  • メモリ内の変化を確認して、変更すべき場所を探す
  • メモリを書き換えるアプリケーションを使用して、HPなど減らないようにパッチを当てる

常にサーバから取得する(ローカルには保存しないようにする)

サーバーからダウンロード行い、メモリにアセットが常駐している場合はメモリをダンプしてファイルに保存する。
一般的なファイルフォーマットの場合は保存したファイルから、データを一般的なツールを使い表示できます。

暗号化して常駐しさせていれば保存しただけでは表示できません。
ですが、複合ロジックが解析された場合には、アセットのデータと合わせて元のデータを復元される可能性があります。

インジェクション 5

対象のアプリケーションに、ハッキング用のアプリケーションを同梱して実行・攻撃する方法です。

  • アプリケーションのハックするためのバイナリを付与する
  • アプリケーションを再署名する
  • 実機でアプリケーションを動かして動作や変更したいポイントを調査する
  • パッチを作り必要に応じてアプリケーションの動作を変更する

対応方法

解析をするようなアクションが行われたことを検知してアプリケーションをクラッシュさせる。

注意点としては、検出ロジックで端末やOSのバージョンによって想定した挙動と異なる場合。
普通にプレイしている場合でもクラッシュさせてしまう可能性があります。
メーカー、OS、バージョン、端末の設定、常駐するアプリをイロイロなパターン組み合わせてチェックする必要があります。

  • 端末がroot(Android)、jailbreak(iOS)されていないかチックする
    • 特徴的なファイルを検知してアプリケーションを終了させる
  • エミュレータチェック
    • 特徴的なファイルを検知してアプリケーションを終了させる
  • アプリケーションが改変されていないか
    • 署名のチェックを行う
    • ファイルのハッシュチェックを行い、ファイルが置き換えられていないか確認する
    • チェックに引っかかった場合はアプリケーションを終了する
  • デバッグモードの設定がオン(許可)になっていないかをチェックする
    • デバッグモードがオン(許可)にはアプリケーションを終了する
  • デバッガーがアタッチされていないかチックする
    • アタッチされている場合はアプリケーションを終了する
    • シンボル情報を残さないようにする
  • メモリ書き換え
    • 複数箇所に書き込みを行い定期的に比較する
    • 書き換えられて胃が場合にはアプリケーションを終了する

すべてのチェックを同じタイミングで行うと、まとめて対策コードが潰される可能性があります。
コードを分散する、チェックタイミングを分散する、複数準備して別々にチェックするなど対応を行う必要があります。

ネットワーク

スマートフォン(Android、iOS)とサーバ間の通信で行われるクラッキングの内容と対策になります。

解析

WebAPI

https(SSL/TLS)を使用して呼び出している場合は、そのままではクラッキングされることはありません。

  • 最近はAndroid/iOS共に、アプリケーションからのhttpアクセスはは許可されていない。そのため、通信内容を簡単には見れません
  • バージョンや設定になどにより通信内容を見たり、改変でき、クラッキングされるケースはある

証明書の正当性チェックをしない実装になっていると、証明書を端末にインストールすることで通信内容の読み取りおよび改変されてしまいます。
(デバッグのために証明書チェックを切った状態でリリースしてしまったなど)

証明書の正当性チェックを行なっていても、実行ファイルの証明書チェックコードを無効化されたり。
プログラム実行中にパッチを当てて無効化される可能性があります。
通信内容にチェックサムを付けてリクエスト内容が改変されていないかをサーバ側で確認する必要があります。
チェックサム自身も解析される可能性があります。
対策方法に関しては、静的解析、動的解析の項目を確認してください。

WebAPIのリクエストエラー時に表示するテキストも解析される可能性があります。
リリース時には、エラーコードのみにして詳細なエラー内容を表示するための文字列を含まないようにしましょう。
(エラーコードとエラー内容の対応表をチーム内のみ参照できるようにする)

Tcp、Udp

オリジナルデータ構造をそのまま送信している場合には暗号化されません。
データ内容を読み改変できないように暗号化する。
パケットが改変されていないかチェックサムを付与してチェックする必要があります。

暗号化キーや証明書の管理、チェックサムロジックを解析されないように注意する必要があります。
対策方法に関しては、静的解析、動的解析の項目を確認してください。

ミドルウェア

ミドルウェア(Photonモノビットエンジンなど)では通信内容を暗号化機能機能を搭載しています。

暗号化キーや証明書の管理に注意する必要があります。
対策方法に関しては、静的解析、動的解析の項目を確認してください。

生成

リプレイ

端末とゲームサーバの中間にProxyサーバを置き、WebAPIやパケット(Tcp/Udp)送信したものをもう一度送る攻撃方法です。
Proxyで一部のパラメータを変更して送信することも可能です。
例えば、スコアの項目を増やして送るなどです。

証明書の正当性チェックを行い通信内容を改変できないようにましょう。

リクエスト生成

解析した情報を元にクライアントを使用せずにゲームの情報が操作できるようになります。(ゲームの仕様に即した範囲までの操作)
WebAPIはcurl。Tcp/UdpはScapyでリクエストを生成できます。

対策

  • 暗号化キーやロジック変更する
  • 一定タイミングで、暗号化キーを切り替える
  • チェックサムのロジック変更
  • リクエストの監視をして、一定処理を繰り返している、明らかに実行できないアカウントを抽出する
    • アカウントを削除する。もしくは行動制限をする
    • F2Pタイトルだとすぐに新しいアカウントが生成できてしまう。そのため、イタチごっこにはなってしまう

BOT

解析した情報を元に、WebAPIやパケット(Tcp/Udp)のリクエスト生成する。キャラクター操作など自動でゲームが進行するように自動的に操作を生成する手法です。6
(MMOなどでは、開発時に負荷チェックやデバッグために使ったりもする)

リクエスト解析を解析するだけではなく、追加のアプリや、OSの機能、エミュレータなどで簡易操作BOTを作成することもできます。

対策

  • 暗号化キーやロジック変更する
  • 一定タイミングで、暗号化キーを切り替える
  • チェックサムのロジック変更
  • リクエストの監視をして、一定処理を繰り返している、明らかに実行できないアカウントを抽出する
    • アカウントを削除する。もしくは行動制限をする
    • F2Pタイトルだとすぐに新しいアカウントが生成できてしまう。そのため、イタチごっこにはなってしまう
関連資料

サーバ

サーバへ行われるクラッキングの内容と対策になります。

サーバはクラウドサービス(orオンプレミス)、マネージドサービス、mBaasなど使用するサービスにより対応するべき範囲が変わります。

例えば、クラウドサービスではサーバ構成、OSからアプリケーションまで自分たちで面倒見なくてはいけません。(オンプレミスだとハードウェアやハウジング環境もプラスされる)

mBaaSではサーバ上で実行するスクリプトを考慮すれば問題ありません。
(正確には、mBaasサービスを運営しているチームがサーバやOSを管理してくれているだけです。
mBaaSの運営チームのスキルレベルにより問題が発生した場合に、自分たちで使用しているmBaaSサービスの解決できない問題になる可能性はあります)

専門ではないので、よく出ているワードと資料へのリンクになります。

  • DoS攻撃:サーバリソースを枯渇させてサーバを使用できない状態にする
  • インジェクション攻撃:SQLやOSのコマンドを外部から実行さすることができてしまう問題
  • ディレクトリ・トラバーサル攻撃:Webサーバをクローリングを行い、不正にアクセスしファイルの閲覧や改ざんをする攻撃
  • ゼロデイ攻撃:サーバ、OS、ミドルウェア、アプリケーションが持つ脆弱性が発見されてから、ベンダーによりセキュリティパッチ(修正プログラム)が適用されるまでの期間を狙って実行されるサイバー攻撃
  • ブルートフォースアタック:IDとパスワードでユーザー認証する際のパスワードを手当たり次第試して、パスワードを解析しようとする手法

関連資料

その他

ハードウェア

GPS、加速度センサー、ジャイロセンサーなど位置ゲーを作成する場合に使用する。
位置情報は、GPSだけではなく、無線LANのアクセスポイント、mobile(4Gなど)での位置情報計測も含まれる。
位置情報の補正や、歩数などに加速度センサー、ジャイロセンサーを使用している。

  • エミュレータやデバッグ機能でデータを設定されていないかを確認する
    • チェックに引っかかった場合は、アプリケーションを終了させる。もしくはデータを採用しない。
  • 取得したパラメータが、理論上ありえない移動数値だった場合
    • データを採用しないようにする
    • 合わせてログを自社で管理するサーバ取得して通知するようにする
  • 怪しいユーザーがいた場合に、移動量などが可視化するツールを作成して確認できるようにする

ソーシャルハッキング

ソーシャルハッキング(そーしゃるはっきんぐ)とは - コトバンク

ユーザーIDやパスワードを盗み出すのに、技術的な手段を利用せず、直接本人の口から聞き出す、タイプ内容を盗み見る、書類やメモを入手する、といった手段を利用する行為。盗み見た上司のパスワードを使って情報を盗む組織内犯罪や、掃除夫などを装ってオフィスに侵入する手口等がある。システム管理者の技術的な対応では防ぐことが困難であり、個人個人にセキュリティー意識を徹底させることが重要である。

ソフトウェアからだけではなく、人間自身がセキュリティーホールになる場合もあります。

おすすめ資料

合わせて読むと理解が進むと思います。


  1. と書きつつ、この文章も長いので読むの大変ですが 

  2. 各種資料の受け売りも含む 

  3. 公開しなければ、クラッカーというよりハッカー? 

  4. どんなことでも、途中から想定していない対応項目を追加する場合はコストが増えると思いますが 

  5. Unity用のものもあるのかは不明 

  6. 最近だとコントローラ(ゲームパット)の入力での操作とかもありそう 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Unity初級]UIの色を変更する

概要

ボタン(UI)の背景色を変えたい時

本文

    prefab.gameObject.GetComponent<Image>().color = new Color(207f/255f, 199f/255f, 147f/255f, 255f/255f);

スクリーンショット (154).png

colorはfloat型で0f~1fの範囲で指定します。なので(設定したい色の値)f/255fで指定するとうまくいきます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity ContentSiaeFitterのHorizontalFitのモードをスクリプトから選ぶ方法

これも分かり辛かったのでメモ。

var fitter = object.GetComponent<ContentSizeFitter>();
fitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;

最後のPreferredSizeはモードの種類なので、好きなものを選んでください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# Enumの文字列変換は重い

EnumをToStringすると激重なので、事前にswitch文やarrayで文字列を定義しておくと、パフォーマンスを上げることができる

コード

[RankColumn]
public class EnumTest
{
    [Params(Weapon.WeaponType.A)]
    public Weapon.WeaponType weaponType;

    [Benchmark]
    public string Case1()
    {
        return weaponType.GetNameSwitch();
    }

    [Benchmark]
    public string Case2()
    {
        return weaponType.GetNameArray();
    }

    [Benchmark]
    public string Case3()
    {
        return weaponType.GetNameToString();
    }
}

public static class Weapon
{
    public enum WeaponType
    {
        A,
        B,
        C,
    }

    private static string[] weaponTypeArray = {
        "A",
        "B",
        "C",
    };

    public static string GetNameSwitch(this WeaponType type)
    {
        switch (type)
        {
            case WeaponType.A: return "A";
            case WeaponType.B: return "B";
            case WeaponType.C: return "C";
        }
        throw new NotImplementedException();
    }

    public static string GetNameArray(this WeaponType type)
    {
        return weaponTypeArray[(int)type];
    }

    public static string GetNameToString(this WeaponType type)
    {
        return type.ToString();
    }
}

結果

//BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362
//Intel Core i5-9600K CPU 3.70GHz(Coffee Lake), 1 CPU, 6 logical and 6 physical cores
// [Host]     : .NET Framework 4.8 (4.8.4121.0), X64 RyuJIT
//  DefaultJob : .NET Framework 4.8 (4.8.4121.0), X64 RyuJIT

//| Method | weaponType |        Mean |     Error |    StdDev | Rank |
//|------- |----------- |------------:|----------:|----------:|-----:|
//|  Case1 |          A |   1.2287 ns | 0.0263 ns | 0.0246 ns |    2 |
//|  Case2 |          A |   0.0523 ns | 0.0123 ns | 0.0115 ns |    1 |
//|  Case3 |          A | 282.2946 ns | 1.4594 ns | 1.3652 ns |    3 |
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# Stringパフォーマンス計測

よく使うintから文字列への変換Performance測定

計測コード

[MemoryDiagnoser, RankColumn]
public class ScoreBoard
{
    [Params(999999)]
    public int score;

    [Benchmark]
    public string Case1()
    {
        return "Score : " + score.ToString();
    }

    [Benchmark]
    public string Case2()
    {
        return "Score : " + score;
    }

    [Benchmark]
    public string Case3()
    {
        string scoreText = score.ToString();
        return string.Format("Score : {0}", scoreText);
    }

    [Benchmark]
    public string Case4()
    {
        return string.Format("Score : {0}", score);
    }

    [Benchmark]
    public string Case5()
    {
        return $"Score : {score}";
    }

    [Benchmark]
    public string Case6()
    {
        string scoreText = score.ToString();
        return $"Score : {scoreText}";
    }

    [Benchmark]
    public string Case7()
    {
        string scoreText = score.ToString();
        return ZString.Concat("Score : " + scoreText);
    }

    [Benchmark]
    public string Case8()
    {
        return ZString.Concat("Score : ", score);
    }

    [Benchmark]
    public string Case9()
    {
        string scoreText = score.ToString();
        return ZString.Format("Score : {0}", scoreText);
    }

    [Benchmark]
    public string Case10()
    {
        return ZString.Format("Score : {0}", score);
    }
}

結果

//BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362
//Intel Core i5-9600K CPU 3.70GHz(Coffee Lake), 1 CPU, 6 logical and 6 physical cores
// [Host]     : .NET Framework 4.8 (4.8.4121.0), X64 RyuJIT
//  DefaultJob : .NET Framework 4.8 (4.8.4121.0), X64 RyuJIT

//| Method |  score |      Mean |    Error |   StdDev | Rank |  Gen 0 | Gen 1 | Gen 2 | Allocated |
//|------- |------- |----------:|---------:|---------:|-----:|-------:|------:|------:|----------:|
//|  Case1 | 999999 |  59.98 ns | 0.670 ns | 0.627 ns |    2 | 0.0204 |     - |     - |      96 B |
//|  Case2 | 999999 |  60.23 ns | 0.331 ns | 0.310 ns |    2 | 0.0204 |     - |     - |      96 B |
//|  Case3 | 999999 | 132.33 ns | 1.099 ns | 0.975 ns |    5 | 0.0203 |     - |     - |      96 B |
//|  Case4 | 999999 | 139.31 ns | 1.503 ns | 1.406 ns |    6 | 0.0253 |     - |     - |     120 B |
//|  Case5 | 999999 | 139.09 ns | 0.712 ns | 0.666 ns |    6 | 0.0253 |     - |     - |     120 B |
//|  Case6 | 999999 |  61.18 ns | 0.730 ns | 0.683 ns |    2 | 0.0204 |     - |     - |      96 B |
//|  Case7 | 999999 | 117.34 ns | 0.965 ns | 0.903 ns |    4 | 0.0323 |     - |     - |     152 B |
//|  Case8 | 999999 |  51.19 ns | 0.522 ns | 0.488 ns |    1 | 0.0119 |     - |     - |      56 B |
//|  Case9 | 999999 | 196.12 ns | 1.129 ns | 1.056 ns |    7 | 0.0203 |     - |     - |      96 B |
//| Case10 | 999999 | 108.29 ns | 0.723 ns | 0.676 ns |    3 | 0.0118 |     - |     - |      56 B |

ZStringが使えるならこれを使ったほうが一番パフォーマンスがいい。
無理なら
ToString() と +演算子の組み合わせ
ToString() と $ - 文字列補間がパフォーマンスが良い

文字列の変換数が増えるとパフォーマンスは変わってくるが...

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unityを使ってクリエィティブなゲームを作れるようになるまで。(2日目)

題名を(〇日目)のようにしました。
色んなゲームを作る予定ですので、私の成長過程を楽しんでもらえたら幸いです!:relaxed:

今日は何を作るの?

ネットサーフィンをしていると、なんとN予備校の受講が無料らしいとの事!!

image.png
引用:N予備校
  https://www.nnn.ed.nico/

中を漁ってみると、なんとunityのコースが!
早速受講してゲームを作ろう!

作成したゲームはこれ

unity_chan.gif

受講したコース通り、キャラクターの後ろに付いている赤い球を箱にぶつけるゲームを作りました。

学んだこと

  • Terrainオブジェクトを使用して、簡単に地形が作れる。
  • Spring jointでキャラクターと玉の位置関係、バネの力で制御することができる。
  • scriptの中のpublicで定義された変数・クラスは、GUIから代入することができる。
  • ↑の仕組みであるため、script内にconstructorは持たない。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity + AdMob + CocoaPods + iOS】エラーなくビルドを通すための最適解【Androidにも応用できる】

こんにちは!
ねこじょーかー(@nekojoker1234)と申します。

みなさんは、UnityEditor 上だけでなく、実機で動作確認したことはありますか?

実機で動作確認をするには「ビルド」が必要になるのですが、これがまた本当にやっかいです。
何がやっかいなのかと言うと、とにかくエラーが出ることが多く、そしてそのエラー解決も一筋縄ではいきません。

私が遭遇したやっかいなエラーたちは以下の通り。

  • GoogleMobileAds/GoogleMobileAds.h' file not found
  • Undefined symbols for architecture arm64
  • Undefined symbols for architecture armv7
  • linker command failed with exit code 1

エラー内容で調べるといろいろと情報は出てくるのですが、Unity のバージョンが新しいせいか、どれも解決にはいたりませんでした。

もうダメかな...と諦めかけていたとき、ついに解決方法を見つけ、無事にエラーなくビルドすることができるようになりました。

私は解決に数日間かかりハゲそうになったので、他の人がハゲてしまわないよう、この記事に残しておきます。

ということで、さっそく見ていきましょう。

エラーなくビルドを通すための最適解

External Dependency Manager for Unity のインポート

まずは、External Dependency Manager for Unityをダウンロードしてください。

これが今回の肝となるプラグインで、簡単な記述をするだけで、iOS や Android でやっかいな依存関係のエラーを解決してくれるという優れものです。

Google が出しているプラグインなので、安心ですね。

この中にある、external-dependency-manager-latest.unitypackageをインポートしていきます。

スクリーンショット 2020-04-26 16.23.35.png

こんな画面が出てくるので、そのままインポートしましょう。

スクリーンショット 2020-04-26 16.29.38.png

Google Mobile Ads Unity Plugin をすでに入れている場合は、一部更新マークになります。

すると、インポート中にできたゴミファイルを削除していいか聞かれるので、Apply で削除しておきましょう。

スクリーンショット 2020-04-26 16.32.48.png

ここから少し時間が経つと、統計情報の取得をしてもいいか聞かれます。

スクリーンショット 2020-04-26 16.35.16.png

どちらでもいいですが、私は No を選択しました。

あと2画面出てくるのですが、両方とも右下のボタンを押せばOKです。

「Add Selected Registries」を選択

スクリーンショット 2020-04-26 16.38.06.png

「Apply」を選択

スクリーンショット 2020-04-26 16.39.42.png

これでインポートは完了です。

Dependencies.xml の準備

続いて、SampleDependencies.xml をダウンロードしてきます。

これをプロジェクトのフォルダに置くのですが、2つルールがあるので注意してください。

  • MyPlugin/Editorの直下に置く
  • Editor の上位階層のフォルダ名と、Dependencies の前につけた文字列を一致させる。(今回の例だとMyPluginDependencies.xml

たとえば、上位階層のフォルダが「DownhillRush」だとすると、以下の構造になっていればOKです。

スクリーンショット 2020-04-26 17.10.52.png

次に、XML ファイルの中身を修正していきます。

ファイルの一番下を見ると、以下の記載があります。

<iosPod name="Google-Mobile-Ads-SDK" path="../google-mobile-ads-sdk" version="~> 7.0" bitcodeEnabled="true"
        minTargetSdk="6.0">
  <!-- Set of source URIs to search for this Cocoapod spec.
       By default Cocoapods will attempt to fetch the pod specs from:
       * $HOME/.cocoapods/repos
       * https://github.com/CocoaPods/Specs
       -->
  <sources>
    <source>https://github.com/CocoaPods/Specs</source>
  </sources>
</iosPod>

今回は、Google-Mobile-Ads-SDKだけあればいいので、ここを少し修正します。

<iosPod name="Google-Mobile-Ads-SDK" version="~> 7.0" bitcodeEnabled="false" minTargetSdk="6.0">

bitcodeEnabledは色んなサイトで「false にしとけ」と書かれていたので、とりあえず false にしておきます。
pathはローカルの CocoaPod のパスらしいのですが、よくわからないので消しました。

また、ビルド時に「cocoapods.mycompany.com/Specs が見つからない」とエラーになるので、以下のsourcesタグはコメントアウトしておきましょう。

修正前

<!-- Global set of sources to search for Cocoapods.
     These sources will be searched for all Cocoapods specified by
     iosPod. -->
<sources>
  <source>https://cocoapods.mycompany.com/Specs</source>
</sources>

修正後

<!-- Global set of sources to search for Cocoapods.
     These sources will be searched for all Cocoapods specified by
     iosPod. -->
<!--<sources>
  <source>https://cocoapods.mycompany.com/Specs</source>
</sources>-->

これで XML の準備は完了です。

iOS Resolver の設定変更

Assets > External Dependency Manager > iOS Resolver > Settings を開きます。

スクリーンショット 2020-04-26 16.59.27.png

Cocoapods Integration を、Xcode Project - Add Cocoapods to the Xcode projectに変更します。

スクリーンショット 2020-04-26 17.02.29.png

xcworkspace のファイルを作らずに、Xcode のプロジェクトファイルの中に CocoaPods の設定を含めるようにする、という変更です。

これですべての準備が整いました。

ビルド

あとはビルドをすれば、エラーなくビルドができるはずです。

私はこの方法にたどり着くまでに、35回もビルドを失敗してしまいました...

スクリーンショット 2020-04-26 13.18.17.png

今回は iOS で試しましたが、Android でも専用の手順の通りやることで、ビルドが通るようになると思います。

みなさんがこの記事を参考に、一発でビルドが通れば嬉しいです。

おまけ

PlayFab に関するブログも書いているので、よければ合わせてご覧ください。
https://playfab-master.com/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】UIの透過をスクリプトからまとめて調整するCanvasGroup

環境

Unity 2019.3.7f1

はじめに

ボタンの透過率を変更しても
ボタンに付けているテキストの透過率は一緒に変化しません。

まとめて透過率を変更したいときはCanvasGroupが便利です。

手順

1.透過値を変更したいUIを適当オブジェクトにまとめる
2.まとめたオブジェクトにCanvasGroupコンポーネントを追加
3.スクリプトからCanvasGroupコンポーネントを取得しアルファ値を変更する
 CanvasGroup型の変数.alpha=0f~1f;

具体例

3つのボタンの透過値を実行後50%に変更していきます。

1.ボタンを3つ作成
image.png
 
2.CanvasにCanvasGroupコンポーネントを追加
image.png

CanvasGroupコンポーネントが追加されました。
image.png

3.スクリプト作成
スクリプトを新規作成します。スクリプト名はCanvasTestとしました。

CanvasTest
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;//UIを使うとき追加

public class CanvasTest : MonoBehaviour
{
    [SerializeField] private CanvasGroup a;//CanvasGroup型の変数aを宣言 あとでCanvasGroupをアタッチする

    void Start()
    {
        a.alpha = 0.5f;//変数aのalpha値を変更
    }
}

4.空のゲームオブジェクト作成&スクリプトをアタッチ
image.png

5.スクリプトにCanvasGroupをアタッチ
Canvasを空のオブジェクトのスクリプトの変数a欄にアタッチ
image.png

 
CanvasGroupの場所を覚えさせることができました。
image.png

6.実行結果
透過率がまとめて変わりました~。
image.png

おわりに

個別の透過率変更とはサヨナラです♪

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】否定演算子の使い方

環境

Unity 2019.3.7f1

はじめに

否定演算子の使い方がわからなくなるのでメモ

使い方

使い方は2種類
・if文で使用
 if(変数 != 数値)
 {
  処理内容
 }

・trueとfalseを逆にする
 変数 = !変数;

具体例

・if文で使用

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

public class test : MonoBehaviour
{
    int a;

    void Start()
    {
        a = 2;

        //aが1ではないなら処理
        if(a != 1)
        {
            Debug.Log("aは1じゃないよ");//コンソールに表示
        }       
    }
}

実行結果
image.png

 
・trueとfalseを逆にする

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

public class test : MonoBehaviour
{   
    bool a;

    void Start()
    {
        a = true;

        a = !a;
        Debug.Log("a=" + a);
        a = !a;
        Debug.Log("a=" + a);
        a = !a;
        Debug.Log("a=" + a);
        a = !a;
        Debug.Log("a=" + a);        
    }
}

実行結果
image.png

おわりに

なにかと使える否定演算子

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Azure] Microsoft Azure で出来ること

はじめに

さて、皆さんは Microsoft Azure をご存知でしょうか?
恐らく「Azure って、クラウドサーバーでしょ?」って感じで認識されていると思いますが、実は結構色々なサーバーを利用したサービスが統合されているソリューションと思っていただいたほうがいいです。

Microsoft Azure で出来ること

では、具体的に何が出来るかというと…

という感じです。
他にも色々あるのですが特に利用する頻度が高いものを列挙しています。

おわりに

今後はこの辺りの紹介や説明を公開していきたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】 Transform.SetParentを使わずに子オブジェクトの相対位置を追従させたい

何がしたいのか

Transform.SetParentを使わずに、衝突したオブジェクトとの相対位置を一致させたい。

rotation.gif
(こういうの。CubeとSphereはそれぞれ独立したオブジェクトだが、相対値を維持している。)

実装例

using UnityEngine;

public class AttachSample : MonoBehaviour
{
    /// <summary>
    /// 追従する対象
    /// </summary>
    private Transform _target;

    /// <summary>
    /// 相対位置
    /// </summary>
    private Vector3 _deltaPosition;

    /// <summary>
    /// 相対角度
    /// </summary>
    private Quaternion _deltaRotation;

    private void Update()
    {
        if (_target != null)
        {
            // 相対ベクトルにぶつかったときの角度の逆を反映してから、
            // 現在の角度を反映させる
            transform.position = _target.position + _target.rotation * _deltaRotation * _deltaPosition;
        }
    }

    /// <summary>
    /// 衝突したらくっつく
    /// </summary>
    /// <param name="other"></param>
    private void OnCollisionEnter(Collision other)
    {
        _target = other.transform;

        // 衝突したワールド座標
        var cp = other.contacts[0];

        // 対象の中心部から衝突地点への相対ベクトル
        _deltaPosition = cp.point - _target.position;

        // 対象の現在角度の「逆」
        _deltaRotation = Quaternion.Inverse(_target.rotation);
    }
}

備考

  • 追従する側の回転角は操作していません(位置のみ追従)
  • 対象のScaleが変化したことを考慮していません
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Azure DevOps] Unity Tools for Azure DevOps の使い方

はじめに

Unity Tools for Azure DevOps とは Azure DevOps の Pipelines で利用可能な Unity のビルドタスクです。

ただ、注意点としては、まだ結構内容に変更が多いです。
執筆時点でのバージョンは V3 ですが、V3 と言いながら違う動作をする V3 がデプロイされています。
また、Github で公開されているソースコードと実際にタスクを利用した時の挙動が違います。
この記事は Github も参照してますが、実際に Pipelines で実行してみた結果を記載しております。
とはいえ、この投稿以降でも更新されている可能性はありますのでご注意を…。

Azure DevOps Marketplace:
https://marketplace.visualstudio.com/items?itemName=DinomiteStudios.64e90d50-a9c0-11e8-a356-d3eab7857116
Github:
https://github.com/Dinomite-Studios/unity-azure-pipelines-tasks

image.png

タスク一覧

Unity Tools for Azure DevOps には以下のタスクが実装されています。

  • UnityGetProjectVersion
  • UnityActivateLicense
  • UnityBuild
  • UnityCMD
  • UnityTest

UnityGetProjectVersion

このタスクの利用目的はプロジェクトで利用している Unity のバージョンと同じバージョンをインストールするためです。

これは指定された Unity プロジェクトで利用している Unity のバージョンを ProjectVersion.txt から読み込んで返します。
成功すると projectVersion にバージョン番号が設定されます。

引数 省略 説明
projectPath Unity プロジェクトへのパス

Unity プロジェクトがリポジトリ直下にある場合は projectPath には $(Build.SourcesDirectory) を渡しておけば問題ないです。

戻値 説明
projectVersion Unity のバージョン番号

Unity プロジェクトに設定されているバージョン番号が返ります。

- task: UnityGetProjectVersionTask@1
  name: UnityGetProjectVersion
  displayName: 'Getting a version of Unity'
  inputs:
    unityProjectPath: '$(Unity.ProjectPath)'

UnityActiveteLicense

このタスクの利用目的は Unity のライセンスをアクティベートするためです。
また、ポスト処理が入っており、ジョブ終了時にそのライセンスをリリースも実行してくれます。

Microsoft-hosted で実行する場合、毎度新規で Unity をインストールしますし、ライセンスが毎度がリリースされますので、この処理が必要になります。

引数 省略 説明
username × ユーザー名
password × パスワード
serial × シリアル番号
unityEditorsPathMode Unity エディタのインストールディレクトリモードを選択。省略された場合は unityHub が設定される。

unityHub(デフォルト):Unity Hub のデフォルトのパスを利用

environmentVariable:環境変数 $UNITYHUB_EDITORS_FOLDER_LOCATION に指定されたパスを利用

specify:customUnityEditorsPath に指定されたパスを利用
customUnityEditorsPath Unity エディタへのパスを絶対パスで指定。unityEditorsPathMode に specify が指定された場合に有効。省略した場合はカレントディレクトリが使用される。
unityProjectPath Unity のプロジェクトが配置されているパス。省略された場合はカレントディレクトリが設定される。
戻値 説明
logsOutputPath ログファイルパス
- task: UnityActivateLicenseTask@1
  displayName: 'Unity License Activations.'
  condition: succeeded()
  inputs:
    username: $(Unity.UserName)
    password: $(Unity.Password)
    serial: $(Unity.SerialKey)
    unityEditorsPathMode: 'unityHub'
    unityProjectPath: '$(Unity.ProjectPath)'

UnityBuild

このタスクは Unity プロジェクトをビルドするために利用します。

Unity を利用した場合に、バッチビルドで様々な処理をさせずに単にビルドしたいというだけであれば、バッチファイルも自動生成してくれますので非常に便利に使えます。

引数 省略 説明
additionalCmdArgs 追加でコマンドラインに渡したい引数がある場合に、この引数で指定する。自前のバッチビルドファイルを指定する時に -quit とかに利用できる。
buildScriptType ビルドに利用するスクリプトの指定。

default:ビルドスクリプトは自動生成されたものを利用する。

inline:inlineBulidScript に記述されたスクリプトを利用する。

existing(デフォルト):
inlineBuildScript buildScriptType に 'inline' が指定された場合に executeMethod で実行されるスクリプトを指定する。
buildTarget × ビルドするターゲット名を指定する。指定方法は『Unity のコマンドライン』の『-buildTarget』を参照。
unityEditorsPathMode UnityActiveteLicense と同じ。
customUnityEditorsPath UnityActiveteLicense と同じ。
unityProjectPath UnityActiveteLicense と同じ。
outputFileName 出力するファイル名。省略された場合は 'drop' という名前に設定される。
outputPath 出力するディレクトリパス。省略された場合はカレントディレクトリが設定される。
戻値 説明
logsOutputPath UnityActiveteLicense と同じ。
- task: UnityBuildTask@3
  displayName: 'Building.'
  condition: succeeded()
  inputs:
    unityEditorsPathMode: 'unityHub'
    buildTarget: '$(Unity.TargetBuild)'
    unityProjectPath: '$(Unity.ProjectPath)'
    outputPath: '$(Build.BinariesDirectory)'
    outputFileName: 'Build'

UnityCMD

このタスクは、ビルドまで大層な事をしたいわけではなく、バッチ処理をしたいというときには手軽に使えるタスクです。
UnityBuild の細かい設定を全部自前でやるというだけのタスクです。

引数 省略 説明
cmdArgs 追加でコマンドラインに渡したい引数がある場合に、この引数で指定する。自前のバッチビルドファイルを指定する時に -quit とかに利用できる。
buildTarget × UnityBuild と同じ。
unityEditorsPathMode UnityActiveteLicense と同じ。
customUnityEditorsPath UnityActiveteLicense と同じ。
unityProjectPath UnityActiveteLicense と同じ。

UnityTest

(省略)

おわりに

正直言って、結構ソースコードの内容を読まないとまともにパラメータの指定方法も分からなかったりで苦労しましたが、Unity と Pipelines で利用するには重宝するタスクに違いありません。

どちらにせよ、直接 YAML を編集する上ではこの辺りのタスクは覚えておいて損はしませんので!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity コンポーネントの有無を調べる方法

ちょっと引っかかったのでメモ。

var component = object.GetComponent<Image>();

if(component == null)
{
   //メソッド
}

コンポーネントが無い場合にコンポーネントを追加する方法。

var component = object.GetComponent<Image>();

if(component == null)
{
   object.AddComponent<Image>();
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】シーンの事前ロード

環境

Unity 2019.3.7f1

はじめに

移動先のシーンが重くて遷移まで間が開いてしまう問題を解決するべく
シーンの事前ロードについて調べました。

方法

事前ロードし、ボタンを押したらシーン移動するコード

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;//追加

public class SceneTest : MonoBehaviour

{
    AsyncOperation a;//AsyncOperation型の変数aを宣言
    void Start()
    {
        //SceneManager.LoadSceneAsync("GameScene")の返り値(型はAsyncOperation)を変数aに代入
        a = SceneManager.LoadSceneAsync("GameScene");

        //AsyncOperationの中の変数allowSceneActivationをfalseにする
        //これがtrueになるとシーン移動する
        a.allowSceneActivation = false;
    }

    //ボタンに割り当て
    public void Change_Scene_button()
    {       
        //trueにしてシーン移動
         a.allowSceneActivation = true;
    }
}

解説

SceneManager.LoadSceneAsync("シーン名");のみを使ってシーン移動する場合、
下図のことが処理されています。
image.png

 
allowSceneActivationの情報を下図のように一旦falseにしておくことで
シーン読み込み後のシーン移動をストップします。
image.png

おわりに

ちょっとややこしいですが、定型文として覚えておく程度で良いと思います。
動けばOKの精神♪

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UnityのPackageManagerに自分のパッケージを入れるまで

概要

最近、自作のEditor拡張等を入れてAssetフォルダに増えるのが厳しい感じがしたので、Unityの機能であるPackageManagerに登録することにしました。その備忘録になります。
PCに直接npmやらVerdaccio入れるのはつらい気持ちがあるので、Dockerなるものを使います。

材料

※私の環境はWindows10 Homeのため
* Docker Toolbox - v19.03.1

手順

1. 下準備

上記のDocker Toolboxはあらかじめインストールしておきます。
参考 - Windows環境にDocker Toolboxをインストールする

2. docker-compose.ymlを作る

docker composeってなんぞや?

Compose とは、複数のコンテナを使う Docker アプリケーションを、定義・実行するツールです。Compose はアプリケーションのサービスの設定に、Compose ファイルを使います。そして、コマンドを1つ実行するだけで、設定した全てのサービスを作成・起動します。 - Docker ドキュメント日本語化プロジェクトより

その複数のコンテナを使う Docker アプリケーションを、定義するためのファイルがdocker-compose.ymlとのことです。
まずは今回のプロジェクト用のフォルダを作成し、その中にdocker-compose.ymlファイルを配置します。
フォルダ構成はこんな感じ。(Cドライブのどこかに置くこと)

- LocalNpmPackageServer
  ├ verdaccio
  │ ├ config
  │ │ └ config.yml
  │ ├ plugins
  │ └ storage
  └ docker-compose.yml

中身は下記

docker-compose.yml
version: '3.1'

services:
  verdaccio:
    image: verdaccio/verdaccio
    container_name: "verdaccio"
    networks:
      - node-network
    environment:
      - VERDACCIO_PORT=4873
    ports:
      - "4873:4873"
    volumes:
      - "./verdaccio/storage:/verdaccio/storage"
      - "./verdaccio/config:/verdaccio/conf"
      - "./verdaccio/plugins:/verdaccio/plugins"  
networks:
  node-network:
    driver: bridge

今回はUnityのPackageManagerのレジストリサーバーとして、Verdaccioの構成を定義しています。

3. config.yamlを作る

Verdaccioのコンフィグファイルを作成します。docker-compose.ymlのvolumesに定義されている ./verdaccio/configに配置します。
中身は下記。

config.yml
storage: ../storage
auth:
  htpasswd:
    file: ./htpasswd
uplinks:
  npmjs:
    url: https://registry.npmjs.org/
packages:
  '**':
    access: $all
    publish: $authenticated
    proxy: npmjs
logs:
  - {type: stdout, format: pretty, level: http}

ここで定義されているstoregeの位置はdocker-compose.ymlのvolumesに定義した./verdaccio/storageの位置です。

4.起動する

Docker Toolboxを入れたタイミングでDocker Quickstart Terminalが追加されているはずなので、起動します。
くじらのAAの黒い画面が出てきたら、docker is configured to use the default machine with IP なんとかと、IPアドレスが出ているので覚えておきましょう。
そしてdocker-compose.ymlを置いたプロジェクトフォルダへ移動します。

コマンド
cd "プロジェクトフォルダのパス"

移動したらDockerを起動するためのコマンドを入れます。

コマンド
# デーモン状態で起動する場合はこっち(このターミナルを閉じても動いてる)
docker-compose up -d

# 普通に動かす場合はこっち
docker-compose up

http://先ほど出ていたIPアドレス:4873をブラウザに入れて、Verdaccioの画面が表示されたらこれで環境は作成完了です。

5. パッケージを作成する

参考 - UNITYPACKAGEMANAGERのレジストリサーバーを立てる話

Unityでの作業になります。

  • パッケージ構成について

個人的にはこのようなフォルダ構成にしています。

- OriginalPackage
  │ ├ Runtime
  │ │ └ OriginalPackage.Runtime.asmdef
  │ ├ Editor
  │ │ └ OriginalPackage.Editor.asmdef
  └ package.json

この時に注意するのはEditorのAssembly DefinitionのPlatformsの設定で、Any Platformのチェックを外し、Editorのみにしておきます。
また、後ほどPackageManagerからインストールする際に既に存在しているとインストールできないため、同じプロジェクトに入れる場合は最初の作成が終わったら登録する前に別の(プロジェクト外の)場所に移しておきましょう。

  • package.jsonについて

中身は下記

package.json
{
  "name": "jp.co.mypackage.originalpackage",
  "displayName": "Original Package",
  "version": "1.0.0",
  "unity": "2018.1",
  "description": "My original package.",
  "keywords": [
    "editor"
  ],
  "category": "",
  "relatedPackages": {},
  "dependencies": {},
  "repository": {},
  "author": {
    "name": "My Name",
    "url": ""
  },
  "license": {
    "type": "MIT",
    "url": "https://monry.mit-license.org"
  }
}

このうち、上から5項目(name・displayName・version・unity・description)は必須です。
上記の必須でない項目の一部はVerdaccioで表示されたりします。

6.パッケージを登録する

またDocker Quickstart Terminalに戻ります。

まずユーザー登録をします。

コマンド
npm adduser --registry  http://4で出てたIP:4873

ユーザー名、パスワード、メアドを登録します。
で、ログイン。

コマンド
npm login --registry  http://4で出てたIP:4873

で、登録。

コマンド
cd "OriginalPackageのフォルダパス"
npm publish --registry  http://4で出てた:4873

これで登録完了です。
ちなみに上記のコマンドたちは、立ち上げたVerdaccioのページに書いてあるのでコピペすると良いです。

この状態でVerdaccioのページを読み込み直すとパッケージが登録されているのが確認できると思います。

7.UnityのPackageManagerからインストールする

最後です。Unityのプロジェクトフォルダ/Package/manifest.jsonを開きます。
そこに下記のようにscopedRegistriesを追加します。(dependencies以下は元からあるはずです)

manifest.json
{
  "scopedRegistries": [
    {
      "name": "my-package-name",
      "url": "http://4で出てたIP:4873/",
      "scopes": [
        "jp.co.mypackage"
      ]
    }
  ],
  "dependencies": {
    "com.unity.collab-proxy": "1.2.16",
    "com.unity.ide.rider": "1.1.4",
    "com.unity.ide.vscode": "1.1.4",
    "com.unity.test-framework": "1.1.11",
    "com.unity.textmeshpro": "2.0.1",
    "com.unity.timeline": "1.2.12"
  }
}

これでUnityに戻りPackageManagerを見ると出ていると思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】繰り返しアニメーションさせるだけのコンポーネント

経緯

Unityで2Dゲームを作る際、繰り返しアニメーションをするだけのオブジェクトや、アニメーションが終わったら消滅するだけのオブジェクトを配置する事はよくありますが、一つひとつUnity標準のAnimatorControllerを使っていてはキリがありません。そこで、繰り返しアニメーションさせるだけのコンポーネントを実装しました。

実装

「SpriteAnimation」というスクリプトを作成し、下記をコピペして使用できます。

SpriteAnimation.cs
using System.Collections.Generic;
using UnityEngine;
using System;

public class SpriteAnimation : MonoBehaviour
{
    [SerializeField]
    SpriteRenderer spriteRenderer;
    [SerializeField]
    List<Sprite> sprites;
    [SerializeField]
    bool loop, destroy;
    [SerializeField]
    float second;

    int count;
    float time;

    void Start()
    {
        spriteRenderer.sprite = sprites[0];
    }

    void Update()
    {
        time += Time.deltaTime;
        if (time > second)
        {
            time -= second;
            count++;
            spriteRenderer.sprite = sprites[count];

            if (count < sprites.Count - 1) return;
            if (loop) count = -1;
            else if (destroy) Destroy(gameObject);
            else disposable.Dispose();
        }
    }
}

インスペクター上の項目説明

  • SpriteRenderer : アニメーションさせるSpriteRendererをアタッチ
  • Sprites : アニメーションに使用するSpriteを順番にアタッチ
  • Loop : Loopさせる場合はチェック
  • Destroy : アニメーションの終了後に破壊するならチェック
  • Second : アニメーションの速度

UniRX版

こっちの方が低負荷

SpriteAnimation.cs
using System.Collections.Generic;
using UnityEngine;
using UniRx;
using System;

public class SpriteAnimation : MonoBehaviour
{
    [SerializeField]
    SpriteRenderer spriteRenderer;
    [SerializeField]
    List<Sprite> sprites;
    [SerializeField]
    bool loop, destroy;
    [SerializeField]
    float second;

    int count;
    IDisposable disposable;

    void Start()
    {
        spriteRenderer.sprite = sprites[0];

        disposable = Observable.Interval(TimeSpan.FromSeconds(second))
        .Subscribe(_=>
        {
            count++;
            spriteRenderer.sprite = sprites[count];

            if (count < sprites.Count - 1) return;
            if (loop) count = -1;
            else if (destroy) Destroy(gameObject);
            else disposable.Dispose();

        }).AddTo(this);
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity 2DGameKit Gunner戦

2DGameKitの導入や詳細はこちらのページが詳しいので任せる

目次

1.Gunner戦とは

1.Gunner戦とは

image.png
2DGameKitのZone5シーンでGunnerというボスが登場する。

2.主人公 Ellen

image.png
このゲームの主人公Ellenです。

3.Gunner

ライフ設計

Gunnerにはシールド(青いゲージ)があって、シールドを削り切るとライフ(赤いゲージ)にダメージを与える事が出来ます。

シールド(青いゲージ)について

image.png
GuunerShieldというGameObjectがシールドの役割を担っています。
image.png
シールドに主人公が触れたら、主人公はダメージを受ける設計になっています。
Damagerがその役割を持っています。
Damageableが主人公からダメージを受ける役割を持っています。

ライフ(赤いゲージ)について

image.png
Damageableが主人公からダメージを受ける役割を持っています。

Components

Gunner戦において必要となるComponentについて

Damager

image.png
Damagerはその名の通り、ダメージを与える役割をもったComponentです。

Damageable

image.png
Damagerがダメージを与える役割をもったComponentに対して、Damageableはダメージを受け取る役割をもったComponentです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む