皆様、お久しぶりです。
アメーバ事業本部 経営本部局の小林です。
本日は以前、お知らせさせて頂きました、クリエイティブサマーインターンシップ2014についてレポートさせて頂きたいと思います。

本年度は時流を踏まえ、ソーシャルゲームコースとスマートフォーンコースの
2コースでの開催となりました。

①ソーシャルゲームコース
└当社で開発・運営しているカードゲーム「天空のクリスタリア」を題材としたコース

②スマートフォンコース
└当社開発・運営している「Ameba 新スマートフォンプラットフォーム」を題材としたコース

今回は後半に行われたスマートフォーンコースについてごレポートさせて頂きます!
(※ソーシャルゲームコースについてはコチラ)

今年のインターンシップは・・・・・・!

当社のインターンシップといえば、トレンド最前線を体験できる実践形式の課題!
昨年は「アメーバピグ」のエリアの制作からリリースまでを課題としましたが、
今年は、当社で開発・運営しているAmeba新プラットフォームを題材に、
14名のインターン生が4チームに分かれて制作にチャレンジしました。

【主なカリキュラム・イベント】
①「Ameba 新スマートフォンプラットフォーム」についての講義
②情報設計についての講義
③スマートフォンにおけるフロントエンド開発についての講義
④中間発表
⑤社内見学
⑥社員との交流ランチ
⑦最終発表
⑧公表会

【課題】
スマートフォーンコースの課題はずばり「新規会員獲得」。
Amebaの新しい「スマートフォンプラットフォーム」の未登録者用TOPページの改修がお題となりました。

一見、簡単そうなお題ですがアメーバの全サービスへの入り口となる大事なページです。
実はトレーナー社員の間でも「難問だね」という声が多くあがる課題となりました。

1 pixel|サイバーエージェント公式クリエイターズブログ

記念すべき3回目

今回は、サイバーエージェント初のインターンシップから入社した新入社員が、
インターンのトレーナーとして初めて参加した記念すべき回でもあります。

前回インターンのお知らせにも登場した一昨年インターンシップに参加した2012年の新入社員にレポートを依頼しましたので、是非お読みください!

トレーナ社員から

2012年度入社でデザイナーの山幡です。
私は2010年の7月に行われたクリエイティブサマーインターンシップから
今年入社した新入社員です!
なんと、今年は念願かなってインターンのトレーナーとして参加させて頂きました!

【毎年現れるインターン生にとっての壁】
レポートという事なのですが、今回はインターン生が必ず出会う壁について、
トレーナーの視点から書かせて頂きたいと思います。

今年のインターン生が特に苦労していたところ、制作における「壁」は大きく2つありました。
その壁の1つは「Amebaサービスの魅力を伝える方法」の部分だったと思います。

僕自身もそうでしたが、学生のうちはとにかく色んなアイデアが出てきて、
「システムを1から作ってしまえ!」「とにかくエフェクトをかけて動かしてみよう!」
という大枠のみを見た意見になりがちです。

結果、内部にあるサービスをいかに魅力的に魅せるかではなく、
いかに「新しいインパクト」のあることをして人を引きつけるかの方に注力してしまっていたと思います。

「とにかくおもしろいことをして人を集める」のではなく、「内部のサービスの魅力を最大限に広げてみせるための最低限の手法を使う」というのが、学生の制作と会社の制作の違うところです。
インターン生とのやりとりでは、主にその違いについてのアドバイスをしていました。

1 pixel|サイバーエージェント公式クリエイターズブログ

【試行錯誤】
また、今回はせっかくの機会なので、ものづくりでありがちな「停滞」に気づいてもらうため、
かなり細かい部分の指摘を何回も出しました。

特にデザイン面でその「停滞」が見受けられました。
どの班もどこにどの色を置くかを一度決めると、そこでデザインが止まってしまい、
その結果企画は良いのにデザインの精度が上がらない事態に・・・・・・。

確かにレイアウトもデザインの内ですが、そこにかける処理を怠るってしまうと、
そこから先の良いデザインは産まれません。
たった一箇所、1つのアイコンに対して試行錯誤して、色んな人からフィードバックを受けて修正を繰り返す。

僕自身、会社でやるこの作業がスキルアップにつながっていると実感していたので、
インターン生にはこの試行錯誤を経験して帰って欲しいと思い、重点的に繰り返し
試行錯誤の為のヒントを出し続けました。

1 pixel|サイバーエージェント公式クリエイターズブログ

インターン生には辛い指摘だったと思いますが、どのインターン生も指摘のひとつひとつの意味を噛み砕いてよく消化してくれたと思います!

【最終発表】
実は、内心どの班も「どう魅せるか」の企画の部分にとても多くの時間を使っていて内心は実装の時間が十分に取れないのでは?と不安になる事もありました。

しかし、最終発表では必要なエフェクトやレイアウトをうまく取り入れながら、
内部のサービスの魅力をたっぷり引き出した成果物が上がってきたので本当に嬉しかったです!

僕らを担当してくれたトレーナーの社員もこんな気持ちだったのだろうかと思うと、
感慨深いものがありました(笑)

$1 pixel|サイバーエージェント公式クリエイターズブログ

【トレーナーとして勉強になったこと】
僕は4月に入社したばかりなので、会社で人に教える機会は今の所そう多くありません。
今回トレーナーとして参加する事で、自分が教えたいことをどう言えば伝わるかを知ることもできたし、何より普段先輩がどういう意味を持って教えてくれているのかも知ることができました。

インターン生もトレーナーも経験した立場から、一つ言えることがあるとすれば
「サイバーエージェントのインターンシップに参加してみてほしい!」これに尽きます。

今年のインターンは終了してしまいましたが、年内には学生版クリエイティブアカデミー
控えていますので、こちらも是非参加してみてください!

来年のインターンに応募しようかな?と思っている方、僕自身も次回もよりパワーアップしたトレーナーとしてあえることを楽しみにしています!


2012年6月にサービスを開始したスマートフォン向けソーシャルゲーム ULTIMATE RACERアルティメットレーサー)でデベロッパーをしている横田と申します。

今回はこの弊社提供のサービスULTIMATE RACERの開発時に、画面内のアニメーションをjQueryのanimate処理からcssアニメーションに切り替えた経緯について、自分なりの考察ともにご紹介したいと思います
対象としてはデザイナー、マークアップエンジニアでJSやcssでアニメーションの実装を始めたばかりの方が最適だと思います。

また本稿はjQueryのバージョン1.7.1を使用した場合を想定して記述しています。

ULTIMATE RACER(アルティメットレーサー)のご紹介

最初に簡単ですがULTIMATE RACERのご紹介をさせていただきます。
ULTIMATE RACERはスマートフォン向けのソーシャルゲームで、毎週入れ替わるグループ内のプレイヤー同士でレースをして順位を競うゲームです。
そのレースに使用するマシンが3D(3Dプリレンダリンググラフィック)になっているのが特徴のゲームです。
よろしければ一度ぜひお試しください。

1 pixel|サイバーエージェント公式クリエイターズブログ

ULTIMATE RACER
$1 pixel|サイバーエージェント公式クリエイターズブログ-UR_URL

※スマートフォン専用サービスとなっています。スマートフォンでアクセスしてください。


またもう一つの特徴が画面のUIにアニメーションを多用している点です。

マイカート(マイページ)のUI例




今回はこのUIアニメーションの開発時のお話をさせていただきたいと思います。


jQueryからCSSアニメーションに切り替え

ULTIMATE RACER開発初期の主にPC画面で開発している期間はUIアニメーションをjQueryのanimateを使用して制作を進めていました。
PCではあまり問題を感じなかったのですが、いざ実機確認するとストレスを感じる程に辛そうにアニメーションするのが分かりました。
ユーザーが使いやすくなるようにアニメーションさせているのに、これでは逆に使いにくくゲームの楽しささえ損ねてしまうことになりかねません。

そこでjQueryによるアニメーションをほかのアニメーション処理に差し替えることにし、その際に導入においての学習コストが軽いCSSによるアニメーションを選択しました。

まずはjQueryによるアニメーションと、CSSによるアニメーションについて簡単にご紹介させていただきます。


jQueryのanimateによるアニメーション

jQueryにはanimateというメソッドがあり、簡単に要素をアニメーションさせながら変化させることができます。

例えば、leftの値が0の要素をanimateで{left: 1000}とした場合、leftの値が0pxから1000pxに徐々に変化し要素は右へアニメーションしながら移動していきます。
このときjQueryの内部処理としては、setIntervalというJavaScriptのタイマー機能を使って毎1ミリ秒毎に処理を繰り返して徐々に変化させているのです。

しかしこの一回ごとの処理は、様々な要因で処理が追いつかなくなった場合、実行が後回しになってしまいスキップされたように見えてしまいます。(※setInterval()の細かな仕様ついてはここでは触れません)
そのため処理能力が低いと意図したアニメーションを再現することはできません。
これがPCブラウザでは滑らかに見えても実機ではぎこちなくなる原因となります。

以下に用意したサンプルをPCとiPhone/Android実機でそれぞれでご確認ください。

jQuery animeの処理のステップ確認 - jsdo.it - http://jsdo.it/perma/59n1
var box = $('#box');

function countStep(){$('span').append( "|")};//spanタグにパイプを挿入

