【Html&Cssアプリ】Html/JavascriptとSassを使った四択クイズゲームの作り方


2021/07/06
2022/01/18

Html&CssゲームプログラミングはScratchプログラミングを卒業し、本格的なWebプログラミングをスタートしたい中高生向けに最適な課題です。

このページでは簡単に作れる
Html&Cssゲーム「四択クイズ」の作成過程をまとめています。

本稿の内容に出てくるHtml&Cssゲームプログラミング難易度はさほど高くないとは思いますが、HTMLとCSSの使い方の基礎はそれなりにお勉強が必要になります。

Html&Cssのオススメの勉強法は
後述する節の内容で紹介しています。

ゲーム概要


【プログラムバージョン1.0】
更新日:2022/01/18
画面に出されるクイズを4つの選択肢の中から選んでみて遊んでください。


HTML&CSSプログラミングの学習法3選

改めまして『HTML&CSS』ウェブサイトのデザインを組み立てていくことに特化した言語です。

まずCSSは他のプログラミング言語と違い、数を計算したり、文字を処理したり、そういうことが出来るようにはそもそも作られていません。

逆に言うと、数学やコンピュータ分野の専門知識などを覚える必要が圧倒的に少ないことが特徴でもあります。

プログラミングの習得においては、まず
学習途中で「躓かないこと」や「持続できること」がもっとも重要なことです。

かなりの人が躓いた経験のあるはずのC言語は言わずもがな、最近では人気トップに躍り出たPython言語でも、学習途中で、あまり興味のない計算処理などのテーマになってくるほど長続きせずに挫折する人が結構いるでしょう。

一方、HTML&CSSプログラミングの入り口は広く誰にでも開かれています。

HTML&CSSなら、数学やコンピュータ界隈の細かい話は後回しにできますし、プログラミング結果がビジュアルにも即座に反映されますので、目に見えて達成感も得られてやりながら楽しく、学習のモチベーションも持続されやすいです。

ただし、
HTML&CSSプログラミングは簡単だ、とは言っていません。

勉強が進んでやりたいことが増えてくると、Javascriptなど他のテーマも勉強する必要性が出てくるようになるでしょう。

HTML&CSSは今やインターネットを支える重要な技術の一つであり、その関連技術の裾野は広大で、フロントエンド技術と呼ばれる奥の深いものになります。

個人の経験から言わせていただくと、プログラミング学習に問わず、成功する学習方法とは
「自分が夢中になれる学習方法に出会えるか・選択できるか」にかかっていると考えている次第です。

HTML&CSSプログラミングを本気でモノにしたい方は、下の内容を参考にして、自分にあった学習方法を一度じっくり考えてみてはいかがかと思います。

①書籍から学ぶ

本から学ぶので、モクモクと独習できる方に向いています。

本から一人で集中するのが苦手な方や学習時間を効率的に使いたい方にはあまり好ましくはないかも知れません。

スラスラわかるHTML&CSSのきほん

合同会社タコスキングダム|TacosKingdom,LLC.

ベストセラーのHTML&CSS本の改訂第二版です。

シンプルなWebサイト作りを通して、HTMLとCSSの基礎が学べる入門書になります。

第2版では、フレキシブル対応の内容をより拡充させて、PCだけなくスマホ・タブレットにも対応した内容になっています。

本のレベルも基本的で、非常にわかりやすく、平たい言葉ながらも説明も丁寧です。

プログラミングを初めてやってみる人で、自分のホームページを作りたいというモチベーションのある方にぴったりで、本の流れに沿って、コツコツと着実に演習していけば、かならずホームページが作成できます。

入門書としては完璧で、あとは読者の継続次第でHTML&CSSの基礎がマスターできるはずです。

これからWebサイトの制作を始める人や、HTML&CSSを学び直したい人にもおすすめの一冊です。

1冊ですべて身につくHTML & CSSとWebデザイン入門講座

