20200728のAndroidに関する記事は3件です。

ゲームアプリ制作におけるベジェ曲線の可能性

はじめに

アプリの絵がぼやける理由

アプリを遊んでいて「絵がぼやけてる」と感じたことはありませんか?
解像度の大きな端末に乗り変えたら、お気に入りのアプリの表示がボケボケで、切ない気分になった人は少なからずいるのではないでしょうか?

これは、アプリの画像素材のほとんどがペイント形式で作成されているからです。
ペイント形式とは画素を並びで保持する形式なのですが、拡大や回転による品質の劣化が起こります。

例えば、1280x720 のキャラクタの立ち絵がペイント形式で用意されていて、アプリが画面全体に表示させたとします。
解像度 1280x720 の端末であればクッキリと表示されますが、2560x1440 の端末だと画素が引き延ばされて画像がぼやけてしまうことになります。

アプリの文字がぼやけない理由

さて、こうも思ったことはありませんか?

「文字だけやけにきれいに見える」

これは、文字がドロー形式で表示されているからです。
ドロー形式とは、データとして座標や色情報などを保持しておき、計算で絵を生成する形式のことです。

先の例の場合、1280x720 の端末でも 2560x1440 の端末でも、解像度に合わせた素材が実行時に生成されるため、文字がぼやけずきれいに表示されるというわけです。

絵も文字と同じ形式にしたらどうか

ペイント形式のデータが端末によってぼやけてしまうのは、ゲーム制作者にとって悩ましい問題です。
一般的な対策として、低解像度の端末向けに小さな画像、高解像度の端末向けに大な画像を用意しておき、端末の性能に応じて切り替えて利用するというものがあります。

ただし、この対応はデータの管理や作成が煩雑になりますし、スマホの性能の向上とイタチごっこになる側面があります。

そこで素朴な疑問です。
アプリの絵素材も文字と同じく、ドロー形式で作成してみたらどうでしょうか?
ものは試しです。ドロー形式でゲーム素材を作成する場合、どんなことができるか可能性を探ってみましょう。

ベジェ曲線

ドロー形式の基本要素が「ベジェ曲線」です。
ベジェ曲線とは「座標+アルファ」で曲線を表現する手法です。

アンカーポイント

ベジェ曲線では座標のことを「アンカーポイント」と呼びます。
このアンカーポイントを複数置き、順番につなげることでパス(線)を表現します。

↓ 図1:アンカーポイントを4つ配置(4つの黒い四角)
ss00_00

↓ 図2:アンカーポイントを繋げてパスを構成(4本の線)
ss00_01

アンカーポイント0→1、1→2、2→3、3→0の順番でパスが引かれ、菱形が描画されました。

方向線

さて、アンカーポイントだけだとまっすぐなパスしか表現できないので、パスを曲げるための「+アルファ」が必要です。
パスを曲げるには、アンカーポイントに「方向線」と呼ばれる2種類のベクトルを付与します。

*誘導ベクトル*
1つ目の方向線は、そのアンカーポイントから「出発するパスを誘導する」効果を持ち、パスの引き始めほど強く影響します。
ここでは「誘導ベクトル」と呼びます。

↓ 図3:アンカーポイントに誘導ベクトルを追加(4本の青い矢印)
ss00_02

それぞれのアンカーポイントから伸びる誘導ベクトル(青い線)に沿って、パスの描き始めが曲げられました。

*誘引ベクトル*
2つ目の方向線は、そのアンカーポイントに「到着するパスを誘引する」効果を持ち、パスの終わり際ほど強く影響します。
ここでは「誘引ベクトル」と呼びます。

↓ 図4:アンカーポイントに誘引ベクトルを追加(4本の赤い矢印)
ss00_03

誘導ベクトルとは逆に、アンカーポイントの誘引ベクトル(赤い矢印)に引っ張られて、パスの終わり際が曲げられました。

誘導ベクトルと誘引ベクトルは、1つのアンカーポイントに両方指定することもできます。

↓ 図5:誘導ベクトルと誘引ベクトルで円の描画
ss00_04

アンカーポイントと方向線を工夫することで、自由自在にパスを描けるのがベジェ曲線の強みです。

↓ 図6:試しに描いた歯っぽいもの
ss00_05

では、ベジェ曲線を土台とした描画環境を実装し、アプリ上で表示できる絵素材を作成してみましょう。

