Monkey Circus ← トップページに戻る。


how to...
2016年12月 iOS の WKWebView で。 canvas 外から canvas 内へ何度かすばやくスワイプした時の touchmove イベント内で、 まれに changedTouches が undefined になってしまう。(ルーペ虫眼鏡が出る時?)
2016年10月 iOS のタッチと、Web Audio の scriptProcessorNode の始動との関係で、不思議なこと。 (iPhone でも iPad でも同じ。Safari でも Chrome でも同じ。) 複数同時タッチの touchstart 内では始動できない。 複数同時タッチ中の、追加のシングルタッチの touchstart 内でも始動できない。 ただのシングルタッチの touchstart 内なら、もちろん普通に始動できる。 タッチをわずかにでも動かした後の touchend では始動できない。 タッチを動かさずに放した時の touchend では始動できる。 iPhoneChrome の組合せの場合のみ、マルチタッチの不思議なこと。 最初のタッチを 20 ピクセル程度以上動かすと、 二つ目以降のタッチが無視される。 web 用に HTML5 と Java Script で作ったゲームを、iOS のガワネイティブに組込む時。 UIWebView を使えば問題ないけれど、より速い WKWebView を使おうとしたところ、 ローカルのページを表示する段階までは、iOS 9 のおかげでちゃんとできるようになったけど、 Web Audio で音を鳴らすために、XMLHttpRequest で音のデータを読込もうとすると、 CORS 制限のせいでローカルのファイルを読込めないので、 WKWebView の configuration や、Info.plist の ATS を調整すれば読込めるかな、とか、 メディアエレメントならローカルでも再生できるのだから、 そこから AudioBuffer を取り出せないかな、とか、 いろいろな方法を半年間ぐらい試行錯誤してたけど結局だめで、 iOS 10 になっても XHR できなくて、 しょうがなく、面倒くさそうなローカルサーバを立てるとか、Base64 に変換するとか、 今までやりたくなかったことも調べていたら、意外に Base64 ってすごく単純で、 昔のマイコン時代にやってたこととよく似ていて親しみを感じたので、 ぜんぶ Base64 にして解決。 WKWebView から App Store (iTunes Store) を開く。 UIWebView ならば、js で、location.assign するだけで iTunes Store を開くことができたので楽チンでしたが、 WKWebView は、そのリクエストを Swift 側でキャッチしてあげる必要がある。やり方は……。 class ViewController: UIViewController, WKNavigationDelegate{…} と、インプリメントして、 wkwv.navigationDelegate = self として、 func webView( _ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void){…} の中で、 UIApplication.shared.openURL( url!) とすればよい。 iOS 10 専用にするなら UIApplication.shared.open( url!, options: [:], completionHandler: nil}) 下の方に SwiftUI 版、書いた。
2016年6月 たまたま three.js のデモのページを見つけて、web でいろいろ 3D できるの見て感動。 3年ぐらい前から WebGL ですごいグラフィックスができるってことは、薄々知ってたけど、 ややこしすぎて自分には無理と思ってたけど、three.js のおかげで、 自分でもできそうと思って1ヶ月間ほどで fpw を作った。unity やめた。


web
onresize と onorientationchange で、viewport を変えるのは、iPhone や Android では うまくいくけど、iPad で不安定。WindowsPhone で無効。 なので収縮 canvas 方式にする。 ★Edge は、F12 キーで開発者画面。 ★画像・音リソースのロード失敗時にリトライ。 ★NOID と MOUSEID は負の数に。NOWHERE を使用。 ★setInterval ではなく requestAnimationFrame。 ★touch の ID は Int32Array に代入してはいけない。(long だから?) (iOS では Chrome でも Safari でも、非常に大きな数になる。) (Android では 0 からの小さい数。何度も 0 に戻る。) (Win 10 Edge は、pointer ID、小さい数から始まってどんどん増えてゆく。) ★タッチ座標を Float32Array に代入してはいけないかもしれない。(long だから?) ★加速度の扱い。 ★Windows 用に viewport や pointerdown 等を入れる。 ★一部の機種では、touch 系と mouse 系のイベントが続けざまに発生する。 リスナは両方登録する。一方がスタート後 0.5 秒間は、他方のスタートを無視。 ★js ファイルを、メールに添付して送ると改行コードが増えることがある。 圧縮してから添付すれば大丈夫。 ★Windows の Edge でタッチを離した時、pointerup → pointerout 続けざまに呼ばれる。 ★Windows では、絶対 URL を書くと CORS ひっかかる。相対 URL にする。 <各デバイスでのチェック項目> ★マルチタッチできるか。 ★スクロール中に停止しないか。 ★音が出るか。
Web Audio
★無音の AudioBufferSourceNode のループから ScriptProcessorNode を通して 音を出すのは、Android には荷が重い。iOS では大丈夫。(2015年頃。) ★MediaElementAudioSourceNode で音を出すのは、遅延がある。 ★AudioBufferSourceNode で音を出すのが良い。 ★iOS では、最初の touchend で、ac.createBufferSource().start( 0); と、しておけば、後はいつでも再生できる。 ★アプリがバックグラウンドに行って、 また帰ってきたら音が出なくなるのを防ぐには…… visibility が hidden になった時に、 is_muted というようなフラグを自分なりに立てておいて、 アプリが再開した時、フラグが立っていれば、 タッチのタイミングで、ac を resume() すればよい。 ScriptProcessorNode で、継続的に音を出しているアプリ(fst のこと)では、 この対策が不要? destination に対して、hidden で disconnect して、 visible で connect するだけでいいみたい。 ★wav ファイルを XMLHttpRequest で ArrayBuffer として読み込む。 →ArrayBuffer を decodeAudioData で、AudioBuffer に変換。 →AudioBuffer から getChannelData で、Float32Array を取り出す。