合同会社タコスキングダム|TacosKingdom,LLC.

この書籍はCSSの入門書として分かりやすいと、好評を箔したWebサイトのデザイン本です。

自分のWebサイトを一からデザインしてみたい人のはじめの一歩に最適な本です。

テキストエディタやブラウザのダウンロード手順まで、分かりやすく書かれていて、Webサイトの仕組みや作り方、ページのデザインについて知りたいWebデザイン初心者にもやさしい内容になっています。

演習形式にもなっており、スマホ・パソコンからでも見れるレスポンシブなWebサイトのサンプルが作れます。

実際に作る過程までよくまとまっていて、読みながら初心者が挫折しないように良く考慮された本の構成になっているのも特徴です。

サンプルで制作するサイトのデザインも綺麗で、作りながら良いモチベーションにもなって、読者に学習を持続してもらいやすい読み口にもなっているので、購入しても損はない一冊です。

これだけで基本がしっかり身につく HTML/CSS&Webデザイン1冊目の本

合同会社タコスキングダム|TacosKingdom,LLC.

2021年に出版された内容の新しいHTML&CSS本です。

Webサイト制作をこれから学ぶ人のための「1冊目」に最適な入門書、と紹介されています。

初学者向けの内容でやさしい書き口で説明が進むため、これを読めば必要な基礎知識がひととおり身につくように工夫されています。

学習の進め方は、読者が手を動かしながら4つのWebサイトのサンプルを作り、HTML/CSSとWebデザインの基本を楽しく学べるようになっています。

作成するサイトで、初歩的な内容から少しずつレベルアップしていき、最終的には、Flexbox・グリッドレイアウトなどのレスポンシブデザインをじっくり理解できます。

知識ゼロからスタートした人でも、この一冊を読み終えるまでには、HTML&CSSのプログラミング能力にかなり自信がつくことでしょう。

初心者からちゃんとしたプロになる HTML+CSS標準入門

合同会社タコスキングダム|TacosKingdom,LLC.

2020年頃に出版された比較的新しいHTML&CSS本になります。

HTMLの技術は短い期間で変化の激しいため、より最新の内容で学習したい人に向いています。

本のタイトルが示すように、プロのWebデザイナーになるための基礎づくりに主眼が置かれています。

内容は演習形式で、「1日30分」形式のテーマを確実にこなしていくことで、継続的なステップアップが可能になっています。

最終的には、Webサイトをイチから作る程度の実力を目指し、HTML+CSSの基本から応用までを習得していく教科書的な内容で構成されています。

現在主流となっているレスポンシブなレイアウトや手法、制作技術・ツールに至るまで、現場でプロとして活躍するためにベースとなる知識もあわせて習得できます。

サンプルとしてタイプや難易度の異なる5つのWebページを作りながら、モバイルファーストな設計と、FlexboxやCSS Gridといった最新のスタイルも取り入れたになっています。

HTML&CSSの2冊目の本に購入してみるのも良いかも知れません。

②動画配信型教材で学ぶ〜Udemy

合同会社タコスキングダム|TacosKingdom,LLC.

Udemyの動画講座は充実したテーマで、自分のレベルにあった様々なトピックを購入して動画をみながら学習していくスタイルの教材です。

分からなかったところが何度も動画を見ながら、自分のペースで学習を進めたい方にオススメです。

③オンラインスクールで学ぶ〜テックアカデミー

合同会社タコスキングダム|TacosKingdom,LLC.

テックアカデミーは技術力とコミュニケーション力に長けた現役エンジニアのメンターとのマンツーマン授業により、質の高い授業を自宅で受けることができます。

じっくりと学習している暇の取れない日々忙しい方に便利なサービスです。


プログラム制作ログ

プログラムの作成の取り組みや機能の更新履歴を時系列でまとめたリリース履歴兼プログラム作成記事へのリンク集です。

不定期で更新しているため、プログラムのバージョンが進むほど高度なプログラムになっていますのでご注意ください...。

