2019.09.17

マルバツゲームを作ってみよう~その2:画面コーディング編


「マルバツゲームを作ってみよう」の連載第二回目です。

今回は「画面コーディング編」ということで、前回設計したゲーム画面を、HTMLとCSSでコーディングしていきます!

前回の図

全体

始めにHTMLとCSSで全体の構造とスタイルを書きます。
色々なやり方があるとは思いますが、私はこんな感じで書いてみました。

See the Pen tic-tac-toe1 by Shinichi Kurita (@kuri-ta) on CodePen.

HTML

HTMLは全体をwrapperで囲い、さらにゲーム部分をgame-containerで囲っています。
game-container内は、メッセージ欄を囲うmessage-container、マス目を囲うsquares-container、リセットボタンを囲うbtn-containerで構成されています。

メッセージはulリストで作っています。
メッセージの○×記号は色を変えたいのでspanタグで囲っています。
マス目はflexboxを使う予定です。

CSS

CodePenではコメントアウトしてしまっていますが、最初にリセットCSSを読み込んでいます。

リセットCSSとは、ブラウザによってWebページに適用されるスタイル(例えば、わざわざCSSを書かなくても、h1タグで囲まれた文字は勝手に大きくなってくれますよね)を打ち消すCSSです。
ブラウザ間の表示の差異をなくすために、まずリセットCSSでスタイルをリセットします。

今回は、数あるリセットCSSの中でもメジャーな(かつCodePen でも採用されている)、Eric Meyer’s “Reset CSS” 2.0 を使用しています。

app.cssの方ではh1のスタイルを整えたり、文字やゲーム画面が中央に配置されるように、ラッパーとコンテナのmarginpaddingを指定したりしています。

app.css
* {
  box-sizing: border-box;
}

あとはユニバーサルセレクタ(これ→*。すべての要素を対象にするセレクタ)でbox-sizing: border-box;を指定しています。
こうすると、borderpaddingをボックスサイズ(widhtheight)の中に含めて算出してくれるようになるので、スタイリングがしやすくなります!

マス目

現時点ではマス目がなくて寂しいので(笑)、まずマス目を表示させましょう。

以下のようにCSSを記述します。

app.css
.squares-box {
  width: 202px;
  height: 202px;
  display: flex;
  flex-wrap: wrap;
  border: solid 2px #333;
}

.square {
  position: relative;
  width: calc(198px / 3);
  height: calc(198px / 3);
  border: solid 2px #333;
}

おそらくgridを使うのが一番スマートだと思うのですが、IE11対応がつらいことになりそうだったので、今回はflexboxで実装しました。

あと実はtableも使おうとしたのですが、すべてのブラウザに対応するのが難しかった(マス目のなかに○×記号を表示させたところ、Firefox で表示が崩れてしまいました…)のと、そもそもtable(=表)本来の用途として微妙じゃないかということで、tableは使わないことにしました。

マス目の幅と高さは、全体からボーダーの太さを引いて3で割った値を設定しています。

マス全体の幅は202px にしました。これならスマホでも見れますね!

※ちなみになぜ202px なのかというと、CodePen をChrome で表示したときに、3で割り切れない値だと隙間ができてしまうという現象が発生したためです…。CodePen を使わず、ブラウザで直接HTMLファイルを表示させる分には、200px でも大丈夫です。

あとはsquares-containerクラスで位置を整えれば、マス目は完成です。

See the Pen tic-tac-toe1-2 by Shinichi Kurita (@kuri-ta) on CodePen.

やっぱりマス目があると一気にそれっぽくなりますね!

メッセージ欄

続いてメッセージ欄です。
デザインでは「○」や「×」に色がついていたので、それを再現します。
ついでにフォントのサイズやマージンも整えてしまいましょう。

app.css
.message-container {
  margin-bottom: 20px;
  font-size: 1.25rem;
  font-weight: bold;
}

.batsu {
  color: #00b0f0;
}

.maru {
  color: #ff0000;
}

.js-hidden {
  display: none;
}

js-hiddenクラスを付与することで、いらないメッセージを隠します。

index.html
<li class="js-hidden">
  <span class="maru"></span>のばん
</li>

js-hiddenをつけ替えることでメッセージの切り替えを行いますが、これは次回実装します。
とりあえず「×のばん」以外のメッセージを隠しておきましょう。

