Unity3d Note6(适配者模式改进版)

1.作业要求

改进飞碟(Hit UFO)游戏:

游戏内容要求:

  • 按 adapter模式 设计图修改飞碟游戏
  • 使它同时支持物理运动与运动学(变换)运动

2.具体设计

2.1知识准备

了解apdater模式
apdater模式的核心思想是让不同的实现类同时实现一个接口,其他类再用这个接口来实例化使得其他类不会因为接口不兼容而只能使用其中一个类。就比如我们要做一个电商网站,它有一个实用简单统一的支付接口,它能同时对接工行、农行、微信、支付宝等等支付方式的接口。想要具体了解的可以参考下面的链接。
参考链接设计模式(五)适配器模式Adapter(结构型)适配器模式

了解物理运动
刚体是一个组件。一个游戏对象加上刚体之后游戏引擎就会对其进行物理效果模拟。同时我们也可以给这个对象施加各种作用力,让它运动起来。另外如果要实现重力的效果,那么相应的游戏物体都必须附上刚体组件。

  • 添加刚体
    点中一个游戏对象,点击Inspector窗口里面的Add Component按钮,选择下拉框的physics,然后第一个就是刚体Rigidbody了。

  • 各种属性

Mass 指定物体的质量,而由于质量越大,惯性越大,所以最好不要让场景里的物体的质量相差百倍以上,因为如果它们碰撞了就会产生极大的速度,增加游戏的负担,影响游戏的性能。
Drag 空气阻力,影响物体的移动速度和移动距离,如果设为infinity,物体会无法移动。
Angular Drag 物体旋转时受到的空气阻力,设为infinity,物体会无法旋转。
Use Gravity 是否使用重力。
Is Kinematic 是否动态,勾选这个选项会使游戏对象不受物理引擎的影响,但这不等同于没有刚体组件。能够在动画中除去惯性的影响。
Interpolate 如果看到刚体移动的时候一直抽风或者运动的不是很平滑,可以选择一种平滑方式: None(无差值):不使用差值平滑。 Interpolate(差值):根据上一帧来平滑移动。 Extrapolate(推算):根据推算下一帧物体的位置来平滑移动。
Collision Detection 碰撞检测,Discrete(离散检测),Continuous(连续碰撞检测),Continuous Dynamic(连续动态碰撞检测)
Freeze Position/Rotation 可以在三个轴上防止进行位置/旋转的变化,即使受到相关力。

其实这里主要是了解一下刚体而已,物理运动还包括其他的很多东西,甚至不止游戏的物理引擎,还有一些物理知识。

2.2分析需求

在保留运动学,即不放弃CCActionManager类的前提下,添加一个有关物理运动的类,即PhysicActionManager类,为了在FirstSceneController类中能够由用户选择使用哪种运动方式来决定使用哪个动作管理类,所以两个动作管理类应该同时实现一个接口,于是抽象出两个动作管理类应该在FirstSceneController类中使用的方法,作为一个接口IActionManager。这里用到了adapter模式。

2.3具体设计

PhysicActionManager类也继承了SSActionManager类,复制CCActionManager类的代码,然后在Update()哪里进行修改,同时在Start()方法哪里进行一些前期准备,通知SSActionManager类它的子类是谁。
在SSActionManager类中添加一个属性flag,两个子类可以修改它,进而影响SSActionManager类中的Update()方法的实现。
在FirstSceneManager类中修改动作管理器的类型为接口IActionManager,添加某些方法来影响IActionManager的具体实例。
在UserGUI中添加按钮代码给用户选择使用哪一种方式来使飞碟运动。
在DiskFactory中为新生成的对象添加刚体。

3.源代码

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>();
    protected bool flag = true;

    protected void Update()
    {
        foreach (SSAction action in waitingAdd)
        {
            actions[action.GetInstanceID()] = action;
        }
        waitingAdd.Clear();

        foreach (KeyValuePair<int, SSAction> i in actions)
        {
            SSAction value = i.Value;
            if (value.destroy)
            {
                waitingDelete.Add(value.GetInstanceID());
            }
            else if (value.enable && flag)
            {
                value.Update();
            }
            else if (value.enable && flag == false)
            {
                value.FixedUpdate();
            }
        }

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

    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 stopRigidbodyAction()
    {
        foreach (SSAction action in actions.Values)
        {
            action.stopAction();
        }
    }
}

PhysicActionManager.cs

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

public class PhysicActionManager : SSActionManager, ISSActionCallback, IActionManager
{
    private FirstSceneControl sceneControl;
    private List<CCFlyAction> flys = new List<CCFlyAction>();
    private int diskNumber = 0;

    private List<SSAction> used = new List<SSAction>();
    private List<SSAction> free = new List<SSAction>();

    public void setDiskNumber(int dn)
    {
        diskNumber = dn;
    }

    public int getDiskNumber()
    {
        return diskNumber;
    }

    public SSAction getSSAction()
    {
        SSAction action = null;
        if (free.Count > 0)
        {
            action = free[0];
            free.Remove(free[0]);
        }
        else
        {
            action = ScriptableObject.Instantiate<CCFlyAction>(flys[0]);
        }
        used.Add(action);
        return action;
    }

    public void freeSSAction(SSAction action)
    {
        foreach (SSAction a in used)
        {
            if (a.GetInstanceID() == action.GetInstanceID())
            {
                a.reset();
                free.Add(a);
                used.Remove(a);
                break;
            }
        }
    }

    protected void Start()
    {
        sceneControl = (FirstSceneControl)Director.getInstance().current;
        sceneControl.actionManager = this;
        flys.Add(CCFlyAction.getCCFlyAction());
        base.flag = false;
    }

    private new void Update()
    {
        if (sceneControl.getGameState() == GameState.RUNNING)
        {
            base.Update();
        }
        else
        {
            base.stopRigidbodyAction();
        }
    }

    public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Completed, int intPram = 0
        , string strParm = null, Object objParm = null)
    {
        if (source is CCFlyAction)
        {
            diskNumber--;
            Singleton<DiskFactory>.Instance.freeDisk(source.gameObject);
            freeSSAction(source);
        }
    }

    public void startThrow(Queue<GameObject> diskQueue)
    {
        foreach (GameObject i in diskQueue)
        {
            runAction(i, getSSAction(), (ISSActionCallback)this);
        }
    }
}

CCFlyAction.cs

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

public class CCFlyAction : SSAction
{
    float acceleration;
    float horizontalSpeed;
    Vector3 direction;
    float time;
    Rigidbody rigidbody;
    // Vector3 old;

    public static CCFlyAction getCCFlyAction()
    {
        CCFlyAction action =  ScriptableObject.CreateInstance<CCFlyAction>();
        return action;
    }

    public override void Start()
    {
        enable = true;
        acceleration = 9.8f;
        time = 0;
        // old = gameObject.transform.position;
        horizontalSpeed = gameObject.GetComponent<DiskData>().getSpeed();
        direction = gameObject.GetComponent<DiskData>().getDirection();
        rigidbody = gameObject.GetComponent<Rigidbody>();
        if (rigidbody)
        {
            rigidbody.velocity = horizontalSpeed * direction;
        }
    }

    public override void Update()
    {
        if (gameObject.activeSelf)
        {
            time += Time.deltaTime;
            transform.Translate(Vector3.down * acceleration * time * Time.deltaTime);
            transform.Translate(direction * horizontalSpeed * Time.deltaTime);
            clear();
        }
    }

    public override void FixedUpdate()
    {
        if (gameObject.activeSelf)
        {
            clear();
        }
    }

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

    public override void stopAction()
    {
        if (rigidbody)
        {
            rigidbody.velocity = Vector3.zero;
            rigidbody.useGravity = false;
        }
    }
}

DiskFactory.cs

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

public class DiskFactory : MonoBehaviour {
    public GameObject diskPrefab;
    public List<DiskData> used = new List<DiskData>();
    public List<DiskData> free = new List<DiskData>();

    private void Awake()
    {
        diskPrefab = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/disk"), Vector3.zero, Quaternion.identity);
        diskPrefab.SetActive(false);
    }

    public GameObject getDisk(int round, ActionMode mode)
    {
        GameObject disk = null;
        if(free.Count > 0)
        {
            disk = free[0].gameObject;
            free.Remove(free[0]);
        }
        else
        {
            disk = GameObject.Instantiate<GameObject>(diskPrefab, Vector3.zero, Quaternion.identity);
            disk.AddComponent<DiskData>();
        }

        if(mode == ActionMode.PHYSIC && disk.GetComponent<Rigidbody>() == null)
        {
            disk.AddComponent<Rigidbody>();
        }

        int start;
        switch (round)
        {
            case 0: start = 0; break;
            case 1: start = 100; break;
            default: start = 200; break;
        }
        int selectColor = Random.Range(start, round * 499);
        round = selectColor / 250;
        DiskData diskData = disk.GetComponent<DiskData>();
        Renderer renderer = disk.GetComponent<Renderer>();
        Renderer childRenderer = disk.transform.GetChild(0).GetComponent<Renderer>();
        float ranX = Random.Range(-1, 1) < 0 ? -1.2f : 1.2f;
        Vector3 direction = new Vector3(ranX, 1, 0);
        switch (round)
        {
            case 0:
                diskData.setDiskData(new Vector3(1.35f, 1.35f, 1.35f), Color.white, 4.0f, direction);
                renderer.material.color = Color.white;
                childRenderer.material.color = Color.white;
                break;
            case 1:
                diskData.setDiskData(new Vector3(1f, 1f, 1f), Color.gray, 6.0f, direction);
                renderer.material.color = Color.gray;
                childRenderer.material.color = Color.gray;
                break;
            case 2:
                diskData.setDiskData(new Vector3(0.7f, 0.7f, 0.7f), Color.black, 8.0f, direction);
                renderer.material.color = Color.black;
                childRenderer.material.color = Color.black;
                break;
        }
        used.Add(diskData);
        diskData.name = diskData.GetInstanceID().ToString();
        disk.transform.localScale = diskData.getSize();

        return disk;
    }

    public void freeDisk(GameObject disk)
    {
        DiskData temp = null;
        foreach (DiskData i in used)
        {
            if (disk.GetInstanceID() == i.gameObject.GetInstanceID())
            {
                temp = i;
            }
        }
        if (temp != null)
        {
            temp.gameObject.SetActive(false);
            free.Add(temp);
            used.Remove(temp);
        }
    }
}

FirstSceneManager.cs

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

public class FirstSceneControl : MonoBehaviour, ISceneControl, IUserAction {
    public IActionManager actionManager { set; get; }
    public ScoreRecorder scoreRecorder { set; get; }
    public Queue<GameObject> diskQueue = new Queue<GameObject>();
    private ActionMode actionMode = ActionMode.NOTSET;
    private int diskNumber = 0;
    private int currentRound = -1;
    private float time = 0;
    private GameState gameState = GameState.START;

    void Awake()
    {
        Director director = Director.getInstance();
        director.current = this;
        diskNumber = 10;
        this.gameObject.AddComponent<ScoreRecorder>();
        this.gameObject.AddComponent<DiskFactory>();
        scoreRecorder = Singleton<ScoreRecorder>.Instance;
        director.current.loadResources();
    }

    public void loadResources()
    {

    }

    private void Update()
    {
        if(actionMode == ActionMode.NOTSET || actionManager == null)
        {
            return;
        }
        if(actionManager.getDiskNumber() == 0 && gameState == GameState.RUNNING)
        {
            gameState = GameState.ROUND_FINISH;
            gameState = GameState.ROUND_FINISH;
            if(actionMode == ActionMode.PHYSIC)
            {
                gameState = GameState.FUNISH;
                return;
            }
            if(currentRound == 2)
            {
                gameState = GameState.FUNISH;
                currentRound = 0;
                return;
            }
        }
        if(actionManager.getDiskNumber() == 0 && gameState == GameState.ROUND_START)
        {
            currentRound++;
            nextRound();
            actionManager.setDiskNumber(10);
            gameState = GameState.RUNNING;
        }
        if(time > 1 && gameState != GameState.PAUSE)
        {
            throwDisk();
            time = 0;
        }
        else
        {
            time += Time.deltaTime;
        }
    }

    private void nextRound()
    {
        DiskFactory diskFactory = Singleton<DiskFactory>.Instance;
        for(int i = 0; i < diskNumber; i++)
        {
            diskQueue.Enqueue(diskFactory.getDisk(currentRound, actionMode));
        }
        actionManager.startThrow(diskQueue);
    }

    void throwDisk()
    {
        if(diskQueue.Count != 0)
        {
            GameObject disk = diskQueue.Dequeue();
            Vector3 pos = new Vector3(-disk.GetComponent<DiskData>().getDirection().x * 10, Random.Range(0f, 4f), 0);
            disk.transform.position = pos;
            disk.SetActive(true);
        }
    }

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

    public GameState getGameState()
    {
        return gameState;
    }

    public ActionMode getActionMode()
    {
        return actionMode;
    }

    public void setGameState(GameState gameState)
    {
        this.gameState = gameState;
    }

    public void setActionMode(ActionMode mode)
    {
        actionMode = mode;
        if(actionMode == ActionMode.PHYSIC)
        {
            this.gameObject.AddComponent<PhysicActionManager>();
        }
        else if(actionMode == ActionMode.KINEMATIC)
        {
            this.gameObject.AddComponent<CCActionManager>();
        }
    }

    public void hit(Vector3 pos)
    {
        RaycastHit[] hits = Physics.RaycastAll(Camera.main.ScreenPointToRay(pos));
        for(int i = 0; i < hits.Length; i++)
        {
            RaycastHit hit = hits[i];
            if(hit.collider.gameObject.GetComponent<DiskData>() != null)
            {
                scoreRecorder.record(hit.collider.gameObject);
                hit.collider.gameObject.transform.position = new Vector3(0, -5, 0);
            }
        }
    }
}

UserGUI.cs

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

public class UserGUI : MonoBehaviour {
    private IUserAction action;
    bool isFirst = true;
    GUIStyle red;
    GUIStyle black;
    bool pause_flag = true;
    // Use this for initialization
    void Start () {
        action = Director.getInstance().current as IUserAction;
        black = new GUIStyle("button");
        black.fontSize = 20;
        red = new GUIStyle();
        red.fontSize = 30;
        red.fontStyle = FontStyle.Bold;
        red.normal.textColor = Color.red;
        red.alignment = TextAnchor.UpperCenter;
    }

    private void OnGUI()
    {
        if (action.getGameState() == GameState.FUNISH)
        {
            GUI.Label(new Rect(Screen.width / 2 - 100, Screen.height / 2 - 150, 200, 100), action.getScore() >= 30 ? "You win" : "You fail", red);
            if(GUI.Button(new Rect(Screen.width / 2 - 60, Screen.height / 2 - 50, 120, 40), "Restart", black))
            {
                SceneManager.LoadScene("DiskAttack");
            }
            return;
        }
        Rect rect = new Rect(Screen.width / 2 - 100, 0, 200, 40);
        Rect rect2 = new Rect(Screen.width / 2 - 60, 60, 120, 40);

        if (Input.GetButtonDown("Fire1") && action.getGameState() != GameState.PAUSE)
        {
            Vector3 pos = Input.mousePosition;
            action.hit(pos);
        }

        if (!isFirst)
        {
            GUI.Label(rect, "Your score: " + action.getScore().ToString(), red);
        }
        else
        {
            GUIStyle blackLabel = new GUIStyle();
            blackLabel.fontSize = 16;
            blackLabel.normal.textColor = Color.black;
            GUI.Label(new Rect(Screen.width / 2 - 250, 120, 500, 200), "There are 3 rounds, every round has 10 disk " +
                "whose color is different.\nIf you attack the white one, you will get 1 score. And you will get 2 score\n" +
                "if you attack the gray one. Finally, if you can attack the black and most\nfast one, you will get 4 " +
                "score. Once you get 30 scores, you win!", blackLabel);
        }

        if (pause_flag)
        {
            if (action.getGameState() == GameState.RUNNING && GUI.Button(rect2, "Paused", black))
            {
                action.setGameState(GameState.PAUSE);
            }
            else if (action.getGameState() == GameState.PAUSE && GUI.Button(rect2, "Run", black))
            {
                action.setGameState(GameState.RUNNING);
            }
        }

        if(action.getActionMode() == ActionMode.NOTSET)
        {
            if(GUI.Button(new Rect(Screen.width / 2 - 60, 0, 120, 40), "PHYSIC", black))
            {
                action.setActionMode(ActionMode.PHYSIC);
                pause_flag = false;
            }
            if (GUI.Button(rect2, "KINEMATIC", black))
            {
                action.setActionMode(ActionMode.KINEMATIC);
            }
        }
        else if (isFirst && GUI.Button(rect2, "Start", black))
        {
            isFirst = false;
            action.setGameState(GameState.ROUND_START);
        }

        if(!isFirst && action.getGameState() == GameState.ROUND_FINISH && GUI.Button(rect2, "Next Round", black))
        {
            action.setGameState(GameState.ROUND_START);
        }
    }
}

4.结果视频

http://v.youku.com/v_show/id_XMzU2MjQ3NDAxNg==.html?spm=a2h0k.8191407.0.0&from=s1.8-1-1.2

猜你喜欢

转载自blog.csdn.net/m0_37782473/article/details/80071010
今日推荐