Cocos Creator

UnityからCocosCreatorへの移行情報まとめ・メモ

Unity → Cocos Creator移行メモ

先日Cocos Creator(以下Cocos)について調べたことについてまとめた記事を投稿しました。

Cocos
UnityエンジニアがCocos Creatorについて調べてみたUnityとよく似たエディタを持つエンジンとして挙げられることのあるCocos Creatorを試してわかったことや調べたことについてまとめています。...

今回の記事では引き続き、開発中アプリ『いつでも本屋さん』の移行作業をしながらUnityとの違いで気づいたことや、ハマったところなどをメモしていきます。随時更新されていきます。

開発中のアプリがCocosと相性が良かったため移行したというだけで、他の開発はUnityを使っています!Unityからの移行を推奨するものではない点はご注意下さい。

Cocosで開発中のアプリはこちらで触ることができます。

エディタ関連

開けない時

CocosDashboardで既存プロジェクトを追加できなかったり、ダブルクリックしても何も起きず開けない時がある。そういう時はpackage.jsonであったり、何かしらのファイルにミスがあって正しくロードできなかった時。エラー表示がないので戸惑うけどそういうもの。

Inspectorの自動保存

マテリアルなどアセットの設定をInspectorで編集したあと、右上に表示されるチェックマークを押さないと保存されない。

フォーカスがInspectorから外れると保存するか聞いてくれるけど、毎回聞かれると面倒なので自動で保存してくれる設定があるので有効にしておくと良さそう。

File > Preference > Inspector > Auto-save when leaving edit

実行中にPrefabを保存してはいけない

Cocosではエディタ内テストプレイ実行中もPrefabはPrefabとして認識されている(Unityではインスタンス化されたオブジェクトになる)

ということで実行中に行った変更を保存するボタンが編集中と同じく表示されるのだが、押してはいけない

保存できるけどテストプレイ停止時にハングし、エディタを再起動する羽目になる。

プレハブの実行中保存は、後述するCocosInspectorというアセットを使うと可能になる。

拡張のnode_modulesがgit管理されない

拡張機能はプロジェクト直下にextensionsディレクトリを作成して入れるが、プロジェクトに自動生成されるgitignoreにより拡張機能に含まれるnode_modulesフォルダがgitで共有されない。

チーム開発や複数PCで困りそう。サイズが大きいケースなどは各自でnpm installを叩いてもいいが、うまくいかないこともありそう(手元では、Win/Mac間で結果が変わってしまった)なのでgit共有する場合はgitignoreを修正してプロジェクト直下のnode_modulesを対象にすればOK。

#//////////////////////////
# NPM
#//////////////////////////
#node_modules/
#↓
/node_modules/

パッケージ関連

UnityやC#ではできたアレをCocosでも出来るようにするために調査中。

パッケージ管理はnpmで

Unityが独自のPackageManagerを導入しているのに対し、JavaScriptベースのCocosではnpmを使用できるのが大きなメリットの1つになりそう ← と思ったが、後述するESとCommonJSによりCocosCreator3.x系では普通にインストールするだけでは動かないライブラリ多数。まだこのあたり整備されておらず難しい部分が多い。

使用にあたりPCへNode.jsをインストールすること(Node.jsが入ってないとWebStormのインテリセンスも効かない)

「ES Modules」と「CommonJS」

jsに詳しい方は読み飛ばしていただいて構わない。JavaScriptのモジュールには大きく「ES Module(以下ES)」と「CommonJS」という規格が存在する。詳しくはこちらを参考に。

Cocosの実行環境は特殊で、ビルド設定はカスタマイズできるけどテスト用のコンパイル環境はエディタ依存なので、IDE上ではエラーがでなくてもCocosのエディタやテストプレイ時はエラーが出るということが多々あり、導入に癖のあるモジュールも多い。

linq

.NetのLINQに相当するものをjs実装してくれているもので、TypeScriptも考慮されておりIEnumerable型が使える。

C#のように言語仕様レベルで統合されているわけではないので Enumerable.from(Array) から書き始める必要はあるが、それ以外はLINQと同じように扱うことができる。

しばらく開発してみた結果として最近のArrayは機能が充実しており、今のところ標準機能だけでも事足りていてlinqの出番はないかも(例えばwhereはArrayのfilter、selectはArrayのmapで書ける)。

標準Arrayのメソッドを使った方が次に解説するrxjsのpipe系メソッドと名前が統一されていて覚えやすいという点も。

ちなみに現在はフォークされてメンテナンスされているが、もともとの実装をしたのはC#でお馴染みのneuecc先生とあって、安心感もある。

一般的には類似の機能が欲しい場合、lodashを使うことが多い模様(LINQ的な機能も含まれている、ユーティリティライブラリ)

RxJS

Rx.NETのJavaScript移植版。

ReactiveExtensionsの全てが必要かというとそんなこともない気がするけどObserverパターンくらいは採用しないと設計がゴチャることはUnityで経験済みである。最低限のところだけ用意しても別にいいけどパッケージがあるならそれに乗っかっておきたい。

UniRxのようにCocosに特化したRxライブラリはまだ見つかっていない(多分ないと思う)のでJavaScriptで広く使われているRxJSを使うのが良さそうな印象。

ただし、先述したESとCommonJSの関係により、CocosCraetor3.xでは導入に癖がある(2.xまでは正常動作していたとのこと)

フォーラムでいくつか情報は見つかるものの、完全に動く方法はなかなか見つからず、Discordなど漁ったら動かす方法がわかった。

ドキュメントにもある程度まとまっている(例はprotobuf)

動いた方法

まずtsconfigでallowSyntheticDefaultImportsコンパイラオプションを追加する。これをやっておかないとCocos上で動く状態になってもコンパイルエラーがでる。

"compilerOptions": {
  "allowSyntheticDefaultImports": true,
}

import-map.jsonを作成し、ProjectSettingsのScriptingの項目から参照を設定する。min.jsもあるが、Cocosのビルド時にも軽量化されるのでどちらでもOK(min.jsにすると20Kbくらいは小さくなった)

{
  "imports": {
    "rxjs": "./node_modules/rxjs/dist/bundles/rxjs.umd.js"
  }
}

この方法で動かすとimportの書き方が変わる。これはCommonJSのものをESで動かすルールなので仕方ない。

import rxjs from 'rxjs';
const { Observable, Subject } = rxjs;

ReactiveProperty

