Twitter Kit for UnityでTwitter認証を行う - UnityでFirebaseを使ったオンラインランキングシステムを作るvol1

はじめに

この記事はシリーズ物です。
シリーズの記事は以下を参照ください

www.project-unknown.jp www.project-unknown.jp www.project-unknown.jp

今日はTwitter Kit for UnityでTwitter認証の記事を書きます。

アセットはこれ

最初の背景はどちらかと言うと愚痴りたかったので書いているだけで、中身がありません。急いでいる方は「アセットのimport」から読み進めてください

背景

FirebaseとUnityとを連携させたかったので、公式を見ながら始めようとしたのですが、、

f:id:project-unknown:20170827033146p:plain

思いっきりNativeコードでOAuth認証まで終わらせて来いやって書いてあり衝撃を受け、UnityでのTwitter認証について調べた次第です。

ちなみに、その時の愚痴がこれ

また、何故Twitter Kit for Unityなのかと言うと、よく調べると至るサイトがFabricでやれって書いてあったのですが、

FabricってGoogle(Firebase)に吸収されるから今後どうなるかわからんやん
というか、FabricのTwitterKitが見当たらん

その時の愚痴がこれ

んで、途方に暮れてNativeで書こうか悩んだんですが、折角このAssetがあるなら使ってみようと思った次第です。

