【対戦型タイピングゲーム】攻撃のモーションをつくろう

タイピングゲーム(Step2)

コトゼニ
コトゼニ

タイピングゲーム制作を通してプログラミングに強くなろう!

ということで、オリジナル 対戦型タイピングゲームの制作過程を紹介しています。

このタイピングゲームは

  • HTML
  • CSS
  • JavaScript

の3種類の言語で制作していて初心者でも取り扱いやすい内容になっていて、

高校の情報1のプログラミング実技授業としてもオススメです!

この記事では制作過程の「Step2攻撃のモーションをつくろう」ということで、CSSとJavaScriptファイルを更新して、攻撃のモーションを加えてみましょう!

Step2の詳細

checklist2

Step2の目標

Step2では

  • ライフバーを作成する
  • 攻撃のモーションをjavascriptで表現

これらを実装することを目標にします。

Step2の必要時間(目安)

Step2を終えるには目安として1〜2時間必要です。

Step2のゴール

Step2で入力するコードで正しく機能を実装できれば以下のようなプログラムの状態になります。

入力画面

Step2のゴール

攻撃のモーションが実行されることが確認できます。

Step2の流れ

programming

それではStep2をどのように進めていくか順に説明します。

課題①ライフバーを作成する

授業では、下記の課題ファイルを準備し、穴埋め方式にします。

HTMLファイルのどの部分に入力するのか、CSSでどのように装飾するのかを解説することが大事です。

この課題はHTMLファイルのコメント「<!– ライフバーを作成する箇所 –>」のところに必要なコードを入力させるようにしましょう。

<div>要素と<span>要素を入力する必要がありますので、模範コードを元に記述させましょう。

課題②攻撃のモーションをつける

授業では、課題用コードの穴埋めを実施させましょう。

攻撃のモーションをつけるロジックは難しいため、自力で考えさせることは難しいと考えていますので、どのファンクション(関数)が攻撃のモーションをつけるものに関わっているのかを考えさせることに留めても良いでしょう。

模範コードと課題用コードの例

programming-code

最後に模範コードと課題用コードの例を紹介します。

課題用コードの編集箇所は好きにアレンジしてください!

HTMLファイル

HTMLファイルの名前は「index.html」にしましょう。ライフバーの箇所を穴埋めにしています。CSSと見比べながら、div要素に付加するidやclassの名称を考えさせてみても良いでしょう。

模範コード

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>タイピング対戦ゲーム</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div id="startScreen" class="screen active">
        <h1>タイピング対戦ゲーム</h1>
        <button id="startButton">ゲーム開始</button>
    </div>
    
    <div id="battleScreen" class="screen">
        <div id="lifeSection" class="character-container">
            <div id="playerLife" class="lifeBar"><span class="lifeText">100</span></div>
            <div id="enemyLife" class="lifeBar"><span class="lifeText">100</span></div>
        </div>
        
        <div id="battleSection">
            <div class="character player">プレイヤー</div>
            <div class="character enemy">敵キャラ</div>
        </div>
        
        <div id="typingSection">
            <span id="currentWord"></span>
            <input type="text" id="typingInput">
        </div>
        
        <div id="result"></div>
    </div>

    <script src="script.js"></script>
</body>
</html>

課題用コードの例

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>タイピング対戦ゲーム</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div id="startScreen" class="screen active">
        <h1>タイピング対戦ゲーム</h1>
        <button id="startButton">ゲーム開始</button>
    </div>
    
    <div id="battleScreen" class="screen">
        <div id="lifeSection" class="character-container">
     <!-- ライフバーを作成する箇所 -->
            <div >100</div>
            <div >100</div>
        </div>
        
        <div id="battleSection">
            <div class="character player">プレイヤー</div>
            <div class="character enemy">敵キャラ</div>
        </div>
        
        <div id="typingSection">
            <span id="currentWord"></span>
            <input type="text" id="typingInput">
        </div>
        
        <div id="result"></div>
    </div>

    <script src="script.js"></script>
