KLabGames Creative Blog

KLabは、多くのスマートフォン向けゲームを開発・提供しています。このブログでは、KLabのクリエイターがスマートフォン向けのゲームを制作・運営していく中で培った様々な技術や挑戦とそのノウハウについて紹介していきます。

はじめに

この記事は KLab Creative Advent Calendar 2018 の 21日目の記事になります。

こんにちは。テクニカルアーティストグループ(以下:TAG)のこかろまです。

モバイル端末は日々進化を遂げており、その成長は先日発売されたApple社のiPadProは「Microsoft社のXboxOneSに匹敵するグラフィックス性能」とまで言われるほどです。

モバイルゲームもそれに比例するように高画質・リッチ化が進んでおり、お客様に体験いただく質の向上に取り組むことがKLabのクリエイティブ部の命題ともなっています。

しかしながら、モバイル端末は世界中で生産されているので端末性能の差が激しいです。
そういった端末群がメインプラットフォームであるモバイルゲームにとって「品質を追求したうえでより多くのお客様に遊んでいただくこと」はとても重要です。

今回は私が担当した3D案件においての負荷対策の一例についてお話ししたいと思います。

負荷対策を行うまでの経緯

今回担当した案件は、15000ポリゴンほどのアニメ調のキャラクターが10人前後画面に入り乱れ、ポストエフェクトによる被写界深度やブルームなどの要素を盛り込んだ、演出重視のアクションゲーム。

すでに開発は中盤に差し掛かっており、ある程度アセットの作成指針や仕様、クオリティラインが出来上がったころの参加でした。

アクションゲームの要であるキャラクターモデルも例外ではなく、すでに30体ほどが完成した状態で開発環境に実装されていました。

前段でお話しした通り、より多くのお客様に遊んでいただくためにも「世界でより多く流通していて、性能的に十分な端末を最低動作環境とするターゲット端末」を策定する必要があったため、今回の案件では検討の結果GalaxyS7において30FPSを達成することを目標としました。

見た目の部分が一定のクオリティラインに到達してきたため、「とりあえず現状の処理負荷を計測してみよう!」という動きになりました。

意気揚々と計測したところ、ゲームパート1週目(約2分前後)は30FPSをキープできましたが、長時間プレイすることによって端末が熱を持ちはじめ、2週目から30FPSを割るようになってしまいました。

モバイルゲームにおいて、周回にストレスがたまることはゲームの離脱要因にもつながります。これはマズイと言うことで、対策を行うためのチームを結成し対策を練ることにしました。

開発の方がプロファイラーで負荷計測したところ、開発中のゲームはレンダリング・スキニング・タイムライン処理が高負荷のTOP3を占めていることが分かりました。

レンダリング処理は、シェーダー、演出、ポストエフェクトなど、複数の要因が絡み合って構築されている都合上、手を付けづらいのもあり、モデルデータがそのまま影響を与えるスキニング処理に一旦目を向けることにしました。

スキニングのコストが高い要因として、スキニングインフルエンス数の最大値
(一つの頂点に何本の骨が影響するか)
と、ジョイント(骨)数が多いことがあげられます。
改めてデータを検証したところ、以下のことがわかってきました。

  • モデルデータはすべて2インフルエンスで作成されていた
    • モバイルゲームにおいて、2インフルエンスは頂点数にもよるが現実的
    • インフルエンス数が処理負荷に影響を与えていたわけではないと判断
  • 髪の毛、衣装などのいわゆる『揺れもの』制御用ジョイント過多
  • 表情制御用のジョイント過多
    • 本数の制限を決めきれていなかった
    • クオリティラインを突破するために、動かしたい箇所を増やして行った結果、いつのまにか想定していた骨数よりも多くなってしまっていた

つまり、今回のモデルデータで負荷対策を行うとするならば、ジョイント数を削減することがスキニングコストを下げることにつながると判断しました。

その中で、揺れもののジョイント本数削減はダイレクトに品質に影響してしまうので、品質を落とさずに負荷を下げることができる箇所である表情の仕組みから着手することにしました。

やったこと

前置きが長くなりましたが、今回負荷対策として行ったことは表情をジョイントベースの
仕組みから、ブレンドシェイプベースの仕組みに構造を変更したことになります。

ここで、ジョイントベース、ブレンドシェイプベースそれぞれのメリット・デメリットについて振り返ろうと思います。

ジョイントベース … ジョイント(骨)を頂点に紐付け、動かすことで表情制御

【メリット】

  • 表情制御するデータ容量はジョイントを制御するアニメーションデータのみなので、ジョイントが共通であればモデルデータが増えても一定使いまわすことが可能
【デメリット】
  • 骨を動かして表情を制御するので、現状の骨でクオリティが足らない場合骨を追加する必要がある
  • 骨数・端末スペックにもよるが現状のモバイル端末では比較的重い処理
ブレンドシェイプベース … 表情のメッシュを用意しブレンドすることで表情を制御

【メリット】

  • 頂点を増減させなければ自由に形状を調整することができるため、クオリティをアップさせやすい
  • 基本、頂点変形を行うのみなので、ジョイントベースに比べると比較的軽い

【デメリット】

  • あらかじめMaya等のDCCツールでブレンドしたい表情をセットアップしておく必要があり、ブレンドシェイプしたいメッシュごとに表情分の容量が増える
  • 作成したブレンドシェイプ以上の形状にすることはできない
  • 表情アニメーションデータの共通化が基本不可能
    (※頂点IDや頂点のトポロジーを揃えることで共通化は可能だが今回データでは不可能だった)

