3D游戏编程与设计6——物理系统与碰撞

1、改进飞碟(Hit UFO)游戏:
  • 游戏内容要求:
    • adapter模式设计图修改飞碟游戏
    • 使它同时支持物理运动与运动学(变换)运动

代码 UML 图:
3D游戏编程与设计6——物理系统与碰撞_第1张图片

代码说明:

  • 飞碟回收工厂类(DiskFactory.cs)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DiskFactory : MonoBehaviour
{
    private List<DiskData> used = new List<DiskData>();
    private List<DiskData> free = new List<DiskData>();
    private List<Color> c = new List<Color>();

    private void Awake()
    {
        c.Add(Color.black);
        c.Add(Color.blue);
        c.Add(Color.cyan);
        c.Add(Color.gray);
        c.Add(Color.green);
        c.Add(Color.magenta);
        c.Add(Color.red);
        c.Add(Color.white);
        c.Add(Color.yellow);
    }

    public GameObject GetDisk(int round)
    {
        GameObject newDisk = null;
        if (free.Count > 0)
        {
            newDisk = free[0].gameObject;
            free.Remove(free[0]);
        }
        else
        {
            newDisk = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("prefabs/disk"), Vector3.zero, Quaternion.identity);
            newDisk.AddComponent<DiskData>();
        }
        int flag;
        flag = UnityEngine.Random.Range(0, 5);
        newDisk.GetComponent<DiskData>().color = c[(round + flag) % 9];
        newDisk.GetComponent<Renderer>().material.color = c[(round + flag) % 9];
        newDisk.GetComponent<DiskData>().speed = round * 5;
        newDisk.GetComponent<DiskData>().size = getSize(round);
        newDisk.transform.localScale = getSize(round);
        float RanX = UnityEngine.Random.Range(-1f, 1f) < 0 ? -1 : 1;
        newDisk.GetComponent<DiskData>().direction = new Vector3(RanX, 1, 0);
        used.Add(newDisk.GetComponent<DiskData>());
        newDisk.name = newDisk.GetInstanceID().ToString();
        return newDisk;
    }

    public void FreeDisk(GameObject disk)
    {
        disk.SetActive(false);
        free.Add(disk.GetComponent<DiskData>());
        used.Remove(disk.GetComponent<DiskData>());
    }

    private Vector3 getSize(int r)
    {
        if (r == 0)
        {
            return new Vector3(5, 0.1f, 5);
        }
        else if (r == 1)
        {
            return new Vector3(4, 0.1f, 4);
        }
        else if (r == 2)
        {
            return new Vector3(3, 0.1f, 3);
        }
        else if (r == 3)
        {
            return new Vector3(2, 0.1f, 2);
        }
        else
        {
            return new Vector3(1, 0.1f, 1);
        }
    }
}
  • 飞碟数据信息类(DiskData.cs)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DiskData : MonoBehaviour
{
    public Vector3 size;
    public Color color;
    public float speed;
    public Vector3 direction;
}
  • 所有场景控制器要实现的接口(ISceneControl.cs)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public interface ISceneControl
{
    void LoadResources();
}
  • 控制场景切换类(Director.cs)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Director : System.Object
{
    // singlton instance
    private static Director _instance;

    public ISceneControl currentSceneController { get; set; }
    public bool running { get; set; }

    //get instance anytime anywhere!
    public static Director getInstance()
    {
        if (_instance == null)
        {
            _instance = new Director();
        }
        return _instance;
    }

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

    public void setFPS(int fps)
    {
        Application.targetFrameRate = fps;
    }
}
  • 控制第一个场景的类(FirstSceneControl.cs)
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FirstController : MonoBehaviour, ISceneControl, IUserAction
{
    public ActionMode mode { get; set; }

    public IActionManager actionManager { get; set; }

    public ScoreRecorder scoreRecorder { get; set; }


    private int trial;
    private GameState gameState;
    public int round = 1;

    void Awake()
    {
        Director director = Director.getInstance();
        director.setFPS(60);
        director.currentSceneController = this;
        this.gameObject.AddComponent<ScoreRecorder>();
        this.gameObject.AddComponent<DiskFactory>();
        this.gameObject.AddComponent<CCActionManager>();

        gameState = GameState.START;
        scoreRecorder = Singleton<ScoreRecorder>.Instance;
        //actionManager = Singleton.Instance;
        setMode(ActionMode.KINEMATIC);
        LoadResources();
    }

    private void Update()
    {
        if (gameState == GameState.START)
        {
            scoreRecorder.Reset();
            trial = 10;
            round = 1;
            ThrowDisk();
            gameState = GameState.RUNNING;
        }
        if (gameState == GameState.NEXT_ROUND)
        {
            round++;
            gameState = GameState.RUNNING;
            trial = 10;
            ThrowDisk();
        }
        if (gameState == GameState.DROP)
        {
            gameState = GameState.RUNNING;
            trial--;
            if (trial == 0)
            {
                gameState = GameState.GAME_OVER;
            }
            else
            {
                ThrowDisk();
            }
        }

    }

    void ThrowDisk()
    {
        DiskFactory df = Singleton<DiskFactory>.Instance;
        GameObject disk = df.GetDisk(round);
        float y = UnityEngine.Random.Range(0f, 4f);
        Vector3 position = new Vector3(-disk.GetComponent<DiskData>().direction.x * 7, y, 0);
        disk.transform.position = position;
        disk.SetActive(true);
        actionManager.StartThrow(disk);
    }

    public void LoadResources()
    {

        GameObject greensward = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("prefabs/greensward"));
    }


    public void hit(Vector3 pos)
    {
        Ray ray = Camera.main.ScreenPointToRay(pos);
        //Return the ray's hit
        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)
            {
                scoreRecorder.AddRecord(round);
                DiskFactory df = Singleton<DiskFactory>.Instance;
                df.FreeDisk(hit.collider.gameObject);
                setGameState(GameState.ROUND_END);
            }

        }
    }

    public void setGameState(GameState gs)
    {
        gameState = gs;
    }

    public int getRound()
    {
        return round;
    }

    public int getScore()
    {
        return scoreRecorder.score;
    }

    public int getTrial()
    {

        return trial;
    }

    public GameState getGameState()
    {
        return gameState;
    }

    public ActionMode getMode()
    {
        return mode;
    }

    public void setMode(ActionMode m)
    {

        if (m == ActionMode.KINEMATIC)
        {
            this.gameObject.AddComponent<CCActionManager>();
        }
        else
        {
            this.gameObject.AddComponent<PhysicActionManager>();
        }
    }
}
  • 动作管理器的基类(SSActionManager.cs)
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  
    protected 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();// update action
            }
        }

        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();
    }
}
  • 动作管理器要实现的接口(IActionManager.cs)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public interface IActionManager
{
    void StartThrow(GameObject disk);
    /*int getDiskNumber();
    void setDiskNumber(int num);*/
}
  • 运动学动作管理器(CCActionManager.cs)
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CCActionManager : SSActionManager, ISSActionCallback, IActionManager
{

    public FirstController sceneController;
    public List<CCFlyAction> Fly;
    public int DiskNumber = 0;

    protected new void Start()
    {
        sceneController = (FirstController)Director.getInstance().currentSceneController;
        sceneController.actionManager = this;
    }

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

    public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted, int intParam = 0, string strParam = null, UnityEngine.Object objectParam = null)
    {
        if (source is CCFlyAction)
        {
            sceneController.setGameState(GameState.DROP);
            DiskFactory df = Singleton<DiskFactory>.Instance;
            df.FreeDisk(source.gameobject);
        }
    }

    public void StartThrow(GameObject disk)
    {
        RunAction(disk, CCFlyAction.GetSSAction(disk.GetComponent<DiskData>().speed, disk.GetComponent<DiskData>().direction), (ISSActionCallback)this);
    }
}
  • 动力学动作管理器(PhysicActionManager.cs)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PhysicActionManager : MonoBehaviour, IActionManager, ISSActionCallback
{
    public FirstController sceneController;

    private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();
    private List<SSAction> waitingAdd = new List<SSAction>();
    private List<int> waitingDelete = new List<int>();

    protected void Start()
    {
        sceneController = (FirstController)Director.getInstance().currentSceneController;
        sceneController.actionManager = this;

    }

    // Update is called once per frame  
    protected void FixedUpdate()
    {

        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.FixedUpdate();
            }
        }

        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();
    }


    public void SSActionEvent(SSAction source,
        SSActionEventType events = SSActionEventType.Competeted,
        int intParam = 0,
        string strParam = null,
        UnityEngine.Object objectParam = null)
    {
        if (source is CCFlyAction)
        {
            sceneController.setGameState(GameState.DROP);
            DiskFactory df = Singleton<DiskFactory>.Instance;
            df.FreeDisk(source.gameobject);
        }
    }

    public void StartThrow(GameObject disk)
    {
        /*CCFlyActionFactory cf = Singleton.Instance;
        foreach (GameObject tmp in diskQueue)
        {
            RunAction(tmp, cf.GetSSAction(), (ISSActionCallback)this);
        }*/
        RunAction(disk, CCFlyAction.GetSSAction(disk.GetComponent<DiskData>().speed, disk.GetComponent<DiskData>().direction), (ISSActionCallback)this);
    }
}
  • 动作的基类(SSAction.cs)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SSAction : ScriptableObject
{

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

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

    protected SSAction() { }

    public virtual void Start()
    {
        throw new System.NotImplementedException();
    }

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

    public virtual void FixedUpdate()
    {
        throw new System.NotImplementedException();
    }

    public void reset()
    {
        enable = false;
        destroy = false;
        gameobject = null;
        transform = null;
        callback = null;
    }
}
  • 飞碟飞行的动作(CCFlyAction.cs)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CCFlyAction : SSAction
{
    public float acceleration;
    public float horizontalSpeed;
    public Vector3 direction;
    public float time;

    public static CCFlyAction GetSSAction(float speed, Vector3 d)
    {
        CCFlyAction action = ScriptableObject.CreateInstance<CCFlyAction>();
        action.acceleration = 9.8f;
        action.horizontalSpeed = speed;
        action.time = 0;
        action.direction = d;
        action.enable = true;
        return action;
    }

    public override void Start()
    {

    }

    // Update is called once per frame  
    public override void Update()
    {
        if (gameobject.activeSelf)
        {
            time += Time.deltaTime;
            //Debug.Log(time);
            transform.Translate(Vector3.down * acceleration * time * Time.deltaTime);
            transform.Translate(direction * horizontalSpeed * Time.deltaTime);

            if (this.transform.position.y < -4)
            {
                this.destroy = true;
                this.enable = false;
                this.callback.SSActionEvent(this);
            }
        }

    }

    public override void FixedUpdate()
    {
        if (gameobject.activeSelf)
        {
            time += Time.deltaTime;
            //Debug.Log(time);
            transform.Translate(Vector3.down * acceleration * time * Time.deltaTime);
            transform.Translate(direction * horizontalSpeed * Time.deltaTime);

            if (this.transform.position.y < -4)
            {
                this.destroy = true;
                this.enable = false;
                this.callback.SSActionEvent(this);
            }
        }
    }
}
  • 动作与动作管理器间通信的接口(ISSActionCallback.cs)
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);

}
  • 界面与场景控制器间通信的接口(IUserAction.cs)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum GameState { START, NEXT_ROUND, GAME_OVER, RUNNING, ROUND_END, DROP }
