unity3D-learning:打飞碟的工厂模式

作业与练习

1、编写一个简单的鼠标打飞碟(Hit UFO)游戏

  • 游戏内容要求:
    1. 游戏有 n 个 round,每个 round 都包括10 次 trial;
    2. 每个 trial 的飞碟的色彩、大小、发射位置、速度、角度、同时出现的个数都可能不同。它们由该 round 的 ruler 控制;
    3. 每个 trial 的飞碟有随机性,总体难度随 round 上升;
    4. 鼠标点中得分,得分规则按色彩、大小、速度不同计算,规则可自由设定。
  • 游戏的要求:
    • 使用带缓存的工厂模式管理不同飞碟的生产与回收,该工厂必须是场景单实例的!具体实现见参考资源 Singleton 模板类
    • 近可能使用前面 MVC 结构实现人机交互与游戏模型分离

这星期学习的内容是与游戏世界的交互,而且学习了工厂模式,结合我们之前学得动作分离模式和导演场记模式,我们将要来实验这样的一个打飞碟游戏,游戏还是很好玩的。我们先大致了解一下工厂模式,因为之前已经讲过动作分离和场记,那么实际上我们的另外两个理解起来就很容易了。

飞碟工厂的设计模式如下:单实例化的工厂,分数控制和场景控制以及各种预设的分离和使用。


我们先声明一下我们想要实现的游戏的大概模式:

这样的一个模式的进阶有利于增加游戏的趣味性,难度越来越高,完成就越来越难。但得分也会增多,我们主要通过控制飞碟的飞出频率,飞出速度以及飞碟的大小来改变难度。具体实现我们下面慢慢讲。

1.首先是飞碟工厂的实现,工厂里面应该实现两个功能:getDisk(ruler) 和FreeDisk(disk):

 * getDisk(ruler) //伪代码   
IF (free list has disk)   
THEN      
 a_disk = remove one from list   
ELSE      
 a_disk = clone from Prefabs   
ENDIF   
Set DiskData of a_disk with the ruler  
Add a_disk to used list   
Return a_disk    

FreeDisk(disk) //伪代码  
Find disk in used list   
IF (not found)   
THEN THROW exception   
Move disk from used to free list 
 */

同时这里面也实现了飞碟的轮次的改变以及难度的增加

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/*游戏规则:
 * 游戏总共分三轮,每一轮改变飞碟的大小和速度以及频率。
 * 采用积分晋级制
 * 打中第一轮的盘子奖励3分
 * 打中第二轮的盘子奖励5分
 * 打中第三轮的盘子奖励8分
 * 每一轮总共飞出20个盘子
 * 第一轮累计得分达到30分晋级,
 * 第二轮累计得分达到100分晋级,
 * 第三轮累计得分达到200分胜利!
 * 有点类似于我们在游乐场里面的投篮
 * 难度逐渐增大
 */
public class DiskFactory : System.Object
{
   private static DiskFactory _instance;
   public SceneController sceneControler { get; set; }
   public List<GameObject> used;
   public List<GameObject> free;
    // Use this for initialization
    private static List<GameObject> diskList;

   public static DiskFactory getInstance()
   {
       if (_instance == null)
       {
           _instance = new DiskFactory();
           _instance.used = new List<GameObject>();
           _instance.free = new List<GameObject>();
       }
       return _instance;
   }

