スマートフォン版Amebaで、フロントエンドの開発をしている2012年度新卒入社の鈴木と申します。

スマートフォン版Amebaではこれまで弊社ブログでも紹介された通りchikuwa.jsでクライアントサイドを、node.jsでサーバサイドの開発をしております。
そこで今回はJavascriptを用いた開発でメリットがあるWebStormの機能について、私が業務を通して便利と感じた点を紹介させていただきたいと思います。
 

WebStormとは

WebStormはJetBrains社が有償で販売している*IDE(統合環境開発)で、Windows/Mac/Linuxのクロスプラットフォームに対応しています。
*IDE…エディタ、デバッカ、コンパイラなどを1つにまとめたGUIベースで操作できる1つのアプリケーション。

WebStorm以外にもJetBrains社のIDEには PhpStorm(WebStormの機能も含む )や RubyMine(WebStormの機能も含む )があり、 2つ以上の言語(PHP と Ruby など)を使用したい場合は PhpStorm + RubyMine でなく IntelliJ を購入するのが良いです!(IntelliJ はすべてのWeb系パッケージが含まれています。)
 

エディタとしてのWebStorm

・オートコンプリートが秀逸
WebStormは強靭な静的解析を行っており、そのお陰で自分が作ったクラスやオブジェクトのメンバを呼び出す際に曖昧な名前しか覚えていない場合でもすぐに候補が出ます。そのため、タイポなどのミスを防ぐことができます。

以下に紹介する機能はkeymapsが、MacOSXになっている場合です。

・宣言元ジャンプ (cmd + B or カーソルをあわせてクリック)

関数やメソッドなどの宣言元へ直接ジャンプすることができます。ファイルやディレクトリが異なっていても同じプロジェクトにあればジャンプできるので、フレームワークやライブラリなどのメソッドの中でどんな処理をしているか確認する際にとても重宝します。
(CDNの場合もsourceをDownloadしてExternal Librariesに配置しておけばジャンプできます。)

・Find Action (cmd + shift + A )

WebStormのメニューには数多くの機能が含まれています。使い始めてしばらくはたくさん存在するメニューの中から自分が行いたい機能探したり、ショートカットを逐一覚えるのに学習コストがかかってしまいます。
そのようなとき、Find Actionを使って自分が使用したい機能のキーワードを入力することで自分の行いたい処理が見つかります。

・HTML/JSの周りの言語に多数対応している

最近では、JadeやHamlなどHTMLのテンプレートエンジン、またsassやlessなどのCSSプリプロセッサを用いることが多くなってきました。WebStormはそれら多数の言語に標準対応しているため追加でシンタックスハイライトさせるプラグインなどを入れる必要はありません。

バージョン管理としてのWebStorm

・Git, Subversion, CVS等に対応

基本的なバージョン管理には対応しており、WebStorm上からGithubに直接プッシュして公開することができます。

・diff機能が秀逸

自分のローカルとリモートブランチとのdiffを簡単に見ることができます。またコンフリクトした際に解決する場合は'Merge Resolve'を使って上記のようにそれぞれを比較しながらマージすることができ、マージでのミスを極力抑えることができます。

・Local History
Local Historyの機能ではユーザーが意識していなくても保存したすべての履歴をローカル内に格納しています。そのため、subverstionでうっかりupdateしたり、gitでpullしてしまった場合にすぐにその差分を確認してRevertすることができます。また、過去にどのような作業をしたか時系列でさかのぼり確認することもできます。

デバッグツールとしてのWebStorm

・Nodeリモートデバッグ
WebStormではnode.jsを実行することができます。
今回は実際にexpressを使用したnodeアプリケーションのリモートデバッグを行ってみたいと思います。

コマンドからアプリケーションを生成したほうが速いですが今回は折角なので、WebStormの機能を使ってアプリケーションを生成したいと思います。
まずはプロジェクトの作成を行います。

先ほど紹介したFind Action(cmd + shift + A)にNew Projectと入力しProjectTypeのNode.jsExpressAppを選択し、ExpressAppのプロジェクトを作成します。

ExpressGeneratorの処理が終わったらNode.jsのlocationを選択します。

まだNode.jsをインストールしていない場合は'Download from the Internet'から安定版をインストールしてください。

Node.jsのインストールが終わるとWebStorm上で自動的にプロジェクトが開きます。先ほどのhogeAppにapp.jsが作成されていますね。


では、リモートDebugを行ってみたいと思います。

プロジェクトを開いたデフォルトの状態だと、上部のNode.jsのマーク部分にapp.jsが記載されるのでdebugボタンを押してサーバを起動させてください。


そうするとDebugのタブのConsoleのタブの中に以下のログが表示されているはずなのでブラウザで実際にlocalhost:3000で開いてみましょう。

/usr/local/bin/node --debug-brk=53593 app.js debugger listening on port 53593 Express server listening on port 3000



welcome Expressが表示されconsoleに
GET / 200 10572ms - 170b
が返ってきました。

app.jsでrequireしているuser.jsを開いて
res.send("respond with a resource");
の行数が書かれている部分にブレイクポイントを打つと赤い丸の目印がつきます。


その状態でもう一度サーバを起動し、localhost:8080/usersにブラウザでアクセスすると WebStomにアプリケーションが切り替わり画面下のFramesにこれまでの経路とVariablesが表示されました!


おわりに

今回のWebStormの記事はいかがでしたでしょうか?
私自身上記の機能を使うことによりコードの品質が向上しただけでなく、単純なミスや見落としが減り作業効率が上がったと感じています。

今回紹介した機能以外にも

・HTMLやCSSをブラウザの更新なしに反映させるLiveEdit
・File Watcher(ファイル保存をトリガーにして処理を走らせる)
・TypeScriptをコンパイル=>デバッグ
・JShint,JSLint=>構文チェックを行いマークを付けて指摘される
・JsTestDriver
・ステートメント補完
などコーディングをするのが楽しくなる機能が含まれています。

WebStormを使ったことがない方は是非この際にトライアル版(30日無償で有償版と機能は同じ)をDLして使ってみてはいかがでしょうか?
はじめまして。「不良魂~全国制覇~」というソーシャルゲームでデザイナーをしております、岡田(@akoookday)と申します。今回はデザイナーがアニメーションを作成する、メリット・デメリットについてご紹介したいと思います。

「不良魂~全国制覇~」( ※以下、「ギャングソウル」と表記 )では、アニメーションの開発にFlash CS6とToolkit For CreateJS(※以下、「TFC」と表記)使用しています。TFCはFlashで作成したアニメーションをJSに書き出してくれる便利な拡張機能です。
(TFC制作の流れについて佐藤圭明さんの記事に詳しく記載されていますので、そちらもご覧ください。)

この機能によりJSの知識が浅くとも、実務でのアニメーションの大部分をデザイナーが作成できるようになりました。ギャングソウルが他の内製のタイトルと大きく異なる点もこの部分です。

デザイナーがアニメーションを作成するメリット

1. 素材に合わせた動きの追求

ギャングソウルは不良をテーマにしたゲームで、登場するアイテムや背景も実際に街にあるようなリアルな物が多いのが特徴です。
その世界観を表現するアニメーションにもリアリティが求められます。
まずは動きのリアリティについて、シンプルな物が落ちるアニメーションで説明します。
動かす物体の重さや質感などをイメージしながら進めました。

物の重さ・質感を表すには、反動が重要です。周りに与える影響、受ける影響を意識すると上手く表現できると思います。

実際に素材を割り当てると↓のような表現になります。

必要な場合は物に合わせて影も追加するとよりリアルになります。ギャングソウルでは右3つのような表現をよく使用します。

2. アニメーションのリッチな表現

次は素材を組み合わせる事でよりリアルでリッチなアニメーションになる例をご紹介します。ギャングソウルで多用する【銃を撃った後】のようなアニメーションを素材ごとに分解してみました。

1.背景がある場合は打ち込まれた瞬間にガタガタと動かすと、銃の反動を表現できます。
2.フラッシュの表現はよく映画などで見られるものです。穴を目立たせるために銃痕よりも後ろに配置しました。



4.立ち上る煙はただ拡大するだけでなく、左右の比率を変えることで伸びていくような表現になります。


真ん中の穴に破片が通るようにして動きを目立たせます。
また銃痕が白いので、それに破片がかぶり過ぎないように調節します。



このようにほぼ1色しか使用していませんが、ちょっとした細部の素材が加わるだけでリアリティが増します。
それぞれのパーツの動きはYoutube等で動画を検索して、実際の動きを参考にしています。

アニメーションで使用した素材は以下です↓

使用する素材はスプライトシートにまとめてFlashに読み込みます。
スプライトシートとは複数の画像をまとめて1枚にまとめた物です。読み込む画像の枚数が減る事でアニメーションのデータ容量を抑える事ができます。
素材それぞれにマスクを追加しパーツを作成します。

3. デザイン、アニメーション間の融通