素材作りの流れ

素材(画素)の生成はベジェ曲線で行うわけですが、それ以外のところは一般的な 2D 素材作成の流れを想定します。
いろいろなゲームで採用されている Live2D っぽく、パーツ別に素材を作成して組み合わせ、1枚の絵としてのバリエーションを出すことを目標としましょう。

下絵の作成

パーツわけした下絵を作成してエディタに取り込みます。
(※Live2Dサンプルを真似してミクさんを作成してみます)

↓ 図7:パスを作成する際の下絵(パーツ単位で素材を作るのでバラバラ)
ss01_00

パーツ素材の作成

エディタ上で下絵をトレースしていきます。

↓ 図8:目(アイラインと白目)の素材作成
ss01_01

↓ 図9:シャツの素材作成
ss01_02

パーツ素材の組み合わせ

バラバラに作成したパーツを組み合わせることで、絵を構築していきます。

↓ 図10:顔パーツの組み合わせ
ss01_03

↓ 図11:胴体パーツの組み合わせ
ss01_04

↓ 図12:ミクさん完成
ss01_05

サンプルアプリ

作成した素材の表示を確認するためのサンプルアプリが GitHub へアップロードしてあります。

サンプルアプリの環境一式はこちら

iOS / Android それぞれのプロジェクトがあるので、興味のある方はビルドして実行してみてください。

サンプルアプリで確認できる表現

ベジェ曲線的に実装した表現手法として、サンプルアプリで確認できる内容は下記となります。

移動値補正パラメータ

アンカーポイントは移動値(風やキャラクタの動き)からの影響されやすさを保持します。
髪や布などのアンカーポイントにこの補正値を高めに適用することで、キャラクタの動き(移動)に沿った変化をさせることが可能です。
(※サンプルプアプリの「SWY」と「MOV」ボタンの機能です)

↓ 図13:移動値による髪などのなびき(移動方向を目で追います)
ss_06

体型補正パラメータ

アンカーポイントは体型パラメータからの影響されやすさを保持します。
各アンカーポイントにこの補正値を適用することで、体を細くしたり、背をのばしたりすることが可能です。
(※サンプルプアプリの「H」と「V」ボタンの機能です)

↓ 図14:体型補正による変化:体が細い〜太い
ss_00

↓ 図15:体型補正による変化:身長が低い〜高い
ss_01

パーツ毎のサイズパラメータ

パーツはサイズパラメータからの影響されやさを保持します。
各部位のサイズをまばらに変え、パーツ毎に個性の表現が可能です。
(※サンプルプアプリの「S」ボタンの機能です)

↓ 図16:サイズパラメータによる変化(ツインテールを大きめに補正)
ss_03

パーツ毎の回転パラメータ

パーツは回転パラメータからの影響されやすさを保持します。
腕や肘などの稼働領域の影響されやすさとなり、絵によって受け入れられる角度を制限することで、関節があらぬ方向へ曲がらないように制限することが可能です。
(※サンプルプアプリの「T」ボタンの機能)

↓ 図17:回転パラメータによる変化
ss_02

擬似的な傾け

絵全体を擬似的に傾けることで、画像に左右/上下に傾いた印象を与えることが可能です。
(※サンプルプアプリの「LR」と「UD」ボタンの機能)

↓ 図18:傾き適用による変化:左右
ss_04

↓ 図19:傾き適用による変化:上下
ss_05

ストロークの切り替え

パスの描画ブラシを変えることで、ストロークの差し替えが可能です。
(※サンプルプアプリの「STR」ボタンの機能)

↓ 図20:ストロークの切り替え
ss_07

パレットの切り替え

厳密にはベジェ曲線の機能というわけではありませんが、処理の単純化と容量を浮かせるために画像をパレット形式で管理しているため、パレット差し替えが可能です。
(※サンプルアプリの「COL」ボタンの機能)

↓ 図21:パレットの切り替え
ss_08

素材の差し替え

こちらも厳密にはベジェ曲線の機能というわけではありませんが、ベジェ曲線(ドロー形式データ)の場合、ストロークや塗りの表現を統一できるので、素材の差し替えの自由度を高められます。
(※サンプルアプリの「COSTUME」ボタンの機能)

↓ 図22:基本素材によるアニメ
ss_13

↓ 図23:胴体をネギの着ぐるみで差し替えたアニメ
ss_12