最新バージョンのソースコード

このプロジェクトで適用中のソースコードを以下に記載する。

なおバージョン1.0からSvelteフレームワークでのアプリに移行したため、ベースとなるファイルは主に
App.sveltestyle.scssの2つで構成することにする。

Svelteアプリ開発のベースプロジェクトに関しては、
こちらの別ブログの記事を参照のこと。

App.svelteの内容は以下のコード:

            
            <script lang="ts">
    let stages = [...Array(6).keys()].map(m => ({id: `${m+1}`, stageName: `stage${m+1}`, checkbox: `checkmark${m+1}`}));
    let result = 0;
    let total = 0;
    let gameStateSpan: any;
    $: clearStageArr = Array(6).fill(false);
    $: checkmarkArr = Array(6).fill(false);
    $: resultMessage = result == total ? `全問正解🎉あなたは「タコマスター」の称号を得ました!!` : `正解率は ${result}/${total} でした。`;

    function initGame() {
        gameStateSpan.textContent = 'さあ4択クイズを始めましょう';
        checkmarkArr = Array(6).fill(false);
        clearStageArr = Array(6).fill(false);
        result = 0, total = 0;
    }
    function liClick(event: any) {
        const m_ = window.getComputedStyle(event.target,'::after').content.match(/(\d+)\/correct/);
        if (m_) {
            checkmarkArr[parseInt(`${m_[1]}`, 10) - 1] = true;
            result++;
            gameStateSpan.textContent += '⭕ ';
        } else {
            gameStateSpan.textContent += '❌ ';
        };
        total++;
    }
    function invalidClick(event: any){
        gameStateSpan.textContent += '😢 ';
        total++;
    }
</script>

