@hihoraaaaash

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

scriptタグ内からHTML,CSS,JSへの分解

解決したいこと

プログラミング初心者です。
先日、動画を参考にTETRISをJavaScriptで作成したのですが、
HTMLのscript内に全てが記述されていたので、今度はそれをHTML,CSS,JSに分解しようと奮闘しています。
ですが理解できていないところも多く、正解がわかりません。
下記のコードをHTML,CSS,JSに分解したときの正解を教えていただけないでしょうか。

    //ブロックのサイズ
    const BLOCK_SIZE_X = 30;
    const BLOCK_SIZE_Y = 30;

    //テトロミノのサイズ
    const TETRO_SIZE = 4;

    //フィールドサイズ
    const FIELD_COL = 10;
    const FIELD_ROW = 20;

    const GAME_SPEED = 500;                  //落ちるスピードの関数

    let can = document.getElementById("can"); //"Canbuse"のidの取得 document.getElementByIDはよく使う
    let con = can.getContext("2d");   //2D表示

    can.style.border = "4px solid #555";      //キャンバスの枠線を#555で表示

    const TETRO_COLORS = [
        "#000",                  //0無
        "#6CF",                  //1水
        "#F92",                  //2橙
        "#66F",                  //3青
        "#C5C",                  //4紫
        "#FD2",                  //5黄
        "#F44",                  //6赤
        "#5B5",                  //7緑          
    ]

    const TETRO_TYPE = [                    //テトロミノのタイプ([1次元[2次元]]なので3次元配列)
        [],                     //0.空    
        [
            [0, 0, 0, 0],
            [1, 1, 1, 1],
            [0, 0, 0, 0],
            [0, 0, 0, 0],          //1.I
        ],
        [
            [0, 1, 0, 0],
            [0, 1, 0, 0],
            [0, 1, 1, 0],
            [0, 0, 0, 0],          //2.L
        ],
        [
            [0, 0, 1, 0],
            [0, 0, 1, 0],
            [0, 1, 1, 0],
            [0, 0, 0, 0],          //3.J
        ],
        [
            [0, 1, 0, 0],
            [0, 1, 1, 0],
            [0, 1, 0, 0],
            [0, 0, 0, 0],          //4.T
        ],
        [
            [0, 0, 0, 0],
            [0, 1, 1, 0],
            [0, 1, 1, 0],
            [0, 0, 0, 0],          //5.O
        ],
        [
            [0, 0, 0, 0],
            [1, 1, 0, 0],
            [0, 1, 1, 0],
            [0, 0, 0, 0],          //6.Z
        ],
        [
            [0, 0, 0, 0],
            [0, 1, 1, 0],
            [1, 1, 0, 0],
            [0, 0, 0, 0],          //7.S
        ],
    ]

    const START_X = FIELD_COL / 2 - TETRO_SIZE / 2;             //真ん中からスタート
    const START_Y = 0;

    let tetro;                //テトロミノ本体

    let tetro_x = START_X;
    let tetro_y = START_Y;                   //テトロミノの座標

    let tetro_t;                     //テトロミノの形の関数

    let field = [];                       //fieldは1次元配列であるという定義(Javascriptでは2次元配列のクリアができないので1次元ずつ分解して行う)

    let over = false;                         //ゲームオーバー

    let lines = 0;                            //消したライン数

    tetro_t = Math.floor(Math.random() * (TETRO_TYPE.length - 1)) + 1;      //テトロミノの種類をランダムにする(length)手前の関数がいくつあるか引っ張ってくれる
    tetro = TETRO_TYPE[tetro_t]         //テトロミノの形の呼び出し

    init();                          //最初の1回表示             
    drawAll();

    setInterval(dropTetro, GAME_SPEED)       //一定間隔で落ちるスピードの関数を呼び出し

    function init()                     //初期化の関数
    {
        for (let y = 0; y < FIELD_ROW; y++)   //forを使ってfield_yをループ
        {
            field[y] = [];                   //yの1次元配列1行目が配列であるという定義(1次元配列の中に1次元配列があるので実質2次元配列)
            for (let x = 0; x < FIELD_COL; x++)    //forを使ってfield_xをループ
            {
                field[y][x] = 0;              //すべてのfield_y.xに0を代入
            }
        }

    }


    function drawBlock(y, x, c)                //ブロック1つを描画
    {
        let px = x * BLOCK_SIZE_X;    //xが増えるたびにBLOCK_SIZE_Xずつ増える
        let py = y * BLOCK_SIZE_Y;    //yが増えるたびにBLOCK_SIZE_Yずつ増える

        con.fillStyle = TETRO_COLORS[c];    //"red"で表示
        con.fillRect(px, py, BLOCK_SIZE_X, BLOCK_SIZE_Y);   //(ブロックの座標,ブロックのサイズ)
        con.strokeStyle = "black";
        con.strokeRect(px, py, BLOCK_SIZE_X, BLOCK_SIZE_Y);   //ブロックの枠線
    }


    function drawAll()               //フィールド,テトロミノを描画する関数
    {
        con.clearRect(0, 0, can.width, can.height);     // 画面をクリア

        for (let y = 0; y < FIELD_ROW; y++)   //forを使ってyをループ
        {
            for (let x = 0; x < FIELD_COL; x++)    //forを使ってxをループ
            {
                if (field[y][x])  //y→xの順番で表示
                {
                    drawBlock(y, x, field[y][x]);
                }
            }
        }


        for (let y = 0; y < TETRO_SIZE; y++)   //forを使ってyをループ
        {
            for (let x = 0; x < TETRO_SIZE; x++)    //forを使ってxをループ
            {
                if (tetro[y][x])  //y→xの順番で表示
                {
                    drawBlock(tetro_y + y, tetro_x + x, tetro_t);
                }
            }
        }

        if (over) {
            let s = "GAME OVER";
            con.font = "40px'MSゴシック'";
            let w = con.measureText(s).width;
            let x = can.width / 2 - w / 2;
            let y = can.height / 2 - 20;
            con.lineWidth = 4;
            con.strokeText(s, x, y);
            con.fillStyle = "white";
            con.fillText(s, x, y);

        }
    }

    function checkMove(mx, my, ntetro)                   //移動先の当たり判定関数
    {
        if (ntetro == undefined) ntetro = tetro;          //新しいテトロミノが描画できるかチェック
        for (let y = 0; y < TETRO_SIZE; y++)                   //forを使ってyをループ
        {
            for (let x = 0; x < TETRO_SIZE; x++)               //forを使ってxをループ
            {
                if (ntetro[y][x])                         //y→xの順番で表示
                {
                    let nx = tetro_x + mx + x;
                    let ny = tetro_y + my + y;                    //テトロミノ(4*4)+動きたい方向分1+座標

                    if (ny < 0 ||
                        nx < 0 ||
                        ny >= FIELD_ROW ||
                        nx >= FIELD_COL ||
                        field[ny][nx]) {
                        return false;                   //それ以外はfalesを返す
                    }
                }
            }
        }
        return true;
    }


    function rotate()                               //ローテーションの関数
    {
        let ntetro = [];                              //新しいテトロミノを1時配列で初期化
        for (let y = 0; y < TETRO_SIZE; y++)               //forを使ってyをループ
        {
            ntetro[y] = [];                            //1次元配列を2次元配列化
            for (let x = 0; x < TETRO_SIZE; x++)           //forを使ってxをループ
            {
                ntetro[y][x] = tetro[TETRO_SIZE - x - 1][y];             //ローテーションの動作
            }
        }
        return ntetro;

    }

    function fixTetro()                             //テトロミノを固定する関数
    {
        for (let y = 0; y < TETRO_SIZE; y++)               //forを使ってyをループ
        {
            for (let x = 0; x < TETRO_SIZE; x++)           //forを使ってxをループ
            {
                if (tetro[y][x]) {
                    field[tetro_y + y][tetro_x + x] = tetro_t;
                }
            }
        }
    }

    function checkLine()                           //ラインが揃ったかのチェック
    {
        let linec = 0;                                //消えたラインのカウント
        for (let y = 0; y < FIELD_ROW; y++)               //forを使ってyをループ
        {
            let flag = true;
            for (let x = 0; x < FIELD_COL; x++) {
                if (!field[y][x]) {
                    flag = false;
                    break;
                }
            }
            if (flag)                                    //flagがfalseじゃないときの処理
            {
                linec++;
                for (let ny = y; ny > 0; ny--) {
                    for (let nx = 0; nx < FIELD_COL; nx++) {
                        field[ny][nx] = field[ny - 1][nx];    //上の行から持ってくる
                    }
                }
            }
        }
    }

    function dropTetro()                            //GAME_SPEED関数を呼び出し
    {
        if (over) return;
        if (checkMove(0, 1)) tetro_y++;                //処理内容(下)
        else {
            fixTetro();
            checkLine();

            tetro_t = Math.floor(Math.random() * (TETRO_TYPE.length - 1)) + 1;
            tetro = TETRO_TYPE[tetro_t];
            tetro_x = START_X;
            tetro_y = START_Y;

            if (!checkMove(0, 0))                     //新しいブロックがおけなかったとき
            {
                over = true;                          //overという関数を実行
            }
        }
        drawAll();                                  //再描画
    }

    document.onkeydown = function (e)                  //キーボードが押された時の処理    
    {
        if (over) return;
        switch (e.keyCode)                           //onKeydown→Codeで検索
        {
            case 37://左
                if (checkMove(-1, 0)) tetro_x--;       //function checkMove(mx,my)で当たり判定と移動座標を確認した値がtrueの時移動を実行
                break;
            case 38://上
                if (checkMove(0, -1)) tetro_y--;
                break;
            case 39://右
                if (checkMove(1, 0)) tetro_x++;
                break;
            case 40://下
                if (checkMove(0, 1)) tetro_y++;
                break;
            case 32://スペース
                let ntetro = rotate();                //↓の処理が通貨できれば、テトロミノをローテーションする
                if (checkMove(0, 0, ntetro)) tetro = ntetro;      //checkMoveから受けた結果がturueであれば処理を実行
                break;
        }
        drawAll();
    }