デザイナーがアニメーションを作成する最大のメリットは、デザイン・素材作成と、アニメーション作成を同時進行できることだと思います。
作業を進めていくと要素を足したい場合や、修正したいと思う場合が多く発生します。
その際デザイナーの場合、自分で作成した素材なので修正や追加を容易に行う事ができます。

実際にスプライトシートを修正する時に気を付けなければいけない点があります。以下は銃痕のアニメーションのスプライトを修正する例です。


Flashでは画像の基点は左上になります。パーツのマスクがずれないように、右下に向かってキャンバスを広げ、素材を追加していきます。アニメーションを作成する時はFlashとPhotoshopを同時に開き、画像の修正を重ねながら動きをつけていきます。


デザイナーがアニメーションを作成するデメリット

1.複雑なデータはディベロッパーとの連携が困難

アニメーションの中でシステムとの連携が必要な部分はディベロッパーに対応して貰います。
ディベロッパーはフラッシュのデータと、TFCが書き出したJSのみでアニメーションの構造を把握しなければいけません。書き出されたJSは癖があり、読み解くのはとても時間がかかり困難です。

これを解決するために、アニメーションの構造をエクセルでまとめた仕様書を作成する事をおススメします。

この仕様書に、ラベル名、オブジェクト名、画像名、テキストの指定、タップでスキップの設定、差し替え画像、分岐など、実装に必要な情報をなるべく分かりやすいようにまとめます。

↓下記はガチャアニメのあとに加わるスロットの演出の仕様書です。

仕様書があっても分岐が多く複雑なアニメーションの場合は連携が非常に難しくなります。
構造を説明しつつ、アニメーションのデータも修正を重ね、その度に仕様書を更新して対応していきます。
よりスムーズに連携していくには、デザイナーであっても書き出されたJSを読み解く努力が必要になってきます。

2.作成段階で実機でデータを確かめることができない

デザイナーであれば、バナーやイラストなど作成したデータは実機のスマートフォンで確かめたいと思うのが普通です。しかしアニメーションのデータをサーバーにコミット・リリースするのはディベロッパーなので、作成段階では気軽にアニメーションを実機で確認する事ができません。
テストのサーバーに自分でコミット・リリースできる環境があれば確かめる事が可能です。ただ分岐のある複雑なアニメーションを、きちんとを確かめたい場合は自分でJSを書いて確かめるしかありません。(ラベル名を指定するだけのシンプルなコードなのでそこまで難しくないと思います。)

3.素材の追加の容易さから、ついデータが重くなる

アニメーションにこだわりすぎると、必要な素材がどんどん増えていき、容量が重くなってしまいがちです。

これを解決するには、同じアニメーションの演出をどれだけ小さく、単純な画像でできるかを常に意識することが大切です。
スプライトシート(PNG)は減色ソフトで減色し容量を減らす事ができます。しかし限度があります。大きな画像で色数が多く、グラデーションを多用していると、劣化が激しくかつ容量が大きくなってしまいます。

以下のことに気をつけてると容量を減らせると思うので、試してみて下さい。

・デザインの段階で色数を抑える(同じような色味であれば統一する)
・グラデーションはなるべく使わない(特ににアルファのグラデーション)
・左右対称の画像であれば、半分だけ作成し、Flash内で反転して使用する
・劣化しても見栄えが悪くないような素材を意識する(元からかすれた表現など)
・シンプルなグラデーションはFlash内で作成し画像は使わない

・ユーザーにとって本当に必要かどうかを考る。

おわりに

最後までお付き合い頂きありがとうございます。
職種に限らずアニメーションを作成する人が増えて欲しいという希望で書かせて頂きました。これからも課題は数多くありますが、表現にこだわっていきたいです。

最後にいくつか実際にゲームで使用したアニメーションを紹介させて頂きたいと思います。少しでも参考になれば幸いです!


皆様、こんにちは。
Ameba コミュニティ部門 チーフクリエイティブディレクターのサトウ(@sugaar)です。
今回は、弊社コミュニティ部門における、サービスクオリティの向上とデザイナーのスキルアップを目的として開催している、「デザイナーロワイヤル」についてご紹介すると共に、普段忘れがちなスマホデザインの5つのルールをまとめさせて頂きます。

デザイナーロワイヤルとは

デザイナーロワイヤルとは、8~10名のデザイナーが、お題となったAmebaサービスを使い込み、自分なりのブラッシュアップ案を持ち寄って点数を競う、バトル形式の会議です。

1 pixel|サイバーエージェント公式クリエイターズブログ
1時間の会議で提案されるデザイン案は約50案。
当然ですが、デザイナーだからこそ気付ける、デザイナー視点の改善案のレベルは高く、「なぜ今までそうしなかったのか?」という、サービス側では気付けないデザイナー視点の改善案ばかりが集まります。

1 pixel|サイバーエージェント公式クリエイターズブログ
発表者以外にも、誰でも傍聴参加が出来るこの会議。
他のデザイナーの提案を間近で聞けるという事で、
回を追う毎に傍聴人が増え、会議終盤では熱気に包まれるほどの人気会議となりました。

1 pixel|サイバーエージェント公式クリエイターズブログ
そんなデザイナーロワイヤルの詳しい内容については、弊社「広報ブログ」で取材頂いておりますので、今回は、出された案の全てに採点をし、アドバイスをする上で、私自身が常に意識している、スマホデザインにおける忘れがちな5つのルールについて紹介させて頂きます。

1. 圧倒的なユーザー目線

画面の構成を検討する際、「ここにあったら気付くだろう」や、「ここまでやらなくても伝わるだろう」という、根拠の無いサービス都合の判断では、実際の所ユーザーは思ったように使ってくれません。

一般的に考えて「なにが普通か」ではなく、ユーザー的に考えて「なにが自然か」を意識する事がとても重要です。
「ユーザー目線」というフレーズが一人歩きしがちですが、実際の所、現場のデザイナー自身がユーザーを100%意識する事は非常に難しく、また、意識しているようで実は自分の都合の良いように置き換えていたりします。

そうした場合に有効なのが、他のデザイナーを巻き込んでの「スクラップ&ビルド」です。
一度デザインしたものを自分1人で壊す(ブラッシュアップする)場合、壊す角度をどうしても意識してしまいますが、他のデザイナーを巻き込む事で、まったく意図していない角度からの指摘で本質が浮かび上がるので、ブラッシュアップの方法としてはとても有効です。

そういった「第3者が指摘する機会」を意図的に作っている取り組みが、今回ご紹介させて頂いた「デザイナーロワイヤル」です。

2. ページ内の情報の優先度

そのページを訪れたユーザーに「何を一番に訴求したいのか」というシンプルな要求に対し、スマホ特有のレイアウト的な理由(スペースが限られているなど)からその優先度を決めてしまうケースがあります。

「目立たせたいからこの位置に」などのレイアウトのバランスも重要ですが、限られたスペースの中でページ内の優先度を表現する際、大きさや場所だけでなく、「色や質感」に頼ることも有効です。
1 pixel|サイバーエージェント公式クリエイターズブログ
上記はスマホブログ「Simplog」のマイページです。右側の優先度に従って、各細部をデザインしています。

【優先度レベル1】
ホーム画面であるマイページでは、「マイブログ」感を演出するため、ユーザー情報をファーストビューで完結させるようデザイン。カバー画像を大きく見せ、自己表現のエリアに馴染むようにユーザー情報を表現しています。

【優先度レベル2】
機能的なボタンと投稿本文が閲覧のメインなので、周りの要素は可能な限り黒子に徹する表現としています。

【優先度レベル3】
お知らせと記事編集、詳細への導線はメインではないため、背景に馴染む質感にして優劣をつけています。

【優先度レベル4】
投稿日時はサブ的な情報なので、この画面では優先度を一番下げ、文字色に背景色と近い色を用いて、存在感を薄くしています。

このように、限られたスペースでも、「色や質感」のちょっとした工夫で情報の優劣をつける事が出来ます。特に、画面サイズに制限のあるスマホサービスにおいては、情報の優先度をいかに簡潔に表現するかが重要です。

3. 複雑さと快適性

D.A.ノーマンの著書にもありますが、技術の進展から機器に「複雑性」が伴うのは当然であり、問題なのは「複雑性」ではなく「わかりにくさと、その結果生まれる矛盾」にあるのでは、という考えがあります。つまり、デザインは「やたらと込み入った(complicated)」り「人を戸惑わせた(confused)」りしてはならないが、「複雑(complex)」になるのは、ある意味当然であるという考えです。

この事はサービス運用にも言える事で、目先の機能追加によって、いつの間にか使いにくくなっているサービスケースが世に多くあります。「より便利」なユーザー体験を創出する際には、「快適性」とのバランスがとても重要であり、シンプルな機能でも、露出のさせ方1つでユーザーの困惑を招いてしまいます。そのため、「何が最適か」を常に自問自答しながらデザインし、複雑な機能をいかに快適に使えるようにするかが、デザイナーの腕の見せ所だったりします。

