zoukankan      html  css  js  c++  java
  • Unity3D 学习笔记

    注:本游戏设计环境为Unity 3D 4.6

    游戏要求:

    1. 假设有一支枪在摄像机位置,在放置三个小球作为距离标记,调整视角直到小球在下中部
    2. 将鼠标所在平面坐标,转换为子弹(球体)射出的角度方向。子弹使用物理引擎,初速度恒定。
    3. 游戏要分多个 round , 飞碟数量每个 round 都是 n 个,但色彩,大小;发射位置,速度,角度,每次发射数量按预定规则变化。
    4. 用户按空格后,321倒数3秒,飞碟飞出(物理引擎控制),点击鼠标,子弹飞出。飞碟落地,或被击中,则准备下一次射击。
    5. 以下是一些技术要求:

      • 飞碟用一个带缓存的工厂生产,template 中放置预制的飞碟对象

    一、游戏UML类图设计

    游戏最核心的几个部分是:

    SceneController

    GameModel

    ActionManager

    UFOFactory

    BulletFactory

    Judge

    UserInterface

    没有在UML类图中出现的两部分:

    SelfDestruct,挂载在Bullet Prefab上作为飞碟预设

    public class SelfDestruct : MonoBehaviour {
    
        public float t = 0;
    
        // Update is called once per frame
        void Update () {
            t += Time.deltaTime;
    
            if (t > 1.5f) {
                this.gameObject.GetComponent<shootBullet> ().Free();
                BulletFactory.getInstance().releaseBullet(this.gameObject);
    
                SceneController.GetInstance().trials++;
                SceneController.GetInstance().okToShoot = true;
            }
        }
    }

    Detect Collision,挂载在UFO Prefab上作为子弹预设

    public class DetectCollision : MonoBehaviour {
        
        UFOFactory ufoFactory = UFOFactory.getInstance();
        BulletFactory bulletFactory = BulletFactory.getInstance();
    
        void OnTriggerEnter (Collider other){
            SceneController.GetInstance ().onHittingUFO ();
            ufoFactory.releaseUFO (this.gameObject);
    
            other.gameObject.GetComponent<SelfDestruct> ().t = 0f;
            bulletFactory.releaseBullet (other.gameObject);
    
            SceneController.GetInstance ().okToShoot = true;
        }
        
    }

    二、核心类分析

    SceneController 场景控制类

    在这个类存放了许多相关的游戏数据:游戏状态,关数,分数,最高得分,与关数相关的场景数据;同时也存放了主摄像机的实例

    这个类扩展了两个接口:IJudgeEvents, IUserAction,分别由Judge,UserInterface使用

    namespace Com.ShootThatUFO {
        
        public interface IJudgeEvents {
            void onHittingUFO ();
    
            void onNotHittingUFO ();
    
            int getTrial();
    
            void gameFailed();
    
            void updateHighscore();
    
            void setGameStatus (int gameStatus);
    
            int getPoint ();
    
            int getHighscore();
    
            int getRound ();
        }
    
        public interface IUserActions {
    
            //Game Status, 0 for init, 1 for in game, 2 for mid round, 3 for failed, 4 for game cleared
            int getGameStatus ();
    
            void gameStart();
    
            int getRound ();
    
            int getUFONum ();
    
            int getPoint ();
    
            int getTrial ();
    
            int getHighscore ();
    
            void resetGame ();
        }
    
        public class SceneController : System.Object, IJudgeEvents, IUserActions {
            
            //Global Variables
            public static float GROUND = 0f;
    
            //Init
            public Camera mainCam;
    
            SceneController () {
                mainCam = UnityEngine.Camera.main;
                mainCam.transform.position = new Vector3 (0f, 2.56f, -10f);
                mainCam.transform.eulerAngles = new Vector3 (6.475f, 0f, 0f);
                mainCam.fieldOfView = 76f;
                mainCam.backgroundColor = Color.black;
    
                Physics.gravity = new Vector3 (0, -9.8f, 0);
    
                GameObject lightObj = new GameObject ();
                lightObj.name = "light";
                Light light = lightObj.AddComponent<Light> ();
                light.type = LightType.Directional;
    
            }
            //Game Data
            public int gameStatus = 0;
            public float gameStartCountDown = 3f;
            public bool okToShoot = true;
    
            public int round = 0;
            public int point = 0;
            public int trials = 0;
            public int ufoToToss = 20;
            public int highscore = 0;
    
            public float[] intervalPerRound = {2f, 2f, 1f, 1f, 0.5f};
            public float[] tossRange = {10f, 10f, 15f, 20f, 20f};
            public float[] tossSpeed = {20f, 20f, 25f, 25f, 30f};
            public float[] ufoScale = {0.5f, 0.8f, 1f};
            public Color[] ufoColor = {Color.green, Color.yellow, Color.red};
            public float[] tossMaxAngle = {0.6f,0.6f,0.6f,0.6f,0.7f};
    
            public void updateHighscore (){
                highscore = point;
            }
    
            //UI Interface
            public int getGameStatus (){
                return gameStatus;
            }
    
            public void gameStart (){
                gameStatus = 1;
            }
    
            public int getRound (){
                return round;
            }
    
            public int getUFONum (){
                return ufoToToss;
            }
    
            public int getPoint (){
                return point;
            }
    
            public int getTrial (){
                return trials;
            }
    
            public void resetGame (){
                round = 0;
                point = 0;
                trials = 0;
                ufoToToss = 20;
                _base_code.gameModel.t = 0f;
            }
    
            public int getHighscore (){
                return highscore;
            }
    
            //Judge Interface
            public void onHittingUFO (){
                point += 10;
            }
    
            public void onNotHittingUFO (){
                trials++;
            }
    
            public void gameFailed (){
                gameStatus = 3;
            }
    
            public void setGameStatus (int _gameStatus){
                this.gameStatus = _gameStatus;
            }
    
            //Singleton
            private static SceneController _instance;
            private BaseCode _base_code;
    
            
            public static SceneController GetInstance() {
                if (_instance == null) {
                    _instance = new SceneController ();
                }
                return _instance;
            }
            
            public BaseCode getBaseCode(){
                return _base_code;
            }
            
            internal void setBaseCode(BaseCode bc){
                if (_base_code == null) {
                    _base_code = bc;
                }
            }
    
        }
    
    //End of Namespace
    }

    GameModel 游戏模型

    这个类是游戏中最核心的部分,和SceneController, ActionManager, UFOFactory, BulletFactory聚合,负责投出飞碟,获取用户鼠标输入,发射子弹

    飞碟投出颜色,大小,射出角度,速度,方向,均有此类随机产生,随机范围由SceneController中静态决定

    public class GameModel : MonoBehaviour, IU3dActionCompleted {
    
        SceneController sceneController;
        public ActionManager actionManager;
        public UFOFactory ufoFactory;
        public BulletFactory bulletFactory;
        public float t = 0f;
    
        // Use this for initialization
        void Start () {
            sceneController = SceneController.GetInstance ();
            actionManager = ActionManager.GetInstance ();
            ufoFactory = UFOFactory.getInstance ();
    
            actionManager = ActionManager.GetInstance ();
            ufoFactory = UFOFactory.getInstance ();
            bulletFactory = BulletFactory.getInstance ();
    
        }
    
        public void OnActionCompleted (U3dAction action){
            sceneController.okToShoot = true;
            ufoFactory.releaseUFO (action.gameObject);
        }
    
        // Update is called once per frame
        void Update () {
    
            
            if (Input.GetMouseButtonDown (0) && sceneController.getGameStatus() == 1 && sceneController.okToShoot) {
    
                    Ray ray = sceneController.mainCam.ScreenPointToRay (Input.mousePosition);
    
                    GameObject bullet = bulletFactory.getNewBullet();
                    actionManager.applyShootBullet(bullet, ray.direction);
                    sceneController.okToShoot = false;
                }
            
            if (sceneController.getGameStatus() == 1){
    
                t += Time.deltaTime;
                if (t > sceneController.intervalPerRound[sceneController.round]) {
                    t = 0;
                    if (sceneController.ufoToToss != 0){
                        Vector3 tossTo = new Vector3 (Random.Range(-sceneController.tossRange[sceneController.round], 
                                                                   sceneController.tossRange[sceneController.round]), 
                                                      SceneController.GROUND, 10f);
                        
                        float tossAngle = Random.Range(0.56f, sceneController.tossMaxAngle[sceneController.round]);
                        
                        float tossSpeed = Random.Range(15f, sceneController.tossSpeed[sceneController.round]);
                        
    //                    Debug.Log ("Tossing! " + tossTo.x + " " + tossAngle + " " + tossSpeed);
                        
                        GameObject aUFO = ufoFactory.getNewUFO (sceneController.ufoScale[Random.Range(0, 3)], 
                                                                sceneController.ufoColor[Random.Range(0, 3)]);
                        actionManager.applyU3dTossUFO (aUFO, 
                                                       aUFO.transform.position, 
                                                       tossTo, 
                                                       tossAngle, 
                                                       tossSpeed, 
                                                       this);
                        sceneController.ufoToToss--;
                    } else {
                        sceneController.round++;
                        sceneController.ufoToToss = 20;
                        sceneController.gameStatus = 2;
                    }
                }
    
            }
        }
    }

    ActionManager 动作控制类

    控制动作的发生,包括UFO的投出,子弹的发射。并未通过复杂数学来实现,而是在void setting()中为rigidbody添加Impulse类型力。

    要注意的是,在回收动作时,要为rigidbody的velocity, angularVelocity设Vector3.zero,否则下次再添加Impulse时会出问题。

    public class ActionManager :System.Object {
        private static ActionManager _instance;
        
        public static ActionManager GetInstance(){
            if (_instance == null) {
                _instance = new ActionManager();
            }
            return _instance;
        }
    
        public U3dAction applyU3dTossUFO (GameObject obj, Vector3 _startPoint, Vector3 _finishPoint, float _initialAngle, float _speed, IU3dActionCompleted _completed){
    
            U3dTossUFO UTU = obj.AddComponent<U3dTossUFO> ();
            UTU.setting (_startPoint, _finishPoint, _initialAngle, _speed, _completed);
            return UTU;
        }
    
        public U3dAction applyU3dTossUFO (GameObject obj, Vector3 _startPoint, Vector3 _finishPoint, float _initialAngle, float _speed){
            return applyU3dTossUFO (obj, _startPoint, _finishPoint, _initialAngle, _speed, null);
        }
    
        public U3dAction applyShootBullet (GameObject obj, Vector3 direction){
            shootBullet sb = obj.AddComponent<shootBullet> ();
            sb.setting (direction);
            return sb;
        }
    }
    
    public class U3dActionException : System.Exception {}
    
    public interface IU3dActionCompleted {
        void OnActionCompleted (U3dAction action);
    }
    
    public class U3dAction : MonoBehaviour {
        public void Free(){}
    }
    
    public class U3dActionAuto : U3dAction {}
    
    public class U3dActionMan : U3dAction {}
    
    
    public class U3dTossUFO : U3dActionAuto {
    
        public Vector3 startPoint;
        public Vector3 finishPoint;
    
        public float forceStrength;
        public float initialAngle;
    
        private IU3dActionCompleted monitor = null;
    
    //        initialAngle in Radial
        public void setting (Vector3 _startPoint, Vector3 _finishPoint, float _initialAngle, float _speed, IU3dActionCompleted _monitor){
            this.startPoint = _startPoint;
    
            this.finishPoint = _finishPoint;
    
            this.initialAngle = _initialAngle;
    
            this.forceStrength = _speed;
    
            this.monitor = _monitor;
    
            Vector3 force = Vector3.Slerp(Vector3.Normalize(finishPoint - startPoint), Vector3.up, initialAngle);
    
            Debug.Log (force + " " + forceStrength);
    
            this.gameObject.rigidbody.AddForce (force * forceStrength, ForceMode.Impulse);
        }
    
        void FixedUpdate() {
    
            if (transform.position.y < SceneController.GROUND + 0.1f) {
                if (monitor != null){
                    monitor.OnActionCompleted (this);
                }
                this.Free();
            }
    
        }
    
        public void Free(){
            this.gameObject.rigidbody.velocity = Vector3.zero;
            this.gameObject.rigidbody.angularVelocity = Vector3.zero;
            Destroy (this);
        }
    
    }
    
    public class shootBullet : U3dActionAuto {
    
        Vector3 direction;
        float t = 0;
    
        public void setting (Vector3 direction){
            this.gameObject.rigidbody.AddForce (direction * 50f, ForceMode.Impulse);
        }
    
        void FixedUpdate() {
        }
    
        void OnTriggerEnter() {
            this.Free ();
        }
    
        public void Free(){
            this.gameObject.rigidbody.velocity = Vector3.zero;
            this.gameObject.rigidbody.angularVelocity = Vector3.zero;
            Destroy (this);
        }
    
    }

    UFOFactory 飞碟工厂

    维持两个List<GameObject>分别保存用过的飞碟,正在使用的飞碟,仅在没有用过的飞碟来重用的时候才Instantiate新的飞碟

    外部通过get, release两个方法来获取,消除飞碟

    public class UFOFactory : System.Object {
    
        //Singleton Implementation
        private static UFOFactory _instance = null;
    
        public static UFOFactory getInstance() {
            if (_instance == null) {
                _instance = new UFOFactory();
            }
            return _instance;
        }
    
        //Initalizing
        private GameObject _UFOPrefab;
        private GameObject _ExplosionPrefab;
    
        UFOFactory (){
    //            GameObject UFOPrimitive = GameObject.CreatePrimitive (PrimitiveType.Cylinder);
    //            UFOPrimitive.transform.localScale = new Vector3 (1f, 0.1f, 1f);
    //            UFOPrimitive.AddComponent<DetectCollision> ();
    //            Rigidbody uRB = UFOPrimitive.AddComponent<Rigidbody> ();
    //            uRB.drag = 1f;
    //            UFOPrimitive.collider.isTrigger = true;
    
    
    //            UnityEditor.AssetDatabase.CreateFolder ("Assets/Resources/Prefab");
    //            _UFOPrefab = UnityEditor.PrefabUtility.CreatePrefab ("Assets/Resources/Prefab/UFOPrefab.prefab", UFOPrimitive);
    
    
            _UFOPrefab = Resources.Load ("Prefab/UFOPrefab") as GameObject;
    //            UnityEngine.GameObject.Destroy (UFOPrimitive);
    
            _ExplosionPrefab = Resources.Load ("Exploson7", typeof (GameObject)) as GameObject;
    
            usedUFO = new List<GameObject> ();
            usingUFO = new List<GameObject> ();
        }
    
        //List of Gameobjects
        private List<GameObject> usedUFO;
        private List<GameObject> usingUFO;
    
        public GameObject getNewUFO (float scale, Color color) {
    
            GameObject aUFO;
    
            if (usedUFO.Count == 0) {
                aUFO = UnityEngine.GameObject.Instantiate(_UFOPrefab, new Vector3 (0f, SceneController.GROUND + 0.11f, -10f), Quaternion.identity) as GameObject;
                usingUFO.Add (aUFO);
                aUFO.tag = "Finish";
            }
            else {
                aUFO = usedUFO[0];
                usedUFO.Remove(aUFO);
                usingUFO.Add(aUFO);
                aUFO.SetActive (true);
            }
    
            aUFO.transform.position = new Vector3 (0f, SceneController.GROUND + 0.11f, -10f);
            aUFO.transform.localScale = new Vector3 (1f, 0.07f, 1f);
            aUFO.transform.localScale = aUFO.transform.localScale * scale;
            aUFO.renderer.material.color = color;
    
            return aUFO;
        }
    
        public void releaseUFO (GameObject UFOtoRelease){
            usingUFO.Remove (UFOtoRelease);
            usedUFO.Add (UFOtoRelease);
            UFOtoRelease.SetActive (false);
    
            UnityEngine.Object.Instantiate (_ExplosionPrefab, UFOtoRelease.transform.position, Quaternion.identity);
        }
    
    }

    BulletFactory 子弹工厂

    与UFO工厂类似

    public class BulletFactory : System.Object {
        
        //Singleton Implementation
        private static BulletFactory _instance = null;
        
        public static BulletFactory getInstance() {
            if (_instance == null) {
                _instance = new BulletFactory();
            }
            return _instance;
        }
        
        //Initalizing
        private GameObject _BulletPrefab;
        
        BulletFactory (){
    //            GameObject BulletPrimitive = GameObject.CreatePrimitive (PrimitiveType.Sphere);
    //            UFOPrimitive.transform.localScale = new Vector3 (0.3f, 0.3f, 0.3f);
    //            Rigidbody bRB = BulletPrimitive.AddComponent<Rigidbody> ();
    //            BulletPrimitive.AddComponent<SelfDestruct> ();
    
    //            _BulletPrefab = UnityEditor.PrefabUtility.CreatePrefab ("Assets/Resources/Prefab/bulletPrefab.prefab", BulletPrimitive);
    
            usedBullet = new List<GameObject> ();
            usingBullet = new List<GameObject> ();
    
        }
        
        //List of Gameobjects
        private List<GameObject> usedBullet;
        private List<GameObject> usingBullet;
        
        public GameObject getNewBullet () {
            GameObject bullet;
    
            if (usedBullet.Count == 0) {
                bullet = UnityEngine.Object.Instantiate (Resources.Load ("Prefab/Bullet", typeof (GameObject))) as GameObject;
                usingBullet.Add (bullet);
            }
            else {
                Debug.Log ("Here");
                bullet = usedBullet[0];
                bullet.transform.position = UnityEngine.Camera.main.transform.position;
                bullet.SetActive(true);
                usedBullet.Remove(bullet);
                usingBullet.Add (bullet);
            }
    
            bullet.GetComponent<SelfDestruct> ().t = 0f;
            return bullet;
        }
        
        public void releaseBullet (GameObject bullettoRelease){
            bullettoRelease.SetActive (false);
            usingBullet.Remove (bullettoRelease);
            usedBullet.Add (bullettoRelease);
        }
        
    }

    Judge 裁判类

    通过检测SceneController中的关键游戏数据,来判断游戏状态

    //这些关键数据是否存放在裁判类中会更好?

    public class Judge : MonoBehaviour{
    
        IJudgeEvents iJudege;
    
        void Start (){
            iJudege = SceneController.GetInstance() as IJudgeEvents;
        }
    
        void Update (){
    
            if (iJudege.getPoint() > iJudege.getHighscore()) {
                iJudege.updateHighscore();
            }
            
            if (iJudege.getRound() == 5) {
                iJudege.setGameStatus(4);
            }
    
            if (iJudege.getTrial() > 9) {
                iJudege.gameFailed();
            }
        }
    
    }

    UserInterface 界面类

    控制游戏界面,同时接受用户启动游戏的命令(空格)

    public class UserInterface : MonoBehaviour {
    
        IUserActions userAction;
        public bool spaceHitted = false;
        public float gameStartTimer = 3f;
    
        // Use this for initialization
        void Start () {
            userAction = SceneController.GetInstance () as IUserActions;
        }
    
        void OnGUI () {
            // Make a background box
    
            if (userAction.getGameStatus() == 1) {
                GUI.Label (new Rect(20,20,80,20), "Round " + (userAction.getRound() + 1));
                GUI.Label (new Rect (20, 40, 80, 20), userAction.getUFONum () + " to Go");
            }
    
            if (userAction.getGameStatus() == 0 || userAction.getGameStatus() == 2) {
                GUI.Label (new Rect(20,20,300,20), "Shoot That UFO! Hit Space Bar to Begin!");
                GUI.Label (new Rect (20, 40, 100, 20), "Time to start: " + gameStartTimer.ToString("0.0"));
                GUI.Label (new Rect (20, 100, 100, 20), "Next Round: " + (userAction.getRound() + 1));
            }
    
            if (userAction.getGameStatus () == 3) {
                GUI.Label (new Rect (20, 20, 200, 20), "Failed! Hit Spacebar to Restart.");
                GUI.Label (new Rect (20, 40, 100, 20), "Time to start: " + gameStartTimer.ToString("0.0"));
            }
    
            if (userAction.getGameStatus () != 4) {
                GUI.Label (new Rect (20, 80, 150, 20), "You have " + (10f - userAction.getTrial()) + " bullets left.");
                GUI.Label (new Rect (700, 20, 100, 20), "Highscore: " + userAction.getHighscore());
            }
    
            if (userAction.getGameStatus () == 4) {
                GUI.Label (new Rect(20,20,150,20), "Game Cleared!");
            }
    
            GUI.Label (new Rect (20, 60, 150, 20), "Your score is " + userAction.getPoint());
        }
    
        // Update is called once per frame
        void FixedUpdate () {
    
            if (userAction.getGameStatus () != 1) {
                if (Input.GetKey (KeyCode.Space)) {
                    spaceHitted = true;
                    if (userAction.getGameStatus () == 3){
                        userAction.resetGame();
                    }
                }
                if (spaceHitted) {
                    gameStartTimer -= Time.deltaTime;
                }
                if (gameStartTimer < 0f) {
                    userAction.gameStart();
                    gameStartTimer = 3f;
                    spaceHitted = false;
                }
            }
            
        }
    }

    BaseCode 基础代码

    将此代码挂载到游戏中,此代码将挂载Judge, GameModel, UserInterface到主摄像机中

    public class BaseCode : MonoBehaviour {
    
        SceneController sceneController;
        public Judge judge;
        public GameModel gameModel;
        public UserInterface ui;
    
        // Use this for initialization
        void Start () {
            sceneController = SceneController.GetInstance ();
    
            sceneController.setBaseCode (this);
    
            gameModel = UnityEngine.Camera.main.gameObject.AddComponent <GameModel> ();
    
            judge = UnityEngine.Camera.main.gameObject.AddComponent <Judge> ();
    
            ui = UnityEngine.Camera.main.gameObject.AddComponent <UserInterface> ();
        }
    
    }
  • 相关阅读:
    拉格朗日插值
    文档 所有空格变为Tab
    windows 计算器
    map 结构体
    插入图片 图片地址
    扩展中国剩余定理
    欧拉定理、欧拉函数、a/b%c
    hdu1033Defragment
    Minimum Inversion Number_线段树||树状数组
    hdu1166敌兵布阵_线段树单点更新
  • 原文地址:https://www.cnblogs.com/wangsta/p/5376328.html
Copyright © 2011-2022 走看看