一、 作业要求
1. 编写一个简单的自定义 Component
用自定义组件定义几种飞碟,做成预制
2. 编写一个简单的鼠标打飞碟游戏
内容要求:
• 游戏有多个轮次,每个轮次都包括10个轨迹;
• 每个轨迹的飞碟的色彩、大小、发射位置、速度、角度、同时出现的 个数都可能不同。它们由该轮次的 ruler 控制;
• 每个轨迹的飞碟有随机性,总体难度随轮次上升;
• 鼠标点中得分,计分规则按色彩、大小、速度不同计算,可自由设定
设计要求:
• 使用带缓存的工厂模式管理不同飞碟的生产与回收,该工厂必须是场景单实例的!具体实现见参考资源 Singleton 模板类
• 尽可能使用前面 MVC 结构实现人机交互与游戏模型分离
二、设计思路
本次采用了MVC结构,主要设计有DiskAction, DiskController和UserGUI三个类分别承担结构中model, controller和viewer的功能,另外还有类DiskFactory与Singleton用于使用带缓存的工厂模式管理不同飞碟的生产与回收。
三、具体代码
DickAction类
// 飞碟动作类
// 飞碟可以从视图边界的任意处飞入,沿直线运行直至飞出
// 飞碟具有水平方向速度与垂直方向速度
public class DiskAction : MonoBehaviour
{
public float speedX=1;
public float speedY=1;
public int score=2;
static DiskFactory fc = Singleton.GetInstance();
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
// 飞碟超出边界,销毁
if (this.transform.position.x>100||this.transform.position.x<-100 || this.transform.gameObject.active==false){
fc.FreeDisk(this.transform.gameObject);
return;
}
// 飞碟的移动
transform.position += new Vector3(speedX, speedY, 0) * Time.deltaTime * 2;
}
}
DiskController类
public class DiskController : MonoBehaviour
{
static public int isRun = 0; //标识游戏是否运行
private int diskNum = 0; //目前场景上的飞碟数量
private int round = 1;
private int max_round = 3; //最大关卡等级
private float timer = 0.5f;
private float roundTime = 30f;
public UserGUI userGUI;
DiskFactory fc = Singleton.GetInstance();
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if(isRun==0 || isOver())
return;
GetHit();
roundTime -= Time.deltaTime;
timer -= Time.deltaTime;
if (timer <= 0) {
//从工厂中得到round个飞碟
for (int i = 0; i < round; ++i) {
GameObject disk;
disk = fc.GetDisk(round);
}
timer = 0.5f;
}
if (roundTime<=0){
round += 1;
userGUI.setRound (round);
}
}
public void GetHit() {
if (Input.GetButtonDown("Fire1")) {
Camera ca = Camera.main;
Ray ray = ca.ScreenPointToRay(Input.mousePosition);
//Return the ray's hit
RaycastHit hit;
if (Physics.Raycast(ray, out hit)) {
userGUI.setScore( userGUI.getScore() + hit.transform.gameObject.GetComponent<DiskAction>().score);
hit.transform.gameObject.SetActive(false);
}
}
}
public bool isOver()
{
if (round > max_round){
userGUI.setGameMessage("Game Over!");
isRun = 2;
return true;
}
return false;
}
}
UserGUI类
public class UserGUI : MonoBehaviour
{
public int round;
string gameMessage;
int score;
public GUIStyle bigStyle, smallStyle;
public Font pixelFont;
private int menu_width = Screen.width / 5, menu_height = Screen.width / 10;//主菜单每一个按键的宽度和高度
// Start is called before the first frame update
void Start()
{
gameMessage = "";
//大字体初始化
bigStyle = new GUIStyle();
bigStyle.normal.textColor = Color.white;
bigStyle.normal.background = null;
bigStyle.fontSize = 50;
bigStyle.alignment=TextAnchor.MiddleCenter;
//小字体初始化
smallStyle = new GUIStyle();
smallStyle.normal.textColor = Color.white;
smallStyle.normal.background = null;
smallStyle.fontSize = 20;
smallStyle.alignment=TextAnchor.MiddleCenter;
}
// Update is called once per frame
void OnGUI()
{
if(DiskController.isRun==0)
mainMenu();
else
GameStart();
}
void mainMenu() {
GUI.Label(new Rect(Screen.width / 2 - menu_width * 0.5f, Screen.height * 0.1f, menu_width, menu_height), "Hit Disks", bigStyle);
bool button = GUI.Button(new Rect(Screen.width / 2 - menu_width * 0.5f, Screen.height * 3 / 7, menu_width, menu_height), "Start");
if (button) {
DiskController.isRun = 1;
}
}
void GameStart() {
GUI.Label(new Rect(300, 60, 50, 200), gameMessage, bigStyle);
GUI.Label(new Rect(0,0,100,50), "Score: " + score, smallStyle);
GUI.Label(new Rect(560,0,100,50), "Round: " + round, smallStyle);
}
public void setGameMessage(string m){ gameMessage =m; }
public void setScore(int s){ score = s; }
public void setRound(int r){ round = r; }
public int getScore(){return score;}
}
DiskFactory类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 飞碟工厂类
public class DiskFactory : MonoBehaviour
{
List<GameObject> used = new List<GameObject>();
List<GameObject> free = new List<GameObject>();
System.Random rand= new System.Random(100);
// Start is called before the first frame update
void Start()
{
}
public GameObject GetDisk(int round) {
GameObject disk ;
if (free.Count > 1) {
disk = free[0];
free.Remove(disk);
}
else{
if (rand.Next(0,10)<8){
disk =Instantiate(Resources.Load("Prefabs/blueDisk")) as GameObject;
disk.AddComponent<DiskAction>();
}
else{
disk = Instantiate(Resources.Load("Prefabs/yellowDisk")) as GameObject;
disk.AddComponent<DiskAction>();
disk.GetComponent<DiskAction>().score=5;
}
}
//根据不同round设置飞碟速度
disk.GetComponent<DiskAction>().speedX *= round;
disk.GetComponent<DiskAction>().speedY *= round;
//飞碟可从四个方向飞入(左上、左下、右上、右下)
int direction = rand.Next(1,5);
if (direction == 1) {
disk.transform.Translate(new Vector3(5,0,-5));
disk.GetComponent<DiskAction>().speedY *= -1;
}
else if (direction == 2) {
disk.transform.Translate(new Vector3(-5,0,-5));
}
else if (direction == 3) {
disk.transform.Translate(new Vector3(5,0,5));
disk.GetComponent<DiskAction>().speedX *= -1;
disk.GetComponent<DiskAction>().speedY *= -1;
}
else if (direction == 4) {
disk.transform.Translate(new Vector3(-5,0,5));
disk.GetComponent<DiskAction>().speedX *= -1;
}
used.Add(disk);
disk.SetActive(true);
Debug.Log("generate disk");
return disk;
}
public void FreeDisk(GameObject disk) {
disk.SetActive(false);
//将位置速度恢复到预制,这点很重要!
disk.transform.position = new Vector3(0, 0,0);
disk.GetComponent<DiskAction>().speedX=2;
disk.GetComponent<DiskAction>().speedY=2;
Debug.Log("free disk");
used.Remove(disk);
free.Add(disk);
}
}
Singleton类
public class Singleton
{
private static DiskFactory _instance = null;
private Singleton()
{
}
public static DiskFactory GetInstance()
{
if (_instance == null)
{
_instance = new DiskFactory();
}
return _instance;
}
}
四、运行结果