4. 利用シーンを考える

制作の現場は主に室内だと思いますが、ユーザーの利用シーンを想定すると屋外で利用するケースがあります。屋外での利用時に「光の加減で画面が見えない」など、自然要因による使いにくさを体験している方も多いはず。ディスプレイの設定など、機種に依存する場合もありますが、デザイン的な配慮として、背景とのコントラストを注意する事が重要です。ブログや掲示板など、読み物系のサービスであれば、テキストが置かれる背景に中間色を使ってしまうとコントラストが弱く、屋外に限らず閲覧しにくくなってしまいます。配色に関してはサービスコンセプトとの兼ね合いになるので一概には言えませんが、文字と背景とのコントラストを強める事で、快適に閲覧する事が可能です。

5. 隙間について

デザイナーとして、忘れてほしくないのは、「空いているスペースを埋めて、縦幅を縮める」ことよりも、「スペースが空いていても、情報的に自然に見える」ことの方が、ユーザーにとっての心地よさにつながる場合もあるということです。

特に、スマホのように、画面サイズがある程度決まっているものに対して、「いかに詰め込むか」の意識になりがちですが、「本当に詰め込むべきか?」という問いを、常に心がける必要があります。

おわりに

最後まで読んでいただきありがとうございます。
きたる10月5日(土)に「WebSig1日学校」というセミナーで、僭越ながら「スマホサービスにおけるあたりまえのデザイン」をテーマに講演をさせて頂きます。弊社コミュニティサービスを例に出して具体的なデザイン例を紹介する予定ですので、皆様ぜひお越し頂ければと思います。

【WebSig1日学校】
http://1ds.websig247.jp/2013/
はじめましての方も、おひさしぶりの方も、こんにちは経営本部の小林です。

今日は広報ブログなどで取り上げられているエンジニアリングの頂上決戦
Rookie Engineer HACK Competition 2013」の裏側をちらっとお見せしたいと思います。
広報ブログはこちら


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

Rookie Engineer HACK Competition 2013」社内では「ルーキーハックコンペ」の愛称で呼ばれています。エンジニア(※サーバーサイドエンジニアと、ディベロッパーの総称)に向けに3部門構成となっており、「チューニンガソン」「タイムアタック」「クリエイティブRUN」と各々の特技、特性を生かせるようになっています。

すでに、「チューニンガソン」と「タイムアタック」は本選が終了しており、残すは「クリエイティブRUN」のみとなっています。

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

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

※写真は第2回目のタイムアタックより

1pixelといえば、デザイナー、ディベロッパーのみなさんが読むブログですので「クリエイティブRUN」について少しだけご紹介したいと思います。


「クリエイティブRUN」は2つの言語を使用するメンバー向けに開催されるもので、「JavaScript」と「ActionScript」を普段の業務で主に使用しているメンバーが参加します。

先日予選が終了し、本選出場者が発表されたばかりですが、「業務で日々使っているよ!」というメンバーから、「業務では使っていないけど腕に覚えがあるよ!」というメンバーまで、社内の人材の厚さを感じさせる結果となりました。

予選問題は・・・
JSはタグの理解などの初歩問題からローカルストレージの使い方までと幅広く。
ASについても、アニメーション制作に必要な基礎知識から、クラス設計の力を問う問題までと幅広く出題しています。

普段の業務で使用していれば5割は解けるであろう難易度でしたので、予選通過のは応用編の得点をどれだけとれたか…というところで差が付きました。
また周囲から大きく差をつけての予選通過者もおり、当日に期待が持てる結果となりました。

しかし当日は、決められた時間内にレギュレーションに沿ったアプリやサービスを作る事になるので、予選の実力がそのまま本選にも通用するかというと一概にそうとは言えないのがフロントの面白いところです。
使う人を魅せるところまでがフロントの見せ場なのでどんな物がでてくるか非常に楽しみです。


来週行われる「クリエイティブRUN」の本選の様子は1pixelでもお伝えしようと思いますので楽しみにしていてください!!



それでは!またお会いする日まで!

初めまして。広報・IR室でディレクター兼デザイナーを担当している山下と申します。今回は当社の企業サイトをリニューアルするにあたって特に注力した、 「運用効率を向上させるサイト構築のポイント」というテーマで、 構築、運用をした結果、重要度が高かった点をいくつかピックアップして紹介させて頂きます。



※今回のリニューアルでは、PCサイトの日本語・英語サイトとスマートフォンサイトを刷新しています。


1.運用体制を明確にする

プロジェクト初期は決まっていないことが多いためか運用体制の話は後回しになりがちです。しかし、サイト設計の初期段階に明確にしておくことは効率化を図る上で意外と重要です。

運用体制(人員数、スキル等)が正確に見積もれていれば、使えるリソースから逆算して効率性、効果性が高い合理的な設計をすることができるからです。

逆にここが不十分なまま進んでしまうと構築フェーズから運用フェーズにバトンタッチした途端にミスマッチが発生して、想定以上に運用負荷がかかる仕様になってしまったなんてことになりかねません。

初期段階にわかる範囲でかまわないので更新範囲と頻度、担当をリストアップして体制図を作成することをお勧めします。
コンテンツなどを企画する際に関係者にそれを共有しておけば、運用時のイメージを持ちながら判断することができます。また他部署と連携する場合は、合わせて連携フローとルールを用意しておくと運用時にスムーズに進める事ができます。


2.マルチデバイス対応は要件に合った手法を選ぶ

スマートフォン、タブレット端末の急速な普及に伴い、マルチデバイス対応は必須になりつつあります。運用効率の観点からみると、対応するデバイスが多い程、コストや運用負荷がかかるので、どこまで、どのように対応するかは慎重に検討する必要があります。

今回はスマートフォンサイトに関しては2度目ということもあり、要件は明確でしたが、マルチデバイスの対応手法の選択には検討に検討を重ねました。
まずマルチデバイス対応手法の種類を理解し、次にWebサイトの要件と照らし合わせて、プロジェクトに最も適した手法を選択することをお勧めします。

マルチデバイス対応手法を理解する

以下の3つが主なマルチデバイス対応手法になります。簡単にですがメリット・デメリットを確認していきましょう。

(1)自動変換サービス

変換エンジンを用いて「PC→スマートフォン」などPCサイトのソースを各デバイス向けに自動変換する方法。

既存のPCサイトを変換するので他の対応方法に比べて導入コストかかりません。ただし、自動変換となる分、デザイン面など細かな制御ができません。

(2)レスポンシブWebデザイン

全端末共通のHTMLファイル(ワンソース)を一つ用意し、画面サイズに応じてレイアウトを最適化する方法。
HTML開発のみで実装できるためサーバーなどを用意する必要がありません。またワンソースのためコンテンツの一元管理ができ、運用が効率的に行えます。逆にワンソースであるがゆえに各デバイスで情報構造を変えることができません。

(3)マルチデバイス対応CMS

コンテンツをCMSで一元管理し、テンプレートをデバイスごとに用意することで最適化したサイトを構築する方法。
コンテンツが一元管理でき、各デバイスに最適化することもできます。理想的な出力が可能ですが、CMS用のサーバー用意など初期導入のコストがかかります。

Webサイトの必須要件の洗い出し

次に構築するWebサイトの必須要件を洗い出して、どの手法が要件と合致するか確認しましょう。今回のリニューアルの要件では、PCサイトのほぼ全てのページをスマートフォン対応にすることと、グローバルサイトもリニューアルすることが決まっていました。1000ページ近い規模で、且つ運用人員が少数であることを勘案すると以下の必須要件となりました。

(1)コンテンツの一元管理

PCサイト、スマートフォンサイト、グローバルサイトなど複数のサイト運営を行う場合、コンテンツの一元管理ができないと、一度の更新に倍以上の作業が発生するため。

(2)デバイス毎のコンテンツの出し分け

PCサイトのほぼ全ページをスマートフォン対応にすることを考えると、ページによってはコンテンツ量や内容を調整できないとデバイス毎の最適化を図るのは難しいため。


上記の必須要件と各マルチデバイスの特性を照らし合わせ今回のリニューアルでは、(3)マルチデバイスCMS対応を選択しています。 サイト規模や予算、運用方針で最適な手法は違ってきます。プロジェクトに最適な手法を選択できるかどうかは効率化の根幹に関わり、後から変更するのは難しいのでよく検討しましょう。


3.可能な限りパーツ、画像の共通範囲を広げる

当社のコーポレートサイトは、スマートフォンサービスの情報を掲載しているため更新上げ下げが多く、関連ページ、各デバイスごとに作業していくとそれだけで1日の作業工数が埋まってしまいかねません。
デバイス毎の作業はマルチデバイス対応手法で軽減できるとして、さらに効率性を高めるなら、複数ページで共通のパーツや画像を流用できるようにするのが有効です。例えば、一つの画像を比率は変えずサイズだけを変えて使用できるように設計すれば、最も大きい画像を作れば済んでしまいます。

