スタイルシート 神経衰弱 の説明
戻る


JavaScript を使わず,スタイルシートだけで神経衰弱ゲームを作ってみます.
全くのお遊びです.これが何か実用の役に立つようなことは多分無いでしょう.スタイルシートは本来,動作を記述するものではありませんので,かなり力ずくで作っています.

画面
(これはキャプチャ画像です.これで遊ぶことはできません.)

使い方と仕組みについて説明します.


使い方
「ゲーム開始」ボタン
「ゲーム開始」をクリックすると,カードが裏向きの状態で表示されます.
カードをクリックするとそのカードが表向きになります.カードを 2 枚表向きにします.
2 枚のカードのランク(A,2,3,…)が同じであれば「取る」ボタンが表示されます.「取る」をクリックすると,そのカードが場から取り除かれます.
「取る」ボタン
カードのランクが異なるときは「伏せる」ボタンが表示されます.「伏せる」をクリックすると,カードが裏向きに戻ります.
「伏せる」ボタン
カードをすべて取り終えると「もう一度」ボタンが表示されます.ボタンをクリックすると,またプレイできます.


カードを並べる処理

カードを並べる場はグリッド レイアウトで作っています.カードがグリッドのアイテムです.
カードをシャッフルするためにアニメーションの機能を使います.それぞれのカードについて,アイテムの order が変化するアニメーションを無限に繰り返すように設定します.
アニメーションを停止する処理のためにチェック ボックスを使います.チェック ボックスは非表示にしておき,それにラベル(LABEL 要素)を関連付けて,「ゲーム開始」ボタンを作っています.
ボタンをクリックするとチェック ボックスが :checked 状態になるので,アニメーションを停止します.その時点の order の値でカードの並び順が決まります.
各カード毎にアニメーションのパラメータを変えてあるので,ボタンをクリックするタイミングによって並び順が変わります.

2024年に W3C の Working Draft で乱数に関する機能が提案されました.その機能が実装されれば,この処理はもっと簡単になると思います.

一般に,ゲームを作るときに構成要素の配置などをランダムに決めたいことがよくありますが,それをスタイルシートだけで行おうとすると問題が起きます.
何らかの方法で乱数のようなものを発生させて,それに従って配置などを決めようとすると,発生させた数が重複した場合に困ります.JavaScript を使うのであれば,重複しない数が得られるまで乱数の発生を繰り返せばよいのですが,スタイルシートではそのようなことはできません.
しかし,このプログラム(?)の場合はそのような問題はありません.order は重複していても構いませんし,連続している必要もありません.ですので,単にアニメーションで決めた値をそのまま order に指定すればよいことになります.
このプログラムではカードの位置関係で処理が変わることは無く,表示上カードの位置が変わってさえいればよいので,order を使って並び順を変えるだけで充分です.


カードを開ける処理

各カードについて,裏向き/表向きの状態を表すカスタム プロパティを定義します.ゲーム開始時はプロパティの値を裏向きの状態にします.
カードを表向きにする処理のためにボタン(TYPE 属性 BUTTON の INPUT 要素)を使います.ボタンは非表示にしておき,各ボタンに対してラベルを関連付けて,それをカードに付けています.
カードを押下すると対応するボタンが :active 状態になるので,そのカスタム プロパティを表向きの状態にします.
裏向きのカードはラベルで覆われているので,ランク/スート(スペード,クラブ など)の表示はラベルで隠されています.表向きにしたカードはラベルを背面にして,ランク/スートが見えるようにします.

これだけだと,ボタンが :active 状態でなくなるとプロパティの値が初期値に戻ってしまうので,:active 状態でなくなった後も値を保持しておくために transition の機能を流用します.
transition では,プロパティの設定値が変更されてから実際に値が変わり始めるまでのディレイを設定できます.
たとえば,カスタム プロパティ --a の値を保持しておきたい場合,transition でこのようにディレイを設定します.
transition:--a 0s 10000s;
そうすると,--a の設定が変わってもディレイで指定した時間が経過するまでは元の値が保持されます.ディレイに充分大きな値を指定することで設定した値を保持できます.
値を変更するときにはディレイをゼロにして,すぐに値が変わるようにします.
そのような方法で,ボタンが :active 状態でなくなった後もカードの表向きの状態を保持します.
ただし,実際には複数のカードのうちのひとつだけについてこの処理を行う必要があるので,ディレイの部分をカスタム プロパティで指定するようにして,該当のカードのディレイだけを変えるような処理にしています.

カードを裏向きに戻す処理のために,もうひとつボタンを使います.ボタンは非表示にしておき,ボタンに対してラベルを関連付けて,「伏せる」ボタンを作っています.ボタンはその前面に別の要素を置いて隠しておき,カードが 2 枚開けられたら前面に出します.
「伏せる」を押下するとボタンが :active 状態になるので,transition を無効にしてプロパティの値を裏向きの状態に戻しています.

カスタム プロパティに transition を使うために,後述のように @ルールの @property でプロパティを定義しています.


カードを取る処理

カードを取る処理のためにチェック ボックスを使います.
チェック ボックスは取れるカードの組み合わせの数分用意します.たとえば,「A」のカードを取る組み合わせは,次のように全部で 6 通りあります.
  • スペードの A と クラブの A
  • スペードの A と ハートの A
  • スペードの A と ダイヤの A
  • クラブの A と ハートの A
  • クラブの A と ダイヤの A
  • ハートの A と ダイヤの A
他のランクについても同様に 6 通りずつありますから,組み合わせの数は全部で 13 × 6 = 78 通りになります.その組み合わせに対応して 78 個のチェック ボックスを使います.
チェック ボックスは非表示にしておき,各チェック ボックスに対してラベルを関連付けて,「取る」ボタンを作っています.ボタンはその前面に別の要素を置いて隠しておき,開けたカードのランクが同じだった場合,その組み合わせに対応するボタンを前面に出します.
このとき「伏せる」ボタンも前面に出ますが,「取る」ボタンの方を「伏せる」ボタンより前面にしているので,「伏せる」ボタンは「取る」ボタンに隠されます.
「取る」をクリックすると対応するチェック ボックスが :checked 状態になるので,カードを取る処理を行います.
カードを取る処理では,開けたカードと,対応する「取る」ボタンを非表示にし,開けたカードの裏向き/表向きを表すプロパティを裏向きの状態に戻します.
非表示にしても裏向きの状態にするのは,内部的に表向きの状態が残っていると,残りのカードの処理に支障があるためです.


カスタム プロパティの定義

このプログラムでは,いくつかのカスタム プロパティを @ルールの @property で定義しています.
@property の定義は,たとえばこのように書きます.
@property --a {
  syntax:'<integer>';
  inherits:true;
  initial-value:0;
}
上述したように,カスタム プロパティの値を保持するために transition を使っている他,シャッフルの処理でカスタム プロパティにアニメーションを使っています.カスタム プロパティに計算値によるアニメーションや transition を使うためには,プロパティのデータ型を定義する必要があります.
@propertysyntax<integer> を指定することで,そのプロパティを計算値によるアニメーション可能にしています.


このおもちゃは Opera 83 以上用に作っていますが,Google Chrome 107 で動くことが確認できています.Google Chrome の他のバージョンでの動作は未確認です.


戻る