</body>
</html>

CSSファイル

HTMLファイルで読み込むCSSファイルの名称を指定しているので、「style.css」というファイル名にしておきましょう。

このステップではCSSの修正はありませんので、課題用コードはありません。

模範コード

/* スタイル.css */
body, html {
  height: 100%;
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}

.screen {
  display: none;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
}

.screen.active {
  display: flex;
}

#lifeSection, #typingSection {
  width: 100%;
}

.lifeBar {
  width: 300px;
  height: 30px;
  background-color: #ddd;
  margin: 5px;
  position: relative;
}

.lifeBar .lifeText {
  position: absolute;
  width: 100%;
  line-height: 30px;
  left: 0;
}

.player .lifeBar {
  background-color: green;
}

.enemy .lifeBar {
  background-color: red;
}

#battleSection {
  display: flex;
  justify-content: space-around;
  width: 100%;
}

.character {
  transition: transform 1s;
}

.character.player {
  transform: translateX(0);
}

/* 攻撃アニメーション */
.character.player.attacking {
  transform: translateX(50%);
}

.character.enemy {
  transform: translateX(0);
}

/* 攻撃アニメーション */
.character.enemy.attacking {
  transform: translateX(-50%);
}

#currentWord {
  display: block;
  margin: 20px 0;
  font-size: 24px;
}

#typingInput {
  font-size: 24px;
}

#result {
  font-size: 2em;
  color: red;
}


/* スタイル.css */
body {
  font-family: Arial, sans-serif;
  text-align: center;
  background-color: #f4f4f4;
  margin: 0;
  padding: 20px;
}

.character-container {
  display: flex;
  justify-content: space-around;
  margin-bottom: 20px;
}

.character {
  text-align: center;
}

.health-bar {
  width: 200px;
  height: 24px;
  background-color: #ddd;
  border-radius: 12px;
  overflow: hidden;
  margin-bottom: 10px;
}

.health {
  height: 100%;
  background-color: #f00;
  border-radius: 12px;
  transition: width 0.3s ease-in-out;
}

.character-name {
  font-size: 1.2em;
  margin-bottom: 10px;
}

JavaScriptファイル

JavaScriptのファイル名は「script.js」としておきましょう。

課題用コードではHTMLから取得する要素の名前を穴埋め式にしています。画面をロードした際に、HTMLからどの要素を取得すれば良いかを考えながら進めましょう。

模範コード

// スクリプト.js

