2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

クイズ

Posted at

クイズゲームを作ろう!

今回は、3択クイズゲームを作っていきます!

目次

ステップ① スプレッドシート(CSVスクリプト)でクイズの作成
ステップ② クイズ用のクラスの作成
ステップ③ クイズの表示
ステップ④ 当たり判定
ステップ⑤ 正誤判定
ステップ⑥ 選択肢をランダムに
ステップ⑦ 複数問題の出題
おまけ   複数ステージの実装

ステップ① スプレッドシートでクイズを作成しよう

CSVファイルを用意しよう!

まず、Googleドライブから新しいスプレッドシートを作成します。






















いちばん左のセルから順に問題文、正答、誤答1、誤答2を書き込んでいきましょう。
最後「;(セミコロン)」を入力するのを忘れずに!
保存する時は、【ファイル】→【形式を指定してダウンロード】→【カンマ区切りの値
    (.csv、現在のシート)】でCSVファイルとして自分のPCに保存します。
その際、ファイルの名前を「QuizForStage1」としておきましょう。

UnityプロジェクトにCSVファイルを入れよう!

Unityプロジェクトに戻って、Projectビューの[Create]から新しいフォルダを作成し、
「Resources」という名前にします。
さらに、Resourcesフォルダの中に「CSV」という名前のフォルダを作成し、自分のPC
のファイル/ファインダーから保存したCSVファイルをぽいっとドラッグ&ドロップしま
す。
これで、作問の準備は完了です!   

ステップ② クイズ用のクラスを作ろう!

クイズを入力するためのファイルはできたので、次はUnity上でCSVファイルのデータを使えるようにする準備をしていきます。
Unityプロジェクトを開いて、Projectビューの[Create]から新しいスクリプトを作ります。
スクリプトの名前を「QuizManager」にし、以下のようにコードを書いていきます

これで、クイズクラスの完成です。

ステップ③ クイズを表示しよう!

クイズの準備が一通りできました!
今度はUnityでクイズを1問だけ表示させてみましょう。

まず、Unityプロジェクトを開いて以下3種類を↓の画像のように配置していきましょう。
・問題文を表示するためのテキスト
・選択肢を表示するためのテキスト ×3つ
・選択肢ボタンがわりになるキューブ ×3つ
(キューブは、上から名前を「AnswerWallTop」、「AnswerWallMiddle」、「AnswerWallBottom」としておきます。)

テキスト・オブジェクトの配置が終わったら、次は「CsvManager」というスクリプトを用意するのですが、内容はコピペでOKです(注1)。
Projectビューの中にドラッグ&ドロップで入れておきます。

次に、新しいスクリプトを作成し、名前を「GameManagerScript」にします。
スクリプトを開いて↓のようにコードを書いていきます。

最後にスクリプトで宣言した変数とテキストとを紐付けし、実行してクイズが表示されればOKです。

ステップ④ 当たり判定をしよう!

今回は、”Ray”というコードを使って、タップしたキューブに当たり判定を設定していきます。

まず、「AspectRatioManager」という名前のスクリプトを作り内容をコピぺして(注2)、Projectビューの中にドラッグ&ドロップで入れておきます。

Rayはオブジェクトのタグで当たりを検知するので、選択肢のキューブ3つに「Answer」という名前のタグを作り、タグ付けします。

次に、GameManagerScriptのvoid Update関数内に当たり判定のコードを書いていきます↓

Unityプロジェクトに戻り、実行してみましょう!

ステップ⑤ 正誤判定をしよう!

押した選択肢が反応してくれるようになりました。
今、AnswerWallTopに正答が反映されるようになっています。
そこで、今度は上記の選択肢をタッチすると「正解」とコンソールに表示させてみましょう。

正誤判定をするために、以下のようにコードを追記していきます。

・「answerString」というstring型の変数を宣言(void Start関数の前)
・65行目のanswerStringに正答を保存
・75〜81行目の"AnswerSelected"関数

最後に、各選択肢にRayが当たった時の処理のところで、AnswerSelected関数を呼び出します。

ステップ⑥ 選択肢をランダムにしよう!