<form id="game-wrapper">
    <p class="game-header"><span id="gamestate" bind:this="{gameStateSpan}">さあ4択クイズを始めましょう</span></p>
    <input type="reset" id="reset"/>
    <input type="checkbox" id="start" on:change="{e => { gameStateSpan.textContent = '成績: ';}}"/>

    <div id="stage---op" class="stage stage--op"><label for="start">クリックしてスタート</label></div>

    {#each stages as stage, i}
        <input type="checkbox" id="stage{stage.id}" bind:checked="{clearStageArr[i]}" />
        <input type="checkbox" id="checkmark{stage.id}" bind:checked="{checkmarkArr[i]}"/>
    {/each}

    {#each stages as stage}
        <div id="stage---{stage.id}" class="stage stage--main stage--main--{stage.id}">
            <label for="stage{stage.id}" on:click="{e => invalidClick(e)}">
            <ul class="flexlist">
                {#each Array(4) as _}
                    <li on:click|stopPropagation="{e => liClick(e)}">
                {/each}
            </ul>
            </label>
        </div>
    {/each}

    <div id="stage---reset" class="stage stage--end">
        <p class="score-board"><span id="score" contenteditable="true" bind:textContent={resultMessage}></span></p>
        <label for="reset" on:click="{e => initGame()}">もう一度トライ</label>
    </div>
</form>

<style lang="scss">
@import "./style.scss";
</style>
        
なお、sytleタグでインポートしているファイル・style.cssは、このApp.svelteと同じフォルダにおいておく必要がある。

以下はstyle.cssの元になるscssのソースコード:

            
            #game-wrapper {
    width: 100%;
    height: 300px;
    background-color: darkgray;
    box-sizing: border-box;
    position: relative;
    input { display: none; }
    .game-header {
        position: absolute;
        top: 0;
        left: 0;
        background: #220c0c;
        color: #ceeece;
        z-index: 1;
        margin: 0;
        font-size: 22px;
        padding: 6px 0 6px 0;
        width: 100%;
    }
    $stages: (
        'start' : ('op', ''),
        'stage1': ('1', 'タコの足は何本?', ('5本', '8本', '10本', '足はない'), 2, blue),
        'stage2': ('2', 'タコは何動物?', ('地球外生物', '緩歩動物', '実は植物', '軟体動物'), 4, rgb(231, 185, 99)),
        'stage3': ('3', 'タコの水揚げ量が世界一の国?', ('中国','カナダ','モーリタニア','日本'), 1, rgb(211, 51, 203)),
        'stage4': ('4', 'タコの心臓は何個?', ('1個','2個','3個','8個'), 3, rgb(68, 67, 8)),
        'stage5': ('5', 'タコの弱点は?', ('イソギンチャク','直射日光','真水','ウミウシ'), 3, rgb(179, 53, 15)),
        'stage6': ('6', 'タコの脳は何個?', ('3個','9個','12個','実は存在しない'), 2, rgb(79, 100, 4))
    );
    .stage {
        position: absolute;
        display: block;
        width: 100%;
        height: 100%;
        font-size: 24px;
        &--op {
            display: flex;
            align-items: center;
            justify-content: center;
            label {
                display: block;
                flex: 0 0 auto;
                margin: 0 auto;
                animation: flash 1s linear infinite;
            }
        }
        &--main {
            display: flex;
            align-items: center;
            justify-content: center;
            label {
                flex: 0 0 auto;
                margin: 0 auto;
            }
            @for $i from 2 through length($stages) {
                $item: nth($stages,$i);
                $item: nth($item,2);
                &--#{nth($item,1)} {
                    color: nth($item,5);
                    font-size: 22px;
                }
            }
        }
        &--end {
            display: flex;
            align-items: center;
            justify-content: center;
            flex-direction: column;
            background: darkgray;
            label {
                flex: 0 0 auto;
                display: inline-block;
                font-weight: bold;
                padding: 0 0 0 0;
                text-decoration: none;
                color: #00BCD4;
                background: #ECECEC;
                margin: 0 auto;
                animation: flash 1s linear infinite;
            }
        }
        @keyframes flash {
            0%,100% { opacity: 1; }
            50% { opacity: 0; }
        }
    }
    .stage--main {
        ul.flexlist {
            border: 1px solid #666;
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
            list-style-type: none;
            margin: 10px auto 2px;
            padding: 0;
            width: 300px;
            li {
                border: 1px solid #aaa;
                line-height: 110%;
                margin: 5px 2px 5px 2px;
                padding: 8px;
                text-align: center;
                width: 35%;
                &:hover { color: chartreuse; }
                &::after {
                    content: "wrong";
                    visibility: hidden;
                    display: block;
                    width: 0;
                    height: 0;
                }
            }
        }
    }
    #stage {
        &---op { display: flex }
        @for $i from 2 through length($stages) {
            $item: nth($stages,$i);
            $item: nth($item,2);
            &---#{nth($item,1)} { display: none; }
        }
        &---reset { display: none; }
    }
    @for $i from 1 through length($stages) {
        $item: nth($stages, $i);
        $item: nth($item,1);
        ##{$item}:checked ~ {
            $item: nth($stages, $i);
            $item: nth($item, 2);
            $item: nth($item,1);
            #stage---#{$item} { display: none }
            @if $i == length($stages) {
                #stage---reset {
                    display: flex;
                    opacity: 1;
                    animation: fadeIn 0.3s ease-in 0s forwards;
                }
            } @else {
                $item: nth($stages, $i + 1);
                $item: nth($item,2);
                $item: nth($item,1);
                #stage---#{$item} {
                    display: flex;
                    opacity: 1;
                    animation: fadeIn 0.3s ease-in 0s forwards;
                    $item: nth($stages, $i + 1);
                    $item: nth($item,2);
                    $item: nth($item,2);
                    label::before { content: '問題#{$i}:#{$item}'; }
                    $item: nth($stages, $i + 1);
                    $item: nth($item,2);
                    $quiz_option: nth($item,3);
                    $answer: nth($item,4);
                    @for $j from 1 through length($quiz_option) {
                        ul.flexlist {
                            li:nth-child(#{$j}) {
                                &::before {content: '#{nth($quiz_option,$j)}';}
                                @if $j == $answer {
                                    &::after { content: "#{nth($item,1)}/correct"; }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    @keyframes fadeIn {
        0% {
            display: none;
            opacity: 0;
        }
        1% {
            display: flex;
            opacity: 0;
        }
        100% {
            display: flex;
            opacity: 1;
        }
    }
}
        
このstyle.scssを元にdart-sassコンパイラでstyle.cssファイルへ変換する。

※なおScssを手元でコンパイルする環境がない場合には、
Sassオンラインコンパイラでも代用可。

合同会社タコスキングダム|TacosKingdom,LLC.

同フォルダ内に、
index.htmlstyle.cssをおいた上で、適当なブラウザでindex.htmlを開くとhtml&cssアプリが起動する。

バージョン1.0 (2022/01/18修正分)

軽量なJSフレームワーク・Svelteの導入し、Svelteアプリに移行した。

前回までのブログ記事の内容より大幅な変更になったため、アプリのメジャーバージョンを1.0に切り上げた。

参考記事 | 【Html&Cssで作る四択クイズゲーム開発記録⑦】Svelteアプリの作り方入門!HTML&CSSゲームをSvelteプロジェクトへ移行(マイグレーション)する

バージョン0.7 (2022/01/14修正分)

JSフレームワークの導入のポイントを説明するため、バージョン0.6のコードからネイティブJavascriptの関数を使ってDOM構造を操作する手法を紹介。

参考記事 | 【Html&Cssで作る四択クイズゲーム開発記録⑥】ネイティブなJavascriptスクリプトをフル活用してHTMLタグ要素(DOM)を自動生成する

バージョン0.6 (2022/01/11修正分)

リファクタリングの目的で、バージョン0.5のコードからSassリストを使ったクイズ問題の動的管理を行う手法を紹介。

参考記事 | 【Html&Cssで作る四択クイズゲーム開発記録⑤】Sassリストでクイズの問題を動的に管理する

バージョン0.5 (2022/01/09修正分)

リファクタリングの目的で、バージョン0.4のコードからJavascriptのfunction等を使って機能の集約・分離による効率化手法を紹介。

参考記事 | 【Html&Cssで作る四択クイズゲーム開発記録④】HTMLのスクリプト部分(Javascript)をfunctionでまとめる

バージョン0.4 (2021/12/22修正分)

リファクタリングの目的で、バージョン0.3のコードからSassリストを構造化し、擬似要素のcontentプロパティを使ってHTMLの内容を初期化する手法を紹介。

参考記事 | 【Html&Cssで作る四択クイズゲーム開発記録③】Sassリストでゲームデータを集約してHTML要素を初期化する

バージョン0.3 (2021/12/18修正分)

リファクタリングの目的で、バージョン0.2のコードからSassリストから擬似要素のcontentプロパティにデータを保存する手法を紹介。

参考記事 | 【Html&Cssで作る四択クイズゲーム開発記録②】擬似要素のcontentプロパティでクイズの正誤判定に使う

バージョン0.2 (2021/12/15修正分)

リファクタリングの目的で、バージョン0.1のコードからSassの@for及びリストの手法を紹介。

参考記事 | 【Html&Cssで作る四択クイズゲーム開発記録①】Sassのリストと@forループを使ったコード省力化の話

バージョン0.1 (2021/07/06作成分)

Html&Cssゲームとしてプログラムを新規作成。

ゲームの元にした内容は
別ブログのこちらの記事から引っ張ってきたソースコードに変更を加えたものを利用。
記事を書いた人

記事の担当:taconocat

ナンデモ系エンジニア

これからの"地方格差"なきプログラミング教育とは何かを考えながら、 地方密着型プログラミング学習関連テーマの記事を不定期で独自にブログ発信しています。