【Sassで作るCssミニゲーム】Cssミニゲームで使えるアニメーションテクニックの基礎


2021/07/30
蛸壺の技術ブログ|Cssミニゲームで使えるアニメーションテクニックの基礎

CSSで動きのあるミニゲームを作るとなるとどうしても避けて通れないのがCSSアニメーションです。

純粋なCSSでアニメーション効果をつけるには
transitionanimationを使うことになります。

この記事では、この二つのプロパティの使い方を比較し、長所短所を説明してから、効果的に利用する方法を検討していきます。


transitionを使ったCSSゲームの基礎

transitionプロパティを使えば簡単にCSSアニメーションが作れます。

まずは以下のようにインプットチェックボックスを使ったメンタコが動くだけのCSSアニメーションを操作してみてください。

実装の中身

先程のCSSプログラムの中身を見ていきます。

まずは核になるhtml要素のコードは以下です。

            
            <div id="wrapper">
    <input type="checkbox" id="move1">
    <input type="checkbox" id="move2">
    <input type="checkbox" id="move3">
    <label for="move0">0</label>
    <label for="move1">1</label>
    <label for="move2">2</label>
    <label for="move3">3</label>
    <div class="mentaco"></div>
</div>
        
ここでもやはりCSSゲームの基礎テクである通称input-label法を利用しています。

このhtmlに割り当てているのはCssパートは以下です。

            
            #wrapper {
    width: 100%;
    height: 300px;
    background-color: darkgray;
    box-sizing: border-box;
    position:relative;
    .mentaco{
        position: absolute;
        display: block;
        padding: 0 0 0 0;
        transition: all 1s cubic-bezier(.68,-0.55,.27,1.55);
        top: 20px;
        left: 0;
        width: 80px;
        height: 72px;
        background: transparent url('https://raw.githubusercontent.com/tacoskingdom/commonBlogMaterial/main/deep-tacopots/blog116/mentaco.png') no-repeat 0 0;
        background-size: 80px 72px;
    }
    #move1:checked ~ .mentaco {
        transform: translateX(120px) translateY(120px);
    }
    #move2:checked ~ .mentaco {
        transform: translateX(90px) translateY(-20px);
    }
    #move3:checked ~ .mentaco {
        transform: translateX(200px) translateY(10px);
    }
    input {
        position: absolute;
        top: 0px;
        &#move1 { left: 0px; }
        &#move2 { left: 20px; }
        &#move3 { left: 40px; }
    }
    label {
        background-color: aqua;
        width: 20px;
        height: 20px;
        display: block;
        border-radius: 50%;
        position: absolute;
        z-index: 1;
        text-align: center;
        &[for="move0"] { top: 46px; left: 28px; }
        &[for="move1"] { top: 165px; left: 148px; }
        &[for="move2"] { top: 22px; left: 118px; }
        &[for="move3"] { top: 55px; left: 228px; }
    }
}
        

プログラムの解説

transitionを使ったアニメーションはデモを動かしてもらった通り、現在のアニメーションターゲット要素の位置と行き先の2点間を直線的に移動するように作用します。

またチェックボックスをオンオフしても分かるように、
往復移動も含めてアニメーションにすることができます。

transitionを使ったアニメーションはこの特性ゆえに、CSSプログラムを実装する分には理解しやすいというメリットがあります。

つまりチェックボックスのオンオフフラグを上手く利用することで、メンタコのスプライトを好きな2点間で自由に移動させることができます。

例えば
2 --> 01 --> 3のルートで動かしたい場合のチェックボックスの組み合わせを探すのも簡単です(条件はご自分の手で探してみてください)。


animationを使ったCSSゲームの基礎

animationプロパティを使った場合、transitionよりも高度なアニメーションが作成できます。

座標値や通過時間などを細かく定義することで、複雑な曲線軌道をうごくアニメーションも定義することができますが、その分より複雑なCSSコードを実装しなければならないのが難点です。

先ほどのtransitionで使ったデモプログラムを少々改造してanimationを使ったプログラムに仕立て直したものが以下です。

デモプログラムの中身

こちらも先ほどのように中身を見ていきましょう。

html部は以下のようになります。

            
            <form id="wrapper">
    <input type="reset" value="リセット">
    <input type="checkbox" id="move1">
    <input type="checkbox" id="move2">
    <input type="checkbox" id="move3">
    <label for="move0">0</label>
    <label for="move1">1</label>
    <label for="move2">2</label>
    <label for="move3">3</label>
    <div class="mentaco"></div>
</form>
        
今回のポイントはform要素を親要素にして中に入れた他の要素をリセットできるようにしているところです。

