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>
自分で試したこと。
コードごとに処理内容や定義の内容をコメントでつけてみました。
間違っている個所も多いかと思いますがよろしくお願いします。