See the Pen tic-tac-toe1-3-2 by Shinichi Kurita (@kuri-ta) on CodePen.

これでメッセージ欄は完成です!

リセットボタン

続いてリセットボタンを作ります。

app.css
.btn {
  display: inline-block;
  width: 150px;
  padding: 10px 20px;
  border-radius: 5px;
  cursor: pointer;
}

.btn-reset {
  color: #fff;
  background-color: #ffc000;
  font-weight: bold;
}

今回、ボタンは一つだけなので、クラスを分けなくてもよかったのですが、一応分けてみました。
上のようにボタンに共通のスタイルをbtnクラスとして切り出しておくと、違う種類のボタンを作るときにスタイルを流用できるので便利です。

上でbox-sizing: border-box;を指定したので、paddingも含めて全体で幅150px に収まっているかと思います。

あとは:hoverを使って、マウスオーバー時に色が薄くなるようにします。

app.css
.btn-reset:hover {
  background-color: #ffd347;
  transition-duration: 0.4s;
}

See the Pen tic-tac-toe1-4 by Shinichi Kurita (@kuri-ta) on CodePen.

これでリセットボタンも完成しました。
リセットボタンは、ゲーム開始時には隠しておき、ゲーム終了時にJavaScriptで表示させますが、これも次回実装します。

○と×

最後に、マス目の中に表示させる「○」と「×」を作ります。
画像を用意しても良かったのですが、せっかくなのでCSSで作りたいと思います!

マルバツの記号は疑似要素を使って作成します。
JavaScript で疑似要素表示用のクラス(js-maru-checked)をマス目に付与すると、そのマス目に疑似要素(js-maru-checked::before)が表示される、という実装を考えています。

今回は表示を確認するだけなので、HTMLに直接、疑似要素表示用のクラスを書き込みます。

index.html
<div class="square js-maru-checked"></div>
<div class="square js-batsu-checked"></div>

また疑似要素の表示位置をposition: absolute;で指定するために、squareのスタイルにposition: relative;を加えます。

app.css
.square {
  position: relative;
  width: calc(198px / 3);
  height: calc(198px / 3);
  border: solid 2px #333;
}

○(マル)

まずは「○」から作ってみましょう。

app.css
.js-maru-checked::before {
  content: '';
  width: 50px;
  height: 50px;
  border: solid 8px #ff0000;
  border-radius: 50%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

border-radius: 50%;にすると円になります。
top: 50%;left: 50%;transform: translate(-50%, -50%);で、親要素の上下左右中央に配置されます。

これでマルは完成!…かと思いきや、マルが大きく表示されています。

大きくなったマル

box-sizing: border-box;が効いていない感じがしますね。
50px × 50px のボックスの外にボーダーが出ちゃっています。

調べてみたところ、どうやらbox-sizing: border-box;はそのままでは疑似要素には適用されないらしい…。
特定のCSSプロパティが :after や :before などの擬似要素に適用されない場合の解決策

box-sizingは親から疑似要素に継承されないようなので、疑似要素にも明示的にbox-sizingを指定します。

app.css
*, *::before, *::after {
  box-sizing: border-box;
}

これで期待通りの表示になりました!

正しい表示のマル

×(バツ)

続いてバツの方です。バツは疑似要素を2つ使って作成します。

app.css
.js-batsu-checked::before {
  content: '';
  width: 50px;
  height: 8px;
  margin: auto;
  background-color: #00b0f0;
  border-radius: 4px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) rotate(45deg);
}

.js-batsu-checked::after {
  content: '';
  width: 8px;
  height: 50px;
  margin: auto;
  background-color: #00b0f0;
  border-radius: 4px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) rotate(45deg);
}

斜めの棒を::before::afterで出して、それらを重ね合わせることで、「×」印を再現しています。

○と×の完成です!

See the Pen tic-tac-toe2-7 by Shinichi Kurita (@kuri-ta) on CodePen.


これで今回のゲームで使用するすべてのパーツが出そろいました🥳

次回からはいよいよJavaScript の実装に入ります。今回作成した画面に動きをつけていきます。

次回もお楽しみに!

連載記事


<!-- Font Awesome Free 5.0.13 by @fontawesome - https://fontawesome.com --><!-- License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) -->