4人チームで作るWinFormsクイズアプリ(完全チュートリアル)

はじめに

チーム開発の練習題材として最適なのが「クイズアプリ」です。

本記事では、GitHub Desktopを使った最小運用ルールと、4人での役割分担を取り入れながら、WinFormsでクイズアプリを完成させるチュートリアルを紹介します。


Step 1. プロジェクト準備

  1. リーダーが新規WinFormsアプリを作成
    • プロジェクト名:TeamQuizApp
    • フォームに配置するUI:
      • Label(問題文表示用、名前:lblQuestion)
      • Button ×4(選択肢、名前:btnAnswer1 ~ btnAnswer4)
      • ListBox(解答ログ、名前:listBoxLog)
  2. リポジトリ作成と共有
    • GitHub Desktopでリポジトリを作成し、初期Push。
    • メンバー全員がCloneして開発スタート。

Step 2. CSVファイルの用意

プロジェクト直下に questions.csv を作成し、プロパティで「出力ディレクトリにコピー → 常にコピー」に設定します。

questions.csv のサンプル

question,choice1,choice2,choice3,choice4,correctIndex
日本の首都はどこ?,大阪,東京,名古屋,札幌,1
C#で文字列を扱う型は?,int,string,bool,char,1
Unityでシーン切り替えに使うメソッドは?,LoadScene,ChangeScene,OpenScene,StartScene,0

Step 3. チームでの役割分担

リーダー(L)

  • プロジェクト作成・UI配置
  • Form1 にイベントハンドラを空実装
  • 各メンバーのコードをレビュー&統合

担当A:問題読み込み

  • QuestionLoader.cs を作成
  • CSVから問題を読み込む処理を実装

担当B:解答判定

  • AnswerChecker.cs を作成
  • 正解/不正解を判定する処理を実装

担当C:スコア管理

  • ScoreManager.cs を作成
  • 正解数や正答率を管理

担当D:UI更新

  • UiUpdater.cs を作成
  • 問題文・選択肢の表示や、ログ出力を担当

Step 4. コード実装

QuestionLoader.cs(担当A)

using System;
using System.Collections.Generic;
using System.IO;

namespace TeamQuizApp
{
    public class Question
    {
        public string Text { get; set; } = "";
        public string[] Choices { get; set; } = new string[4];
        public int CorrectIndex { get; set; }
    }

    public class QuestionLoader
    {
        private readonly List<Question> _questions = new List<Question>();
        private readonly Random _rand = new Random();

        public QuestionLoader(string path = "questions.csv")
        {
            foreach (var line in File.ReadAllLines(path))
            {
                if (line.StartsWith("question")) continue;
                var cols = line.Split(',');
                _questions.Add(new Question
                {
                    Text = cols[0],
                    Choices = new[] { cols[1], cols[2], cols[3], cols[4] },
                    CorrectIndex = int.Parse(cols[5])
                });
            }
        }

        public Question GetRandomQuestion()
        {
            int idx = _rand.Next(_questions.Count);
            return _questions[idx];
        }
    }
}

AnswerChecker.cs(担当B)

namespace TeamQuizApp
{
    public class AnswerChecker
    {
        public bool CheckAnswer(Question q, int selectedIndex)
        {
            return q.CorrectIndex == selectedIndex;
        }
    }
}

ScoreManager.cs(担当C)

namespace TeamQuizApp
{
    public class ScoreManager
    {
        public int CorrectCount { get; private set; }
        public int TotalCount { get; private set; }

        public void Record(bool isCorrect)
        {
            TotalCount++;
            if (isCorrect) CorrectCount++;
        }

        public string GetResult()
        {
            return $"正解数: {CorrectCount} / {TotalCount} (正答率 {((double)CorrectCount / TotalCount * 100):F1}%)";
        }
    }
}

UiUpdater.cs(担当D)

using System.Windows.Forms;

namespace TeamQuizApp
{
    public class UiUpdater
    {
        private readonly Label _lblQuestion;
        private readonly Button[] _buttons;
        private readonly ListBox _log;

        public UiUpdater(Label lblQuestion, Button[] buttons, ListBox log)
        {
            _lblQuestion = lblQuestion;
            _buttons = buttons;
            _log = log;
        }

        public void ShowQuestion(Question q)
        {
            _lblQuestion.Text = q.Text;
            for (int i = 0; i < 4; i++)
            {
                _buttons[i].Text = q.Choices[i];
            }
        }

        public void LogResult(string msg)
        {
            _log.Items.Add(msg);
        }
    }
}

Form1.cs(リーダー統合)

using System;
using System.Windows.Forms;

namespace TeamQuizApp
{
    public partial class Form1 : Form
    {
        private readonly QuestionLoader loader;
        private readonly AnswerChecker checker;
        private readonly ScoreManager score;
        private readonly UiUpdater ui;

        private Question current;

        public Form1()
        {
            InitializeComponent();

            loader = new QuestionLoader();
            checker = new AnswerChecker();
            score = new ScoreManager();
            ui = new UiUpdater(lblQuestion,
                new[] { btnAnswer1, btnAnswer2, btnAnswer3, btnAnswer4 },
                listBoxLog);

            LoadNextQuestion();
        }

        private void btnAnswer_Click(object sender, EventArgs e)
        {
            var btn = sender as Button;
            int index = Array.IndexOf(new[] { btnAnswer1, btnAnswer2, btnAnswer3, btnAnswer4 }, btn);

            bool result = checker.CheckAnswer(current, index);
            score.Record(result);

            ui.LogResult(result ? "正解!" : "不正解...");
            ui.LogResult(score.GetResult());

            LoadNextQuestion();
        }

        private void LoadNextQuestion()
        {
            current = loader.GetRandomQuestion();
            ui.ShowQuestion(current);
        }
    }
}

すべての回答ボタンの Click イベントを btnAnswer_Click に設定しておきましょう。


Step 5. GitHub Desktopでの運用ルール

  • 全員が徹底すべき流れは 「Pull → 編集 → Commit → Push」
  • 自分の担当クラス以外は触らない
  • コミットメッセージ例:
    • Add QuestionLoader.cs (担当A)
    • Implement AnswerChecker.cs (担当B)

これでコンフリクトは最小限に抑えられます。


まとめ

  • データは CSVファイル、コードは クラスごとに分担 → 競合を回避できる
  • GitHub Desktopの最小運用ルールを守れば、初心者でも安全にチーム開発可能
  • 各クラスが責務を持つため、オブジェクト指向の理解にもつながる

👉 これで「4人チーム開発チュートリアル」が完成しました。


ご希望なら、これを 授業配布用PDF に整形(見出し・図表入り)することもできますが、作成しますか?

訪問数 5 回, 今日の訪問数 5回