</script>

自分で試したこと。

コードごとに処理内容や定義の内容をコメントでつけてみました。
間違っている個所も多いかと思いますがよろしくお願いします。

0 likes

1Answer

そもそも 分けるもなにも そのコードは javascript しかないのではないでしょうか?(先頭の <script> タグを忘れたのはまぁいいとして、他 html に必要なタグ等未記載なので動作しないのではないでしょうか?

とりあえず必要要素とそのサイズが明記されていないので対応すると次の様な感じでしょうか?

playground

<canvas id="can"></canvas>
<script type="module">
     //ブロックのサイズ
    const BLOCK_SIZE_X = 30;
    const BLOCK_SIZE_Y = 30;

    //テトロミノのサイズ
    const TETRO_SIZE = 4;

    //フィールドサイズ
    const FIELD_COL = 10;
    const FIELD_ROW = 20;

    const GAME_SPEED = 500;                  //落ちるスピードの関数

    let can = document.getElementById("can"); //"Canvas"のidの取得 document.getElementByIDはよく使う
    // #region canvas のサイズ
    can.width = FIELD_COL * BLOCK_SIZE_X;
    can.height = FIELD_ROW * BLOCK_SIZE_Y;
    // #endregion
    let con = can.getContext("2d");   //2D表示

    can.style.border = "4px solid #555";      //キャンバスの枠線を#555で表示

    const TETRO_COLORS = [
        "#000",                  //0無
        "#6CF",                  //1水
        "#F92",                  //2橙
        "#66F",                  //3青
        "#C5C",                  //4紫
        "#FD2",                  //5黄
        "#F44",                  //6赤
        "#5B5",                  //7緑          
    ]

    const TETRO_TYPE = [                    //テトロミノのタイプ([1次元[2次元]]なので3次元配列)
        [],                     //0.空    
        [
            [0, 0, 0, 0],
            [1, 1, 1, 1],
            [0, 0, 0, 0],
            [0, 0, 0, 0],          //1.I
        ],
        [
            [0, 1, 0, 0],
            [0, 1, 0, 0],
            [0, 1, 1, 0],
            [0, 0, 0, 0],          //2.L
        ],
        [
            [0, 0, 1, 0],
            [0, 0, 1, 0],
            [0, 1, 1, 0],
            [0, 0, 0, 0],          //3.J
        ],
        [
            [0, 1, 0, 0],
            [0, 1, 1, 0],
            [0, 1, 0, 0],
            [0, 0, 0, 0],          //4.T
        ],
        [
            [0, 0, 0, 0],
            [0, 1, 1, 0],
            [0, 1, 1, 0],
            [0, 0, 0, 0],          //5.O
        ],
        [
            [0, 0, 0, 0],
            [1, 1, 0, 0],
            [0, 1, 1, 0],
            [0, 0, 0, 0],          //6.Z
        ],
        [
            [0, 0, 0, 0],
            [0, 1, 1, 0],
            [1, 1, 0, 0],
            [0, 0, 0, 0],          //7.S
        ],
    ]

    const START_X = FIELD_COL / 2 - TETRO_SIZE / 2;             //真ん中からスタート
    const START_Y = 0;

    let tetro;                //テトロミノ本体

    let tetro_x = START_X;
    let tetro_y = START_Y;                   //テトロミノの座標

    let tetro_t;                     //テトロミノの形の関数

    let field = [];                       //fieldは1次元配列であるという定義(Javascriptでは2次元配列のクリアができないので1次元ずつ分解して行う)

    let over = false;                         //ゲームオーバー

    let lines = 0;                            //消したライン数

    tetro_t = Math.floor(Math.random() * (TETRO_TYPE.length - 1)) + 1;      //テトロミノの種類をランダムにする(length)手前の関数がいくつあるか引っ張ってくれる
    tetro = TETRO_TYPE[tetro_t]         //テトロミノの形の呼び出し

    init();                          //最初の1回表示             
    drawAll();

    setInterval(dropTetro, GAME_SPEED)       //一定間隔で落ちるスピードの関数を呼び出し

    function init()                     //初期化の関数
    {
        for (let y = 0; y < FIELD_ROW; y++)   //forを使ってfield_yをループ
        {
            field[y] = [];                   //yの1次元配列1行目が配列であるという定義(1次元配列の中に1次元配列があるので実質2次元配列)
            for (let x = 0; x < FIELD_COL; x++)    //forを使ってfield_xをループ
            {
                field[y][x] = 0;              //すべてのfield_y.xに0を代入
            }
        }

    }


    function drawBlock(y, x, c)                //ブロック1つを描画
    {
        let px = x * BLOCK_SIZE_X;    //xが増えるたびにBLOCK_SIZE_Xずつ増える
        let py = y * BLOCK_SIZE_Y;    //yが増えるたびにBLOCK_SIZE_Yずつ増える

        con.fillStyle = TETRO_COLORS[c];    //"red"で表示
        con.fillRect(px, py, BLOCK_SIZE_X, BLOCK_SIZE_Y);   //(ブロックの座標,ブロックのサイズ)
        con.strokeStyle = "black";
        con.strokeRect(px, py, BLOCK_SIZE_X, BLOCK_SIZE_Y);   //ブロックの枠線
    }


    function drawAll()               //フィールド,テトロミノを描画する関数
    {
        con.clearRect(0, 0, can.width, can.height);     // 画面をクリア

        for (let y = 0; y < FIELD_ROW; y++)   //forを使ってyをループ
        {
            for (let x = 0; x < FIELD_COL; x++)    //forを使ってxをループ
            {
                if (field[y][x])  //y→xの順番で表示
                {
                    drawBlock(y, x, field[y][x]);
                }
            }
        }


        for (let y = 0; y < TETRO_SIZE; y++)   //forを使ってyをループ
        {
            for (let x = 0; x < TETRO_SIZE; x++)    //forを使ってxをループ
            {
                if (tetro[y][x])  //y→xの順番で表示
                {
                    drawBlock(tetro_y + y, tetro_x + x, tetro_t);
                }
            }
        }

        if (over) {
            let s = "GAME OVER";
            con.font = "40px'MSゴシック'";
            let w = con.measureText(s).width;
            let x = can.width / 2 - w / 2;
            let y = can.height / 2 - 20;
            con.lineWidth = 4;
            con.strokeText(s, x, y);
            con.fillStyle = "white";
            con.fillText(s, x, y);

        }
    }

    function checkMove(mx, my, ntetro)                   //移動先の当たり判定関数
    {
        if (ntetro == undefined) ntetro = tetro;          //新しいテトロミノが描画できるかチェック
        for (let y = 0; y < TETRO_SIZE; y++)                   //forを使ってyをループ
        {
            for (let x = 0; x < TETRO_SIZE; x++)               //forを使ってxをループ
            {
                if (ntetro[y][x])                         //y→xの順番で表示
                {
                    let nx = tetro_x + mx + x;
                    let ny = tetro_y + my + y;                    //テトロミノ(4*4)+動きたい方向分1+座標

                    if (ny < 0 ||
                        nx < 0 ||
                        ny >= FIELD_ROW ||
                        nx >= FIELD_COL ||
                        field[ny][nx]) {
                        return false;                   //それ以外はfalesを返す
                    }
                }
            }
        }
        return true;
    }


    function rotate()                               //ローテーションの関数
    {
        let ntetro = [];                              //新しいテトロミノを1時配列で初期化
        for (let y = 0; y < TETRO_SIZE; y++)               //forを使ってyをループ
        {
            ntetro[y] = [];                            //1次元配列を2次元配列化
            for (let x = 0; x < TETRO_SIZE; x++)           //forを使ってxをループ
            {
                ntetro[y][x] = tetro[TETRO_SIZE - x - 1][y];             //ローテーションの動作
            }
        }
        return ntetro;

    }

    function fixTetro()                             //テトロミノを固定する関数
    {
        for (let y = 0; y < TETRO_SIZE; y++)               //forを使ってyをループ
        {
            for (let x = 0; x < TETRO_SIZE; x++)           //forを使ってxをループ
            {
                if (tetro[y][x]) {
                    field[tetro_y + y][tetro_x + x] = tetro_t;
                }
            }
        }
    }

    function checkLine()                           //ラインが揃ったかのチェック
    {
        let linec = 0;                                //消えたラインのカウント
        for (let y = 0; y < FIELD_ROW; y++)               //forを使ってyをループ
        {
            let flag = true;
            for (let x = 0; x < FIELD_COL; x++) {
                if (!field[y][x]) {
                    flag = false;
                    break;
                }
            }
            if (flag)                                    //flagがfalseじゃないときの処理
            {
                linec++;
                for (let ny = y; ny > 0; ny--) {
                    for (let nx = 0; nx < FIELD_COL; nx++) {
                        field[ny][nx] = field[ny - 1][nx];    //上の行から持ってくる
                    }
                }
            }
        }
    }

    function dropTetro()                            //GAME_SPEED関数を呼び出し
    {
        if (over) return;
        if (checkMove(0, 1)) tetro_y++;                //処理内容(下)
        else {
            fixTetro();
            checkLine();

            tetro_t = Math.floor(Math.random() * (TETRO_TYPE.length - 1)) + 1;
            tetro = TETRO_TYPE[tetro_t];
            tetro_x = START_X;
            tetro_y = START_Y;

            if (!checkMove(0, 0))                     //新しいブロックがおけなかったとき
            {
                over = true;                          //overという関数を実行
            }
        }
        drawAll();                                  //再描画
    }

    document.onkeydown = function (e)                  //キーボードが押された時の処理    
    {
        if (over) return;
        switch (e.keyCode)                           //onKeydown→Codeで検索
        {
            case 37://左
                if (checkMove(-1, 0)) tetro_x--;       //function checkMove(mx,my)で当たり判定と移動座標を確認した値がtrueの時移動を実行
                break;
            case 38://上
                if (checkMove(0, -1)) tetro_y--;
                break;
            case 39://右
                if (checkMove(1, 0)) tetro_x++;
                break;
            case 40://下
                if (checkMove(0, 1)) tetro_y++;
                break;
            case 32://スペース
                let ntetro = rotate();                //↓の処理が通貨できれば、テトロミノをローテーションする
                if (checkMove(0, 0, ntetro)) tetro = ntetro;      //checkMoveから受けた結果がturueであれば処理を実行
                break;
        }
        drawAll();
    }

</script>
1Like

Comments

  1. @hihoraaaaash

    Questioner

    回答ありがとうございます。
    先頭の〈script〉タグは記載するとそれ以下の文章が全部消えてしまったので、削除してました。

    言葉足らずで申し訳ないです。
    例えばブロックのサイズや、色、種類などはCSSで記載することはできないのでしょうか?
    というような質問をしたかったのが本題です。
    お時間ありましたら、ご解答よろしくお願いします。

  2. 多分それは コードブロック(```) で囲う前の段階では……?(囲んでいるのであれば私のコードの様に消えることは無い筈です。

    まず、第一に canvas 自体は css で装飾できますが、そこでの描画に css を使うことはできません。

    ただ、 css は 変数として カスタムプロパティの仕組みがあるので、 css で 宣言したプロパティを取得するはできます

    カスタムプロパティ (--*): CSS 変数 - CSS: カスケーディングスタイルシート | MDN

    ただ、それをするくらいならまだ canvas に data カスタム属性を使って値を設定した方が幾分かシンプルです。

    data-* - HTML: ハイパーテキストマークアップ言語 | MDN

  3. @hihoraaaaash

    Questioner

    そのような決まりがあるとも知らず、失礼しました。
    できること、できないことの境が1つはっきりして助かります。
    丁寧にソースも示していただき、ありがとうございます。
    こちらを参考に勉強を進めていきます。
    お忙しいところ、ありがとうございました。

Your answer might help someone💌