CSSだけでTemple Runをつくる

どうも。 おっぱいの人 です。未だホンモノにはありつけていません。

このエントリは、CSS Programming Advent Calendar 2012 23日目の記事です。

CSS Programmingとは

もう今更説明しようがないですよね。

おっぱいとかそんなんより数倍変態な人たちがHTMLとCSSだけでゲームとか作っちゃうアレです。

ぼくはというと、今年一年中ガラケーと戦ってた(業務的に)のでCSS3とか実はほとんど触れていないので、リハビリの意味を込めての復帰戦となります。

CSSだけでTemple Runをつくる

Temple Runってコレです。iPhoneのランニングゲー(?)では一番有名ですよね。

手前味噌で恐縮ですが、Tap Ninja なんというのもKAYACさんからでております。ああ、すみません宣伝では無いんです。石を投げないでください。ごめんなさいごめんなさい。

というのも、今回はちょっと実装量的にもTap Ninjaに近くしたので紹介しました。

さて残された時間も少ないので早速のお披露目です!

CSS Temple Run

動作コードはjsdo.itに貼り付けました。

Chromeでのみ動作を確認しています。

相変わらず、少し大きく作り過ぎちゃったので(jsrun.it)http://jsrun.it/damele0n/jofZのほうで画面を広げて見てください。

キャラクターあたりをクリックして迫り来るサンタクロースの恐怖を回避しよう!!!!

…すごい…!これは、すごい!なんだろう、このすごいコレジャナイ感!!!!!!

Temple Runのいいところを120%握りつぶすようなシュール感と、絶対にやっちゃいけないゲームの琴線に触れたつまらなさが本作品のウリです。

大反省会

難しいですね。CSS Programming。いや、舐めてたわけじゃ無いんですよ?いやホントに。ああ、石を投げないでください。犬を放たないでください。ああ。

今回、sassのsassたる形式を使ったのでjsdo.it上でコンパイルできなくなってしまい、仕方なくjsdo.it上にはコンパイル後のファイルをベタで置いてあります。

動作品の完全なコードは、githubに上げておきました。

画面上の仕組みとしては、

  • 背景
  • 人物やサンタ、隠れたradioなど

で構成してあります。

背景レイヤーについては大まかに

    // 背景のコンテナ
    .bg {
        // 遠近感を設定
        -webkit-perspective: 500;
    }
    // .road, .wall_l, .wall_rの3面で壁を構成
    .road {
        top: 35px;
        width: 300px;
        height: 500px;
        -webkit-transform: rotateX(-290deg);
    }
    .wall_l {
        top: 23px;
        left: -360px;
        width: 770px;
        height: 200px;
        -webkit-transform: rotateY(98.6deg);
    }
    .wall_r {
        top: 23px;
        left: -110px;
        width: 770px;
        height: 200px;
        -webkit-transform: rotateY(-98.6deg);
    }

として、座標系とrotateをいじりながら3面を3Dで配置しました。DevTool見ながら手で。(強引)

逆に操作系というか、パーツ系というか、アニメーションなどが絡むものは全て2Dで扱っています。

// input[type=radio]へ適用。radio自体は隠しちゃう。
.state {
    -webkit-appearance: none;

    &[value=left]:checked {
        // checkedとは逆の要素にむけたlabelのz-indexを上げる
        & ~ .ctrl_r {
            z-index: 10;
        }
        // background-positionで裸のオッサンの位置を変える
        & ~ .action {
            background-position: left top;
        }
    }
    &[value=right]:checked {
        & ~ .ctrl_l {
            z-index: 10;
        }
        & ~ .action {
            background-position: right top;
        }
    }
}