今の状態では、一番上の選択肢が正解になってしまっています。今度は、正答をランダムないちに表示する処理を行います。

はじめに、選択肢の表示をランダムにするための「ListExtension」スクリプトを作成し(注3)、UnityプロジェクトのProjectビューに入れておきましょう。

次に、GameMnager上で選択肢テキストを以下のようにシャッフルします
これで選択肢のテキストをランダムに表示できます。

ステップ⑦ 複数の問題を出題しよう!

最後に正誤判定をした後に、次の問題をランダムに出題する、という実装を行います。
GameMangerScriptを開いて、以下のようにコードを加筆・修正して行ってください。

コードの変更については、以下の4点を確認しましょう。

・CreateQuestion関数で、quizListがfor文になっているか
・「currentQuizIndex」変数が宣言されているか
・ShowQuestion関数の、quizList[0]がquizList[i]に変わっているか
・currentQuizIndexのランダム設定ができているか・ShowQuestion関数の引数がcurrentQuizIndexになっているか

これで、クイズゲームの基本システムができました!!

おまけ 複数のステージを実装しよう!

おまけですが、複数ステージを作る時、ステージの数だけシーンを作る必要はありません。

ステップ①を参考に、もう一つのステージ用のCSVファイルを作成し、名前を「QuizForStage2」として作成します。

次に、新しくProjectビューの[Create]からステージ選択用のシーンを作ります。
また、新しいステージにボタンを2つ追加し、片方を押下するとCSVファイルのQuizForStage1が読み込まれ、もう片方を押下するとQuizForStage2が読み込まれるようにしていきます。

シーンができたら、今度は新しいスクリプトを作成し、「MenuManagerScript」と名前をつけ、以下のようにボタンをクリックした時の関数を2つ作ります。

ボタンと関数を紐付けられたら、GameManagerScriptに戻り、以下のようにコードを書き換えます。

以上、複数ステージの実装でした!

コピペファイル一覧

注1. CsvManager

using UnityEngine;
using System.Collections;
using System.IO;
using System.Collections.Generic;
using System.Text;


public class CsvManager
{
 ///


 /// Resourcesフォルダ内のcsvファイルを読み込み、string型2次元配列で返す。
 /// example) CsvManager.ReadTextFile("Commands/BasicCommands.csv")
 /// 
 public static string ReadTextFile (string dataPath)
 {
 string data;
 string textData = OpenTextFile (GetPath () + dataPath);
 if (textData != "ERROR") {
 data = textData;
 } else {
 data = "";
 }

 if (string.IsNullOrEmpty (data)) {
 return null;
 }
 return data;
 }

 public static string[,] ReadCsvFile (string dataPath)
 {
 string data;
 string textData = OpenTextFile (GetPath () + dataPath);
 if (textData != "ERROR") {
 data = textData;
 } else {
 TextAsset textAsset = (TextAsset)Resources.Load (dataPath.Split ('.') [0]);
 data = textAsset.ToString ();
 }

 if (data == null) {
 return null;
 }
 string[] rows = data.Replace ("\ r\n", "\ n").Split ('\ n');
 int dataRows = rows.Length;
 int dataCols = rows [0].Split ("," [0]).Length;
 string[,] csvData = new string[dataRows, dataCols];
 for (int i = 0; i < csvData.GetLength (0); i++) {
 for (int j = 0; j < csvData.GetLength (1); j++) {
 string[] value = rows [i].Split ("," [0]);
 csvData [i, j] = value [j];
 }
 }
 return csvData;
 }