実際にリニューアルしたサイトから画像の例を挙げてみます。
長方形と正方形の2つのパターンが複数ページで共通して使われています。

上記は、PCサイト内だけを挙げていますが、スマートフォンサイト、グローバルサイトもほぼ同じ画像を流用できています。またCMSを使っている場合は、画像だけでなく、ページやパーツ単位も共通化することができより効率的なサイト運営をすることが可能です。


おわりに

以上、いかがだったでしょうか。リニューアルを通じで運用効率の向上に特に効果的であったポイントをまとめて紹介してみました。皆様のお役にたてば幸いです。
当社のコーポレートサイトは、機動的に日々アップデートしていく方針ですので、よろしければ時折チェックしてみてください。

Special Thanks
当社コーポレートサイトのマルチデバイス対応は、株式会社ディバータが提供している「RCMS」を利用しています。本稿のテーマである運用効率の向上にも強力なバックアップをして頂きました。この場を借りてお礼申し上げます。

RCMSについてはこちら



はじめまして。
Q&Aコミュニティ テルミー」(以下、テルミー)のフロントエンドを担当しています富澤と申します。
今回は、テルミーの開発で使用しています、「Haml」について紹介と使用しての雑感をお話させて頂ければと思います。


まず、Hamlを採用した経緯について少しお話しさせて頂きます。

テルミーは、昨年の終わり頃から今年の2月中旬にかけてRuby on Railsで作り直していたのですが、その際、Hamlならサーバーサイドエンジニアにもviewのテンプレートが扱いやすいということで採用しました。


では、Hamlについて簡単ではありますが、紹介させて頂きます。

そもそもHamlって?

Hamlとは、「HTML abstraction markup language」の略で、“はむる”と読みます。
HTMLを生成するためのマークアップ言語で、インデントと簡単な構文でシンプルに記述することができます。
本家サイトには、Haml開発の基本原則として以下の4つを掲げています。

1. Markup Should be Beautiful
2. Markup Should be DRY
3. Markup Should be Well-Indented
4. HTML Structure Should be Clear

英語が得意ではないので和訳は避けますが、なんとなく伝わりますでしょうか?


インストール方法は、以下のようにgemコマンドでインストールします。

$ gem install haml

基本的な記述

以下のように、タグ名の先頭に%をつけて、記述していきます。

Hamlの記述

%p= "ゆる~いQ&Aコミュニティ テルミー"

出力結果

<p>ゆる~いQ&Aコミュニティ テルミー</p>

入れ子の場合は以下のように記述します。インデントが必ず必要で半角2spaceとします。

Hamlの記述

%div
  %p
    %span= "ゆる~いQ&Aコミュニティ テルミー"

出力結果

<div>
  <p>
    <span>ゆる~いQ&Aコミュニティ テルミー</span>
  </p>
</div>

空白・改行の制御

"<"をタグの後ろにつけることでタグの内側、">"をタグの後ろにつけることでタグの外側の空白・改行を取り除きます。インライン要素が続く場合や<li>を"display: inline-block;"にした時などに有効です。また、"><"のように両方使用することも可能です。

Hamlの記述

%p<
  %i><= "ゆる~いQ&Aコミュニティ "
  %span><= "テルミー"

出力結果

<p><i>ゆる~いQ&Aコミュニティ </i><span>テルミー</span></p>

属性の追加

href, alt, titleなどの属性を追加する場合は、タグ名の後に{}もしくは()を書き追加することができます。

Hamlの記述

%a{:href => "http://tell-me.jp", :title => "テルミー"}
  %img{:src => "hoge.png", :alt => "ゆる~いQ&Aコミュニティ テルミー"}

出力結果

<a href="http://tell-me.jp" title ="テルミー">
  <img alt="ゆる~いQ&Aコミュニティ テルミー">
</a>

idとclass

idとclassは、cssセレクタと同じ記述方法で追加することができます。

Hamlの記述

%a#hoge.fuga{:href => "http://tell-me.jp", :title => "テルミー"}
  %img{:src => "hoge.png", :alt => "ゆる~いQ&Aコミュニティ テルミー"}

出力結果

<a href="http://tell-me.jp" title ="テルミー" id="hoge" class="fuga">
  <img alt="ゆる~いQ&Aコミュニティ テルミー">
</a>

その他

Rubyの構文が使用できるため、変数・条件分岐などが使用できます。ですので、以下のように配列をeachでループさせることも可能です。

Hamlの記述

%ul
  - numbers = [1, 3, 5, 7, 9]
  - numbers.each do |number| 
    %li= number

出力結果

<ul>
  <li>1</li>
  <li>3</li>
  <li>5</li>
  <li>7</li>
  <li>9</li>
</ul>

まとめ

基本的な使用方法を紹介しましたが、いかがだったでしょうか?
実際使用しての良かったと感じる部分は、

・少ないコードでシンプルに記述できる
・インデントに厳しいのでhaml・出力されるhtmlに作業者の癖が出にくい
・タグの閉じ忘れが無い
・学習コストも高くない

などが挙げられます。
学習コストに関しては、「Emmet(zen-cording)」を使用したことがある方であれば自然に書けるかと思います。

もし、ちょっと書いてみたいという方は、こちらで試してみてはいかがでしょうか?
Haml to HTML to Coffeecup to Javascript to CoffeeScript Converter!

以上、ありがとうございました。

はじめまして。にみやと申します。
エースのワンダーランド でデザイナーをしております。

今回は、エースのワンダーランドというアプリについてと、
その中で使用されているクエスト背景イラストの一部をご紹介いたします。

エースのワンダーランドとは?

エースのワンダーランドは、不思議の国のアリスの世界観をモチーフとした、
新感覚トランプアクションゲームです。(AppStore よりダウンロードが可能です。)

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

舞い上がるトランプをスワイプで切る爽快なバトルが、このゲームのメインなのですが、

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

その世界観にもこだわっています。
バトルに出現するモンスターは、どこか不気味で狂気的かつ美しく、
思わず惹きこまれるようなキャラクターにしています。
1 pixel|サイバーエージェント公式クリエイターズブログ

入手したモンスターは育てて、自分のデッキに入れてバトルを有利に進めることができます。
現在も、定期的に新しいクエストを開放しており、
強くて魅力的なモンスターもどんどん追加されています!

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

それでは次項から、そんなクエストに使われている背景の一部を、
作り方の概要とともにご紹介いたします。

クエスト背景イラストの作り方

まず、各クエストのテーマを決めます。
今回は、飛行系のモンスターが登場する【雷まといし城】のクエストを例にします。
こちらは、“遠くに妖しげな城の浮かぶ、不気味な上空(キーカラー:紫)”をテーマとしております。
1 pixel|サイバーエージェント公式クリエイターズブログ


テーマを決める時に心がけていることは、
新しいクエストが開放された時、ユーザーが飽きずに“このクエストを進めたい!”と思わせる工夫です。

そのために、
■「同じような色調の背景を続かせない」
■「建造物内のなど閉鎖されたクエストの次は、なるべく野外など開けた場所にする」
■「殺伐とした暗い雰囲気の場所の次には、明るく綺麗な場所」

など、必ず変化をもたせるように心がけています。

そのほうが、クエストの選択画面も色鮮やかになり、視覚的にも新しいクエストの開放へのモチベーションが高まります。

さて、【雷まといし城】のイラスト製作工程に戻ります。
テーマが決まり、イメージに近い参考画像を集めたら、
全体のラフ作成に入ります。制作環境は主にphotoshopCS5を使用しております。
大体の明暗からつけていきます。
1 pixel|サイバーエージェント公式クリエイターズブログ

大体のイメージラフが出来たら、バトルのUIと照らし合わせながら、位置を調整します。
ポイントとなるものは、画面の中央辺り、目につきやすいところに配置します。
上下には固定のナビゲーションUIが置かれてしまうので、作り込み過ぎないようにします。
※画面は開発段階中のものです。
1 pixel|サイバーエージェント公式クリエイターズブログ

位置の調整が出来たら、要素のディテールを作りこみます。
適宜テクスチャなどの素材を使ったり、
雲など有機的で不思議な空間を演出しやすい要素は手描きにしたり、工夫します。
(こちらでは雲で不気味な雰囲気が出る様に、工夫しました。)
1 pixel|サイバーエージェント公式クリエイターズブログ

大体完成したら、出現するモンスターを配置して、最終調整を行います。
背景イラストですので、あくまでモンスターが引き立つように、
立体感が出すぎていたり色が濃すぎたりして、過度に存在感があるものはこの段階で調整します。
逆に何があるのかよくわからない要素は、取ってしまったり、
はっきりさせてポイントにしたり、シルエットだけ残すようにしたりします。
※画面は開発段階中のものです。
1 pixel|サイバーエージェント公式クリエイターズブログ

最後に、雰囲気を演出できるポイントとなる部分に光の効果を足したり、
色調を整えたい部分を直して、完成です。

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


(こちらは、12番目に開放されるクエストです。是非ゲーム内でご覧になって下さい!)

