👾

拝啓、CSSでドット絵を描きたくなったあの日(数週間前)の自分へ

2023/12/15に公開

3-shake Advent Calendar 2023の15日目のエントリー記事です。
※ 12/21追記: CSS Advent Calendar 2023の21日目のエントリー記事として追加しました。投稿期間とズレてしまっていますが、CSSアドベントカレンダー盛り上がりの一助になればと思います。

今年は数年離れていたデータエンジニアを再スタートし、データ基盤構築やGoogleCloudのProfessional試験を受けて合格したり…とテッキーな事に触れることが多い年でした。
最近はDBやSRE領域に触れる機会もあり、自分の知識不足に凹みながらも「今は学ぶ時期だ」と1つずつ知識を拾い集めています。

そんな盛りだくさんだった2023年も大詰め、アドベントカレンダーを書く機会をいただきました。
語ることは勿論データエンジニア……ではなくCSSについての記事です!
テーマは「自由」だと偉い人が言っていたので!!

対象読者

  • HTML・CSSについて軽くでも触ったことのある方
  • Sass(SCSS)についてどのようなものか知っている・触ったことがある方
  • とりあえずドット絵でCSS書いてみたくなった人

はじめに

皆さん、アニメは好きでしょうか?
私は好きです。
今期も面白いアニメが沢山放送され、日々の合間に見ております。
フリーレン、シャンフロ、薬屋…これを読んでいる方もきっと「今期の推しアニメ」というものがあるでしょう。

私の今期の推しアニメは…「16bitセンセーション ANOTHER LAYER」です!!
https://16bitsensation-al.com/

美少女ゲームが大好きなイラストレーターの女の子、秋里コノハが(色々あって)2023年から1992年にタイムスリップし、当時の人々と美少女ゲームを作る…というストーリーです。
SFとギャグを交えたストーリーの他、更には1992当時のゲーム開発の技術やPC機種(PC-98)についても語られており、美少女ゲームを知っている人はもちろんのこと、そうでない人も楽しめる内容となっています。
(ED曲は作詞と作曲にKOTOKOさんと中沢伴行さんが関わっているというこだわりを感じます…)

さて、今でこそ美少女ゲームやノベルゲームに出てくるゲーム内イラスト(スチルとも言います)は、イラストソフトやペンタブ等を使って描かれていますが、1992年当時はマウスを使いドット絵の要領で1つずつポチポチしながら描かれていました。
(更には当時のPC性能の都合上、色は16色しか使えなかったという)

そんなアニメを見ていてふと思いました。

「ドット絵楽しそうだな」と

更にこう思いました。

「CSSでドット絵って描けるのだろうか?」

この業界に足を入れては出たりして早数年。フロントエンドは全くわかりませんが、CSSで絵を書く事に楽しさを感じ、趣味として触れてきました。
というわけで、CSSでドット絵を描けるか調べてやってみました!

作ったもの

今回作ったものがこちらです。
「ポケットモンスター・ソード/シールド」に登場する「ユキハミ」というポケモンのドット絵です。
(とても可愛い)
今回の内容通り、「CSSのみ」で書いています。

ソースコードは以下に掲載しています。
https://codesandbox.io/embed/s27gsq?view=Editor+%2B+Preview&module=%2Flib%2Fstyles.css

CSSでドット絵を描く方法

調べてみるといくつか方法があるようですが、今回はbox-shadowを利用した方法を使って描いていきます。

box-shadowとは

box-shadow とはCSSのプロパティの一つで、要素の周りに影(シャドウ効果)を追加することができます。
https://developer.mozilla.org/ja/docs/Web/CSS/box-shadow

使用例で言えば、Googleの検索欄の下

BigQuery画面にあるボタンの下

あまり意識することは少ないですが、様々なサイト、検索エンジンで使われています。

box-shadowの基本

box-shadowは要素に対して影をつけることができます。
先に構文例を記載します。
(書き方が色々あるため、一例です)

/*  影と影色を指定  */
box-shadow: offset-x offset-y color
/*  影色と影のぼかしを指定  */
box-shadow: offset-x offset-y blur-radius color
/*  影色と影のぼかし、影の大きさを指定  */
box-shadow: offset-x offset-y blur-radius spread-radius color
/*  影色と、影を要素の内部に指定  */
box-shadow: inset offset-x offset-y color