// ページのコンテンツが読み込まれた後に実行されるイベントリスナー
document.addEventListener('DOMContentLoaded', () => {
    // 各HTML要素を取得
    const startButton = document.getElementById('startButton');
    const startScreen = document.getElementById('startScreen');
    const battleScreen = document.getElementById('battleScreen');
    const typingInput = document.getElementById('typingInput');
    const currentWordSpan = document.getElementById('currentWord');
    const playerLifeBar = document.getElementById('playerLife');
    const enemyLifeBar = document.getElementById('enemyLife');
    const playerCharacter = document.querySelector('.character.player');
    const enemyCharacter = document.querySelector('.character.enemy');
    const resultDisplay = document.getElementById('result');

    // プレイヤーと敵の初期ライフを設定
    let playerLife = 100;
    let enemyLife = 100;

    // 現在の単語を格納する変数
    let currentWord = '';

    // タイマーを管理する変数
    let timer;

    // タイピング用の単語リスト
    const words = ["challenge", "opponent", "battle", "victory", "defeat"];

    // ゲームを開始する関数
    function startGame() {
        startScreen.classList.remove('active'); // 開始画面を非表示に
        battleScreen.classList.add('active');   // 対戦画面を表示
        nextWord();                             // 次の単語を表示
    }

    // 次の単語を表示する関数
    function nextWord() {
        currentWord = words[Math.floor(Math.random() * words.length)]; // ランダムな単語を選択
        currentWordSpan.textContent = currentWord; // 単語を表示
        typingInput.value = ''; // 入力フィールドをクリア
        typingInput.focus();    // 入力フィールドにフォーカス
        startTimer();           // タイマーを開始
    }

    // 入力をチェックする関数
    function checkInput() {
        const typedValue = typingInput.value; // 入力された値を取得
        if (typedValue === currentWord) {     // 入力値が単語と一致した場合
            attackEnemy();                    // 敵に攻撃
        } else {                              // 一致しない場合
            // 入力された部分をグレーで表示
            let matchedPart = currentWord.substring(0, typedValue.length);
            let remainingPart = currentWord.substring(typedValue.length);
            currentWordSpan.innerHTML = `<span style="color: grey;">${matchedPart}</span>${remainingPart}`;
        }
    }

    // プレイヤーが敵に攻撃する関数
    function attackEnemy() {
        playerCharacter.classList.add('attacking'); // 攻撃アニメーション
        setTimeout(() => {
            playerCharacter.classList.remove('attacking'); // アニメーション終了
            enemyLife -= 20;                              // 敵のライフを減らす
            updateLifeBar(enemyLifeBar, enemyLife);       // 敵のライフバーを更新
            checkGameOver();                              // ゲームオーバーをチェック
            nextWord();                                   // 次の単語へ
        }, 1000);
    }

    // 敵がプレイヤーに攻撃する関数
    function enemyAttacks() {
        enemyCharacter.classList.add('attacking'); // 攻撃アニメーション
        setTimeout(() => {
            enemyCharacter.classList.remove('attacking'); // アニメーション終了
            playerLife -= 20;                             // プレイヤーのライフを減らす
            updateLifeBar(playerLifeBar, playerLife);     // プレイヤーのライフバーを更新
            checkGameOver();                              // ゲームオーバーをチェック
        }, 1000);
    }

    // ライフバーを更新する関数
    function updateLifeBar(lifeBar, life) {
        lifeBar.style.width = `${life}%`; // ライフバーの幅を更新
        lifeBar.querySelector('.lifeText').textContent = life; // ライフ値を更新
    }

    // ゲームオーバーをチェックする関数
    function checkGameOver() {
        if (playerLife <= 0) {
            gameOver("負け"); // プレイヤーの負け
        } else if (enemyLife <= 0) {
            gameOver("勝ち"); // プレイヤーの勝ち
        }
    }

    // ゲームオーバー時の処理を行う関数
    function gameOver(result) {
        resultDisplay.textContent = result; // 結果を表示
        clearTimeout(timer);                // タイマーを停止
    }

    // タイマーを開始する関数
    function startTimer() {
        clearTimeout(timer); // 既存のタイマーをクリア
        timer = setTimeout(() => {
            enemyAttacks();   // 敵が攻撃
            nextWord();       // 次の単語へ
        }, 10000);           // 10秒後に実行(入力の制限時間)
    }

    // イベントリスナーの設定
    startButton.addEventListener('click', startGame);
    typingInput.addEventListener('input', checkInput);
});

課題用コードの例

// スクリプト.js

