Unity3D学习笔记(4) 飞碟(Disk)基础版(仅使用射线,动作管理)

声明

本作业借鉴自 https://blog.csdn.net/zzj051319/article/details/66475328
本人学习重点为设计模式,尽力做到高内聚低耦合

描述

  1. 分多个round,每个 round 都是 n 个 trail;
  2. 不同round的飞碟的色彩、大小、速度不一。
  3. 鼠标击中得分,得分与round相关。
    这里写图片描述

设计

这里写图片描述

https://gitee.com/Ernie1/unity3d-learning/tree/hw4/hw4

飞碟数据DiskData

这个方法通过一个变量给游戏对象附加数据属性(分数),将作为Component挂载到一个 disk 游戏对象。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DiskData : MonoBehaviour {
    public int score;
}

飞碟管理员DiskFactory

这个方法负责接收场记的请求(直接被场记调用),对 disk 游戏对象产生和回收。
这里运用了工厂模式,减少了游戏对象创建与销毁成本,减少创建和销毁次数,使程序易于扩展。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DiskFactory : System.Object {

    private static DiskFactory _instance;
    public SceneController sceneControler { get; set; }
    public List<GameObject> used;
    public List<GameObject> free;

    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() {

        GameObject newDisk;
        if (free.Count == 0)
            newDisk = GameObject.Instantiate(Resources.Load("prefabs/Disk")) as GameObject;
        else {
            newDisk = free[0];
            free.Remove(free[0]);
        }
        newDisk.SetActive(true);
        used.Add(newDisk);
        return newDisk;
    }

    public void freeDisk(GameObject g) {
        for (int i = 0; i < used.Count; i++) {
            if (used[i] == g) {
                used.Remove(g);
                g.SetActive(false);
                free.Add(g);
            }
        }
    }

    public void hideAll() {
        for (int i = 0; i < used.Count; i++)
            used [i].SetActive (false);
        for (int i = 0; i < free.Count; i++)
            free [i].SetActive (false);
    }
}

记分员Scorekeeper

负责根据游戏对象的DiskData组件中的变量score进行分数的累加,分数被场景直接访问,提供清零方法。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Scorekeeper {

    private static Scorekeeper _instance;
    public static Scorekeeper getInstance(){
        if (_instance == null)
            _instance = new Scorekeeper();
        return _instance;
    }

    public int score;

    public void reset(){
        score = 0;
    }

    public void record(GameObject hit) {
        score += hit.GetComponent<DiskData> ().score;
    }
}

抛出动作FlyDisk

继承了动作基类SSAction,接收速度级别参数,实现抛出Disk动作。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FlyDisk : SSAction {
    Vector3 start;   //起点
    Vector3 target;   //要到达的目标  
    Vector3 speed;    //分解速度
    float countTime;
    Vector3 Gravity;

    private int level;

    public override void Start() {
        start = new Vector3(7 - Random.value * 14, 0, 0); 
        target = new Vector3(Random.value * 80 - 40, Random.value * 29 - 4, 30);

        this.transform.position = start;

        float mainSpeed = 5 + level * 6;
        float time = Vector3.Distance(target, start) / mainSpeed;

        speed = new Vector3 ((target.x - start.x) / time, (target.y - start.y) / time + 5 * time, (target.z - start.z) / time);
        Gravity = Vector3.zero;
        countTime = 0;
    }
    public static FlyDisk GetSSAction(int level) {
        FlyDisk action = ScriptableObject.CreateInstance<FlyDisk>();
        action.level = level;
        return action;
    }
    public override void Update() {
        float g = -10;
        Gravity.y = g * (countTime += Time.fixedDeltaTime);// v=gt
        this.transform.position += (speed + Gravity) * Time.fixedDeltaTime;//模拟位移

        if (this.transform.position.z >= target.z) {
            DiskFactory.getInstance().freeDisk(gameobject);
            this.destroy = true;
            this.callback.SSActionEvent(this);
        }
    }
}

主体

导演类SSDirector

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public interface ISceneController{
    void LoadResources();
}

public class SSDirector : System.Object {

    private static SSDirector _instance;

    public ISceneController currentScenceController { get; set; }
    public bool running { get; set; }

    public static SSDirector getInstance(){
        if (_instance == null)
            _instance = new SSDirector();
        return _instance;
    }

    public int getFPS() {
        return Application.targetFrameRate;
    }

    public void setFPS(int fps) {
        Application.targetFrameRate = fps;
    }
}

用户交互类UserGUI

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public interface IUserAction{
    void StartGame();
    void ReStart();
}

public class UserGUI : MonoBehaviour{

    private IUserAction action;

    void Start() {
        action = SSDirector.getInstance ().currentScenceController as IUserAction;
    }

    void OnGUI() {
        if (GUI.Button(new Rect(Screen.width/2-38, 38, 76, 20), "(RE)PLAY"))
            action.ReStart();
    }
}