$('#start').click(function() {

box.css('left', 0).stop();//css初期化
$('span').empty();//ゲージ初期化

box.animate({ left: 1000 },{//animateメソッドで左に移動
duration: 1000,
step: function() {
countStep();//ステップごとにゲージ増加
}
});

box.animate({ left: 0 },{//animateメソッドで元の位置へ
duration: 1000,
step: function() {
countStep();
}
});

});
$1 pixel|サイバーエージェント公式クリエイターズブログ-UR_jsdo_59n1
このサンプルではanimateの引数のstepで、アニメーション中に値が書き換わった(処理が行われた)ごとにゲージが伸びるようにしたものです。
これでステップ数をPCと実機で比較してみると、実機ではPCよりステップ数がかなり少なくなります。

つまりjQueryのanimateはかなり重い処理を行っており、PCに比べて処理能力の低いスマートフォンのアニメーションには向かないと言えます。
ULTIMATE RACER開発時にぶつかったのがこの点です。


CSSによるアニメーション

そこで選択したのがcss3から追加されたcssのアニメーションです。
このcssを使ったアニメーションはanimationsとtransitionsの二つのプロパティがあります。
どちらもJSで要素を操作するのではなく、ブラウザ自身のエンジンを使用するため挙動が比較的軽いという特徴が共通しています。
また、どちらもcssのみでは(hover等の疑似要素でしか)スタート/エンドのタイミングを制御が難しくある程度複雑なアニメーションの場合はJSでの制御が必要になります。

以下に、上記のjQueryのanimateで行ったものと同じ動きを、animationsとtransitionsの二つのプロパティで再現したものをご用意しました。

animations

特徴してはkeyframesを使って細かい設定が出来る点と、ループ処理が手軽に出来る点です。

CSS animations - jsdo.it - http://jsdo.it/perma/h9Fw
#box { 
width: 100px;
height: 100px;
border: 1px solid #333;
background: #EEE;
position: relative;
top: 0;
}

#box.moveBox {
left:0;
-webkit-animation-name: moveBoxAnimation; /*@keyframesで定義したアニメーション名*/
-webkit-animation-duration: 1s; /*アニメーションにかかる時間*/
-webkit-animation-iteration-count: 1; /*ループ回数*/
}

@-webkit-keyframes moveBoxAnimation { /*キーフレームルールの定義*/
0%{ /*始点*/
left: 0;
}
50% { /*中間点 複数設定可能*/
left: 1000px;
}
100% { /*終点*/
left: 0;
}
}
$1 pixel|サイバーエージェント公式クリエイターズブログ-UR_jsdo_h9Fw
@keyframesでアニメーション名と細かい動きの定義ができ、それを別で記載しておきます。
そのうえで適用したい要素に定義したアニメーション名と、ループの定義をします。
@keyframesの定義によってはかなり複雑な動きも再現できそうです。

transitions

特徴としては細かな動きの設定が難しい点と、ループさせられない点。

CSS Transitions - jsdo.it - http://jsdo.it/perma/x0MP
#box { 
width: 100px;
height: 100px;
border: 1px solid #333;
background: #EEE;
position: relative;
top: 0;
left:0;
-webkit-transition-property: left;
-webkit-transition-duration: 1s;
-webkit-transition-timing-function: linear;
}

#box.moveBox {
left:1000px;
}
1 pixel|サイバーエージェント公式クリエイターズブログ-UR_jsdo_x0MP
アニメーションさせたい要素に、アニメーションさせたい値とアニメーションの定義をします。
該当の要素の該当の値が変化すると設定した定義に従ってアニメーションします。
ループは出来ませんが、記述が簡単で画面のUIのアニメーションに向いていそうです。


実際の改修例 サブメニューの改修

ULTIMATE RACERではゲームという特性上ユーザーのアクションで一回だけ変化する要素が多く、その部分では主にtransitionsを使用しました。
animationは@keyframes設定が煩雑であったのとその管理コストかかりそうだったためです。
(ただし点滅する文字、アイコンといった永続的なループ処理が必要なものにはanimationを採用しています)

$1 pixel|サイバーエージェント公式クリエイターズブログ
※この点滅する文字のアイコンにはanimationを使用しています

以下に、画面の横から出てくるサブメニューを、jQueryのanimateからcssのtransitionsに書き換えた例をご紹介します。

1 pixel|サイバーエージェント公式クリエイターズブログ

jQueryのanimateの場合

/* メニューを表示する jQueryのanimateの場合*/
var subM = $("#sub");
var Main = $("#main");
var bothM = $("#sub,#main");

$(document).on("click",".showSub", function() {//表示ボタン押下したら

subM.css({"visibility":"visible","opacity":"1"});//非表示解除

subM.animate( //アニメーションしながら
{left : "60px"},//サブメニューの移動位置(=メインコンテンツが残るスペース分)
500);
Main.animate({
right : "260px" //メインコンテンツの移動距離
}, 500);


Main.css({"min-height" : "1000px"}); //サブメニュー以上の高さ指定
Main.prepend("<div id='mask'></div>"); //メインコンテンツ上を押下したら戻るマスク用意
});

/* メニューを非表示する jQueryのanimateの場合*/
$(document).on("click","#mask,#sub header", function() {//マスク、サブメニュー上部押下したら

subM.animate(
{left : "320px","opacity":"0"},//もとの位置へ
{
duration: 500,
complete: function(){subM.css({"visibility":"hidden"});}//アニメーション完了後に非表示
}
);
Main.animate({
right : "0"//もとの位置へ
}, 500);


  Main.css({"min-height" : "0"});//高さ指定解除
$("div").remove("#mask");//マスク解除
});

cssのtransitionsの場合

/* メニューを表示する cssのtransitionsの場合*/
var subM = $("#sub");
var Main = $("#main");
var bothM = $("#sub,#main");

$(document).on("click",".showSub", function() {//表示ボタン押下したら

subM.css({"visibility":"visible","opacity":"1"});//非表示解除+透過アニメーション
bothM.css({
'-webkit-transform' : 'translate(-260px,0)',//移動距離、アニメーション設定はCSS
});


Main.css({"min-height" : "1000px"});//サブメニュー以上の高さ指定
Main.prepend("<div id='mask'></div>"); //メインコンテンツ上を押下したら戻るマスク用意
});

/* メニューを非表示する */
$(document).on("click","#mask,#sub header", function() {//マスク、サブメニュー上部押下したら

subM.css({"opacity":"0"});//透過しながら
bothM.css({
'-webkit-transform' : 'translate(0,0)',//もとの位置へ、アニメーション設定はCSS
});


subM.on('webkitTransitionEnd', function () {//アニメーション完了後イベント設定
subM.css({"visibility" : "hidden"});//アニメーション完了後に非表示
subM.off('webkitTransitionEnd');//アニメーション完了後イベント設定解除
});


Main.css({"min-height" : "0"});//高さ指定解除
$("div").remove("#mask");//マスク解除
});

■cssにアニメーション設定
#sub,
#main {
-webkit-transition:-webkit-transform 500ms linear,opacity 500ms linear;
}

それぞれ赤くハイライトしたanimateメソッドの部分と、transformを設定しているcssメソッドがそのまま差し変わっています。この改修にhtmlの変更は行っていませんし、cssも動かす要素にアニメーションの設定を一行追加しただけです。

※ただし注意していただきたいのが黄色くハイライトした部分でアニメーション終了のタイミングでvisibilityを切り替える処理の部分も変わっている点です。
jQueryはanimateの引数のcompleteでタイミングを取って処理していますが、transformの場合はwebkitTransitionEndをイベントリスナーとして要素に登録して処理しています。
この際、一回毎に要素からwebkitTransitionEndの登録を削除しないと、同じ処理を繰り返した場合に意図しない不具合が発生することがあります。ご注意ください。


以下に前後の動きの動画をご用意しました。

サブメニュー jQueryのanimateの場合



サブメニュー cssのtransitionsの場合



かなり動きが滑らかになっているのが分かるかと思います。
こういった改修を全体に施し、ユーザーに快適にプレイをしていただけるように努力を続けています。


最後に

最後までお付き合いいただきまして、本当にありがとうございます。

動画がiPhoneでのデモンストレーションのみで、多くの方が「Androidはどうなの?」と思われているかと思います。
正直Androidは機種間の差異が激しくjQueryのanimateはもちろん、cssアニメーションでもぎこちなくなる端末があります。
ただしjQueryによるアニメーションよりは大きく改善することは明らかです。
実際にその動きを確認されたい方というは、ぜひぜひULTIMATE RACERを遊んでみて確かめていただければと思います!

今回は開発時の苦労話のご紹介ということで、多少お恥ずかしい部分も含めてご紹介させていただきました。
スマートフォンが対象でありながら実機での画面確認を後に回してしまったことも、jQueryのanimateが重いことを知っていながら開発を急いで安易に選択してしまったことも非常に反省しています。
スマートフォンでサービスを制作する際、PCとはまったく違う感覚で、よりユーザーの立場を考慮した制作を心掛けなければならないと再認識させられました。

今後とも、より良いサービスを目指して改善を続けていきますので、何卒よろしくお願いします。
まだまだ技術的にも未熟で足らない部分が多々あるかと思います。
今回の内容についても不備や誤認識等ありましたら、ご指摘いただければ幸いです。

最後に、改めてではありますが、何卒ULTIMATE RACERをよろしくお願いします!

ULTIMATE RACER
$1 pixel|サイバーエージェント公式クリエイターズブログ-UR_URL
※スマートフォン専用サービスとなっています。スマートフォンでアクセスしてください。


こんにちは、ピグディビジョンでFlashやってますnbhd(@nbhd)です。バイク乗りです。好きなゲームは鉄拳です。野ステ山ステは標準装備です。


FlashPlayer 11.4よりWorkerというマルチスレッド機能が搭載されました。従来の処理では処理落ちの原因になっていたような重い処理も、プログラムの実行を複数に分けることで解決できるようになります。使用するための条件はあるものの、描画処理と演算処理を別軸で実行できるのは非常に魅力的です。