public enum ActionMode { PHYSIC, KINEMATIC, NOTSET }

public interface IUserAction
{

    int getRound();
    int getScore();
    int getTrial();
    GameState getGameState();
    void setGameState(GameState gs);
    void hit(Vector3 pos);
}
  • 负责界面的类(UserGUI.cs)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UserGUI : MonoBehaviour
{
    private IUserAction action;
    private int score;
    private int trial;
    private int round;

    // Use this for initialization  
    void Start()
    {
        action = Director.getInstance().currentSceneController as IUserAction;
        //Debug.Log(action.getTrial());
    }

    private void Update()
    {
        Debug.Log("hi");
        score = action.getScore();
        trial = action.getTrial();
        round = action.getRound();
    }

    void OnGUI()
    {
        //Debug.Log(action == null);
        //Debug.Log(action.getTrial());


        GUI.Box(new Rect(Screen.width / 2 - 75, 10, 150, 55), "Round " + (round) + "\nYour score:  " + score + "\nYour trial left:  " + (trial));
        if (action.getGameState() == GameState.ROUND_END)
        {
            GUI.Window(0, new Rect(Screen.width / 2 - Screen.width / 12, Screen.height / 2 - Screen.height / 12, Screen.width / 6, Screen.height / 6), next_round_window, "Success !");
        }
        if (action.getGameState() == GameState.GAME_OVER)
        {
            GUI.Window(0, new Rect(Screen.width / 2 - Screen.width / 12, Screen.height / 2 - Screen.height / 12, Screen.width / 6, Screen.height / 6), game_over_window, "Game Orver!");
        }
        if (Input.GetButtonDown("Fire1"))
        {

            Vector3 pos = Input.mousePosition;
            action.hit(pos);

        }

    }

    void next_round_window(int id)
    {
        if (GUI.Button(new Rect(Screen.width / 24, Screen.height / 24 + 5, Screen.width / 12, Screen.height / 12), "Next"))
        {
            action.setGameState(GameState.NEXT_ROUND);
        }

    }

    void game_over_window(int id)
    {
        if (GUI.Button(new Rect(Screen.width / 24, Screen.height / 24 + 5, Screen.width / 12, Screen.height / 12), "Restart"))
        {
            Debug.Log("Restart");

            action.setGameState(GameState.START);
        }
    }
}
  • 记分员类(ScoreRecorder.cs)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ScoreRecorder : MonoBehaviour
{
    public int score;

    // Use this for initialization  
    void Start()
    {
        score = 0;
    }

    public void AddRecord(int round)
    {
        score += round;
    }

    public void Reset()
    {
        score = 0;
    }
}
  • 实现单实例的模板(Sinigleton.cs)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{

    protected static T instance;

    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                instance = (T)FindObjectOfType(typeof(T));
                if (instance == null)
                {
                    Debug.LogError("An instance of " + typeof(T)
                        + " is needed in the scene, but there is none.");
                }
            }
            return instance;
        }
    }
}

你可能感兴趣的:(3D游戏编程与设计)