UniRxのReactivePropertyにあたるものが欲しかったのでrxjsをラップしたものとして作成した。npmでインストール可

InversifyJS vs TSyringe

TypeScriptにおけるDIコンテナの有名どころは「InversifyJS」と「TSyringe」がある。

Inversifyのほうがメジャーで情報も多いみたい。どちらも十分に軽量だけど、TSyringeのほうがより機能を絞って軽量に作られているみたいなので、こちらを使ってみたかったのだが…。

一部の機能は使えるがコンストラクタインジェクションをするとエラーになってしまい解決方法がわからなかったので、Inversifyを使うことにした。今のところ問題なく動いている。

rxjsと同様、tsconfigにallowSyntheticDefaultImportsオプションが必要。

Jest

TypeScriptのユニットテストするならJestを使えという情報が多かった。まだ試してない。

CocosCreator2.xへの導入記事はあった。導入先が3.xなのでやり方は変えた方がいいかも?

エンジンに統合されている機能

Unityでは別途パッケージやアセットで導入できるが、Cocosではエンジンに統合されていて別途入れる必要がないもの。ないものばかり目を向けず、あるものに感謝しよう。

Tween

UnityでいうところのDOTweenと似ていて、同じようなことはおおよそ実現できそう。

SafeArea対応

正確にはUnityでもAPIは提供されているが、Cocosではコンポーネント化までされている。

SafeArea内に含めたいUI要素のルートにコンポーネントをつけるだけでOK。

UserDataStorage

これもUnityにはPlayerPrefsがあるけども、皆さんEasySave使ってたり何かしらPlayerPrefsをラップするか保存先を変えたりしているかと思うところ。

CocosはWebではWebStorageAPIを叩き、ネイティブではSQLiteを使用するとのこと。ドキュメントに書かれているとおり暗号化したい時はencryptjsを使用すればそれで事足りそうな印象。

ビルトインシェーダ

一般的なPBRシェーダの他にアウトラインも表示可能なトゥーンシェーダが入っている。よくあるトゥーンシェーダの実装でアウトラインはDrawCallを追加で発生させるので要注意だが、絵作りを試すのに最初から入っているのはありがたい。

その他、新しく追加されたものとして「eye」「heir」「leaf」「skin」「water」などがadvanced/以下に入っている。

WebView

サポートはWeb、iOS、Androidのみ。

アセット関連

CocosDashboardからストアを開いてカテゴリ「Creator Extension」を選択したら、上位表示されている中に良さそうなものがいくつかあった。

CocosのアセットストアCocosのアセットストア

CocosInspector

現在、CocosStoreを開くと常にトップに出てくるということで長らく人気のアセットかと思われる。2.x系と3.x系のサポートを同時に続けている。

$37とそこそこのお値段だが、DrawCall分析ができるということが書かれていたので気になったので購入(CocosではUnityのFrameDebuggerのようなものがないため、何がドローコールを発生させているのかわかりにくい)

結果としてDrawCallの分析は数を表示してくれるだけでFrameDebuggerのようなことはできないが、ないよりはありがたい。前回の記事でも紹介したPreviewerの上位互換という感じだった。

CocosではSceneビューとGameビューにあたるものを同時に表示できないので、テスト実行しないと実行中の表示やStats(FPS、DrawCall、ポリゴン数等)が確認できない。

CocosInspectorのプレビューモードを立ち上げておくと、シーン保存時やスクリプト変更時に自動でプレビューがリロードされるので、変更をすぐ確認できる。

また、後述するPreviewerはテストプレイを実行するだけのものなのでシーン上のオブジェクトを編集して見た目に反映させるということはできないが、CocosInspectorには専用のHierarchyやInspectorのようなものも付いているので可能。

これ自体はCocos本体のエディタ内テストプレイ機能でもできるのでそこまで重要ではないが、先述したようにCocosエディタはテストプレイ中にプレハブを保存するとバグるのに対して、CocosInspectorでは正常に保存できる。このためデバイス解像度で確認しながらUIプレハブを作るような用途には便利そう。

Cocosエディタのテストプレイではできない画面上のオブジェクトを選択したらHierarchyのどこにあるのか表示してくれたり、ドラッグ操作で移動させるツールなども付いている。

適当にどこかで右クリックして出せるメニューの中に「Toggle Mini Mode」があり、有効にするとプレビューだけに絞ることができるので縦型の小さいモニターにプレビュー用として置きっぱなしにしてる。

プレビュー機能としては現時点では最強なのでぜひ入れておきたいアセットの1つになりそうだが、CocosCreator本体の機能が充実してくればいずれ不要になりそうではある。

また問題点としてはエディタと別ウィンドウで立ち上がってしまうのでエディタ内に固定できない。エディタ内に固定したい場合はPreviewerを使うのが良さそう。ディスプレイ2枚以上あって表示領域に余裕があると使い勝手としては良い感じ。

まだ試せていない機能として「build Desktop Mode」があり、Mac/Win共に起動できなかった。「build Mobile Mode」はMacだけ立ち上がったが通常のpreview Modeとの違いがよく分からなかった。

Cinestation

どこからどう見てもCinemachineです。しかも無料。ありがとうございます。

サンプルシーンを動かしてみたけどUnityのCinemachineほぼ完コピで、個人的に欲しい機能はほとんど入ってた。凄い。

問題点としてはCocosエディタ内のテストプレイでは動かないっぽい?CocosInspectorと組み合わせて利用するのが良さそう。

Cocos Creator Code Obfuscation

iOS / Android / HTML5でコードの難読化をしてくれるみたい。

Dynamic Bone

名前のとおり。今のところ使わないけど必要になったら存在するというのは安心感がある。

TextMeshPro

ストアにも有料のものがあるけど、OSSにもあるのでまずはこちらを試した。

普通にTextMeshProとしての機能がひととおり揃っているけど、困るポイントは以下のとおり。

  • ランタイムでの文字アトラス生成未対応(UnityおけるDynamicがなく、事前生成のみ)
  • 最新バージョンのCocosではアトラス生成ウィンドウが開けない(旧バージョンで生成してアセットだけ持ち越すか、開けるように直す。あるいは対応待ち)

ストアに売っているTextMeshProは$30ということで、使えなかった時のことを考えるとちょっと悩んだ。

今回のアプリではランタイムでの生成が必要不可欠なので、そこが対応されていなかったら…と心配だったが、ストアで中国語のスクリーンショットにそれらしい項目があったので勢いで買ってみた。