ちなみに新規で追加されるテーマは、
BOSSモンスターのイラストのイメージから決めることもあれば、
フレーバーテキストと呼ばれる、雰囲気や世界観を表すためのシナリオから導き出すこともあります。
初回リリース分に含まれている25エリア分は、チームのメンバーで前述した工夫を意識して、
自分たちでテーマを考えました。

内製しているクエスト背景

現在、ご紹介した背景イラストの他にも、通常クエストでは20個以上が用意されています。
※以下一部の画像

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

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

また曜日ごとのクエストや毎週追加されるスペシャルクエストなど、毎月たくさんの背景をチーム内で制作しています。

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

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

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

新しいクエストが開放された時、世界観が直感的に伝わり、
思わずワクワクしてプレイしてみたくなるような、魅力的な背景になるように、
メンバー一同、気を配りながら制作しています。

また、それぞれのクエストのイメージに合ったタイトルなどのテキストも、壮大なシナリオになるよう、こだわっております。こちらも是非チェックして見て下さいね。

最後に

エースのワンダーランドでは、毎週魅力的なトランプをGET出来るイベントを開催したり、より使いやすく楽しんでプレイして頂けるように、機能改善などにも尽力しております。

是非、色々なご意見・ご感想お待ちしております。
今後とも、エースのワンダーランドを宜しくお願いいたします。

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


こんにちは。ピグファンタジアでデザイナーをしておりますMachi Eriと申します。
記事を書くのは2回目でございます。

今回は私が現在制作しているピグファンタジアにおけるデザインや世界観設定の工夫について書かせていただきます。

はじめに:ピグファンタジアとは?

1 pixel|サイバーエージェント公式クリエイターズブログ-ピグファンタジアとは?自分そっくりに作れるアバターコミュニティサービスの「アメーバピグ」を「ファンタジー」という新たなジャンルで展開したスマートフォン向けソーシャルゲームです。

ユーザーはドールとなっておもちゃの世界にある不思議を探索し、旅の途中で出逢うたくさんのキャラたちを仲間にしていきます。アメーバピグならではの着せかえやお部屋の模様替えなどの要素はもちろん、個性豊かなキャラたちが400体以上登場するのが最大の魅力です。

1 pixel|サイバーエージェント公式クリエイターズブログ-2ピグファンタジア
そんなピグファンタジアですが、長年愛され続けている「アメーバピグ」のよさを崩さず、新しい世界を持ったサービスを作るために我々はいくつかのこだわりをもって工夫してきました。

ピグをもとに新たなデザインを作る工夫

●その1:ピグアバターをフィギュアらしくリデザイン
アメーバピグのアバターデザインとピグファンタジアのアバターデザインを並べてみると大きな違いがあります。

1 pixel|サイバーエージェント公式クリエイターズブログ-4ピグファンタジア
「おもちゃの世界を冒険する」というストーリーのもと、ユーザーはピグドールというフィギュアになって世界中を冒険します。おもちゃらしさを出すためにピグアバターから更にボディをシンプルに丸みのあるフォルムに仕上げました。

ファンタジアアバターではしっかり正面を向かせることでユーザーがアバターに見つめられているようなデザインにしました。更に、ピグにはなかった輪郭線を強調することで小さなスマホ画面でも形を意識できるよう、またはっきりとした線で「フィギュアらしさ」や「集めたくなるような人形の可愛さ」を表現させています。

このことから、実は5000個近くある体のパーツもひとつひとつすべて描き直して新しいデザインに作り直しているのです。

1 pixel|サイバーエージェント公式クリエイターズブログ-ピグモン一覧
様々な種類のドールを統一する手段として、ドールにはすべて規定の枠線をつける事でおもちゃの世界に住むキャラたちのデザインを統一しております。枠をつける目的は他にも、スマホなどの小さい画面でも背景やUIに同化する事なくハッキリ認識させるための工夫でもあります。

1 pixel|サイバーエージェント公式クリエイターズブログ-PF_すれ違い
ちなみに、ドールの足下に付いているステージはそのキャラクターのレアリティを表しています。ユーザーも着せる服のレアリティによってステージのカラーが変化します。友達とすれ違うと着ている服のレアリティが一目瞭然でわかりますね!

●その2:NPCのキャラクター化
$1 pixel|サイバーエージェント公式クリエイターズブログ-NPC
ゲームのストーリーを進めていくのに重要な存在であるストーリーキャラ(NPC)たち。ピグファンタジアではストーリー中に通常のピグドールではなく新たにキャラクターイラストが登場します。ストーリーキャラにはキャラクターイラストとピグドールの2パターンを制作しています。

1 pixel|サイバーエージェント公式クリエイターズブログ-ピグファンタジアーキャラクターシネマ
キャラクターイラストもピグドールイラストも感情にあわせてもちろん表情が変化します。セリフにあわせて表情が変わるので、ピグドールよりも細かい表情表現がなされています。

ストーリーを進めるとキャラクターたちは仲間にすることができ、ユーザードールと同じく服を着せ替えることもできます。また着せ替える服にあわせて、これらのイラストはすべて服やポーズを書き下ろししています。ストーリーキャラの服は期間限定イベントを進めることで手に入れる事ができます。
1 pixel|サイバーエージェント公式クリエイターズブログ-9ピグファンタジア
完成したストーリーキャラのイラストを元に、ピグドールのデザインは装飾をディフォルメして作られています。非常に細かい装飾も特徴的な部分を残してすっきりシンプルに作る工夫をしました。

ストーリーキャラたちは冒険の仲間に出来たり、具体化されたイラストが登場することで画面から飛び出して自分に話しかけてくれるような、いつもより身近に感じられる存在となりました。

ピグの世界観を崩さない工夫

ピグ×ファンタジーのテーマには今までのピグになかった「戦い」や「勝ち負け」といった新しい要素が加わりました。これらの要素をもともとのピグの世界観にあわせた表現にするために、ストーリー上いくつか配慮をしております。

●その1:ボスの存在について
1 pixel|サイバーエージェント公式クリエイターズブログ-ボスについて
ストーリーを進める中で手強いボスが出てきます。これらのボスは戦って倒す存在ではなく「みんなで協力して冒険で得た知恵を見せて満足させてあげる」という設定にして登場させました。満足させるには冒険を重ねて知恵を鍛える必要があります。

●その2:敵の存在について
1 pixel|サイバーエージェント公式クリエイターズブログ-ベルベット
敵も存在しますが、同じ目的をめざしてユーザーに勝負を仕掛けてくるライバル的存在です。お互いが戦って傷つけあったりすることはしません。ストーリー中によく登場してユーザーにちょっかい出しながらも互いにある目標を目指して競い合う実はいいヤツらです。

おもちゃらしいカワイイデザインの工夫

ここからはピグファンタジアにおけるおもちゃの世界を表現するためのデザインの工夫についてご説明します。

●1:エリアにミニチュア感を
1 pixel|サイバーエージェント公式クリエイターズブログ-エリア
エリアの中にはいくつか巨大化したアイテムを置いてミニチュア感を出させています。また、ドールハウスのようなお店や全体的にアメーバピグよりもカラーが少々明るめのデザインを使っておもちゃらしさを表現しました。草原や洞窟といった自然エリアにも、プラスチックで出来たような花や宝石を置いてかわいらしい工夫をしております。

ユーザードールはこのエリアの中をトコトコと進んで行きます。

●2:ドールにおもちゃの装飾をつける
1 pixel|サイバーエージェント公式クリエイターズブログ-おもちゃらしさ
すべてのドールにフィギュアらしいステージ台を付ける他に、ドールたちのデザインにおもちゃらしい要素を加えています。縫い目やねじまきであったり、他の物質と混ぜて置き物のようにしたり、プラスチックや柔らかい感触など素材に合わせて描き分ける事でおもちゃの世界のドールを表現しました。

さいごに

いかがだったでしょうか?
細かい部分でありながらも、ひとつひとつこだわりを加えることでピグファンタジアのかわいいおもちゃの世界が完成しました。

ピグは今年で4周年。今後も皆様に親しまれながらも、今までにない新しいゲームを
続々制作していく予定です。

最後までお読み頂きありがとうございました!
今後とも、アメーバピグとピグファンタジアをよろしくおねがいします!

ピグファンタジアへ

はじめまして。
アメーバ事業本部コア室コアディベロップメントグループの新屋です。
コア室では3Dアニメーションライブラリの開発を行っています。


リアルタイム・プリレンダ問わず3Dモーショングラフィックが好きで趣味でも3Dの制作をしています。プリレンダはTAKCOMさん、WOWのdaihei shibataさん、ogaoooooさん、リアルタイムはMasato Tsutsuiさん等の作品が好きです。

リアルタイム3D


昨今のスマートフォンの処理能力の向上に伴い、webブラウザでも一昔前にPCで動作していたようなリッチなコンテンツも高速に処理できるようになりました。
スマホサービスでは必ずと言っていい程CSSやjavascriptでのUI・演出アニメーションが組込まれています。
そうしたアニメーションの表現力を引き上げる方法の一つとしてリアルタイム3Dを紹介します。