以下がcssスタイルコードになります。

            
            #wrapper {
    width: 100%;
    height: 300px;
    background-color: darkgray;
    box-sizing: border-box;
    position:relative;
    .mentaco{
        position: absolute;
        display: block;
        padding: 0 0 0 0;
        top: 20px;
        left: 0;
        width: 80px;
        height: 72px;
        background: transparent url('https://raw.githubusercontent.com/tacoskingdom/commonBlogMaterial/main/deep-tacopots/blog116/mentaco.png') no-repeat 0 0;
        background-size: 80px 72px;
    }
    #move1:checked ~ .mentaco {
        animation: xy1 2s cubic-bezier(.45,.05,.55,.95) forwards;
        @keyframes xy1 {
            0% {
                top: 20px;
                left: 0px;
            }
            30% {
                top: 50px + 20px;
                left: 280px;
            }
            70% {
                top: 170px + 20px;
                left: 200px;
            }
            100% {
                top: 120px + 20px;
                left: 120px;
            }
        }
    }
    #move2:checked ~ .mentaco {
        animation: xy2 2s cubic-bezier(.45,.05,.55,.95) forwards;
        @keyframes xy2 {
            0% {
                top: 120px + 20px;
                left: 120px;
            }
            30% {
                top: -40px + 20px;
                left: 150px;
            }
            70% {
                top: 50px + 20px;
                left: 10px;
            }
            100% {
                top: -20px + 20px;
                left: 90px;
            }
        }
    }
    #move3:checked ~ .mentaco {
        animation: xy3 2s cubic-bezier(.45,.05,.55,.95) forwards;
        @keyframes xy3 {
            0% {
                top: -20px + 20px;
                left: 90px;
            }
            30% {
                top: 140px + 20px;
                left: 70px;
            }
            70% {
                top: 110px + 20px;
                left: 190px;
            }
            100% {
                top: 10px + 20px;
                left: 200px;
            }
        }
    }
    input {
        position: absolute;
        top: 0px;
        &#move1 { left: 0px; }
        &#move2 { left: 20px; }
        &#move3 { left: 40px; }
        &[type="reset"] {
            left: 80px;
        }
    }
    label {
        background-color: aqua;
        width: 20px;
        height: 20px;
        display: block;
        border-radius: 50%;
        position: absolute;
        z-index: 1;
        text-align: center;
        &[for="move0"] { top: 46px; left: 28px; }
        &[for="move1"] { top: 165px; left: 148px; }
        &[for="move2"] { top: 22px; left: 118px; }
        &[for="move3"] { top: 55px; left: 228px; }
    }
}
        

ブログラムの解説

先ほどのanimationのでもプログラムを触ってもらうと実感して分かると思いますが、transitionアニメーションとは異なり、このアニメーションは一方通行になります。つまり、最初に定義した開始フレームから最後の終了フレームまで進むとそこで打ち止めになり、再び最初の状態から再生されるしかなくなります。

このことがtransitionアニメーションとの大きな違いであり、アニメーションの巻き戻しは自動ではおこなえません。デモプログラムでいうと、チェックボックスを順番にクリックして、
0 > 1 > 2 > 3 > [リセット]というように流す以外は正しくアニメーションされないのはこのためです。

よってもし逆再生のアニメーションが必要になる場合には、新しくアニメーションフレームを定義して、html要素に割り当てる必要あります。

このanimationプロパティの
再生不可逆の性質もあって、もしCSSゲームのなかでアニメーションを利用したい場合には、transitionでは「往復」の動きがセットになって定義出来たのに対して、animationでは「行き」と「返り」のアニメーションを2つ用意しないといけません。

そこだけ考えてもanimationの実装の労力は2倍であり、更に
@keyframesを個別に用意しなけれならないため更に実装のコストは跳ね上がります。

とはいえ、animationの持つ高度なアニメーションの表現力は魅力的であり、華やかな演出をWebサイトにもたらすことで、他のサイトにはない印象で集客性に差をつけることも可能かもしれません。


結論

以上、アニメーションをCSSゲームに取り入れる時に念頭においておきたいポイントとしては、

            
            + CSSアニメーションには、transitionかanimationを使う
+ transitionを使うと2点間を往復するアニメーションが作れる
+ animationを使うと始状態から終状態まで@keyframesで定義したアニメーションが作れる
+ transitionはアニメーションの逆再生(巻き戻し)ができるが、
    animationは巻き戻しできない
+ transitionは2点間を直線的にしか移動できないが、
    animationはフレームごとに定義した座標を通過するパスで
    細かくアニメーション軌道上を移動できる
+ CSSアニメーションを作る際には、transitionとanimationを混ぜない方がベター
        
基本は作りたいアニメーションがtransitionで出来そうならばtransitionで作成し、どうしてもtransitionで実現しそうにない動きになるならばanimationを使うように実装していくと良いでしょう。


参考サイト

【初めてアニメーションを作る方必見!!】Transitionと@Keyframe animationの違いとは?

記事を書いた人

記事の担当:taconocat

ナンデモ系エンジニア

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