めろたんのアレ

色々書いてくやつ

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

うぇい。

昨日はお酒を飲んで陽気な気分になっていました。

今は厳しい顔をしながら書いています。

今回は Misoca Advent Calendar 2017 の16日目の記事です。

前の記事は

tech.misoca.jp

これでした!

merotan ( @renyamizuno_ ) が、HTML と CSS を使って究極の何かを作ったことについて語ってくれるそうです。

究極の何かを作ったので書きます。

HTMLとCSSだけでどこまで出来るか

HTMLとCSSはすごい表現力を秘めているのは皆さんご存知かと思いますが、 その御蔭でわけわからんと思うことも多々ありますね!

で「HTMLとCSSチューリング完全」という話もあったりする。

hoo89.hatenablog.com

まぁあくまでネタくらいに受け取ったほうが良いと思う。たぶん。

とりあえずそれくらい表現力があるわけなのです。

それで色々すごいものがありまして、例えばこれ。

Pure CSS3 calculator

HTMLとCSSだけで書かれている電卓なんですって。

やばくない?

電卓出来るならなんでも出来るでしょ。

と思ってミニゲームを作りました。

HTMLとCSSミニゲーム

これです!

github.com

マークアップは適当&CSSとかclassの命名規則とかも適当!動けば良いんだよ!()

f:id:renyamizuno:20171216190331p:plain

以下のリンクからであそべます。 スタート後、赤い丸をクリックするゲームです。 左上にポイント、右上に制限時間があります。

https://renyamizuno.github.io/minigame-only-css/

あとiframeでも

スマホだと見れないかも。 HTML見てもらえばわかりますが、JSは一ミリもありません。

どうやってうごいているか

いろいろ説明しないといけないところがあるので、個別に書いていこうと思う。

タイマー

ゲーム画面右上のタイマーについてはどうなっているかというと、

  <div class="mini-game-timer">
    <div class="mini-game-timer__a">
      <div>
        <div>0</div>
        <div>1</div>
        <div>2</div>
      </div>
    </div><div class="mini-game-timer__b">
      <div>
        <div>0</div>
        <div>1</div>
        <div>2</div>
        <div>3</div>
        <div>4</div>
        <div>5</div>
        <div>6</div>
        <div>7</div>
        <div>8</div>
        <div>9</div>
      </div>
    </div>
  </div>

HTMLはこうなっている。 タイマーで使う数値を全部書いている。

実際にどう数値を動かしているかというと

.mini-game-timer__a, .mini-game-timer__b {
  display: inline-block;
  position:relative;
  overflow: hidden;
  height: 24px;
  width: 12px;
}

.mini-game-timer__a > div, .mini-game-timer__b > div {
  position: absolute;
}

.mini-game-timer__a > div {
  animation: timer-mekuri-a 30s steps(3) 1;
}

.mini-game-timer__b > div {
  animation: timer-mekuri-b 10s steps(10) 3;
}

@keyframes timer-mekuri-a {
  0% { top: -48px; }
  100% { top: 24px; }
}

@keyframes timer-mekuri-b {
  0% { top: -216px; }
  100% { top: 24px; }
}

こんな感じ。 animationをつかって動かしている。

stepsを使うことで、ステップで動かすことが出来るので、それでうまくやっている。

スコア

これの説明が大変。

まず的からかな。

<input id="target1" class="point" type="checkbox">

<!-- .... -->

<label class="target" for="target1"></label>

的の構成はこんな感じ。 labelが実際の的になっている。

.target {
  position: absolute;
  top: calc(50% - 13px);
  left: calc(50% - 13px);
  width: 26px;
  height: 26px;
  background: red;
  border-radius: 50%;
}

.target[for="target1"] {
  animation: target1-animation 5s infinite;
}

#target1:checked ~ .stage--main > [for="target1"] {
  background: blue;
  pointer-events: none;
}

@keyframes target1-animation {
  0% { transform: translate(100px, 50px) }
  50% { transform: translate(50px, 100px) }
  100% { transform: translate(100px, 50px) }
}

それでCSSはこんな感じになっている。

的を画面中心において、animationtransformをつかい位置をずらしている。

またlabelなので、labelforで指定したidの要素と関連付けが出来ることを活かして、checkboxにチェックが付くようになっている。

それでクリックされた(forcheckboxにチェックが入った)labelに関しては、pointer-eventsを使ってクリックが出来ないようにしている。

チェックが入っているかはcheckedという擬似クラスを使うことで確認できる。

それでスコアの表示の部分については、HTMLは大したことないので割愛。

body {
  counter-reset: score;
}

.point:checked {
  counter-increment: score 10;
}

.mini-game-score::before {
  content: "Score: " counter(score);
}

CSSはこのようになっている。

counterを使ってスコアを計算している。

画面の遷移

最初の「スタート」をクリックしてから「おわり」までの画面の遷移についてどうなっているかというと

<input id="game-start" type="checkbox">
<input id="reset" type="reset">
<label class="stage stage--op" for="game-start">スタート</label>
<div class="stage stage--main">....</div>
<label class="stage stage--end" for="reset">...</label>
.stage {
  position: absolute;
  display: block;
  width: 100vw;
  height: 100vh;
}

.stage--op {
  display: flex;
  align-items: center;
  justify-content: center;
}

.stage--main {
  display: none;
}

.stage--end {
  display: none;
  opacity: 0.3;
  background: black;
  color: white;
  font-size: 36px;
}

#game-start:checked ~ .stage--op {
  display: none;
}

#game-start:checked ~ .stage--main {
  display: block;
}

#game-start:checked ~ .stage--end {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  animation: game-end-animation 30s;
}

@keyframes game-end-animation {
  0% { visibility: hidden; z-index: -1; }
  99% { visibility: hidden; z-index: -1; }
  100% { visibility: visible; z-index: 100; }
}

こうなっている。

「スタート」は的と同様の作りになっています。

チェックが入ったらゲームの画面が表示されます。

同時に「おわり」の画面も"表示"されますが、visibilityを使い非表示にしています。 またz-indexを下げてゲームの画面に覆いかぶさらないようにします。

30秒立つと「おわり」の画面をanimationで、z-indexが100になり、またvisibilityで表示するようにします。

「おわり」の画面は的と同様にlabelとなっていて、for<input type="reset">に向けます。

resetは、クリックするとform要素内のinputを初期値に戻します。 これによりスコアはリセットされ、また「スタート」の画面が表示されるようになっています!

まとめ

つかれた。。。

なんだかcounterとかpointer-eventsとかさ、お前らまじでCSSなの?大丈夫?みたいなこと思ったりしたけど、まぁ便利なのでいいんじゃないかな〜(ハナホジ

とりあえず、これぐらいのことは結構普通にできるんやで〜CSSすごいな〜って思いました。

みんなもHTMLとCSSだけで面白いものつくっていこうな!!!!

あしたはえんださんが「日報bot」について書いてくれるそうです!楽しみだな!