今回はこのWorkerについてお話します。つい先日FlashBuilder 4.7 ベータ版がリリースされたので、併せてご確認ください。

Workerサンプルコード

Workerがどういったものか調べるためにサンプルコードをgithubに用意しました。
https://github.com/nbhd/1pixel/blob/master/worker/src/workertest.as
描画、演算ともに重い処理を記述してあります。
L85, L88のメソッドを切り替えることで、シングルスレッド、マルチスレッドの違いを確認することができます。

描画処理(400個のSpriteを同時に動かす)
private var seed:Number = 1;
private const MAX_BALL:int = 400;
private function enterFrameHandler(e:Event):void
{
   var i:int;
   var ball:Sprite;
   var radian:Number;
   seed += 0.00001;

   for (i = 0; i < MAX_BALL; i++)
   {
      ball = balls[i];
      radian = i * seed * Math.PI * ((1 + Math.sqrt(5)) * .5);
      ball.x += ((Math.cos(radian) * i) - ball.x) * .5;
      ball.y += ((Math.sin(radian) * i) - ball.y) * .5;
   }
}
演算処理 (for文で1億回まわす)
private const HARDCOST_LOOP:int = 100000000;
private function hardCostMethod():void
{
   var n:int = 0;
   var i:int;
   for (i = 0; i < HARDCOST_LOOP; i++)
   {
      n = i;
   }
}
まずはシングルスレッドで上述の処理を実行してみましょう。
singlethead
logに出力されている"left time"は起動までにかかった時間を示しています。演算処理でつっかえて、起動までに5870msかかっていることが分かります。
なんと重いのでしょうか。

今度は同じ処理をマルチスレッドで展開してみましょう。描画処理をメインスレッドで、演算処理をサブスレッドで実行した結果が以下のものになります。multithread1
なんと起動までにかかった時間は21msです。そして以下がサブスレッドの演算処理にかかった時間を受信したものになります。
multithread2
演算処理の終了までにかかった時間は5988msで、シングルスレッド版との差は118msほどマルチスレッド版のほうが遅い結果となりました。

まとめ

今回のケースではマルチスレッドを選択したほうがパフォーマンスの向上が得られることが分かりました。このようにマルチスレッドプログラミングは非常に難しいものではありますが、適切に使いこなすことが出来れば強い武器になります。プログラム全般に言えることですが、シンプルな設計、シンプルな実装を心がけておくことが重要です。

おまけ

スレッド間のデータのやりとりの一例をご紹介します。サブスレッドの準備が整ったところで、メインスレッドからサブスレッドに"worker ready"を通知し、それを受信したサブスレッドはメインスレッドに値を送り、その値をworkerToHandler内で加工しています。send()で値を順番に送り、receive()で値を順番に取得するこのやりかたは、ByteArrayでデータをやりとりする時のイメージと近いなと思いました。

private var mainTo:MessageChannel;
private var workerTo:MessageChannel;

// メインスレッドから送られてきたメッセージを処理する
private function mainToHandler(e:Event):void
{
     if (e.target.messageAvailable == false) return;
     var message:* = mainTo.receive();
     workerTo.send(message);

     if (message == 'worker ready')
     {
          workerTo.send(1);
          workerTo.send(2);
          workerTo.send(3);
     }
}

// worker の state を見張る
private function workerToHandler(e:Event):void
{
     if (e.target.messageAvailable == false) return;
     var message:* = workerTo.receive();

     if (message == 'worker ready')
     {
          // 送られてきた順に値を取得する
          var inc:int = workerTo.receive() + workerTo.receive() + workerTo.receive();
     }
}


[参考リンク]
http://cuaoar.jp/2012/07/actionscript-worker-flas.html
http://esdot.ca/site/2012/intro-to-as3-workers-hello-world

はじめまして。こんにちは。
スマートフォン版Amebaプラットフォームでフロントエンドの開発を担当している遠藤と申します。
今回は、新しくなったAmebaのCSSに関するお話を簡単にさせていただこうと思います。



$1 pixel|サイバーエージェント公式クリエイターズブログ-画面イメージ

Stylus

最近多く聞くようになったCSSメタ言語は、SassやLESSが有名ですが、今回はStylusというものを採用しました。
新しいAmebaはnode.jsを採用しており、当初HTMLテンプレートの選定でexpressモジュールのデフォルトでも使われているJadeが候補となり、それと同時にCSSではStylusが挙がりました。
JadeとStylusは作者が同じexpressモジュールの開発者であるため、expressモジュールと相性がよく、記法が似ていてタブによる階層構造で記述するのが特徴です。
Stylusは初めて触りましたが、SassとLESSが持っている機能は基本的に兼ね備えており、コードの見通しの良さと、変更のしやすさから採用を決定しました。
Stylus

サンプルコード(Stylus)

// common prop
com-bdc = #ccc
com-bd = solid 1px com-bdc
fr = 4px

// mixin
vendor(prop, args)
  -webkit-{prop} args
  {prop} args

border-radius(var)
  vendor('border-radius',var)

// style
.extend_sample_class
  display block
  border com-bd

.sample_box
  @extend .extend_sample_class
  border-radius(fr)
  &.heading
    border-bottom com-bd
  .sample_container &
    background red

最初は見慣れないので書き辛いように感じますが、慣れてくると{}や:;を書かなくてもいいことと、セレクタの親子関係をタブ一つで変えることができるので、コーディングが非常に楽になります。


サンプルコード(CSS)

.extend_sample_class,.sample_box{
  display:block;
  border:solid 1px #ccc;
}
.sample_box{
  -webkit-border-radius:4px;
  border-radius:4px;
}
.sample_box.heading{
  border-bottom:solid 1px #ccc;
}
.sample_container .sample_box{
  background:#f00;
}


StylusとCSSファイル

Stylusのファイル構造を以下のようにすることで、変更があった際に対応しやすくしています。
基本的に追加・改修が頻繁に発生するため、可能な限り共通化を行い、ユニークなレイアウトのみ、各画面用のstylusに記述する形をとっています。

  • ノーマライズ
     各ブラウザ間で相違のあるstyleの共通化
  • 共通変数&mixin&extend
     共通で使う色やサイズの変数、mixin、extendの定義
  • コンポーネント
     共通で使うCSSコンポーネント定義
  • 共通モジュール
     共通で使うCSSモジュール定義
  • 共通全体import
     共通系stylusのimport
  • 各画面用スタイル
      各画面ごとの特殊なstyleの定義

StylusファイルとCSSファイル構成イメージ


node.js+expressモジュールを使う前提であれば、Stylusのコンパイルはnodeで行えますが、そういう環境ではない場合、Macの方にはCodeKitというツールがお勧めです。
CSS系ではStylusだけでなく、SassやLESSもコンパイルできますし、CofeeScriptやHTMLテンプレートのJadeやHamlなども対応しています。
liveReloadも行なってくれるので、かなり開発が楽になります。
CodeKit


サムネイル画像

ユーザやアプリなどのサムネイル画像の表示方法ですが、今回はimg要素ではなく、i要素に背景として設定するようにしています。
サムネイルの上にオンライン表示のマークや編集アイコンをかぶせたり、サムネイル自体にCSSで装飾を施す場合、img要素は空要素なのでCSSの擬似要素である::beforeや::afterを使って要素の追加をすることができません。
また、画像のサイズ変更の際に、img要素にしていると変倍されてしまったり表示崩れの原因になることもあります。
マークアップとしては本来img要素にすべきですが、デザイン変更を柔軟に行うためにi要素の背景としました。
i要素は代替音声や気分などを表す要素で、img要素の代替として使用するのに適していると判断しました。

デザイン

1 pixel|サイバーエージェント公式クリエイターズブログ-サムネイル

サンプルコード(Stylus)※一部抜粋


border-radius(size)
  vendor('border-radius',size)

box-sizing(prop)
  vendor('box-sizing',prop)

square(size)
  height unit(size,'px')
  width @height

.thumbnail
  display inline-block
  box-sizing(border-box)
  square(50)
  border solid 1px #ddd
  background-color #fff
  background-size cover
  background-repeat no-repeat
  background-position center center
  border-radius 2px
  &.online
  &.offline
    position relative
    &::after
      content ''
      position absolute
      bottom 1px
      left auto
      right -4px
      display block
      box-sizing(border-box)
      height 14px
      width @height
      margin 0 5px -1px 0
      border solid 1px #fff
      background-color #ccce
      border-radius(@height/2)

  &.online
    &::after
      background #6fc10d

  &.large
    square(100)
    border-radius 4px
  &.small
    square(40)

サンプルコード(CSS)


.thumbnail {
  display: inline-block;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  height: 50px;
  width: 50px;
  border: solid 1px #ddd;
  background-color: #fff;
  background-size: cover;
  background-repeat: no-repeat;
  background-position: center center;
  -webkit-border-radius: 2px;
  border-radius: 2px;
}
.thumbnail.online,
.thumbnail.offline {
  position: relative;
}
.thumbnail.online::after,
.thumbnail.offline::after {
  content: '';
  position: absolute;
  bottom: 1px;
  left: auto;
  right: -4px;
  display: block;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  height: 14px;
  width: 14px;
  margin: 0 5px -1px 0;
  border: solid 1px #fff;
  background-color: rgba(204,204,204,0.933);
  -webkit-border-radius: 7px;
  border-radius: 7px;
}
.thumbnail.online::after {
  background: #6fc10d;
}
.thumbnail.large {
  height: 100px;
  width: 100px;
  -webkit-border-radius: 4px;
  border-radius: 4px;
}
.thumbnail.small {
  height: 40px;
  width: 40px;
}