场记类SceneController

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class SceneController : MonoBehaviour, ISceneController, IUserAction
{
    public CCActionManager actionManager { get; set; }
    public Scorekeeper scorekeeper;
    public DiskFactory DF;
    private int round = 0;
    public int totalRound = 3;
    public int trial = 10;
    public Text ScoreText;
    public Text RoundText;
    public Text GameText;
    private bool play = false;
    private int num = 0;
    private float heartbeat;

    GameObject disk;
    GameObject explosion;

    void Awake() {
        SSDirector director = SSDirector.getInstance();
        DF = DiskFactory.getInstance();
        DF.sceneControler = this;
        director.setFPS(60);
        director.currentScenceController = this;
        director.currentScenceController.LoadResources();

        scorekeeper = Scorekeeper.getInstance ();
    }
    void Start() {
        round = 1;
        heartbeat = 0;
    }
    public void LoadResources() {
        explosion = Instantiate(Resources.Load("prefabs/Explosion"), new Vector3(-40, 0, 0), Quaternion.identity) as GameObject;
        Instantiate(Resources.Load("prefabs/Light"));
    }

    void Update() {
        if (play) {
            if (heartbeat >= 1) {
                launchDisk ();
                heartbeat = 0;
            }
            heartbeat += Time.deltaTime;
        }

        if (Input.GetButtonDown("Fire1") && play) {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit)) {
                GameText.text = "";
                if (hit.transform.tag == "Disk")
                {
                    explosion.transform.position = hit.collider.gameObject.transform.position;
                    explosion.GetComponent<ParticleSystem>().Play();
                    hit.collider.gameObject.SetActive(false);
                    scorekeeper.record (hit.collider.gameObject);
                }
            }
        }
        updateStatus ();
    }

    public void StartGame() {
        num = 0;
        play = true;
        scorekeeper.reset ();
    }
    public void ReStart() {
        round = 1;
        heartbeat = 0;
        num = 0;
        play = true;
        GameText.text = "";
        DF.hideAll ();
        scorekeeper.reset ();
    }

    private void launchDisk() {
        GameObject newDisk = DF.getDisk ();
        if (!newDisk.GetComponent<DiskData> ())
            newDisk.AddComponent<DiskData> ();
        newDisk.GetComponent<DiskData> ().score = round;
        newDisk.GetComponent<Renderer> ().material.color = new Color ((round % 10) * 0.1F, 0.4F, 0.8F);


        float size = (float)(1.0f - 0.2 * round);
        newDisk.transform.localScale = new Vector3(4*size, size/5, 4*size);
        num++;
        actionManager.singleRunAction (newDisk, round);
    }

    private void updateStatus() {
        ScoreText.text = "Score:" + scorekeeper.score.ToString();
        RoundText.text = "Round:" + round.ToString();
        if (scorekeeper.score >= 6) {
            ++round;
            GameText.text = "Round " + round.ToString();
            scorekeeper.reset ();
            num = 0;
        }
        if (round > totalRound) {
            play = false;
            GameText.text = "Win";
        }
        if (num >= trial) {
            play = false;
            GameText.text = "Game Over";
        }
    }
}

动作基类SSAction

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum SSActionEventType : int { Started, Competeted }

public interface ISSActionCallback {
    void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,
        int intParam = 0, string strParam = null, Object objectParam = null);
}

public class SSAction : ScriptableObject {

    public bool enable = true;
    public bool destroy = false;

    public GameObject gameobject { get; set; }
    public Transform transform { get; set; }
    public ISSActionCallback callback{ get; set; }

    protected SSAction () {}

    //Use this for initialization
    public virtual void Start () {
        throw new System.NotImplementedException ();
    }

    // Update is called once per frame
    public virtual void Update () {
        throw new System.NotImplementedException ();
    }
}

动作管理基类SSActionManager

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SSActionManager : MonoBehaviour
{
    private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();
    private List<SSAction> waitingAdd = new List<SSAction>();
    private List<int> waitingDelete = new List<int>();

    // Use this for initialization
    void Start() {

    }

    // Update is called once per frame
    protected void Update() {
        foreach (SSAction ac in waitingAdd) actions[ac.GetInstanceID()] = ac;
        waitingAdd.Clear();

        foreach (KeyValuePair<int, SSAction> kv in actions) {
            SSAction ac = kv.Value;
            if (ac.destroy)
                waitingDelete.Add(ac.GetInstanceID());
            else if (ac.enable)
                ac.Update();
        }

        foreach (int key in waitingDelete) {
            SSAction ac = actions [key];
            actions.Remove (key);
            DestroyObject (ac);
        }
        waitingDelete.Clear();
    }

    public void RunAction(GameObject gameobject, SSAction action, ISSActionCallback manager) {
        action.gameobject = gameobject;
        action.transform = gameobject.transform;
        action.callback = manager;
        waitingAdd.Add(action);
        action.Start();
    }
}

实战动作管理CCActionManager

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CCActionManager : SSActionManager, ISSActionCallback {

    public SceneController sceneController;
    public DiskFactory diskFactory;

    void Start() {
        sceneController = (SceneController)SSDirector.getInstance ().currentScenceController;
        sceneController.actionManager=this;
        diskFactory = DiskFactory.getInstance();
    }

    public new void Update () {
        base.Update ();
    }

    public void singleRunAction (GameObject gameObject, int speedLevel) {
        this.RunAction (gameObject, FlyDisk.GetSSAction (speedLevel), this);
    }

    #region ISSActionCallback implementation
    public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,
        int intParam = 0, string strParam = null, Object objectParam = null){

    }
    #endregion
}

猜你喜欢

转载自blog.csdn.net/z_j_q_/article/details/79962788