// ページのコンテンツが読み込まれた後に実行されるイベントリスナー
document.addEventListener('DOMContentLoaded', () => {
    // 各HTML要素を取得
    const startButton = document.getElementById('■■■');
    const startScreen = document.getElementById('■■■');
    const battleScreen = document.getElementById('■■■');
    const typingInput = document.getElementById('■■■');
    const currentWordSpan = document.getElementById('■■■');
    const playerLifeBar = document.getElementById('■■■');
    const enemyLifeBar = document.getElementById('■■■');
    const playerCharacter = document.querySelector('.character.player');
    const enemyCharacter = document.querySelector('.character.enemy');
    const resultDisplay = document.getElementById('■■■');

    // プレイヤーと敵の初期ライフを設定
    let playerLife = 100;
    let enemyLife = 100;

    // 現在の単語を格納する変数
    let currentWord = '';

    // タイマーを管理する変数
    let timer;

    // タイピング用の単語リスト
    const words = ["challenge", "■■■", "■■■", "■■■", "■■■", "■■■", "■■■", "■■■", "■■■", "■■■"]; //10個作成する

    // ゲームを開始する関数
    function startGame() {
        startScreen.classList.remove('active'); // 開始画面を非表示に
        battleScreen.classList.add('■■■');   // 対戦画面を表示
        nextWord();                             // 次の単語を表示
    }

    // 次の単語を表示する関数
    function nextWord() {
        //単語を格納している配列からランダムに単語を取得する
        currentWord = words[■■■]; // ランダムな単語を選択
        currentWordSpan.textContent = ■■■; // 単語を表示
        typingInput.value = ''; // 入力フィールドをクリア
        typingInput.focus();    // 入力フィールドにフォーカス
        ■■■();           // タイマーを開始
    }

    // 入力をチェックする関数
    function checkInput() {
        const typedValue = typingInput.value; // 入力された値を取得
        if (typedValue === currentWord) {     // 入力値が単語と一致した場合
            ■■■();                    // 敵に攻撃
        } else {                              // 一致しない場合
            // 入力された部分をグレーで表示
            let matchedPart = currentWord.substring(0, typedValue.length);
            let remainingPart = currentWord.substring(typedValue.length);
            currentWordSpan.innerHTML = `<span style="color: grey;">${matchedPart}</span>${remainingPart}`;
        }
    }

    // プレイヤーが敵に攻撃する関数
    function attackEnemy() {
        playerCharacter.classList.add('attacking'); // 攻撃アニメーション
        setTimeout(() => {
            playerCharacter.classList.remove('■■■'); // アニメーション終了
            enemyLife -= 20;                              // 敵のライフを減らす
            ■■■(enemyLifeBar, enemyLife);       // 敵のライフバーを更新
            ■■■();                              // ゲームオーバーをチェック
            ■■■();                                   // 次の単語へ
        }, 1000);
    }

    // 敵がプレイヤーに攻撃する関数
    function enemyAttacks() {
        enemyCharacter.classList.add('attacking'); // 攻撃アニメーション
        setTimeout(() => {
            enemyCharacter.classList.remove('■■■'); // アニメーション終了
            ■■■ -= 20;                             // プレイヤーのライフを減らす
            ■■■(playerLifeBar, playerLife);     // プレイヤーのライフバーを更新
            ■■■();                              // ゲームオーバーをチェック
        }, 1000);
    }

    // ライフバーを更新する関数
    function updateLifeBar(lifeBar, life) {
        lifeBar.style.width = `${life}%`; // ライフバーの幅を更新
        lifeBar.querySelector('.lifeText').textContent = life; // ライフ値を更新
    }

    // ゲームオーバーをチェックする関数
    function checkGameOver() {
        if (playerLife <= 0) {
            gameOver("■■■"); // プレイヤーの負け
        } else if (enemyLife <= 0) {
            gameOver("■■■"); // プレイヤーの勝ち
        }
    }

    // ゲームオーバー時の処理を行う関数
    function gameOver(result) {
        resultDisplay.textContent = ■■■; // 結果を表示
        clearTimeout(■■■);                // タイマーを停止
    }

    // タイマーを開始する関数
    function startTimer() {
        clearTimeout(timer); // 既存のタイマーをクリア
        timer = setTimeout(() => {
            ■■■();   // 敵が攻撃
            ■■■();       // 次の単語へ
        }, 10000);           // 10秒後に実行(入力の制限時間)
    }

    // イベントリスナーの設定
    startButton.addEventListener('click', startGame);
    typingInput.addEventListener('input', checkInput);
});

まとめ

Step2の詳細と進め方を紹介しました。

次はStep3「装飾して完成させよう」です。

次回もお楽しみに!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA