クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

CSSだけでドット絵マリオを描けるのか

ブラウザ3Dの大海原を漕ぎ始めた私ですが、世代的にはドット絵ゲーム世代です。*1そんな私が前から試してみたかったのは、お絵描きソフトを使わずに、CSSだけでドット絵が描ける?。
多分描けると思う。描けるんじゃないかな。まちょと覚悟はしておけ。な、実録です。では、どうぞ。

※目次:

※参考:【CSSの基本】書ける前に読む!HTML、CSS、JSの書式-3 - クモのようにコツコツと

ドット絵とは

ドット絵すなわち、ドットの絵。ドットとは点。点描みたいなものだが、デジタルでは正確に並んだ正方形に色を打っていく。
ファミコン世代には懐かしいスーパーマリオブラザーズのマリオはこんなお姿です。16マス四方で描かれています。

※参考:[ドット絵]ファミコンのマリオを描く | albatrus.com

この記事ではフォトショップを使っているようですが、私はこのマリオをCSSだけで描こう(書こう?)と思います!

ちなみに色の数も制限があり最大で52色だった模様。GIFが256色なのでその1/5ほどですね!

※参考:ファミコンの画面について

ついでに言うと音にも制限があり音色は4種で同時発声数は4音。いわゆるチップチューン、8bit音楽と言われるピコピコサウンド。

※参考:8bit(ファミコン風)ゲーム音楽の作り方。仕様を理解し正しく打ち込みましょう。 – 直伝・サウンドクリエイターへの道

何をドットにするか。

それでは始めましょう。まずはHTML上に16マス16マスのドットを置きたい。マスを横に並べるのでインライン要素がいいな。spanかな。

<div class="dot_e">
    <!--1行目-->
    <span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
</div>

ふぃ〜、これで1行〜。なんだか、長いな…。キリがないな。
要素の中には何も入れず、要素自体に色をつけるわけだから、「空要素」でもいいよね、これ。HTMLの基本でも書いたように、空要素は単独で成立し、閉じタグが不要。調べてみると空要素って結構種類がある。

※参考:Empty element (空要素) - 用語集 | MDN

なんでもいいんだけど、馴染深いのはimgbrhrとかかな。brは改行でhrは区切り線。意味的にちょっと違う。imgは画像リンクだけど、一番意味的にはしっくりくるかも。うむ、これでいこう。

<div class="dot_e">
     <!--1行目-->
     <img><img><img><img><img><img><img><img><img><img><img><img><img><img><img>
</div>

さっきよりコンパクトになった。これで、行の変わり目にbrを入れればいいのかな。

<div class="dot_e">
     <img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><br>
     <img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><br>
     <img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><br>
     <img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><br>
     <img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><br>
     <img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><br>
     <img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><br>
     <img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><br>
     <img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><br>
     <img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><br>
     <img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><br>
     <img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><br>
     <img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><br>
     <img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><br>
     <img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><br>
     <img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><br>
</div>

できた。

CSSを当ててマスが出現

CSSも当てよう。

.dot_e {
  font-size: 0;
  margin: 0;
  padding: 0;
}

img {
  background: #eee;
  width: 20px;
  height: 20px; 
}

外を囲っている. dot_eはフォントサイズも余白も全部0にして、密着させる。
中のimgはわかりやすいように背景色#eeeを付けて、サイズも20pxにしてみた。

おお、マス目が登場した!ここから色を付けていくんだが、何番目系のCSSで指定していくイメージだ。

※参考:何番目系の便利なCSSまとめ

まてよ?16×16マスってことは256番目まであるってことだよな。このままでは何番目がめちゃめちゃ指定しにくいぞ。

2行目の何番目、とか行で区切れた方が良さそう。行で区切るにはHTMLをliリストにするのがわかりやすいな。

<ul class="dot_e">
  <li><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img></li>
  <li><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img></li>
  <li><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img></li>
  <li><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img></li>
  <li><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img></li>
  <li><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img></li>
  <li><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img></li>
  <li><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img></li>
  <li><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img></li>
  <li><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img></li>
  <li><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img></li>
  <li><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img></li>
  <li><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img></li>
  <li><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img></li>
  <li><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img></li>
  <li><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img><img></li>
