游戏介绍
(注:之后希望在此项目基础上修改为动画可变的小游戏,于是采用了水果模型代替飞碟)
游戏有 10个 round,每个 round 都包括若干次trial;
每个 trial 的飞碟(水果)的色彩、大小、发射位置、速度、角度、同时出现的个数都可能不同。飞碟根据飞碟样式分别记为1-3分,分数越高,平均移动速度越快;
每个 trial 的飞碟有随机性,总体难度随 round 上升,体现为增加所有样式飞碟的速度,要求分数提高;
鼠标点中飞碟即可得分。
每个round有目标分数,达到了则离开晋级下一关,未达到则游戏失败。
(测试下来难度还是非常高的,可以一试,改为切水果或许能更具游玩性)
打飞碟作业
代码仓库:派大星星行/Unity3D_LAB3
游戏结构:
参考设计模式:
本个实验结构较为简单,没有采用动作分类等模式,设计模式如下,随后展示部分重要代码:
1.工厂模式
该项目的核心在于使用对象池实现带缓冲的创建、销毁对象的工厂模式。
创建对象的工厂模式是一种软件设计模式,用于封装对象的实例化过程。它通过提供一个公共的接口或基类来创建对象,而不是直接调用对象的构造函数。这样可以使代码更加灵活、可扩展和可维护。
首先,我们使用list结构首先了工厂中的对象池。
使用对象池是在工厂模式中常见的一种技术手段。对象池包含两个主要部分:正在使用的对象池和空闲对象池。
正在使用的对象池存储当前正在被使用的对象实例。这些对象已经通过工厂方法创建并提供给客户端使用。当客户端不再需要某个对象时,该对象将被释放并返回到空闲对象池中,以供其他客户端重新使用。
空闲对象池存储可以被重复使用的对象实例。这些对象已经被创建并初始化,但当前没有被任何客户端使用。通过将这些对象存储在空闲对象池中,可以避免频繁地创建和销毁对象,从而提高性能和效率。
public class DiskFactory : MonoBehaviour
{
private List<GameObject> free = new List<GameObject>();
private List<GameObject> used = new List<GameObject>();
然后在工厂中利用对象池,定义创建、“销毁”产品的工厂方法,提供产品接口。
代码中没有展示的DiskAttributes是承载disk基本属性及点击控制的自定义组件。
public void GetDisk(int round)
{
GameObject disk;
if (free.Count != 0)
{
disk = free[0];
free.Remove(disk);
}
else
{
rand = new System.Random();
int IScore = rand.Next(1, 4);
if (IScore == 1)
disk = GameObject.Instantiate(Resources.Load("Banana", typeof(GameObject))) as GameObject;
else if (IScore == 2)
disk = GameObject.Instantiate(Resources.Load("Hamburger", typeof(GameObject))) as GameObject;
else
disk = GameObject.Instantiate(Resources.Load("Cheese", typeof(GameObject))) as GameObject;
disk.AddComponent<DiskAttributes>();
disk.GetComponent<DiskAttributes>().score = IScore;
disk.GetComponent<DiskAttributes>().fdisk = disk;
}
Rigidbody rb = disk.GetComponent<Rigidbody>();
rb.useGravity = true;
float initialSpeed = 10f; // 初始速度
// initialSpeed = initialSpeed*(1f + ((float)round)/10);
float angle = 75f; // 发射角度
float randomX = Random.Range(-8f, 6f);
float randomZ = Random.Range(-3f, -4f);
// 将角度转换为弧度
float radianAngle = angle * Mathf.Deg2Rad;
// 水平速度和垂直速度
float horizontalSpeed = initialSpeed * Mathf.Cos(radianAngle);
float verticalSpeed = initialSpeed * Mathf.Sin(radianAngle) *(1.25f + ((float)round)/15);
// 施加初始速度的力
Vector3 launchVelocity = new Vector3(-horizontalSpeed, verticalSpeed, 0f);
if (randomX < 0)
{
launchVelocity.x = -launchVelocity.x; // 将向左抛的物体的水平速度取反
}
rb.velocity = launchVelocity;
disk.transform.position = new Vector3(randomX, -3.6f, randomZ);
used.Add(disk);
disk.SetActive(true);
}
public void FreeDisk(GameObject disk)
{
Rigidbody rigidbody = disk.GetComponent<Rigidbody>();
// 停止飞碟的运动并重置速度
rigidbody.Sleep();
disk.SetActive(false);
//将位置和大小恢复到预制
disk.transform.position = new Vector3(0f, -10f, 0f);
// Debug.Log("free disk");
used.Remove(disk);
free.Add(disk);
}
}
2.ScoreController
作为计分的专职场记,为更高层的控制器提供分数记录、重置、查询接口。不在乎分数外的转换细节。
public class ScoreController : MonoBehaviour
{
private int score;
public ScoreController(){
score = 0;
}
public void reset(){
score = 0;
}
public void Start(){
reset();
}
public void Record(int s)
{
score += s;
}
public int GetScore(){
return score;
}
}
3.RoundController
作为轮次转换的专职场记,拥有一个分数场记对象,根据当前局数制定转换条件与游戏参数,根据其分数场记判断游戏状态。为核心控制器。
public class RoundController : MonoBehaviour
{
private ScoreController scoreController;
public int targetScore; // 每轮所需达到的目标分数
public float roundDuration; // 每轮的时间限制
public int currentRound; // 当前关卡
public float timer; // 计时器
public bool isGameOver; // 游戏是否结束
public string StateTip;
public RoundController(){
targetScore = (4 + 8) * 2;
roundDuration = 25f;
currentRound = 1;
timer = 0f;
isGameOver = false;
scoreController = new ScoreController();
StateTip = "在水果落地前抓住它!";
}
public void SetRoundController(int round){
targetScore = (round*4 + 8) * 2;
roundDuration = 25f;
currentRound = round;
timer = 0f;
isGameOver = false;
scoreController.reset();
StateTip = "在水果落地前抓住它!";
}
public void Record(int s){
scoreController.Record(s);
}
public int GetScore(){
return scoreController.GetScore();
}
public bool CheckGameState(){
if(timer > roundDuration){
if(scoreController.GetScore() >= targetScore){
if(currentRound == 10){
isGameOver = true;
StateTip = "恭喜你完成全部关卡!";
return isGameOver;
}
SetRoundController(currentRound + 1);
isGameOver = false;
return isGameOver;
}else{
isGameOver = true;
StateTip = "太多水果掉在地上,你的挑战失败了!";
return isGameOver;
}
}
return isGameOver;
}
}
4.firstController
场景的主控制器,负责使用单例对象使用飞碟工厂的接口驱动游戏,初始化roundControl等控制器,挂载userGUI视图,实现游戏场景的更新。(其余代码请查阅仓库)
public class FirstController : MonoBehaviour
{
private RoundController roundController;
private GUIView view;
public void Record(int s){
roundController.Record(s);
}
void Awake()
{
// 创建ScoreController实例并分配给scoreController属性
roundController = new RoundController();
// 设置单例对象中只有唯一一个主控制器
SingleObject instance = SingleObject.getInstance();
instance.SetMainController(this);
instance.SetDiskFactory(new DiskFactory());
view = gameObject.AddComponent<GUIView>() as GUIView;
view.SetView(roundController);
}