iOS
SwiftUIWKWebView でガワネイティブアプリを作る方法。(AdMob と Firebase 付きで。) まず、Xcode 12.1 を起動して、新規プロジェクト。 プラットフォームは iOS、Application の App を選択して Next。 Product Name 入れて、SwiftUI・SwiftUI App・Swift で…… と、思ったけど、SwiftUI App を選ぶと、iOS 14 以降限定になって、 LaunchScreen.storyboard が無くなって、代わりに Info.plist だけで launch screen を設定することになる。だから LaunchScreen.storyboard が不要。 Assets.xcassets の中に背景色画像を用意して、その名前を Info.plist で指定する。 画像のサイズは「1x の横幅」で言った場合、iPhone 5 と SE までは、320。 iPhone 6・7・8・SE 2・X・11 Pro は、375。 iPhone 6・7・8 の plus と、11 と、11 ProMax は 414。 ざっくりそんな感じ。iPhone 5 はもう気にしないで、 例えば 1x に 320px の画像を入れて、2x に 640px の画像を入れたなら、 11 Pro では画面幅の 85%、11 では画面幅の 77% ぐらいのサイズで表示される。 3x は空欄でいい。 ドラッグ&ドロップしたら、画像ファイルはプロジェクト内に即座にコピーされるので、 よそのフォルダからドラッグ&ドロップしてきてもいい。 ビットマップ画像の代わりに、svg や pdf を入れてもいい。 この場合アートボードのサイズが、ビットマップ画像の場合のサイズと同じ意味を持つ。 svg にビットマップが含まれていたら無視されて、パスだけ出る。 pdf ならビットマップもちゃんと出る。 ところが、launch screen が、あまりにも一瞬だけで消えてしまう。 表示時間をのばす方法が分からなかった。 LaunchScreen.storyboard が欲しければ、 File → New → File で、User Interface の Storyboard で、Next、 LaunchScreen.storyboard と名前を付けて、Create。 TARGETS の General の中で、App Icons and Launch Images の Launch Screen File に、 これを選ぶ。という風にすれば、自分で作ることができる。 だけど AppDelegate.swift が無いので、やっぱり表示時間をのばす方法が分からなかった。 なので、SwiftUI・UIKit App Delegate・Swift にする。 これなら iOS 13 の人にもプレイしてもらえる。(13 と 14 の対応機種は同じだけど。) 『2021年4月以降、App Storeに提出するiOS AppとiPadOS Appはすべて、 Xcode 12およびiOS 14 SDKでビルドする必要があります。』 とのお知らせがあるけど、これは SDK を 14 にしなければいけないだけで、 デプロイメントは 13 でもいい?Life Cycle を SwiftUI App にしなくても大丈夫? Use Core Data、Include Tests はチェックしない。→ Next。 保存場所画面 → Create。 PROJECT の Info の中で、iOS Deployment Target を設定。(13.0 で。) TARGETS の General の中で、Display Name、Version、Build、Device Orientation を設定。 それと、Status Bar Style は、launch screen の短い時間だけの、ステータスバーの文字等の色。 Dark Content なら黒、Light Content なら白。 実機から、このアプリの旧バージョンをアンインストールした上で、 さっそく、実機テスト。 ホーム画面に、仮アイコンが出て、その下にアプリ名が出て、 起動した後の画面には Hello, World! が出るのを見て、ちょっと安心する。 LaunchScreen.storyboard を編集して起動時の画面を作ってゆく。 Xcode の右上の「+」ボタンを押して、 ただの View や Image View 等を置く。 コンストレイントを付けるには、ツリー表示の中で、 control キーを押しながら、子要素から親要素へ青い線を伸ばす。 要素は入れ子にしないで、全部ルートに置いて、 透明のビューをいろいろ配置して、 その位置やサイズを要素へ適用すればよい。 画像や背景色の変更が実機に反映されない時は、実機を再起動。 電源が完全に切れるまで約20秒。電源を入れることができるようになるまで約3秒。 再起動に約15秒。Xcode が実機を認識するまで約5秒。待つ。 画像はアスペクトフィットできるけど、ビューはできない。 ビューをアスペクトフィットする方法は…… view1 は領域に対して、横小なりで、縦ピッタで、アスペクトの優先度は下げるのがミソ。 view2 は view1 に対して、横ピッタで、縦小なりで、アスペクトをリクワイアドに。 LaunchScreen が一瞬しか表示されないのを防ぐためには、 AppDelegate.swift の didFinishLaunchingWithOptions() の中に sleep( 2) と書く。 Assets.xcassets の中にアイコン画像をいっぱい入れる。 ドラッグ&ドロップ。全サイズ入れる。角は丸くしなくていい。 この時、画像ファイルはプロジェクト内に即座にコピーされるので、 よそのフォルダからドラッグ&ドロップしてきてもいい。 ここでまた実機テスト。 アイコンと、LaunchScreen を見て喜ぶ。でも当然ながら Hello, World! しか出ない。 Firebase を入れていきます。 Mac に CocoaPods (←ターミナルで使うアプリ?) が入っていない場合、インストール。 Firebase のサイト内で、Firebase のプロジェクトにアプリを追加。 GoogleService-Info.plist をダウンロードして、ルート (Info.plist の並び) に置く。 このファイルが、Xcode の中のツリーにも表示されるように、ドラッグ&ドロップする。 (Firebase のサイトで、個別アプリに対して App Store ID と、チーム ID を入れる必要がある?) いったん Xcode を終了。 ターミナルで ls と cd を駆使して、どうにかしてこのアプリの ◍◍◍.xcodeproj がある フォルダに行く。pod init すると、Podfile (←これがファイル名。拡張子がない。) というファイルができる。これをテキストエディタで開いて、end の前に pod 'Firebase/Analytics' と書いて保存。そしてターミナルで pod install。 後で、ContentView.swift に import Firebase して FirebaseApp.configure() したら、 Firebase のサイトで、アクセスがあったことが確認できる。らしい。 AdMob を入れていきます。 Podfile の中に pod 'Firebase/AdMob' を追加して保存。 ターミナルで pod install --repo-update Podfile とターミナルは、もう閉じていい。 Xcode を起動。この時、◍◍◍.xcodeproj (青アイコン) ではなくて、 ◍◍◍.xcworkspace (白アイコン) を開くことに注意。 Info.plist の中に GADApplicationIdentifier というキーを作る。 型は String 、値は、AdMob のアプリID (途中にスラッシュが入っている方ではなくて、チルダが入っている方。) ContentView.swift の中に…… import Firebase import GoogleMobileAds import WebKit と、インポートして、すぐ続けて、WKWebView と AdMob の struct を…… struct mywv : UIViewRepresentable { let wv = WKWebView( frame: .zero) //◍◍◍ WKWebView 内の js から app store を開くために。 func makeCoordinator() -> coo { return Coordinator()} class coo: NSObject, WKNavigationDelegate, WKScriptMessageHandler { let u = "https://itunes.apple.com/jp/artist/◍◍◍◍◍◍◍/id◍◍◍◍◍◍◍◍◍?mt=8" func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { if message.name == "appstore" { UIApplication.shared.open( URL( string: u)!, options: [:], completionHandler: nil ) } } } func makeUIView( context: Context) -> WKWebView { FirebaseApp.configure() GADMobileAds.sharedInstance().start( completionHandler: nil) wv.scrollView.bounces = false //◍◍◍ WKWebView 内の js から app store を開くために。 wv.configuration.userContentController.add( makeCoordinator(), name: "appstore") wv.load( URLRequest( url: URL( string: "file://" + Bundle.main.resourcePath! + "/◍◍◍/◍◍◍.html" )!)) return wv } //★ ↓この中は、iPad で縦⇔横、の時、実行される。 //★ ↓バックグランドから復帰した時にも。 func updateUIView(_ v: WKWebView, context: Context) {} } struct AdView: UIViewRepresentable { func makeUIView( context: Context) -> GADBannerView { let bv = GADBannerView( adSize: kGADAdSizeBanner) bv.adUnitID = "ca-app-pub-3940256099942544/2934735716" //← テスト用。 bv.rootViewController = UIApplication.shared.windows.first?.rootViewController bv.load( GADRequest()) return bv } func updateUIView(_ uiView: GADBannerView, context: Context) {} } と書く。そして、ステータスバーの背景を暗いブラーにする struct を次のように入れる。 struct blur_view: UIViewRepresentable { func makeUIView( context: UIViewRepresentableContext<Self>) ★←この角カッコは半角で。 -> UIVisualEffectView { UIVisualEffectView()} func updateUIView( _ uiView: UIVisualEffectView, context: UIViewRepresentableContext<Self> ★←この角カッコは半角で。 ) { uiView.effect = UIBlurEffect( style: .dark); uiView.alpha = 0.4 } } 最初から Hello, World! が入っている ContentView は、次のように書き換える。 struct ContentView: View { var s = kGADAdSizeBanner.size var body: some View { GeometryReader { g in ZStack( alignment: .bottom) { mywv() VStack { blur_view().frame( height: g.safeAreaInsets.top) Spacer() AdView().frame( width: self.s.width, height: self.s.height) Spacer().frame( height: 30) } }.edgesIgnoringSafeArea( .all) //★iOS 14 では、ignoresSafeArea( .all) } } } 最後の ContentView_Previews は、そのまま。 web 版を入れていきます web 版のフォルダをプロジェクト内に置いて、 Xcode の中のツリーにも表示されるように、ドラッグ&ドロップする。 app store を開くには、js で、webkit.messageHandlers.appstore.postMessage( 0); 加速度を swift から WKWebView へ知らせる場合 ContentView.swift に、import CoreMotion をインポートして、 mywv の冒頭で、let mman = CMMotionManager() return wv の前に、 mman.accelerometerUpdateInterval = 1 / 20 mman.startAccelerometerUpdates( to: OperationQueue.current!, withHandler: { ( d: CMAccelerometerData?, errorOC: Error?) in on_acc( acc: d!.acceleration) } ) func on_acc( acc: CMAcceleration){ wv.evaluateJavaScript( "window.acc_from_swift(" + String( format: "%.5f", 10 * acc.x) + "," + String( format: "%.5f", 10 * acc.y) + ");", completionHandler: nil ) } js では、window.acc_from_swift = function( vx, vy){} で受け取る。 加速度のパーミッションダイアログで用途を説明する必要があるらしい。 ダイアログはいつ出るんだろう。 ローカリゼーション ツリーで Info.plist (または並びのファイル)を選択した状態で File → New → File。 ダイアログで、Resource の中の Strings File を選んで Next。 InfoPlist.strings という名前のファイルを Create。 できたファイルをツリーで選択して、画面右の情報コーナーで Localize ボタンを押す。 次に PROJECT の Info で、Localizations の「+」ボタンを押して、Japanese を選択。 次の画面で LaunchScreen は、チェックをはずして、InfoPlist.strings だけにして Finish。 それぞれに… NSMotionUsageDescription = "to control game character."; NSMotionUsageDescription = "傾けてパズルを操作します。"; それから… 長押しでコピーペースト等のメニューが出ないようにするには、 html 全体に対する style で -webkit-user-select: none。 長押しでハプティックが振動しないようにするには、 ContentView.swift の中の wv の設定で、 let r: UILongPressGestureRecognizer = UILongPressGestureRecognizer( target: nil, action: nil) r.minimumPressDuration = 0.2 wv.addGestureRecognizer( r) ダブルタップや、ピンチを禁止するには、meta タグの viewport で user-scalable=no。 Safe Area の上へ、画面の端まで壁紙を伸ばすには、meta の viewport に viewport-fit=cover。 Safe Area の下へ、画面の端まで壁紙を伸ばすには、 html 全体に対する style で height: 100% ではなく height: 100vh。 バックグラウンドに行って、帰ってきた時に音が出るか、対処法は上の方に書いた。 ハイスコア等、サーバ上の php ファイルにアクセスするためには、Info.plist の中に、 App Transport Security Settings (Dictionary) →Exception Domains (Dictionary) →→www.◍◍◍.com (Dictionary) →→→NSTemporaryExceptionAllowsInsecureHTTPLoads (Boolean) YES ステータスバーの、文字の色を白くしたい場合は、 SceneDelegate.swift の中に、 class hc: UIHostingController<ContentView> { ★←この角カッコは半角で。 override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent} } というクラスを用意して、scene() の中の window.rootViewController = UIHostingController(rootView: contentView) という行を、 window.rootViewController = hc( rootView: contentView) に変える。 iPad で縦⇔横くりかえした時に、たまに変な風に拡大されないようにするには、 html の viewport で width=device-width にする。(ちなみに Android だと数値で指定する。) skeleton_3.js の set_target_mag() で、 //★例えば、canvas の上に 25px、下に 125px 余白をとっている場合、 h = window.innerHeight - 130; //★mypadding ですでに 20 なのを 150 にしたい。 みたいにして、ランドスケープの時の縦幅を押さえ込む。 この計算がぴったりだと、わずかにスクロールできてしまうので、 数字を少しだけ大きめにすることで、canvas をその分小さめにする。 永続できているか、iOS のオーディオの始動も確認。 ここで実機テスト。これで完成。
App Store に申請
debug と release で、optimization 等の設定が違うと、 公開するものが開発中と違う挙動をするかもしれないので注意。 前もって App Store Connect のサイトで、アプリを登録する。 Xcode で Archive して、Validate App してから、Distribute App する。 15 分ぐらい待って、処理完了のメールが来たら、App Store Connect のサイトで、それを選択。 スクリーンショットや、1024 ドットサイズのアイコンもサイト内で登録。
iAd は、2016年6月で終了したので、AdMob にする。Firebase も入れるとよい。 4S でも動かしたければ、デプロイメント 9.3。 iPodTouch (第6世代) でも動かしたければ、デプロイメント 12.4。 iPodTouch (第7世代) なら、デプロイメント 13、大丈夫。 Requires full screen をチェックすると、iPad の2画面表示ができなくなる。 チェックしないで、Validate に合格するには、iPad で4方向に対応する必要がある。 iPad で4方向に対応したくなくて、Validate に合格したければ、チェックすれば楽。 html ファイルに、maximum-scale=1 を入れる? ストアの購入までちゃんと行けるか確認する。
ターミナル
/ はルート、~/ はユーザーのホーム $ ls $ cd
CocoaPods
★インストール $ sudo gem update --system $ sudo gem install cocoapods ←ではなくて…… $ sudo gem install cocoapods -n /usr/local/bin ←とする。 インストールされたバージョンを表示。 $ pod --version $ pod setup ←数時間かかる。待つ。 ★AdMob と Firebase を、最新バージョンにする。 $ pod update
2021/01/23〜 iPhone 8 Plus スペースグレイ 64 GB A11 Bionic z 2020/11/20〜 iPhone 12 Blue 64 GB 2019/11/22〜 iPhone 11 A13 Bionic iOS 13.4.1 CPUとノッチ画面確認するため。■画面一部無反応。 2019/03/09〜 iPhone Xs A12 Bionic f 2017/10/22〜 iPhone 7 A10 iOS 13.3.1 2016/10/01〜 iPhone 6 A8 f ■バッテリーがすぐに切れる。 2016/06/21〜 iPhone 4S A5 ■iOS 10 を入れられないので使用中止。 2010/10/02〜 iPhone 4 A4 ■ 2009/06/27〜 iPhone 3GS Samsung S5PC100 ■ 2008/08/02〜 iPhone 3 (?) ARM 1176JZ(F)-S ■ 2017/02/02〜 iPad Air 2 Retina A8X 2013/12/02〜 iPad mini 第2世代 Retina A7 ■バッテリー残量がゼロになったり 100 % になったり。 2013/02/21〜 iPad 第4世代 A6X 2010/08/31〜 iPad 第1世代 A4 iOS 5.1.1 ■ 2018/01/27〜 iPod touch 第6世代 スペースグレイ A8 32GB z 2015/07/17〜 iPod touch 第6世代 ブルー A8 32GB iOS 11.2.2 ■バッテリー膨張。 2012/10/10〜 iPod touch 第5世代 イエロー A5 ■バッテリー膨張、使用不可。 2011/06/11〜 iPod touch 第4世代 A4 ■Swift 不可。 2020/05ごろ Apple Watch 2019/12/10〜 MacBook Air Core i5 Gold 13.3 / 1.6 GHz / 8 GB / 256 GB (m → f) 2019/11/10〜 MacBook Air Core i5 Space Gray 13.3 / 1.6 GHz / 8 GB / 128 GB (Coffee Lake) (z) 2018/04/04〜 MacBook 12 インチ Retina Core m3 ローズゴールド (Kaby Lake) (f) 2018/01/16〜 MacBook Pro 13 インチ デュアルコア i5 2.3 GHz 256 GB スペースグレイ (Kaby Lake) 2015/06/25〜 MacBook 12 インチ Retina Core M ゴールド (Broadwell) (m → z) 2013/06/15〜 MacBook Air 13 インチ 256 GB Core i5 (Haswell) 2012/01/23〜 MacBook Air 13 インチ 1.7 GHz Core i5 128 GB ■ 2010/09/04〜 MacBook Pro ■ 2010/08/02〜 MacBook Pro ■ 2010/01/31〜 MacBook 13 インチ 白 ■ 2008/06/11〜 MacBook 白 ■
6.5 インチというのは 1242 x 2688 ピクセル = 2.164 5.8 インチ (X): 1125 x 2436 = 2.165 5.5 インチ (plus): 1242 x 2208 = 1.777 4.7 インチ (iPhone 6・7): 750 x 1334 = 1.778666 4 インチ (ステータスバー込みで): 640 x 1136 = 1.775 3.5 インチ (ステータスバー込みで): 640 x 960 = 1.5 12.9 インチ (iPad Pro): 2048 x 2732 = 1.333 11 インチ (iPad Pro): 1668 x 2388 = 1.432 10.5 インチ (iPad シリーズ): 1668 x 2224 = 1.333 9.7 インチ (iPad ステータスバー込みで): 1536 x 2048 = 1.333


Windows
2019/10/29〜 LG gram Windows 10 Home 64bit Core i5 2015/12/03〜 ASUS TransBook T300 ★タスクマネージャーを起動するには…… タスクバーを右クリック。 ★コマンドプロンプトを起動するには…… 画面の左下の旗ボタン → すべてのアプリ → Windows システムツール → コマンドプロンプト
★Visual Studio でエミュ使うには、64 ビット版の Win 機が必須。 CPU が SLAT 対応であることも必須。 開発に使う Windows 10 は「開発者モード」を有効にしておく。 <UWP にメディエーション付きの広告を追加する方法。> Visual Studio を使う。 Microsoft ユニバーサル広告クライアント SDK のインストール https://msdn.microsoft.com/ja-jp/library/windows/apps/xaml/mt621245.aspx 広告メディエーター コントロールの追加と使用 https://msdn.microsoft.com/ja-jp/library/windows/apps/xaml/mt219682.aspx assets 以下の HTML ファイルを読み出す方法 $.webview.url = "ms-appx-web:///test.html"; js と HTMLでは、メディエーションできないみたいなので、 C# と XAML にしよう。 ↓メディエーション付きで、広告を入れる方法 https://msdn.microsoft.com/library/windows/apps/xaml/mt219691.aspx


Android
Android で、ガワネイティブアプリを作る方法。(Firebase と AdMob 付きで。) ともあれ、Android Studio を起動。 新規プロジェクト。「Phone and Tablet」の「Empty Activity」。Next。 Kotlin で。Minimum SDK は、21。(← 64K 問題に対応しやすいから。) Finish ボタン押して、自動処理が完全に終わるまで待つ。 Firebase と AdMob のサイトで、できることは済ませておく。 AdMob のサイトで、アプリを登録、Firebase にリンクして、ユーザー指標を有効にしておく。 さっそく、Firebase を入れていきます。 Firebase のサイトから、google-services.json をダウンロードして、app フォルダに入れる。 build.gradle (←プロジェクトレベルの方。注意。) の dependencies に、 classpath 'com.google.gms:google-services:4.3.4' と入れる。 build.gradle (:app) (←アプリレベルの方。注意。) の最初の plugins の中に、 id 'com.google.gms.google-services' を追加。 そして、dependencies の中に、 implementation 'com.google.firebase:firebase-analytics-ktx:17.6.0' と入れる。 最初から入っている行についても、新しいバージョンをおすすめされたら、 全部新しくすればいい。 右上の「Sync Now」をクリック。 ここで実機テスト、Hello World! の画面が出る。 デバイスログ(Run)で、Firebase の initialization が successful だったことを確認。 Analytics を入れていきます。 MainActivity.kt の中に、 import com.google.firebase.ktx.Firebase import com.google.firebase.analytics.ktx.analytics import com.google.firebase.analytics.FirebaseAnalytics と、3行インポートして、クラスの中に… private lateinit var fba: FirebaseAnalytics と宣言して、onCreate() の中の最下部に、 fba = Firebase.analytics と書く。 これで任意に logEvent() できる。 AdMob を入れていきます。 AndroidManifest.xml の中の、application タグの間の、activity タグの上にでも、 <!-- ca-app-pub-◍◍◍◍◍~◍◍◍◍◍ 本番用 --> ★←角カッコ半角。 <!-- ca-app-pub-3940256099942544~3347511713 テスト用 --> ★←角カッコ半角。 <meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="ca-app-pub-3940256099942544~3347511713" ★↑開発中からずっと本番用でよい。 /> ★←この角カッコは、全角で表示していますが、実際は半角で。以下同じ。 を追加。 build.gradle (:app) の中の dependencies の中に、 implementation 'com.google.android.gms:play-services-ads:19.5.0' を追加。 あと、このファイルを開いたついでに、 上の方で versionCode と versionName を設定。 64K 問題を回避するために、versionName に続けて multiDexEnabled true 右上の「Sync Now」をクリック。 MainActivity.kt に、 import com.google.android.gms.ads.MobileAds と、インポートして、 その onCreate() の中の最下部に、 MobileAds.initialize( this){} を追加。 activity_main.xml で、 <androidx.constraintlayout.widget.ConstraintLayout を、 <FrameLayout に変える。閉じるタグも自動的に、FrameLayout になる。 TextView のタグの下に、以下のバナーのタグを追加。 <!-- ca-app-pub-◍◍◍◍◍/◍◍◍◍◍ 本番用 --> <!-- ca-app-pub-3940256099942544/6300978111 テスト用 --> <com.google.android.gms.ads.AdView xmlns:ads="http://schemas.android.com/apk/res-auto" ads:adSize="BANNER" ads:adUnitId="ca-app-pub-3940256099942544/6300978111" (↑◍開発中は、このテスト用の広告ユニット ID を使う。公開直前に本物の ID にする。) android:id="@+id/adView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|center" android:layout_marginBottom="7dp" /> MainActivity.kt に、 import com.google.android.gms.ads.AdRequest import com.google.android.gms.ads.AdView と、2行インポートして、クラス内の最初に、 lateinit var av : AdView を宣言して、onCreate() の中の最下部に、 av = findViewById( R.id.adView) av.loadAd( AdRequest.Builder().build()) を追加。 ここで実機テスト、Hello World! の画面の下部に、テスト用のバナーが出ることを確認する。 アイコンを入れていきます。 app/src/main/res/ の下記それぞれのフォルダにアイコンの画像ファイルを入れる。(角丸の四角型で。) mipmap-hdpi (72 ピクセル) mipmap-mdpi (48 ピクセル) mipmap-xdpi (96 ピクセル) mipmap-xxdpi (144 ピクセル) mipmap-xxxdpi (192 ピクセル) 最初から入っているアイコンの画像ファイル、5サイズぜんぶ削除。 AndroidManifest.xml の中の android:icon="@mipmap/ic_launcher" の、 ic_launcher の部分を変えて、アイコンのファイル名を指定。拡張子はなぜか不要。 アイコンを古い仕様にする(まん丸にしない)ためには… android:roundIcon="@mipmap/ic_launcher_round" 削除。 drawable フォルダの中の、ic_launcher_background.xml 削除。 drawable-v24 と mipmap-anydpi-v26 フォルダごと削除。 デバイス上での見た目を変えていきます。 strings.xml の中の app_name を変えると、アイコンの下のタイトルが変わる。 colors.xml の中に……(ARGB の8ケタで。) purple_700 は、画面上端のステイタスバーの背景色。 purple_500 は、複数アプリを切り替える画面での、タイトルの背景色。 purple_200 は、ナイトモードでの purple_500 の代替。 アプリを起動した時の一瞬の背景色は、colors.xml の中に、 <color name="launch_bg">#FF000000</color> themes.xml の中の style タグの間に、 <item name="android:windowBackground">@color/launch_bg</item> (ベタ色ではなく、画像も入れたければ、drawble を指定することもできるらしい。) あと、ナイトモード(?)の方の themes.xml も同様に。 実行中の画面上部のタイトルを消すためには… MainActivity.kt で、 class MainActivity : AppCompatActivity() { を class MainActivity : android.app.Activity() { に変える。 デバイスを傾けても画面が回転しないようにするには、AndroidManifest.xml の中の (application タグではなく) activity タグ開始タグ自体の中に、 android:screenOrientation="portrait" でも、Chrome OS で実行された場合を考慮して?推奨されていないらしい。 ここで実機テスト。アイコンがちゃんと出るのを確認。 WebView を入れていきます。 activity_main.xml で、 Hello World! の TextView のタグをまるごと、次のタグで置き換え。 <WebView android:id="@+id/webview1" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#000000" /> app/src/main/ の中に assets フォルダを作成して、web 版のフォルダを入れる。 MainActivity.kt では、 import android.webkit.WebView import android.webkit.WebViewClient …と、2行インポートして、 lateinit var wv : WebView と宣言して、onCreate() の中の最下部に、 wv = findViewById( R.id.webview1) as WebView wv.webViewClient = WebViewClient() //★↑アプリ内で、リンクをタッチした時に、外部ブラウザが立ち上がらないようにする。 wv.settings.useWideViewPort = true //★html 内の viewport 設定を有効にする。 wv.settings.javaScriptEnabled = true wv.settings.domStorageEnabled = true //★localStorage を使うには、これが必要になったみたい。 wv.settings.loadWithOverviewMode = true wv.settings.allowFileAccessFromFileURLs = true //★SDK 16 以上で、使用可能。 wv.loadUrl( "file:///android_asset/◍◍◍/◍◍◍.html") アプリ内から、Google Play を開いて「自分のアプリ一覧」の画面を表示するには、 MainActivity.kt に、 import android.content.Intent と、インポートして、wv の設定の並びに、 wv.addJavascriptInterface( WebAppInterface( this), "myAndroid") を追加して、 onCreate の下にでも、次の関数を入れる。 class WebAppInterface( private val c: android.content.Context) { @android.webkit.JavascriptInterface fun myGooglePlay(){ try { c.startActivity( Intent( Intent.ACTION_VIEW, android.net.Uri.parse( "market://search?q=pub:www.mameson.com") )) } catch( e: android.content.ActivityNotFoundException) {} } } js 側では、myAndroid.myGooglePlay(); を呼ぶだけ。 プライバシーポリシーのページ等からアプリに帰ってくることができるようにするためには、 MainActivity.kt に、import android.view.KeyEvent と、インポートして、次の関数を追加。 override fun onKeyDown( k: Int, e: KeyEvent?): Boolean { if( k == KeyEvent.KEYCODE_BACK && wv.canGoBack()) { wv.goBack(); return true; } return super.onKeyDown( k, e); } これで以上。ビルド。実機で試す。 公開後に、バージョンアップの<開発中に>、テスト広告を出すには。 (こども向けの場合だけ?) 公開後に、テスト用の広告ユニット ID に戻すと、何も出なくなる。本番用を使い続けること。 MainActivity.kt の MobileAds.initialize( this){} の前に、 MobileAds.setRequestConfiguration( RequestConfiguration.Builder() .setTestDeviceIds( java.util.Arrays.asList( "◍◍◍◍◍◍◍◍◍◍", "◍◍◍◍◍◍◍◍◍◍" )) .setTagForChildDirectedTreatment( TAG_FOR_CHILD_DIRECTED_TREATMENT_TRUE) .build() ) を追加して、以下をインポート。 import com.google.android.gms.ads.RequestConfiguration import com.google.android.gms.ads.RequestConfiguration.TAG_FOR_CHILD_DIRECTED_TREATMENT_TRUE リリースする前に、削除するのを忘れないこと。 それから…… AndroidManifest.xml の中に、 <meta-data android:name="google_analytics_adid_collection_enabled" android:value="false" /> と <meta-data android:name="firebase_analytics_collection_enabled" android:value="false" /> を入れて広告IDの収集と、アナリティクスの収集を禁止する必要がある? privacy policy を入れたのと引き換えに、もう不要? 画面の長押し、ピンチ、ダブルタップ、等をチェック。永続できるか。 長押しを禁止するには、html タグに、 style="height: 100%; -webkit-user-select: none;" ppa と sfa2 は、ちゃんとしているので参考に。 <meta name="viewport" content="width=840, user-scalable=no"> みたいに「数値」で指定する。width=device-width だと、背景が拡大されてしまう? skeleton_3.js の set_target_mag() で、 h = window.innerHeight - 130; //★mypadding で 20 であるところ、150 にしたい。 (canvas よりも上の余白と、下の余白(回転ボタンやバナー領域を含む。)の合計が 150 である場合。) みたいにして、ランドスケープの時の縦幅を押さえ込む。 この計算がぴったりだと、わずかにスクロールできてしまうので、 数字を少しだけ大きくして、その分 canvas を小さくする。
・Android Studio が出る以前は、Eclipse に ADT と SDK を入れて使っていた。 Eclipse は、2015年末で Android のサポート終了になった。 ・AdMob を入れるためには、Google Play Services も入れる必要があり、 そのせいで 64K 問題が起こらないように、Android 4.4 (SDK 19) 以上が必要。 ・マルチウィンドウは SDK 24 以上。 ・WebView をやめて、Chrome Custom Tabs を使った方がいいのか? ・開発者モードにする方法 (設定→端末管理→端末情報へ移動後、最下部の「ビルド番号」連打。) ・Android 4.4 以降の WebView は Chromium になった。 ・KitKat 端末では、AndroidStudio DDMSパネル上のボタン押して 表示されるダイアログ上のボタンから録画開始。
★2017/06/18〜 Android 8.0 AQUOS EVER SH-02J Champagne Gold ■テスト用として、まだ使っている。 2017年10月、モバイル Suica とか全部 iPhone 7 にまとめたので、 もう Android は持ち歩かない。 ★2016/06/21〜 Android 6.0.1 arrows Fit F-01H (2015年10月発売) ■テスト用として、まだ使っている。 MVNO でテザリング不可、Android 7 不可、1年使用。 ★2016/02/25〜 Android 4.4.2 S8N (ウォッチ) ■ ★2015/09/04〜 Android 5.0.2 XPERIA Z3 Compact ■画面破壊。 ★2014/11/04〜 Android 6.0.1 Nexus 5 ■ ★2014/04/03〜 Android 4.2.2 AQUOS PHONE ■
apk を作って、申請するやり方
★広告ユニット ID を本番用にするのを忘れない。 ★「Build」 → 「Generate Signed Bundle / APK...」 → APK を選択して次へ。 パスワード入れる。release を選択。v1・v2 両方にチェック。
★リリースビルドの apk ファイルを念のため、実機に入れてみる。 ★apk ファイルを実機に入れるには、 メールで添付するか、いったんネット上に置くか。 ★アプリ名は、後でいつでも変更できる。 ★説明文は、英語も追加すべき。 最初の5行の文章は常に表示されるので大切。 タブレット用のスクリーンショットも入れよう。 アイコンは 512 x 512 角丸。 ヘッダー画像 1024 x 500 プロモーション画像 180 x 120 テレビバナー 1280 x 720
Crosswalk
★デバイスごとにまちまちな webview を使用せずに、 必ず Chromium を使用するために、Crosswalk を使用する。 ★Android 5.0 以降は Crosswalk 不要。 ★Crosswalk のバージョンを↓ここで選ぶ。 https://download.01.org/crosswalk/releases/crosswalk/android/maven2/ ★app/build.gradle の中に… repositories{ maven{ url 'https://download.01.org/crosswalk/releases/crosswalk/android/maven2' } } dependencies の中カッコの中に… compile 'org.xwalk:xwalk_core_library:17.46.448.10' ★layout は↓このように <org.xwalk.core.XWalkView android:id="@+id/xwalkWebView" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#000000" /> ★コードは↓このように import org.xwalk.core.XWalkPreferences; import org.xwalk.core.XWalkView; public class MainActivity extends ActionBarActivity { private XWalkView xwv; @Override protected void onCreate( Bundle savedInstanceState){ super.onCreate( savedInstanceState); setContentView( R.layout.activity_main); xwv = (XWalkView) findViewById( R.id.xwalkWebView); xwv.load( "http://www.yahoo.co.jp", null); xwv.load( "file:///android_asset/index.html", null); // turn on debugging //★↓ これいるの? XWalkPreferences.setValue( XWalkPreferences.REMOTE_DEBUGGING, true); } @Override protected void onPause(){ super.onPause(); if( xwv != null){ xwv.pauseTimers(); xwv.onHide(); } } @Override protected void onResume(){ super.onResume(); if( xwv != null){ xwv.resumeTimers(); xwv.onShow(); } } @Override protected void onDestroy(){ super.onDestroy(); if( xwv != null){ xwv.onDestroy(); } } }


AdMob
広告配信を停止されたアプリは、新バージョンを公開してから48時間後ぐらいに、 広告配信が再開される?




THREE.js
image を描画した canvas をテクスチャにすると、Chrome で CORS 制限。 canvas をテクスチャにして、後でそのテクスチャを変えたかったら、ctx だけを保存しておけばよい。 ctx に描画した後で、mymesh.material.map.needsUpdate = true; する必要がある。


Blender
Blender から THREE.js のために json で書出し THREE のバンドルの中にあるアドオンを Blender の .app の内部にコピー。 書出す際、Vertices と Faces と Face Materials だけチェックすればいい。 2本指ドラッグで、見る角度を変える。9キーで反対側から見る。 2本指ピンチで、拡大縮小。 shift + 2本指ドラッグで、見る位置を変える。 tab で、オブジェクトモード ⇔ 編集モード。 モディファイアー、マテリアル、テクスチャ T キー、N キーで、メニューが出る。 matcap したかったら、全部グレーで表示されるモードで。 backface culling は、マテリアルの中にある。 タイムライン上にキーフレームを打つ方法は、 3D View で、ボーンを選択して I キー → LocRot。 Dope Sheet で、アクションを作る。 新規アクションを製作したら F ボタンを押しておく。 NLA Editor で、アクションを使う。 2つのストリップを選択して shift + T でトランジション。 ストリップを選択して shift + D でデュープ。


Adobe Animate
as でできた fla ファイルを開いて、 canvas 用にパブリッシュしてくれるのかと思ったら、違っていた。 そうではなくて、canvas 版の新しい fla ファイルを作ってくれる。 その際、スクリプトはコメントアウトされる。自分で js に書換える必要がある。 コメントアウトならまだいいけど、削除されることもある。 その場合は記憶に頼って js で書き直す必要がある。 ムービークリップは勝手にちゃんとアニメーションしてくれる。 ボタンは勝手にマウスに反応してくれる。 手間なく、良い感じになる。 mouseleave の直後に、一回だけ stagemousemove が発生するのが困る。


unity
↓ 関数などのリファレンス https://unityads.unity3d.com/help/unity/api-unity ↓ unity Dashboard https://dashboard.unity3d.com/ Box2D の DebugDraw みたいにコライダを表示してくれる。 レガシーシェーダを Find() している場合、 そのシェーダを Always Include のリストに含めておかないと、実行時に Find() できない。 iOS には角アイコン、Android には角丸アイコン。 1個だけテクスチャみたいに用意して、全部のサイズに貼付けできる。 Product Name は、アイコンの下に出るタイトル。 これを入力した瞬間、Package Name に反映されてしまうので直す。注意。 Unity Ads Window → Package Manager で、Unity Monetization を Import する。 チェックボックスは、全部チェックで。 Unity Developer アカウントは当然。 unity Dashboard で Placement を作る。 バナー広告。Android では出る。iOS では、no fill。 どちらでも、Advertisement.GetPlacementState( placement_ID) は、 最初は、PlacementState.NotAvailable で、 すぐに、PlacementState.Ready になる。 (iOS では no fill なのになぜか、PlacementState.Ready になる。) リスナは、 ・no fill の場合、OnUnityAdsReady() が4回呼ばれる。 (no fill なのになぜか ready が呼ばれる。) ・バナーが表示された場合、OnUnityAdsReady() が計8回呼ばれる。 その後、何度別のバナーに切替わっても、リスナは呼ばれない。 ・game_ID や placement_ID を間違えている場合、OnUnityAdsDidError() が呼ばれる。 no fill であったことを感知することができない? いつまでも、Advertisement.Banner.isLoaded が false であれば、no fill だということか。 600フレーム (10秒) (FPS によるけど) ずっと、isLoaded が false だったら、AdMob へ。 Unity Ads を hide() しても、さっきまで表示されていたバナーが消えるだけで、 30秒ぐらいたつと復活して、次のバナーが表示される。AdMob の上にかぶってしまう。 リスナを remove して、クラスのインスタンスに null を代入すれば、もう復活してこない。 しばらく no fill だったら、AdMob にする。 github から、GoogleMobileAds-v5.2.0.unitypackage をデスクトップにでもダウンロード。 Assets → Import Package → Custom Package で、それを import。 Android の場合、依存関係を resolve。 iOS 書出し。 初回公開時に Metal じゃなかったアプリは、 バージョンアップ時に Metal 化することが Apple に認められないのか。 違うか。Metal を追加することはできる、Metal オンリーにすることは認められない。 永遠に GLES3 と GLES2 を含める必要がある。 unity 上では deprecated と出るけど、しょうがないから含める。 Build Settings の Player Settings をよく見る。 Xcode で、 TARGETS の General の中で、Display Name、Bundle Identifier、Version、Build、 Device Orientation を設定。 それと、Status Bar Style は、ステータスバーの文字の色、 Dark Content なら黒、Light Content なら白、これは LaunchScreen の短い時間だけ有効。 それと、TARGETS の Signing。 Firebase を入れるなら、CocoaPods 使う? 青アイコンではなく白アイコンで Xcode を開く。 Android 書出し。 64ビットに対応するには、Scripting Backend を IL2CPP にして、ARM64 をチェック。 Build Settings の Player Settings だけで全部できる。 Android Studio も使わず、いきなり1本の .apk ファイルが完成する。信じられないほど簡単。 アップロードした apk ファイルを取り消した買ったら、アーティファクトの画面で削除することができる。 macOS 10.15.6 にアップデートした直後からか? unity を起動したら、jdk を入れてくださいというモーダルなアラートが出たので、 最新の jdk14 を入れたら、Android ビルドできなくなったので、 仕方なく jdk8 を入れて、jdk14 は消したら、直った。 どちらの jdk も oracle のサイトからダウンロードしたもの。 AdMob と Firebase を入れたらビルドは成功するのに実機で動かなくなった。 Assets/Firebase/Editor/AppDependencies.xml の中を見ると…… androidPackage spec="com.google.android.gms:play-services-base:15.0.1" Assets/GoogleMobileAds/Editor/GoogleMobileAdsDependencies.xml の中を見ると…… androidPackage spec="com.google.android.gms:play-services-ads:12.0.1" と、書いてあったので、両方とも 15.0.1 に揃えたら動くようになった。


Illustrator
★cs5 で保存すべき。cs2 で保存すると、矢印がアウトライン化されてしまうから。


Photoshop
★クリックした位置にあるレイヤーが選択されるようにする方法。 画面上部のオプションバーの中にチェックボックスがある。 ★背景を消す方法。ウィンドウ → アプリケーションフレームのチェックを外す。 ★カンバスが変な角度に回転してしまって、まっすぐに戻すことができない時、escキーで直る。 ★パスと画像を同時にコピペするの、どうやるんだろう。


Mac
Finder で、ドットで始まる名前のファイルを表示させる方法 defaults write com.apple.finder AppleShowAllFiles true killall Finder (非表示に戻すには、true を false に。) 意外なことに Finder が CPU を大量消費して熱が出る Macintosh HD → ライブラリ、ではなく、 Macintosh HD → ユーザ → ライブラリ、の中の、 com.apple.finder.plist を削除して、本体を再起動。 「ライブラリ」フォルダが表示されない場合、 「ユーザ」フォルダを表示中に、表示メニューの、表示オプションを表示、で、 「"ライブラリ"フォルダを表示」にチェックを入れる。 ……いや、……そうじゃない。 Dock の Finder を option キーを押しながら長押し、「再度開く」で、 すぐに軽くなる。でも数分でまた重くなる。 ……そこで、 command + M、またはウインドウの黄色ボタンで、 ウインドウを「しまう」のが最善の様子。 USB SuperDrive を、ハブ経由で使用する方法 Command + R 起動でリカバリーモード。ターミナルで csrutil disable (SIP を一時的に無効化。) 普通に再起動。ターミナルで sudo nvram boot-args=”mbasd=1″ Command + R 起動でリカバリーモード。ターミナルで csrutil enable (SIP を元に戻す。) 普通に再起動。 macOS Sierra で、Photoshop CS5 を使う方法 下記の2つの『空フォルダ』を作るだけでよい。 ● /System/Library/Java/JavaVirtualMachines/1.6.0.jdk ←拡張子が付いてるけど、これがフォルダの名前。 ● /System/Library/Java/Support/Deploy.bundle ←拡張子が付いてるけど、これがフォルダの名前。 でも、この場所には、通常のやり方では新しいフォルダを作れない。 作るためには、Command + R 起動でリカバリーモード。ターミナルで csrutil disable 今度は普通に再起動して、普通にファインダで上記の2つの空フォルダを作る。 そしたらまた、Command + R 起動でリカバリーモード。ターミナルで csrutil enable あとは普通に再起動。Photoshop CS5 が使えるようになる。 ちなみに Audition CS5 は、これをしなくても macOS Sierra で使える。 (上記を設定済の MacBook Sierra から、新しい MacBook Pro High Sierra に、マシン全体を 移行した後では、ただ単にレガシー Java 6 ランタイムをインストールするだけで使えるようになった。) Illustrator CS5 が、終了時にクラッシュする場合 /Library/Application Support/Adobe/CS5.5ServiceManager を /Library/Application Support/Adobe/CS5.5ServiceManager.bak 等にリネームすればいいらしい。