实验要求:
编程实践,请写步骤,贴代码并解释:
实验效果:
写个用鼠标打飞碟的游戏。
游戏要分多个 round , 飞碟数量每个 round 都是 n 个,但色彩,大小;发射位置,速度,角度,每次发射数量可以变化。
游戏过程中,仅能创建 n 个飞碟, 且不容许初始化阶段生成任何飞碟。 飞碟线路计算请使用 mathf 类。 向下加速度 a 是常数。 飞碟被用户击中,则回收。并按你定义的规则计算分数。飞碟落地(y < c),则自动回收。
请画 UML 图,描述主要对象及其关系。
代码UML图:
代码说明:
1.飞碟回收类工厂DiskFactory:
这个类主要是用来实现飞碟的资源管理,如生成飞碟,自动回收飞碟等。我在这里用List数组存储飞碟对象,方便管理。对外有三个函数可供调用,一个是getDisk获取飞碟对象,setDisk设置飞碟属性,DisableDisk用于回合更改时灭活还在Used里的飞碟。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DiskFactory : MonoBehaviour {
//private static int MaxSize = 10;
private SceneController Controller;
private List<GameObject> Used;
private List<GameObject> Free;
// Use this for initialization
void Awake () {
Used = new List<GameObject> ();
Free = new List<GameObject> ();
Controller = SceneController.getControllerInstance ();
Controller.setFactory (this);
}
public void setDisk(Color c, float speed, float size, int t) {
for (int i = 0; i < Free.Count; i++) {
Free [i].GetComponent<DiskData> ().setDisk (c, speed, size, t);
}
for (int i = 0; i < Used.Count; i++) {
Used [i].GetComponent<DiskData> ().setDisk (c, speed, size, t);
}
}
// Update is called once per frame
void Update () {
freeDisk ();
}
public void DisableDisk() {
for (int i = 0; i < Used.Count; i++) {
Used [i].SetActive (false);
}
}
public GameObject getDisk() {
GameObject temp;
if (Free.Count != 0) {
temp = Free [0];
temp.SetActive (true);
Free.RemoveAt (0);
} else {
temp = Instantiate (Resources.Load ("Prefabs/disk")) as GameObject;
DiskData data = temp.GetComponent<DiskData> ();
if (Controller.getRound() == 1)
data.setDisk (Color.green, 0.5f, 1f, 1);
else
data.setDisk (Color.red, 0.7f, 0.8f, 2);
temp.SetActive (true);
}
Used.Add (temp);
return temp;
}
void freeDisk() {
int R = Controller.getRound ();
for (int i = 0; i < Used.Count; i++) {
GameObject temp = Used [i];
//Debug.Log (Used [i].transform.position.y);
if (!Used [i].activeInHierarchy) {
temp.SetActive (false);
Used.RemoveAt (i);
Free.Add (temp);
} else if (Used [i].transform.position.y < 0.5f || Used [i].transform.position.z > 60f) {
Controller.getRecorder ().lostScore (temp);
temp.SetActive (false);
Used.RemoveAt (i);
Free.Add (temp);
}
}
}
}
2.场景控制类SceneController:
这个类主要用来注册工厂等功能类,注册有自己,Diskfactory和ScoreRecorder的实例,为了符合单例模式,调用其他功能类的单实例只能通过SceneController的get方法。里面有发射飞碟的方法emitDisk以及设置回合的方法NextRound和Round。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SceneController : System.Object {
private static SceneController Controller_instance;
private ScoreRecorder Recorder_instance;
private DiskFactory Factory_instance;
private int RoundNow = 0;
Vector3 emitPos = new Vector3(-1.5f, 0.5f, 0.5f);
Vector3 emitDir = new Vector3(10f, 50.0f, 67f);
float emitSpeed = 0.5f;
Color color;
public static SceneController getControllerInstance () {
if (Controller_instance == null) {
Controller_instance = new SceneController();
}
return Controller_instance;
}
public ScoreRecorder getRecorder() {
return Recorder_instance;
}
public void setRecorder(ScoreRecorder R) {
Recorder_instance = R;
}
public DiskFactory getFactory() {
return Factory_instance;
}
public void setFactory(DiskFactory F) {
Factory_instance = F;
}
public void emitDisk() {
GameObject temp = Factory_instance.getDisk();
if (temp == null) {
Debug.Log ("No disk, Please wait!");
return;
}
temp.transform.position = emitPos;
Debug.Log (temp.transform.position);
Vector3 nextDir = emitDir;
nextDir.x *= Random.Range (-4f, 4f);
Debug.Log (emitDir * Random.Range (emitSpeed * 5, emitSpeed * 7) / 10);
temp.GetComponent<Rigidbody>().AddForce (nextDir * Random.Range (emitSpeed * 5, emitSpeed * 7) / 10, ForceMode.Impulse);
temp.GetComponent<Rigidbody>().velocity = new Vector3(0, 0, 0);
}
public void nextRound() {
Factory_instance.DisableDisk ();
if (RoundNow == 1) {
RoundNow = 2;
} else if (RoundNow == 2) {
RoundNow = 1;
} else {
RoundNow = 1;
}
Round (RoundNow);
}
public void Round(int R) {
Debug.Log ("Round = " + R);
switch(R) {
case 1:
emitPos = new Vector3 (-1.5f, 0.5f, 0.5f);
emitSpeed = 0.5f;
color = Color.green;
Factory_instance.setDisk (color, emitSpeed, 1f, 1);
break;
case 2:
emitPos = new Vector3 (1.5f, 0.5f, 0.5f);
emitSpeed = 0.7f;
color = Color.red;
Factory_instance.setDisk (color, emitSpeed, 0.8f, 2);
break;
}
}
public int getRound() {
return RoundNow;
}
}
3.记分员类ScoreRecorder:
这个类主要用于飞碟计分工作,有两个方法,分别为得分getScore和失分lostScore,两个都根据飞碟的种类进行计分,不同种飞碟分值不同。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ScoreRecorder : MonoBehaviour {
private SceneController Controller;
private int Score;
private int TotalScore;
// Use this for initialization
void Start () {
Score = 0;
TotalScore = 0;
Controller = SceneController.getControllerInstance ();
Controller.setRecorder (this);
}
// Update is called once per frame
void Update () {
}
public void getScore(GameObject d) {
Score += 10 * d.GetComponent<DiskData> ().getType();
Debug.Log (d.GetComponent<DiskData> ().getType ());
TotalScore += 10 * d.GetComponent<DiskData> ().getType();
if (Score >= 100) {
Score = 0;
Controller.nextRound ();
}
Debug.Log (Score);
}
public void lostScore(GameObject d) {
Score -= 10 * d.GetComponent<DiskData> ().getType();
TotalScore -= 10 * d.GetComponent<DiskData> ().getType();
}
public int returnScore() {
return Score;
}
public int returnTScore() {
return TotalScore;
}
}
4.飞碟数据类DiskData:
这个类主要用于记录和设置飞碟的相关属性,挂在飞碟的预制体上即可。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DiskData : MonoBehaviour {
Color color;
float speed;
float size;
int DiskType;
public void setDisk(Color c, float sp, float s, int t) {
color = c;
speed = sp;
size = s;
DiskType = t;
Debug.Log (this.name);
this.GetComponent<MeshRenderer>().material.color = c;
Debug.Log ("SetDisk");
this.transform.localScale = new Vector3 (1f * size, 0.1f, 1f * size);
}
public int getType() {
return DiskType;
}
}
5.用户交互类UserInterface:
这个类主要用于用户交互,界面的产生和处理点击操作在Update里处理。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UserInterface : MonoBehaviour {
GameObject cam;
Camera ca;
float time = 0;
public ParticleSystem explosion;
public Text scoreText;
public Text roundText;
//public Text DiskNumText;
public Text TotalScore;
public Text TimeText;
SceneController Controller;
// Use this for initialization
void Awake () {
cam = GameObject.Find ("Camera");
ca = cam.GetComponent<Camera> ();
//explosion = Instantiate (Resources.Load ("Prefabs/explosion")) as ParticleSystem;
Controller = SceneController.getControllerInstance ();
Controller.nextRound ();
}
// Update is called once per frame
void Update () {
if (Input.GetMouseButtonDown (0)) {
Vector3 mp = Input.mousePosition;
Ray ray = ca.ScreenPointToRay (Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast (ray, out hit, Mathf.Infinity) && hit.collider.gameObject.tag == "Disk") {
print ("I hit " + hit.transform.gameObject.name);
hit.collider.gameObject.SetActive (false);
PlayExplosion (hit.collider.gameObject);
Controller.getRecorder ().getScore (hit.collider.gameObject);
}
}
if (Input.GetKeyDown("space")) {
Debug.Log ("Space Down");
if (time <= 0) {
time = 3;
if (Controller.getRound () == 2) {
Controller.emitDisk ();
}
Controller.emitDisk ();
}
}
if (time > 0) {
time -= Time.deltaTime;
TimeText.text = "Next Disk Time : " + (int)(time + 0.5);
} else {
TimeText.text = "Disk Ready!";
}
scoreText.text = "TempScore : " + Controller.getRecorder ().returnScore ();
roundText.text = "Round : " + Controller.getRound ();
TotalScore.text = "TotalScore : " + Controller.getRecorder ().returnTScore ();
}
void PlayExplosion(GameObject t) {
Debug.Log (explosion);
explosion.transform.position = t.transform.position;
explosion.GetComponent<Renderer>().material.color = t.GetComponent<Renderer>().material.color;
explosion.Play ();
}
}
Unity资源设置:
首先创建一个底面plane,一个空对象Controller用于挂载脚本,将UserInterface,DiskFactory和ScoreRecorder挂上去。然后创建UI对象显示分数等,将每一个创建的Text拖进UserInterface的变量框里。坐标我是以(0,0,0)为基准,摄像机在(0,1,0)。
粒子对象的设置主要有