リアルタイム3Dを扱う言語としてはopenGLやDirectXがありますが、スマートフォンのブラウザでは動作しません。
しかし3D理論は数学で成り立っているので、線を描いたり塗りつぶしたりするメソッドがあれば作ることができます。
スマートフォンでも動作可能なjavascriptのcanvasAPIでも実装が可能です。


もちろん専用の言語ではないので表現力ではopenGL等には及びません。
しかしアイディアとの組合せによって実用するに耐えうるコンテンツを作ることができます。


canvasAPIで実装されたデモを用意しました。
サンプルコード(jsdo.it)

デモには今回解説には含まれていない理論なども含まれていますが、javascriptのみでもこのような表現が可能です。


今回はリアルタイム3Dの基本となる座標変換を解説します。
長いですが最後までお付き合いください。


座標変換

3次元のオブジェクトにはXYZ方向への座標情報がありますが、デバイスのディスプレイは2次元のXY方向にしかピクセルが存在しません。


その為3次元のオブジェクトを投影するには座標を2次元に変換する必要があります。
この算出方法は形が決まっていてこのような流れで行われます。


この変換の流れをまとめて3次元アフィン変換と言います。
こうして見ると大変そうな感じもしますが、実際に実装しながら覚えていくと理解しやすいと思います。


カメラの定義

3Dは2Dと違いオブジェクトだけではなく観察者が存在します。
空間内のある位置からある角度でオブジェクトを観察した時の座標を算出する為です。
この観察者がビュー座標変換のビューです。


アフィン変換を実装する前にこの観察者の情報を定義しましょう。
ビューに必要な情報は様々ありますが今回は下記の最小限に絞ります。

・視点
・注視点
・視点と注視点の距離
・視線角度
・拡縮率
・ディスプレイ座標(本来ビューの情報ではないですがここにまとめます)
・距離と視線角度のアップデート用メソッド

camera変数として定義します。

var camera = {
 self : {
  x : 0,
  y : 0,
  z : 300
 },
 target : {
  x : 0,
  y : 0,
  z : 0
 },
 distance : {
  x : 0,
  y : 0,
  z : 0
 },
 angle : {
  cosPhi : 0,
  sinPhi : 0,
  cosTheta : 0,
  sinTheta : 0
 },
 zoom : 1,
 display : {
  x : setup.width/2,
  y : setup.height/2,
  z : 0
 },
 update : function() {
  camera.distance.x = camera.target.x - camera.self.x;
  camera.distance.y = camera.target.y - camera.self.y;
  camera.distance.z = camera.target.z - camera.self.z;
  camera.angle.cosPhi = -camera.distance.z / Math.sqrt(camera.distance.x*camera.distance.x + camera.distance.z*camera.distance.z);
  camera.angle.sinPhi = camera.distance.x / Math.sqrt(camera.distance.x*camera.distance.x + camera.distance.z*camera.distance.z);
  camera.angle.cosTheta = Math.sqrt(camera.distance.x*camera.distance.x + camera.distance.z*camera.distance.z) / Math.sqrt(camera.distance.x*camera.distance.x + camera.distance.y*camera.distance.y + camera.distance.z*camera.distance.z);
  camera.angle.sinTheta = -camera.distance.y / Math.sqrt(camera.distance.x*camera.distance.x + camera.distance.y*camera.distance.y + camera.distance.z*camera.distance.z);
 }
};

このカメラ変数を元にアフィン変換のメソッド群を実装します。


アフィン変換の実装

行列計算

アフィン変換には行列計算を使うため行列計算自体を行うメソッドを用意し下記のような行列を当てはめて値を求めると便利ですが、スマートフォンで動かすには計算量が多いため手順毎に省略したメソッドを用意しました。


例式はY軸に対して回転を行う行列です。


省略した式がこのようになります。