アイコン

サイトを彩る上で欠かせないアイコンですが、スマートフォンの場合、解像度別に画像を作成して、配置を調整して…とやると、変更、追加が多いサービスでは運用しづらくなってしまいがちです。
また、画像が多くなるにつれて、スプライトで実装をしても、画像ファイルが重くなり、あとから表示されてしまいます。
これを多少でも改善するため、アイコンをフォントで作成しました。

デザイン


これにより、大きさや色の違うアイコンであっても、作り直す必要もなくなり、重くなり過ぎて後から表示されるようなことも改善されています。
コードは、ちょっと不恰好ですが、各文字のクラスに対応した文字が入るものを用意しました。
記号に当たるものに限り、名称をつけています。

サンプルコード(Stylus)※一部抜粋

symbols = "a","b","c","d","e","f","g","h","i","j"
marks = ('footstamp' '"')('add' '+')('back' '<')('allow' '>')('crown' '¥')('alert' '!');

.i
  display inline-block
  font-family 'AmebaSymbols'
  font-size 2.0rem
  line-height 1

.icon
  @extend .i
  color tc-default !important
  vertical-align -.2em

  for t in symbols
    &.{t}::before
      content t

  for key,index in marks
    &.{key}::before
      content key[1]

サンプルコード(CSS)

.i,.icon{
  display:inline-block;
  font-family:'AmebaSymbols';
  font-size:2rem;line-height:1;
}
.icon{vertical-align:-.2em;}
.icon.a::before{content:"a"}
.icon.b::before{content:"b"}
.icon.c::before{content:"c"}
.icon.d::before{content:"d"}
.icon.e::before{content:"e"}
.icon.f::before{content:"f"}
.icon.g::before{content:"g"}
.icon.h::before{content:"h"}
.icon.i::before{content:"i"}
.icon.j::before{content:"j"}
.icon.footstamp::before{content:'"'}
.icon.add::before{content:'+'}
.icon.back::before{content:'<'}
.icon.allow::before{content:'>'}
.icon.crown::before{content:'¥'}
.icon.alert::before{content:'!'}

スプライト画像の扱い

スマートフォンサイトでは、画像を最適に表示するために、1倍、1.5倍、2倍のサイズ違いの画像を用意剃る必要があります。
装飾で使う画像はCSSスプライトという技法を用いることが多いですが、変更や追加で画像サイズに変更がある場合、CSSのバックグラウンドサイズも変えなければなりません。
Stylusには、画像のサイズを取得するメソッドが用意されているため、これを採用して運用コストを減らしました。
また、最適な解像度の画像の選択ですが、メディアクエリを用いる方法では、対象端末以外では無駄なコードになってしまうため、javascriptを使って、pixel ratioを取得し、CSSを書き換えることで最適な画像を表示するようにしています。

サンプルコード(Stylus)

image-path = '../../../../public/img/1'
bg(name)
  path = '../img/$ratio/external/'+name
  overflow hidden
  background-image url(path)
  background-size image-size(image-path + '/external/'+ name) style="color:#f00">// 画像のサイズを取得
  background-repeat no-repeat

.bg-sprite
  bg('amb_bg_sprite.png')
  text-indent 100%

.ameba
  display block
  height 18px
  width 85px
  @extend .bg-sprite
  background-position 0 0

サンプルコード(CSS)

.bg-sprite,.ameba {
  overflow:hidden;
  background-image:url("../img/$ratio/amb_bg_sprite.png"); // $ratioをjavascriptで端末のpixelratioに書き換え
  background-size:285px 65px;
  background-repeat:no-repeat;
  text-indent:100%;
}

サンプルコード(Javascript)

function adjustCss(href) {
  var pixelRatio = window.devicePixelRatio || 1;
  // find target css
  var stylesheets = document.styleSheets;
  for (var i = 0; i < stylesheets.length; i++) {
    var stylesheet = stylesheets[i];
    if (stylesheet.ownerNode.getAttribute('href') == href) {
      var rules = stylesheet.cssRules;
      for (var j = 0; j < rules.length; j++) {
        var rule = rules[j];
        var style = rule.style;
        if (style) {
          var bgimage = style.getPropertyValue('background-image');
          if (bgimage && bgimage.indexOf('$ratio') >= 0) {
            rule.style.setProperty('background-image', bgimage.replace(/\$ratio/, pixelRatio), null);
          }
        }
      }
    }
  }
}

最後に

細かい点は色々と端折らせていただきましたが、このような感じでCSSを構築しております。
今回のプラットフォームの構築にあたり、構築過程で大きなデザイン変更が3度ほどありましたが、こういった設計によって、基本的にCSSの変更だけで対応できています。

Stylusに関しては、初めて使用しましたが、Sass+Compassほどの高機能でもなく、LESSよりやれることが多いといった印象で、個人的には非常に使いやすく感じました。
記法への慣れは必要ですが、:や;や{}を書かなくて済むのと、コードが非常に見やすくなるので、今後は積極的に使ってみようと思っています。



こんにちは、ピグディビジョンでフロントエンドのプログラムを書いています。maginemu (@maginemu) です。


今回は「どこでもピグライフ」を制作する上でハマった点を踏まえつつ、スマートフォンwebアプリでのメモリ管理とデバッグ方法について少しだけお話させていただきたいと思います。


どこでもピグライフとは

弊社の提供しておりますPC向けソーシャルゲーム「ピグライフ」をスマートフォン向けに移植したサービスです。『ピグライフのお庭をスマートフォンでも』を目標に移植を行いました。

(「どこでもピグライフ」はPCで「ピグライフ」を開始し、「いちごジュースを作ってみて!」というクエストをクリアするとお使い頂けるようになります)


どこでもピグライフ

PC版ピグライフはFlashでつくられていますが、どこでもピグライフはHTML5/CSS3/Javascriptで制作を行いました。画面遷移をせず、直接エリア上のテーブルや作物をタップして操作したり、メニューを下から引き出したり、ネイティブアプリのような操作を目標にしています。


方針

そもそものメモリ容量が少ないスマートフォン端末で遷移の無いアプリケーションを制作する上で、多少のパフォーマンスを犠牲にしてもこまめにデータを削除する必要がありました。


例えば普段隠れて表示されていない「ピグとも」のビュー部分を画面外に生成しておけば、表示のパフォーマンスは上がるのですが、その分メモリを消費してしまいます。どこでもピグライフでは、ほとんど常に表示されている「お庭」がリソースの大部分を消費してしまうので、表示されていない部分に関しては都度生成/削除するようにしています。


「お庭」に関しても、地面部分をタップするとその周辺の作物が表示され、表示されている領域が一定以上になると、古い領域から表示を消すようになっています。



小さく描いて大きく見せる

メモリ消費を抑えるために、そもそもの描画サイズを小さくするのは重要です。Imageもそうですが、Canvasに画像を描画することは、単純にそのサイズの分メモリを消費します。


どこでもピグライフでは床面の菱形を描画するのにcanvasを用いていますが、等倍のサイズで描いてしまうと、最も拡張されたお庭だと実に1024[幅]x768[高さ]x16[床グループ数]ピクセルものサイズになってしまいます。


そのため256x128ピクセル程度に描画したものをcssで4倍にscalingして表示しています。これで等倍で描画した場合に比べて実際のメモリ使用量がざっと70~80MBも低下します。
表示は荒くなってしまいますが、床面の描画ということもあり、軽量化を優先した部分です。


参照を削除する

画面遷移の無いアプリケーションでは、その要素をこまめに削除してその分のメモリを開放することが重要になります。


基本的なところとして、要らなくなったデータを開放するためには、ガベージコレクタに削除してもらう必要があり、つまり参照を確実に削除する必要があります。


参照が削除されて、データがヒープ領域から開放されたことを確認するためにWeb Inspectorでヒープスナップショットを撮ることができます。


下記のような簡単なスクリプトを考えてみます。起動時に幾つかのオブジェクトを生成して、キーボードで「a」をタイプするとviewにあたるオブジェクトをModelのメンバーから削除します。


(function() {
    var ns = {};

    // viewを保持
    ns.Model = function() {
        var d = new ns.Delegate(this);
        this.view = new ns.View(d);

    };

    // viewメンバーを削除する
    ns.Model.prototype.removeView = function() {
        delete this.view;
    };

    // modelを保持
    ns.Delegate = function(model) {
        this.m = model;
    };

    // delegateを保持
    ns.View = function(delegate) {
        this.d = delegate;
    };

    // 生成
    var model = new ns.Model();

    //
    // "a" がタイプされたら model.viewを削除する
    //
    window.addEventListener('keydown', function(e) {
        if (e.keyCode === 65) {
            model.removeView();
        }
    });
}());

このスクリプトを含むページを開き、Web Inspectorを起動します。View > Develop > Developer Tool とメニューを辿って開きます。Macであれば Command + Option + i がショートカットになっています。


Profiles > Take Heap Snapshot を選択し、Startをクリックしてスナップショットを撮影します。



このとき自動的にGCが走るので、GCについては気にしなくても大丈夫です。


取得されたSnapshot1を見てみると、ns.Model, ns.View, ns.Delegateが存在しているのがわかります。
さらに例えばns.Viewを選択すると、ns.Modelによって参照されているということもわかります。


Snapshot1

「a」をタイプしてmodel.viewを削除する処理を走らせてから、もう一度スナップショットを撮ります。こうして取得されたSnapshot2ではns.View, ns.Delegateが消えているはずです。


Snapshot2