</ul>

これで何行目かがわかりやすくなった。

さて、CSSの当て方は2つ考えられる。

  • この色は何行目の何番目、何行目の何番目、何行目の何番目、何行目の何番目、何行目の何番目…と色でまとめる
  • 1行目の1番目は何色、2番目は何色、3番目は何色、4番目は何色、5番目は何色、6番目は何色…と行でまとめる

両方試してみたところ、すぐに1番目は見通しが悪いことがわかった。
たとえ色を何度も書くとしても行ごとにまとめた方が見通しがいい。下記は、1行目に順番に背景色を#eeeから透明noneに上書きしています。

/*1行目*/
.dot_e li:nth-child(1) img:nth-child(1) {background: none;}
.dot_e li:nth-child(1) img:nth-child(2) {background: none;}
.dot_e li:nth-child(1) img:nth-child(3) {background: none;}
.dot_e li:nth-child(1) img:nth-child(4) {background: none;}
.dot_e li:nth-child(1) img:nth-child(5) {background: none;}
.dot_e li:nth-child(1) img:nth-child(6) {background: none;}
.dot_e li:nth-child(1) img:nth-child(7) {background: none;}
.dot_e li:nth-child(1) img:nth-child(8) {background: none;}
.dot_e li:nth-child(1) img:nth-child(9) {background: none;}
.dot_e li:nth-child(1) img:nth-child(10) {background: none;}
.dot_e li:nth-child(1) img:nth-child(11) {background: none;}
.dot_e li:nth-child(1) img:nth-child(12) {background: none;}
.dot_e li:nth-child(1) img:nth-child(13) {background: none;}
.dot_e li:nth-child(1) img:nth-child(14) {background: none;}
.dot_e li:nth-child(1) img:nth-child(15) {background: none;}
.dot_e li:nth-child(1) img:nth-child(16) {background: none;}

nth-child()を2つ並べてみたけど、ちゃんときくのねコレ。

16行目まで全部やったらこんな感じ。さあ、これで背景が透明になったので、いよいよドットを描いて(書いて?)いきましょう!

体の色を描く(書く)

ではさっそく始めます。スーパーマリオブラザーズのマリオは3色だけで作られています。

/*基本色 
うすだいだい: #facd89;
赤: #ff0000;
緑: #638c0b;
*/

シャツの色は意外と青じゃなくて緑なんですね!背景の水色と識別するためかな?
まずは一色目、肌色#facd89;から。1行目でいうと14、15、16番目です。