x` = x*cosθ+ z*sinθ
y` = y
z` = -x*sinθ + z*cosθ

今回は行列についての解説は省略しますが、3Dを掘り下げていくには行列計算は必須なので3Dが面白くなったら調べてみてください。
特にwebGLはopenGLと違い座標変換メソッドが省かれているので一式自分で用意する必要があります。


実装

アフィン変換は三角関数を多用する為、まずは角度をディグリーからラジアンに変換するメソッドを用意します。


var dtr = function(d) {
 return d*Math.PI/180;
};

アフィン変換のオブジェクトを用意してメソッドを一つずつ書き加えていきます。

var affine = {};

ローカル→ワールド変換
affine内にworldオブジェクトを用意し記述していきます。


△拡大・縮小
座標とXYZ三方向への拡縮率を受取り、ローカル座標の拡大・縮小を行います。

size : function(p, size) {
 return {
  x : p.x * size.x,
  y : p.y * size.y,
  z : p.z * size.z
 }
}

△回転
座標とXYZ軸の回転角を受取り、ローカル座標の回転を行います。

rotate: {
 x : function(p, rotate) {
  return {
   x : p.x,
   y : p.y*Math.cos(dtr(rotate.x)) - p.z*Math.sin(dtr(rotate.x)),
   z : p.y*Math.sin(dtr(rotate.x)) + p.z*Math.cos(dtr(rotate.x))
  }
 },
 y : function(p, rotate) {
  return {
   x : p.x*Math.cos(dtr(rotate.y)) + p.z*Math.sin(dtr(rotate.y)),
   y : p.y,
   z : -p.x*Math.sin(dtr(rotate.y)) + p.z*Math.cos(dtr(rotate.y))
  }
 },
 z : function(p, rotate) {
  return {
   x : p.x*Math.cos(dtr(rotate.z)) - p.y*Math.sin(dtr(rotate.z)),
   y : p.x*Math.sin(dtr(rotate.z)) + p.y*Math.cos(dtr(rotate.z)),
   z : p.z
  }
 }
}

△位置移動
座標とXYZ三方向への移動量を受取り、ローカル座標の移動を行います。

position : function(p, position) {
 return {
  x : p.x + position.x,
  y : p.y + position.y,
  z : p.z + position.z
 }
}

○ワールド→ビュー変換
affine内にviewオブジェクトを用意し記述していきます。


△視線方向の回転
座標を受け取り、視点と注視点を元に算出した回転角で座標の回転を行います。

phi : function(p) {
 return {
  x : p.x*camera.angle.cosPhi + p.z*camera.angle.sinPhi,
  y : p.y,
  z : p.x*-camera.angle.sinPhi + p.z*camera.angle.cosPhi
 }
}
theta : function(p) {
 return {
  x : p.x, 
  y : p.y*camera.angle.cosTheta - p.z*camera.angle.sinTheta,
  z : p.y*camera.angle.sinTheta + p.z*camera.angle.cosTheta
 }
}

△視点位置補正
視点を(x0, y0, z0)に補正します。

viewReset : function(p) {
 return {
  x : p.x - camera.self.x,
  y : p.y - camera.self.y,
  z : p.z - camera.self.z
 }
}

○ビュー→パースペクティブ変換
座標を透視投影で近いものは大きく、遠いものは小さく見えるように変換します。

perspective : function(p) {
 return {
  x : p.x * camera.distance.z/p.z * camera.zoom,
  y : p.y * camera.distance.z/p.z * camera.zoom,
  z : p.z * camera.zoom,
  p : camera.distance.z/p.z
 }
}

○パースペクティブ→ディスプレイ変換
webブラウザは左上を基準にX方向は右・Y方向は下に行くほど大きくなりますが、
3Dではステージの原点を基準にX方向は右・Y方向は上・Z方向は手前に来るほど数値が大きくなるのでこの座標変換で向きを変えます。

display : function(p, display) {
 return {
  x : p.x + display.x,
  y : -p.y + display.y,
  z : p.z + display.z,
  p : p.p
 }
}

○一括処理
最後にここまで書いてきたメソッドを一括で処理するためのメソッドを用意します。
頂点・拡縮・回転・移動・ディスプレイ座標を引数で渡すと座標変換された値が返ってきます。

process : function(model, size, rotate, position,display) {
 var ret = affine.world.size(model, size);
 ret = affine.world.rotate.x(ret, rotate);
 ret = affine.world.rotate.y(ret, rotate);
 ret = affine.world.rotate.z(ret, rotate);
 ret = affine.world.position(ret, position);
 ret = affine.view.phi(ret);
 ret = affine.view.theta(ret);
 ret = affine.view.viewReset(ret);
 ret = affine.perspective(ret);
 ret = affine.display(ret, display);
 return ret;
}

以上ここまでがアフィン変換です。



頂点クラス

更にここまで組んできたアフィン変換を楽に使うために頂点情報を保持するクラスを作ります。
内容としてはインスタンス生成時に初期値を引数で受取ってaffineInに保持し、それを元にアフィン変換された値をaffineOutに入れるものです。


単純な内容ですが多数の頂点情報を扱う際にはこうした管理用クラスが無いと大変です。

var vertex3d = function(param) {
 this.affineIn = new Object;
 this.affineOut = new Object;
 this.affineIn.vertex = (param.vertex);
 this.affineIn.size = (param.size);
 this.affineIn.rotate = (param.rotate);
 this.affineIn.position = (param.position);
};
vertex3d.prototype = {
 vertexUpdate : function() {
  this.affineOut = affine.process(
   this.affineIn.vertex,
   this.affineIn.size,
   this.affineIn.rotate,
   this.affineIn.position,
   camera.display
  );
 }
};

描画

ここまで長くなりましたが書いてきたコードで立方体を描画してみましょう。
8つの頂点を線で結んだだけの単純なオブジェクトです。


先ほども書いた通り3Dでは画面の中心を基準にXは右・Yは上・Zは手前に数値が大きくなるので、頂点は原点を基準に-1~1の範囲で配置します。
さらに拡縮率100を3方向に掛け合わせることで一辺200の立方体の頂点が完成します。

var v = new Array();
v[0] = new vertex3d({
 vertex : {x:-1,y:1,z:1},
 size : {x:100,y:100,z:100},
 rotate : {x:20,y:-20,z:0},
 position : {x:0,y:0,z:0}
});
v[1] = new vertex3d({
 vertex : {x:1,y:1,z:1},
 size : {x:100,y:100,z:100},
 rotate : {x:20,y:-20,z:0},
 position : {x:0,y:0,z:0}
});
v[2] = new vertex3d({
 vertex : {x:1,y:-1,z:1},
 size : {x:100,y:100,z:100},
 rotate : {x:20,y:-20,z:0},
 position : {x:0,y:0,z:0}
});
v[3] = new vertex3d({
 vertex : {x:-1,y:-1,z:1},
 size : {x:100,y:100,z:100},
 rotate : {x:20,y:-20,z:0},
 position : {x:0,y:0,z:0}
});
v[4] = new vertex3d({
 vertex : {x:-1,y:1,z:-1},
 size : {x:100,y:100,z:100},
 rotate : {x:20,y:-20,z:0},
 position : {x:0,y:0,z:0}
});
v[5] = new vertex3d({
 vertex : {x:1,y:1,z:-1},
 size : {x:100,y:100,z:100},
 rotate : {x:20,y:-20,z:0},
 position : {x:0,y:0,z:0}
});
v[6] = new vertex3d({
 vertex : {x:1,y:-1,z:-1},
 size : {x:100,y:100,z:100},
 rotate : {x:20,y:-20,z:0},
 position : {x:0,y:0,z:0}
});
v[7] = new vertex3d({
 vertex : {x:-1,y:-1,z:-1},
 size : {x:100,y:100,z:100},
 rotate : {x:20,y:-20,z:0},
 position : {x:0,y:0,z:0}
});

ここで頂点を-100~100、拡縮率は1で記述しても構いません。


必要な頂点インスタンスが用意できたらタイマー関数内でアップデートと描画を行ないます。
せっかくなので回転もさせてみましょう。

//オブジェクトの回転角変数
var cubeRotate = {
 x : 0,
 y : 0,
 z : 0
};
var loop = function() {
 setTimeout(function() {
  ctx.clearRect(0, 0, 500, 500); //canvasクリア
  camera.update(); //注視距離・視線角のアップデート


  //回転角を加算
  cubeRotate.x += 0.5;
  cubeRotate.y += 1;
  cubeRotate.z += 3;


  //各頂点に回転角を代入し頂点をアップデート
  for(var i=0; i<v.length; i++) {
   v[i].affineIn.rotate = cubeRotate;  v[i].vertexUpdate();
  };


  // それぞれの頂点座標を結ぶ
  ctx.beginPath();
  ctx.moveTo(v[0].affineOut.x, v[0].affineOut.y);
  ctx.lineTo(v[1].affineOut.x, v[1].affineOut.y);
  ctx.lineTo(v[2].affineOut.x, v[2].affineOut.y);
  ctx.lineTo(v[3].affineOut.x, v[3].affineOut.y);
  ctx.lineTo(v[0].affineOut.x, v[0].affineOut.y);


  ctx.moveTo(v[4].affineOut.x, v[4].affineOut.y);
  ctx.lineTo(v[5].affineOut.x, v[5].affineOut.y);
  ctx.lineTo(v[6].affineOut.x, v[6].affineOut.y);
  ctx.lineTo(v[7].affineOut.x, v[7].affineOut.y);
  ctx.lineTo(v[4].affineOut.x, v[4].affineOut.y);


  ctx.moveTo(v[0].affineOut.x, v[0].affineOut.y);
  ctx.lineTo(v[4].affineOut.x, v[4].affineOut.y);


  ctx.moveTo(v[1].affineOut.x, v[1].affineOut.y);
  ctx.lineTo(v[5].affineOut.x, v[5].affineOut.y);


  ctx.moveTo(v[2].affineOut.x, v[2].affineOut.y);
  ctx.lineTo(v[6].affineOut.x, v[6].affineOut.y);


  ctx.moveTo(v[3].affineOut.x, v[3].affineOut.y);
  ctx.lineTo(v[7].affineOut.x, v[7].affineOut.y);


  ctx.stroke(); //描画
  loop();
 }, 1000/60);
};
loop();

実行結果


サンプルコード(jsdo.it)


キューブが描画されました。次は線ではなく面を描画してみましょう。
どの面かが分かりやすいよう一面ずつ色を塗り分けます。


描画部分をこのように記述します。

//面1の描画
  ctx.beginPath();
  ctx.moveTo(v[0].affineOut.x, v[0].affineOut.y);
  ctx.lineTo(v[1].affineOut.x, v[1].affineOut.y);
  ctx.lineTo(v[2].affineOut.x, v[2].affineOut.y);
  ctx.lineTo(v[3].affineOut.x, v[3].affineOut.y);
  ctx.closePath();
  ctx.fillStyle = "hsla(0, 100%, 70%, 1)";
  ctx.fill();
  
  //面2の描画
  ctx.beginPath();
  ctx.moveTo(v[1].affineOut.x, v[1].affineOut.y);
  ctx.lineTo(v[5].affineOut.x, v[5].affineOut.y);
  ctx.lineTo(v[6].affineOut.x, v[6].affineOut.y);
  ctx.lineTo(v[2].affineOut.x, v[2].affineOut.y);
  ctx.closePath();  
  ctx.fillStyle = "hsla(30, 100%, 70%, 1)";
  ctx.fill();


  //面3の描画
  ctx.beginPath();
  ctx.moveTo(v[5].affineOut.x, v[5].affineOut.y);
  ctx.lineTo(v[4].affineOut.x, v[4].affineOut.y);
  ctx.lineTo(v[7].affineOut.x, v[7].affineOut.y);
  ctx.lineTo(v[6].affineOut.x, v[6].affineOut.y);
  ctx.closePath();
  ctx.fillStyle = "hsla(60, 100%, 70%, 1)";
  ctx.fill();


  //面4の描画
  ctx.beginPath();
  ctx.moveTo(v[4].affineOut.x, v[4].affineOut.y);
  ctx.lineTo(v[0].affineOut.x, v[0].affineOut.y);
  ctx.lineTo(v[3].affineOut.x, v[3].affineOut.y);
  ctx.lineTo(v[7].affineOut.x, v[7].affineOut.y);
  ctx.closePath();
  ctx.fillStyle = "hsla(90, 100%, 70%, 1)";
  ctx.fill();


  //面5の描画
  ctx.beginPath();
  ctx.moveTo(v[4].affineOut.x, v[4].affineOut.y);
  ctx.lineTo(v[5].affineOut.x, v[5].affineOut.y);
  ctx.lineTo(v[1].affineOut.x, v[1].affineOut.y);
  ctx.lineTo(v[0].affineOut.x, v[0].affineOut.y);
  ctx.closePath();
  ctx.fillStyle = "hsla(120, 100%, 70%, 1)";
  ctx.fill();


  //面6の描画
  ctx.beginPath();
  ctx.moveTo(v[3].affineOut.x, v[3].affineOut.y);
  ctx.lineTo(v[2].affineOut.x, v[2].affineOut.y);
  ctx.lineTo(v[6].affineOut.x, v[6].affineOut.y);
  ctx.lineTo(v[7].affineOut.x, v[7].affineOut.y);
  ctx.closePath();
  ctx.fillStyle = "hsla(150, 100%, 70%, 1)";
  ctx.fill();

実行結果


サンプルコード(jsdo.it)


明らかにおかしいです。
これは本来奥にあるはずの面を関係なしに面1~6の順に描画してしまっている為です。
正確に描画するには奥にあるか手前にあるかを面ごとに判定する必要があります。


このように面ごとにソートする方法をZソートと言います。


奥行き判定する際、手前か奥かというとz座標を使うことを考えてしまいます。
しかしz座標はカメラに対する順序ではないのでオブジェクトの裏に回り込んだ際などに描画が崩れてしまいます。


判定に使用するのはアフィン変換で出力したパースペクティブ値です。

instance.affineOut.p;

各面に使用されている頂点のパースペクティブ値を平均してsort関数にかけましょう。


面ごとにどの頂点を使用するかを保持する配列を作ります。
そのまま配列をsortするとどの面が何色か判断できないので一緒に面の色も持たせます。

var f = new Array();
f[0] = {
 color : "hsla(0, 100%, 70%, 1)",
 verticies : [0,1,2,3]
};
f[1] = {
 color : "hsla(30, 100%, 70%, 1)",
 verticies : [1,5,6,2]
};
f[2] = {
 color : "hsla(60, 100%, 70%, 1)",
 verticies : [5,4,7,6]
};
f[3] = {
 color : "hsla(90, 100%, 70%, 1)",
 verticies : [4,0,3,7]
};
f[4] = {
 color : "hsla(120, 100%, 70%, 1)",
 verticies : [4,5,1,0]
};
f[5] = {
 color : "hsla(150, 100%, 70%, 1)",
 verticies : [3,2,6,7]
};

各面が使用する頂点のパースペクティブ値を平均化して比較するsort関数を作成します。
これで面の配列が視点からの距離に則した順序に並び替えられます。

f.sort(
 function(a, b) {
  var pA = 0;
  for(var i=0; i<a.verticies.length; i++) {
   pA += v[a.verticies[i]].affineOut.p;
   if(i == a.verticies.length-1) {
    pA /= 4;
   };
  };
  var pB = 0;
  for(var i=0; i<b.verticies.length; i++) {
   pB += v[b.verticies[i]].affineOut.p;
   if(i == b.verticies.length-1) {
    pB /= 4;
   };
  };
  if (pA < pB) {
   return -1;
  };
  if (pA > pB) {
   return 1;
  };
  return 0;
 }
);

並び替えた面を描画します。

for(var i=0; i<f.length; i++) {
 ctx.beginPath();
 for(var j=0; j<f[i].verticies.length; j++) {
  if(j==0) {
   ctx.moveTo(v[f[i].verticies[j]].affineOut.x, v[f[i].verticies[j]].affineOut.y);
  } else {
   ctx.lineTo(v[f[i].verticies[j]].affineOut.x, v[f[i].verticies[j]].affineOut.y);
  };
 };
 ctx.closePath();
 ctx.fillStyle = f[i].color;
 ctx.fill();
};

実行結果


サンプルコード(jsdo.it)


正確に描画されました。
ここまで組めれば後は頂点位置を変更して様々な形が組めるようになっています。
更にこうしたオブジェクトも形ごとにクラス化しておくと大量のオブジェクトを動かす際に便利です。



まとめ

以上ここまでいかがでしたでしょうか?
今回解説したのは3Dの基本中の基本ですが、自分はここまで理解するだけでも大変だったのを覚えています。
しかしその分動いた時の喜びも大きかったです。


3Dに興味があるけど難しいという人は先に理論から考えずに、3D空間を思い浮かべて一つ一つの事象をイメージで理論と結びつけてください。
数学自体を追及しているわけではないので、行列式などは「この式が空間を再現する為の式なんだ」くらいの認識で十分です。


簡単だったという人は、次にライティングとテクスチャマッピングを調べて実装するとぐっと表現力が増します。


この記事で一人でも多くの方の人に3Dに興味を持ってもらえるとうれしいです。
最後までお付き合いいただきありがとうございました。



みなさまこんにちは。
アメーバピグ(以下ピグ) でFlashディベロッパーをしております、
2012年入社のスズキヨシフミと申します。

私の所属するチームでは、ユーザビリティ向上のために既存機能のUI改修や
基盤部分の新機能開発、運用などを行っています。

今回は、前回 の鈴木彩夏さんに引き続き、
ピグのミニゲームとしてリリースされたピグ麻雀についてご紹介します。
技術的な真新しさはありませんが、一つのプロジェクトのリリースまで、
フロント面を一から作り上げる過程を感じ取っていただけたら幸いです。

麻雀の開発について

今回このプロジェクトの立ち上げを担当した開発メンバーは、
2012年新卒入社の、デザイナー、ディベロッパー、エンジニアの各一名ずつでした。

麻雀は非常に複雑な通常のルールに加え、ローカルルールがあり、それらを採用するかどうかはサービス毎に異なります。(トランプの大富豪でいう8切りや11バックなどのルールです)
これらのルールの選定は麻雀ゲームとしての面白さに直結する部分であり、非常に難しいものでした。

詳しい人にアンケートをとったり、他のサービスのルールを調査することも大事ですが、私たちは実際に雀荘に通い、その場の雰囲気や卓を囲んでいる楽しさを体感し、それをいかにオンラインで表現するかを考えながら、ゲームの企画をしていきました。また、プロジェクト管理については、責任者として開発を任される機会を得る事ができたことも、とても貴重な経験でした。

運用を見据えた開発

私がピグ麻雀開発時、特に心掛けていたディベロップメントのポイントは、サービスの今後の拡張性です。麻雀ゲームはローカルルールが変わることはありますが、基本的にゲーム自体が大幅に変わることはありません。そのため、リリース後に継続してユーザに楽しんでもらうための一つの運用方法として、麻雀ゲームの面白さに加え、ピグの特徴であるエリアや世界観を最大限に活かしたいと考えました。企画の段階でもゲーム自体の方向性を考える際に、

「こういう世界観で統一していきたいね」
「あのエリアで出しても面白いんじゃない?」

といった会話もあり、今後新しい世界観で作る時のために、大きな改修を行わずにリリースできるようにしました。
例えば、前回の記事 では麻雀牌のフォントデザインについて触れていましたが、仮にもっとポップな文字と柄のものをリリースしたいとなった時に、麻雀牌のflaデータをコピーする、又は作り直すとなっては非常に手間が掛かります。
そこで麻雀牌のアニメーションが入った牌のベースとなるデータと全ての柄が入ったデータを分けて作成しました。

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

上記の構成にすることで、柄のデザインができるだけでアニメーションの入った別の麻雀牌を簡単に置き換えることができます。また、UIや演出部分についても同様に設計されており、色味や読み込むUIを、エリアに置かれている麻雀卓の家具の設定データを変更するだけで反映させることができます。

企画やキャラクター等とのコラボレーションが多いピグにとっては、このような運用を考えた開発が後々の拡張性や開発効率に大きく影響し、スピード感を持って開発を進めることに繋がるのだと思います。

ピグらしさのある演出

次に、麻雀の重要なポイントであるツモやポンといったユーザの重要な行為と麻雀牌の動きなどの演出表現をどうまとめていったかをお話ししたいと思います。企画段階で、様々な麻雀コンテンツを見ていた際に感じた事としては、
・ギャンブル
・リアルさ
といったイメージが強かったです。私は元々ギャンブルが好きだったこともあり、高揚する演出はユーザとして数多く目にしていたので苦労せずに進められると思っていました。

1 pixel|サイバーエージェント公式クリエイターズブログ-麻雀(before)

こちらは麻雀のリアルな倒れる感じを表現した牌の動きですが、ゲームの流れの中で目にするとこの部分だけに生々しさが残ってしまい、ピグのゲームとしての全体的なリズム感が崩れてしまいます。

1 pixel|サイバーエージェント公式クリエイターズブログ-麻雀(after)

そこで最終的な完成イメージに近いものがこちらになります。
・麻雀牌の倒す時のワクワク感を保つ
・ピグらしくポップさのある動き
・UIとして場の構成を破綻させずに魅せる

アニメーションを作る過程では、違和感のない動きに加えてその世界観にマッチした演出、デザイナーのデザインを忠実に表現する事の難しさを体感しました。

また、モック作成の段階で出てきた演出のアイディアを実装し、わかりづらさやゲームの流れの中での問題点を早期に解決できた、「ポン」の一例をあげたいと思います。

1 pixel|サイバーエージェント公式クリエイターズブログ-鳴き(before)

こちらが改善前の演出、


1 pixel|サイバーエージェント公式クリエイターズブログ-鳴き(after)

こちらが改善後の演出になります。

一見改善前の演出の方が大きく目立ち、アバターが表示されるのでわかりやすく感じられるかもしれませんが、この「ポン」とは簡単に説明すると相手の捨てた牌を持ってくる行為であり、ここで重要なのは誰が、どこにいる人がしたのかということ、またゲームにおいてそこまで重要な局面ではない事から他の重要な演出との差別化をはかり、テンポ感を壊さないことの方が重要となります。

このように、実際に動かしてみないと気付きにくいことにもモックの段階から演出のパターンを比較して、ゲーム全体のバランスを調整していきました。

最後に

以上がディベロッパーとして一つのサービスのリリースまでに学ぶことができたことです。
最後までお読みいただき、ありがとうござます。

引き続きアメーバピグ を宜しくお願い致します!