游戏规则
这个游戏考验你的口算能力,你需要在规定时间内输入口算题的答案,然后才能进入下一题;如果超时,游戏失败。
游戏结束以后会显示你的答对题目数,你可以不断挑战自己的记录!
先放上我的代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class puzzle : MonoBehaviour {
// these two is used to make time bar
public Texture progressBackground; // green background
public Texture progressFrontground; // red 'frontground'
private int mode; // 1->introduction, 2->playing, 3->gameover
private float start_time;
private float answer_time;
private int operand1;
private int operand2;
private int _operator; // 0->+, 1->-, 2->*, 3->/
private int answer;
private GUIStyle question_style;
private string user_answer;
private int count;
// Use this for initialization
void Start () {
mode = 1;
question_style = new GUIStyle ();
question_style.fontSize = 50;
}
void start_game() {
mode = 2;
start_time = Time.time;
answer_time = 10F;
user_answer = "";
generate_question ();
count = 0;
}
void OnGUI() {
if (mode == 1) {
page_introduction ();
} else if (mode == 2) {
page_game ();
} else if (mode == 3) {
page_gameover ();
}
}
void page_introduction() {
GUI.Label (new Rect (80, 20, 400, 60), "You should input the answer before time run out!");
if (GUI.Button (new Rect (80, 60, 150, 50), "Start")) {
start_game ();
}
}
void page_game() {
if (Time.time - start_time > answer_time) {
mode = 3;
}
if (user_answer == answer + "") { // right answer
start_time = Time.time;
generate_question ();
user_answer = "";
count++;
}
draw_time_bar ((Time.time - start_time) / answer_time);
draw_question ();
if (GUI.GetNameOfFocusedControl () == string.Empty) {
GUI.FocusControl ("User_answer");
GUI.SetNextControlName ("User_answer");
}
draw_text_area ();
}
void page_gameover() {
GUI.Label (new Rect (80, 20, 400, 60), "You lose!\nYou get "+count+" right");
if (GUI.Button (new Rect (80, 60, 150, 50), "Restart")) {
start_game ();
}
}
// // // // // // // // // utility function // // // // // // // // //
void generate_question() {
_operator = Mathf.FloorToInt(Random.value * 4);
if (_operator == 0) { // +
operand1 = Mathf.FloorToInt(Random.Range(-100F , 100F));
operand2 = Mathf.FloorToInt(Random.Range(-100F , 100F));
answer = operand1 + operand2;
}
else if (_operator == 1) { // -
operand1 = Mathf.FloorToInt(Random.Range(-100F , 100F));
operand2 = Mathf.FloorToInt(Random.Range(-100F , 100F));
answer = operand1 - operand2;
}
else if (_operator == 2) { // *
operand1 = Mathf.FloorToInt(Random.Range(-10F , 10F));
operand2 = Mathf.FloorToInt(Random.Range(-100F , 100F));
answer = operand1 * operand2;
}
else if (_operator == 3) { // /
operand2 = Mathf.FloorToInt(Random.Range(-100F , 100F));
while (operand2 == 0) {
operand2 = Mathf.FloorToInt(Random.Range(-100F , 100F));
}
answer = Mathf.FloorToInt(Random.Range(-10F , 10F));
operand1 = operand2 * answer;
}
//Debug.Log (operand1 + " " + _operator + " " + operand2);
}
void draw_time_bar(float percent) {
GUI.DrawTexture(new Rect(100, 10, 260, 10), progressBackground);
GUI.DrawTexture(new Rect(100, 10, 260*percent, 10), progressFrontground);
}
void draw_question() {
string question = "";
question += operand1;
question += " ";
if (_operator == 0) { // +
question += "+";
}
else if (_operator == 1) { // -
question += "-";
}
else if (_operator == 2) { // *
question += "*";
}
else if (_operator == 3) { // /
question += "/";
}
question += " ";
question += operand2;
question += " = ?";
GUI.Label(new Rect(100, 40, 400, 300), question, question_style);
}
void draw_text_area() {
user_answer = GUI.TextField(new Rect(170, 100, 80, 20), user_answer, 200);
}
}
使用方法:这是一个2D项目,我的所有代码就是这个C#的Script,挂载在camera上面。progressBackground和progressFrontground是两个图片资源(asset),我们自己做一张红色和一张绿色的图片加入Asset,然后将这两个资源拖到Inspector的对应栏目里面,就可以运行了。
下面我来解释我的代码:
void Start () {
mode = 1;
question_style = new GUIStyle ();
question_style.fontSize = 50;
}
mode是一个私有int变量,表示现在的游戏状态,1->introduction,2->playing,3->gameover。question_style是一个GUIStyle类型的变量,我们后面用它来控制GUI.Label的样式。Start ()只在程序启动的时候执行一次。
GUI.Label是一个GUI组件,用来显示文字、图片等内容。我们等一下会解释它的使用。
void start_game() {
mode = 2;
start_time = Time.time;
answer_time = 10F;
user_answer = "";
generate_question ();
count = 0;
}
start_game()将在我们每次点击Start、Restart按钮之后执行,用来将一些变量设成游戏初始状态。generate_question ()用来生成一个新的题目。题目信息将保存在
private int operand1;
private int operand2;
private int _operator; // 0->+, 1->-, 2->*, 3->/
private int answer;
这四个变量中。
我们将start_time设成了现在的时间,answer_time 表示时间限制,在这里是10秒,我们等一下会用这两个变量来计算时间的进度、检查是否超时。count 表示已经答对的题目数,我们要将它初始化为0。
Time.time返回从游戏程序启动到现在过了多少秒。
void OnGUI() {
if (mode == 1) {
page_introduction ();
} else if (mode == 2) {
page_game ();
} else if (mode == 3) {
page_gameover ();
}
}
OnGUI()函数很简单,因为我们将画出页面的工作封装在了这3个函数中。
OnGUI()函数每一帧调用一次,用来画出这一帧要显示的界面,在上一帧OnGUI画出的界面不会保留到下一帧,因此游戏程序在不断地清除、画图、清除、画图。。。
我们来看看page_introduction()是怎么画出介绍页面的
void page_introduction() {
GUI.Label (new Rect (80, 20, 400, 60), "You should input the answer before time run out!");
if (GUI.Button (new Rect (80, 60, 150, 50), "Start")) {
start_game ();
}
}
在这里我们画出了一个Label控件,new Rect (80, 20, 400, 60)这个参数表示:这个控件距离上边80,距离左边20,宽400,高60。
GUI.Button (new Rect (80, 60, 150, 50)
创建了一个Button控件,注意这个表达式返回的是这个button在这一帧是否被点击(bool),所以每次我们一点击Start按钮就会调用start_game(),开始游戏。
接下来我们看看要怎么画出口算进行时的界面:
void page_game() {
if (Time.time - start_time > answer_time) {
mode = 3;
}
if (user_answer == answer + "") { // right answer
start_time = Time.time;
generate_question ();
user_answer = "";
count++;
}
draw_time_bar ((Time.time - start_time) / answer_time);
draw_question ();
if (GUI.GetNameOfFocusedControl () == string.Empty) {
GUI.FocusControl ("User_answer");
GUI.SetNextControlName ("User_answer");
}
draw_text_area ();
}
第一个if判断是否超时。第二个if判断是否已经输入正确答案(user_answer表示目前的输入,answer + ""
巧妙地将answer从int转变为了string类型),如果输入正确则将进入下一题(重新生成题目、将输入清空、增加已答题数量并将题目开始时间设为现在)。
draw_time_bar用来画出一个进度条,传进去的参数其实就是一个0~1地小数,表示时间已经进行了百分之几。
draw_question用来将题目画出来。
下一个if语句比较难理解,它的作用是让输入框自动聚焦(也就是你玩游戏的时候不需要用鼠标去点输入框,直接输入就行了)。SetNextControlName给下一个产生的控件定了一个名字User_answer
(我们将在最后一句draw_text_area ();
中画出一个输入框)。FocusControl就将光标聚焦到这个输入框上。
因为OnGUI()函数会不断执行,所以这两句话的顺序无关紧要,也就产生一帧的差别。
GUI.GetNameOfFocusedControl () == string.Empty
这个判断语句的意思是只在光标没有聚焦的时候执行下面的代码块,用来增加一点性能,大大减少了这段代码的执行次数。
draw_text_area画出了一个输入框。
page_gameover画出gameover页面的方式与之前的page_introduction类似,我就不介绍了。
接下来介绍我们之前调用的一些工具函数是怎么实现的
void generate_question() {
_operator = Mathf.FloorToInt(Random.value * 4);
if (_operator == 0) { // +
operand1 = Mathf.FloorToInt(Random.Range(-100F , 100F));
operand2 = Mathf.FloorToInt(Random.Range(-100F , 100F));
answer = operand1 + operand2;
}
else if (_operator == 1) { // -
operand1 = Mathf.FloorToInt(Random.Range(-100F , 100F));
operand2 = Mathf.FloorToInt(Random.Range(-100F , 100F));
answer = operand1 - operand2;
}
else if (_operator == 2) { // *
operand1 = Mathf.FloorToInt(Random.Range(-10F , 10F));
operand2 = Mathf.FloorToInt(Random.Range(-100F , 100F));
answer = operand1 * operand2;
}
else if (_operator == 3) { // /
operand2 = Mathf.FloorToInt(Random.Range(-100F , 100F));
while (operand2 == 0) {
operand2 = Mathf.FloorToInt(Random.Range(-100F , 100F));
}
answer = Mathf.FloorToInt(Random.Range(-10F , 10F));
operand1 = operand2 * answer;
}
//Debug.Log (operand1 + " " + _operator + " " + operand2);
}
我们先随机产生了一个操作符(_operator分别用0123来表示加减乘除)。然后我们产生2个操作数,这里我限制了操作数的范围,控制了一下口算难度(否则蹦出一个345*674那就不用玩了!)。注意我们如果产生除法题目的方法,先产生operand2和answer,再相乘得到operand1,这样可以保证题目都是整数。
draw_time_bar用两句话画出了时间进度条!
void draw_time_bar(float percent) {
GUI.DrawTexture(new Rect(100, 10, 260, 10), progressBackground);
GUI.DrawTexture(new Rect(100, 10, 260*percent, 10), progressFrontground);
}
GUI.DrawTexture用来画出一个矩形内容框
draw_question主要是将几个题目数字转化成了一个string,然后在GUI.Label中显示:
void draw_question() {
string question = "";
question += operand1;
question += " ";
if (_operator == 0) { // +
question += "+";
}
else if (_operator == 1) { // -
question += "-";
}
else if (_operator == 2) { // *
question += "*";
}
else if (_operator == 3) { // /
question += "/";
}
question += " ";
question += operand2;
question += " = ?";
GUI.Label(new Rect(100, 40, 400, 300), question, question_style);
}
draw_text_area用来画出输入框。GUI.TextField返回的是在这一帧输入框的内容,我们又将它赋值给了user_answer,注意user_answer为什么在这句话中出现2次,这样才能让user_answer一直是我们的输入。
void draw_text_area() {
user_answer = GUI.TextField(new Rect(170, 100, 80, 20), user_answer, 200);
}
你们还可以在原有代码的基础下做出一些改进:
- 让用户可以选择难度(难度可以通过答题时间、操作数范围来控制)
- 目前的难度波动还是有点大,比如有时候乘法会有点难,如何降低这个难度?
- 做一个排行榜!展示你的历史最高纪录!
- 用一个文件放所有代码太长了,能不能将工具函数放到另一个script中?