Unity3D 从入门到放弃(四)----打飞碟

来源:互联网 发布:源码社区uuv9 编辑:程序博客网 时间:2024/04/30 01:18

Unity3D 从入门到放弃(四)

—–打飞碟


填坑计划的万恶之源

写在开头:
本来感觉应该是不会写博客的,而且也不是很擅长写 博客。但在后来,突然醒悟到,博客这个东西,实际上并不是给别人看的(感觉也不会有人看),更重要的,是给自己一个提示,一个记录。只有把原始森林中所有的危险地点全部都标记下来,下次来的时候才不会重蹈覆辙。
该说的就这么多,要赶紧填坑了。


打飞碟

程序规则:写个用鼠标打飞碟的游戏。
游戏要分多个 round , 飞碟数量每个 round 都是 n 个,但色彩,大小;发射位置,速度,角度,每次发射数量可以变化。
游戏过程中,仅能创建 n 个飞碟, 且不容许初始化阶段生成任何飞碟。 飞碟线路计算请使用 mathf 类。 向下加速度 a 是常数。 飞碟被用户击中,则回收。并按你定义的规则计算分数。飞碟落地(y < c),则自动回收。


预设部分

程序中用到的对象如下:

飞碟:创建一个Cylinder,将该物体的scale.y改成0.1,加入预设。