どちらの手法も一長一短あります。

モデラーの方が本番モデルをブレンドシェイプ化出来るように手動で対応してくださったので、こちらを使ってジョイントベースのデータと処理負荷を比較してみたところ、すべてのテスト端末において
ブレンドシェイプの方がジョイントベースのデータより高速であることが判明しました。
処理負荷対策の構造として是非とも採択したいところです。 
しかしながら、一般的なブレンドシェイプのように頭部モデル全体に対して表情分のブレンドシェイプを設定してしまう場合、同一キャラクターの別メッシュ(例えば、生え際を変更したい等、頂点の増減に影響が出る変更の場合)の際にもブレンドシェイプを用意する必要があり、かつ表情が一つでも増えた場合、すべての頭部モデルに対して追加表情分のブレンドシェイプをマージしなければいけません。

ジョイントベースであれば、ジョイントのアニメーションデータを作成するだけで
表情を追加することができ、容量には大きく影響がありません。

また、アセットとしても頭部モデルデータに手を加える必要もありません。

あらかじめ表情ごとに.meshを作成、ランタイム時に表情ごとの.meshを取得してベースメッシュに対してブレンドシェイプを追加する方法もありますが、ランタイム負荷はロード時間の増大につながるので避ける必要がありました。

とはいえモバイルゲームにおいて過去配信したアセットに表情を追加する等、手を加えることはテストケースの増大、なによりお客様へのデータダウンロードの負担にもつながるので、こちらもなるべく避けたいところです。

どちらもいいとこどりをした手法ができないだろうか…

改めて、頭部のモデルについて振り返ってみます。

上のGIF動画はいらすとやさんからお借りした、
『いろいろな表情の猫のイラスト「ひらめいた顔・驚いた顔・焦った顔・悩んだ顔」』を
連番にしたものです。

あることに気づきませんか?

上記動画の頭部でアニメーションする範囲は「目の周囲・口の箇所」のみで、
それ以外の部位は動いていないのです。これは、アニメ調である3Dモデルも同様のことが言えそうです。
※リアリスティック系は筋肉の動きを再現するように表情を作ることもあるので3Dモデルが一概にはそうとは言い切れません。

つまり、

  • 目、口など、あらかじめジョイントベースで作った表情から動く頂点を計算
  • 「動く部分のメッシュ」を切り出して、表情分ブレンドシェイプ化
  • それ以外の頂点を「動かないメッシュ」とする
  • ゲーム再生時に目、口などの「動く部分のメッシュ」と「動かないメッシュ」を合体させる

とすることで、ブレンドシェイプメッシュを最小限に抑えることができます!

しかも、動かないメッシュ部分は差し替えることができるので、例えば別の髪型を設定する際は生え際の部分のメッシュの流れを変更したい!となっても問題なく対応できます。

さらには動かない部分のメッシュはスキニングしなくて済むため、スキニングコストをさらに削ることもできます。

完全に勝ちました。

実装について簡単にご説明します。

実装

まず、ジョイントベースで作成された表情データを取得します。私はイテレータを多用する都合上PythonAPI 2.0ではなく、Python API 1.0とmaya.cmds、UIにqtを併用しています。

あらかじめ、デフォルトとなる表情の頂点IDと各頂点座標を取得します。

cmds.pointPositionでも取得することは可能ですが、数千、数万ポリゴンを超えてくると永遠と処理に時間がかかってしまうので、基本的にPython APIで取得します。

表情名 フレーム(60FPS)
通常 000
010
030
050
070
090
110
微笑み+目閉じ 130
微笑み+目閉じ笑い 150

今回表情データは上記のリストのように、それぞれの表情を一つのmaデータの指定フレームごとに作成されていたので、あらかじめ辞書として用意指定表情分のフレームの頂点IDと頂点座標を取得します。

import maya.cmds as cmds
import maya.OpenMaya as om


class ReducedBlendShape(object):

def __init__(self):
   self.eye_expression_mesh_name = 'eye_facial'
   self.mouth_expression_mesh_name = 'mouth_facial'
   self.eye_blend_shape_mesh_name = 'Eye_Around'
   self.mouth_blend_shape_mesh_name = 'Mouth'
   self.eye_blend_shape_attr_name = 'EyeBlendShape'
   self.mouth_blend_shape_attr_name = 'MouthBlendShape'

@staticmethod
def get_selection_mesh_node():
  
   selection_list = om.MSelectionList()
   m_node_fn = om.MFnDagNode()
   m_dag_path = om.MDagPath()
   m_object = om.MObject()

   om.MGlobal.getActiveSelectionList(selection_list)

   try:
       selection_list.getDagPath(0, m_dag_path, m_object)
       iter = om.MItSelectionList(selection_list)

       while not iter.isDone():
           iter.getDagPath(m_dag_path, m_object)
           if m_dag_path.hasFn(om.MFn.kMesh):
               m_node_fn.setObject(m_dag_path)
               return m_dag_path
           iter.next()

   except ValueError:
       pass

# 頂点IDと頂点座標を取得(頂点座標は5桁で丸める)
@staticmethod
def get_vertex_information(dag_path):
   '''
   :return : dict
   '''

   m_dag_path = dag_path
   m_object = om.MObject()

   mesh_fn = om.MFnMesh(m_dag_path)
   vtx_iter = om.MItMeshVertex(m_dag_path, m_object)

   vtx_dict = {}

   while not vtx_iter.isDone():

       m_point = vtx_iter.position(om.MSpace.kWorld)

       vtx_id = vtx_iter.index()
       vtx_position = [round(m_point.x, 5), round(m_point.y, 5), round(m_point.z, 5)]

       vtx_dict[vtx_id] = vtx_position
       vtx_iter.next()

   return vtx_dict