結果、正解!OSSのものと違い生成ウィンドウが開けない等のバグもなく、動的生成にも対応している(ただしUnityと違ってDynamicにしても文字セットを必ず選択しなければいけない)

ちょっと癖はあるもののデフォルトのテキスト系コンポーネントと比べて多機能で良い感じ。

注意点としてはマテリアルがデフォルト設定のままだとちゃんと表示されない(なぜかマテリアルからシェーダが剥がれてしまってるみたい) → 別プロジェクトでインポートし直したらなぜか治った。表示される場合でも、デフォルトのマテリアルはOVERLAY TEXTUREに無駄なテクスチャが設定されておりこれがビルド時に含まれてしまうため、下記のとおり新規マテリアルを作成した方がいい。

といっても新規マテリアルを作成して、シェーダをtext-mesh-sdf(3D表示で他のオブジェクトに隠れるようにするにはtext-mesh-sdf-depth)に変更したものを適用するだけでOK。

また、Unityと違って設定をマテリアル単位ではなくコンポーネント単位で行うため、おそらくコンポーネント側からマテリアルインスタンスを作って設定を書き換えてる。これだと1つのテキストあたり1回DrawCallが発生してしまうので、中身を改造してマテリアル共通化できる仕組みを入れないとダメな気がする。

素晴らしい仕組みになっていた。マテリアル設定のうちOverlayTextureとGlowの有無以外の設定は頂点データに詰め込んでいるので、共通のドローコールで描画できる。よってこの2つをONにすることだけ慎重に検討すればOK(例えばGlowがONになっているもの同士は共通のマテリアルを使う…というような仕組みにはなっていないので注意。ここはそうしたかったらスクリプトを書き換えた方がいいと思う)

逆に言うとアウトライン(Stroke)やShadowは使わなくても計算が走っちゃうので、要らない場合は負荷対策で削ったバージョンを用意してもいいかもしれない(該当箇所を消すだけなのでまだ楽そう)

試した時にドローコールが発生したのは別々のCanvasに描画していたからで、共通のCanvas内に複数のTextMeshLabelをまとめることでドローコールが共通化された。

Unityのようにマテリアル単位で設定することでプリセット化できるメリットもあるので一概にはどちらが良いとは言えないが、アウトラインやシャドウの色を負荷を気にせず個別に設定できるのはありがたい。プリセット化したい場合はテキスト単体でプレハブ化しておくのが良いと思う。

あとは見慣れないものとして「Slot」という機能があった。サンプルシーンが正しく動いてなくてよく分からなかったけどRichタグでプレハブを指定して差し込むようなことができる…?

TextMeshProで使われているSDFアルゴリズムはテキストを大きく表示したときにキレイに見えるのが魅力なので、3Dでテキストが小さめに表示される場面が多い今回のアプリで恩恵を受ける点は多くなさそうだけどUI関連の項目にメモした縦書きに使えるため導入することにした。

ただしWeb以外のビルドに対応していないためiOS/Androidのネイティブビルドができなくなる問題があった(OSSのほうもWeb/Androidのみ対応)。これについては後述するCapacitorでビルドする方針とした。

導入方法

プロジェクト直下に「extensions」フォルダを作り「text-mesh」の名前にリネームして入れる。

通常の拡張はextensionsの中に入っていれば名前はなんでもいいが、TextMeshProは付属しているサンプルのコードがextensions内を参照するので「text-mesh」の名前にしておく必要あり。

上記以外にもサンプルを見るのにちょっと手間があった。サンプルは Extension > TextMeshPro > Install Samples から導入できる。

まずTextMeshLabelへの参照が外れている。sceneファイルをテキストエディタで開いて正常に参照できているものに振られている「__type__」へ置き換えで解決。

マテリアルも上記のとおり新規で作成したマテリアルに置き換える(これもuuidをテキストエディタで置き換えるのが楽)

ビルド時の注意

フォントアセットをアセットバンドルに含めて明示的にロードしないと、なぜか画面に文字が表示されるまでにとんでもない時間がかかる。

やり方はサンプルに含まれているLoader.tsが参考になる。

easyMenu

こちらは2023年9月にリリースされたOSS。デバッグメニューを実装するためのもので、UnityでいうとUnityDebugSheetが近いと思う。

Super Post Processor

TextMeshProと同じパブリッシャーさんの新作アセット。

エンジン側にもポストエフェクトは実装されてるけどCustomRenderPipelineの有効化が必要で、標準パイプラインと比べて重くなるのとエフェクトの種類がまだ少ない。

このアセットはエフェクト種類豊富で、ひとまず欲しかった全画面ブラーとビネット(こちらではMaskという名前)を入れてAndroidのブラウザでも動かしてみたけどパフォーマンスへの影響ほぼ無かった。こちらもTextMeshProと同じく$30だが価値は大いにありそう。

(11月25日に2Dライティングの機能が追加されましたが、同時に価格も$30 → $120の4倍に…)

デモで使われてるデバッグメニューは先述したeasyMenuで、エディタ上ではエフェクトが反映されないのでデバッグメニューを使って実行テストしながら調整するのが良いと思う。

エディタ内テストプレイなら調整できるけど、まさかの同作者のTextMeshProとの相性が悪く、文字がチラつくという問題が発生する(ブラウザなら発生しないが、FPSを実行中に変更するとチラつくバグがあるよう)

また、このアセットを使うと画面サイズが変わった時やモバイルでは横向き/縦向きが変わったときにサイズ変更が反映されない問題がある。

game-frame

無料のゲーム制作フレームワーク。現在のCocos最新バージョン3.8に対応して作られていて、シーンやUI、サウンドの管理、タスクシステムなどが入っている。

チーム制作で共同編集するときの衝突を避けられるよう、このフレームワークのような設計は何かしら必要なので一から自作する大変さを考えると十分に採用の余地がある充実っぷりに思える。

今回は1人でノンゲームの開発なので不要な機能が多く採用はしないけど、部分的に参考になりそうな点は多々ありそう。

問題点はコメントなども含め全部中国語なので、使うならまず英語ないし日本語に置き換えたい。

Behaviour Creator

BehaviourTreeの制作ツール。2023年5月までしか更新されていないのでちょっと怖いが、便利そう。

BehaviourTreeは標準で入ってないからアセットが必要という点はUnityと同じだ。

Effect Exporter