なので、最終的にTwitter Kit for UnityでTwitter認証を行い、そこから取得したToken情報を使ってFirebaseまでを目的にしており、
Twitter Kit for Unityの使い方にフォーカスしてます。
(Firebaseだけじゃなくて、このAssetの使い方で戸惑っている人も活用いただけると思い

と言うか、他のサイトで記載されているのが、PINコード認証の古い認証のやり方かFabricでの認証くらいしか見当たらなくて私自身が本当に困ったのが、この記事を書く一番の要因だったのですがね(;・∀・)

アセットのimport

まずは、アセットをダウンロードしましょう。
上記のTwitter Kit for UnityのAsset Storeでimportするか、公式のInstallationからの直リンでpackageを落としてUnityにimportします。
公式からDLする場合は、以下のキャプチャを参考にしてください。

f:id:project-unknown:20170827034004p:plain

Twitter Developerの設定

Twitter API初期設定

https://dev.twitter.comでTwitterの設定を行います。
アカウントを取得していないのであれば、アカウント取得から実施してください。

画面上部のMy Appから、以下の画面が開くのでCreate New Appをクリックします。
キャプチャでは、Firebase用のSampleを造りたかったので、「UnityFirebaseSample」としてます。

f:id:project-unknown:20170827034326p:plain

次が、登録の入力例です。

f:id:project-unknown:20170827034442p:plain

コールバックURLは、Firebaseの場合は指定されています。
これについては、Firebaseの記事を直ぐに執筆予定ですので、そこで説明します。

Twitter APIのPermission設定

ここまで、来ると、以下の画面になっていると思いますので、Permissionをクリックします。

f:id:project-unknown:20170827034621p:plain

特に認証をするだけなら、Read onlyだけで良いのですが、Tweetとかもさせたい場合は、 Read and Writeとかに設定しておきます。

f:id:project-unknown:20170827034724p:plain

Twitter APIのSetting

次はSettingをクリックします。

f:id:project-unknown:20170827034803p:plain

公式にある通り、Call backのロックを解除しておきます。(デフォルトは解除されていると思います)

f:id:project-unknown:20170827034829p:plain

Twitter APIのKeys and Access Tokensの API Keyと API Secretを控える

以下のConsumer Key (API Key)と、Consumer Secret (API Secret)を控えます。後で使います。 f:id:project-unknown:20170827034951p:plain

ここまでで、Twitter APIの設定が完了です。
お次はUnityの設定を行っていきます。

Unityの設定。

Twitter Kitのセットアップ

冒頭で書いた通り、ここまででTwitter Kit for Unityがimportされていると思うので、その体で進めます。

まず、importが正常に完了していると、メニューにTools > Twitter Kitとあると思うので、それをクリックします。

f:id:project-unknown:20170827035202p:plain

inspectorが、以下のキャプチャの通りの画面になっていると思うので、ここに先程控えていた Consumer Keyと Consumer Secretを入力します。

f:id:project-unknown:20170827035233p:plain

TwitterInit.csをGameObjectにアタッチ

importパッケージの中に、「TwitterInit.cs」が梱包されているので、これをScene上のGameObjectにアタッチします。

f:id:project-unknown:20170827035446p:plain

これを忘れると動きませんのでご注意ください(私が、この作業をすっ飛ばしたせいで、3時間位悩みました)

コードを記載

さぁやっとここでコードの記載です。
ここでは、Gameを起動した瞬間にTwitter認証を行おうとしています。

gist.github.com

上記をTwitterKitForUnitySample.csというファイル名で保存して、適当なGameObjectにアタッチします。

注意
ファイル名を別にしたい場合は、クラス部分の

public class TwitterKitForUnitySample : MonoBehaviour {

public class 変更したいファイル名 : MonoBehaviour {

に変更してください。
ファイル名とクラス名が違う場合はアタッチができなくなるのでご注意ください。

Twitterの認証が成功した際に、

public void LoginComplete(TwitterSession session) {

上記メソッドが呼ばれ、TwitterSessionの中に、ユーザを識別する情報が入っています。
ここでは、その他のTweetや、別なサードパーティのシステム(Firebase等)の認証にAccessTokenとAccessTokenのSecretが必要になってくるので、それを抜き出しています。

Build

iOSの場合は、Twitter Kit for UnityがiOS 7.0をサポートしていないので、8とか9以上に設定しておきましょう。
(デフォルトが7になっているはずです)

f:id:project-unknown:20170827040645p:plain

実際に動かす。

すんません、認証のキャプチャ取るのを完全に忘れてました
しかも一度認証すると次は自動で認証が走るので取れませんでした(;・∀・)

XCode等でのログで以下が流れていればOKです

[Info] : Login success. アクセストークン"

さいごに

次はFirebaseと認証して、Firebase上でTwitterユーザでランキングを作るって所まで最終的に記事に書いていきたいと思います。
また、UnityのTwitter認証で調べると、大体がFabricかSocialConnectorとかが引っかかってアプリ内でTwitter認証, Tweetを実施したかった人の助けになれば幸いです。

余談

Twitter Kit for UnityをUnity Cloud buildに掛けると、以下のようなログが出ます。

[Unity] *** {GUID} replaces {GUID} at path assets/plugins/ios/twitterkit.framework/twitterkitresources.bundle/twttr-icn-tweet-place-holder-photo-error@2x.png 

(GUID}は人によって違うランダムな英数が出ると思います。
こちらですが、単純に「twttr-icn-tweet-place-holder-photo-error」のerrorがエラーが起きたかのように解釈されてエラーっぽく見えてしまっていますが、これについては問題ありません。
(私がこれに凄いハマった)

余談2

Twitter Kit for Unityは米国在住の13歳以下の子供への提供は出来ません。
(と言うかTwitterのサービス自体がCOPPA(Children's Online Privacy Protection Act)に引っかかっているので、Twitterを使ったアプリを展開する時点で13歳以下の利用が出来ないように対策を練らないといけないかと思います。)

日本在住の子供向けであれば問題はありませんが、海外展開を考えている場合は十分に注意しましょう。

詳細はTwitter Kit for Unity licenseの「7. End Users」に記載がありますので一読ください。

参考

Arbor2とUniRxで指定回数Click/Tapされたら次のStateへ遷移する機能を作る

はじめに

最近、少しずつLinqを覚え始め(C#はまだ毛が生えた程度のSkillなので)、ずっとやりたかったUniRxを使ってみました。
また、折角なので最近一番のお気に入りのAssetのArbor2と組み合わせて何か便利な挙動が作れないかしらと思い、タイトルの通りの機能を造ります。

UniRx

詳しい使い方等は、沢山のサイト様があるので、そちらにおまかせします

Arbor2

project-unknown.hatenablog.com

今回作る仕様

  • 指定回数クリックされたら次のStateに移る
  • クリックを受け付ける入力期間を指定出来るようにする

作ってみた

gist.github.com

上記をコピペするだけで動くと思います。

すると挙動追加で、「Original > Transition > MultiClick」で以下の様な画面が出ると思います。

f:id:project-unknown:20170822004744p:plain

上記のSampleだと、10回クリックされたら次のStateに移るようになります。

実行結果をキャプって見たのですが、動画でも何やっているのかわからなかったのでキャプチャは割愛。

さいごに

UniRxはまだまだ使い慣れていないけど、これまで面倒な処理を書いていたのが簡潔に掛けるのが良いですね。
特に可読性(Linqのskillは必要になってくるけど、ソースファイルの至る所まで処理を追いかけなくても良い意味で)が上がってくると思います。

Arborを使ってハマったこと - 常駐ステート

はじめに

今日はArbor 2を使っていて少しハマった所を記載。

事象と解決策

前回書いた以下の記事

project-unknown.hatenablog.com


で、扉の開閉を検知してボタンの活性/非活性を行う処理を、
最初以下のように、ボタンを押した時の処理と常駐ステートでボタンの活性/非活性を同一FSM内で定義してました。

f:id:project-unknown:20170815231353p:plain


これを実行すると、以下みたいになります。

f:id:project-unknown:20170815231111g:plain

常駐ステートの方は動くのですが、ボタンクリックの方が検知してくれていない。

原因としては、以下の公式に記載されている通り、常駐ステートの位置付けは「割り込み」である為に、
ボタンよりも常に条件が合致する常駐ステートの方が優先して処理されてしまっていたと推察しています。

arbor.caitsithware.com

この件の対応としては、GameObjectに別なArbor FSMをアタッチして、新規に作成したFSMの方に常駐ステートを置いて想定通りの動作が実現できるようになりました。
↓な感じ

f:id:project-unknown:20170815231829p:plain


今日は簡単ですが、以上で。

開発効率を上げ、手軽に拡張できるFSM「Arbor2」紹介

この記事はUnity アセット真夏のアドベントカレンダー 2017 : ATND 17日目の記事です。

はじめに

つらつらとArbor2の事例紹介の記事を書いていたら、Arbor2の作者のケットシーウェア @caitsithwareさんに、アドカレの紹介を戴いたので、勢いで参加です(∩´∀`)∩

さて、本題です。

Arbor2

Arbor2を導入する事で、FSMベースのヴィジュアルスクリプティングを実現することが出来ます。(↓な感じ

f:id:project-unknown:20170816212003p:plain
プログラムベースじゃなくて、上記みたいに状態遷移図をポチポチしていくだけである程度のゲームが作れてしまうと言う夢のような機能ですね。

(勝手ながら私が思うに)Arbor2の競合として、PlayMakerが挙げられると思うのですが、PlayMakerと比べて私が特に秀でていると思うのは以下の通りです

  • 拡張性の高さ(拡張難易度が低い)
  • サポートが日本語
  • シンプルで迷うことが殆ど無い

拡張性が高いため、簡単に機能追加していけます。
例えば↓みたいに機能をエンジニアが用意し、プランナーやデザイナーがゲームを構築すると言う形で分業もし易いです。

f:id:project-unknown:20170816194149g:plain

この記事の構成

  • まず最初に導入部分として、簡単な機能を作る所まで
  • 拡張性が高い所の紹介
  • 実際開発している最中の機能事例の紹介

上記で行こうと思います。
後、2Dベースで紹介していますが、勿論3Dでも使えますよ!

説明を文字に起こすとかなりごちゃごちゃしてますが、実際にやってみるとビックリするほど簡単ですよ!
無料試用版もあるので、是非使ってみてください。

導入

より詳しい説明はArbor2公式のチュートリアルを参照いただく事として、ここでは最初の足がかりを紹介します。

ここでは、以下のgifの用に、画面クリックで吹き出しが切り替わる単純な動作を作る所まで紹介します。 (さり気なく宣伝ですが、キャラクタは活動中のProject.Unknownのマスコットキャラの「う〜のん」を使ってます。

f:id:project-unknown:20170816165936g:plain

上記の構成は以下の4つのSpriteで構成されています。

  • 「背景」Sprite
  • 「う〜のん」Sprite
  • 「・・・」Sprite
  • 「?」Sprite

でわでわ、早速作っていきます。

AssetStoreからimport

特に何も考えず全部Projectに突っ込みます。
f:id:project-unknown:20170816163211p:plain

CommponentからArborFSMでアタッチ

ArborをアタッチしたいGameObjectのInspectorから以下の用にArborFSMをアタッチ。
f:id:project-unknown:20170816164122p:plain

次に、下の画像のOpen Editorをクリックします。
f:id:project-unknown:20170816164428p:plain

専用のEditorが表示されます。ここにステートを設定していきます。 f:id:project-unknown:20170816164506p:plain

「マウスクリックで次のステートへ」機能を作成

右クリックすると下の画像のようになるので、「ステートを作成」をクリックします。 f:id:project-unknown:20170816164617p:plain

これで空っぽのステートが作成されます。 f:id:project-unknown:20170816164644p:plain

「New State」と書いてある右側の歯車をクリックして「挙動を追加」をクリックします。
f:id:project-unknown:20170816164756p:plain

すると初期に用意された機能一覧が表示されます。
f:id:project-unknown:20170816164851p:plain

Behaviorから「Transition > Input > ButtonDownTransition」を選択。
Transitionは、次のステートに遷移させる事を指しています。今回選択した、ButtonDownTransitionは、マウス等がクリックされれば次のステートに遷移します。

現段階で、Arbor Editorが以下の用になっていると思うので、

f:id:project-unknown:20170816170632p:plain

遷移させたいステートを造ります。先ほどと同じ要領で右クリック -> 「ステートを作成」をクリック。

f:id:project-unknown:20170816170728p:plain

後は、「開始ステート」のButton Down Transitionの遷移先に、追加で作ったステートを選択します。

f:id:project-unknown:20170816171015g:plain
(キャプったら荒ぶった感じになってしまった(;・∀・)

Sprite変更

ここで、吹き出しを切り替える設定を埋め込みます。
追加で作ったステートで、挙動の追加 -> 「Renderer > SetSprite」を選択。
Targetと、Spriteに入力する所があるので、今回はSprite部分に切り替えたいSpriteをセットします。

また、次にクリックされた際に、別ステートに選択させたいので、先程と同様に、ButtonDownTransitionを追加します。

f:id:project-unknown:20170816171509p:plain

仕上げ

吹き出しを元に戻すステートを追加し、全てのステートをつなぎ合わせると以下のようになります。

f:id:project-unknown:20170816171624p:plain

動作確認

f:id:project-unknown:20170816173200g:plain

見て頂ければ分かるのですが、現在どのステートに居るのか、そのステートは何回目遷移してきたのかが視覚化されており、デバッグ機能としても非常に優秀です。

拡張性

お次は、拡張性の所を少しご紹介します。
Arbor2は、最初から用意されている機能を修正する事も出来ますし、自分で挙動をホイホイと追加することもできます。

既存の機能の修正

ArborのライセンスはEULAとなるかと思うので、デフォルトにあるコードを載せておりません。一部コードの抜粋で紹介を行いますが、問題がある場合は削除するのでご連絡ください。

導入部分でも登場した、ButtonDownTransitionに、ボタンが押された際に現在どのステートなのかをLogに出力するように改修を加えてみましょう。
まず、先程の例だと、全て「New State」になっているので、Logが分からないことになるので、以下のキャプチャのようにステート名を変更します。

f:id:project-unknown:20170816190642p:plain
(それぞれ「Start」「QuestionState」「SilentState」で名前を設定)

次に、実際にコードをいじっていきます。
↓の用に「スクリプト編集」をクリックすると該当するコードを直接編集できるようになります。

f:id:project-unknown:20170816181005p:plain

Updateメソッド内のInput.GetButtonDownのif文の中に以下のコードを埋め込む。

Debug.Log("Current State : " + state.name);

「state.name」で現在のステート名を取得できます。stateについての詳細はArbor 3: FSM & BT Graph Editor: State Class Referenceを御覧ください。

これを実行すると、ログはこんな感じで出力されます。

f:id:project-unknown:20170816191002p:plain

新規に挙動を追加

次に自分の好きなようにArbor2に機能(挙動)を追加します。

ここで作るものは、SpriteRendererの色を変更する機能を追加してみます。

Project Windowで、右クリック -> Create -> Arbor -> StateBehaviour -> 言語を選択
ファイル名は「ArborChangeSpriteColor.cs」としました。

f:id:project-unknown:20170816202126p:plain

コードの中身は以下な感じです。
(Arborに関する所にコメントを入れてます。

gist.github.com

上記で保存すると、ArborFSMの挙動追加で↓みたいに今回追加した機能を追加出来ます。
f:id:project-unknown:20170816204851g:plain

でわ、早速これを導入部分で作ったものに組み込んでみます。
1秒に1度う〜のんの色が変わるようにします。

う〜のんにArborFSMをアタッチして、こんな感じでFSMを編集します。
f:id:project-unknown:20170816204239p:plain

  • TimeTransitionは指定した秒数が経過したら次のステートに遷移させる機能です。
  • Arbor Change Sprite Colorに変更したいSpriteRendererを登録し、色もセットします

実行してみます。

f:id:project-unknown:20170816204456g:plain
(う〜のんがこわい…

こんな感じで、機能を自分で埋め込んでいくと、最終的に以下のように、開発しているゲーム独自に特化した機能を揃えることができ、
コアに作り込まないと行けない所は、ゴリゴリコードを書いて、
レベルデザイン等は、Arbor上で行うと言った作業の割り振りが出来そうです。

f:id:project-unknown:20170816194149g:plain
(懺悔すると、上記はgif用にメニューを用意しただけで、中身は空っぽの機能だらけです

他の機能実例

以下に、これまでに記載したより具体的なArbor2の実例を記してますので、もし興味があればmm

project-unknown.hatenablog.com

project-unknown.hatenablog.com

まとめ

紹介のはずがダラダラと色々詰め込んでしまいました(;・∀・)
総括すると、デフォルトで用意されている機能だけでもだいぶ効率的に開発出来ますし、
かゆい所に手が届かなかったら自分でサクッと機能追加出来る意味で、おすすめなAssetですよ(∩´∀`)∩

Unity アセット真夏のアドベントカレンダー 2017 : ATND次の担当は、baba_sさんで「Hierarchy を拡張する多機能アセット「PRO Hierarchy + Memory Monitoring + Navigator for Selecting」紹介」ですね!個人的に勢いで買ったは良いけど使わずにいるAssetなので凄い楽しみです。

baba-s.hatenablog.com


参考

ArborでuGUIのボタンを活性/非活性にする

はじめに

Arbor上からuGUIのボタンのinteractableをいじりたい時のサンプルです。

凄い簡単ですが、以下のコードを「ArborButtonInteractable.cs」とかで好きな場所に保存すると、「挙動追加 > UI > Button > ボタンの活性、非活性」から使えるようになります。

gist.github.com

使い方Sample

前回の↓の記事の延長で行います。

project-unknown.hatenablog.com

やりたい事

  • 戸棚が開いていたらまたたびボタンを活性化させたい
  • 逆に戸棚が閉まったらまたたびボタンを非活性化させたい

戸棚の状態を見てState先を変えるコード

今回はArborの常駐ステートで常に監視する方針ですすめます。
まずは、以下のコードで戸棚の状態を見て、戸棚が開いていたら遷移するステート、逆にしまっていたら遷移するステートを選択できるようなコードを用意します。

gist.github.com

Arbor FSM編集

  • ArborOpenDoorTransition
  • ArborButtonInteractable

上記を、↓のように組みます。

f:id:project-unknown:20170815225646p:plain

以下が実行結果です。

f:id:project-unknown:20170815230020g:plain

(ふと思ったけど、こうも開発中の画面をポンポンだして良いものだろうか…(;・∀・)

UnityのPosition周りの共通っぽい処理

Writtern by ゆう@あんのうん

はじめに

以下のゲームジャムに参加し、その成果物をアプリ化すべく、現在リファクタリングを行っているのですが、
今後のゲームジャムであると私が楽になりそうな共通処理を抜き出しました。

project-unknown.hatenablog.com

Tranform.positionの各座標の操作は、readonlyになっている為、一度別な変数に置き換えて、編集してから再代入しないといけなく、その手間を省いたものです。
staticクラス化して外部から読めるようにしているのは、

ゲームジャムだと時間がまったくなく、クラス設計がぐちゃぐちゃになって1つのクラスで他のGameObjectをいじりまくるシーンが頻発しているので、 外部参照出来るようにしています。

※本来はPosiitonをいじる時点で専用のクラス化を行うのが設計上正しいと思ってます。

「BehaviourUtility.cs」と「NumberUtility.cs」のクラスを作成して、以下のソースコードを丸コピすれば動く(はずです。
コードより下に、軽い説明を記載していますので、一緒に参照ください。

gistが使い慣れていないせいか、space4, spaceインデント設定なのにtab化されてしまってます。申し訳orz

gist.github.com

以下、各コードを軽く紹介。

NumberUtility.cs

Utilityと書いてますが、2つしか機能がありません。

GetValueOrMaximum(float value, float limit)

いわゆる 「x %= y;」とかをするメソッドです。
ただ、私自身がコードのど真ん中に「x %= y;」とか書いてあると、ん?とコードリーディングが止まったりするので、私自身への可読性向上の為に、回りくどい書き方をしています。
使用例としては、

// GameObjectのX座標を10以上いかないようにする。
float x = gameObject.transform.position.x
x++;
NumberUtility.GetValueOrMaximum(x, 10);
GetValueOrMinimum(float value, float limit)

GetValurOrMaximumの逆です。説明は省略。

BehaviourUtility.cs

BehaviourUtility.PositionX,Y,Z

引数として渡されたGameObjectの各座標を取得します。

float posX = BehaviourUtility.PositionX(gameObject);

BehaviourUtility.PositionFrom

GameObjectの座標を引数で渡されたVectorで加算/減算します。

// gameObjectの座標を、Vector3(1,0,0)へ移動させます
BehaviourUtility.PositionFrom(ref gameObject, new Vector3(1,0,0));

BehaviourUtility.PositionFromX,Y,Z

BehaviourUtility.PositionFromの各座標のみ指定するVerです。

// gameObjectのX座標を1ずらします
BehaviourUtility.PositionFromX(ref gameObject, 1);
// gameObjectのY座標を1ずらします
BehaviourUtility.PositionFromY(ref gameObject, 1);
// gameObjectのZ座標を1ずらします
BehaviourUtility.PositionFromZ(ref gameObject, 1);

BehaviourUtility.PositionAt

GameObjectのTransform.positionを引数で渡されたVector3で置き換えます。
(単純に毎回gameObject.transform.positionと書きたくなかっただけです)

// gameObjectをVector3(1,0,0)でセットします
BehaviourUtility.PositionAt(gameObject, new Vector3(1, 0, 0));

BehaviourUtility.PositionAtX,Y,Z

BehaviourUtility.PositionAtの各座標Ver, 説明は省略

BehaviourUtility.PositionAtToMaximum, BehaviourUtility. PositionAtToMinimum

BehaviourUtility.PositionAtの最大値以上/最小値以下に移動しないように制限する機能です。

BehaviourUtility.PositionAtToMaximumX,Y,Z
// gameObjectを+1移動させます。(最大値の10を超えると、10がセットされたままになります)
BehaviourUtility.PositionAtToMaximumX(ref gameObject, 1, 10);
BehaviourUtility. PositionAtToMinimumX,Y,Z
// gameObjectを-1移動させます。(最小値の0より下回ると、10がセットされたままになります)
BehaviourUtility.PositionAtToMinimumX(ref gameObject, 1, 0);

余談 自分自身のPositionを変更する場合

今回公開したコードは、あくまで別クラスからの操作に特化させています。
自分自身のpositionをいじる場合は、上記みたいな煩雑なやり方をしなくても、親クラスに以下のようなコードを記載しておいて、PositionXとかでいじったほうが良いと思います。
(コードはWebで他の先人たちのコードを取り込んでいます。

gist.github.com

SwiftでKVO (Swift3.0)

はじめに

今日のお題はSwift(3.0)でKVO。

KeyHolderのSwift化を行っていた際に思いっきり詰まってしまったので、その備忘録です。

そもそもKVOって?

KVOは、Key-Value ObservingでObjective-Cから引き継いだ機能の1つで、
指定した変数に変化があった際に、通知を行ってくれるそこそこ便利な機能です。

KeyHolderでは、以下のキャプチャの様に、タイトル・ID・パスワードのTextFieldをKVOで監視して、それぞれが何か1文字でも入力されれば、SaveボタンをEnable処理を行うようにしています。
f:id:project-unknown:20170717015611p:plain

(これくらいの機能であれば、didSet使えばなんとでもなるのですが、ほぼ脳死状態でそのまま移植したのでKVOを引き継いでいます)

SwiftでのKVO

さて本題です。
今回はKeyHolderでやっている例を元に、別クラスの変数に変更が入った際に通知を受け取れる様な仕掛けにします。

監視対象のクラス生成

まず最初、通知対象のクラスから記載します。
上記キャプチャの例で言うと、タイトル等のそれぞれのTableViewCellの部分のクラスで、TextFieldに何かしら入力があればKVO監視対象の変数を書き換えます。
(脳死状態で持ってきたソースなので我ながら何やっているんだろ?というところはあるのですが、ご了承ください

class ChildTableViewCell: UITableViewCell {
    dynamic var kvoVal: String = ""
    
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        var textMu: NSMutableString? = NSMutableString()
        textMu?.append(textField.text!)
        
        if textMu == nil, string.characters.count <= 0 {
            textMu = nil
        } else if string.characters.count <= 0 {
            textMu?.deleteCharacters(in: range)
        } else {
            textMu?.append(string)
        }
        
        if textMu != nil {
            self.kvoVal = textMu as! String
        }        
    }
}

これで、textFieldに何かしら変更があった際に、kvoValに値が入るようになります。
ここで私が詰まった所ですが、KVO監視対象はdynamicで指定する必要があります。

dynamic var kvoVal: String = ""
dynamic

dynamicはメソッドにも変数にも適用できるdeclaration-modifierです。
dynamicはランタイムにdynamic dispatchを使うようにしているのですが、暗黙的に@objcをメソッドや変数に付与します。
ここまで掛けばおそらく推測できてくると思いますが、dynamicはSwiftではなくObjective-Cのラインタイムを利用します。
Objective-C時代のdynamicとは違う挙動を取ります。

このdynamicをガリガリ使っている所で有名所だとCoreDataがこれでしょうね。

監視側(通知を受ける)クラスの作成

監視対象のクラスが出来上がったので、監視する側のクラスを作っていきます。
やっている事は、Cell生成と同時にKVO監視を始め、値に変更があれば、専用のメソッドで通知を受信します。
また、監視はviewWillAppearのタイミングで実施し、viewWillDisappearのタイミングで監視を破棄します。
KVOの性質上、画面が破棄されるまでにKVOの監視を解いて置かないとCrashするので、このタイミングでKVO監視破棄は必須です。

class MainViewController: UIviewController {

    var kInputCellKindTitle    = "title"; 
    var titleCell: ChildTableViewCell? = nil

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        // Cell生成+監視
        self.makeCell()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        // Viewを閉じる際に監視を解除
        self.removeKVO()
    }

    /// Cellの生成
    func makeCell() {
        let titleCell: ChildTableViewCell = ChildTableViewCell()
        titleCell.addObserver(self, forKeyPath: "kvoVal", options: NSKeyValueObservingOptions.new, context: &InputCellKindTitle)
    }

    /// KVO通知を受け取った際のメソッド
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        
        if context == &InputCellKindTitle {
            self.saveButton.isEnabled = self.IsEnableSaveButton()
        }
    }
   
    func removeKVO() {
        if self.titleCell != nil && self.isKVOObserveTitleCell {
            self.titleCell?.removeObserver(self, forKeyPath: "kvoVal")
            self.isKVOObserveTitleCell = false
        }
    }
}

必要な所だけ読み解いていきます。

titleCell.addObserver(self, forKeyPath: "kvoVal", options: NSKeyValueObservingOptions.new, context: &InputCellKindTitle)

こちらで、titleCell(ChildTableViewCell)にある"kvoVal"変数を監視対象にする宣言を行います。
(変数をselectorみたいにリテラルで書くのがちょっと気持ち悪いですが…)
また、optionsは変更後の値を知れれば良いので.newを対象とします。(変更前の値も監視対象にしたければ、.oldも付与します)

また、contextは普段はあまり使わないかもしれませんが、ようはUnsafeMutableRawPointerなので、何をしても良いです。
私の場合は、ここに一意なポインタを渡して、どの監視対象から通知を来たのか判別するために使ってます。

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
}

このメソッドで、KVO監視している値に変更が加わった際に通知が届きます。

  • keyPath : 監視対象の変数名が入ります
  • object : 対象のobject
  • change : ["old" : 変更前の値, "new" : "変更後の値", "kind" : NSKeyValueChangeSettingと等価の値]
  • context : 監視設定した際のUnsafeMutableRawPointer

上記が通知されるのですが、今回のSampleでは、contextさえわかってればなんとでも出来るので、

if context == &kInputCellKindTitle {
    self.ibSaveButton.isEnabled = self.isEnableSaveButton()
}

指定されたcontextのみ、評価を行うようにしています。

KVOは結構簡単にMVC構造を組み立てる事が出来るので非常に便利なのですが、
ちゃんと管理しないと速攻Crashに繋がったりするので、注意が必要です。
また、冒頭でも言いましたが、これくらいの処理であればdidSetの方にシフトするなどを行っても良いと思います。
とはいえ、まだまだ非常に便利な機能な為、なくなるまでは使っていきそうですw

A* Pathfinding Project Proを使ってみる

はじめに

Navmeshの使い方もままならないのに、A* Pathfindingに手を出してしまって、Sample的な使い方をしているのでそのメモ。

10,000円以上払ったのだから是非とも自分のものにしないと!

A* Pathfinding Project Pro

以下は今回作ったものです。 適当にTerrainを用意して、カプセルをPlayerに、スフィアを目的地としています。

パターン1 直線距離で移動できる f:id:project-unknown:20170601012308g:plain

パターン2 直線距離を崖で塞ぐ f:id:project-unknown:20170601012440g:plain

うむ、まだNavmeshとの機能差異はさっぱわからない(;・∀・)

とりあえず、ここまでやった内容をメモがてらに記載します。


(Terrainとカプセル、スフィアを作るところは流石に割愛)

まず、A*をSceneに配置する為に空のGameObjectを置きます。 このObjectに「Pathfinder」をアタッチします。 f:id:project-unknown:20170601012719p:plain (Pathfinderでアタッチしたのに、ComponentがAster Path(Script)となっているのは気にしない)

そうすると、↓のようなComponentがアタッチされます。 f:id:project-unknown:20170601012830p:plain

GraphsがNavmeshっぽい所を意味しているっぽく、とりあえず意味も分からず「Grid Graph」を選択します。 すると追加されるので、Grid Graphの中身を見てみます。

f:id:project-unknown:20170601013022p:plain 上記のWidthとDepthはAの適用範囲を指しており、1010で指定すると以下のように見えます (後述しますが、nodeのサイズを10*10で敷き詰めています)

f:id:project-unknown:20170601013153p:plain 今回のTerrainを100100に設定しているので、Aもそれに合わせておきます。

次にnodeですが、これを細かく設定するときめ細やかにA*が設定され、小道などの小回りが聞くようになりますが高負荷っぽいです。 以下は、0.1で設定した際のキャプチャ f:id:project-unknown:20170601013545p:plain 今度は2で設定した際のキャプチャ f:id:project-unknown:20170601013608p:plain

Max Climbは登ることが出来る高さを設定しているっぽく、ここを大きく設定すると、頑張って崖を登るようになりました。 f:id:project-unknown:20170601013807g:plain

後はまだ良くわかってないので割愛(;・∀・)

このタイミングで、Scanを押してみると、設定したA*がどのように貼られるのかが見ることが出来ます。 f:id:project-unknown:20170601013858p:plain

次にカプセルにNavmeshで言うところのagent設定を行います。

まず、SeekerのComponentをアタッチします。 f:id:project-unknown:20170601014022p:plain

次に、カプセルにagent設定を行います。 今回はサンプルとして、「SampleAI.cs」を作って最低限必要な処理を書きます。

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

[RequireComponent(typeof(Seeker))]
public class SampleAI : AIPath {

    void Start () {
        base.Start();
    }
    
    void Update () {
    }
}

(ほんとうに基礎の基礎ですな…

[RequireComponent(typeof(Seeker))]

を宣言することで、HierarchyでA* agentのパラメータが設定出来るようになります。

f:id:project-unknown:20170601014337p:plain ここのTargetにゴールとなるGameObjectを設定し、Speedとかをちょいちょいといじります。

void Start() {
    base.Start();
}

上記で経路探索して、移動を開始します。

途中で経路を再計算したい際は

base.Update();

で行けるっぽい。

ここまで設定することで、以下のように簡単に経路探索が出来るようになりました。 f:id:project-unknown:20170601012440g:plain

まだまだ初歩の初歩なのでNavmeshのほうが遥かに使えそうに見えますが…もうちょい勉強を重ねます。。。


https://www.assetstore.unity3d.com/jp/#!/content/87744

A* Pathfinding Project

Objective-CからSwiftへ移行する (AppDelegate)

はじめに

ゆう@あんのうんです。

提供中のKeyHolderですが、 これ半分くらいまだObjective-Cのコードで動いています。

流石に今後の保守の事を考えると、All Swift化した方が良いと思ってがちゃがちゃ開発しているにあたり、ちょっとつまずいた所のメモ書き

AppDelegateの書き換え

AppDelegateを単純にSwift化するだけなら問題ないのですが、 こちらの対応を行うと同時に、Objective-CでProjectを作るとデフォルトで入ってくるmain.mにて、

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

こんな感じでAppDelegateを呼び出しているのですが、Swift化から、AppDelegateにて

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
/*省略*/
}

こうしているため、実はmain.mは不要となります。

(逆に残しているとLinker errorとなります) なので、こいつを削除してあげると正常動作します。

WKWebViewのSample

ゆう@あんのうんです。

ATSの問題で、WKWebViewが流行ってきました。 (WKWebViewでもUIWebViewでもNSAllowsArbitraryLoadsInWebContentが使えるので、ATSだから…と言うのは色々とミスリードな気がしてますが)

ただ、ChromeアプリもWKWebViewに乗り換えたという話も出ていますし、今後の世の流れはWKWebViewに移っていくだろうと、想定してやろうやろうと思っていたWKWebViewの勉強を始めたので、そのメモ的なものです。

最初に

WKWebViewはInterfaceBuilderで作成できません。

ココらへんで、昔ながらのiOSエンジニアなら問題ないのでしょうが、Storyboard台頭以降のiOSエンジニアに取っては敷居の高いものと鳴っているかと思います。
かくいう私もすっかりIBに慣れてしまった為、中々勉強しなかった原因がこれなのですが…。

話を戻すと、現状(XCode8.0段階)でもIBは用意されていなかったので、自前でコツコツとコードを書いていきます。

XCode8.0 iOS10ではすでに設定を入れないとHTTP通信は出来ません。

なので、作っていてViewが真っ白の場合は、これに引っかかっているケースが多いので、設定をいじりましょう。 Info.plistに以下の用に設定します

img

コードを書く

単純にWebページを表示させるだけならUIWebViewと使用感は変わりません。

以下に、ViewControllerの画面いっぱいにWKWebViewでWebページを読み込むコードを記載します。

class ViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {

    @IBOutlet var ibWKWebView: WKWebView?
    
    required init?(coder aDecoder: NSCoder) {
        super.init(nibName: nil, bundle: nil)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        self.ibWKWebView = WKWebView()
        self.ibWKWebView!.navigationDelegate = self
        self.ibWKWebView!.translatesAutoresizingMaskIntoConstraints = false
        
        self.view.addSubview(self.ibWKWebView!)
        
        var viewBindingsDict = [String: AnyObject]()
        viewBindingsDict["webView"] = self.ibWKWebView
        self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[webView]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: viewBindingsDict))
        self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[webView]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: viewBindingsDict))
        
        let url = NSURL(string: "http://project-unknown.hatenablog.com/entry/2015/04/18/201907")
        let request = NSURLRequest(url: url! as URL)
        
        self.ibWKWebView!.load(request as URLRequest)
        
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        
    }


}

やっていることは

self.ibWKWebView = WKWebView()

で、WKWebViewのオブジェクトを作って、

self.ibWKWebView!.load(request as URLRequest)

で、NSURLRequestを読んでいるだけとなります。

Sampleコードについて

以下のGitにUploadしていますので、参考にしてください https://github.com/yxuyxu/WkWebViewSample/blob/master/WKWebViewSample/ViewController.swift

この後は、Delegateやら色々あるのですが、今日はここまでで。

CoreDataでエンティティの新規レコードを生成し、NSManagedObjectのサブクラスにキャストしたら「Could not cast value of type 'NSManagedObject_' to」のエラー

はじめに

ゆう@あんのうんです。

最近もっぱらswiftばっか開発していて、Objective-Cに戻れません。。。

今日は珍しくCoreDataの話題。

タイトル通りのエラーに悩まされており、解決したので投稿

現象

例えば、以下のCoreDataのエンティティがあるとして

03.png

これのNSManagedObjectのサブクラスとして以下で用意していました(言語はSwift2.0です)

import Foundation
import CoreData

@objc(Hoge)
public class Hoge: NSManagedObject {
    
    @NSManaged public var name:         String?
    @NSManaged public var id:           Int16
    @NSManaged public var updatedate:   NSDate
        
}

んで、以下の通りにNSManagedObjectを生成しました。

import Foundation
import CoreData

public class CoreData {
    
    private static let instance = CoreData()
    
    public class var sharedCoreData: CoreData {
        return instance
    }
    
    // 省略
    
    public func newRecordWithName(name: String, inContext context: NSManagedObjectContext) -> NSManagedObject {
        return NSEntityDescription.insertNewObjectForEntityForName(name, inManagedObjectContext: context)
    }
}

class func newHogeManagedObjectContext(context: NSManagedObjectContext) -> Hoge {
        return CoreData.sharedCoreData.newRecordWithName(NSStringFromClass(CategoryInfo), inContext: context) as! Hoge
    }

そしたら、タイトル通り、

Could not cast value of type 'NSManagedObject_Hoge_' to 省略

なるエラーが発生。

解決方法

04.png

上記の用にHogeエンティティに対応するClassをインスペクタ内で指定してあげれば問題なく動作しました。

Unityのスプラッシュ画像表示中に、処理を止める方法 (iOS)

ゆう@あんのうんです。

例えば、Unityの初回シーンで広告を生成して表示しようとした際に、スプラッシュ画像にまで広告が出てしまう事が度々あります。

スプラッシュ画像がちゃんと非表示になったのを確認してから表示するには、初回シーンのUpdate等に以下の判定を入れましょう

void Update() {
    if (Application.isShowingSplashScreen == false) {
        // 広告などの処理
    }
}

Application.isShowingSplashScreenは、スプラッシュが表示されている際にはtrueを返し、非表示の際にfalseを返します。

Unity2d Scriptで生成したGameObjectを子として登録する。

はじめに

ゆう@あんのうんです。

とあるGameObjectがあり、動的にそのGameObjectに新しいGameObjectを子として登録する方法です。

やりかた

子としてGameObjectを登録すると、親の座標移動に従って相対的に座標変更できるので、パーティクルとかでGameObjectに一定時間エフェクトを表示する時など便利です。

例では、動的に生成したPrefabを子として登録します。

// プレハブを取得
GameObject prefab = (GameObject)Resources.Load("Prefabs/Effects/Prefab名");

Vector2 pos = new Vector2(gameObject.transform.position.x, gameObject.transform.position.y);
// プレハブからインスタンスを生成
GameObject obj = (GameObject)Instantiate(prefab, transform.position, Quaternion.identity);
// 作成したオブジェクトを子として登録
obj.transform.parent = transform;

上記Sampleの様に、生成したオブジェクトの、transform.parentに親にしたいオブジェクトのtransformを登録する事で、親子関係にすることが出来ます。

上記Sampleでやっている、Prefabの動的生成については、こちらの記事に詳細を載せています。

Unity2d PrefabをScript上からロードする。

はじめに

ゆう@あんのうんです。 喉の調子がずっと悪い…。

Prefabを作成し、それをScript上からロードするやり方です。

やりかた

Prefabを

01.png

上記みたいに、Assets > Resources > Prefabs > Effectsに設置した際、

以下の用にScript上からコール出来ます。

// プレハブを取得
GameObject prefab = (GameObject)Resources.Load("Prefabs/Effects/Prefab名");
// インスタンス生成
Instantiate (prefab, pos, Quaternion.identity);

これをResources以外のところに置くとうまく動かなかったのですが、他のところに置く場合はなにか別なやり方あるのですかね…。

とりあえず、ご紹介でした。

Unity2d 別のGameObjectにメッセージを送る(SendMessage)

はじめに

ゆう@あんのうんです。

今日は標題のUnityのオブジェクト間の情報のやり方の1つのSendMessageについてです。

概要

例えば、アクション等で、プレイヤーキャラがダメージを受けた場合、

ゲーム全体を管理しているマネージャーにその情報を送りたいという事は往々にしてあります。

その時に情報を送るやり方の1つとして、SendMessageと言う機能があります。

(iOSで言うところの、Notificationが近しいかも)

やり方

オブジェクトAから、オブジェクトBにメッセージを送る例

// オブジェクトAの処理
GameObject objectB = GameObject.FindGameObjectsWithTag ("ObjectB");
objectB.SendMessage("hogehogeMessage");
// オブジェクトBの処理
void hogehogeMessage() {
    /* 処理 */
} 

上記の用に記述することによって、オブジェクトAの然るべきタイミングで、オブジェクトBのhogehogeMessageメソッドをコールすることが出来ます。

また、引数を持たせたい場合は以下のように記述します。

// オブジェクトAの処理
GameObject objectB = GameObject.FindGameObjectsWithTag ("ObjectB");
objectB.SendMessage("hogehogeMessage", 10);
// オブジェクトBの処理
void hogehogeMessage(int arg) {
    /* 処理 */
} 

その他にも、SendMessageは単純にメッセージを送る機能ですから、同時にいくつも送信しても問題ありません。

// オブジェクトAの処理
GameObject[] objectBArray = GameObject.FindGameObjectsWithTag ("ObjectB");
foreach (var obj in objectBArray) {
    obj.SendMessage("hogehogeMessage");
}
// オブジェクトBの処理
void hogehogeMessage() {
    /* 処理 */
}