上記で取得した表情を元にあらかじめ取得しておいたデフォルト表情の頂点IDと

各頂点座標を比較し、動く頂点を{頂点ID : 頂点座標の辞書}として追加します。

取得した頂点IDを元にメッシュを分割すると、各パーツごとに分離されたメッシュができます。

@staticmethod
def split_mesh_from_vertex_id(dag_path, vertex_ids):
   '''
   :return : string[]
   '''
   # validate
   if not vertex_ids and not dag_path:
       return

   dag_path_name = dag_path.partialPathName()

   cmds.select(clear=True)

   for vertex_id in vertex_ids:
       vertex_attr = dag_path_name + '.vtx[%d]' % vertex_id
       cmds.select(vertex_attr, add=True)

   face_list = cmds.polyListComponentConversion(fromVertex=True, toFace=True)
   cmds.select(face_list, replace=True)
   cmds.polyChipOff(ch=True, kft=True, dup=False, off=0)

   dag_path.extendToShapeDirectlyBelow(0)
   split_meshes = cmds.polySeparate(dag_path_name)

   return split_meshes

しかし、ウェイト付けの方法にもよりますが、これでは左目部分、右目部分、口部分、
動かない部分など、それぞれのアイランドごとで分離されてしまいます。

できる限りまとめた方がブレンドシェイプターゲットの最小限化による容量削減、
ゲームエンジン上でのマテリアルコール数の節約に繋がります。

今回のデータはすべてのキャラクターの原点が同一だったので、どのキャラクターも分離されたメッシュ全体の頂点の平均座標を取得することで左目付近、右目付近、口付近、それ以外の動かないメッシュとして部位の特定を行うことができました。

部位の特定後、目付近、口付近、動かないメッシュとしてメッシュ結合、名前を整理します。

次に、整理された分離メッシュでのそれぞれの表情のブレンドシェイプメッシュを作成します。

先ほど取得した表情ごとの動く頂点IDと頂点座標辞書を元に新たに1からメッシュを作成してもいいのですが、ここではメッシュ分離した際のスキニングヒストリが残っているBADノウハウと言いますか、Mayaの仕組みを利用しました。

あえてジョイントでスキニングされた状態のヒストリを残してあげることで、頂点やメッシュが分離されているにもかかわらずそのままジョイントベースのアニメーションを流し込むことで再生することができるので、これを利用します。

cmds.file等でジョイントベースのフェイシャルアニメーションを読み込み、該当の表情データのフレームにcurrentTimeで変更した後、ブレンドシェイプ化したいメッシュをduplicateします。

@staticmethod
def get_duplicate_from_time_mesh(dag_path, time, rename):

   # validate
   if not dag_path:
       return

   dag_path_name = dag_path.partialPathName()

   cmds.currentTime(time, edit=True)
   mesh = cmds.duplicate(dag_path_name)
   cmds.rename(mesh, rename)

   return mesh

これを表情分繰り返すことで、分離した状態での頂点IDでのブレンドシェイプターゲットメッシュを簡単に作成することができます!

もちろん、最後には不必要ヒストリを削除する必要があるので、表情作成後にヒストリを削除します。今回はスキニングウェイト等のデフォーマーも不必要なので特に考慮することなく、

cmds.delete(dag_nodes, ch=True)

を呼んであげればまるごとヒストリが削除されます。

表情作成後にブレンドシェイプターゲットを設定します。

ブレンドシェイプアトリビュート名がそのままUnityのanimationclipのpropertyNameとなるので、きちんと明示してあげます。

@staticmethod
def set_blend_shape_attrbute(dag_path, expression_mesh_name, blend_shape_attr_name):

   # validate
   if not dag_path:
      return

   dag_path_name = dag_path.partialPathName()

   expression_meshes_naming = expression_mesh_name + '*'

   cmds.select(clear=True)
   cmds.select(expression_meshes_naming, add=True)
   cmds.select(dag_path_name, add=True)
   target_blend_shape_mesh = cmds.ls(sl=True, transforms=True)
   cmds.blendShape(target_blend_shape_mesh, name=blend_shape_attr_name)

   return

切り出された箇所の頂点の法線はそのままだとばらばらとなってしまっているので、シェーディングによっては継ぎ目がバレてしまったり、法線を使用しているアウトラインシェーダー等に影響が出てしまいます。

そこで、メッシュとメッシュの頂点位置が近似の箇所を取得し、法線を合わせます。


class Normal(object):

   def __init__(self):
       self.tolerance = 0.05

   def tolerance(self):
       return self.tolerance

   @tolerance.setter
   def tolerance(self, value):
       self.tolerance = value
       return

   @staticmethod
   def get_vector(x_point, y_point):

       delta_x = x_point[0] - y_point[0]
       delta_y = x_point[1] - y_point[1]
       delta_z = x_point[2] - y_point[2]

       vector = math.sqrt(delta_x * delta_x + delta_y * delta_y + delta_z * delta_z)
       return vector

   def copy_normal(self, object_a, object_b):

       # validate
       if not object_a and not object_b:
           return

       object_a_vertex = cmds.polyListComponentConversion(object_a, tv=1)
       object_b_vertex = cmds.polyListComponentConversion(object_b, tv=1)
       object_a_vertex_expand = cmds.filterExpand(object_a_vertex, sm=31)
       object_b_vertex_expand = cmds.filterExpand(object_b_vertex, sm=31)

       for object_a_vertex in range(0, len(object_a_vertex_expand)):
           object_a_vertex_position = cmds.pointPosition(object_a_vertex_expand[object_a_vertex], w=1)
           for object_b_vertex in range(0, len(object_b_vertex_expand)):
               object_b_vertex_position = cmds.pointPosition(object_b_vertex_expand[object_b_vertex], w=1)
               compare_vector = self.get_vector(object_a_vertex_position, object_b_vertex_position)
               if compare_vector > self.tolerance:
                   continue

               normal_value = cmds.polyNormalPerVertex(object_b_vertex_expand[object_b_vertex], q=1, xyz=1)
               cmds.polyNormalPerVertex(object_a_vertex_expand[object_a_vertex], x=normal_value[0], y=normal_value[1], z=normal_value[2])

       return