次に設定値です。
offset-x:影の位置を指定します。要素の左上を原点とし、水平方向(X軸)に動かします。
offset-y: 影の位置を指定します。要素の左上を原点とし、垂直方向(Y軸)に動かします。
color: 影の色を指定します。指定しない場合、影の色は黒になります。
blur-radius: 影のぼかし(ブラー効果)の半径を指定します。
spread-raduis: ぼかしの大きさを指定します。
inset:影を要素の内部に描写し、凹んだように見せます。

例えば以下のようなCSSを実行します。

.shadow {  
  /*  要素(四角形)の設定  */
  width: 100px;
  height: 100px;
  border: solid;
  /*  影の設定  */
  box-shadow: 10px 5px;
}

すると、四角形の背後に黒い影がでてきます。
これがbox-shadowの基本的な動きになります。

また、これらの設定はカンマ区切りで複数指定する事ができ、これによって影を複数生成することができます。

.shadow {  
  /*  要素(四角形)の設定  */
  width: 100px;
  height: 100px;
  border: solid;
  /*  影の設定  */
  box-shadow: 
    10px 5px,
    0px 150px red,
    150px 150px blue,
    150px 0px green;
}

なお、box-shadowで生成される影の大きさは要素と同じです。

box-shadowでドット絵を描く

「box-shadowはわかった。でもここからどうやってドット絵を描くの?」

ここからが本題です。
と言っても、あまり難しい事はないと思います。
ポイントとなるのは以下2つです。

  • 影のサイズは要素と同じ
  • 影はカンマ区切りで複数指定できる

ドット絵は、かなりざっくり言ってしまえば「点(正方形)を並べて」描きます。
box-shadowも、要素の形を正方形にすれば複数作成することによって同じような要領で描くことができます。

一例としてりんごのドット絵を作ってみます

box-shadowを利用して色と位置を指定し、1ドットずつ影を配置しています。

.container{
   /* ドット絵のサイズ */
  width: 100px;
  height: 100px;
}
.appple {  
  /*  要素(四角形)の設定  */
  width: 20px;
  height: 20px;
  border: none;
  background: white;
  /*  ドット絵の設定  */
  box-shadow: 
  /*    1行目   */
    20px 0px white,
    40px 0px maroon,
    60px 0px white,
    80px 0px white,
  /*   2行目   */  
    0px 20px red,
    20px 20px red,
    40px 20px maroon,
    60px 20px red,
    80px 20px red,
 /*   3行目   */  
    0px 40px red,
    20px 40px red,
    40px 40px red,
    60px 40px red,
    80px 40px red,
 /*   4行目   */     
    0px 60px red,
    20px 60px red,
    40px 60px red,
    60px 60px red,
    80px 60px red,
  /*   5行目   */       
    0px 80px yellow,
    20px 80px yellow,
    40px 80px yellow,
    60px 80px yellow,
    80px 80px yellow
    ;
}

このように、少しコツがいるかも知れませんがCSSでドット絵を描くことができました。
あとは好きなイラストのドット絵を用意すれば、いつでも描くことができますね!
…配置とかを考えるとすごく頭が痛くなるかもしれませんが。

Sassを使ってもっと簡単に

CSSでドット絵を描ける事はわかりましたが、大きな難関が…。

修正しにくい

先程のコードを見ていただいて分かるように、1ドットずつ位置と色を指定して配置しているため、可読性がとても低いです。
なので「ここ直したいな」と思っても探すのが難しい。
作ってから時間が経ったものを修正することになった時なんて……😇
(そもそもドット絵をCSSで描いてサイトで公開するなんて人、そうそういない説が)

そこで、ドット絵コードをメンテナンスしやすくする方法について調べてみました。
身近にCSSでお絵かきをする心の友はいませんが、ここは広いインターネット。
CSSをこよなく愛する人々が色々な方法を駆使してドット絵イラストをメンテナンスしやすいように工夫する方法を研究し、公開していました。

その中でもSassを利用する方法が良かったのでやってみます。