お前…ShaderGraphじゃないか!

公式サイトがあり、こちらから体験できる。英語設定も可。

なかなか良さそう。でも取り急ぎ必要ないのと最終更新が1年前ということで最新バージョンで動くかわからないのでアップデートがあるまで様子見。

Quick Editor

Godotみたいにエディタ上でスクリプトを編集できるようになるアセット。無料なのにやたらと多機能で凄い。ただv3のサポートがちょっと薄めかも?v2の動画でHierarchyからのドラッグ&ドロップでプロパティ追加してるものがあるけど同じことしても追加されなかった。

シェーダの編集もサポートしていて、それだけでも価値アリな予感。あとはAlt+Fでファイル検索できる機能なども追加される。

TypeScriptのサポートとしては、そのファイル内でimportしてるクラスはチェックしてくれるけど継承元までは見てくれないらしく、インテリセンスは弱め。本格的にコード書く時には使えないけど部分的に修正しながら確認したい時は便利そう。

Unity2Cocos

Unityのアセットストアで購入したアセットは他エンジンで使用できるというライセンスである。せっかくならFBXやテクスチャだけじゃなく、シーンごと移行できたら嬉しいじゃないか!

ということで移行ツールを書いてみた(実験的プロジェクト)

手順としてはUnity上からエクスポートするシーンを指定してCocosのassets以下に入れるファイル群を出力する。

FBX内のMesh参照をUnity側だけで特定することがどうしてもできなかったので、そこだけCocosへのインポート後にPythonスクリプトで紐付けを行う。ただし完全ではなくFBXによっては手動での紐づけが一部必要になることがあるので注意。

VSCode vs WebStorm

結論としてVSCodeとWebStormを併用することにした。通常のコーディングはどちらでも可能だが、VSCodeにはいくつか利点がある。

シェーダのシンタックスハイライト

公式のVSCode拡張でシェーダのシンタックスが効くようになる。同時に「Shader languages support for VS Code」もインストールされてGLSLのシンタックスが効く。

シェーダを書く時はVSCode必須になりそう。惜しいポイントとして関数などの定義元はマウスカーソルを合わせたら表示されるのに定義へのジャンプができない。なんでや…。

ChromeデバッグとVSCodeのみでコンパイル

Chromeでのデバッグ連携とCocosのエディタに戻ることなくVSCode上だけでコンパイル実行する機能が提供されている。まだ使っていないがいざという時に役に立ちそうなもの。

その他の拡張

WebStorm向けにCocos関連の拡張は存在しないが、VSCodeには公式提供のもの以外にもいくつか存在する。

基本的に中国語の説明しかなく、言うまでもなく更新されていないようなものは古いエンジンがターゲットになっていて使い物にならないと思われるので注意。

以下に良さそうなものをピックアップする。

Cocos Creator Prefab

プレハブをVSCodeで開いた時、ちゃんとjson形式として認識してくれるようになる。プレハブ内にある「__id__」という項目を認識して、そのidが示す範囲も教えてくれる。

あとは説明を見るとuuidから参照してるファイルを特定してくれたりするっぽいんだけど、それは動かなかった(Windowsで検証)

TypeScript関連

js自体も触るのが久々なので勉強し直し中。特にC#ではできたアレコレをTypeScriptではどうやるのか中心に。

Collectionについて

C#におけるCollectionの対応。jsではArray(配列)がListを兼ねているような形で、以下のメソッドを組み合わせることでStack、Queueも再現できる。

unshift() 先頭に入れる
shift() 先頭から取り出す
push() 末尾に入れる
pop() 末尾から取り出す

現在のバージョンではDictionalyにあたるMap、HashSetにあたるSetも標準で使うことができる。

ただしC#のListのようにCapacityを指定して要素数と確保メモリを別管理にするようなことはできない。

関数などで配列を受け取るとき、中身を書き換えないならReadOnlyArrayやReadOnlyMapなどを使用すると事故がなくて良い。

importを書きやすくする

最新バージョンに対応している公式サンプルのCocosCyberpankをコーディングの参考にしようと思ったらimportが相対パスの嵐でカオスだった。

エンジン本体を’cc’だけでインポートできるのと同じように、rxjs導入のところで書いたimport-mapへ追記してアプリ用のコードもインポートしやすくしておく。

{
  "imports": {
    "rxjs": "./node_modules/rxjs/dist/bundles/rxjs.umd.js",
    "library": "./assets/library/index.ts",
    "library/": "./assets/library/",
    "app": "./assets/app/index.ts",
    "app/": "./assets/app/"
  }
}

「library」と「app」はUnityで開発するときも採用している個人開発の設計指針で、libraryはディレクトリごとまるっと次のアプリ開発へ持っていけるもの、appはアプリ専用コード。

Unityにおいてはアセンブリを分けることでlibraryからappへの参照を禁止できるが、TypeScriptではできないのでそこは自主規制でやっていく。このあたりのネーミングはお好みで。