// 衝突やオッサンの方向などは.actionがすべてもつようにする
.action {
    position: absolute;
    z-index: 9;
    top: 410px;
    left: -50px;
    width: 400px;
    height: 150px;
    background-image: url(https://dl.dropbox.com/s/ef9pdfctl2fr12n/charactor.gif);
    background-repeat: no-repeat;
    background-position: left top;
    opacity: 1;
}


// 凶悪なサンタクロースたちのスタイル
// アニメで動かすので、とりあえず適当に配置してopacity:0に
.stage a[class*=block_] {
    position: absolute;
    z-index: 11;
    top: 105px;
    left: 55px;
    width: 190px;
    height: 70px;
    -webkit-appearance: none;
    background-image: url(https://dl.dropbox.com/s/63z2uiqq1060bbu/santa.png);
    background-repeat: no-repeat;
    background-size: auto 100%;
    opacity: 0;
}

// 衝突情報とdelay秒数のブレのセット
// 0 なら left, 1 なら right で衝突
$blocks:
        0 0,
        1 0,
        0 -1,
        ...;
// ディレイのベース秒数
$delay_base: 2.5;

// 衝突情報を元にスタイルを書き出す
@for $i from 1 through length($blocks) {
    $block: nth($blocks, $i);
    $angle: nth($block, 1);
    $delay: ($delay_base * $i) + nth($block, 2);
    $class_name: block_#{$i};

    // 左で衝突の場合、#state_lのcheckboxがcheckedであったらcrashのアニメを.action上で再生
    @if $angle == 0 {
        #state_l:checked ~ .#{$class_name}:hover ~ .action {
            @extend %crash;
        }
    }
    // 右で衝突の場合、#state_rのcheckboxがcheckedであったらcrashのアニメを.action上で再生
    @else {
        #state_r:checked ~ .#{$class_name}:hover ~ .action {
            @extend %crash;
        }
    }

    // $iの番号を元に.base_Nの要素に対して、ディレイと方向を設定する
    .#{$class_name} {
        -webkit-animation-delay: #{$delay}s;
        @if $angle == 0 {
            background-position: left top;
        }
        @else {
            background-position: right top;
        }
    }
}

このあたりは、たぶんCSS Programmerなら誰でもやってる(?) "~"セレクタ連結で状態を管理しながら画面表示を行う手法です。

また、SCSS内各所にてcreate-animeというmixinを呼んでいますが、実は3ヶ月ほど温めておいたものです。

その名の通り、animationを簡単に作れるようにしたものなんですが、ポイントとして設定されたFPSでアニメーションを書き出せるという点です。

// 従来のkeyframes
// 秒数あたりのパーセントで動いてちょっとキモイ。っていうか分かりづらい。(個人的な感想です)
@keyframes hogehoge {
    0% {
        //prop: value;
    }
    ...
    100% {
        //prop: value;
    }
}


// create-animeだと
@include create-anime(
    // アニメーション名を設定。placeholder形式ではき出す(%hogehoge)ので使用するセレクタ内で@extend %hogehogeする
    'hogehoge',
    // その他animationの設定値を設定
    (
        timing-function linear,
        iteration-count infinite
    ),
    // ここでいう1はFPSあたりの1フレームのこと。FPS: 24ならフレーム内秒(1/24)*1
    1, (
        background-position (center top)
    ),
    // 100フレームの意。FPS: 24ならフレーム内秒(1/24)*100
    // animation-durationは最後の設定フレームから自動的に設定される
    100, (
        background-position (center bottom)
    )
);

// 書き出されるとこんな感じ
.hogehoge {
  animation-name: kf-hogehoge;
  animation-duration: 4.16667s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
@keyframes kf-hogehoge {
  1% {
    background-position: center top;
  }
  100% {
    background-position: center bottom;
  }
}

ね、フレーム単位でカウントした方が分かりやすいし、他のアニメーションとも連携しやすそうじゃないですか?

突貫で作ったのでまだまだ機能不足ですが、create-animeはこれだけ別にしてまたの機会に。

おわり

今23:54です。何とか間に合いました。

クリスマスの予定など皆無かつ、当然のように明日も仕事ですが、みなさんは素敵なクリスマスをお過ごしくださいね。

ぼくは駅前で踏み絵でもしようと思います。

さて、明日は No_1026 さんの「ギャンブる」なお話です。お楽しみに!