   public GameObject getDisk(int round)
   {
       //
       if (sceneControler.num == 21)
       //每轮总共发射20个,如果得分达到一定要求进入下一轮,否则GameOver
       {
            
            if (sceneControler.round == 1 && sceneControler.score >= 30)
            {
                sceneControler.round++;
                sceneControler.num = 0;
            }
            if (sceneControler.round == 2 && sceneControler.score < 30)
            {
                sceneControler.game = 2;//输了比赛
            }

            if (sceneControler.round == 2 && sceneControler.score >= 100)
            {
                sceneControler.round++;
                sceneControler.num = 0;
            }
            if (sceneControler.round == 3 && sceneControler.score < 100)
            {
                sceneControler.game = 2;//输了比赛
            }

            if (sceneControler.round == 3 && sceneControler.score >= 200)
            {
                sceneControler.round++;
                sceneControler.num = 0;
            }
            if (sceneControler.round == 4 && sceneControler.score < 200)
            {
                sceneControler.game = 2;//输了比赛
            }
        }
       if (sceneControler.round == 4)
       {
           sceneControler.game = 4;//赢了比赛
       }
       GameObject newDisk;
       if (free.Count == 0)
       {
           newDisk = GameObject.Instantiate(Resources.Load("prefabs/Disk"), new Vector3(0, 0, -10), Quaternion.identity) as GameObject;
       }
       else
       {
           newDisk = free[0];
           free.Remove(free[0]);
       }
       switch (round)
       //根据轮数制定飞碟的颜色和大小
       {
           case 1:
               newDisk.transform.localScale = new Vector3(1.5f, 0.2f, 1.5f);
               newDisk.GetComponent<Renderer>().material.color = Color.blue;
               break;
           case 2:
               newDisk.transform.localScale = new Vector3(1.2f, 0.2f, 1.2f);
               newDisk.GetComponent<Renderer>().material.color = Color.gray;
               break;
           case 3:
               newDisk.transform.localScale = new Vector3(1.0f, 0.2f,1.0f);
               newDisk.GetComponent<Renderer>().material.color = Color.yellow;
               break;
       }
       used.Add(newDisk);
       return newDisk;
   }

   public void freeDisk(GameObject disk1)
   {
       for (int i = 0; i < used.Count; i++)
       {
           if (used[i] == disk1)
           {
               used.Remove(disk1);
               disk1.SetActive(true);//被鼠标击中的disk设置为false,所以这里全部激活一遍
               free.Add(disk1);
           }
       }
   }
}

2.再把大概的游戏主题加架起来,怎么得分,怎么控制,各种游戏的基础。

void Awake()
    //创建导演实例并载入资源
    {
        SSDirector director = SSDirector.getInstance();
        DiskFactory DF = DiskFactory.getInstance();
        DF.sceneControler = this;
        director.currentScenceController = this;
        director.setFPS(60);
        director.currentScenceController.LoadResources();
    }
    void Start()
    {
        round = 1;
    }
    public void LoadResources()
    {
        explosion = Instantiate(Resources.Load("prefabs/Explosion"), new Vector3(0, 0, -20), Quaternion.identity) as GameObject;
        plane = Instantiate(Resources.Load("prefabs/Plane"), new Vector3(0, 0, 0), Quaternion.identity) as GameObject;
    }

    // Update is called once per frame
    void Update()
    {
        Score.text = "Score:" + score.ToString();
        Round.text = "Round:" + round.ToString();
        if (Input.GetMouseButtonDown(0) && game == 1)//游戏中
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit))
            {
                if (hit.transform.tag == "Disk")
                {
                    if (round == 1 && game == 1)
                    {
                        score += 3;
                    }
                    if (round == 2 && game == 1)
                    {
                        score += 5;
                    }
                    if (round == 3 && game == 1)
                    {
                        score += 8;
                    }
                    explosion.transform.position = hit.collider.gameObject.transform.position;
                    explosion.GetComponent<Renderer>().material = hit.collider.gameObject.GetComponent<Renderer>().material;
                    explosion.GetComponent<ParticleSystem>().Play();
                    hit.collider.gameObject.SetActive(false);
                }
            }
        }
        if (game == 2)
        {
            GameOver();
        }
        if (game == 4)
        {
            Win();
        }
    }
    public IEnumerator waitForOneSecond()
    {
        while (StartTimes >= 0 && game == 3)
        {
            Time.text = StartTimes.ToString();
            Time.color = Color.red;
            yield return new WaitForSeconds(1); //一秒减计数1
            StartTimes--;
        }
        Time.text = "";
        game = 1;
    }
    public void ReStart()
    {
        SceneManager.LoadScene("task1");
    }

3.实现飞碟的飞行路径以及速度

飞行路径:

public override void Update()
    {
        Vector3 targetPos = target.transform.position;

        //让始终它朝着目标  
        gameobject.transform.LookAt(targetPos);
        float angle = Mathf.Min(1, Vector3.Distance(gameobject.transform.position, targetPos) / distanceToTarget) * 45;
        gameobject.transform.rotation = gameobject.transform.rotation * Quaternion.Euler(Mathf.Clamp(-angle, -42, 42), 0, 0);
        float currentDist = Vector3.Distance(gameobject.transform.position, target.transform.position);
        gameobject.transform.Translate(Vector3.forward * Mathf.Min(speed * Time.deltaTime, currentDist));
        if (this.transform.position == target.transform.position)
        {
            DiskFactory.getInstance().freeDisk(gameobject);
            Destroy(target);
            this.enable = false;
            this.destroy = true;
        }
    }

飞行速度:

public override void Start()
    {
        speed = 5 + sceneControler.round * 5;//使速度随着轮数变化
        startX = 5 - Random.value * 10;//使发射位置随机在(-5,5)
        targetY = -5;
        this.transform.position = new Vector3(startX, 0, -6);
        target = new GameObject();//创建终点
        target.transform.position = new Vector3(targetX, targetY, 30);
        //计算两者之间的距离  
        distanceToTarget = Vector3.Distance(this.transform.position, target.transform.position);
    }

改变飞碟的发射频率

 protected new void Update()
    {
        if (sceneController.round == 1 && sceneController.game == 1)
        {
            count++;
            if (count == 70)//70帧发射一个,发射的速度不同,发射慢一点
            {
                DiskMove = Emit2.GetSSAction();
                this.addAction(diskFactory.getDisk(sceneController.round), DiskMove);
                sceneController.num++;//记录发射的个数
                count = 0;
            }
            base.Update();
        }
        if (sceneController.round ==2 && sceneController.game == 1)
        {
            count++;
            if (count == 60)//60帧发射一个,发射的速度不同
            {
                DiskMove = Emit2.GetSSAction();
                this.addAction(diskFactory.getDisk(sceneController.round), DiskMove);
                sceneController.num++;//记录发射的个数
                count = 0;
            }
            base.Update();
        }
        if (sceneController.round ==3 && sceneController.game == 1)
        {
            count++;
            if (count == 50)//60帧发射一个,发射的速度不同
            {
                DiskMove = Emit2.GetSSAction();
                this.addAction(diskFactory.getDisk(sceneController.round), DiskMove);
                sceneController.num++;//记录发射的个数
                count = 0;
            }
            base.Update();
        }
    }

UI的实现:

void OnGUI()
    {
        GUIStyle fontstyle1 = new GUIStyle();
        fontstyle1.fontSize = 50;
        if (GUI.Button(new Rect(10, 10, 120, 40), "开始游戏"))
        {
            action.StartGame();
        }
        if (GUI.Button(new Rect(140,10 , 120, 40), "重新开始"))
        {
            action.ReStart();
        }
        if (GUI.RepeatButton(new Rect(750, 10, 120, 40), "游戏规则"))
        {
            action.ShowRule();
        }
    }

积分规则:

if (Physics.Raycast(ray, out hit))
            {
                if (hit.transform.tag == "Disk")
                {
                    if (round == 1 && game == 1)
                    {
                        score += 3;
                    }
                    if (round == 2 && game == 1)
                    {
                        score += 5;
                    }
                    if (round == 3 && game == 1)
                    {
                        score += 8;
                    }
                    explosion.transform.position = hit.collider.gameObject.transform.position;
                    explosion.GetComponent<Renderer>().material = hit.collider.gameObject.GetComponent<Renderer>().material;
                    explosion.GetComponent<ParticleSystem>().Play();
                    hit.collider.gameObject.SetActive(false);
                }
            }
接着实现导演和场记就行就不在这里粘贴了,完整代码移步github。这次的工厂模型还是要好好加强处理。游戏的演示视屏链接是:https://pan.baidu.com/s/1dMO82DvbrXyfnUEodvmPOw



阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页