タイトルでInkscapeと書いていますが、SVG形式を使用することの課題も混ぜているためご注意ください。
Riveはインタラクティブな2Dアニメーションを作成し、クロスプラットフォーム(Web、アプリ、ゲームエンジン等)に提供することができるツールです。
Rive — the interactive experience engine Behind Spotify Wrapped, Duolingo, and products reaching 2 billion users. Design, animate, and code in one place. Ship everywhere.
rive.app Riveはベクターアニメーションに特化しています。ベクターはRiveのエディタ内で作成できますが、外部ツールで作成したSVGをインポートして使うこともできます。
私はSVGの作成にInkscapeを使っていましたが、作成したSVGをインポートしてRiveでアニメーションさせる時にいくつかの詰まりどころがありました。 それらにおける原因・解決策を以下に共有します。
1. スウォッチを使った部分の色が消える
Inkscapeには、「スウォッチ」という複数オブジェクト間で色を共有できる便利な機能があります。 スウォッチを設定したオブジェクトはパレット内の色を参照するため、パレットの色を変更するだけで、対象のオブジェクトの色を一括で変更できます。
しかし、スウォッチを使用したSVGをRiveでインポートすると、スウォッチが設定されているオブジェクトの色が全て消えてしまいます。 オブジェクトのFills and Strokes設定を見ると、値が完全に抜け落ちていることが確認できます。
原因
原因は、直接色を設定する場合とスウォッチを設定する場合で、SVGのマークアップ構造が異なることにあります。
例として、ストロークには単色の#10212bを設定することを考えます。
直接色を設定する場合、対象の<path>の中に色が渡されます。
<path id="path16" style="fill:#ffffff;stroke:#10212b" />スウォッチを設定する場合、対象の<path>にはurl(#スウォッチ定義要素のID)の形式でスウォッチへの参照が渡されます。
スウォッチの定義は<defs>の中に格納されます。
<defs>
<linearGradient id="swatch150" inkscape:swatch="solid">
<stop offset="0" style="stop-color:#10212b" />
</linearGradient>
<linearGradient id="linearGradient150"
xlink:href="#swatch150"
x1="14.215462" y1="38.924416"
x2="67.562681" y2="38.924416" />
</defs>
<path id="path244" style="fill:#ffffff;stroke:url(#linearGradient150)" />ここで注目したいのは、スウォッチの色を定義するために<linearGradient>要素が使われている点です。
本来<linearGradient>は線形グラデーションを定義するための要素ですが、Inkscapeではスウォッチを表現するため1色だけを持つ<linearGradient>を利用しています。
単色を定義するための要素はないため、代わりに単色の<linearGradient>を作ることで複数オブジェクトで同じ色を参照する方法が取られています。
しかしそれでは、線形グラデーション自体がRiveで全く表示できないのでは?という疑問が生まれますが、通常通りグラデーションを設定したオブジェクトは問題なく表示できます。
スウォッチの場合に色が消えてしまう原因は、グラデーションのカラーストップ(<stop>)が1つだけ設定されていることです。
スウォッチを使う場合のSVGは<stop>が1つだけですが、2色のグラデーションを定義する場合は2つになります。
<defs>
<linearGradient id="linearGradient53">
<stop offset="0" style="stop-color:#0984ff" />
<stop offset="1" style="stop-color:#ffffff" />
</linearGradient>
...
</defs>
...上記のイメージを踏まえて、<stop>が1つだけのパスと、同色の<stop>が2つあるパスを含んだSVGを作ります。
このSVGをRiveにインポートすると、<stop>が1つだけのパスのみ色が消えることが確認できます。
解決策
作業をしている時は原因が掴めず、Claudeに投げてSVGを直接直してもらいました。
Inkscapeの作業で完結させるのであれば、手順が少し増えますが、同色2ストップのグラデーションをスウォッチに設定することで今回の問題を回避できそうです。
<stop>を1つ増やすスクリプトを作っても解決できそうですが、Inkscapeで編集するたびに変換が必要になるため面倒くさいかもしれません。
2. Inkscapeで設定したレイヤー(グループ)の名前が表示されない
Inkscapeでは、レイヤーやグループを作って複数のパスをグルーピングすることができます。
キャラクターのベクターを作る際、体の部位ごとにグルーピングすると、体の部位ごとのアニメーション設定が実装しやすくなります。
画像の通り、どのレイヤーがどの体の部位なのかをわかりやすくするためにラベルを設定していました。
Inkscapeで作成したレイヤーをインポートすると、Riveではグループ(Group)として構造が保たれます。
しかし実際には、自分で設定したラベルはRiveのオブジェクトに設定されず、代わりにlayer01のような識別子が設定されました。
ラベルが消えたことで、そのレイヤーが何をまとめていたのかがわからなくなってしまいました。
このことはレイヤーだけでなく、グループを使う場合も発生します。
原因
原因は、ラベルはInkscapeが独自で持っているデータであるためです。
以下がラベルを設定している部分のSVGです。
<g id="layer11"
inkscape:groupmode="layer"
inkscape:label="futon">
<!-- ラベルのオブジェクト -->
</g><g>はSVG要素をグルーピングする要素で、Inkscapeのレイヤーと対応する部分です。
Inkscapeで設定したラベルは<g>のinkscape:labelプロパティに渡されます。inkscape:というプレフィクスを持つプロパティはSVGの仕様に存在せず、Inkscapeだけが使うデータを保持するために使われます。
もちろんRiveはSVG内の独自プロパティを認識することはなく、<g>要素のidをグループの名前に割り当てます。
解決策
Inkscapeでは、対応する<g>のIDを編集できるため、IDをわかりやすいものに設定することで対処できます。
Inkscapeの「レイヤーとオブジェクト」タブから、各レイヤーの「オブジェクトのプロパティ」を開くとIDを編集できます。
注意点として、設定するIDが他のオブジェクトのIDと被らないようにする必要があります。
もしくは、拡張機能やスクリプトを使って対処できそうです。
3. クリッピング関係にあるパスの位置が離れてしまう
例として、耳に包帯を巻くようなデザインを作る時、包帯が耳の形に収まるようにクリッピングを行います。 Inkscapeでクリッピングを行う際は、
- 耳Fillのコピー(クリップ領域用のオブジェクト)
- 包帯(クリップしたいオブジェクト)
を両方選択し、「クリップを設定」をクリックします。
クリップ設定を実行する前は、耳Fillのコピーは包帯と同階層に入っています。 実行すると「レイヤーとオブジェクト」から見えなくなります。
この状態でRiveにimportすると、クリップ設定に使われた耳FillのコピーがclipPath0のような名前で出現しますが、もともとあった階層ではなくルート直下に配置されます。
ヒエラルキー上の位置関係は変わってしまっていますが、包帯オブジェクトのClip設定には耳Fillのコピーが設定されており、クリップ関係は維持されています。
問題として、耳Fillのコピーが耳グループの中から外れた状態になってしまうため、耳グループを移動してもクリップ領域が移動せず、表示が崩れてしまいます。
原因
クリッピングを行ったオブジェクトは、SVGでは以下のコードになります。
<defs>
<clipPath id="clipPath92">
<path id="path93" d="..." />
</clipPath>
</defs>
<g id="layer11"
inkscape:groupmode="layer"
inkscape:label="futon">
<g id="g92" clip-path="url(#clipPath92)">
<!-- クリップされるパスは省略 -->
</g>
</g>Inkscapeでクリップを設定すると、クリップ領域用オブジェクトは通常の描画ツリーから外れ、<defs>の中に<clipPath>として定義されます。
クリップされるオブジェクトは、clip-pathプロパティを通じてクリップ領域用オブジェクトを参照する形になります。
Riveへのインポート時、この<clipPath>内のオブジェクトがルート直下のクリップ用オブジェクトとして展開されるため、元のグループとの親子関係が失われてしまいます。
解決策
Riveへのインポート後、ルート直下に配置されたクリップ領域用オブジェクトをクリップされるオブジェクトと同じグループ内に手動で移動しました。 この階層の修正を自動化する方法はまだ見つけられていません。
所感
Inkscape独自のSVGの扱いと、SVGの仕様がRiveでは扱いづらい点の両方を意識して作業する必要がありました。 Inkscape固有の機能が必要なければ、最初からRiveエディタでパスを作るほうが楽そうです。