【Sassで解説】Cssミニゲームで開始画面と終了画面を(もちろんcssだけで)実装する


2021/06/01
蛸壺の技術ブログ|Cssミニゲームで開始画面と終了画面を(もちろんcssだけで)実装する

以前の回で、Cssミニゲームの中身をどう実装しているのか基礎的なCss&Htmlを使ったテクニックの話題を取り上げてみました。

これだけだとミニゲームとしては物足りないので、今回は補足的にミニゲームにオープニング画面とエンド画面の追加方法を考えてみましょう。


TL;DR

手っ取り早く今回のキモとなるテクニックは以下の項目でまとめます。

            
            1. form要素とinput要素のreset型
2. z-index属性で要素の画面の上げ下げ
3. displayやvisibility属性で要素の表示/非表示の切り替え
        
Cssゲームの作り方は独特で、プログラミングというよりはhtml要素とCssプロパティの使い方や組み合わせ方を知っていることが重要です。


もっとも簡単な開始/終了画面の実装

まずはミニゲームを描画しているhtml要素をクリックするとゲームが始まって、一定時間で終了させるだけのCssミニゲームを作成します。

先に完成品をお見せすると、下のようなプログラムになります。

開始画面から3秒で終了画面に遷移するだけですが、これだけでもゲームっぽく感じられます。

このプログラムの作り方ですが、まずは以下の
index.htmlをベースとしましょう。

            
            <html>
    <head>
        <link rel="stylesheet" type="text/css" href="style.css">
    </head>
    <body>
        <form id="game-wrapper">
            <input id="reset" type="reset">
            <input id="start" type="checkbox">
            <label class="stage stage--op" for="start">クリックしてゲーム開始!</label>
            <div class="stage stage--main">
                <p>このゲームは3秒で終わります。</p>
            </div>
            <label class="stage stage--end" for="reset">リセットしてニューゲーム</label>
        </form>
    </body>
</html>
        
先のこのindex.htmlの構成を説明します。

今回のゲームはform要素で包まれているのがミソです。

というのはform要素内にある
<input type="reset">要素がクリックされた場合、そのform内部の状態は初期化される仕組みを利用して、ゲームオーバーしたらゲームを再開できるようにしています。

            
            ...
<form id="game-wrapper">
    <input id="reset" type="reset">
    <!-- ゲームの中身 -->
    <label class="stage stage--end" for="reset">リセットしてニューゲーム</label>
</form>
...
        
さらにいうと、前回説明していたCss擬似クリックのテクニックを応用して、ボタン形状のinput要素を秘匿し、for属性でリンクを与えたlabel要素でクリックイベントを受けるというやり方も行っています。

また、ゲーム開始をトリガーしているパートは以下の部分です。

            
            ...
<input id="start" type="checkbox">
<label class="stage stage--op" for="start">クリックしてゲーム開始!</label>
<div class="stage stage--main">
    <p>このゲームは3秒で終わります。</p>
</div>
<label class="stage stage--end" for="reset">リセットしてニューゲーム</label>
...
        
ここでもやはりCss擬似クリックのテクニックを利用します。

この一番上ある
<input type="checkbox">で、ゲームを開始するしないの状態をtrue/falseで持ちます。

このチェックボックスがクリックされてチェック状態(true)になった場合、そのinput要素より下の同階層にある要素にスタイルを適用できるようにセレクタ
~を使うことで、ゲーム画面の遷移状態を操作しています。

Css的に表現すると、ゲーム開始後に各要素に適用するスタイルは以下のように書くことができます。

            
            ...
#start:checked ~ .stage--op { /* stage--opクラスのスタイル */ }
#start:checked ~ .stage--main { /* stage--mainクラスのスタイル */ }
#start:checked ~ .stage--end {/* stage--endクラスのスタイル */}
...
        
ではゲームの本体とも言えるCssスタイルを作成するのですが、例によってScssからコンパイルしたものを利用します。

Sassコンパイル前のstyle.scssは以下です。

            
            #game-wrapper {
    width: 100%;
    height: 300px;
    background-color: darkgray;
    box-sizing: border-box;
    position: relative;
    input { display: none; }
    .stage {
        position: absolute;
        display: block;
        width: 100%;
        height: 100%;
        font-size: 24px;
        &--op {
            display: flex;
            align-items: center;
            justify-content: center;
            //👇解説ポイント①
            z-index: 0;
        }
        &--main {
            display: none;
            font-size: 18px;
            //👇解説ポイント①
            z-index: -1;
            p {
                color: blue;
                margin: 0;
                padding: 0;
            }
        }
        &--end {
            display: none;
            background: darkgray;
            color: white;
            //👇解説ポイント①
            z-index: -2;
        }
    }
    #start:checked ~ {
        .stage--op {
            display: none;
        }
        .stage--main {
            display: block;
            //👇解説ポイント①
            z-index: 1;
        }
        .stage--end {
            display: flex;
            align-items: center;
            justify-content: center;
            flex-direction: column;
            //👇解説ポイント②
            animation: ending 3s forwards;
        }
    }
    @keyframes ending {
        0% { visibility: hidden; }
        99% { visibility: hidden; }
        100% {
            visibility: visible;
            //👇解説ポイント①
            z-index: 1;
        }
    }
}
        
上のコメントで、解説ポイント①にまず着目してみましょう。

なお、scssでの
.stage { ... &--op { ...の部分は、コンパイル後に.stage .stage--op { ...でcss展開されることに注意してください。

このミニゲームでは開始画面/ゲーム画面/終了画面の3つの画面が階層として重ねて配置されています。各階層深さは
z-indexで定義できます。デフォルトでは階層深さは0です。

z-indexの仕組みはこの数が大きい要素が前面に配置され、小さい要素が背面に回されます。マイナスも指定できるので、z-indexさえ把握しておけば、1層分だけ背面に回すなどの操作も可能です。

このプログラムのゲーム開始前後の各ステージのz-indexの動きを以下にまとめます。

ゲーム開始前

ゲーム開始後

ゲーム終了時

.stage--op

0

0(非表示)

0(非表示)

.stage--main

-1(非表示)

1

1

.stage--end

-2(非表示)

-2(秘匿)

1

というように、ゲームの状態遷移に対応した画面が一番手前に来るようにz-indexを指定できることが分かります。

なお、z-indexが同じ数の場合には、htmlで記述した順番でより下にくる要素が上に描画されます。

ここでいうと、mainステージよりendステージが上層で描画されるので、明示にz-indexを2にする必要は必ずしもありません。

もう1点だけ重要なポイントとして、
解説ポイント②に挙げたanimation属性のforwardsプロパティです。

これはアニメーションが終わった状態で、そのときのスタイルを保持させる役目があります。このforwardsが指定されていないアニメーションは、勝手に最初のアニメーション前の状態に戻りますので、cssミニゲームでは重要なテクニックになります。


まとめ

今回はCssだけで行うHTMLミニゲームの画面遷移に関して、もっとも簡単なプログラムを使って解説を行いました。

肝心のゲームの中身の話はそのうち別の機会で紹介させていただくとして、Javascript無しのCssでも十分画面遷移が可能であることを理解していただけましたら幸いです。

参考サイト

HTMLとCSSだけでミニゲームをつくる

記事を書いた人

記事の担当:taconocat

ナンデモ系エンジニア

主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。