完成です!

動くメッシュと動かないメッシュを自動的に切り分けることができました。

手でやると時間のかかってしまいコストパフォーマンスに優れない要件であっても、スクリプトで実装することで現実的に実装可能になるのがスクリプトのよいところだと思います。

結果

ブレンドシェイプベース変更前と変更後を再度プロファイリングしたところ、驚きの結果が出ました!

スキニングコストは 中央の赤の箇所ですが、3.521549043 -> 2.208194279 !!

なんと6FPSもの余裕ができ、ゲームプレイを重ねても30FPSで安定するようになりました。

本案件はUnity2018.2.0なのですが、近日リリース予定のUnity2018.3.0ではComputeShaderによるブレンドシェイプのGPU Skinning対応なども盛り込まれており、アップデート後の端末次第でさらなる高速化が期待できそうです。

まとめ

実は、今回実装したデータは表情から動く箇所を抽出した最小限の頂点をブレンドシェイプ対象としていましたが、実装を変更しルートジョイント以外のスキニングウェイト値が入っている頂点を切りだし対象メッシュとするように変更しました。

理由としてはアプリ運営時に表情が増える想定をした際に、現状の表情群では動いていない頂点、ただしスキニングは設定済みの頂点を動かしたくなる可能性が出てくる懸念があり、そうなってしまった時にデータ更新でしか対応できないためです。

なぜ今回上記のような表情ごとの差分での手法を紹介したかと言いますと、上の方が実装が大変だったから

余談ですが、弊社内ではこの動く部分のみブレンドシェイプ化をしたデータを
「バットマン型ブレンドシェイプ」と呼ばれている時期がありました。

切り出された形状がバットマンのマスクのようだったからです。

このページを見てくださった方の何かお役に立てましたら幸いです。

明日のKLab Creative Advent Calendar 2018 の 22日目は、しおりさんです。

よろしくお願いします。

© Unity Technologies Japan/UCL

はじめに

この記事は KLab Creative Advent Calendar 2018 の 20日目の記事です。

はじめまして!
だーやまです。

前職ではDTPの制作進行管理をしており、
1年半ほど前にKLabに入社いたしました。
現在、主に3Dの制作進行管理をしています。

今回のお話は……Unityバージョンアップについてです!

所属する案件でUnityのバージョンアップが実施されることになりました。
単純に「はいっ!アップデート完了!」となる訳もなく、
特にエフェクトは、品質管理者(以後品管)が全キャラ確認する必要がありました。

品管の確認作業を出来るだけスムーズにするために
どんなフローを組んだかを今回はお伝えできたらと思いますので、
暫しお付き合いください!

Unityをバージョンアップする理由

Unityのバージョンアップをすると起きる問題

確認作業の効率化

ただ、これを実現するには開発さんに各種スクリプトを作ってもらわないといけないのと、 早急にSHOTGUNの確認ページを構成する必要がありました…

無理を承知で開発さんのところにお伺いすると…


次に、無理を承知でSHOTGUN担当 ノベさんのところにお伺いすると…

開発さん・SHOTGUN担当ノベさんの絶大なご協力のもと、
1~3のスクリプトと、確認用のSHOTGUNページが無事完成しました!
しかも短納期!
SHOTGUNの具体的なページ構成は、カレンダー3日目ノベさんの記事でご確認下さい。

確認作業の進捗

まとめ

まず、
今回キャプチャ&動画化のスクリプトを作成頂いたfo-taさん、
SHOTGUNへの自動アップロードスクリプトを作成頂いた姫野さん、
SHOTGUNのページ構成を作成いただいたノベさん、
この場をお借りしてお礼申し上げますm(_ _)m

今回のUnityバージョンアップのチェックフロー整備を通して、
できるだけ品管の方が

「確認する」という行為を楽に・スムーズにできるようにするにはどうしたらよいか

ということを改めて考える事案になりました。

「確認する」という作業を効率化することによって、
品管・デザイナーに、「調整する・ブラッシュアップする・新規作成する」
といった時間を多く作ってあげたいと思っています。

そのためには、無駄な作業をとことん省き、
生産性の高いフローづくりが必要だと改めて感じました。

今回の記事が、どなたかの作業効率のヒントになれば幸いです。

以上、お読みいただきありがとうございました!

KLab Creative Advent Calendar 2018 の 21日目は、こかろまさんです!

この記事は KLab Creative Advent Calendar 2018 の 19日目の記事になります。

こんにちは。クリエイティブ部でUI/UXデザイナーをやっているだいさくです。

UIの高解像度対応やってますか?

今年はiPhoneXSやiPad Proが発売されましたが、スマートフォンも画面サイズが大きくなり、だんだんと画面解像度も高くなってきてますね。

