CSS RPG (CSS Programming Advent Calendar 2012)

CSS Programming Advent Calendar 2012 10日目です。

今回の自分の作ったデモは全てOperaでしか動作しませんので、 まだインストールしていない方は予め

opera.com

にてダウンロード、インストールを行なっておいてください。

今回作成したのがこちらです。

CSS RPG

操作方法はShift+矢印キーで移動。
エンターキーを押すことでアクションをおこすことができます。
Shift+エンターは動作しません。

注意

最後の方でいきなりフォーカスしている場所が黒くなってしまうバグがあります…
最後のエリアで矢印キーを連続で押すとワープしてしまいます…

矢印キーがCSSで動作する仕組みは過去にも作られていました

過去にはgrfxdsgnさんが作ったような下のような作品がありましたが、
CSS RPGはこの仕組みとは全く別の仕組みを利用しました。

CSSだけでRPGっぽい仕組み

今回矢印キーを動作されるために利用した仕組み

Operaには

TV 向け Web コンテンツを作成する-空間ナビゲーション

に書いてあるように、空間ナビゲーションという機能が搭載されており、
これによって、家庭用テレビのブラウザやWiiではリモコンを使って、
デスクトップ版ではShift+ 矢印キーを使って、
リンクやフォーム要素などにフォーカスをあてて
コンテンツを移動出来るようになっています。

もしそれ以外の要素にフォーカスを当てたい場合は、
onclick属性onmouseover属性
addEventListenerメソッドを当てることで、
これらの要素にもフォーカスを当てることができるようになります。

なかなか便利な機能(?)ですね。

制作者の意図するフォーカス移動を行わせる

空間ナビゲーションはデフォルトでは自動で方向キーに対する要素を選んでいるため、
時々意図しない意図しないフォーカス移動を行います。

これをcssのプロパティによって制御することができ、
制作者が意図しないナビゲーションの動作を制限することができます。
わかりにくいのでデモを一つ載せます。

まずこれを見てください。 divonclick属性を当てて空間ナビゲーションが有効になるようにしました。

このままでもフォーカスを自由に移動することはできますが、
5の時に右を押したら6に、 6の時に左を押したら5にのように移動できたら便利ですよね。

navプロパティ

空間ナビゲーションの移動の制御にはnavプロパティを利用します。

Directional focus navigation

nav-right:#right;
nav-left:#left;
nav-down:#down;
nav-up:#top;

こんな感じで記述します。 nav-upだったら↑を押した時にid=topにフォーカスさせます。 という感じです。

先ほどのデモを書き換えてみました。

1の時に左を押すと10に、
5の時に右を押すと6に、
6の時に左を押すと5に、
10の時に右を押すと1に戻るようにしてあります。
また、1から5の時は下を押しても動かないようにnav-downに自分のidを指定しています。

こうすることで移動の制限がかけられるので、
input:checkedが入るまで、一時的に通行禁止にするといった処理が可能になります。

空間ナビゲーションを利用するの注意

デフォルトではフォーカスした部分に青いアウトラインが表示されます。
今回の作成したCSS RPGではこれを消しています。
上記に記載したドキュメントでは、

:focus { outline: 0 solid; }

で消えるよ!
って書いてあったのですが、消えませんでした。
仕方がないので、透明色をぶちこんであげたら消えてくれました。

:focus { outline: 1px solid rgba(0,0,0,0); }

キャラクターの表示

疑似クラスfocusに擬似要素beforeをあてて、
contentプロパティ内に画像を表示させています。
これによってマップのタイル上にキャラクターが乗って移動しているように見せることができます。

今回のコード中ではこんな感じです。

 .gameStage [class*="Area"] div:focus::after{
 content: url('data:image/gif ~~(略)');
}

アクションの仕組み

CSS RPGではアクションを起こすことができます。

最初に登場する魔法陣の上でエンターキーを押すことで、
新しい道が出たり、アニメーションが起こったりします。

これはlabel空間ナビゲーションが当たるようにしておくことで、
その場所でエンターキーを押した時に対応するinput:radioがオンになります。

あとは間接セレクタを利用して、

 #sw1:checked ~ .gameStage{
 display: block;
}
/ sw1がチェックされている時のgameStageの処理 /

のように書いていってあげればエンターキーを押した時の
アクションを起こすことが可能になります。

エフェクト

途中でピカーンって光の柱がでたり、
爆発したりするアニメーションはBackgroundプロパティを利用したアニメーションです。

より簡単に位置を管理するために以下の記事が大変参考になりました。

CSS SpritesとCSSアニメーションでコマアニメを作る / inputxoutput

その他

SCSS

今回Scssを利用してコードを書きました。
今回の例で特に力を発揮したのはマップの生成です。

タイルマップは全てposition:absoluteで配置しています。
そのため、素のCSSで書いていくと

top: 320px;
left: 192px;

のように何番目のタイルなのかが分かりにくいです。
そのため、最初に

$boxSize : 32px;

と定義しておいてあげて、

@for $i from 1 through 15 {
    div{
        &:nth-child(#{$i}){
            left: $boxSize * $i;
        }
    }
}

という感じで書くことで面倒な計算をせずにマップを配置しています。

属性セレクタ

  • スタート画面、ゲーム画面 → Stageをクラス名に含める
  • アクションを起こすことで表示されるタイルの集まり → Areaをクラス名に含める
  • 壁 → rowをクラス名に含める
  • タイル → Tileをid名に含める

といった命名規則を作ってあげれば、

[class*="Area"]{
 background-image: url(../images/background.jpg);
}

という指定でAreaクラス名に含んでいる要素全てにスタイルをあてることができます。

どうしてもこういったものはCSSの記述が多くなってしまうので、
こういったルールを決めながらやっていったほうが効率がいいなと感じました。

感想

本当はもっといろいろつめ込みたかったり、
バグの謎が結局解決できなかったり、
とても満足とは言えない感じの制作物になってしまったので悔しいところではあります‥

アニメーションの実装やマップの配置や、
navプロパティの設定による移動管理、
間接セレクタによるスイッチの状態管理、
Operaでのデバッグ

うまくいかないことばかりでしたが、
自分自身の勉強にもなりましたし、
制作自体はとても楽しかったです。

11日目はWeb Design KOJIKA17@kojika17さんです!!

12/12 追記

jsdo.itに英語版移植してみました。

参考記事

素材元