tsconfigにもpathsの追加が必要。


  "compilerOptions": {
    /* ... */
    "paths": {
      "library": [
        "./assets/library/index.ts"
      ],
      "library/*": [
        "./assets/library/*"
      ],
      "app": [
        "./assets/app/index.ts"
      ],
      "app/*": [
        "./assets/app/*"
      ]
    }

それぞれindex.tsをインポートしているものはあってもなくてもOK。例えばindex.tsで共通利用したいオブジェクトをexportしておくと、

const appContainer = new inversify.Container()
export { appContainer };

以下のように’app’だけでインポートできる。Cocos本体のccやlinq、rxjsのようなライブラリは、このような形でモジュールをexportしているというわけだ。

import { appContainer } from 'app';

appやlibraryからの絶対パスでアクセスできるように設定したので、以下のように階層を指定してインポートできるようになった。これはapp/net/AppNetworkService.tsにてexportしているAppNetworkServiceクラスをインポートしている例。

import { AppNetworkService } from "app/net/AppNetworkService";

なお機能的にまとまったものであれば階層内にindex.tsを配置することでexportをまとめるのも良さそう。appで毎回書くのはめんどくさいのでlibary側だけ用意することにした。以下はlibrary/net/index.tsを配置した例。

/**
 * Network library modules export.
 */
export * from "./NetworkService";
export * from "./Request";

次のように書ける。

import { INetworkService, IRequest } from 'library/net';

これまで見てきてわかるとおり、TypeScriptにはC#におけるnamespaceがない(正確にはあるけど非推奨の機能)のでスクリプトを配置する階層でnamespaceを表現することになる(各ライブラリのようにexportを駆使してまとめる場合を除く)

それ自体はC#でも指針として言われることなのであまり気にならないけど、個人開発ではusingをたくさん書かなくてもいいように「namespace Library」や「namespace App」で一部を除くコードをまとめていたので面倒さはある。ただ、ディレクトリ構造を今まで雑に作ってた部分も意識してキレイにすることになり、結果的に設計もUnityで作っていたものよりキレイになりそうな予感。

ベクトル演算の注意

Vec2、Vec3など。他にもUnityではstructで定義されたいたものは全体的に注意が必要。

UI実装関連

テキストまわり

OSSとストアでTextMeshProもあるけど、標準テキスト(Label)もフォントサイズ調整すればそこそこキレイに出せるのでグラデーションや効果を細かくコントロールしたいということがなければ標準テキストでも結構いけそう。

今回はゲームではないことからTextMeshProのような表現はあまり必要ないが、3D表示では後述する縦書きを使うためストアで販売されているTextMeshProを、標準テキストで事足りるところはLabelを使っていこうと思う。

キャッシュモード

キャッシュOFFのLabelやRichText等のコンポーネントを大量に置くと、コンポーネント1つごとにテクスチャを1枚生成するので大変なことになる。キャッシュONにすることは多くのケースで必須と思われる。

BITMAPはLabel1つの内容をまるっとアトラスに書き込む。このモードも大量表示だとアトラスがすぐ埋まってメモリ消費がエグそうなので可能なら避けたい。

CHARは1文字単位で1024×1024のアトラスに書き込む。CHARの場合でもサイズ、色、アウトライン等の条件が変わるとアトラス内で別文字として扱われるので、なるべく表示条件を統一した方が少ないアトラスの枚数で済む。

ちょっと試していた感じアトラスを消費しきると警告が出て表示できなくなるかも…2枚目以降に移行してくれない…?(エディタ上だけの挙動かも。もしそんな仕様だったらアトラスのサイズ書き換えてあげないとダメかも)

絵文字

Unityの標準テキストは論外だが、CocosとGodotは割と普通に使えそうな雰囲気。その中でもGodotはエディタ上でも結合文字が表示され、一番ちゃんとした挙動をしているように見える。

結合文字??という方向けの参考記事: Unicodeの世界 ~絵文字の連結編~

Cocosはエディタ & プレビューでは結合文字が分かれて表示されてしまうが、WebGL出力したアプリでは正常に表示された(プレビューでダメなのはエンジンに組み込まれたArialフォントのバージョンによるものだと思う)。

ネイティブでの動作をまだ確認できていないけど、ひとまずWebでちゃんと動くならこれだけでも大きなメリットになりそうな印象。

ただしキャッシュモードをCHARにすると表示できないので注意。絵文字を使いたい時だけBITMAPを選ぶが吉。

縦書きにする

Unity版ではTextMeshProを使い、全体を90度回転させた上で1文字単位でも回転させて戻すことで縦書きにしていた。

調べても全然出てこないので中国語で検索をかけてみたらLabelコンポーネントを改造して実現している人がいたけど、2年前と情報が古くスクショベースの情報しかなくて厳しかった。

上記の改造コンポーネントが「Vertical Label component」という名前でAssetStoreで公開されていたのを見つけたので、今のバージョンでは動かないけど参考にはできそう。

エンジン側では対応していないけどゴリ押しでいける方法が2種類あった。上記の回転する方法と違って中央揃いがキレイにできるのでうまくいけばUnity版よりも良さそう。

Labelの設定で縦書きにする

Labelコンポーネントを使う場合。

  • キャッシュモードをCHARにする
  • UITransformのContentSizeを1など小さい値にする
  • Overflowを「RESIZE_HEIGHT」

OverflowにSHRINKを使えなくなるので決まった領域内で自動サイズ縮小したい時は使いづらいけど、スケールでも調整できる。

フォントサイズを調整して縮小しようとするとアトラスを消費してしまうので注意。なるべくフォントサイズは統一し、スケールで調整した方がいい。

この方法の問題点として、スペースが使用できない。スペースはOverflowしたという扱いにしてくれないようで、スペース手前の文字位置がずれていってしまう。

改行を2つ入れればLineHeightに設定した値相当の空白を作ることはできるけど、半角スペースのように少し間を開けることはできない。

RichTextを使う

RichTextを使う場合はもうちょっとシンプルで、MaxWidthを1にするだけでOK。RichTextはOverflowモードがそもそもRESIZE_HEIGHTしか対応してないみたい。

こちらも同様にスペースを差し込むことができない。リッチテキストは画像の表示ができるので、空白画像を表示することでスペースの代わりにできるのでは?と予想したが、画像を差し込んでも1行あたりの高さはあくまでLineHeightによって変わってしまうので、LineHeightを途中で変更するような仕組みを作らないと高さはいじれなさそう。

泥臭いけどスペースを空けたい位置でLabelもしくはRichTextを分割するという手はあると思う

TextMeshProを使う

ストアで販売されているTextMeshProを使うと、上記2種類の方法で起きる問題を解消できる。

  • Multilineを有効にする
  • UITransformのContentSizeを1など小さい値にする
  • Overflowを「SHRINK」もしくは「RESIZE_HEIGHT」にする

大変ありがたいことにTextMeshProだとSHRINKも使える。また、半角スペースを入れても空白はできないが全角スペースを入れると少しだけ空白ができる。

多めに全角スペースを入れることで半角スペースも全角スペースも表現できる。ちょっと気持ち悪いけどできないよりはマシ。

共通する問題

縦書きにすると、例えば括弧「」や「〜」などはそのまま表示すると正しくない。そのため90度回転させる必要がある。

RichTextは内部でタグごとに複数のLabelコンポーネントを生成するのでパフォーマンス的にはよくないらしいんだけど、タグの仕組みが入っていることに乗っかってRichTextの改造版を作るのが確実そうではある。

文字単位の回転を実装できたら、全体を回転させる方法でも縦書きにできそう(半角スペース使えることを考えたらそっちの方がいいかも)

ただ、これに関してはRichTextの画像表示機能を使って事前に回転させた文字を用意し、それを使うという手もある。色の変更ができないので、色が統一の場合か種類が少ない場合はそれでも良さそう。

なんとストアのTextMeshProはこの問題にも対処できる!文字単位でTransformをいじることができるのだ。

const angle = Math.PI / 2;
for (let i = 0; i < this.label.charInfos.length; i++) {
    let charInfo = this.label.charInfos[i];
    this.label.setCharTransform(charInfo, 0, 0, angle, 1);
}

ということで今回はTextMeshProを買えば全て解決だった。色々と試行錯誤してしまったがさっさと購入すればよかった(DrawCall対策だけ必要そうではある)

なおUnityで採用していた全体を90度回転させてから文字単位で回転を戻して縦書きにする方法だと文字間のバランスが悪いことが多かったため、先述したMultilineを有効にしつつ改行はTextMeshLabelを分けることで表現しようと思う。

RichTextの画像表示機能に関する注意

試してわかったことだが、CocosにはUnityのSpriteAtlasのような自動アトラス生成の仕組みとしてAutoAtlasというものが入っている。

しかし、このAutoAtlasはRichTextの画像表示には使えない(おそらく)

事前に生成したアトラスじゃないと画像の名前を解決できないようで、ツールとしてはTexturePackerに対応している。

CocosCreatorだけどフレームワークでcocos2d-xを選択し、出力されるplistファイルをインポートするとアトラスと認識され、RichTextでも使えるようになる。

3D空間への表示

試行錯誤してしまったが簡単だった。RenderRoot2Dコンポーネント(Canvasの亜種みたいなやつ)を使うことで3D空間に表示できる。

RenderRoot2Dを付けたNode以下に置かれている2D関連コンポーネントを勝手に収集して3Dカメラに描画されるようにしてくれる(レイヤーを3Dカメラで描画できるものに変更する必要あり)。MeshRenderer等のもともと3D描画用のものは無視される。

そのままだと3Dオブジェクトに隠れないので「for2d/builtin-sprite」シェーダを設定した新規マテリアルを作ってPipeline Statesの設定からDepthTestをONにする。

逆に頭の上に出るプレイヤー名とか隠れない方がいい場合は標準マテリアルのままでOK。

ローカライズ

ローカライズ用の機能がエンジンに統合されている。ExtensionManagerよりインストールできるLocalizationEditorは、Google翻訳などと連携して自動翻訳もできるっぽい。

注意点としてLabelコンポーネントは「L10nLabel」を追加することでLocalizationと統合することができるが、RichTextはできない様子。別途スクリプトを用意する必要あり。

L10Nという表記がよく出てくるが「Localization」のLとnの間に10文字あることからそう省略する業界用語らしい。ライオンとかって読んだりもするらしい(参考

3D関連

FBXモデルのインポート

Cocosの3DモデルはFBXとglTF形式に対応している。

現状UnityはglTF公式サポートがないためプラグインでglTFをゴリっとインポートしているケースもあるが、一般的にFBXがよく使われるので「Unity向けに出力したFBXをインポートできるか」をテスト。

これに関しては問題なくインポートできるが、扱うときにUnityとは座標系が異なるので注意。Y-UP(Y座標が上下)な点は同じなので全く違うということはないけど「デカルト右手座標系」というものでZがUnityとは反転する形になる(プラス方向が手前、マイナス方向が奥)。

シェーダに関してもUnityのStandardシェーダ(URPにおけるLit/SimpleLit等)に相当するCocosの「builtin-standard」があるのでテクスチャ等を適切に割り当ててあげれば正常に表示される。

なお先述したようにbuilt-in-toonも利用できる。

元ファイルを持っていてglTF出力できるならそっちの方が絶対良いと思う。自動マテリアル変換でPBR周りの設定を自動で入れてくれるはず。

シェーダ = Effect

Cocosではマテリアルに割り当てるシェーダのことをEffectと呼ぶ。呼び方だけの話なので覚えておけば大丈夫。

LODの自動生成

モデルのインポート画面でLODの自動生成を行うこともできる。Unityにおいては有料アセット等で提供されているもののエンジン本体には組み込まれていない機能なので、Cocos優位な点の1つ。

LODの自動生成設定LODの自動生成設定

画面に表示される比率が12.5%以上の間、ポリゴン数を25%に削ったLOD1を表示する。というような形で3段階の設定ができる。

LOD0は元のモデルをそのまま表示にあたるが、これについても1つ上にあるMeshOptimizerでポリゴン数の削減ができる。

購入したアセットが対象としたいプラットフォームに対してリッチすぎる場合の削減策として、とても良いと思う。

スケルタルアニメーション

今回は使わないけど今後3Dゲーム開発に使用できるかどうかの判断で必要なので試してみた。

アニメーションさせる上で必要な機能は揃っていて、Unityの標準アニメーションシステムのMecanimによく似た作りになっている。

UnityにおけるAnimationControllerは、AnimationGraphと呼ばれる。AnimationClipはそのままの名前。

最もキツイと思った点は、UnityにおけるHumanoidの概念がないためアニメーションのリターゲッティングができない。

UnityではAvatarというアセットに人型のボーンをマッピングすることで、Humanoid用に作られたアニメーションをあらゆる人型モデルで使い回すことができる。

この仕組みのおかげで適当に拾ってきたアニメーションアセットを使いたい人型モデルに適用することができたわけだが、Cocosにはこの機能がないのでおそらくボーンの階層が統一されていないと正しくアニメーションできない。

アニメーションの制作も含めて自分でできる場合なら問題ないけど、いろんなところから拾ってきたアニメーションを組み合わせて使うようなことがしずらい印象を受ける。これはissueやフォーラムにも挙がっていた話なので、今後の追加に期待したい。

物理関連

PhysicsGroup

UnityではGameObjectに設定できるLayerを衝突判定・Raycastの対象にするかどうか判定するためのMaskにも使用するが、Cocosでは分かれている。ついUnityと同じものと思って実装していたら全然動かなくて悩んでしまった。

まずProjectSettingsからCollisionMatrixの項目にグループを追加する。

PhysicsGroupの設定PhysicsGroupの設定

Unityでは動かすオブジェクトにしかRigidbodyをつけないが、CocosではPhysicsGroupを設定するためにつける(つけない場合Defaultになる)

Rigidbodyを付けるRigidbodyを付ける

TypeにStaticがあるので、動かさない場合はこちらを設定しておく。Raycastに渡すMaskはこちらのPhysicsGroupを使用して作成する。

スクリプトから使う場合は以下のようにEnum関数に対して定義を渡しておくと、Inspectorで選択式の表示として利用できるようになる。

import { Enum } from 'cc';
export enum PhysicsGroup {
    DEFAULT = 1 << 0,
    DRAG_COLLIDER = 1 << 1,
    BOOK = 1 << 2,
}
Enum(PhysicsGroup);

@property({ visible: true, type: PhysicsGroup })
private _layer: number;

Reactの導入

後述するCapacitorを利用することにしたため、iOS/Androidネイティブ向けビルドでもHTML/CSSを使用できるようになった。

せっかくなのでReact.jsを導入してUI構築に使ってみることに。導入したサンプルプロジェクトをGitHubに用意した。以下日本語ドキュメント。

Vue.jsも試してみたがビルド後にエラーが出てしまい解消方法が分からなかった。

ビルド関連

ビルド一覧画面

「Project/Build」を実行で最初はビルド設定画面になるが、一度でもビルドを実行すると以下のようにビルド一覧画面に変わる。

ビルドの一覧画面ビルドの一覧画面

これらはビルドのキャッシュが表示されている状態。プロジェクト直下の「build」以下に出力され、自動で作成されるgitignoreの対象となっている。

この一覧画面にて削除、設定を変えてビルド、リビルド、Run等ができる。

新しいビルド設定を作りたい時は左上の「New Build Task」から作成する。

ビルド中もエディタを操作できる

Unityではビルド中はエディタが完全に止まるが、Cocosはエディタの操作が可能。

ただしアセット構成をいじるとビルドに影響が出てしまうため、Assetsウィンドウに自動でロックがかかるみたい。シーンの編集についてもできるけど保存ができない?

後述するようにiOS・AndroidネイティブビルドだとBuildとMakeが分かれていて、Buildが終わっていれば編集しても問題ないのでMake中は作業ができる。

Unityにおける「Switch Platform」の概念はない(プレビューはWebベースの動作)のでビルドエラーさえ出ない状態にできれば、かなり使いやすいかも。

Xcodeからのipa作成、デプロイまわりは流石に自動化必須とはいえ、1人で作ってる分にはUnityほどCI/CD環境の必須さはないかも?

WebGLビルド

PC用・モバイル用

モバイルのブラウザでも動かすためにはモバイル用で出力する必要がある(PC用は動かなかった)

PC用は解像度指定、モバイル用は全画面表示になる(Unityと同じ)

今回のアプリではPC・モバイル共に全画面実行が欲しいので、モバイル用のみ出力して使うことにする。Unityと同じくindex.htmlで切り分けるような作りにすることはできると思う。

SafeAreaの余白を消す

デフォルトで出力されるhtmlはアプリ側でSafeArea対応されてなくても問題ないようにSafeArea外に余白を表示するようになっている。

アプリ側の実装で対応できている場合は出力されるhtmlで、meta name="viewport"のcontentにviewport-fit=coverを追加する。

出力後のファイルサイズ

three.jsで作られたサイトが高速にロードされているのを見てから、UnityのWebGLのあのロード画面からオサラバしたいな…と思っていたので、Cocosならそれが実現可能なのかどうか、まずは調査。

Mplus2フォント(1.7MB)を含んだ状態でUnityとの違いを比較するため、今回のアプリで不要なものを削った最低限の内容で出力してみた。

デフォルト設定でCocosは約10MB

Sceneの設定からSkyboxを除外し FeatureCropping を使用して不要な機能を削除で約5.4MBまで減った。

FeatureCroppingはUnityでいうところのStripEngineCodeにあたるもので、エンジン側の不要なコードを削除する。Unityでも出力サイズの大きさに強く影響するが、削除のされ方を4段階で指定なので削られ方をコントロールしずらく、以前アプリ開発した際はLowよりも強い設定にするとアプリがクラッシュした。

CocosではProjectSettingsから使う機能を選択することができるため、アプリを通して絶対に使わない機能をオフにしていくことで削減効果をけっこう得られる様子。

ただし今回削った中には3DのPhysicsも含まれておりON/OFFで約1.5MBの差が出た。Raycastくらいしか使わないため一旦はMeshRendererのAABBからサイズを取ってRaycastを自作する方向で作ってみることにしたが、多くのゲームでは物理演算が入ってくるため削らない部分ではあると思う。この機能はツールアプリと相性が良さそう。

なおアニメーション関連も今回はTweenで済ませるため、Tweenだけ残して他も削った。

「Dragon Bone」という機能だけは、なぜか使っていないけどオフにするとエラーが出るので外すことができなかった(issueに挙がっていないか等、要確認) なぜか治って消しても大丈夫だった。

Unityのほうは細かく設定したわけではないが、3DCoreでプロジェクト作成し、Skyboxの除外と要らないパッケージ削除などをして、Gzip圧縮して約10MBだった(圧縮しない場合は28MBくらいになる)

UnityはTextMeshProが実質必須なので、ちょっとサイズが膨らむが入れている。なおMplus2の日本語アセットは作成していない状態(ほぼ間違いなく要るけど、一応ランタイムでの生成だけでもいけないことはないため)

開発中のアプリはGzip圧縮で30MBくらいいってたので、何もない状態だと意外とUnityでもサイズが小さいことがわかった(開発中アプリはURP使ってるので、アセット以外それでちょっと膨らんでるかも)

ロードに関しては、初回のダウンロードはサイズの違い相当にCocosが早いくらいだったのでアセット削減の仕方など、工夫ができればより出力サイズが小さい方が良さそう。

1度でもダウンロードしてキャッシュが効くと、Cocosはめちゃくちゃ早い。おそらくCocosは最適化されたjsがそのまま入っているため、ブラウザのキャッシュとの相性が良い。

あとはUnityに関しては今の料金改定プランで想定されている内容だとPlusプランが消えるため、ロゴを消すためにProが必要ということになる。これは悩ましいものを感じるので、ロゴ表示分も体感ロード時間として乗ってしまうかも。(料金改定の変更により、Personalでもロゴ消せると発表された)

jsをベースとしたTypeScriptで作るということもあって、Web出力とはかなり相性が良いように感じる。出力サイズをコントロールしやすい分、多少の苦労はしてでもWeb版だけCocosで作るとかは全然アリな印象(逆にネイティブはライブラリ不足など解消が大変そうな印象)

Asm.jsの削除

「Cull Engine Asm.js Module」を有効にすると1.5MBほど削ることができた。WebAssemblyに対応していないブラウザ用のためのファイルで、2023年現在主要なブラウザの多くは対応されているのでONでいいんじゃないかと思う。

cc.jsの圧縮

エンジン本体コードのファイル。gzip圧縮することで1792KB → 449KBになったので、圧縮対象にするならこのファイルがいいと思う。

ビルドに含まれるFeatureによっては「_virtual_cc_xxxx.js」のような名前のファイルが肥大化して、cc.jsは小さくなる。

なおCocosはUnityのような圧縮しての出力をサポートしていないので今回は圧縮をかけていないが、アセットなど肥大化した場合でもGzip圧縮と展開のワークフローを追加してあげることで、もっと削減することもできそう。

Androidビルド

NDK / SDK設定

Unityのようにモジュールとして一緒にインストールはできないので、Preferencesにてパスを設定が必要。

NDK・SDK設定NDK・SDK設定

Unityが入っているならインストールされているものを指定してあげれば良いのでは。と思ったが、後述するようにAndroidStudioでビルドするため、JDKとAndroidStudioのインストール、適切なNDKとSDK設定が必要。詳しくは以下のドキュメント参考。

ここでNDK・SDKのパスが間違っていてもビルドは通るので注意。AndroidStudio側の操作に移ってからエラーになる。

Makeでエラー

CocosのAndroidビルドは「Build → Make → Run」の順で、このうちBuildはWebの場合と出力内容はほとんど変わらない(gradle関連の入ったプロジェクトが出力されるくらい)

Makeは実行開始してすぐエラー。JavaRuntimeがないと言われたのでAppleSilicon用のものをインストール

Makeは開始されるようになったが途中でコケた。

推奨はAndroidStudioでビルドっぽい?

ドキュメントの手順ではAndroidビルドでもMakeは押せるようになってるけどapk/abb出力はAndroidStudioを使うよう書かれているので、今のところはそちらに従っておくことにする。

開発環境を途中からWindowsに変えたが、WindowsのAndroidStudioで無事にビルド、アプリ実行できた。

iOSビルド

Xcodeがインストールされてる?などと言われてしまったので、まずはそこから調査

CapacitorでiOS・Andridビルド

TextMeshProや、SuperPostProcessorもiOS/Android対応とストアには書かれていたもののエラーが出てしまったりとWeb以外でアセットが動かない問題などもあったため、今回はCapacitorでiOS/Androidビルドを行う方針にした。

ビルド手順などは以下の記事にまとめている。

CocosCreatorとCapacitorでiOS・AndroidアプリをビルドするCapacitorを使用することで、WebアプリをiOS/Android向けにビルドすることができます。今回はCocosCreatorで制作したWebアプリをCapaitorを経由してiOS/Android向けにビルドしてみました。...

縦書きを標準のLabelを拡張して実現するか、SuperPostProcessorのバグ修正を待つか自前で作成したものに置き換えればCocos標準のiOS/Androidビルドも可能ではあるので、どうしても必要になったら追って検討したいと思う。あるいはWebはCocos、ネイティブはUnityと使い分けるか。

アプリが伸びたら検討しても良さそうだけど、今のところは細く長く運用できたらいいなと思っているのでシンプルにWebを軸にCapacitorを併用する方向でいきたいと思う。

インスタントゲーム

インスタントゲームまわりについてはこちらの記事がけっこう参考になった。

Cocos3.8で出力できるプラットフォーム一覧がこちら

Cocosで出力できるプラットフォームCocosで出力できるプラットフォーム

さすが中国という感じで、Unityで出力できるラインナップと全然違う。

選択肢が多すぎてどう調べたらいいのかもまだ検討つかないけど、世界的に使われてるアプリも並んでるのでちょっと気になる。特にByteDance(TikTok)は次に個人開発するゲームと相性が良さそうなので気になっている。個人レベルで(言語の壁も超えて)パブリッシングできるようなものなのかは知らないけど…。

アプリのインストールなしですぐ遊べるようにする仕様から、どのプラットフォームもアプリサイズを削らないと公開させてもらえない。CocosがFeatureCropping等で出力サイズを削れるようにしている理由がよくわかった。

ドキュメントによるとByteDanceは20MB、WeChatはなんと4MBだそう。Unityでは絶対ムリだが、このあたり解消しようと5年前くらいにスタートしたのがProjectTinyUnityだ。

この市場自体に今後の発展性があるのかどうかはまだ掴めていないけど、いずれTinyUnityが正式リリースされる日が来るかもしれないとしても今はCocosなどで手を付けておくのは知見が広がりそうな予感。

TinyUnityが音沙汰ないことにしびれを切らした(かは知らないけど)WeChatが本気を出し、UnityのWebGLビルドを軽量化する方法をまとめてくれている(ただしドキュメントは全て中国語)

AppClipとGooglePlayInstant

実はAppStore、GooglePlayもインスタントアプリに対応している(なんか聞いたことあったけど全然知らなかった)

他のプラットフォームとは目的が異なり、すぐにさわれる体験版を提供することで完全版アプリのインストールに繋げるのが目的になる。

AppClipは不明だが、GooglePlayに関しては公式のドキュメントも存在していてしっかり対応できる様子。ぜひ体験版を用意してみたい。

プレイアブル広告

軽量なランタイムを出力できるCocosはプレイアブル広告にも向いている。広告を出力するための専用アセットがあった

SmartAd Playabs Ads Pack Tool

無料。サポートしている広告プラットフォームは以下のとおり

Facebook / Google Ads / TikTok / Mintegral / UnityAds / AppLovin / IronSource / Kwai / Vungle

ただし現時点で1年ほどアップデートがかかっていないため動作に期待しずらい。

super-html

このアセットなんだろう?と思っていたらこちらも広告出力用のものっぽい。こちらは有料。

2023年11月3日にアップデートがあり、以下のプラットフォームがリストされていた

applovin、facebook、google、ironsource、mintegral、pangle、unity

プレイアブル広告として動かす条件は単一のHTMLファイルにまとめることらしく、このアセットはコードの難読化も含めてやってくれるらしいのでプレイアブル広告を作成するときは有力な選択肢になりそう。

なおIronSourceやUnityAdsのファイルサイズ上限について少し調べたところ非圧縮状態で5MBということだった。FeatureClippingで不要なエンジン機能を削除した上で、テクスチャなども圧縮して削減する必要があると思われる。