スマートフォンのゲームにおいてもグラフィックのきれいさが重要になってきてますが、ゲームの場合コンテンツ量も多く、なかなかUIを高解像度できれいにするのは難しいものがあるかと思います。

UnityでUIデザインを高解像度対応したい場合、まず考えられるのはUIパーツの画像を大きくすることです。

しかし、低スペック端末でのパフォーマンス維持と両立しなければならなかったりすると、そこまでテクスチャを大きくできないので、UIパーツの画像を大きくするのも慎重にならないといけません。

そこで今回はUnityでUIパーツを大きくしながら素材を軽量化する手法を1つ提案したいと思います。

よくある軽量化手法

まず、デザイナーが画像素材を軽量化する手法として、以下のものがよくあるかと思います。

  • テクスチャフォーマットの圧縮
  • 9パッチ化
  • パーツ反転
  • パーツ縮小

テクスチャフォーマットの圧縮

以前はiPhoneとAndroidの両方で対応している16bit圧縮を使うことが多かったかと思います(最近はちょっと変わってきているかもしれませんが)

16bit圧縮の場合、グラデーションが粗くなってしまうので、パーツ素材にディザをかけるという一手間が必要。

▼参考:OPTPiX Labs Blog『Unityで、もっとキレイな16bitカラーテクスチャを使おう!』

9パッチ化

引き延ばす部分が大きい素材には有効な手法ですが、16bit圧縮を使用している場合、ディザのドットが引き伸ばされてしまうので、相性が悪いという問題があります。

パーツ反転

上下・左右が対象の素材では、分割してUnityで反転して使う方法が有効かと思います。

ただ、反転可能なデザインでないといけないので、使い所は限られるかと思います。

パーツ縮小

あとは、素材自体のサイズを小さくして、Unityで拡大して使うというものですが、背景や模様のように多少ボケてもよい素材では有効かと思います。
これも16bit圧縮を使用している場合、ドットが拡大されてしまうという問題があるので、やはり使い所が限られるかと思います。

今回はこれらの方法を組み合わせつつ、パーツ分割+Unityでの着色を使うことで、さらに軽量化するという手法になります。

やってみる

1)例えば、このようなデザインのウィンドウパーツがあります。




2)単純に9パッチ想定で書き出すとこんな感じになると思います。



3)これを分解して、このようなパーツとして書き出します。

  • ①②Unityでは乗算で着色するのと、アルファも変えられるので、白100%で用意したほうが汎用性が高くなります(白ならディザを気にする必要もないですね)
  • また、左右反転して使う想定にしてますが、1/4にすればさらに小さくなりす。
  • ③シャドウや光沢など、共通の色として使用する部分は1つのパーツにします
  • ④模様などのパーツは拡大してもあまり気にならないので、縮小して書き出します。

4)これを、Unityでこんな感じに着色します。



5)これらを組み合わせることで、素材も小さくなり、サイズ変更も可能+色替えも可能なパーツになります。



6)さらに、グラデーションコンポーネントを導入することで、より複雑なデザインも再現することができます。


メリット・デメリット

メリット

やっていることは単純なことですが、この方法を使うことのメリットとしては、Unityで色を変更できるので、同じデザインでカラーバリエーションがいくつもあるようなUIパーツは素材が1種類のみになり、色違いのパーツを書き出す場合よりもテクスチャのサイズを小さくできるかと思います。
また、Unityでグラデーション着色すれば、拡大してもグラデーションが劣化しないので、高解像度の端末でよりきれいに表示できます。

デメリット

ただ、注意点として以下のようなものがありますので、どの程度まで分割するのかは考慮する必要があるかと思います。

  • パーツを分割することで、素材が増える
  • パーツをUnityで組み合わせるので、基本的にオーバーレイなどの描画モードが使えない
  • 分割が多いとポリゴンとかドローコールが増える
  • 実装の手間が増える

この方法を使うのであれば、UIデザインを作成する時点で分割することを想定しておくと、素材を書き出す時に調整する必要もなくてスムーズかと思います。

また、Unityで素材を組み合わせるので、いろんなところで使用するパーツは共通のPrefabにしておいたほうがまとめて修正できるのでラクというのもあります。

※古いバージョンのUnityだとPrefabのネスト化に問題もあるので、共通化するときは注意

▼参考:テラシュールブログ『【Unity】Nested PrefabやPrefabエディターが追加、新しいPrefabワークフローを触ってみた』

おわりに

もっと大きな目線でUIの高解像度化を考えると、メモリ負荷や解像度設定の変更など、いろいろ検討することがあるかと思いますが、UIの画質を上げるということでは、素材のサイズを大きくするというのがどうしても必要になってくるかと思います。
今後はUIを高解像度化する手法として、素材をベクター化したり、デザイン自体をフラットデザインにするといった流れになってくるのかもしれませんが、いろいろ工夫して頑張っていきたいですね。

この記事はKLab Creative Advent Calendar 2018 の 18日目の記事になります。

こんにちわ。UI/UXグループのcoomaです。

UXってなにをするの?っていう質問をよくされます。その度に長くなっちゃうけどってはぐらかしているのですが、この場を借りて、UXの業務について説明しようと思います。

UXは精神論?

UXという言葉がクリエイティブにおいて重要視されるようになってきています。ものつくりの現場では、まるでバズワードみたいに「これからはUXでしょ!」と語り合い、UXチームを組織したりします。

でもフタを開けてみると、なにをしてよいのか分からずチームを解散したり、再結成したり。そんな話しをいくつかの会社できいたことがあります。そのうち、UXは単なる精神論のようにデザイナーの間でウワサされるようになります。