目視で確認しても良いのですが、下のメニューから Comparisonを選択することで、オブジェクトがどのように変化したのかを調べることができます。



今回の場合はns.View, ns.Delegateについて、# Deltaが–1になっていることが分かると思います。


今回は簡単な例だったので迷わずに削除されたと思いますが、実際のプロジェクトでは意外な参照が残っていてオブジェクトが開放されないといったことが起こる可能性があります。ヒープスナップショットを確認することで、何が原因でオブジェクトが開放されないのかを調べることができます。


DOM要素の増減に注意する

DOM Nodeの存在による消費メモリは javascriptのヒープには現れませんが、ネイティブのメモリを消費しています。Image要素やCanvas要素がネイティブのメモリを消費することはわかりますが、DIV要素などの存在も数が多くなってくると馬鹿にできません。また要素数の増加はCPU使用率の上昇も引き起こします。


Google ChromeのWeb Inspector には現在DOM Node Countを表示してくれる機能があります。Timeline > Memory を開き、監視を開始すると現在のDOM要素数がグラフで表示されます。



その状態で要素の生成→削除が発生する動作を行い、GCボタンを押下してGCを発生させます。このとき要素数が想定通りに上昇→下降するかを確かめます。想定どおりに要素数が変化しなければ、なんらかの原因でDOM Nodeが開放されていないことになります。


生成処理をするとグラフ上でもDOM要素が生成されたのが分かります。


GCボタンを押すとガベージコレクションが行われます。この例ではDOM Node Countが初期の状態に戻りました。


HtimImageElementをpoolする

大量に要素を生成/削除するようなプログラムだと、GCによる開放が間に合わなくなる(若しくは、ブラウザが意図的にキャッシュしているのかもしれません)ケースがあるようです。


少し長くなってしまいましたが、下記のコードは、画像を100件表示し、削除することを繰り返すプログラムです。


(function() {

    var generate_count = 0;

    //
    // url を src に設定した新しいImageを返す
    //
    var loadImage = function(url) {
        var img = new Image();
        img.src = url;
        return img;
    };

    //
    // 読み込むimage (なんとなく3種類)
    //
    var urls = [
        'img/ameba_sq_g.png',
        'img/ameba_sq_r.png',
        'img/ameba_sq_b.png'
    ];

    var urlIndex = 0;
    //
    // 呼ばれる度に異なるurlを返す
    //
    var nextUrl = function() {
        urlIndex++;
        if (urlIndex >= urls.length) {
            urlIndex = 0;
        }
        return urls[urlIndex] + '?cnt=' + (generate_count++);
    };

    //
    // 画像を表示する領域
    //
    var image_area = document.getElementById('image-area');

    //
    // 100件の画像を表示します。
    // 表示した画像はそれぞれ500ms後に削除されます。
    //
    var addremove = function() {
        for (var i=0; i<100; i++) {
            // imageの取得
            var img = loadImage(nextUrl());
            // image-areaに追加する
            image_area.appendChild(img);
            // 500ms後に削除する
            setTimeout(removeImage, 500, img);
        }
    };
    //
    // img を image-area から削除する
    //
    var removeImage = function(img) {
        console.log('removing src:[' + img.src + ']');
        image_area.removeChild(img);
    };
    //
    // 1s 毎に繰り返す
    //
    setInterval(addremove, 1000);
}());

実はこのプログラムはメモリを大量に消費してアプリを不安定にしてしまう可能性があります。というのも


//
// url を src に設定した新しいImageを返す
//
var loadImage = function(url) {
    var img = new Image();
    img.src = url;
    return img;
};

// imageの取得
var img = loadImage(nextUrl());

// image-areaに追加する
image_area.appendChild(img);        

image_area.removeChild(img);

このようにnew Image()すると、そのimageをremoveChildしたとしても、MobileSafariは確保したメモリをなかなか開放してくれないらしいのです。場合によってはクラッシュしてしまうことになります。



これを回避するためには、Imageを使いまわすようにします。既存のImageのsrcに新しいurlを指定すると、Image内部の画像データが開放され、新しいデータが生成されるようです。


そこで下記のようなreuseImageを実装してみます。


var imagePool = [];

//
// url を src に設定したImageを返す
// もしimagePoolにimageがあればそれを使う。
// なければ新しいImageを作成する。
//
var reuseImage = function(url) {
    var img;
    if (imagePool.length < 1) {
        img = new Image();
    } else {
        img = imagePool.pop();
    }

    //
    // release メソッドを追加する
    //
    img.release = function() {

        //
        // releaseされたらimagePoolに追加
        // releaseメソッドを削除
        //
        imagePool.push(this);
        delete this.release;
    };

    img.src = url;
    return img;
};

reuseImageを使うと、imagePoolにImageが存在すればそれを再利用するようになっています。imageのreleaseメソッドを呼ぶとそのImageはimagePoolに入ります。


Imageの取得部分をreuseImageに変更し、


// imageの取得
//var img = loadImage(nextUrl());
var img = reuseImage(nextUrl());

削除部分にreleaseのコールを追加します


//
// img を image-area から削除する
//
var removeImage = function(img) {
    console.log('removing src:[' + img.src + ']');
    image_area.removeChild(img);

    // release
    img && img.release && img.release();
};

このようにすることで、imageの生成が最小限に抑えられ、メモリ使用量を抑えることができます。例えば今回の例だと最初に100だけNodeが増えてそれ以降増えなくなります。



最終的なコードはjsdo.itに書いておきました。(だいぶ整理してしまいました)。



webkit-canvasも同様に

webkitにはbackground-imageに -webkit-canvasを指定することが可能です。これは


background: -webkit-canvas(canvas_id);

のように任意のcanvas_idを指定しておくと、javascriptから


var ctx = document.getCSSCanvasContext(‘2d’, ‘canvas_id’, width, height);

のようにCanvasContextを取得できる仕組みです。


非常に便利な仕組みですが、このとき用いるcanvas_idについても、ユニークなidを次々と生成して使用していると、前述のImage要素と同様にCanvasContextがなかなか開放されなくなってしまいます。


Imageの場合と同様に、使わなくなったcanvas_idを再利用するような実装をすると、以前そのidに紐付いて使われていたデータが開放されて新しいcanavsが生成されるようです。


Canvasについてもソースコードをjsdo.itにアップしました



まとめ

ピンポイントなお話になってしまいましたが、いかがでしたでしょうか。ChromeのWeb Inspectorが高機能で非常に便利です。 DOM Node Countの表示機能などはどこでもピグライフ開発中に追加され、歓喜したのを覚えています。


偉そうに記事を書いたものの、解釈の間違いがあるかもしれません。何を言っているんだ、という部分があればご指摘頂ければと思います。


どこでもピグライフは今後もまだまだパフォーマンス改善、機能追加を行なって参りますのでどうぞよろしくお願いします。



written by
maginemu
twitter: @maginemu
t-blog: http://magichilli.blogspot.jp/


はじめまして!デザイナーのマチダです。2012年4月に新卒入社し、現在はスマートフォンゲームのイラストを制作しています。

今回はIllustratorでイラストを制作する際の基本や小技についてご紹介したいと思います。
これからIllustratorでイラストを描いてみたいと思っている方に是非読んでいただきたいです!

はじめに-Illustratorの仕組み-

多機能によってグラフィックだけでなく文書作成などマルチに活躍できるIllustratorですが、機能の多さに最初は何をどのように使ったらいいのか戸惑うかと思います。

<illustratorでイラストを描く際に心がけていただきたいこと>
普通絵を描く時は線画を描いて着彩すると思いますがIllustratorは少々違い、"様々な形をした図形を配置"して絵を描きます。図形を上に重ねていくことで形が作られていきます。

↑福笑いのようなパーツが図形です。色紙を切って輪郭を作り、目を貼って口を貼って…と図工の時間にこんな工作をしたことがあるかと思いますがIllustratorのイラスト制作はそれによく似ています。


<illustratorの特徴>
各々の図形の輪郭線(パス)には数値による情報を持っています。変形させると数値も変動するのでどんなに拡大しても形を変えても劣化しない、という点がIllustratorのメリットです。

↑Illustratorのデータを試しにテキストエディタで開いてみると、文字化けしてしまう画像データとは違いデータに含まれる要素のコードが書かれています。


またIllustratorで作った素材はこの情報を保持したままFlashやFireworksにコピー・ペーストで持ち込むことができるのでIllustrator以外のツールでも再編集ができます。

↑ロゴはIllustratorで作りPhotoshopで加工しました。加工はIllustratorでも出来ますがより細かい加工をする場合はPhotoshopやFireworksでの編集をおすすめします。


ではイラストをもとによく使う機能と使い方についてご紹介します。具体的な操作説明ではなく、どんなときにどの機能を使うのか覚えていただけると嬉しいです。

※現在業務にて制作しているゲームは未発表なので、技術研修にて制作したイラストを例に挙げます。こちらのリリース予定はございません。


Illustratorの基本機能とそれを用いた小技

<0:その前に…下書きと完成の比較>
前述の通りIllustratorは線画を描いて塗るものではないので、下書きでは構図とキャラクターの特徴がわかるレベルで描いています。違和感のあった部分もIllustrator上で修正していきます。


<1:図形の描き方>
基本の幾何学図形ツールで図形を組み合わせで大半のイラストを描くことができます。

※図形の組み合わせに使用するパスファインダについては後述にて説明します。


複雑な図形を描く場合はいちから描くことも出来ます。また一度描いたものはアンカーポイント(線と線をつなぐ点)を動かして修正ができるので幾何学図形から編集して描くこともあります。


<2:アンカーポイントの数を必要最小限まで減らす>