 /// 
 /// Resourcesフォルダ内に、ファイルを書き込む。
 /// 例) csvManager.WriteData("data.csv", dataArray);
 /// 
 public static void WriteData (string dataPath, string[,] newData)
 {
 Debug.Log("[CsvManager.WriteData(string dataPath, string[,] newData)] called");
 string stringData = "";
 for (int i = 0; i < newData.GetLength (0); i++) {
 for (int j = 0; j < newData.GetLength (1); j++) {
 if (j < newData.GetLength (1) - 1) {
 stringData += newData [i, j] + ",";
 } else if (j == newData.GetLength (1) - 1 && i < newData.GetLength (0) - 1) {
 stringData += newData [i, j] + "\ n";
 } else {
 stringData += newData [i, j];
 }
 }
 }

 string[] directries = dataPath.Split ('/');
 for (int i = 0; i < directries.Length-1; i++) {
 string tmpPath = "";
 for (int j = 0; j < i + 1; j++) {
 tmpPath += directries [j] + "/";
 }
 tmpPath = tmpPath.Remove (tmpPath.Length - 1, 1);
 if (!Directory.Exists (GetPath () + tmpPath)) {
 Directory.CreateDirectory (GetPath () + tmpPath);
 }
 }

 string existingString = OpenTextFile (GetPath () + dataPath);
 FileStream fs;
 if (existingString == "ERROR") {
 fs = new FileStream (GetPath () + dataPath, FileMode.CreateNew);
 } else {
 fs = new FileStream (GetPath () + dataPath, FileMode.Create);
 }
 StreamWriter sw = new StreamWriter (fs);
 sw.Write (stringData);
 sw.Flush ();
 sw.Close ();
 }

 public static void WriteData (string dataPath, List newData)
 {
 Debug.Log("[CsvManager.WriteData(string dataPath, List newData)] called");
 int maxLength = 1;
 for (int i = 0; i < newData.Count; i++) {
 if (maxLength < newData [i].Length) {
 maxLength = newData [i].Length;
 }
 }
 string stringData = "";
 for (int i = 0; i < newData.Count; i++) {
 for (int j = 0; j < maxLength; j++) {
 if (j < maxLength - 1) {
 stringData += newData [i] [j] + ",";
 } else if (j == maxLength - 1 && i < newData.Count - 1) {
 stringData += newData [i] [j] + "\ n";
 } else {
 stringData += newData [i] [j];
 }
 }
 }

 string[] directries = dataPath.Split ('/');
 for (int i = 0; i < directries.Length-1; i++) {
 string tmpPath = "";
 for (int j = 0; j < i + 1; j++) {
 tmpPath += directries [j] + "/";
 }
 tmpPath = tmpPath.Remove (tmpPath.Length - 1, 1);
 if (!Directory.Exists (GetPath () + tmpPath)) {
 Directory.CreateDirectory (GetPath () + tmpPath);
 }
 }
 string existingString = OpenTextFile (GetPath () + dataPath);
 FileStream fs;
 if (existingString == "ERROR") {
 fs = new FileStream (GetPath () + dataPath, FileMode.CreateNew);
 } else {
 fs = new FileStream (GetPath () + dataPath, FileMode.Create);
 }
 StreamWriter sw = new StreamWriter (fs);
 sw.Write (stringData);
 sw.Flush ();
 sw.Close ();
 }

 public static void WriteData (string dataPath, string data)
 {
 Debug.Log("[CsvManager.WriteData(string dataPath, string data)] called");
 string[] directries = dataPath.Split ('/');
 for (int i = 0; i < directries.Length-1; i++) {
 string tmpPath = "";
 for (int j = 0; j < i + 1; j++) {
 tmpPath += directries [j] + "/";
 }
 tmpPath = tmpPath.Remove (tmpPath.Length - 1, 1);
 if (!Directory.Exists (GetPath () + tmpPath)) {
 Directory.CreateDirectory (GetPath () + tmpPath);
 }
 }
 string existingString = OpenTextFile (GetPath () + dataPath);
 FileStream fs;
 if (existingString == "ERROR") {
 fs = new FileStream (GetPath () + dataPath, FileMode.CreateNew);
 } else {
 fs = new FileStream (GetPath () + dataPath, FileMode.Create);
 }
 StreamWriter sw = new StreamWriter (fs);
 sw.Write (data);
 sw.Flush ();
 sw.Close ();
 }


 public static string GetPath ()
 {
 #if UNITY_EDITOR
 return Application.dataPath + "/Resources/";
 #elif UNITY_ANDROID
 return Application.persistentDataPath + "/";
 #elif UNITY_IPHONE
 return Application.persistentDataPath + "/";
 #else
 return Application.dataPath + "";
 #endif
 }