実際、UXの取り組み内容は、各社まちまちのようです。UXセミナーの懇親会などでは、みんな様子を伺っているといった印象です。その姿はさながら、名前をド忘れした知人に出くわして、顔は覚えているんだけど、どこの誰だか分からずにヒヤヒヤしているようです。正解が分からないまま「そうそう、UXっていつもそうだよね」とあわてて話を合わせたり。

確かに、ユーザー体験という言葉は、曖昧で感覚的です。しかし、UXが重要視される背景には心理学などの認知科学の発達が大きく影響しています。「人の購買理由」や、何をしたいという「モチベーション」という目に見えない曖昧な感情が、計測技術の進化により数値化され、分析することが可能になってきたのです。

(画像出典:NedSahin.com 『fMRIの解析画像』)

つまり、UXは今まで聖域と言われてきたクオリティや、デザイナーのセンスといったブラックボックスを解体し、なにが行動を作り出すのかということに体系的に取り組もうとします。

クオリティ神話の崩壊

ハイセンスでオシャレなお店に入りにくいという体験は誰もがしたことがあるんではないでしょうか。この場合、良いデザインだから行動を起こすという訳ではありません。

商品という「もの」があふれた今、良いものをつくれば売れるというクオリティ神話は崩壊しました。わたしたちは自由に商品を選んで買い物を楽しんでいるようで、実は、細かい条件の中から、特定の商品を選ばされています。そこでは「どこで誰がなにをする」というメッセージが鍵になります。

視覚的なデザインはサービスにとって、誰がそのデザインを欲しがるのか、どんな時に、それによってどんな体験するのかを伝える記号でしかありません。ブランドのロゴと同じですね。

認知科学によって、人は本能や、あるいは過去の感情的な記憶により、特定の状況に決められた反応するということがわかりました。環境の要因がトリガーとなって、それに紐づいた感情が引き出されるのです。つまり、どう誘導し、そこで何を体験させるかによって、人の感情を動かすことが可能なのです。

定量化できるもの

UXをわかりにくくしている理由は「UXは単に表面的デザインをつくるものではない」ということだと思います。「じゃあ、何をデザインするんだ?」と言いたくなります。「UXは価値そのものをデザインする」もう曖昧で何がなんだかわかりません。

しかし、UXは曖昧ではありません。先にも書いたように、UXは感覚の数値化、つまり定量化によって発達したものであり、それを抜きに考えることはできません。

UXが目に見えるデザインの改善ではなくユーザー体験の向上、さらに、その矛先がユーザーではなく、それを作るクリエイターの環境改善にまでさかのぼる理由は、「価値をデザイン」するということ、そして、この「定量化」が関係しています。