幾何学図形ではなく自分で描いた場合、どうしてもアンカーポイントが余分に出来てしまい滑らかな曲線になりません。美しいフォルムを作るためにもアンカーポイントの数は出来る限り減らしハンドル操作(曲線の角度を決めるもの)で曲線を調整していきます。



<2.5:ガイドを利用したアンカーポイントを減らす小技>
アンカーポイントを減らしていくともとのフォルムから大きく崩れてしまいます。修正部分をもう1つ同じ位置にペーストし、パスをガイド化すると以下の図のようになり、元のフォルムを参考にながらパスを減らして輪郭を調整することができます。ロゴを作る際にフォントをアウトライン化してパスを減らす、オリジナルのロゴへと変形させるのにも使える小技です。


<3:クリッピングマスクとパスファインダ>
キャラクターを立体的にするために影やツヤを付けますが、その際よくこの2つの機能を使用します。

※クリッピングマスク
はみ出た部分の情報を残したまま必要な部分だけを表示することが出来るツールです。パス以外にも画像の切り抜きなどに使えます。



※パスファインダ
はみ出た部分の情報を切り抜いて完全に消す際に使用するツールです。画像には使えませんが、切り抜く他にも、2つの図形を組み合わせる、2つの重なってる部分だけ切り抜くなどシーンに合わせて色々な機能が使えるツールです。手描きではなく幾何学図形からより複雑な形を作る際によく使います。


<3.5:パスファインダでイラストをシルエット化する小技>
イラストが完成したら全選択しもうひとつペーストします。その1つをパスファインダですべて統合させるとシルエットのみの図形を作ることが出来ます。シルエットデザインの他にも周りに境界線を付ける、ずらして影を作る、など色々効果を出すのに使える小技です。


<4:他、イラスト制作の際によく使うツール>
●線、フォントのアウトライン化:
輪郭線の強調、ロゴや服の模様に文字を変形させる等に使います。


●透明ツール:
通常は不透明度50%ほどにして髪などの光彩に、乗算は影などに使えますが、Illustrator以外のデータに持ち込む場合には正しく表示されないことがあります。画像で書き出すだけのときならば便利な機能です。



Illustratorの機能は数多くありますが、以上の機能がイラストによく使用されるものです。

最後に

今回Illustratorによるイラスト制作の基本と小技について描かせていただきましたが、合わせて是非イラストを描く際のテイストや世界観について書かれている「ピグアイテムのイラストレーションについて」の記事も読んでいただきたいです。

<Illustratorの操作方法について勉強するのにオススメ書籍>
ひとつひとつの機能を細かく書かれている本よりも、どのシーンでどの機能を使っているか書かれているメイキングの本をお薦めします。

