一、简介
该游戏是使用unity编写的鼠标打飞碟游戏。飞碟会从屏幕外的多个方向飞入屏幕中,玩家只要用鼠标点击飞行中的飞碟,即可将其击落(对象消失),并因此得到积分;对于每一只飞碟而言,都有自己的颜色、大小和速度,击落后的得分也会根据这三个属性值的不同而不同。该游戏一共有多个回合(round),且游戏的难度也会随着回合数的增加而增加。
游戏需求:
- 游戏有 n 个 round,每个 round 都包括10 次 trial;
- 每个 trial 的飞碟的色彩、大小、发射位置、速度、角度、同时出现的个数都可能不同。它们由该 round 的 ruler 控制;
- 每个 trial 的飞碟有随机性,总体难度随 round 上升;
- 鼠标点中得分,得分规则按色彩、大小、速度不同计算,规则可自由设定。
在该案例中使用对象池技术实现对游戏对象的创建与回收。
游戏视频
简单的鼠标打飞碟(Hit UFO)游戏--unity
二、对象池原理及游戏框架
1. 对象池原理
对象池是一种常用的设计模式,其目的是为了在需要创建和销毁大量相似对象的场景中提高性能。对象池维护着一组可重用的对象,在需要使用时从池中获取对象,并在使用完成后将其放回池中,而不是每次都创建新对象和销毁旧对象。使用对象池操作可以减少创建对象和销毁对象带来的高成本。
对象池设计主要包含两个部分:
1. 对象池的初始化:在程序启动时,创建一定数量的对象实例,并将它们保存到一个列表中,这些对象被称为“池对象”。
2. 对象池的使用:当需要一个对象时,从对象池(列表)中取出一个可用的池对象,完成相应操作后再将其返回池中。如果池中没有可用的对象,则可以创建新的对象加入池中。
2. 游戏框架
该游戏使用 工厂方法 + 单实例 + 对象池 的设计方法,其UML图如下:
- RoundController类是该游戏的场景控制器,用于控制飞碟各个飞行回合(round)的运行,是一个单实例类。(类似于牧师与魔鬼中的FirstController)
- DiskFactory 类是一个单实例类,用前面场景单实例RoundController创建。
- DiskFactory 类中维护了对象池,有工厂方法 GetDisk 产生飞碟,有回收方法 FreeDisk
- ScoreRecorder类作为“记分员”,用于统计玩家的得分。
- 游戏对象disk拥有一个组件用于保存自己的多个属性(后面介绍),同时每次创建对象时从游戏预制中获取对象。
3. 对象池实现的伪代码
getDisk(ruler)
BEGIN
IF (free list has disk) THEN
a_disk = remove one from list
ELSE
a_disk = clone from Prefabs
ENDIF
Set DiskData of a_disk with the ruler
Add a_disk to used list
Return a_disk
END
FreeDisk(disk)
BEGIN
Find disk in used list
IF (not found) THEN THROW exception
Move disk from used to free list
END
三、游戏设计
1. 游戏对象
该游戏只有一种游戏对象,即飞碟(UFO)。飞碟有三种属性值,颜色、大小和速度。这三种属性值每种都有三种可能的取值。属性不同飞碟最后的得分也会不一样。对于颜色而言,黄色为3分,红色2分,蓝色1分;对于大小而言,小的3分,中2分,大1分;对于速度而言,快3分,中2分,慢1分。这三种属性值对于得分的贡献是线性的,因此飞碟得分的取值为3分(蓝色大体积慢速)到9分(黄色小体积快速)。
每个飞碟的发射位置也是不一样的。一共有三个方向,分别是屏幕的左边、中间和右边。对于左右两边而言,可能在随机的高度发射。
2. 文件组织形式:
Assets中目录如下:
Resources中包含了游戏对象的材质和预制:
Scenes中包含了游戏的场景:
其中场景中除了主相机、灯光外,只有一个空的、挂载了场景控制器的空对象。
Scripts中包含了游戏的脚本,该脚本的组织形式采用了MVC框架和动作分离的框架。其中Actions中包含了动作相关的代码(即飞碟移动),Controllers包含了场景控制相关的代码,Views中包含了图形用户交互界面的代码:
四、代码介绍
1. 动作部分Actions
采用了动作分离的设计方法:
(下图为动作分离的示例,并不完全适用于该游戏)
在该游戏中,没有了上述的CCSquenceAction类和CCMoveToAction类,取而代之的是飞碟飞行的动作CCUFOAction。
(1)回调接口ISSCallback
包含了一个枚举动作事件类型和一个回调函数。作为一个接口,由动作对象实现并用于通信:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum SSActionEventType:int {Started, Completed} // 枚举动作事件类型
public interface ISSCallback
{
//回调函数
void SSActionEvent(SSAction source,
SSActionEventType events = SSActionEventType.Completed,
int intParam = 0,
string strParam = null,
Object objectParam = null);
}
(2)动作基类SSAction
动作的基类,所有动作继承于此:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSAction : ScriptableObject
{
public bool enable = true;
public bool destroy = false;
public GameObject gameObject { get; set;}
public Transform transform {get; set;}
public ISSCallback callback {get; set;}
protected SSAction() {}
public virtual void Start()
{
throw new System.NotImplementedException();
}
public virtual void Update()
{
throw new System.NotImplementedException();
}
}
(3)具体动作CCUFOAction
该类是飞碟的具体运动过程,作为一个组件加入到飞碟对象中。该类继承自动作基类,同时还定义了一些与飞碟飞行有关的参数值。对于飞碟的运动轨迹,这里使用了运动学的公式,使得飞碟的飞行轨迹呈现抛物线的形式。其中, (为了避免下落太快,这里取g=6)。该类有个GetSSAction方法用于获取当前的动作交给动作管理器处理。Start方法用于参数的初始化,nextPosition输入当前的时间(从飞碟开始飞行算起)后得到下一个移动的位置,Update方法用于判断飞碟是否出界以及改变飞碟的位置。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCUFOAction : SSAction
{
public float speedX;
public float speedY;
public float movedTime;
public Vector3 originPosition;
public static CCUFOAction GetSSAction(float x, float y) {
CCUFOAction action = ScriptableObject.CreateInstance<CCUFOAction>();
action.speedX = x;
action.speedY = y;
return action;
}
Vector3 nextPosition(float time){
Vector3 position;
// 根据抛物线轨迹公式计算位置
position.x = originPosition.x + speedX * time;
position.y = originPosition.y + speedY * time - 0.5f * 6f * time * time;
position.z = originPosition.z;
return position;
}
public override void Start(){
movedTime = 0; // 初始化开始运动的时间
originPosition = this.transform.position; // 初始位置
}
public override void Update()
{
Vector3 vec3 = Camera.main.WorldToScreenPoint (this.transform.position);
// 如果飞碟已经被"销毁" 或者 超出屏幕的一定范围内
if (!this.transform.gameObject.activeSelf || vec3.x < -100 || vec3.x > Camera.main.pixelWidth + 100 || vec3.y < -100 || vec3.y > Camera.main.pixelHeight + 100) { // 超出屏幕范围,销毁
this.destroy = true;
this.callback.SSActionEvent(this);
return;
}
this.transform.position = nextPosition(movedTime);
movedTime += Time.deltaTime;
}
}
(4)动作管理器基类SSActionManager
动作管理器的基类。用于处理动作的添加和删除操作。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSActionManager : MonoBehaviour
{
private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();
private List<SSAction> waitingAdd = new List<SSAction>();
private List<int> waitingDelete = new List<int>();
protected void Start(){
}
protected void Update () {
foreach (SSAction ac in waitingAdd) actions [ac.GetInstanceID ()] = ac;
waitingAdd.Clear ();
foreach (KeyValuePair <int, SSAction> kv in actions) {
SSAction ac = kv.Value;
if (ac.destroy) {
waitingDelete.Add(ac.GetInstanceID()); // 添加到等到删除的列表
} else if (ac.enable) {
ac.Update (); // update action
}
}
foreach (int key in waitingDelete) { // 等待删除的动作
SSAction ac = actions[key];
actions.Remove(key); // 从字典中删除动作
Object.Destroy(ac); // 销毁动作
}
waitingDelete.Clear (); // 等到所有动作执行完毕后,清空等待删除的动作列表
}
// 执行动作
public void RunAction(GameObject gameobject, SSAction action, ISSCallback manager) {
action.gameObject = gameobject; // 设置动作的游戏对象
action.transform = gameobject.transform; // 设置动作的游戏对象的 Transform 组件
action.callback = manager; // 设置动作的回调接口
waitingAdd.Add (action); // 添加到等待执行的动作列表中
action.Start (); // 开始执行动作
}
public int RemainActionCount() { // 剩余动作的数量
return actions.Count;
}
}
(5)动作管理器CCActionManager
在start方法中将自己(即动作管理器)传递给场景控制器,并创建一个单实例的工厂对象。该类实现了ISSCallback的接口函数SSActionEvent,用于将使用过的飞碟回收。在MoveDisk方法中创建一个动作并且执行该动作。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCActionManager : SSActionManager, ISSCallback
{
public RoundController roundCtrl;
public CCUFOAction action;
public DiskFactory factory;
protected new void Start()
{
roundCtrl = (RoundController)SSDirector.getInstance().currentSceneController;
roundCtrl.actionManager = this;
factory = Singleton<DiskFactory>.Instance;
}
public void SSActionEvent(SSAction source, // 使用回调函数,当动作完成时,将该飞碟回收
SSActionEventType events = SSActionEventType.Completed,
int intParam = 0,
string strParam = null,
Object objectParam = null) {
factory.FreeDisk(source.transform.gameObject); // 将使用过的飞碟回收
}
public void MoveDisk(GameObject disk) {
action = CCUFOAction.GetSSAction(disk.GetComponent<DiskAttributes>().speedX, disk.GetComponent<DiskAttributes>().speedY);
RunAction(disk, action, this);
}
}
2. 控制器部分Controllers
使用了MVC框架:
(下图为MVC框架的示例,并不完全适用于该游戏)
在该游戏中,FirstController即场景控制器变成了RoundController,并且不再单独创建游戏对象的类了,因为游戏对象被工厂创建并维护在对象池中。其他部分保持不变。
(1)单实例类SSDirector
继承自System.object,可以获取当前的场景。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSDirector : System.Object
{
private static SSDirector _instance;
public ISceneController currentSceneController {get; set;}
public static SSDirector getInstance() {
if (_instance == null) {
_instance = new SSDirector();
}
return _instance;
}
}
(2)场景控制器接口ISceneController
包含了场景控制器与游戏场景相关的函数,等待由场景控制器RoundController实现。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface ISceneController
{
void LoadResource();
}
(3)场景单实例模板Singleton
运用该模板,可以为每个 MonoBehaviour子类创建一个对象的实例。使用单实例时,只需要将 MonoBehaviour 子类对象挂载任何一个游戏对象上即可,然后在任意位置使用代码Singleton<T>获得该对象。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 场景单实例模板
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
protected static T instance;
public static T Instance {
get {
if (instance == null) {
instance = (T)FindObjectOfType (typeof(T));
if (instance == null) {
Debug.LogError ("An instance of " + typeof(T) +
" is needed in the scene, but there is none.");
}
}
return instance;
}
}
}
// 场景单实例的使用很简单,你仅需要将 MonoBehaviour 子类对象挂载任何一个游戏对象上即可。然后在任意位置使用代码 Singleton<YourMonoType>.Instance 获得该对象。
// 运用模板,可以为每个 MonoBehaviour子类 创建一个对象的实例。
(4)工厂类DiskFactory
该文件中包含了三部分代码(三个类):
第一部分:自定义报错类MyException
定义了一个继承自System.Exception的异常检测类型。当出现异常操作时,会显示报错的信息。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MyException : System.Exception
{
public MyException() { }
public MyException(string message) : base(message) { }
}
第二部分:飞碟对象的属性DiskAttributes
定义了一些与飞碟有关的属性值,在后面作为组件添加到飞碟对象中。
public class DiskAttributes : MonoBehaviour // 飞碟的属性
{
public int score;
public float speedX;
public float speedY;
}
第三部分:飞碟“工厂”类DiskFactory
该工厂具有三个成员变量,random是随机数对象,在后面用于生成随机数来决定飞碟的各个属性;userd是一个列表,用于保存使用过的飞碟对象(对象的回收);free是一个列表,用于保存可以使用的飞碟(对象的创建)。在Start函数中实现上述三个变量的初始化。
该工厂定义了两个重要的函数。对于GetDisk函数,接收回合数作为参数(因为要根据回合数增加难度),然后返回一个游戏对象(即飞碟)。在该函数中,首先从对象池free中取出一个游戏对象(如果没有则从预制中获取新的游戏对象并为其添加属性组件)。然后,根据上面的游戏设计部分,随机设置飞碟对象的颜色、大小、速度。在这里,采用随机分配“档位”(即random.Next(1,4))的设计方法,根据“颜色档位”color_w的不同为飞碟附上颜色,根据“大小档位”size_w的不同为飞碟设置大小(即缩放),根据“速度档位”和回合数(随着回合数增加,速度也会加快)的线性叠加为飞碟设置速度。飞碟的得分则根据这三个档位相加即可。同时,为飞碟设置的发射位置(左边、中间、右边)和飞碟的角度(朝向)。在完成所有的设置后,将该飞碟加入到使用过的列表used,并设置为激活状态。
对于FreeDisk函数,该函数用于释放飞碟对象,在接收一个飞碟对象作为输入后,将其状态设置为不可见,同时将飞碟预制恢复到原来的状态(因为伸缩变化是针对原预制进行的,需要恢复到原来的状态以免飞碟越来越小)。至此,完成了飞碟的释放,将飞碟从used列表中移出(移出前需要检测userd列表是否为空),然后加入回free列表中(代表该对象回到对象池中,可以被再次“创建”使用)。
// 工厂:飞碟管理员
public class DiskFactory : MonoBehaviour
{
System.Random random; // 随机数
List<GameObject> used; // 使用过的飞碟
List<GameObject> free; // 可以被使用的飞碟
void Start()
{
used = new List<GameObject>();
free = new List<GameObject>();
random = new System.Random(); // 初始化随机数
}
void Update(){
}
public GameObject GetDisk(int round) {
GameObject disk;
if (free.Count != 0) {
disk = free[0]; // 从列表中取出一个飞碟
free.Remove(disk);
}
else {
disk = GameObject.Instantiate(Resources.Load("Prefabs/UFO", typeof(GameObject))) as GameObject; // 从预设中创建飞碟
disk.AddComponent<DiskAttributes>();
}
// 每个 trial 的飞碟的色彩、大小、发射位置、速度、角度、同时出现的个数都可能不同。
// 每个 trial 的飞碟有随机性,总体难度随 round 上升;
// 鼠标点中得分,得分规则按色彩、大小、速度不同计算,规则可自由设定。
int color_w = random.Next(1,4); // 颜色有三个档位
int size_w = random.Next(1,4); // 大小有三个档位
int speed_w = random.Next(1,4); // 速度有三个档位
switch(color_w){ // 根据颜色的档位为UFO上色
case 1: // 蓝色档位最低
disk.GetComponent<Renderer>().material.color = Color.blue;
break;
case 2:
disk.GetComponent<Renderer>().material.color = Color.red;
break;
case 3: // 黄色档位最高
disk.GetComponent<Renderer>().material.color = Color.yellow;
break;
}
switch(size_w){ // 根据大小的档位调节UFO的尺寸
case 1: // 档位1尺寸不变
break;
case 2:
disk.transform.localScale = new Vector3(1.8f,0.1f,1.8f);
break;
case 3: // 档位越高,尺寸越小
disk.transform.localScale = new Vector3(1.5f,0.1f,1.5f);
break;
}
// 设置UFO的角度
disk.transform.localEulerAngles = new Vector3(-random.Next(20,40),0,0);
DiskAttributes atbt = disk.GetComponent<DiskAttributes>();
atbt.score = color_w + size_w + speed_w; // 根据颜色、大小、速度来决定分数
atbt.speedX = (speed_w * 3 + round) * 0.5f;
atbt.speedY = (speed_w * 3 + round) * 0.8f;
int dir = random.Next(1,4); // 随机一个方向
switch(dir){
case 1:
disk.transform.Translate(Camera.main.ScreenToWorldPoint(new Vector3(0, random.Next(0,Camera.main.pixelHeight/2), 8)));
break;
case 2:
disk.transform.Translate(Camera.main.ScreenToWorldPoint(new Vector3(Camera.main.pixelWidth, random.Next(0,Camera.main.pixelHeight/2), 8)));
atbt.speedX *= -1; // 从右下角时,方向需要调整
break;
case 3:
disk.transform.Translate(Camera.main.ScreenToWorldPoint(new Vector3(Camera.main.pixelWidth/2, 0, 8)));
atbt.speedX = 0; // 竖直向上飞
break;
}
used.Add(disk); // 标记飞碟为用过
disk.SetActive(true); // 设置为激活状态
return disk;
}
public void FreeDisk(GameObject disk) { // 释放飞碟
disk.SetActive(false);
//将位置和大小恢复到预制的初始状态
disk.transform.position = new Vector3(0, 0,0);
disk.transform.localScale = new Vector3(2f,0.1f,2f);
if (!used.Contains(disk)) {
throw new MyException("尝试从不包含项目的列表中删除该项目。");
}
used.Remove(disk); // 从使用过的飞碟中移除
free.Add(disk); // 添加到等待释放的飞碟中
}
}
(5)计分器ScoreRecorder
该类用于统计获得的分数并将分数信息传送给用户交互对象(UserGUI)显示。该类具有一个变量score用于保存得到的总分,同时创建了一个回合控制对象(场景控制器)roundCtrl和一个用户交互对象userGUI。在start函数中,将当前的计分器对象赋值给场景控制器,同时获得当前的用户交互对象。在record函数中,将单次得分的信息传输给用户交互对象,并且将单词得分累积到总分中,并将总分也传输给用户交互对象。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ScoreRecorder : MonoBehaviour
{
int score;
public RoundController roundCtrl;
public UserGUI userGUI;
void Start()
{
roundCtrl = (RoundController)SSDirector.getInstance().currentSceneController;
roundCtrl.scoreRecorder = this;
userGUI = this.gameObject.GetComponent<UserGUI>();
}
public void Record(GameObject disk) {
int add = disk.GetComponent<DiskAttributes>().score;
userGUI.gameMessage = "+"+add; // 显示单次得分
score += add;
userGUI.score = score;
}
}
(6)回合控制器(场景控制器)RoundController
定义了当前回合数,最大回合数,单个回合时间等变量,同时包含了动作管理器对象、计分器对象、工厂对象、用户交互对象等。start函数中对参数进行了初始化(令round为-1是为了确保游戏池中游戏对象的正常加载)。在update函数中,先确保游戏已经开始,然后调用getHit函数处理鼠标点击事件,调用gameover函数判断游戏是否结束。接着,对于每一个回合,time代表单个回合的时间,actionManager.RemainActionCount()代表剩余的动作数(对应于剩余的飞碟数),直到时间结束并且飞碟数为0时该回合结束。对于每个回合,从工厂创建10个飞碟对象,并给它们添加动作,然后将回合数传递给用户交互界面显示。
在awake函数中,实现回合控制器的初始化,包括了当前场景的创建、为游戏对象添加组件、以及工厂对象、用户交互对象的获取。由于加载预制的任务在工厂对象中完成,因此场景控制器不再需要编写LordResource函数。在gameover函数中,如果回合数大于最大回合数并且所有的飞碟动作执行完毕后,游戏结束,并将结束的信息传递给用户交互对象。在getHit函数中,检测鼠标左键的动作。当鼠标点击飞碟对象后,使用计数器对象获得其分数,并将其状态设置为false(即不可见状态)。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 回合控制器(场景控制器)
public class RoundController : MonoBehaviour, ISceneController, IUserAction
{
int round,maxRound;
float time;
GameObject disk;
DiskFactory factory;
public CCActionManager actionManager;
public ScoreRecorder scoreRecorder;
public UserGUI userGUI;
void Start(){
round = -1;
maxRound = 10;
time = 1.0f;
}
void Update()
{
if (!userGUI.isStart) return; // 等待游戏开始
GetHit(); // 鼠标点击事件
gameOver(); // 判断游戏是否结束
if (round > maxRound) { // 回合结束
return;
}
time -= Time.deltaTime; // 单个回合的时间
if (time <= 0 && actionManager.RemainActionCount() == 0) { // 一个回合结束
//从工厂中得到10个飞碟,为其加上动作
for (int i = 0; i < 10; i++) {
disk = factory.GetDisk(round);
actionManager.MoveDisk(disk);
}
round += 1;
if (round <= maxRound) {
userGUI.round = round; // 更新显示的回合数
}
if(round>0) time = 4.0f;
userGUI.gameMessage = "";
}
}
void Awake() { // 初始化
SSDirector director = SSDirector.getInstance();
director.currentSceneController = this;
director.currentSceneController.LoadResource();
gameObject.AddComponent<UserGUI>(); // 添加用户界面
gameObject.AddComponent<CCActionManager>(); // 添加动作管理器
gameObject.AddComponent<ScoreRecorder>(); // 添加分数记录器
gameObject.AddComponent<DiskFactory>(); // 添加飞碟工厂
factory = Singleton<DiskFactory>.Instance; // 创建工厂单实例
userGUI = gameObject.GetComponent<UserGUI>();
}
public void LoadResource() { // 加载资源
// 加载资源的任务交给工厂
}
public void gameOver(){
if(round > maxRound && actionManager.RemainActionCount() == 0){
userGUI.gameMessage = "游戏结束";
}
}
public void GetHit() { // 鼠标点击事件???
if (Input.GetButtonDown("Fire1")) { // 鼠标左键点击
Camera ca = Camera.main;
Ray ray = ca.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit)) {
scoreRecorder.Record(hit.transform.gameObject);
hit.transform.gameObject.SetActive(false);
}
}
}
}
3. 视图部分Views
(1)用户动作接口IUserAction
包含了场景控制器与动作相关的函数。由于采用了动作分离的方式,因此虽然下面的两个函数由场景控制器实现,但实际调用了动作类CCUFOAction的函数来实现。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IUserAction
{
void gameOver();
void GetHit();
}
(2)用户交互类UserGUI
包含了接收用户输入的按钮以及显示给用户看的信息。在Start函数中对部分变量进行初始化,同时从场景控制器处获得用户动作接口,设置了用于显示的两种字体格式。在OnGUI函数中,根据bool值判断是否开始游戏。没开始游戏时处于主页面MainMenu,会显示游戏标题和“开始游戏”的按钮;当按下该按钮后,切换到游戏界面,显示的得分信息和回合信息,等到游戏结合后显示结束信息和最终游戏得分。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UserGUI : MonoBehaviour
{
public bool isStart; // 是否开始游戏
public int score; // 得分
public int round; // 回合数
public string gameMessage; // 游戏信息
private IUserAction action;
public GUIStyle bigStyle, smallStyle;//自定义字体格式
private int menu_width = Screen.width / 5, menu_height = Screen.width / 10;//主菜单每一个按键的宽度和高度
void Start()
{
isStart = false;
gameMessage = "";
action = SSDirector.getInstance().currentSceneController as IUserAction;
bigStyle = new GUIStyle();
bigStyle.normal.textColor = Color.black;
bigStyle.fontSize = 50;
smallStyle = new GUIStyle();
smallStyle.normal.textColor = Color.black;
smallStyle.fontSize = 30;
}
void OnGUI() {
GUI.skin.button.fontSize = 35;
if(isStart) {
GameStart();
} else {
MainMenu();
}
}
void MainMenu() {
GUI.Label(new Rect(Screen.width / 2 - menu_width * 0.5f - 50, Screen.height * 0.1f, menu_width, menu_height), "打飞碟小游戏", bigStyle);
bool button = GUI.Button(new Rect(Screen.width / 2 - menu_width * 0.5f, Screen.height * 3 / 7, menu_width, menu_height), "开始游戏");
if (button) isStart = true;
}
void GameStart() {
GUI.Label(new Rect(300, 60, 50, 200), gameMessage, bigStyle);
if(gameMessage=="游戏结束"){
GUI.Label(new Rect(150, 120, 50, 200), "你最终的分数是:"+score, bigStyle);
}
GUI.Label(new Rect(0,0,100,50), "分数: " + score, smallStyle);
GUI.Label(new Rect(560,0,100,50), "回合数: " + round, smallStyle);
}
}
至此,所有的代码介绍完毕。