(画像出典:Relive Volvo's 'Emotion of Design' Google Hangout 『Volvo EEG Test』)

価値を最大に高めたいと言う時、デザインのクオリティを高めるというのがもっともわかりやすい対応だと思います。しかし、たった一箇所のデザインの見た目を改善したところで、複雑な要素の中から、それがユーザーにどんな影響を与えるかを知ることは難しいでしょう。

どの見た目がクオリティに影響を与えるのかを明確にするよりも、確実にクオリティを高くする変数を上げる調整をするというのが、定量的な対応になります。それはどのようなものでしょうか?

クオリティの量=技術×時間

例えば、技術とそれにかけた工数によってクオリティの量が変化するというのはあたりまえの事実です。そこで技術力の向上を行い、潤沢に工数をかけることさえできれば確実にクオリティをあげられるということが言えます。

技術と工数を増やすのであれば、高い技術を持つクリエイターを採用し、アウトプットのハードルをあげることでそれを短期間で達成することができるでしょう。

しかし、時間が経過すると、高い基準や目標を達成できないデザイナーはストレスを貯め始めます。ましてやフル稼働しているような状態では成長もままならず、流動性のなくなった案件ではデザイナーは常に同じ作業を続けることになります。モチベーションを失えば、最悪、リタイヤしてしまうこともあるでしょう。

長期的には外部からのリソースに頼るのはチームを食いつぶす手法と言わざるを得ません。常にリソースを補填する必要がでます。しかし、UXはそこにあるリソースを最適化し、勝手に成長する生きた仕組みを目指します。

UXの事例でよくあるのは、デザイナーが働く上で精神的に良い環境をつくるというものです。この方法ではサービスのクオリティを継続的に上げ続けることができます。ユーザーに対して長期的に安定したクオリティを提供し続けるためには、環境の改善は欠かせません。

現場の硬直化

環境の改善はなにをするものなのか?

クオリティを水とたとえるならば、デザイナーは蛇口です。それぞれ水の勢いや浄水器の能力の違いはあるものの一定の時間に出せる水量は限られています。つまり時間をかければかけるほど、たくさんのクオリティを引き出すことができます。

ゲーム制作のような短時間で大量のアウトプットが必要になる過酷な現場では、常に水を出し続ける必要があり、フィルター交換の時間も、浄水器のアップグレードもできないまま、水は汚れ、あるいは詰まり、浄水器はいつか壊れてしまいます。もしくは水が枯れてしまうことだってあるでしょう。

UXはそこに循環をつくり、水脈そのものの改善をはかろうとします。経済でも良く言われることですが、プロジェクトや会社組織にとって最も問題なのは流動性がなくなることです。情報や人の流動性がなくなることは成長が止まることを意味します。

会社もプロジェクトも長期化すると、最初はうまく行っていた方法が、状況の変化のためにズレが生じます。しかし、古い手法は疑われることないままに負の遺産となり、やがて積み重なり体制は硬直化してしまいます。結果、そのことはダイレクトにユーザーに提供する価値の質に影響を与えるようになります。

流動性を上げる

仕事の上での流動性の大事さを明らかにした、ある実験があります。どのような方法が働く人のモチベーションをあげるのかというのを調べた実験です。その中で最も効果的だったのは「チャレンジ・シーキング」でした。それは職場環境に変化を与え、新しい課題や、仕事を与えるというものです。

(画像出典:ScienceDirect/Journal of Vocational Behavior『Individual job redesign: Job crafting interventions in healthcare』)

これは最近良く言われる「ジョブクラフティング」という、より良い仕事をつくるため手法の一つです。その中でも効果的な以下の手法に絞られ、実験は行われました。

  • デマンド・レデューシング:効率化、不得意な仕事を得意な人に任せること
  • リソース・シーキング:同僚のフィードバック、意見交換
  • チャレンジ・シーキング:仕事の変化、環境の変化、新しい課題

UXでは上記のような環境を実現するために、社内のクリエイティブのルール化を行い共通言語を作り、交流を活性化することで、流動性を高くすることを試みます。一方で、効率化を行い、デザイナーがやるべきではない仕事をなくし、常に新たな価値の創出に専念できる環境を用意することを目指します。

行動をつくる

では実際の現場ではどのようなことをしているのか?というのが一番知りたいところかと思います。あれこれ指示したところで、人はなかなか動いてはくれません。作りたいのは正しい考えではなく行動です。

たとえば、硬直化してしまった体制に対してコミュニケーションをとれ!といっても、反発と疲弊しか生まれないでしょう。そんな時は関係性を変えることです。席替えや、体制変更です。そして、これまでのやり方を疑い、勇気をもってワークフローを変えてしまうのです。そこでは一見、問題が噴出するようにみえます。それはフタをしてくすぶっていた炎に酸素を送り込むようなものです。風通しがよくなることで、問題は見える化し、コミュニケーションが活発化し、変化が生まれます。行動を変えるのなら環境を変えることです。

思考は行動の後付けでしかないことは行動経済学者ダニエル・カーネマンによって証明されています。簡単に言うと気持ちで行動を変えるのは難しいということです。叱咤激励で人が学ぶようになったり、元気になってくれるのであれば話は単純です。勉強が大事だとわかっていながら、一生懸命になれない経験は誰にでもあったと思います。一方でゲームのように楽しかったり、誰かに勝った経験があれば、ほっておいても人は努力をするものです。

(画像出典:Daniel Kahneman著『ファスト&スロー』より)

しかし、モチベーションを奪うのは、とても簡単なのです。否定し続ければ、すぐです。ちょっとした環境の変化でもヤル気がなくなることもあります。環境によってモチベーションを奪わない配慮は組織にとっての重要な課題です。それに加え、モチベーションを育成するための、やりがいを感じてもらう課題設定や、自分が必要とされている実感を持ってもらうための環境作りも有効です。

作り手からユーザーまで

ここまで、どちらかというと「良い組織とは」とか「ユーザーではなくデザイナーのための」みたいな話をしてきました。それってUXなの?と思われたかもしれません。最初に「価値をデザインする」と言いました。UXはアウトプットではなく価値そのものをデザインしようとします。それを最大化するために価値を作り出す源流の正常化を図ることもUXと言えます。

つまり作り手の環境です。まず安定してアウトプットできる環境をつくる。もちろん、ユーザー環境の整備も行います。しかし、やっていることは実は同じです。「いかにモチベーションをあげるのか」です。

価値にフォーカスして考えると、ユーザーの環境と作り手の間に差はありません。すでにある価値の流れを作り手が汲み取り、あるいは流れを強くしてユーザーに届ける、ユーザーがそれを受け取り、その価値をさらに大きくする。それを作り手に返す。循環です。もし、作り手がその価値を理解できず、ただ垂れ流しているだけなら、どうでしょうか?作り手が価値を感じられる状態を作らなければ、循環は生まれないでしょう。

ユーザーと作り手を区別して考える必要はありません。価値を伝えるとは熱量を伝えることと同じです。そして、その熱量で言えば、今後、コンテンツにとって、作り手とユーザーの差はますますなくなっていくでしょう。ユーザーはどんどんクリエイティブになっています。ユーザーが遊び方を作り出したり、商品の価値を高めるようなコンテンツが増えてきました。単なる作り手とユーザーの関係ではなくなっています。

まとめ

UXはこれまで表面的だったデザイン思考を、プロジェクト全体に適応したものと考えることができます。価値を作り出すには、ひとつのセクションを切り離して考えることは難しく、全体の仕組みとして考える必要があります。さらに、価値の所在を探っていくと、作り手からユーザー、ユーザーから作り手の流れとして捉える必要が出てきます。その循環を最大化するにはその垣根を取り外す必要があったのです。UXとは、「循環を正常化するための活動」と言うのがもっともシンプルな言い方ではないかと思います。

はじめに

この記事は KLab Creative Advent Calendar 2018 の 17日目の記事になります。

初めまして、こんにちは。クリエイティブ部でイラストの制作をしている、しろと申します。

今回の記事ではお仕事で描いているイラストとは別に、普段趣味で描いているイラストの方のお絵描きのメイキングをしたいと思います。よろしくお願いします。

概要

時間が無いけど一枚絵が描きたいときに、なるべく早く絵を仕上げる時用の描き方を紹介致します。技術的な部分よりも、早く仕上げるための考え方の部分がメインのメイキングになります。

使用ソフト

  • CLIP STUDIO
  • Adobe Photoshop CC
(メインの作業はクリスタ、フォトショは加工で使用します。)

絵を描く前に

何も決めずに描き始めると早く絵を仕上げることが出来ないのでまず何を描くか考えます。

何を描きたくないかも考えます。

描きたいもの

  • 女の子
  • お花

描きたくないもの

  • 複雑な背景

見せ所
描きたいもの、描きたくないものを決めたら絵の見せどころを決めます。
描くべきところを決めておく事で描いてる最中にあれこれ悩む事を防ぎます。

  • 女の子
  • 特に目
  • 次に肌

使いたい色
使用する色を決めます。
沢山の色を使うと完成までに時間がかかるので、メインで使う色を1色、サブで使う色を1色の計2色を決めます。

メイン
サブ

ここまで考えたらラフの作業に入ります。

ラフを描く

ここまで考えたことを元にラフを描いていきます。描きたいもの、見せ所は一番わかりやすく目立つところに配置してなるべく背景を描かなくていいように…

使用したブラシ:にじみ縁水彩、シャーペン

色を置く

キャラクター部分は黒、背景に赤を置いて色の対比でキャラを浮き立たせることにします。

使用したブラシ:鉛筆

陰影を決める

最初に決めた見せ所を見せるための陰影を設定していきます。

見せどころが肌なので、肌に光を当てて浮き上がらせたい、また目も見せどころなので、顔全体に影を落として目を光らせることで視線を集めよう…!

使用したブラシ:鉛筆とエアブラシ

ひたすら描き進める

ここまで出来たら後は完成を目指してひたすら描き進めます。

描き進めるときは暗い所から光の当たっている所を掘り起こしていく感覚で、基本的に明るい部分を描き起こしていきます。

早く仕上げるために一番最初にメインのキャラ部分を集中的に描きます。この際、アウトラインを最初に決めてしまうとより早く仕上げることができます。

使用したブラシ:エアブラシ、シャーペン、にじみ縁水彩

エアブラシとシャーペンでざっくり描いて…

アウトラインを決めたら中身の見せどころを最初に描き起こして…

なんかいい感じに質感を描いて…

レイヤーを全部結合して微調整をしたら大体完成!!

最後の調整

このままだと物足りないのでフォトショに移動して効果をかけたり、コントラストを調整したりしていきます。

今回は逆光のフィルタをかけた後に、色収差の効果をかけました。
もともと使用している色数が少ないので、色収差をかけることで色の幅を出します。

完成したのがこちらです。ラフから完成までかかった時間は8時間程でした。

まとめ

絵を描く前の段階で考えることを済ませておくと、描いている最中に迷わずに描き進められて、結果仕上がるのが早くなるよ、という内容でした。
振り返ってみると当たり前の事なのですが、絵の描き方のうちの一つとして見て頂けると嬉しいです。

最後までお付き合いいただきありがとうございました。

お疲れ様です。2Dイラストチームのtana-Kです。

この記事はKLab Creative Advent Calendar 2018 の 16日目の記事になります。

はじめに

KLabのクリエイティブでアドベントカレンダーを作るから執筆して欲しいと言われたのですが、2Dイラストのテクニックってネットでも書籍でもあらゆるものが出尽くしてしまっているので正直何を書いたらいいのか悩みました。
新社会人イラストレーター向けに、たくさんFBを食らった時でもメンタルを保つ方法や、自分がクリスタで使ってる機能の紹介など考えましたがいまいち訴求力に欠ける…。
そこで考えたのが「IPイラストレーターってどんなお仕事?」というテーマです。
KLabの特色といえば他社IPに強いところ。
そのメイン部分を担うIPイラストはどのようにして作られているのか、なかなか知る機会はないかなと思い書いてみることにしました。

だがしかし、私がメインで関わっている某IPの2Dイラストをアドベントカレンダーで使うのはNGということでしたので、技術書典4で私が描かせていただいた同人誌の表紙を使い擬似的にIPイラストレーターの仕事内容を紹介したいと思います。


こいつです。

IPイラストレーターのお仕事とは

基本的な工程は非IPのイラスト業務とほぼ同じですが、各工程で版元チェックが入ります。

絵柄の分析・練習

配属されてすぐに商材として使うものを描くわけではなく、ある程度の練習期間を設けており、KLabではこの工程をキャッチアップと呼んでいます。

キャッチアップの期間は先人たちの築き上げた「絵柄似せマニュアル」を参照しつつ本番と同じ環境で練習していきます。

絵柄似せマニュアルは、原作キャラのボディバランスや顔の比率などを可能な限り数値化することで、主観に頼らず短期間で原作の絵柄を再現できるような資料になっています。

IPイラストレーターって楽しい?

絵を通して表現したいテーマがある、もしくは自分の名前を売りたいという創作意欲の高いタイプの絵描きにとっては正直つらい仕事だと思います。
私は絵を描くという作業そのものと、案件の絵柄に可能な限り近づけつつ魅力を引き出すための技術を勉強したり実験したりするのが好きなので、今の仕事は楽しいし天職であると思っています。

また、様々なジャンルのIPイラストを経験していくことで、突出したオリジナリティは無いがどんなものでもある程度以上のレベルで描ける器用さを得られます。
器用貧乏なんて言葉がありますがなんでも描ける絵描きは流行に左右されないので食いっぱぐれが少ないです。

長々となりましたが、IPイラストレーターという職業に興味が湧いた、または色々な絵柄にチャレンジすることで技術を磨きたいという方は是非ともKLabの門を叩いてみてください。