- 投稿日:2020-07-28T19:27:38+09:00
ゲームアプリ制作におけるベジェ曲線の可能性
はじめに
アプリの絵がぼやける理由
アプリを遊んでいて「絵がぼやけてる」と感じたことはありませんか?
解像度の大きな端末に乗り変えたら、お気に入りのアプリの表示がボケボケで、切ない気分になった人は少なからずいるのではないでしょうか?これは、アプリの画像素材のほとんどがペイント形式で作成されているからです。
ペイント形式とは画素を並びで保持する形式なのですが、拡大や回転による品質の劣化が起こります。例えば、1280x720 のキャラクタの立ち絵がペイント形式で用意されていて、アプリが画面全体に表示させたとします。
解像度 1280x720 の端末であればクッキリと表示されますが、2560x1440 の端末だと画素が引き延ばされて画像がぼやけてしまうことになります。アプリの文字がぼやけない理由
さて、こうも思ったことはありませんか?
「文字だけやけにきれいに見える」
これは、文字がドロー形式で表示されているからです。
ドロー形式とは、データとして座標や色情報などを保持しておき、計算で絵を生成する形式のことです。先の例の場合、1280x720 の端末でも 2560x1440 の端末でも、解像度に合わせた素材が実行時に生成されるため、文字がぼやけずきれいに表示されるというわけです。
絵も文字と同じ形式にしたらどうか
ペイント形式のデータが端末によってぼやけてしまうのは、ゲーム制作者にとって悩ましい問題です。
一般的な対策として、低解像度の端末向けに小さな画像、高解像度の端末向けに大な画像を用意しておき、端末の性能に応じて切り替えて利用するというものがあります。ただし、この対応はデータの管理や作成が煩雑になりますし、スマホの性能の向上とイタチごっこになる側面があります。
そこで素朴な疑問です。
アプリの絵素材も文字と同じく、ドロー形式で作成してみたらどうでしょうか?
ものは試しです。ドロー形式でゲーム素材を作成する場合、どんなことができるか可能性を探ってみましょう。ベジェ曲線
ドロー形式の基本要素が「ベジェ曲線」です。
ベジェ曲線とは「座標+アルファ」で曲線を表現する手法です。アンカーポイント
ベジェ曲線では座標のことを「アンカーポイント」と呼びます。
このアンカーポイントを複数置き、順番につなげることでパス(線)を表現します。アンカーポイント0→1、1→2、2→3、3→0の順番でパスが引かれ、菱形が描画されました。
方向線
さて、アンカーポイントだけだとまっすぐなパスしか表現できないので、パスを曲げるための「+アルファ」が必要です。
パスを曲げるには、アンカーポイントに「方向線」と呼ばれる2種類のベクトルを付与します。*誘導ベクトル*
1つ目の方向線は、そのアンカーポイントから「出発するパスを誘導する」効果を持ち、パスの引き始めほど強く影響します。
ここでは「誘導ベクトル」と呼びます。↓ 図3:アンカーポイントに誘導ベクトルを追加(4本の青い矢印)
それぞれのアンカーポイントから伸びる誘導ベクトル(青い線)に沿って、パスの描き始めが曲げられました。
*誘引ベクトル*
2つ目の方向線は、そのアンカーポイントに「到着するパスを誘引する」効果を持ち、パスの終わり際ほど強く影響します。
ここでは「誘引ベクトル」と呼びます。↓ 図4:アンカーポイントに誘引ベクトルを追加(4本の赤い矢印)
誘導ベクトルとは逆に、アンカーポイントの誘引ベクトル(赤い矢印)に引っ張られて、パスの終わり際が曲げられました。
誘導ベクトルと誘引ベクトルは、1つのアンカーポイントに両方指定することもできます。
アンカーポイントと方向線を工夫することで、自由自在にパスを描けるのがベジェ曲線の強みです。
では、ベジェ曲線を土台とした描画環境を実装し、アプリ上で表示できる絵素材を作成してみましょう。
素材作りの流れ
素材(画素)の生成はベジェ曲線で行うわけですが、それ以外のところは一般的な 2D 素材作成の流れを想定します。
いろいろなゲームで採用されている Live2D っぽく、パーツ別に素材を作成して組み合わせ、1枚の絵としてのバリエーションを出すことを目標としましょう。下絵の作成
パーツわけした下絵を作成してエディタに取り込みます。
(※Live2D のサンプルを真似してミクさんを作成してみます)↓ 図7:パスを作成する際の下絵(パーツ単位で素材を作るのでバラバラ)
パーツ素材の作成
エディタ上で下絵をトレースしていきます。
パーツ素材の組み合わせ
バラバラに作成したパーツを組み合わせることで、絵を構築していきます。
サンプルアプリ
作成した素材の表示を確認するためのサンプルアプリが GitHub へアップロードしてあります。
iOS / Android それぞれのプロジェクトがあるので、興味のある方はビルドして実行してみてください。
サンプルアプリで確認できる表現
ベジェ曲線的に実装した表現手法として、サンプルアプリで確認できる内容は下記となります。
移動値補正パラメータ
アンカーポイントは移動値(風やキャラクタの動き)からの影響されやすさを保持します。
髪や布などのアンカーポイントにこの補正値を高めに適用することで、キャラクタの動き(移動)に沿った変化をさせることが可能です。
(※サンプルプアプリの「SWY」と「MOV」ボタンの機能です)↓ 図13:移動値による髪などのなびき(移動方向を目で追います)
体型補正パラメータ
アンカーポイントは体型パラメータからの影響されやすさを保持します。
各アンカーポイントにこの補正値を適用することで、体を細くしたり、背をのばしたりすることが可能です。
(※サンプルプアプリの「H」と「V」ボタンの機能です)パーツ毎のサイズパラメータ
パーツはサイズパラメータからの影響されやさを保持します。
各部位のサイズをまばらに変え、パーツ毎に個性の表現が可能です。
(※サンプルプアプリの「S」ボタンの機能です)↓ 図16:サイズパラメータによる変化(ツインテールを大きめに補正)
パーツ毎の回転パラメータ
パーツは回転パラメータからの影響されやすさを保持します。
腕や肘などの稼働領域の影響されやすさとなり、絵によって受け入れられる角度を制限することで、関節があらぬ方向へ曲がらないように制限することが可能です。
(※サンプルプアプリの「T」ボタンの機能)擬似的な傾け
絵全体を擬似的に傾けることで、画像に左右/上下に傾いた印象を与えることが可能です。
(※サンプルプアプリの「LR」と「UD」ボタンの機能)ストロークの切り替え
パスの描画ブラシを変えることで、ストロークの差し替えが可能です。
(※サンプルプアプリの「STR」ボタンの機能)パレットの切り替え
厳密にはベジェ曲線の機能というわけではありませんが、処理の単純化と容量を浮かせるために画像をパレット形式で管理しているため、パレット差し替えが可能です。
(※サンプルアプリの「COL」ボタンの機能)素材の差し替え
こちらも厳密にはベジェ曲線の機能というわけではありませんが、ベジェ曲線(ドロー形式データ)の場合、ストロークや塗りの表現を統一できるので、素材の差し替えの自由度を高められます。
(※サンプルアプリの「COSTUME」ボタンの機能)課題
今回作成したベジェ曲線環境ですが、ゲーム制作へ導入するには大きな課題が2つあります。
描画速度
とにかく画像の生成が遅いです(※端末の性能と解像度のバランスにもよりますが、1回の画像生成に数十ミリ秒かかっています)。
特に、大きなサイズでリアルタイムにアニメさせようとすると、処理落ちがひどいことになります。描画速度の改善が課題となります。
一方で、1枚絵ジェネレータとして、アプリ起動時にキーフレーム別に画像を生成しておき、その後、静的に参照する使い方であれば問題なく導入できそうです。
データ作成用のエディタ
今回、サンプルデータの作成はデバッグ画面レベルの簡易エディタで行いました。
が、現段階では、とても人様に提供できるレベルではありません(使いづらい&動作が安定しない等)。ゲーム制作においては、大量のデータをいかに効率よく作成できるかが重要です。
エディタの良し悪しで工数は大きく変わってくるので、使いやすいエディタの準備が課題となります。最後に
さて、ベジェ曲線によるゲーム素材作りを試行錯誤してみましたが、一番手応えを感じたのがストローク描画の部分です。
例えば、ツインテールだけを20%ぐらいのサイズまで縮小した場合でも、アンカーポイントの座標がかわるだけで描かれる線の太さはかわらず、ほかのパーツと共存させても違和感がありませんでした(※ペイント形式データの場合、一部のパーツを20%に縮小しようものなら、主線等が潰れて絵的なニュアンスが変化してしまい、他のパーツとの間で見た目に違和感がでます)。
パーツ毎に派手な拡大&回転をしてしまっても他のパーツと馴染んでくれるのは、動的に線を描くベジェ曲線ならではの強みだと思います。さらに、ストローク描画の際、1画素単位をプログラム側で管理できるので、そこで色々な表現を加えることも可能そうです。
例えば、細密画のように主線に沿ってタッチをつけたり、テクスチャブラシのような表現が考えられます。アンカーポイントの補正を含め、ベジェ曲線によるゲーム作成には十分な可能性がありそうです。
少ない素材&手間によりお手軽に表現のバリエーションがだせるよう、模索していこうと思います。おまけ
GitHub の ReadMe に掲載している、大きめな動作例の画像です。
よろしければご覧くださいませ(※一枚一枚のサイズが大きいので別窓で開きます)。
- 投稿日:2020-07-28T16:21:49+09:00
Toolbarのメニューの文字の色だけ変える方法
AndroidのToolbarのメニューの文字の色を変える方法でうまくいく方法が見つからなかったのでメモです。
やりたいこと
やり方
styles.xmlに以下を記述
styles.xml<style name="BaseToolbar" parent="@style/Widget.AppCompat.Toolbar"> <item name="android:theme">@style/ToolbarTheme</item> </style> <style name="ToolbarTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- Toolbar Title Color--> <item name="android:textColorPrimary">@color/blue</item> </style>追加したstyleをtoolbarに適用
<androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" style="@style/BaseToolbar" app:layout_scrollFlags="enterAlways" />ここまでで、タイトルとメニューの色が変わるのでこんな感じになります。
最後に、タイトルの文字の色を変えます。
Fragmentでtoolbar.setTitleTextColor(this.resources.getColor(R.color.black))を記述すればタイトルだけ色が変わるので目的達成です。
ハマったこと
ググって出てくるのは、
<style name="BaseToolbar" parent="@style/Widget.AppCompat.Toolbar"> <item name="android:theme">@style/ToolbarTheme</item> </style> <style name="ToolbarTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- Toolbar Title Color--> <item name="android:textColorPrimary">@color/black</item> <!-- Toolbar menu overflow icon color --> <item name="android:textColorSecondary">@color/blue</item> </style>みたいな古い情報ばかりでした。
textColorSecondaryの内容がメニューの色に反映されるってことでした。
ただ、実際は、戻るの矢印に反映されてしまい、メニューはtextColorPrimaryの色でした。解決方法
textColorPrimaryでメニューの色が変わるので、タイトル共々変えてしまってタイトルの色はコードで再度指定してしまうと言う方法で解決しました。
もっといい方法を知っていました教えてください。
- 投稿日:2020-07-28T11:35:37+09:00
Re:ゼロから始めるbuild.gradle.kts生活
はじめに
タイトルにあるように
build.gradle.kts
をAndroidアプリ開発で使っていこうと思います.
また,マルチモジュールでプロジェクトを運用する際に同じような記述を共通化させる部分の紹介もしたいと思います.最初,Android StudioでNew Projectすると
build.gradle
というGroovyで記述されたスクリプトファイルが生成されます.
Groovyはあまり聞き慣れませんが,Javaから派生した動的型付けのプログラミング言語です.
build.gradle
でGroovyを使う際のイマイチな点として以下のものが挙げられると思います.
- 普段はJavaやKollinで開発するのにbuild.gradle
だけGroovyで書かなければいけないく,JavaやKotlinで得た知見をbuild.gradle
に活かせない
- 補完が効かない大きくこの2つかなと個人的な見解として思っています.
DroidKaigi2019のセッションでも紹介されていました.これを解決するために
build.gradle
をKotlinで記述してAndroidアプリ開発をしようというときに出てくるのがbuild.gradle.kts
です.今までGroovyで記述していたbuild.gradle
をKotlinで記述することができるようになります.ではやっていきましょう.
Re:ゼロから始めるbuild.gradle.kts生活
やっていく
Step1
まずは,プロジェクト直下に
buildSrc
という名前でdirectoryを作ります.
その中にbuild.gradle.kts
ファイル を作って以下の中身を書きます.buildSrc/build.gradle.ktsplugins { `kotlin-dsl` } repositories { jcenter() }書いたらsync nowします
色々ファイルが生成されますStep2
次に
buildSrc/src/main/java/dependencies
となるようにpackageを作ります
dependencies
の中にLibs.kt
作ります.
この中にはプロジェクトで必要なライブラリの依存関係を記述します.
よくDep.kt
という名前で作りますが,今回は後々Dependencies.kt
というファイルを作るときに名前が被るので適当にLibs.kt
という名前にしておきたいと思います.
Libs.kt
の中身はこのように書いていきます.ここはDroidKaigi/conference-app-2020のDep.ktが参考になると思います.Dep.ktobject Dep { object Kotlin { val stdlib = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61" } }Step3
buildSrc
の中のbuild.gradle.kts
に追記buildSrc/build.gradle.ktsplugins { `kotlin-dsl` } repositories { jcenter() google() // 追記 } dependencies { implementation("com.android.tools.build:gradle:4.0.1") // 追記 }
com.android.tools.build:gradle
のバージョンは適宜変えてください.これを入れることでこのあとマルチモジュールした際の依存関係の共通化をするコードを書くことができるようになります.
Step4
次にもともと
build.gradle
にあったandroid
ブロックを共通化させるためのコードを書いていきます,
マルチモジュールでプロジェクトを作るとき,デフォルトではこの部分はすべてのモジュールで同じことを書かなければいけません.
そこで,buildSrc/src/main/java/dependencies
の中にBuildConfig.kt
というような名前でファイルを作成します.
中身は以下のように記述します.BuildConfig.ktpackage dependencies import com.android.build.gradle.BaseExtension object BuildConfig { const val applicationId = "com.example.app" const val versionCode = 1 const val versionName = "1.0" const val androidTargetSdkVersion = 29 const val androidCompileSdkVersion = 29 const val androidMinSdkVersion = 23 const val buildToolsVersion = "29.0.3" const val testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" const val consumerProguardFiles = "consumer-rules.pro" } fun BaseExtension.baseExtension() { compileSdkVersion(BuildConfig.androidCompileSdkVersion) buildToolsVersion(BuildConfig.buildToolsVersion) defaultConfig { minSdkVersion(BuildConfig.androidMinSdkVersion) targetSdkVersion(BuildConfig.androidTargetSdkVersion) versionCode = BuildConfig.versionCode versionName = BuildConfig.versionName testInstrumentationRunner = BuildConfig.testInstrumentationRunner consumerProguardFiles(BuildConfig.consumerProguardFiles) } buildTypes { getByName("release") { isMinifyEnabled = false proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } } }Step5
実際に使ってみます
appにあるbuild.gradle
をbuild.gradle.kts
にrenameします.
中身は以下のように書き換えます.
一番下のdependencies
のブロックは今はおいておきます.app/build.gradle.ktsplugins { id("com.android.application") id("kotlin-android") id("kotlin-android-extensions") } android { baseExtension() defaultConfig.applicationId = BuildConfig.applicationId }先程の
BuildConfig.kt
でbaseExtension
という名前で定義したものが使えています.
このように実際に使う際はandroid
ブロック内でbaseExtension()
と一行書くだけで冗長だったコードを共通化することができています.Step6
では次に
dependencies
ブロックについてです.いつも使いたいライブラリの依存関係を定義するところですね.
ここも例えばfeatures/home
とfeatures/settings
みたいなモジュールを作った際に使うライブラリの依存関係が同じようになることがよくあります.
その部分もある程度のベースとなる依存関係は共通化させるようにします.
buildSrc/src/main/java/dependencies
にDependencies.kt
を作ります.
中身は以下のように書いてみます.Dependencies.ktfun Project.baseDependencies(additionalConfiguration: DependencyHandlerScope.() -> Unit) { dependencies { implementation(Libs.Kotlin.stdlib) implementation(Libs.AndroidX.appCompat) implementation(Libs.AndroidX.coreKtx) implementation(Libs.AndroidX.constraint) } dependencies(additionalConfiguration) } private fun DependencyHandler.implementation(depName: Any) { add("implementation", depName) }Step7
では,先程作った
baseDependencies
を使ってみようと思います.
appのbuild.gradle.kts
にdependencies
を追加していきます.
中身を以下のようにします.app/build.gradle.ktsimport dependencies.baseExtension import dependencies.BuildConfig plugins { id("com.android.application") id("kotlin-android") id("kotlin-android-extensions") } android { baseExtension() defaultConfig.applicationId = BuildConfig.applicationId } baseDependencies { }いままで
dependencies
で書いて中にimplementation
を書いていましたが,どこでも使うようなベースになる依存関係はすべてbaseDependencies
だけでかけるようになりました.
もちろんbaseDependencies
の中でそのモジュール内でまた別に定義したい依存関係はimplementation
で適宜追加できます.おまけ
1つのライブラリを使いたいときにそれに関連する依存関係を複数まとめて書きたい場合があると思います.
そのときに,Dependencies.kt
に以下のようなコードを追加してみます.Dependencies.ktfun DependencyHandler.groupie() { implementation(Libs.Groupie.groupie) implementation(Libs.Groupie.databinding) }そうすると,
build.gradle.kts
で今回だとGroupieで使いたい複数の依存関係を以下のように一行ですっきり書くことができるようになります.app/build.gradle.ktsimport dependencies.baseExtension import dependencies.BuildConfig plugins { id("com.android.application") id("kotlin-android") id("kotlin-android-extensions") } android { baseExtension() defaultConfig.applicationId = BuildConfig.applicationId } baseDependencies { groupie() }さいごに
こんな感じで今まで冗長だった依存関係の記述をすっきり,共通化させてしかもKotlinで書くことができるようになります.
みなさんも良きbuild.gradle.kts
生活を.参考記事