项目源码:
演示视频:
【Unity3D实验作业】打飞碟小游戏_哔哩哔哩_bilibili
UML图:
游戏简介:
游戏分为普通模式和无尽模式,普通模式仅有两轮,无尽模式除非玩家主动返回主界面,否则一直进行。
一轮游戏有多个轮次,一个轮次有多个回合,每个回合会飞出不同种类的飞碟。系统会根据飞碟的大小、颜色、速度的不同进行计分,玩家可以通过点击飞碟来累积积分。
核心代码介绍:
DiskFactory类为对象池,负责对飞碟对象的克隆、配置属性、分发和销毁。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DiskFactory : MonoBehaviour
{
public GameObject diskPrefab;
private List<DiskData> used;
private List<DiskData> free;
public void Start()
{
diskPrefab = GameObject.Instantiate(Resources.Load<GameObject>("Prefabs/f1"), Vector3.zero, Quaternion.identity);
diskPrefab.SetActive(false);
used = new List<DiskData>();
free = new List<DiskData>();
}
public GameObject GetDisk(Ruler ruler)
{
GameObject disk;
int diskNum = free.Count;
if (diskNum == 0)
{
disk = GameObject.Instantiate(diskPrefab, Vector3.zero, Quaternion.identity);
disk.AddComponent(typeof(DiskData));
}
else
{
disk = free[diskNum - 1].gameObject;
free.Remove(free[diskNum - 1]);
}
disk.GetComponent<DiskData>().speed = ruler.speed;
disk.GetComponent<DiskData>().color = ruler.color;
disk.GetComponent<DiskData>().size = ruler.size;
if (ruler.color == "red")
{
disk.GetComponent<Renderer>().material.color = Color.red;
}
else if (ruler.color == "green")
{
disk.GetComponent<Renderer>().material.color = Color.green;
}
else
{
disk.GetComponent<Renderer>().material.color = Color.blue;
}
disk.transform.localScale = new Vector3(1.2f, 0.1f * (float)ruler.size, 1.2f);
disk.transform.position = ruler.beginPos;
disk.SetActive(true);
used.Add(disk.GetComponent<DiskData>());
return disk;
}
public void FreeDisk(GameObject disk)
{
foreach (DiskData d in used)
{
if (d.gameObject.GetInstanceID() == disk.GetInstanceID())
{
disk.SetActive(false);
used.Remove(d);
free.Add(d);
break;
}
}
}
}
PhysisPlayDiskAction类负责实现对飞碟对象的物理运动的模拟
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PhysisPlayDiskAction : SSAction
{
float speed;
Vector3 direction;
public static PhysisPlayDiskAction GetSSAction(Vector3 direction, float speed)
{
PhysisPlayDiskAction action = ScriptableObject.CreateInstance<PhysisPlayDiskAction>();
action.speed = speed;
action.direction = direction;
return action;
}
public override void Start()
{
gameObject.GetComponent<Rigidbody>().isKinematic = false;
gameObject.GetComponent<Rigidbody>().velocity = speed * direction;
}
public override void Update()
{
if (this.transform.position.y < -5)
{
this.destroy = true;
this.enable = false;
this.callback.SSActionEvent(this);
}
}
}
RoundController类负责请求飞碟工厂生成相应的飞碟对象,控制游戏飞碟对象的发射和轮次的进行。
public class RoundController : MonoBehaviour
{
private IActionManager actionManager;
public ScoreRecorder scoreRecorder;
private MainController mainController;
private Ruler ruler;
void Start()
{
actionManager = gameObject.AddComponent<PhysisActionManager>();
scoreRecorder = new ScoreRecorder();
mainController = Director.GetInstance().mainController;
gameObject.AddComponent<DiskFactory>();
InitRuler();
}
void InitRuler()
{
ruler.trialNum = 0;
ruler.roundNum = 0;
ruler.sendTime = 0;
ruler.roundDisksNum = new int[10];
generateRoundDisksNum();
}
public void generateRoundDisksNum()
{
for (int i = 0; i < 10; ++i)
{
ruler.roundDisksNum[i] = Random.Range(0, 4) + 1;
}
}
public void Reset()
{
InitRuler();
scoreRecorder.Reset();
}
public void Record(DiskData disk)
{
scoreRecorder.Record(disk);
}
public int GetScores()
{
return scoreRecorder.score;
}
public void SetRoundSum(int roundSum)
{
ruler.roundSum = roundSum;
}
public void LaunchDisk()
{
int[] beginPosY = new int[4] { 0, 0, 0, 0 };
for (int i = 0; i < ruler.roundDisksNum[ruler.trialNum]; ++i)
{
int randomNum = Random.Range(0, 3) + 1;
ruler.speed = randomNum * (ruler.roundNum + 4);
randomNum = Random.Range(0, 3) + 1;
if (randomNum == 1)
{
ruler.color = "red";
}
else if (randomNum == 2)
{
ruler.color = "green";
}
else
{
ruler.color = "blue";
}
ruler.size = Random.Range(0, 3) + 1;
randomNum = Random.Range(0, 2);
if (randomNum == 1)
{
ruler.direction = new Vector3(3, 1, 0);
}
else
{
ruler.direction = new Vector3(-3, 1, 0);
}
do
{
randomNum = Random.Range(0, 4);
} while (beginPosY[randomNum] != 0);
beginPosY[randomNum] = 1;
ruler.beginPos = new Vector3(-ruler.direction.x * 4, -0.5f * randomNum, 0);
GameObject disk = Singleton<DiskFactory>.Instance.GetDisk(ruler);
actionManager.PlayDisk(disk, ruler.speed, ruler.direction);
}
}
public void FreeFactoryDisk(GameObject disk)
{
Singleton<DiskFactory>.Instance.FreeDisk(disk);
}
public void FreeAllFactoryDisk()
{
GameObject[] obj = FindObjectsOfType(typeof(GameObject)) as GameObject[];
foreach (GameObject g in obj)
{
if (g.gameObject.name == "Disk(Clone)(Clone)")
{
Singleton<DiskFactory>.Instance.FreeDisk(g);
}
}
}
void Update()
{
if (mainController.GetGameState() == (int)GameState.Playing)
{
ruler.sendTime += Time.deltaTime;
if (ruler.sendTime > 2)
{
ruler.sendTime = 0;
if (ruler.roundSum == -1 || ruler.roundNum < ruler.roundSum)
{
mainController.SetViewTip("");
LaunchDisk();
ruler.trialNum++;
if (ruler.trialNum == 10)
{
ruler.trialNum = 0;
ruler.roundNum++;
generateRoundDisksNum();
}
}
else
{
mainController.SetViewTip("Click Restart and Play Again!");
mainController.SetGameState((int)GameState.GameOver);
}
if (ruler.trialNum == 0) mainController.SetViewRoundNum(ruler.roundNum);
else mainController.SetViewRoundNum(ruler.roundNum + 1);
mainController.SetViewTrialNum(ruler.trialNum);
}
}
}
}
ScoreRecorder类负责实现对飞碟的计分工作
public class ScoreRecorder
{
public int score;
public ScoreRecorder()
{
score = 0;
}
public void Record(DiskData disk)
{
int diskSize = disk.size;
switch (diskSize)
{
case 1:
score += 3;
break;
case 2:
score += 2;
break;
case 3:
score += 1;
break;
default: break;
}
score += disk.speed;
string diskColor = disk.color;
if (diskColor.CompareTo("red") == 0)
{
score += 1;
}
else if (diskColor.CompareTo("green") == 0)
{
score += 2;
}
else if (diskColor.CompareTo("blue") == 0)
{
score += 3;
}
}
public void Reset()
{
score = 0;
}
}
MainController负责控制View、RoundController的显示和运行
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
enum GameState
{
Ready = 0, Playing = 1, GameOver = 2
};
public class MainController : MonoBehaviour
{
private RoundController roundController;
private View view;
private int N = 2;
private int gameState;
void Start()
{
Director.GetInstance().mainController = this;
roundController = gameObject.AddComponent<RoundController>();
view = gameObject.AddComponent<View>();
gameState = (int)GameState.Ready;
}
public int GetN()
{
return N;
}
public void Restart()
{
view.Init();
roundController.Reset();
}
public void SetGameState(int state)
{
gameState = state;
}
public int GetGameState()
{
return gameState;
}
public void ShowPage()
{
switch (gameState)
{
case 0:
view.ShowHomePage();
break;
case 1:
view.ShowGamePage();
break;
case 2:
view.ShowRestart();
break;
}
}
public void SetRoundSum(int roundSum)
{
roundController.SetRoundSum(roundSum);
}
public void SetViewTip(string tip)
{
view.SetTip(tip);
}
public void SetViewScore(int score)
{
view.SetScore(score);
}
public void SetViewRoundNum(int round)
{
view.SetRoundNum(round);
}
public void SetViewTrialNum(int trial)
{
view.SetTrialNum(trial);
}
public void Hit(Vector3 position)
{
Camera camera = Camera.main;
Ray ray = camera.ScreenPointToRay(position);
RaycastHit[] hits;
hits = Physics.RaycastAll(ray);
for (int i = 0; i < hits.Length; i++)
{
RaycastHit hit = hits[i];
if (hit.collider.gameObject.GetComponent<DiskData>() != null)
{
hit.collider.gameObject.transform.position = new Vector3(0, -6, 0);
roundController.Record(hit.collider.gameObject.GetComponent<DiskData>());
view.SetScore(roundController.scoreRecorder.score);
}
}
}
public void FreeAllFactoryDisk()
{
roundController.FreeAllFactoryDisk();
}
}
View类负责实现游戏界面的显示、互动和跳转
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class View : MonoBehaviour
{
private MainController mainController;
private int score;
private string tip;
private string roundNum;
private string trialNum;
void Start()
{
//Debug.Log("q-2");
score = 0;
tip = "";
roundNum = "";
trialNum = "";
mainController = Director.GetInstance().mainController;
}
public void SetTip(string tip)
{
this.tip = tip;
}
public void SetScore(int score)
{
this.score = score;
}
public void SetRoundNum(int round)
{
roundNum = "回合: " + round;
}
public void SetTrialNum(int trial)
{
if (trial == 0) trial = 10;
trialNum = "Trial: " + trial;
}
public void Init()
{
score = 0;
tip = "";
roundNum = "";
trialNum = "";
}
public void AddTitle()
{
GUIStyle titleStyle = new GUIStyle();
titleStyle.normal.textColor = Color.black;
titleStyle.fontSize = 50;
GUI.Label(new Rect(Screen.width / 2 - 80, 20, 60, 100), "Playing Disks", titleStyle);
}
public void AddChooseModeButton()
{
if (GUI.Button(new Rect(Screen.width / 2 - 80, 200, 160, 80), "Normal\n(默认为" + mainController.GetN() + "回合)"))
{
mainController.SetRoundSum(mainController.GetN());
mainController.Restart();
mainController.SetGameState((int)GameState.Playing);
}
if (GUI.Button(new Rect(Screen.width / 2 - 80, 350, 160, 80), "NoEnding\n(回合数无限)"))
{
mainController.SetRoundSum(-1);
mainController.Restart();
mainController.SetGameState((int)GameState.Playing);
}
}
public void ShowHomePage()
{
//Debug.Log("q1");
AddChooseModeButton();
}
public void AddBackButton()
{
//GUI.skin = gameSkin;
if (GUI.Button(new Rect(10, 10, 60, 40), "Return"))
{
mainController.FreeAllFactoryDisk();
mainController.Restart();
mainController.SetGameState((int)GameState.Ready);
}
}
public void AddGameLabel()
{
GUIStyle labelStyle = new GUIStyle();
labelStyle.normal.textColor = Color.black;
labelStyle.fontSize = 30;
GUI.Label(new Rect(Screen.width - 120, 10, 100, 50), "得分: " + score, labelStyle);
GUI.Label(new Rect(Screen.width / 2 - 140, 200, 50, 200), tip, labelStyle);
GUI.Label(new Rect(Screen.width - 120, 60, 100, 50), roundNum, labelStyle);
GUI.Label(new Rect(Screen.width - 120, 110, 100, 50), trialNum, labelStyle);
}
public void AddRestartButton()
{
if (GUI.Button(new Rect(Screen.width / 2 , 350, 100, 60), "Restart"))
{
mainController.FreeAllFactoryDisk();
mainController.Restart();
mainController.SetGameState((int)GameState.Playing);
}
}
public void ShowGamePage()
{
//Debug.Log("q2");
AddGameLabel();
AddBackButton();
if (Input.GetButtonDown("Fire1"))
{
mainController.Hit(Input.mousePosition);
}
}
public void ShowRestart()
{
//Debug.Log("q3");
ShowGamePage();
AddRestartButton();
}
void OnGUI()
{
AddTitle();
//Debug.Log("q-1");
//if(mainController==null) Debug.Log("error");
mainController.ShowPage();
}
}