MdN(http://www.mdn.co.jp/di/
MdN (エムディエヌ) 2012年 01月号 [雑誌]/著者不明
¥1,590
Amazon.co.jp
実際にクリエイターが描いた作品から学べる雑誌です。どのような小技を使って描いたのか、部分的に解説しています。学生時代はこれを手本にしながら絵を描いて小技を使い機能を覚えていきました。

<キャラクターを描く際のモデルにオススメなソフトウェア>
Poser(http://graphic.e-frontier.co.jp/poser/
Poser 9 日本語版/イーフロンティア
¥19,800
Amazon.co.jp
リアルな人体を360°どの角度からでも人体を観察することができ、男性、女性、子供、人骨、筋肉など豊富なモデルに関節の動き、頭や手のサイズも自由に変更できます。頭や腕のバランスを変えて描きたいポーズを作り、ディフォルメされたキャラクターデザインのポーズの参考によく観察しています。

最後まで読んで頂き、ありがとうございました!

---------------------------------------------
written by マチダ
twitter:@macheri67
facebook:Machi Eri

はじめまして。Ameba事業本部スマートフォンゲームDiv所属の神谷です。
6/13に公開したソーシャルカードゲーム「天空のクリスタリア」のアートディレクションを担当させていただきました。その開発の一部、カードイラストのディレクションについてご紹介させていただきます。

ゲーム内容についてはこちらで書かせていただいています。

1.キャラクター設定

制作前のキャラクター設定は、イラストレーターさんがイメージしやすいように、背景(生い立ち)や性格、進化過程(ストーリー)を細かく記載しています。

<どういうキャラクターなのか?という具体的な設定>

カードゲームという特性上、そのキャラクターがどのように戦闘に参加するのか、ということを念頭に置き、一つのコンセプトから性格や外見を詰めていきます。

2.画面構成

<構図>

キャラクター設定は、そのまま「どこを魅せたいか」に繋がってきます。
例えば「シャハラザード」は「千夜一夜物語」の語り部で、智略によって暴君を治めるキャラクターです。千夜一夜物語からのエキゾチックな雰囲気をそのまま活かしながら、物語性を感じさせる女性的らしい衣装と動きで、女性ならではの武器=戦い方を表現しました。
また「オーディン」のような力強いキャラクターの場合、力強さを強調するため、武器を大きく表現しますが、そこに視点がいく構図になってしまうとキャラクター自身の描き込みたい部分が生きてきません。
そのため、武器の全てをカード内に収めず、武器の一部と体の動きで何を行っているかを分かるように気をつけています。


<進化>

前述に記載したように、キャラクターは進化を追うごとにそれぞれにストーリーを伴い成長していきます。
カードでは、その変化が一目で分かるようにしています。
見た目の部分では、目につきやすい髪や衣装には分かりやすい変化を持たせ、さらにそこにキャラクター独自のストーリー性が付随されます。
例えばシャハラザードの場合、キャラクターが顔にまとうヴェールが少しずつ薄くなり、最後にはそれを取る。また背景で夜が少しずつ明けていく―という進化で、キャラクターとプレイヤーが進化ごとに(一緒に時間を過ごすとともに)親しくなっていくイメージを演出しています。(キャラクターに愛着を持ってもらいたいという狙いから。)


3.ブラッシュアップ

最後にカード全体のクオリティを見直します。
具体的には単体としてのまとまり、4枚すべてを通しての進化感です。
進化後は基本的に豪華さを出すために装飾が増えますが、考えなしにただ増やしても見づらく下品なものになってしまうので、視点を集めたい場所を中心にバランスをとりながらディテールの描き込みを行っています。
色数についても同様で、全体のコントラストを大事にしつつ、かつ豪華さを失わないように色を調整していきます。
ファンタジーの世界観では、キャラクターの多くが剣や魔法といった攻撃手段を持ち、画面内でそれを扱います。それらの光、ライティングは特に顕著に進化を表現するため、ブラッシュアップの段階では、特にライティングの調整が重要になります。



これらの工程が、価値(レアリティ)が高まるごとにより重要なポイントになります。


最後に

細かい制作のご紹介ではありませんでしたが、キャラクター設定がとても重要である事が伝わると幸いです。
また、ご紹介させていただきました「天空のクリスタリア」でイラストを描いていただける方を絶賛募集中です!
ご興味がありましたらkamiya_naoyuki@cyberagent.co.jpまでご連絡いただけると幸いです。

最後まで読んでいただき、ありがとうございました。


こんばんは。アメーバ事業本部ピグディビジョン所属の久保です。
今回は6月5日にリリースいたしましたスマートフォンで遊べる「スマートフォン版ピグつりゲーム」(http://fishing.pigg.ameba.jp)の開発の一端をご紹介させていただきます。

スマートフォン版ピグつりゲームとは


スマートフォン版ピグつりゲームとは、PC版アメーバピグの中で遊べるつりゲームをスマートフォンでも気軽に楽しんでもらえるように制作したブラウザゲームです。PC版はFLASHで動作しますが、スマートフォン版ではiPhoneの対応を考慮に入れてHTML/CSS/JavaScriptにて動作します。対応環境は iOS4以上、Android 2.2/2.3 となります。

構成はトップページ/手帳ページ/ショップページ といったメニュー画面と、釣りを楽しむゲーム画面に分けることができます。メニュー画面は運用効率を踏まえ JavaScript MVC を実現する「Backbone.js」※1を使用し、ゲーム画面はレスポンスと制作効率を考え自作フレームワークを使用しています。

メニュー画面について

スマートフォンの画面は小さく表示できる情報に限りがあるため、自ずとページ数が増えていきます。その結果、データ転送量が貧弱なこともあり、読み込む回数と時間の増加に比例してUXを損ねてしまいます。メニュー画面は回遊性が高くこの問題が顕著に発生します。そこで、1つのHTMLファイルに他のページのHTMLも記述し、ユーザーのタップアクションによってHTMLをJavaScriptで切り替えて擬似的にページ遷移しているように見せる工夫を行っています。このような対応をBackbone.jsを使うと手軽に実装することができます。もちろんデメリットも存在し、1ページで複数ページ分のデータを保持するので端末のメモリ使用量や読み込みが局所的に発生しないように気を配る必要があります。

ゲーム画面について

ゲーム画面は装備を選択するシーン/さかなをつるゲームシーン/釣り上げるシーンの3つで構成されています。ゲーム画面の開発で常に意識したのは以下の2点です。
  1. 開発効率をよくする
  2. ゲームのレスポンス
開発当初は、既成のゲームライブラリを検討したのですが、Android OSでまともに動くものがなく採用を断念いたしました。そこで、FLASHらしく作成できるようHTMLやCSSの記述をせずJavaScriptだけで完結するライブラリを自前で作成することにしました。以下がそのサンプルコードです。チェーンメソッドでも記述できる点はFLASHというよりjQueryのようでもありますが、HTML/CSS を書く必要がなく処理をまとめて書くことができます。HTML/CSS/JavaScript それぞれに分けて記述するのは処理が分散し管理しにくいので、数行で済むこの方法は確認する際にとても見通しがよくなります。

// こんな感じでチェーンメソッドで記述できます。
// エレメントを生成

var element = new ww.core.Sprite({
    x:0,
    y:10,
    width:100,
    height:100,
    fill:{color:’rgba(255,0,0,1.0)’}
})
// ステージに配置
.addChildTo(stage)
// 1000msec かけて 右へ 100px 移動
.animate({x:100}, 1000);

ゲームのレスポンスについてですが、一番課題になったのがアニメーション処理。はじめはCSSのtransitionおよびkeyframeアニメーションを採用する予定でしたが、ユーザーのタップ動作に合わせ停止させるとすぐ止まったり、止まらなかったりといったムラのあるラグが発生しました。この現象はアニメーション中繰り返される描画の間隔が広いためタップした直後に停止命令を下しても、反映されるのは次の描画になることが原因でした。運がよければすぐ描画されますが、次の描画まで長いとラグが発生します。iOS/Androidともに古いOSであればあるぼどこの現象は顕著になります。これではタイミングゲームでは致命的な問題となるので採用を見送るほかありませんでした。

次に検討したのが、一定のタイミングでインラインCSSをJavaScriptで更新し続ける方法です。この手法だと、iPhoneでは制作者が意図した通りの挙動をしてくれることが多いのですが、Androidの場合、カクカクとした挙動でゲームにならない状態でした。しかしCSSアニメーションよりはこちらで挙動を管理できる可能性があったので、さらに発展させ各エレメントごとに個別に行っていた更新処理を1つのタイムラインで対応するように一元化しました。下記のような setTimeout を繰り返し実行する関数を用意し、targetArr に再描画の必要があるエレメントをアニメーションさせる間だけ格納し、アニメーションが終了したタイミングで破棄します。これにより一括して描画されるようになり無駄な描画を減らすことができるようになりました。

※実際のコードより簡略化しています。
// 更新する対象と更新処理の関数を管理する配列。// ここに格納されている間は描画を実行します。
var targetArr = [];
// タイムライン
(function(){
    setTimeout(arguments.callee, Math.floor(1000/60));
    for(var i=0, len=targetArr.length; i<len; i++){
        var t = targetArr[i]; // 更新する対象と処理が格納されている配列
        // 以下 対象の更新と描画処理
        t.target.update(); // 更新
        t.target.render(); // 描画
    }
})();

タイムラインで管理することでレスポンスはだいぶ解消されるようになりましたが、Android端末では描画処理のコストが高いため挙動が重くタイミングゲームとして成り立ちませんでした。この問題にはタイムラインに可変フレームレートを採用することで解決を計りました。たとえば固定フレームレートの場合、15fpsの場合1秒間に15回処理を実行しますが、低スペックな端末だときっちりと1/15で処理されることはなく数ミリから数百ミリ秒遅れることがあります。結果1秒間で納まりきらず1秒を超えたタイミングで処理が完了します。つまりどんなに時間がかかろうとも15回きっちり描画します。可変フレームレートであれば、再描画のタイミングでどの程度遅れて実行されるのかを判断しズレを調整しながら描画するので、もし指定した完了時間を超えるようであれば描画回数を減らすことが可能になります。これをつりゲームのさかなの移動距離で表すと下図のようになります。1秒間に15回の描画を行って、右から左に泳ぐアニメーションを作成した場合、スペックが高ければ高い環境ほど15回に近い回数で描画を行います。

JavaScriptで可変フレームレートを実現する場合は、1/15回ごとにDate.now()を実行して前回から本当に1/15秒 かかったかを確認します。もし遅れている場合は、その差分を描画に反映させるようにしました。これにより高スペック端末と低スペック端末でそれぞれ適した描画回数になり、レスポンスを上げることに成功しました。下図の右の図はそのことについて記したものです。1回のループにかかる時間が、Aの移動距離を想定した場合で、Bに相当する距離を移動できるだけの時間がかかってしまった場合、Cの分を補正して帳尻を合わせています。これを繰り返していくと処理が遅い場合、描画回数が減ることになります。
※実際のコードより簡略化しています。
//初期値
var initialVals = {x:0, y:0}; 
//変更後の値
var property = {x:100, y:100}; 
// 時間からアニメーションの何%完了したかを取得する
// 1で100%です。計算結果が1を超えた場合は100%とします。

var progress = Math.min(1, (Date.now() - アニメーションの開始時間) ) / アニメーションの再生時間);

// すべての変更値に補正値を加味して計算を行う
for(var key in property){
    initialVal = initialVals[key];
    delta = property[key]-initialVal;
    update[key] = linear(progress, initialVal, delta, 1);
}

function linear(t, b, c, d){
    return c / d * t + b;
}

以上で、ブラウザゲーム開発のお話は終わりです。猛烈な端折りかたで理解しにくい記事となってしまいましたが、つりゲームの方はわかりやすく作られていますので、ぜひ遊んでいただければと思います。

※1 Backbone.js (http://backbonejs.org/)

はじめまして!
スマホ対応アメーバピグ Webアプリ版の開発を担当している吉川浩太と申します。

今回の記事では、さいきん巷であまり話題になっていないjavaScriptのMVVMフレームワーク「knockout.js」の機能と特徴を、簡単にご紹介できればと思います。

knockout.js?

knockout.js (http://knockoutjs.com/)

knockout.jsはMVVM(Model-View-ViewModel)パターンのフレームワークです。
双方向データバインディングやアイテムテンプレート等の機能があり、SilverlightやWPF開発者にはかなりとっつきやすいフレームワークだと思います。

WebアプリではDOMを動的にゴリゴリ更新/生成することがほぼ必須になると思いますが、knockout.jsではDOMを直接操作することが(ほぼ)無くなります。
データのやり取りだけを指示しUIの更新はknockout.jsに任せるといった形になりHTMLとロジックと切り分けて開発が可能になるハズです。

名前はどんなwebアプリも簡単にノックアウト出来るよ!って意味に違いないです。


導入方法

knockout.jsは単体で動作する素敵なフレームワークです。
特にライブラリとぶつかる事はないのでjQueryと併用すると使いやすいと思います。
※執筆時点での最新版はv.2.1.0ですので、こちらを使用して進めて行きます。

<script type="text/javascript" src="./js/knockout-2.1.0.js"></script>
<script type="text/javascript" src="./js/jquery-1.7.2.min.js"></script>


最初の一歩

簡単な例を用いてknockout.jsの素敵さを説明します。

HTML:

<p><input type="text" data-bind="value: message, valueUpdate: 'afterkeydown'"></p>
<p id="result" data-bind="text: message">ここに入力されたテキストと同じものが入る</p>


javaScript:

$(function(){
    //ViewModelを定義
    function TestappViewModel(){
        var self = this;
        self.message = ko.observable(''); //初期値は空文字列を指定
    }
    //bindingスタート
    ko.applyBindings( new TestappViewModel() );
});



実行結果:

入力されたテキストが、そのまま下に表示されるだけのコンテンツです。
ここで注目して欲しいのは、ソース上でDOM操作は一切行なっていないということです。
messageは常に双方向で監視されているため、値をHTML上で変更すれば、自動的にViewModelのmessageプロパティは更新されます。
その結果、text(挿入テキスト)にmessageを指定している#resultの表示も変わるというワケなのです。
バインディング凄い!(迫真)


基本的なお作法

上の例からknockout.jsの基本的なお作法を説明したいと思います。

バインディングの紐付け
HTML要素にdata-bind属性を指定することにより、ViewとViewModelとの紐付けを行います。
data-bind属性の値にはバインディング内容と、それに紐付くプロパティをJSONのような形で指定します。

<input type="text" data-bind="value: message, valueUpdate: 'afterkeydown'">



バインディングプロパティの設定
ViewModelのプロパティにko.observableメソッドを指定することでバインディング対象とみなし、常に値を監視します。

self.message = ko.observable('');


また、例では使用しませんでしたが複数データをバインディングする場合はko.observableArrayが使用できます。

self.messageList = ko.observableArray();


バインディングの開始
最後にko.applyBindingsで対象のViewModelを渡せばView-ViewModel間でバインディングが開始されます。

ko.applyBindings( new TestappViewModel() );


値の取得・設定
バインディング要素の値を取得・設定するときは関数呼び出しになります。
上の例では下記のようになります

・値の取得

self.message();


・値の設定

self.message( 'huge' );


うっかり

self.message='hoge';


と、やりたくなりますが、そこは違いのわかる大人の対応をしましょう。



色々なバインディング

上の例でさりげにvalue、textといったバインディングを行なっていますが
knockout.jsには標準で様々なバインディングが用意されています。
公式のDocumantation

ここで各々の説明は省きますが大きく分けて3つの種類にカテゴライズされています
・見た目をバインディング(text、visible等)
・繰り返し処理、分岐処理をバインディング(foreach、if等)
・押下、入力などの操作・入力処理をバインディング(click、checked等)

また標準で用意されていないバインディングはカスタムバインディング作成することが出来ます。


実例

いくつかのバインディングを用い、ごくごくシンプルなTODOアプリを作ってみます。

HTML:

<p>
    <input type="text" value="" placeholder="TODOを入力" data-bind="value: todoInputValue"> <button data-bind="click:addTodo">追加</button>
</p>

<ul data-bind="foreach: todoList, visible:todoList().length>0">
    <li><span data-bind="text:todoText">Todo</span> - <button data-bind="click:$root.removeTodo">削除</button></li>
</ul>


javaScript:

$(function(){

    //Modelを定義
    function TodoModel(value){
        var self = this;
        self.todoText = ko.observable(value);
    }

    //ViewModelを定義
    function TestappViewModel(){
        var self = this;

        self.todoList = ko.observableArray(); //todoリスト
        self.todoInputValue = ko.observable(''); //追加todoテキスト

        //追加
        self.addTodo = function(obj, e)
        {
            if( !self.todoInputValue() )return; //空文字の場合は処理しない
       
            //入力されたテキストでTodoModelを作りTodoリストに挿入
            self.todoList.unshift( new TodoModel( self.todoInputValue() ) );
            self.todoInputValue('');
        };

        //削除
        self.removeTodo = function(obj, e)
        {
            //イベントバインディングで実行される関数の第1引数にはModel、第2引数にはイベントオブジェクトが渡される
            self.todoList.remove(obj);
        };
    }
    //bindingスタート
    ko.applyBindings( new TestappViewModel() );
});


実行結果:

見た目はだいぶ残念な感じは否めませんが、最低限それっぽくはなっていると思います。

上の例を説明します。

ViewModelにTestappViewModelを定義し
・Todoリスト
・追加する文言
・追加処理
・削除処理
を管理、
そしてModelにTodoModelを定義し
・表示する文言
を管理しています。

実際の表示の紐付けはView(HTML)で行います。

「追加」ボタンの押下で、addTodoメソッドを呼んでいます。
viewModel内の関数を呼ぶには値に関数名を指定します。


<button data-bind="click:addTodo">追加</button>


リストはforeachバインディングで、その数だけ自動で要素が作られます。
また、件数が0の場合は非表示にしています。

<ul data-bind="foreach: todoList, visible:todoList().length>0">…</ul>


追加されたTodoの削除ボタンはModelからViewModelのremoveTodoメソッドを呼びます。
その場合は$root.を頭に付けることでrootのViewModelにアクセス出来ます。

<button data-bind="click:$root.removeTodo">削除</button>


この例でも処理として行なっているのはTODOリストにデータを追加/削除しているだけでDOM操作は直接していません。
数あるバインディングの中でもforeachバインディングは非常に強力で色々な応用が効くのではないかと!
knockout.js凄い!!(迫真)



おわりに

「knockout.jsでさくさくWebアプリ開発」は以上となります。
ほんの一部の機能をかなりざっくりの説明しかできませんでしたが、この記事で1人でも多くの方がknockout.jsに興味を持ち、触ってもらえれば幸いです。
結果、日本語の情報が充実すればと!

それでは皆様、今後もエレガントな開発ライフを!!

はじめまして!!

2012年度に新卒で入社し、今現在はサイバーエージェントの子会社である株式会社シロクにて、「My365」という写真日記アプリのUIデザインを担当している石山(@t_ishiyama)と申します!


My365とは? http://my365.in/

iPhone, Androidなどのスマートフォンで撮影した写真を、1日1枚ずつカレンダーに貼って共有できる写真投稿アプリです。


撮った写真はフィルターを用いてポラロイド風に加工ができ、ブログや日記よりもかんたんに思い出を残していくことができるサービスです。ぜひダウンロードしてみて下さい!


Screenshots



はじめに

My365がリリースから運用段階に入り、私の業務内容もMy365の新機能追加や改善がメインになっています。

しかし、UIデザイン業務の性質上、成果物でしか周囲が評価できない可能性が高く、ワークフローが不明瞭になりがちです。

シロクではこの問題をクリアにするために、PhotoshopScriptを用いてUIデザイン作業のロギングを行なっています。


まず、Adobe PhotoshopでサポートされているScriptは、大きくわけて3つあります。

  • AppleScript / MacOS
  • Javascript / Windows & MacOS
  • VBScript / Windows

横断的なプラットフォームを考慮するならば、JavaScript一択となります。

詳しくはAdobe社が出している、PhotoshopScriptingGuideを御覧ください。



今回紹介するPhotoshopScriptでできること

・「⌘Command+S」をトリガーに、PNG画像の自動書き出し。


出力されたPNG画像を時系列に見返すことで、UIデザインのワークフローを自身で振り返ることができます。

具体的には、作業中のpsdファイルが保存されている同じ階層のpngフォルダ(無ければ生成)に、⌘Command+Sを押すたびにファイル名+日時情報が付与されたPNG画像が出力されます。



準備

1.Ctrls2png.jsxを用意


Ctrls2png.jsxを、Adobe ExtendedScript Toolkitやテキストエディタで記述し、デスクトップ等に保存してください。ソースは以下。


Ctrls2PNG.jsx


/*


    Ctrls2PNG.jsx


    ctrl+sでPNG画像をpngフォルダに自動吐き出し for MacPhotoshop


    version: 0.9


    Author : Takahiro Isihiyama


    Mail : ishiyama@sirok.co.jp




    導入方法:


    1:スクリプトをhogehoge.jsxとして保存


    2:Applications/Adobe Photoshop CS5.1/Presets/Scripts/ に移動


    3:photoshopの キーボードショートカットのCTRL+S に割り当て




    !ver0.9 bugReport!


    **ドキュメントを保存してないとエラーが出ますが、ドキュメント保存をしていれば正常に動きます**  


*/




#target photoshop




//ドキュメントが開かれているかどうか判別


if (app.documents.length == 0){



}else


{  main(); } 




/****************/


function main(){


  //編集中のファイル情報の取得


  var docObj = activeDocument; //ファイル情報取得


  //flag = docObj.saved;//ドキュメントが保存されているかどうか


  //if(flag == true){ 


    var docPath = docObj.path; //ファイルパス


    var docName = docObj.name; //ファイル名


    var date = dateClass(); //現在時刻の取得


    publishPng(date, docPath, docName);//PNGパブリッシュ


    //上書き保存


    docObj.save();


//}


  return 0;


}/* fin main */




//現在時刻の取得


function dateClass(){


  dTbl = new Array("日","月","火","水","木","金","土");


  dObj = new Date();




  while((new Date()).getTime() < dObj.getTime() + 1000*5){ //timeOut5秒


      //日時の振り分け


      year = dObj.getYear();


      year2 = (year < 2000) ? year + 1900 : year;


      month = dObj.getMonth() + 1;


      day = dObj.getDate();


      day2 = dTbl[day];


      h = dObj.getHours();


      m = dObj.getMinutes();


      s = dObj.getSeconds();


      //alert(year2 + "年" + month +"月" + day +"日"+"("+day2+")"+ h+"時"+m+"分"+s+"秒");


      //new Date()の元データを保持


      dateRaw = year2+"-"+month+"-"+day+"-"+h+"h"+m+"m"+s+"s";


      break;


  }


  return dateRaw;


}/* fin dateClass */




//png保存


function publishPng(date, docPath, docName){


  folderName = docPath +"\/png\/";


  folderObj = new Folder (folderName);


  flag = folderObj.exists; //フォルダの有無


  if(flag == false){


   folderObj.create(); //フォルダがない場合にフォルダを作成


  }


  //フォルダが存在している場合


  fileName = docName.slice(0,-4) + "\_" + date;


  filePath = folderName + fileName + ".png";


  //alert(filePath);


  fileObj = new File(filePath);


  pngOpt = new PNGSaveOptions();


  pngOpt.interlaced = false; //インタレースなし


  activeDocument.saveAs(fileObj, pngOpt, true, Extension.LOWERCASE);


}/* fin publishPng */   


/*end ctir2png*/



2.Ctrls2png.jsxを設置(MacOS)


1で保存したCtrls2png.jsxを、 "/Applications/Adobe Photoshop(CS5.1 or CS6)/Presets/Scripts/" の中に移動してください。


1


次に、Adobe Photoshopを立ち上げ、Ctrls2png.jsxをキーボードショートカットに割り当てます。「⌘Command+S」のキーボードにスクリプトを割り当てる理由はお察しの通り、事あるたびに上書き保存してしまうデザイナーの癖を逆手にとっております。


スクリプトが無事に読み込めていれば、こんな具合に表示されます。


2



キーボードショートカット設定に行きます。


3

ファイルタブの中、かなり下のほうに設置したスクリプトがあります。


3

Ctrls2pngに「⌘command+S」のショートカットを割り当てようとすると、既存のショートカットと衝突しますが、上書きで確定します。僕の場合、元のショートカットにあった上書き保存は、「⌘Command+Control+S」に割り当てています。


4


これで準備完了です。


実行

キーボードショートカットにスクリプトが割り当てたPhotoshopで、普段通りにAdobe Photoshopを用いて作業を行います。Photoshopの作業ファイルを開き「⌘Command+S」を押すたびに、作業中のpsdデータからpngデータが出力されて、同じ階層のpngフォルダにパブリッシュ時間付きで書きだされていきます。


7



まとめ

今回ご紹介したのは、UIデザイン業務にPhotoshopScriptを用いることでワークフロー(作業中の変更点)を時系列で明確に振りかえれるというものでした。

このように自身のワークフローを目で追っていくことで作業上の反省点や、UIデザインにおける作業時間の短縮化のヒントを得やすくなります。

また、ほぼJavascriptで動くスクリプトなので、スライス作業の自動化や明度差検知、Webとの連動等、まだまだ拡張していける要素はたくさんあります。

UIデザイン業務をより便利に、より効率的にこなすために、Photoshopスクリプティングという領域にふみこんでみてはいかがでしょうか?


...何気ないUIデザインのロギングは、振り返ると素敵な改善点でした。


以上、「ワークフローを振り返るためのPhotoshopスクリプティング」でした!


今後とも、スマートフォンアプリ、My365をよろしくお願い致します!