 public static string OpenTextFile (string _filePath)
 {
 FileInfo fi = new FileInfo (_filePath);
 string returnSt = "";
 if (fi.Exists) {
 StreamReader sr = new StreamReader (fi.OpenRead (), Encoding.UTF8);
 returnSt = sr.ReadToEnd ();
 sr.Close ();
 } else {
 returnSt = "ERROR";
 }
 return returnSt;
 }
}



注2. AspectRatioManager

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class AspectRatioManager : MonoBehaviour {

 public float x_aspect = 1242f;
 public float y_aspect = 2208f;
 public CanvasScaler[] canvasScaler = new CanvasScaler[1];

 void Awake()
 {
 //Cameraのアスペクト比を設定する
 Camera camera = GetComponent();
 Rect rect = calcAspect(x_aspect, y_aspect);
 camera.rect = rect;

 //Canvasのアスペクト比を設定する
 for (int i = 0; i scale_height)
 {
 rect.x = 0;
 rect.y = (1.0f - scale_height) / 2.0f;
 rect.width = 1.0f;
 rect.height = scale_height;
 }
 else
 {
 float scale_width = 1.0f / scale_height;
 rect.x = (1.0f - scale_width) / 2.0f;
 rect.y = 0.0f;
 rect.width = scale_width;
 rect.height = 1.0f;
 }
 return rect;
 }


 ///


 /// Checks the screen ratio. Return 0 when width, 1 when height
 /// 
 private int CheckScreenRatio(int i){
 if (Screen.width * canvasScaler[i].referenceResolution.y / canvasScaler[i].referenceResolution.x < Screen.height) {
 return 0;
 } else {
 return 1;
 }
 }
}

注3. ListExtension

// ListExtension.cs
// http://kan-kikuchi.hatenablog.com/entry/ListExtension
//
// Created by kan.kikuchi on 2016.04.29.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

///


/// Listの拡張クラス
/// 
public static class ListExtension {

 //=================================================================================
 //重複
 //=================================================================================

 /// 
 /// 重複しないように追加
 /// 
 public static void AddToNotDuplicate(this List list, T t){
 if(list.Contains(t)){
 return;
 }
 list.Add (t);
 }
 
 /// 
 /// 重複を無くす
 /// 
 public static void RemoveDuplicate(this List list){
 List newList = new List();

 foreach (T item in list) {
 newList.AddToNotDuplicate(item);
 }

 list = newList;
 }

 //=================================================================================
 //並び変え
 //=================================================================================

 /// 
 /// ランダムに並び替え
 /// 
 public static List Shuffle(this List list){

 for (int i = 0; i < list.Count; i++) {
 T temp = list[i];
 int randomIndex = Random.Range(0, list.Count);
 list[i] = list[randomIndex];
 list[randomIndex] = temp;
 }

 return list;
 }

 //=================================================================================
 //取得
 //=================================================================================

 /// 
 /// 指定したNoのものを取得し、リストから消す
 /// 
 public static T GetAndRemove(this List list, int targetNo){
 if(list.Count <= targetNo || targetNo < 0){
 Debug.LogError ("リストの範囲を超えています!(ListCount : " + list.Count + ", No : " + targetNo + ")");
 }

 T target = list[targetNo];
 list.Remove (target);
 return target;
 }

 //=================================================================================
 //先入先出
 //=================================================================================

 /// 
 /// 先頭から取り出し、リストから消す
 /// 
 public static T Pop(this List list){
 return list.GetAndRemove(list.Count - 1);
 }

 //=================================================================================
 //後入先出
 //=================================================================================

 /// 
 /// 最後尾から取り出し、リストから消す
 /// 
 public static T Dequeue(this List list){
 return list.GetAndRemove(0);
 }

 //=================================================================================
 //ランダム取得
 //=================================================================================

 /// 
 /// ランダムに取得する
 /// 
 public static T GetAtRandom(this List list){
 if(list.Count == 0){
 Debug.LogError ("リストが空です!");
 }

 return list[Random.Range(0, list.Count)];
 }

 /// 
 /// ランダムに取得し、リストから消す
 /// 
 public static T GetAndRemoveAtRandom(this List list){
 T target = list.GetAtRandom ();
 list.Remove (target);
 return target;
 }

}


2
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?