地板:新建一个Plane进行拉伸,加上贴图后即可。(PS:如果是x,y拉伸不一样的,会导致墙面变形,建议用多个Plane组合

文本框组:在 Component的UI里新建text,并设置三个文本,分别表示当前回合数,当前分数和倒计时。

效果如下:
这里写图片描述


逻辑部分

先放上UML图:(不知道看不看得懂)
这里写图片描述

场景切换思想:
运用enum Status,进行不同类在执行完动作后的回调函数中切换状态,并且利用Singleton类保持类的唯一性。
类似下面代码:

/*void Update() {    if (nowState == 符合当前类执行的状态) {        // 实现当前类操作        if (任务完成条件达成) {            // 重置或者设置某些状态            nowState = 符合下一个类执行的状态;        }    }}*/

实际代码

辅助类文件

BaseAction类(模板):

// ========================================================// 描 述:动作基类文件// 作 者:hza // 创建时间:2017/04/01 14:14:33// 版 本:v 1.0// ========================================================using System.Collections;using System.Collections.Generic;using UnityEngine;namespace Tem.Action{    public enum SSActionEventType : int { STARTED, COMPLETED }    public interface ISSActionCallback    {        void SSEventAction(SSAction source, SSActionEventType events = SSActionEventType.COMPLETED,            int intParam = 0, string strParam = null, Object objParam = null);    }    public class SSAction : ScriptableObject // 动作的基类    {        public bool enable = true;        public bool destory = false;        public GameObject gameObject { get; set; }        public Transform transform { get; set; }        public ISSActionCallback callback { get; set; }        public virtual void Start()        {            throw new System.NotImplementedException("Action Start Error!");        }        public virtual void Update()        {            throw new System.NotImplementedException("Action Update Error!");        }    }    public class CCMoveToAction : SSAction    {        public Vector3 target;        public float speed;        public static CCMoveToAction GetSSAction(Vector3 _target, float _speed)        {            CCMoveToAction currentAction = ScriptableObject.CreateInstance<CCMoveToAction>();            currentAction.target = _target;            currentAction.speed = _speed;            return currentAction;        }        public override void Start()        {        }        public override void Update()        {            this.transform.position = Vector3.MoveTowards(this.transform.position, target, speed * Time.deltaTime);            if (this.transform.position == target)            {                this.destory = true;                this.callback.SSEventAction(this);            }        }    }    public class CCBezierMoveAction : SSAction // 仅用于三次贝塞尔曲线    {        public List<Vector3> vectors;        public Vector3 target;        public float speed;        private Vector3 begin;        private float t;        private bool firstTime;        /*private Vector3 vector2begin;        private Vector3 vector2mid;        private Vector3 vector2end;        private Vector3 vector3begin;        private Vector3 vector3end;        private float timeBegin;        private float timeDiff;*/        public static CCBezierMoveAction GetCCBezierMoveAction(List<Vector3> _vectors, float _speed)            // vector里面最后一个值是目标位置        {            CCBezierMoveAction action = ScriptableObject.CreateInstance<CCBezierMoveAction>();            action.vectors = _vectors;            action.target = _vectors[_vectors.Count - 1];            action.vectors.RemoveAt(action.vectors.Count - 1);            action.speed = _speed;            return action;        }        public override void Start() // 公式写法        {            //timeDiff = 0;            firstTime = true;            t = 0;        }        public override void Update()        {            if (firstTime)            {                speed = speed / Vector3.Distance(this.transform.position, target) / 1.5f;                // 速度除以相对距离并除以2作为速度比                begin = this.transform.position;                firstTime = false;            }            t += Time.deltaTime * speed;            if (t > 1) t = 1;            float _t = 1 - t;            this.transform.position = begin * Mathf.Pow(_t, 3) + 3 * vectors[0] * Mathf.Pow(_t, 2) * t                + 3 * vectors[1] * _t * Mathf.Pow(t, 2) + target * Mathf.Pow(t, 3);            if (this.transform.position == target)            {                this.destory = true;                this.callback.SSEventAction(this);            }        }        /*public override void Update() // 正常写法        {            timeDiff += Time.deltaTime;            vector2begin = Vector3.Lerp(this.transform.position, vectors[0], speed * timeDiff);            vector2mid = Vector3.Lerp(vectors[0], vectors[1], speed * timeDiff);            vector2end = Vector3.Lerp(vectors[1], target, speed * timeDiff);            // 第一次计算差值            vector3begin = Vector3.Lerp(vector2begin, vector2mid, speed * timeDiff);            vector3end = Vector3.Lerp(vector2mid, vector2end, speed * timeDiff);            // 第二次计算差值            this.transform.position = Vector3.Lerp(vector3begin, vector3end, speed * timeDiff);            // 最后一次计算差值            if (this.transform.position == target)            {                this.destory = true;                this.callback.SSEventAction(this);            }        }*/    }    public class CCSequenceAction : SSAction, ISSActionCallback    {        public List<SSAction> sequence;        public int repeat = -1;        public int start = 0;        public static CCSequenceAction GetSSAction(List<SSAction> _sequence, int _start = 0, int _repead = 1)        {            CCSequenceAction actions = ScriptableObject.CreateInstance<CCSequenceAction>();            actions.sequence = _sequence;            actions.start = _start;            actions.repeat = _repead;            return actions;        }        public override void Start()        {            foreach (SSAction ac in sequence)            {                ac.gameObject = this.gameObject;                ac.transform = this.transform;                ac.callback = this;                ac.Start();            }        }        public override void Update()        {            if (sequence.Count == 0) return;            if (start < sequence.Count) sequence[start].Update();        }        public void SSEventAction(SSAction source, SSActionEventType events = SSActionEventType.COMPLETED,            int intParam = 0, string strParam = null, Object objParam = null) //通过对callback函数的调用执行下个动作        {            source.destory = false; // 当前动作不能销毁(有可能执行下一次)            this.start++;            if (this.start >= this.sequence.Count)            {                this.start = 0;                if (this.repeat > 0) repeat--;                if (this.repeat == 0)                {                    this.destory = true;                    this.callback.SSEventAction(this);                }            }        }        private void OnDestroy()        {            this.destory = true;        }    }    public class SSActionManager : MonoBehaviour    {        private Dictionary<int, SSAction> dictionary = new Dictionary<int, SSAction>();        private List<SSAction> watingAddAction = new List<SSAction>();        private List<int> watingDelete = new List<int>();        protected void Start()        {        }        protected void Update()        {            foreach (SSAction ac in watingAddAction) dictionary[ac.GetInstanceID()] = ac;            watingAddAction.Clear();            // 将待加入动作加入dictionary执行            foreach (KeyValuePair<int, SSAction> dic in dictionary)            {                SSAction ac = dic.Value;                if (ac.destory) watingDelete.Add(ac.GetInstanceID());                else if (ac.enable) ac.Update();            }            // 如果要删除,加入要删除的list,否则更新            foreach (int id in watingDelete)            {                SSAction ac = dictionary[id];                dictionary.Remove(id);                DestroyObject(ac);            }            watingDelete.Clear();            // 将deletelist中的动作删除        }        public void runAction(GameObject gameObject, SSAction action, ISSActionCallback callback)        {            action.gameObject = gameObject;            action.transform = gameObject.transform;            action.callback = callback;            watingAddAction.Add(action);            action.Start();        }    }}

Don’tclickme(用于帮其他文件在创建的时候添加注释):

using UnityEngine;using System.Collections;using System.IO;public class ChangeScriptTemplates : UnityEditor.AssetModificationProcessor{     // 添加脚本注释模板     private static string str =      "// ========================================================\r\n"     +"// 描 述:\r\n"     +"// 作 者:hza \r\n"     +"// 创建时间:#CreateTime#\r\n"     +"// 版 本:v 1.0\r\n"     +"// ========================================================\r\n\n";     // 创建资源调用     public static void OnWillCreateAsset(string path)     {        // 只修改C#脚本        path = path.Replace(".meta", "");        if (path.EndsWith(".cs"))        {            string allText = str;            allText += File.ReadAllText(path);            // 替换字符串为系统时间            allText = allText.Replace("#CreateTime#",System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"));            File.WriteAllText(path, allText);        }     }}

Singleton:

// ========================================================// 描 述:这是一个单实例模板,用来引用别的继承monobehaviour的单实例类   // 作 者:hza // 创建时间:2017/03/27 00:02:13// 版 本:v 1.0// ========================================================using System.Collections;using System.Collections.Generic;using UnityEngine;public class Singleton<T> : MonoBehaviour where T : MonoBehaviour {    // 私有static变量    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 it not!");                }            }            return instance;        }    }}

实际类文件

SceneController:

// ========================================================// 描 述:场景控制,主要控制开始结束按钮和场景启动// 作 者:hza // 创建时间:2017/03/27 00:21:36// 版 本:v 1.0// ========================================================using System.Collections;using System.Collections.Generic;using UnityEngine;using My.Disk;using UnityEngine.UI;public enum Status { BEGIN, COUNTING, WATING, GAMING, OVER }public class SceneController : MonoBehaviour{    public ParticleSystem boom;    public Text centerText;    public Text scoreText;    public Text roundText;    public ScoreRecorder recorder;    RoundController round;    public Status nowState;    void Start()    {        nowState = Status.BEGIN;        centerText.text = "";        roundText.text = "";        recorder = new ScoreRecorder(scoreText);        round = Singleton<RoundController>.Instance;        round.resetRound();        recorder.setDisActive();        // 初始设置    }    private void OnGUI()    {        if (nowState == Status.BEGIN)        {            if (GUI.RepeatButton(new Rect(10, 10, 100, 30), "Game Rule"))                GUI.TextArea(new Rect(120, 10, Screen.width / 2, 30), "点击空格开始关卡,鼠标点击射击,击中所有目标即可进行下一关");            if (GUI.Button(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 25, 100, 50), "Play Game"))            {                nowState = Status.WATING;                recorder.resetScore();            }        }        else if (nowState == Status.OVER)        {            if (GUI.RepeatButton(new Rect(Screen.width / 2 - 50, Screen.height * 3 / 4 - 25, 100, 50), "Reset Game"))                restart();        }    }    public void restart()    {        round.resetRound();        centerText.text = "";        recorder.setDisActive();        roundText.text = "";        nowState = Status.BEGIN;    }}

ScoreRecorder:

// ========================================================// 描 述:用来计算得分,加减分数后将得分面板刷新// 作 者:hza // 创建时间:2017/03/29 21:53:48// 版 本:v 1.0// ========================================================using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.UI;public class ScoreRecorder {    public Text scoreText;    // 计分板    private int score;    // 纪录分数    public ScoreRecorder(Text _scoreText)    {        scoreText = _scoreText;    }    public void resetScore()    {        score = 0;    }    // 飞碟点击中加分    public void addScore(int addscore)    {        score += addscore;        scoreText.text = "Score:" + score;    }    public void setDisActive()    {        scoreText.text = "";    }    public void setActive()    {        scoreText.text = "Score:" + score;    }}

RoundController:

// ========================================================// 描 述:用于回合数控制,回合循环// 作 者:hza // 创建时间:2017/04/02 20:22:30// 版 本:v 1.0// ========================================================using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.UI;public class RoundController : MonoBehaviour {    public Text roundText;    public Text centerText;    // 外部引用    SceneController scene;    Count count;    ScoreRecorder recorder;    DiskActionManager actionManager;    // 类间引用    int round;    // Use this for initialization    void Start () {        scene = Singleton<SceneController>.Instance;        count = Singleton<Count>.Instance;        actionManager = Singleton<DiskActionManager>.Instance;        recorder = scene.recorder;    }    // Update is called once per frame    void Update () {        if (scene.nowState == Status.WATING)            // 等待阶段        {            centerText.text = "Round:" + round;            if (Input.GetKeyDown("space"))            {                count.beginCount();                ResumeRecord();                scene.nowState = Status.COUNTING;                // 开始计数            }        }    }    public void runRound()    {        actionManager.runActionByRound(round);    }    public void NextRound()    {        round++;    }    public void resetRound()    {        round = 1;    }    // 用于暂停显示回合数和当前分数    public void PauseRecord()    {        roundText.text = "";        recorder.setDisActive();    }    // 用于恢复显示回合数和当前分数    public void ResumeRecord()    {        roundText.text = "Round:" + round;        recorder.setActive();    }}

Count:

// ========================================================// 描 述:用于回合间的计数// 作 者:hza // 创建时间:2017/03/27 22:46:23// 版 本:v 1.0// ========================================================using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.UI;public class Count : MonoBehaviour {    public Text centerText;    bool active = false;    float beginTime;    // Update is called once per frame    void Update () {        if (!active) return;        // 不活跃        beginTime -= Time.deltaTime;        if (beginTime > 0)        {            centerText.text = "     " + countingNumber();        }        else        {            centerText.text = "";            SceneController scene = Singleton<SceneController>.Instance;            scene.nowState = Status.GAMING;            RoundController round = Singleton<RoundController>.Instance;            round.runRound();            active = false;            // 设置为不活跃        }    }    public void beginCount()    {        beginTime = 3;        active = true;    }    private int countingNumber()    {        return (int)beginTime + 1;    }}

DiskActionManager:

// ========================================================// 描 述:用于管理飞碟飞行,其中包含FlyAction类// 作 者:hza // 创建时间:2017/04/01 14:25:14// 版 本:v 1.0// ========================================================using System.Collections;using System.Collections.Generic;using UnityEngine;using Tem.Action;using My.Disk;using UnityEngine.UI;public class FlyAction : SSAction{    public Vector3 firstDirect;    // 飞行方向    private float time;    // 已经飞行时间    private static float g = 5f;    public static FlyAction GetSSAction(Vector3 _firstDirect)    {        FlyAction currentAction = ScriptableObject.CreateInstance<FlyAction>();        currentAction.firstDirect = _firstDirect;        currentAction.time = 0;        return currentAction;    }    public override void Start()    {    }    public override void Update()    {        if (!this.gameObject.activeSelf) // 如果飞碟已经回收        {            this.destory = true;            this.callback.SSEventAction(this, SSActionEventType.STARTED);            return;        }        time += Time.deltaTime;        transform.position += Time.deltaTime * firstDirect;        // 各个方向的匀速运动        transform.position += Vector3.down * g * time * Time.deltaTime;        // 竖直方向的匀加速运动        if (this.transform.position.y < 0)        {            this.destory = true;            DiskFactory fac = Singleton<DiskFactory>.Instance;            fac.freeDisk(gameObject);            // 回收飞碟            this.callback.SSEventAction(this);        }    }}public class DiskActionManager : SSActionManager, ISSActionCallback {    public GameObject cam;    public Text centerText;    DiskFactory dic;    Vector3 leftEmitPos = new Vector3(-15, 10, -5);    Vector3 rightRmitPos = new Vector3(15, 10, -5);    int sumNum;    bool isLose;    new void Start () {        dic = Singleton<DiskFactory>.Instance;    }    // Update is called once per frame    new void Update () {        base.Update();        if (Input.GetMouseButtonDown(0))        {            Ray ray = cam.GetComponent<Camera>().ScreenPointToRay(Input.mousePosition);            // 获取射线            RaycastHit hit;            if (Physics.Raycast(ray, out hit) && hit.collider.tag == "Disk")            // 如果点击的物体是Disk            {                SceneController scene = Singleton<SceneController>.Instance;                scene.recorder.addScore(10); // 射中+10分                /*boom.transform.position = hit.collider.gameObject.transform.position;                boom.GetComponent<Renderer>().material.color = hit.collider.GetComponent<Renderer>().material.color;                boom.Play();                // 粒子爆炸效果*/                dic.freeDisk(hit.collider.gameObject);                // 点中,毁掉自身脚本,返回工厂            }        }    }    public void runActionByRound(int round)    {        sumNum = round;        isLose = false;        GameObject disk;        for (int i = 0; i < round; i += 2)        {            disk = dic.setDiskOnPos(leftEmitPos);            FlyAction fly = FlyAction.GetSSAction(new Vector3(Random.Range(5f, 20), Random.Range(2.5f, 5), Random.Range(0, 0.75f)));            this.runAction(disk, fly, this);        }         for (int i = 1; i < round; i += 2)        {            disk = dic.setDiskOnPos(rightRmitPos);            FlyAction fly = FlyAction.GetSSAction(new Vector3(Random.Range(-20f, -5f), Random.Range(2.5f, 5), Random.Range(0, 0.75f)));            this.runAction(disk, fly, this);        }        // 设置飞碟发射,发射round个飞碟    }    // 回调函数    public void SSEventAction(SSAction source, SSActionEventType events = SSActionEventType.COMPLETED,        int intParam = 0, string strParam = null, Object objParam = null)     {        if (events == SSActionEventType.COMPLETED)            // 落到y轴以下        {            isLose = true;            sumNum--;        }        else        {            sumNum--;        }        if (sumNum == 0)            // 如果本回合结束        {            SceneController scene = Singleton<SceneController>.Instance;            if (isLose)            {                centerText.text = "  LOSE";                scene.nowState = Status.OVER;            }            else            {                RoundController round = Singleton<RoundController>.Instance;                round.NextRound();                round.PauseRecord();                scene.nowState = Status.WATING;            }        }    }}

DiskFactory:

// ========================================================// 描 述:飞碟工厂,用来生产飞碟和保留不用的飞碟// 作 者:hza // 创建时间:2017/03/27 00:02:54// 版 本:v 1.0// ========================================================using System.Collections;using System.Collections.Generic;using UnityEngine;namespace My.Disk{    public class DiskFactory : MonoBehaviour    {        private static List<GameObject> used = new List<GameObject>();        // 正在使用的对象链表        private static List<GameObject> free = new List<GameObject>();        // 正在空闲的对象链表        DiskInformation inf;        private void Start()        {            inf = new DiskInformation();        }        // 此函数表示将Target物体放到一个位置        public GameObject setDiskOnPos(Vector3 targetposition)        {            if (free.Count == 0)            {                GameObject aGameObject = Instantiate(Resources.Load("prefabs/Cylinder")                    , targetposition, Quaternion.identity) as GameObject;                // 新建实例,将位置设置成为targetposition                used.Add(aGameObject);            }            else            {                used.Add(free[0]);                free.RemoveAt(0);                used[used.Count - 1].SetActive(true);                used[used.Count - 1].transform.position = targetposition;                inf.processDisk(used[used.Count - 1]);                // 加工disk            }            return used[used.Count - 1];        }        public void freeDisk(GameObject oj)        {            oj.SetActive(false);            used.Remove(oj);            free.Add(oj);        }    }}

DiskInformation:

// ========================================================// 描 述:此类保存Disk的各种information,和构建disk的属性// 作 者:hza // 创建时间:2017/03/28 20:33:20// 版 本:v 1.0// ========================================================using System.Collections;using System.Collections.Generic;using UnityEngine;public class DiskInformation{    private Color color;    private float scale;    public void processDisk(GameObject _disk)    {        color = new Color(Random.Range(0f, 1f), Random.Range(0f, 1f), Random.Range(0f, 1f));        _disk.GetComponent<Renderer>().material.color = color;        // 初始化color        scale = Random.Range(2f, 4f);        _disk.transform.localScale *= scale;        // 初始化大小    }    public void processDisk(GameObject _disk, Color _color, float _scale)    {        _disk.GetComponent<Renderer>().material.color = _color;        _disk.transform.localScale *= _scale;        // 自己设置color和大小    }}

本次作业就结束了,其实搞清楚类的关系后发现蛮好写的。。。
(由于感冒生病现在才补交,不知道还有没有机会了TAT)

0 0
原创粉丝点击