課題

今回作成したベジェ曲線環境ですが、ゲーム制作へ導入するには大きな課題が2つあります。

描画速度

とにかく画像の生成が遅いです(※端末の性能と解像度のバランスにもよりますが、1回の画像生成に数十ミリ秒かかっています)。
特に、大きなサイズでリアルタイムにアニメさせようとすると、処理落ちがひどいことになります。

描画速度の改善が課題となります。

一方で、1枚絵ジェネレータとして、アプリ起動時にキーフレーム別に画像を生成しておき、その後、静的に参照する使い方であれば問題なく導入できそうです。

データ作成用のエディタ

今回、サンプルデータの作成はデバッグ画面レベルの簡易エディタで行いました。
が、現段階では、とても人様に提供できるレベルではありません(使いづらい&動作が安定しない等)。

ゲーム制作においては、大量のデータをいかに効率よく作成できるかが重要です。
エディタの良し悪しで工数は大きく変わってくるので、使いやすいエディタの準備が課題となります。

最後に

さて、ベジェ曲線によるゲーム素材作りを試行錯誤してみましたが、一番手応えを感じたのがストローク描画の部分です。

例えば、ツインテールだけを20%ぐらいのサイズまで縮小した場合でも、アンカーポイントの座標がかわるだけで描かれる線の太さはかわらず、ほかのパーツと共存させても違和感がありませんでした(※ペイント形式データの場合、一部のパーツを20%に縮小しようものなら、主線等が潰れて絵的なニュアンスが変化してしまい、他のパーツとの間で見た目に違和感がでます)。
パーツ毎に派手な拡大&回転をしてしまっても他のパーツと馴染んでくれるのは、動的に線を描くベジェ曲線ならではの強みだと思います。

さらに、ストローク描画の際、1画素単位をプログラム側で管理できるので、そこで色々な表現を加えることも可能そうです。
例えば、細密画のように主線に沿ってタッチをつけたり、テクスチャブラシのような表現が考えられます。

アンカーポイントの補正を含め、ベジェ曲線によるゲーム作成には十分な可能性がありそうです。
少ない素材&手間によりお手軽に表現のバリエーションがだせるよう、模索していこうと思います。

おまけ

GitHubReadMe に掲載している、大きめな動作例の画像です。
よろしければご覧くださいませ(※一枚一枚のサイズが大きいので別窓で開きます)。

動作例1
動作例2
動作例3

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

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でメニューの色が変わるので、タイトル共々変えてしまってタイトルの色はコードで再度指定してしまうと言う方法で解決しました。

もっといい方法を知っていました教えてください。

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

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.kts
plugins {
    `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.kt
object Dep {
    object Kotlin {
        val stdlib = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61"
    }
}

Step3

buildSrcの中のbuild.gradle.ktsに追記

buildSrc/build.gradle.kts
plugins {
    `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.kt
package 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.gradlebuild.gradle.ktsにrenameします.
中身は以下のように書き換えます.
一番下の dependenciesのブロックは今はおいておきます.

app/build.gradle.kts
plugins {
    id("com.android.application")
    id("kotlin-android")
    id("kotlin-android-extensions")
}
android {
    baseExtension()
    defaultConfig.applicationId = BuildConfig.applicationId
}

先程の BuildConfig.ktbaseExtensionという名前で定義したものが使えています.
このように実際に使う際は androidブロック内で baseExtension()と一行書くだけで冗長だったコードを共通化することができています.

Step6

では次にdependenciesブロックについてです.いつも使いたいライブラリの依存関係を定義するところですね.
ここも例えば features/homefeatures/settingsみたいなモジュールを作った際に使うライブラリの依存関係が同じようになることがよくあります.
その部分もある程度のベースとなる依存関係は共通化させるようにします.
buildSrc/src/main/java/dependenciesDependencies.ktを作ります.
中身は以下のように書いてみます.

Dependencies.kt
fun 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.ktsdependenciesを追加していきます.
中身を以下のようにします.

app/build.gradle.kts
import 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.kt
fun DependencyHandler.groupie() {
    implementation(Libs.Groupie.groupie)
    implementation(Libs.Groupie.databinding)
}

そうすると,build.gradle.ktsで今回だとGroupieで使いたい複数の依存関係を以下のように一行ですっきり書くことができるようになります.

app/build.gradle.kts
import 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生活を.

参考記事

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