まずは作ったものを。
ドット絵は「ユキハミ」ですが、SCSSで書かれています
https://codesandbox.io/embed/dhqhn9?view=Editor+%2B+Preview&module=%2Fsrc%2Fsass%2Fmain.scss

// ユーザー定義関数(pixelize)を作成
// 引数は、ドット絵のマトリクス($matrix)、ドット絵のサイズ($size)、ドット絵に利用する色($colors)
@mixin pixelize($matrix, $size, $colors) {
	$ret: "";

	// マトリクス(後述の$yukihami)の配列数を取得
	@for $i from 1 through length($matrix) {
		//マトリクスの特定の位置取得する。(ドット絵の「行」)
		$row: nth($matrix, $i);

		@for $j from 1 through length($row) {
			$dot: nth($row, $j);

			@if $dot != 0 {
				@if $ret != "" {
					$ret: $ret + ",";
				}

				$color: nth($colors, $dot);
				$ret: $ret + ($j * $size) + " " + ($i * $size) + " " + $color;
			}
		}
	}
	// 位置、色の情報をbox-shadowの記述にあわせて格納
	box-shadow: unquote($ret + ";");
}

$yukihami-colors: (
	// 1~3で色を指定
	// 0は透明
	#333,
	rgb(203, 221, 251),
	rgb(171, 171, 171),
	rgb(82, 115, 244)
);
$yukihami: (
	(0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0),
	(0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0),
	(0, 0, 0, 1, 1, 3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0),
	(0, 0, 1, 0, 2, 2, 2, 2, 0, 4, 4, 2, 2, 3, 1, 0, 0),
	(0, 0, 1, 2, 2, 0, 0, 0, 2, 0, 0, 4, 2, 0, 0, 1, 0),
	(0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 4, 0, 2, 2, 1, 0),
	(0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 4, 2, 1),
	(0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 3, 0, 0, 2, 2, 2, 1),
	(0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 4, 1, 0),
	(1, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 2, 0, 2, 2, 1, 0),
	(1, 0, 2, 2, 0, 0, 0, 1, 2, 1, 0, 0, 2, 1, 1, 0, 0),
	(0, 1, 2, 1, 0, 0, 0, 2, 1, 4, 2, 0, 1, 0, 0, 0, 0),
	(0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0),
	(0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
	(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
);

.pixel {
	width: 20px;
	height: 20px;
	@include pixelize($yukihami, 20px, $yukihami-colors);
}

内容としては

  1. box-shadowの記述に合わせてコードを生成するユーザー定義関数(mixmin)を作成。
  2. 1にドット絵の作成に必要ならマトリクス、サイズ、色配列を渡す
    という物となっています。

マトリクスをハイライトすると、ざっくりとユキハミのシルエットが見えます…見えますね?
(遠目からチラ見すると見えるはずです)

これで、直感的にドット絵を作成・メンテナンスすることができるようになりました!

人によっては「わざわざここまでするのか…」なんて思うかもしれません。
ですが、ここまでするからこそ救われる命というものがあるのです。
現に、今ここで干からびかけていた一人の命が救われました。
このように関数を作って作りやすくしようとしたCSSを愛する方々は本当にすごいなと思いました…ありがたいです。

最後に

仕事にほとんど関連がないCSSの話をしてしまい、「本当にこれでよかったのか…」と書きながら思っています。
しかしながら、CSSはただホームページに彩りを与えるだけのツールではなく、絵を描いたりアニメーションを作って楽しんだり…と、様々な楽しみ方をすることができます。
(私も趣味の範囲でですが、CSSを楽しんでいる1ユーザーです)

今はCSSを手軽に書いたり遊んだりできるサービスも多くあるので、少しでも気になれば触ってみるのをおすすめします。

最後になりますが…

「16bitセンセーション ANOTHER LAYER」 をよろしくお願いいたします!!

それでは、ありがとうございました。

ちなみに

CSSでドット絵を書くためのツールを作成している方もおります。
https://pixelartcss.com/
https://make8bitart.com/

「CSS書いたことないけどドット絵描いてみたいな」という人も手軽に触れる環境となっているので、もし興味があれば是非。

参考

https://kuroeveryday.blogspot.com/2018/10/draw-and-animate-pixel-art-using-only-css.html
https://note.com/nati_kamiyama/n/n36ecb669319f

Discussion