/*1行目*/
.dot_e li:nth-child(1) img:nth-child(1) {background: none;}
.dot_e li:nth-child(1) img:nth-child(2) {background: none;}
.dot_e li:nth-child(1) img:nth-child(3) {background: none;}
.dot_e li:nth-child(1) img:nth-child(4) {background: none;}
.dot_e li:nth-child(1) img:nth-child(5) {background: none;}
.dot_e li:nth-child(1) img:nth-child(6) {background: none;}
.dot_e li:nth-child(1) img:nth-child(7) {background: none;}
.dot_e li:nth-child(1) img:nth-child(8) {background: none;}
.dot_e li:nth-child(1) img:nth-child(9) {background: none;}
.dot_e li:nth-child(1) img:nth-child(10) {background: none;}
.dot_e li:nth-child(1) img:nth-child(11) {background: none;}
.dot_e li:nth-child(1) img:nth-child(12) {background: none;}
.dot_e li:nth-child(1) img:nth-child(13) {background: none;}
.dot_e li:nth-child(1) img:nth-child(14) {background: #facd89;}
.dot_e li:nth-child(1) img:nth-child(15) {background: #facd89;}
.dot_e li:nth-child(1) img:nth-child(16) {background: #facd89;}

全部塗るとこうなる。

わかりまっか?

オーバーオールも描く(書く)

どんどんいきましょう。次はオーバーオールの赤#ff0000;。1行目にはなくて2行目から登場。

/*2行目*/
.dot_e li:nth-child(2) img:nth-child(1) {background: none;}
.dot_e li:nth-child(2) img:nth-child(2) {background: none;}
.dot_e li:nth-child(2) img:nth-child(3) {background: none;}
.dot_e li:nth-child(2) img:nth-child(4) {background: none;}
.dot_e li:nth-child(2) img:nth-child(5) {background: none;}
.dot_e li:nth-child(2) img:nth-child(6) {background: none;}
.dot_e li:nth-child(2) img:nth-child(7) {background: #ff0000;}
.dot_e li:nth-child(2) img:nth-child(8) {background: #ff0000;}
.dot_e li:nth-child(2) img:nth-child(9) {background: #ff0000;}
.dot_e li:nth-child(2) img:nth-child(10) {background: #ff0000;}
.dot_e li:nth-child(2) img:nth-child(11) {background: #ff0000;}
.dot_e li:nth-child(2) img:nth-child(12) {background: none;}
.dot_e li:nth-child(2) img:nth-child(13) {background: none;}
.dot_e li:nth-child(2) img:nth-child(14) {background: #facd89;}
.dot_e li:nth-child(2) img:nth-child(15) {background: #facd89;}
.dot_e li:nth-child(2) img:nth-child(16) {background: #facd89;}

全部塗るとこうなる。

おお、見える!マリオの顔が見える! 一色目はこれどんな行だよ?って気分だったが、二色目は形が掴みやすくて気持ちが上がる。どんどん行こう!

顔パーツ、シャツ、靴は全部緑!

いよちよ最後の色です(早っ!)。顔の目とヒゲ、シャツ、靴は全部緑#638c0bでした。4行目から初めて登場。

/*4行目*/
.dot_e li:nth-child(4) img:nth-child(1) {background: none;}
.dot_e li:nth-child(4) img:nth-child(2) {background: none;}
.dot_e li:nth-child(4) img:nth-child(3) {background: none;}
.dot_e li:nth-child(4) img:nth-child(4) {background: none;}
.dot_e li:nth-child(4) img:nth-child(5) {background: none;}
.dot_e li:nth-child(4) img:nth-child(6) {background: #638c0b;}
.dot_e li:nth-child(4) img:nth-child(7) {background: #638c0b;}
.dot_e li:nth-child(4) img:nth-child(8) {background: #638c0b;}
.dot_e li:nth-child(4) img:nth-child(9) {background: #facd89;}
.dot_e li:nth-child(4) img:nth-child(10) {background: #facd89;}
.dot_e li:nth-child(4) img:nth-child(11) {background: #638c0b;}
.dot_e li:nth-child(4) img:nth-child(12) {background: #facd89;}
.dot_e li:nth-child(4) img:nth-child(13) {background: none;}
.dot_e li:nth-child(4) img:nth-child(14) {background: #638c0b;}
.dot_e li:nth-child(4) img:nth-child(15) {background: #638c0b;}
.dot_e li:nth-child(4) img:nth-child(16) {background: #638c0b;}

ついでに、body要素に空の色も加える。

body {
  background: #a0adfe;
}

できた!

See the Pen dot_e_04 by イイダリョウ (@i_ryo) on CodePen.

おお!マリオだ!マリオが飛んでいる!脳内に「トゥ〜ン」とジャンプ音が聞こえてくる。

今後の予定

というわけで、お絵かきソフトなしに、CSSだけでもドット絵は描ける(書ける)んです! 次回は、こいつをルイジにしてやろうか。色を変えられるのもCSSの利点ですね。
その次はJSのクリックイベントで立ちポーズ→ジャンプさせたい。
あと、tone.jsで「トゥ〜ン」って音も鳴らしたい!

それでは、引き続きよろしくお願いします。

追記:ドット絵変換ツールを作っていただけた!!

なんと、この記事を公開後に、面白いと感じてくださった方が画像をCSSに変換するツールを作ってくださいました!
しかも改良版は複数の画像がCSSアニメーションで徐々に変化する仕組みになっています。

housef.net

嬉しくてたまりません!同時に、フロントだけではなかなか実現できなそうなことを形にされるエンジニアの皆さんの偉大さにリスペクト!!

*1:ただし、最初ファミコンは買ってもらえず、勉強に使えるとかなんとか言って「MSX」というマイナーな機種を買ってもらいました。キーボードがついてますがもっぱらYとNしか押さないという…。