私は最近、CSS のみのゲームを作成する楽しさを発見しました。 HTML と CSS がオンライン ゲーム全体のロジックをどのように処理できるかは常に興味深いので、試してみる必要がありました! このようなゲームは通常、HTML 入力のチェック/非チェック状態を :checked
CSS の疑似クラス。 その組み合わせひとつで、たくさんの魔法がかけられます!
実際、私は Checkbox を使わずにゲーム全体を構築することに挑戦しました。 それが可能かどうかはわかりませんでしたが、間違いなく可能です。その方法をお見せします。
この記事で学習するパズルゲームに加えて、 純粋な CSS ゲームのコレクション、それらのほとんどはチェックボックスハックなしです。 (こちらもご用意しております CodePenで.)
始める前に遊びませんか?
私は個人的にフルスクリーンモードでゲームをプレイすることを好みますが、以下または以下でプレイできます。 こっち開けて.
かっこいいでしょ? これは今まで見た中で最高のパズル ゲームではありませんが、CSS と数行の HTML のみを使用するものとしては悪くありません。 グリッドのサイズを簡単に調整したり、セルの数を変更して難易度を制御したり、好きな画像を使用したりできます。
そのデモを一緒に作り直して、最後にちょっとした輝きを加えてキックします。
ドラッグ アンド ドロップ機能
パズルの構造は CSS Grid を使用すると非常に単純ですが、パズルのピースをドラッグ アンド ドロップする機能は少し複雑です。 トランジション、ホバー効果、兄弟セレクターの組み合わせに頼らなければなりませんでした。
そのデモで空のボックスにカーソルを合わせると、画像がその中を移動し、カーソルをボックスの外に移動してもそこにとどまります。 秘訣は、トランジションの継続時間と遅延を大きくすることです。これは、イメージが最初の位置に戻るのに時間がかかるほど大きいためです。
img {
transform: translate(200%);
transition: 999s 999s; /* very slow move on mouseout */
}
.box:hover img {
transform: translate(0);
transition: 0s; /* instant move on hover */
}
のみを指定する transition-delay
で十分ですが、遅延と持続時間の両方に大きな値を使用すると、プレーヤーが画像が後ろに移動するのを見る可能性が低くなります。 待てば 999s + 999s
— これは約 30 分です — その後、画像が移動します。 しかし、あなたはしませんよね? つまり、ゲームから離れない限り、誰もターン間にそれほど長くはかからないということです。 したがって、これは XNUMX つの状態を切り替えるための優れたトリックだと思います。
画像にカーソルを合わせると、変更もトリガーされることに気付きましたか? これは、画像が box 要素の一部であるためです。 追加することでこれを修正できます pointer-events: none
画像に追加しますが、後でドラッグすることはできません。
つまり、内部に別の要素を導入する必要があります .box
:
その余分 div
(私たちはのクラスを使用しています .a
) 画像と同じ領域を取ります (CSS Grid と grid-area: 1 / 1
) であり、ホバー効果をトリガーする要素になります。 そこで兄弟セレクターの出番です。
.a {
grid-area: 1 / 1;
}
img {
grid-area: 1 / 1;
transform: translate(200%);
transition: 999s 999s;
}
.a:hover + img {
transform: translate(0);
transition: 0s;
}
ホバリング .a
要素は画像を移動します。画像はボックス内のすべてのスペースを占めているため、代わりにボックスの上にカーソルを置いているように見えます! 画像をホバリングすることはもはや問題ではありません!
画像をボックス内にドラッグ アンド ドロップして、結果を見てみましょう。
あれ見た? 最初に画像をつかんでボックスに移動しますが、特別なことは何もありません。 ただし、画像を離すと、画像を移動するホバー効果がトリガーされ、ドラッグ アンド ドロップ機能がシミュレートされます。 ボックスの外でマウスを離しても、何も起こりません。
うーん、あなたのシミュレーションは完璧ではありません。ボックスをホバーしても同じ効果が得られるからです。
確かに、これを修正します。 ホバー効果を無効にして、ボックス内の画像を離した場合にのみ許可する必要があります。 私たちは私たちの次元で遊ぶでしょう .a
それを実現する要素。
これで、ボックスをホバリングしても何も起こりません。 しかし、画像をドラッグし始めると、 .a
要素が表示され、ボックス内で離すと、ホバー効果をトリガーして画像を移動できます。
コードを分析しましょう。
.a {
width: 0%;
transition: 0s .2s; /* add a small delay to make sure we catch the hover effect */
}
.box:active .a { /* on :active increase the width */
width: 100%;
transition: 0s; /* instant change */
}
img {
transform: translate(200%);
transition: 999s 999s;
}
.a:hover + img {
transform: translate(0);
transition: 0s;
}
画像をクリックすると起動します :active
を作る疑似クラス .a
要素の全角(最初は 0
)。 アクティブな状態が維持されます アクティブ 画像を公開するまで。 ボックス内の画像をリリースすると、 .a
要素はに戻ります width: 0
、しかし、ホバー効果が発生する前にトリガーし、画像がボックス内に収まるようにします! ボックスの外に放しても何も起こりません。
少し癖があります。空のボックスをクリックすると、画像も移動し、機能が壊れます。 現在、 :active
にリンクされています .box
要素なので、それまたはその子のいずれかをクリックするとアクティブになります。 これを行うことで、最終的に .a
要素とホバー効果のトリガー。
で遊ぶことでそれを修正できます pointer-events
. とのやり取りを無効にすることができます。 .box
子要素との相互作用を維持しながら。
.box {
pointer-events: none;
}
.box * {
pointer-events: initial;
}
Now ドラッグ アンド ドロップ機能は完璧です。 ハッキングする方法が見つからない限り、画像を移動する唯一の方法は、画像をボックス内にドラッグ アンド ドロップすることです。
パズル グリッドの構築
パズルを組み立てるのは、ドラッグ アンド ドロップ機能で行った作業に比べて簡単に感じられるでしょう。 パズルを作成するには、CSS グリッドと背景のトリックに依存します。
便宜上、Pug で記述されたグリッドを次に示します。
- let n = 4; /* number of columns/rows */
- let image = "https://picsum.photos/id/1015/800/800";
g(style=`--i:url(${image})`)
- for(let i = 0; i < n*n; i++)
z
a
b(draggable="true")
コードは奇妙に見えるかもしれませんが、プレーンな HTML にコンパイルされます。
<g style="--i: url(https://picsum.photos/id/1015/800/800)">
<z>
<a></a>
<b draggable="true"></b>
</z>
<z>
<a></a>
<b draggable="true"></b>
</z>
<z>
<a></a>
<b draggable="true"></b>
</z>
<!-- etc. -->
</g>
これらのタグがどうなっているのか疑問に思っているに違いありません。 これらの要素には特別な意味はありません。 <z>
たくさんのより <div class="z">
または何でも。
これは私がそれらをマッピングした方法です:
<g>
を含むグリッドコンテナですN*N
<z>
要素。<z>
グリッド項目を表します。 の役割を果たします。.box
前のセクションで見た要素。<a>
ホバー効果をトリガーします。<b>
私たちのイメージの一部を表しています。 私たちは適用しますdraggable
デフォルトではドラッグできないためです。
よし、グリッドコンテナを登録しましょう <g>
. これは、CSS ではなく Sass にあります。
$n : 4; /* number of columns/rows */
g {
--s: 300px; /* size of the puzzle */
display: grid;
max-width: var(--s);
border: 1px solid;
margin: auto;
grid-template-columns: repeat($n, 1fr);
}
実際にグリッドの子を作成します — <z>
要素 — グリッドもあり、両方を持っています <a>
& <b>
同じグリッド領域内:
z {
aspect-ratio: 1;
display: grid;
outline: 1px dashed;
}
a {
grid-area: 1/1;
}
b {
grid-area: 1/1;
}
ご覧のとおり、特別なことは何もありません — 特定のサイズのグリッドを作成しました。 必要な CSS の残りの部分はドラッグ アンド ドロップ機能用であり、ボードの周りにピースをランダムに配置する必要があります。 これについては Sass を使用します。これもまた、関数を使用してすべてのパズルのピースをループしてスタイルを設定できるようにするためです。
b {
background: var(--i) 0/var(--s) var(--s);
}
@for $i from 1 to ($n * $n + 1) {
$r: (random(180));
$x: (($i - 1)%$n);
$y: floor(($i - 0.001) / $n);
z:nth-of-type(#{$i}) b{
background-position: ($x / ($n - 1)) * 100% ($y / ($n - 1)) * 100%;
transform:
translate((($n - 1) / 2 - $x) * 100%, (($n - 1)/2 - $y) * 100%)
rotate($r * 1deg)
translate((random(100)*1% + ($n - 1) * 100%))
rotate((random(20) - 10 - $r) * 1deg)
}
}
私が Sass を使用していることに気付いたかもしれません。 random()
関数。 これが、パズルのピースのランダム化された位置を取得する方法です。 私たちがすることを覚えておいてください disable にカーソルを合わせたときのその位置 <a>
対応するドラッグ アンド ドロップ後の要素 <b>
グリッド セル内の要素。
z a:hover ~ b {
transform: translate(0);
transition: 0s;
}
同じループで、パズルの各ピースの背景構成も定義しています。 それらはすべて背景と同じ画像を論理的に共有し、そのサイズはグリッド全体のサイズと同じにする必要があります ( --s
変数)。 同じものを使用して background-image
そしていくつかの数学、私たちは更新します background-position
画像の一部だけを表示します。
それでおしまい! 私たちのCSSのみのパズルゲームは技術的に完成しています!
しかし、私たちはいつでもより良くすることができますよね? 私はあなたに見せた パズルピースの形のグリッドを作る方法 別の記事で。 同じ考え方をここで適用してみましょう。
パズルピースの形
これが私たちの新しいパズルゲームです。 機能はそのままに、よりリアルな形状に!
これは、グリッド上の形状の図です。
よく見ると、パズルのピースの形が XNUMX 種類あることがわかります。 四隅 XNUMXつのエッジ, 他のすべてのための XNUMX つ.
私が参照した別の記事で作成したパズルのピースのグリッドは、もう少し単純です。
CSS マスクとグラデーションを組み合わせてさまざまな形状を作成する同じ手法を使用できます。 よくわからない場合は mask
とグラデーション、チェックすることを強くお勧めします その単純化されたケース 次のパートに進む前に、テクニックをよりよく理解するために。
まず、特定のセレクターを使用して、同じ形状を共有する要素の各グループをターゲットにする必要があります。 XNUMX つのグループがあるため、XNUMX つのセレクターと、それらすべてを選択するデフォルトのセレクターを使用します。
z /* 0 */
z:first-child /* 1 */
z:nth-child(-n + 4):not(:first-child) /* 2 */
z:nth-child(5) /* 3 */
z:nth-child(5n + 1):not(:first-child):not(:nth-last-child(5)) /* 4 */
z:nth-last-child(5) /* 5 */
z:nth-child(5n):not(:nth-child(5)):not(:last-child) /* 6 */
z:last-child /* 7 */
z:nth-last-child(-n + 4):not(:last-child) /* 8 */
これがグリッドにどのようにマッピングされるかを示す図は次のとおりです。
それでは、形状に取り組みましょう。 図形はすべて同じテクニックを使用しているため、XNUMX つまたは XNUMX つの図形だけを学ぶことに集中しましょう。
グリッドの中央にあるパズルのピースは、 0
:
mask:
radial-gradient(var(--r) at calc(50% - var(--r) / 2) 0, #0000 98%, #000) var(--r)
0 / 100% var(--r) no-repeat,
radial-gradient(var(--r) at calc(100% - var(--r)) calc(50% - var(--r) / 2), #0000 98%, #000)
var(--r) 50% / 100% calc(100% - 2 * var(--r)) no-repeat,
radial-gradient(var(--r) at var(--r) calc(50% - var(--r) / 2), #000 98%, #0000),
radial-gradient(var(--r) at calc(50% + var(--r) / 2) calc(100% - var(--r)), #000 98%, #0000);
コードは複雑に見えるかもしれませんが、一度に XNUMX つのグラデーションに注目して、何が起こっているのかを見てみましょう。
XNUMX つのグラデーションは XNUMX つの円を作成し (デモでは緑と紫でマークされています)、他の XNUMX つのグラデーションは他のピースが接続するスロットを作成します (青でマークされたものは形状の大部分を塗りつぶし、赤でマークされたものは上部を塗りつぶします)。 CSS 変数、 --r
、円形の半径を設定します。
中央のパズルのピースの形(マーク 0
図の ) は、XNUMX つのグラデーションを使用し、XNUMX つの曲率を持っているため、最も作成が困難です。 他のすべての作品は、より少ないグラデーションをジャグリングします。
たとえば、パズルの上端にあるパズルのピース ( 2
図では) XNUMX つではなく XNUMX つのグラデーションを使用します。
mask:
radial-gradient(var(--r) at calc(100% - var(--r)) calc(50% + var(--r) / 2), #0000 98%, #000) var(--r) calc(-1 * var(--r)) no-repeat,
radial-gradient(var(--r) at var(--r) calc(50% - var(--r) / 2), #000 98%, #0000),
radial-gradient(var(--r) at calc(50% + var(--r) / 2) calc(100% - var(--r)), #000 98%, #0000);
最初 (上部) のグラデーションを削除し、残りのスペースをカバーするように XNUMX 番目のグラデーションの値を調整しました。 XNUMX つの例を比較しても、コードに大きな違いはありません。 同じ形状を作成するために、さまざまな背景構成を見つけることができることに注意してください。 グラデーションをいじり始めると、きっと私がやったものとは違うものを思いつくでしょう。 もっと簡潔なものを書くこともできます — もしそうなら、コメントで共有してください!
形状を作成することに加えて、以下のように要素の幅や高さを増やしていることもわかります。
height: calc(100% + var(--r));
width: calc(100% + var(--r));
パズルのピースは、接続するためにグリッド セルをオーバーフローさせる必要があります。
最終デモ
これが完全なデモです。 最初のバージョンと比較すると、グリッドとドラッグ アンド ドロップ機能を作成するための同じコード構造と、形状を作成するためのコードが表示されます。
可能な機能強化
記事はここで終わりますが、さらに多くの機能を追加してパズルを強化し続けることができます! タイマーはどうですか? それとも、プレイヤーがパズルを完成させたときのお祝いの言葉でしょうか?
これらすべての機能を将来のバージョンで検討する可能性があるので、 私の GitHub リポジトリに注目してください.
包み込む
CSSはプログラミング言語ではない、 彼らが言うには。 ハ!
それによって #HotDrama を刺激しようとしているわけではありません。 これは、非常にトリッキーなロジックを実行し、その過程で多くの CSS プロパティとテクニックをカバーしたためです。 CSS グリッド、トランジション、マスキング、グラデーション、セレクター、背景のプロパティを試しました。 コードを簡単に調整できるようにするために使用したいくつかの Sass トリックは言うまでもありません。
目標はゲームを構築することではなく、CSS を調査し、他のプロジェクトで使用できる新しいプロパティとトリックを発見することでした。 CSS でオンライン ゲームを作成することは、CSS の機能を詳細に調査し、それらの使用方法を習得することを強いる課題です。 さらに、すべてが完了したときに何かで遊ぶことができるのは、とても楽しいことです。
CSS がプログラミング言語であろうとなかろうと、私たちが常に革新的なものを構築し、作成することによって学ぶという事実は変わりません。