1 总体设计
1)基本技术路线
运用面向对象的设计理念,设计了病人类,牙医类,候诊椅类,诊疗椅类等,通过对这些类的封装,创立各个类的对象,并调用类的成员函数。
2)总体结构
软件总体结构如图 3-1 所示,包括:界面设计、算法设计、结果显示等。
界面设计方面包括使用 Unity3D 进行场景地图布置以及对病人和医生等人物建模,设置人物属性,设置地图属性等。
算法设计方面主要包括创建多线程进行多道缓冲区协同操作,利用信号量机制进行对临界资源的互斥同步访问。
结果显示方面主要包括界面实时参数显示和对实时数据进行保存。
图 3-1 软件总体结构图
Unity 的 Update 主线程
Loom 类(子线程对 UI 的操作放到主线程的处理队列中)
主控类(maincontrol)
候诊椅(chair),病人(patient),治疗椅各一个类,包含以下几个属性:
两种椅子被占有的 bool,病人主动占有椅子的 bool
候诊椅有一个列表,包含了未被占有的椅子的 GameObject 对象,
人物有一个列表,包含了占有椅子的人物的 GameObject 对象,
线程数组,
计时变量,
多种 mutex,lock,以及一个对诊断椅子的 semaphore 的专用信号量。
可重用的读写文件类
3)模块功能及关系
主要分为病人线程模块、临界资源抢占模块、数据处理模块。
模块之间关系为临界资源抢占模块调用病人线程,线程模块用于临界资源的抢占模块,而数据处理模块是基于其他两个模块之上的。总的来说,各个模块之间互相影响,相辅相成。
具体模块见图 3-2。
图 3-2 功能模块图
5)线程创立
在 Update 里面调用 loom 类的函数可以保留子线程的操作到的消息处理队列中,然后主线程对队列的内容进行处理并且不会中断子线程的状态。
Loom.RunAsync(() =>//子线程在里面写可以保持子线程状态{if (allbeoccupied == true)//如果队列小于 5 证明有一个座位被占了{//Debug.Log(onchairpersons.Count);//抢占开始for (int i = 0; i < onchairpersons.Count; i++){th[i] = new Thread(Refresh2);//线程创建th[i].Start(i); //线程创建if (timer1 > time1){timer1 = 0;//计时器清零}}}});public void Refresh2(object o2){temp = (int)o2;Loom.QueueOnMainThread((parama) =>//操作放到主线程队列,对 Action 进行加锁回调,所以可以返回到在主线程操作 UI{se.WaitOne();//信号量 P 操作//Debug.Log(temp + "抢占成功" + Time.deltaTime);//Debug.Log(onchairpersons.Count + "shuliang");if (onchairpersons.Count > 0)//onchairpersons.Count >-1&&{= onchairpersons[0];//onchairpersons.RemoveAt(0);if (onchairpersons[0].GetComponent<person>().gettwo == false)//没走到 workchair 就走onchairpersons[0].GetComponent<person>().walk2();if (onchairpersons[0].transform.position == GameObject.Find("work_chair").transform.position){onchairpersons[0].GetComponent<person>().gettwo = true;Debug.Log("到达了椅子");//g = onchairpersons[0];}if (onchairpersons[0].GetComponent<person>().gettwo == true && onchairpersons[0].GetComponent<person>().getthreed == false)//向死亡点走路之前的判断{sleepcount += speed;//Debug.LogError(sleepcount);GameObject.Find("Canvas").GetComponent<Power>().mutex = true;if (sleepcount >100){onchairpersons[0].GetComponent<person>().walk3();if (onchairpersons[0].transform.position == GameObject.Find("death").transform.position){Debug.Log("到达了终点");onchairpersons[0].GetComponent<person>().getthreed = true;havedPer--;ch.Add(onchairs[0]);//空椅子 ++onchairpersons.RemoveAt(0);onchairs.RemoveAt(0);per.RemoveAt(0);sleepcount = 0;}}}}elseth[temp].Abort();//子线程中断se.Release(1);//信号量 V 操作}, null);
2 详细设计
1)进程操作函数、原语、API
进程操作函数:
Loom.RunAsync(() =>//进程的状态保持进队列
{
}
Loom.QueueOnMainThread((parama) =>//对action进行加锁回调,在主线程的消息队列操作UI
{});
th[i] = new Thread(Refresh2);//子线程创建
th[i].Start(i);
在 unity 中没有对话框,需要调用 win32API 进行弹出对话框的操作等。
public class Messagebox{
[DllImport("User32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]public static extern int MessageBox(IntPtr handle, String message, String title, int type);}
2)模块内部流程和实现算法
首先是多人对候诊椅的抢占,成功就可以更改状态为,失败继续找座位,人数超过座位数则加到等待队列中。然后再诊断椅状态位为未被使用的时候,所有在椅子上的人对诊断椅子进行抢占,抢占成功的执行走路函数,诊断结束释放后下一组人方可进行抢占。
实现算法:多线程抢占,抢占成功进行步行操作。
Semaphore se = new Semaphore(1,1);//初始允许请求为1,最大请求为1
Loom.QueueOnMainThread((parama) =>
{se.WaitOne();//信号量P操作if (onchairpersons.Count > 0)//onchairpersons.Count >-1&&{= onchairpersons[0];
//onchairpersons.RemoveAt(0);if (onchairpersons[0].GetComponent<person>().gettwo == false)//没走到候诊椅workchair就走onchairpersons[0].GetComponent<person>().walk2();//走路函数if (onchairpersons[0].transform.position == GameObject.Find("work_chair").transform.position){onchairpersons[0].GetComponent<person>().gettwo = true;Debug.Log("到达了诊断椅");}if (onchairpersons[0].GetComponent<person>().gettwo == true && onchairpersons[0].GetComponent<person>().getthreed == false){sleepcount += speed;GameObject.Find("Canvas").GetComponent<Power>().mutex = true;if (sleepcount >100){onchairpersons[0].GetComponent<person>().walk3();if (onchairpersons[0].transform.position == GameObject.Find("death").transform.position){Debug.Log("到达了终点");onchairpersons[0].GetComponent<person>().getthreed = true;havedPer--;//人物个数ch.Add(onchairs[0]);//空椅子++onchairpersons.RemoveAt(0);onchairs.RemoveAt(0);per.RemoveAt(0);sleepcount = 0;}}}}elseth[temp].Abort();se.Release(1);//信号量V操作}, null);
3 编码设计
1)开发环境的设置和建立
Unity3D 开发环境,VS2017 环境,下载 unity 中专用 Visual Studio Tools for Unity
2)程序设计时要注意的事项
在 Unity 中,Update 是每一帧更新的,所以在这个里面设计的代码会被实时刷新到 UI,而且只有这一个入口函数,如果在此写线程代码则会提示:UI 操作只能在主线程调用。此时应该着重注意子线程调用主线程 UI 操作的问题。需要把对 UI 的操作,放到主线程得到 UI 消息处理队列中,进而让主线程去调用。
Awake 函数是在 Start 函数之前运行,也就是游戏的初始化的问题。每次重新加载场景或者重新调用 awake 就可以初始化游戏参数。
3)关键构件/插件的特点和使用
Loom 在工程上的使用,类似于协程,但是他是在子线程的操作。
但是其与协程的区别在于,Loom 里面可以做主线程的工作,但是协程不可以。典型如,事件检测硬件的变化(0 到 1,1 到 0),我平常会定义一个静态的标志位,用来传出变化,但是,用 Loom 的话,不用传出变化,直接可以在里面进行操作。再比如,UI 显示和 3D 物体的形态变化,直接可以 loom 里面写,不用转到主线程,进行操作。
4)主要程序的代码设计及注释
Maincontrol 类:(作用:主控类,包含各种属性状态,比如线程,人的列表,空椅子队列,在椅子上的人的队列,信号量互斥 Semaphore,时间计时变量,以及各种用于互斥的,开关的,人物椅子个数数组的变量等等)
关键代码:using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;
using UnityEngine.UI;
public class maincontrol : MonoBehaviour {public int max_patient=8;public int min_patient=3;public int chair_number;public int havedPer=0;//已经存在的人public Thread[] th=new Thread[9];//测试线程public int create_num;public List<GameObject> per;//人的列表public GameObject[] chairs;//椅子public List<GameObject> ch;//空椅子队列public List<GameObject> ou;//外面排队空队列public List<GameObject> onchairpersons;//在椅子上的人的队列public List<GameObject> onchairs;//在椅子上的人的椅子的队列public bool allbeoccupied = false;Semaphore se = new Semaphore(1,1);//初始允许请求为1,最大请求为1Semaphore se2 = new Semaphore(1, 1);//初始允许请求为1,最大请求为1public GameObject g;//保存中介用来访问三条条路int mutex = 1;int ii = 0;int chansheng=0;//产生private float timer = 0;//计时变量//等候的人public int time = 3;//计时频率private float timer1 = 0;//计时变量//医生public int time1 = 3;//计时频率public string thname;//当前线程的名字public int temp;//线程编号public float sleepcount = 0;public float speed;public bool lock1 = false;public int waitnumber=0;public int waitmax=5;//等待人数的最大值void Awake(){chair_number = GameObject.FindGameObjectsWithTag("chairs").Length;//得到椅子数量chairs = GameObject.FindGameObjectsWithTag("chairs");ch.Clear();for (int i = 0; i < chair_number; i++)//椅子是否占有队列初始化 chair_number{if (chairs[i].GetComponent<chair>().isOccupied() == false)//没有被占的椅子保存{ch.Add(chairs[i]);}}
//Debug.Log(chair_number+"______________________________");}void OnEnable(){}void Start(){}
//public void lthmdo()//less than min do 小于最小该做的
//{
// create_num = Random.Range(1, 5);//max_patient - havedPer
// for (int i = 0; i < create_num; i++)//create_num
// {
// GameObject g = (GameObject)GameObject.Find("Camera").GetComponent<create_patient>().create();
// per.Add(g);//增加人
// //Debug.Log(per.Count+"拥有的人:"+havedPer);
// }
//}public void lthmdo(int num)//less than min do 小于最小该做的{for (int i = 0; i < num; i++)//create_num{GameObject g = (GameObject)GameObject.Find("Camera").GetComponent<create_patient>().create();per.Add(g);//增加人
//Debug.Log(per.Count+"拥有的人:"+havedPer);}}public void onenable(){Awake();}void Update(){if (lock1 == true){
//Debug.Log(chair_number+"QQQQQQQQQQQQQQQQQQQ");speed = GameObject.Find("Slider").GetComponent<Slider>().value + 0.1f;max_patient = GameObject.Find("Canvas").GetComponent<Power>().bottom1;waitmax = GameObject.Find("Canvas").GetComponent<Power>().bottom2;if (ii == 0){lthmdo(5);}ii++;
//if (havedPer < 5)
// lthmdo(create_num);//产生outdo();lthmdo(create_num);//产生for (int i = 0; i < havedPer; i++){
//Debug.Log(havedPer+"、、、、、"+per.Count);if (per[i].GetComponent<person>().nothavein == false){for (int j = 0; j < chair_number; j++){if (per[i].GetComponent<person>().transform.position == GameObject.Find("Camera").GetComponent<maincontrol>().chairs[j].transform.position){GameObject.Find("Camera").GetComponent<maincontrol>().onchairpersons.Add(per[i]);GameObject.Find("Camera").GetComponent<maincontrol>().onchairs.Add(GameObject.Find("Camera").GetComponent<maincontrol>().chairs[j]);per[i].GetComponent<person>().isgetchair = true;per[i].GetComponent<person>().nothavein = true;}}}}
// Refresh();Loom.RunAsync(() =>{if (allbeoccupied == true)//如果队列小于5 证明有一个座位被占了{
//Debug.Log(onchairpersons.Count);
//抢占开始for (int i = 0; i < onchairpersons.Count; i++){th[i] = new Thread(Refresh2);th[i].Start(i);
//timer1 += Time.deltaTime;if (timer1 > time1){
//th[i].Resume();timer1 = 0;//计时器清零}}Loom.QueueOnMainThread((parama) =>{}, null);
//抢占成功的人开始走路}});}}void outdo(){if (waitnumber == 0){if (create_num > ch.Count){waitnumber += create_num - ch.Count;create_num = ch.Count;}}if (waitnumber>0){if (waitnumber > ch.Count){waitnumber -= ch.Count;if (waitnumber + create_num > waitmax){waitnumber = waitmax;create_num = 0;}else{waitnumber += create_num;create_num = 0;}}else{waitnumber = create_num - (ch.Count - waitnumber);create_num = ch.Count - waitnumber;if (waitnumber > waitmax){waitnumber = waitmax;}}}}void LateUpdate(){timer += Time.deltaTime;if (timer > time){create_num = Random.Range(0, max_patient);//max_patient - havedPerDebug.Log("门外等候的人" + create_num);timer = 0;//计时器清零Debug.Log("计时1次");}}public void Refresh2(object o2){temp = (int)o2;
//se.WaitOne(1); //Thread.Sleep(30);Loom.QueueOnMainThread((parama) =>{se.WaitOne();
//thname = "线程编号:{" + temp.ToString() + "}";
//th[temp].Join();
//Debug.Log(temp + "抢占成功" + Time.deltaTime);
//Debug.Log(onchairpersons.Count + "shuliang");if (onchairpersons.Count > 0)//onchairpersons.Count >-1&&{= onchairpersons[0];
//onchairpersons.RemoveAt(0);if (onchairpersons[0].GetComponent<person>().gettwo == false)//没走到workchair就走onchairpersons[0].GetComponent<person>().walk2();if (onchairpersons[0].transform.position == GameObject.Find("work_chair").transform.position){onchairpersons[0].GetComponent<person>().gettwo = true;Debug.Log("到达了椅子");
//g = onchairpersons[0];}if (onchairpersons[0].GetComponent<person>().gettwo == true && onchairpersons[0].GetComponent<person>().getthreed == false){
//Debug.Log("asdasdassssssssssssssssssssssssssssssssssssssssssssss");sleepcount += speed;
//Debug.LogError(sleepcount);GameObject.Find("Canvas").GetComponent<Power>().mutex = true;if (sleepcount >100){onchairpersons[0].GetComponent<person>().walk3();if (onchairpersons[0].transform.position == GameObject.Find("death").transform.position){Debug.Log("到达了终点");onchairpersons[0].GetComponent<person>().getthreed = true;havedPer--;ch.Add(onchairs[0]);//空椅子++onchairpersons.RemoveAt(0);onchairs.RemoveAt(0);per.RemoveAt(0);sleepcount = 0;
// Destroy(g, 0);
//th[temp].Abort();}}}}elseth[temp].Abort();se.Release(1);}, null);
//if (onchairpersons[0].GetComponent<person>().getthreed == true)
//{
// th[temp].Suspend();
//}}public void Refresh(){Loom.QueueOnMainThread((parama) =>{if (mutex == 1){mutex--;for (int i = 0; i < onchairpersons.Count; i++){if (onchairpersons[i] != null){if (onchairpersons[i].GetComponent<person>().gettwo == false)//没走到workchair就走,走到第三个椅子onchairpersons[i].GetComponent<person>().walk2();Debug.Log("开始走");if (onchairpersons[i].transform.position == GameObject.Find("work_chair").transform.position){onchairpersons[i].GetComponent<person>().gettwo = true;Debug.Log("到达了椅子");
//g = onchairpersons[0];}if (onchairpersons[i].GetComponent<person>().gettwo == true && onchairpersons[0].GetComponent<person>().getthreed == false){onchairpersons[i].GetComponent<person>().walk3();if (onchairpersons[i].transform.position == GameObject.Find("death").transform.position){Debug.Log("到达了终点");
//g = onchairpersons[0];onchairpersons[i].GetComponent<person>().getthreed = true;= onchairpersons[i];
//onchairpersons.RemoveAt(i);onchairpersons[i] = null;sleepcount = 0;
//Destroy(g, 0);}}}}mutex++;}else{
//跳过这个for的一个}},null);}
}
LOOM 类:(作用:将子线程中操作 UI 的消息,放入到主线程操作队列中,在 Unity 流程管理中 Update 方法下检查需要回调的 Action 进行加锁回调,确保主线程执行,回调序列本身又作为静态数据保存,在任意线程调用添加)
关键代码:
public static void Initialize()
{if (!initialized){if (!Application.isPlaying)return;initialized = true;var g = new GameObject("Loom");_current = g.AddComponent<Loom>();
# if !ARTIST_BUILDUnityEngine.Object.DontDestroyOnLoad(g);
# endif}
}
public struct NoDelayedQueueItem
{public Action<object> action;public object param;
}
private List<NoDelayedQueueItem> _actions = new List<NoDelayedQueueItem>();
public struct DelayedQueueItem
{public float time;public Action<object> action;public object param;
}
private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
public static void QueueOnMainThread(Action<object> taction, object tparam)
{QueueOnMainThread(taction, tparam, 0f);
}
public static void QueueOnMainThread(Action<object> taction, object tparam, float time)
{if (time != 0){lock (Current._delayed){Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = taction, param = tparam });}}else{lock (Current._actions){Current._actions.Add(new NoDelayedQueueItem { action = taction, param = tparam });}}
}
public static Thread RunAsync(Action a)
{Initialize();while (numThreads >= maxThreads){Thread.Sleep(100);}Interlocked.Increment(ref numThreads);ThreadPool.QueueUserWorkItem(RunAction, a);return null;
}
private static void RunAction(object action)
{try{Action)action)();}catch{}finally{Interlocked.Decrement(ref numThreads);}
}
Message 类:(作用:调用 win32API,进行 messagebox 输出,因为 unity 不包含文本框)
关键代码:
public class Messagebox
{[DllImport("User32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]public static extern int MessageBox(IntPtr handle, String message, String title, int type);
}
主界面类:(主要是动态按钮的生成,比如:动态椅子生成删除,或者操作按钮生成等等)
关键代码:
public void OnGUI()
{
//just to show how to make a button on GUI - this is hardcoded, not dynamicalif (GUI.Button(new Rect(230, 10, 100, 70), "暂停/恢复")){print("暂停/恢复");if (Time.timeScale == 0) Time.timeScale = 1; // pause toggle / 1 = 100%, 0 = 0%else Time.timeScale = 0;
// Time.timeScale = 1;}if (GUI.Button(new Rect(340, 10, 100, 70), "保存记录")){DateTime dt = DateTime.Now;GameObject.Find("Camera").GetComponent<write>().WriteIntoTxt("当前保存时间:"+dt);Messagebox.MessageBox(IntPtr.Zero, "已经保存完毕", "提示", 0);}if (GUI.Button(new Rect(10, 10, 100, 70), "开始")){
//SceneManager.LoadScene(1);GameObject.Find("Camera").GetComponent<maincontrol>().lock1 = true;}if (GUI.Button(new Rect(120, 10, 100, 70), "结束")){SceneManager.LoadScene(0);
//GameObject.Find("Canvas").GetComponent<maincontrol>().lock1 = true;}if (GUI.Button(new Rect(450, 10, 100, 70), "增加椅子")){switch (use){case 0:GameObject Gg = Instantiate(g1, v3[0], Quaternion.identity);Add(Gg);Gg.transform.SetParent(GameObject.Find("chairs").transform);GameObject.Find("Camera").GetComponent<maincontrol>().onenable();use++;break;case 1:GameObject Gg1 = Instantiate(g1, v3[1], Quaternion.identity);Add(Gg1);Gg1.transform.SetParent(GameObject.Find("chairs").transform);GameObject.Find("Camera").GetComponent<maincontrol>().onenable();use++;break;case 2:GameObject Gg2 = Instantiate(g1, v3[2], Quaternion.identity);Add(Gg2);Gg2.transform.SetParent(GameObject.Find("chairs").transform);GameObject.Find("Camera").GetComponent<maincontrol>().onenable();use++;break;case 3:GameObject Gg3 = Instantiate(g1, v3[3], Quaternion.identity);Add(Gg3);Gg3.transform.SetParent(GameObject.Find("chairs").transform);GameObject.Find("Camera").GetComponent<maincontrol>().onenable();use++;break;}}if (GUI.Button(new Rect(560, 10, 100, 70), "删除椅子")){switch (G.Count){case 4:GameObject a = G[0];Destroy(a,0);GameObject.Find("Camera").GetComponent<maincontrol>().onenable();RemoveAt(0);use--;break;case 3:GameObject a1 = G[0];Destroy(a1, 0);GameObject.Find("Camera").GetComponent<maincontrol>().onenable();RemoveAt(0);use--;break;case 2:GameObject a2 = G[0];Destroy(a2, 0);GameObject.Find("Camera").GetComponent<maincontrol>().onenable();RemoveAt(0);use--;break;case 1:Debug.Log("adasd");GameObject a3 = G[0];DestroyImmediate(a3);GameObject.Find("Camera").GetComponent<maincontrol>().onenable();RemoveAt(0);use--;break;}}
}
Create_patient 类:(作用:通过应用预设体,动态的生成人物)
关键代码:
public Object create()//创建人物
{Obj = null;//防止引用上一个prefabs = Resources.LoadAll("person") as Object[];swpan = GameObject.FindGameObjectsWithTag("create");int tempa, tempb;tempa = Random.Range(0, 5);tempb = Random.Range(0, 1);//1个出生点Vector3 vec = new Vector3(0,0.5f,0);Obj =Instantiate(prefabs[tempa],swpan[tempb].transform.position,Quaternion.identity);if (Obj != null){GameObject.Find("Camera").GetComponent<maincontrol>().havedPer += 1;}return Obj;
}
Person 类:(作用:包含三种方式的步行函数(走向候诊椅,走向诊断椅),自身是否拥有椅子,自身速度,和疼痛条等属性)
关键代码:
void Awake()
{gos[0] = GameObject.Find("fir_obj").transform.position;gos[1] = GameObject.Find("sec_obj").transform.position;if (GameObject.Find("Camera").GetComponent<maincontrol>().ch.Count != 0){gos[2] = GameObject.Find("Camera").GetComponent<maincontrol>().ch[0].transform.position;GameObject.Find("Camera").GetComponent<maincontrol>().ch.RemoveAt(0);}bloodbar = Instantiate(Resources.Load("Slider/Slider"),this.transform.position, Quaternion.identity) as GameObject; //生成预设体bloodbar.GetComponent<Slider>().fillRect.transform.GetComponent<Image>().color = new Color(0, 255, 0, 255);
}
// Use this for initialization
void Start () {bloodbar.transform.SetParent(GameObject.Find("Canvas").transform);speedd = Random.Range(0.1f,0.5f);
}
public float count=0;
float speedd;
// Update is called once per frame
void Update () {if (count < 500){count += speedd;bloodbar.GetComponent<Slider>().value = count / 100.0f;}if (isgetchair == false)//是否得到过第一个椅子{walk();}bloodbar.transform.position = transform.position + new Vector3(0, 0.5f, 0);
}
public void walk()
{Vector3 v = (gos[i] - transform.position).normalized;//向量transform.right = v;//朝向目标点des = Vector3.Distance(this.transform.position, gos[i]);transform.localPosition = Vector3.MoveTowards(this.transform.position, gos[i], Time.deltaTime * movespeed);if (this.transform.position == gos[2])//gos[2]是座位{this.GetComponent<Rigidbody2D>().isKinematic = true;//刚体运动学防抖动}if (des < 1f && i < 2){i++;}
}
public void walk2()
{Vector3 v = (GameObject.Find("work_chair").transform.position - transform.position).normalized;//向量transform.right = v;//朝向目标点des = Vector3.Distance(this.transform.position, GameObject.Find("work_chair").transform.position);
//Debug.Log(this.transform.position+"+++++++++++++"+ GameObject.Find("work_chair").transform.position);transform.localPosition = Vector3.MoveTowards(this.transform.position, GameObject.Find("work_chair").transform.position, Time.deltaTime * movespeed);
}
public void walk3()
{Vector3 v = (GameObject.Find("death").transform.position - transform.position).normalized;//向量transform.right = v;//朝向目标点des = Vector3.Distance(this.transform.position, GameObject.Find("death").transform.position);transform.localPosition = Vector3.MoveTowards(this.transform.position, GameObject.Find("death").transform.position, Time.deltaTime * movespeed);
}
}
Chair 类:(作用:是否被占有等属性)
关键代码:
public bool occupied = false;
public GameObject[] person= {null};
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {this.occupied = isOccupied();//每次都判断当前被占用情况
}
public bool freeChair()
{return false;
}
public void occupyChair(GameObject patient)
{
}
public bool isOccupied()//椅子被占,人和椅子的状态都改变
{person = GameObject.FindGameObjectsWithTag("Player");for (int i = 0; i < person.Length; i++){if (this.transform.position == person[i].transform.position){person[i].GetComponent<person>().isgetchair = true;GameObject.Find("Camera").GetComponent<maincontrol>().allbeoccupied = true;//被占一个就改变状态return true;}}return false;
}
Text 类:(作用:输出到界面变化的值)
关键代码:
private Text uiText;
private Text uiText1;
private Text uiText2;
//储存中间值
private string words;
private string words2;
private float timer;
//限制条件,是否可以进行文本的输出
// Use this for initialization
void Start () {uiText = GameObject.Find("Text").GetComponent<Text>();uiText1 = GameObject.Find("Text (1)").GetComponent<Text>();uiText2 = GameObject.Find("Text (2)").GetComponent<Text>();
}
// Update is called once per frame
void Update () {words = "当前病人数量:"+GameObject.Find("Camera").GetComponent<maincontrol>().havedPer+"\n"+ "候诊椅的剩余情况:" + (GameObject.Find("Camera").GetComponent<maincontrol>().ch.Count) +"\n"+ "候诊椅的使用情况:" + (GameObject.Find("Camera").GetComponent<maincontrol>().chair_number - GameObject.Find("Camera").GetComponent<maincontrol>().ch.Count) + "\n" +"当前运行线程编号:" + GameObject.Find("Camera").GetComponent<maincontrol>().temp + "\n" + "阻塞线程个数:" + (GameObject.Find("Camera").GetComponent<maincontrol>().onchairpersons.Count)+"\n"+"当前外面等待人数:"+ GameObject.Find("Camera").GetComponent<maincontrol>().waitnumber;if(words!=words2)GameObject.Find("Camera").GetComponent<write>().WriteIntoTxt(words+"游戏时间"+Time.deltaTime);uiText1.text = "随机生成的最大人数:" + GameObject.Find("Canvas").GetComponent<Power>().bottom1;uiText2.text = "室外等待最大人数:" + GameObject.Find("Canvas").GetComponent<Power>().bottom2;uiText.text = words;words2 = words;
}
Write 类:(作用:写文件的作用)
关键代码:
public void WriteIntoTxt(string message)
{FileInfo file = new FileInfo(Application.dataPath + "/mytxt.txt");if (!file.Exists){writer = file.CreateText();}else{writer = file.AppendText();}writer.WriteLine(message);writer.Flush();writer.Dispose();writer.Close();
}
Power 类:(作用:包含各种进度条(人物头上的疼痛长条,以及牙医身上的工作圆条))
关键代码:
public float speed;
public float count;
public bool mutex = false;//不用圆盘
public int bottom1;
public int bottom2;//底下的第一条,第二条
void Awake()
{
// gg.GetComponent<Slider>().fillRect.transform.GetComponent<Image>().color = new Color(255, 0, 0, 255);
//gg.SetParent(GameObject.Find("Canvas").transform);
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {bottom1 = (int)(GameObject.Find("Slider (1)").GetComponent<Slider>().value * 10);bottom2 = (int)(GameObject.Find("Slider (2)").GetComponent<Slider>().value * 5);Debug.Log(bottom1+"_________________________");
//speed= GameObject.Find("Slider").GetComponent<Slider>().value + 0.1f;count = GameObject.Find("Camera").GetComponent<maincontrol>().sleepcount;if (mutex == true){if (count <= 100){
//count += speed;
//Debug.Log(count);GameObject.Find("Image").GetComponent<Image>().fillAmount = count / 100.0f;GameObject.Find("Text (3)").GetComponent<Text>().text = ((int)count).ToString() + "%";}if (count >= 99){GameObject.Find("Image").GetComponent<Image>().color = new Color(255, 0, 0, 255);}if (count > 100){
//count = 0;GameObject.Find("Image").GetComponent<Image>().color = new Color(0, 255, 0, 255);GameObject.Find("Text (3)").GetComponent<Text>().text = "0%";GameObject.Find("Image").GetComponent<Image>().fillAmount = 0;mutex = false;}}
4 测试时出现的问题及其解决方法
1)Unity 本不支持多线程
因为 unity 制作的游戏是以单线程和协程的方式,以时间动画帧的形式更新运行,所以在 UI 采用多线程是不支持的,但是即使需要多线程的,也只是在自动寻路,比如 Star A 算法中,或者需要后台网络通信的,比如需要下载图片,下载文件等等的时候需要。更因为在 unity 中每一帧都要调用 update,他会在上一帧没解决的问题会被覆盖中断,然后下一个 update 里面会重新执行,对多线程状态的保存和更新会受到极大的困扰,在 Unity 的 update 里面加 UI 的多线程无疑使难上加难
可以在多线程运行的内容分别是
-
变量(都能指向相同的内存地址)都是共享的
-
不是 UnityEngine 的 API 能在分线程运行
-
UnityEngine 定义的基本结构(int,float,Struct 定义的数据类型)可以在分线程计算,如 Vector3(Struct)可以 , 但 Texture2d(class,根父类为 Object)不可以。
-
UnityEngine 定义的基本类型的函数可以在分线程运行
但是如何在子线程调用主线程 UI 呢?就需要引用 loom 类对 UI 操作的保存处理。
经常犯的错误无疑使对各种共享数据的处理,此时应该加入信号量和各种状态位的判断,防止其他操作在此操作未使用完之前更改信息。
2)子线程无法调用主线程的 UI 并且在 Update 里面会更改这一帧子线程的状态
解决方法:
我们只需要引用 loom 类的两个函数:RunAsync(Action)和 QueueOnMainThread(Action, [optional] float time) 就可以轻松实现一个函数的两段代码在 C#线程和 Unity 的主线程中交叉运行。原理也很简单:用线程池去运行 RunAsync(Action)的函数,在 Update 中运行 QueueOnMainThread(Acition, [optional] float time)传入的函数。将子线程处理 UI 的操作放入主线程处理操作的队列中,并且保持此子线程的状态不被破坏而且还能实时刷新,update 不受影响。就可以解决无法调用主线程 UI 的问题。
3)动态的增加椅子之后,全局初始化问题
解决 awake 函数在其他类无法调用的问题,可以在此类封装好 awake 函数,然后在其他函数中调用即可。
4)人物拥挤椅子的问题
解决方法:给人物加上碰撞体刚体属性,采用内插值的方法解决人物碰撞拥挤和碰撞后抖动问题。
5 软件使用说明
1)基本功能
- 提供良好图形界面,显示整个系统操作过程,可以暂停和继续系统的执行;
- 可以设定候诊椅容量;
- 可以设定 Patient 到达的数度和 Dentist 治疗一名患者的最小时间;
- 实时显示候诊椅的使用情况量、空闲空间的数量、室外 Patient 的数量、已经治疗的 Patient 的数量;
- 实时显示线程、进程所处于等待(阻塞)状态的个数;
- 程序运行结束,显示汇总数据:
- 总的运行时间;处理 Patient 的个数;平均候诊椅中的 Patient 数量,平均室外 Patient 的数量,已经治疗的 Patient 的数量;
- 能够将每次的实验输入和实验结果存储起来,随时可查询。
2)需要运行的环境
Windows7 及以上版本
3)安装
Unity 3D 支持直接生成可执行文件,无需安装。
4)运行
图 7-1 开始界面
图 7-2 运行界面
图 7-3 增加椅子
图 7-4 记录文件保存
图 7-5 文件保存结果
5)操作
- 拖动底下滚动条设置初始参数
- 点击开始按钮开始运行
- 点击结束按钮结束当前页面并重新加载
- 点击暂停/恢复按钮暂停及恢复当前页面
- 点击增加椅子可以增加候诊椅数量
- 点击删除椅子可以删除候诊椅数量
- 点击保存记录可以将实时数据保存成 txt 文件
6 总结
1)完成的部分
- 提供良好图形界面,显示整个系统操作过程,可以暂停和继续系统的执行;
- 可以设定候诊椅容量;
- 可以设定 Patient 到达的数度和 Dentist 治疗一名患者的最小时间;
- 实时显示候诊椅的使用情况量、空闲空间的数量、室外 Patient 的数量、已经治疗的 Patient 的数量;
- 实时显示线程、进程所处于等待(阻塞)状态的个数;
- 程序运行结束,显示汇总数据:
- 总的运行时间;处理 Patient 的个数;平均候诊椅中的 Patient 数量,平均室外 Patient 的数量,已经治疗的 Patient 的数量;
- 能够将每次的实验输入和实验结果存储起来,随时可查询。
2)创新功能
交互较 C#窗体程序更好
实时显示人物动态运动情况
可以设置各种初始参数
设定病人忍耐度机制
♻️ 资源
大小: 22.5MB
➡️ 资源下载:https://download.csdn.net/download/s1t16/87645805
注:如当前文章或代码侵犯了您的权益,请私信作者删除!