Untiy3D – 3 Frisbee Game

Time:2019-5-15

Frisbee games

The main task of this time is to write a flying saucer game, familiar with the previous MVC mode and action separation, you can reuse part of the previous code.

Game Scene Screenshots

This time a simple sky box and ground were built. The field record is still loaded on the empty object GameObject and controls the UFO loading.
Untiy3D - 3 Frisbee Game
Untiy3D - 3 Frisbee Game

Code organization structure

Untiy3D - 3 Frisbee Game

  1. The core of this code is Disk and DiskFactory, which use factory mode to control the generation and reuse of objects.

    • Disk class: Stores some basic attributes and can modify gameObject attributes directly through these attributes.
        public class Disk : MonoBehaviour {
            public Vector3 StartPoint { get { return gameObject.transform.position; } set { gameObject.transform.position = value; } }
            public Color color { get { return gameObject.GetComponent<Renderer>().material.color; } set { gameObject.GetComponent<Renderer>().material.color = value; } }
            public float speed { get;set; }
            public Vector3 Direction { get { return Direction; } set { gameObject.transform.Rotate(value); } }
        }
    • DiskFactory class: Disk prop factory, reduce the number of destruction of game objects, reuse game objects, and shield the business logic of creation and destruction, making the program easy to expand. Specifically in this code, DiskFactory is also responsible for randomly specifying the starting position, direction, speed and color when producing Disk.

Untiy3D - 3 Frisbee Game

Public class DiskFactory {// Inherited MonoBehaviour at the beginning and later found that inheritance was OK without inheritance.
    public GameObject diskPrefab;
    public static DiskFactory DF = new DiskFactory();

    Private Dictionary < int, Disk > used = new Dictionary < int, Disk >(); //use is used to save flying saucers in use 
    Private List < Disk > free = new List < Disk >(); //free is used to save inactive UFOs 

    private DiskFactory()
    {
        DiskPrefab = GameObject. Instantiate < GameObject > (Resources. Load < GameObject > ("Prefabs/disk"); //Get prefabricated game objects
        diskPrefab.AddComponent<Disk>();
        diskPrefab.SetActive(false);
    }

    public void FreeDisk()
    {
        foreach (Disk x in used.Values)
        {
            if (!x.gameObject.activeSelf)
            {
                free.Add(x);
                used.Remove(x.GetInstanceID());
                return;
            }
        }
    }

    public Disk GetDisk(int round)  
    {
        FreeDisk();
        GameObject newDisk = null;
        Disk diskdata;
        if (free.Count > 0)
        {
            // Take out available Disks from previous production
            newDisk = free[0].gameObject;
            free.Remove(free[0]);
        }
        else
        {
            // Cloning Prefabricated Objects to Produce New Disk
            newDisk = GameObject.Instantiate<GameObject>(diskPrefab, Vector3.zero, Quaternion.identity);
        }
        newDisk.SetActive(true);
        diskdata = newDisk.AddComponent<Disk>();

        int swith;

        /** 
        * According to the number of rounds to generate the corresponding flying saucer, the difficulty gradually increases.
        */
        float s;
        if (round == 1)
        {
            swith = Random.Range(0, 3);
            s = Random.Range(30, 40);
        }
        else if (round == 2)
        {
            swith = Random.Range(0, 4);
            s = Random.Range(40, 50);
        }
        else {
            swith = Random.Range(0, 6);
            s = Random.Range(50, 60);
        } 
        
        switch (swith)  
        {  
            
            case 0:  
                {  
                    diskdata.color = Color.yellow;  
                    diskdata.speed = s;  
                    float RanX = UnityEngine.Random.Range(-1f, 1f) < 0 ? -1 : 1;  
                    diskdata.Direction = new Vector3(RanX, 1, 0);
                    diskdata.StartPoint = new Vector3(Random.Range(-130, -110), Random.Range(30,90), Random.Range(110,140));
                    break;  
                }  
            case 1:  
                {  
                    diskdata.color = Color.red;  
                    diskdata.speed = s + 10;  
                    float RanX = UnityEngine.Random.Range(-1f, 1f) < 0 ? -1 : 1;  
                    diskdata.Direction = new Vector3(RanX, 1, 0);
                    diskdata.StartPoint = new Vector3(Random.Range(-130, -110), Random.Range(30, 80), Random.Range(110, 130));
                    break;  
                }  
            case 2:  
                {  
                    diskdata.color = Color.black;  
                    diskdata.speed = s + 15;  
                    float RanX = UnityEngine.Random.Range(-1f, 1f) < 0 ? -1 : 1;  
                    diskdata.Direction = new Vector3(RanX, 1, 0);
                    diskdata.StartPoint = new Vector3(Random.Range(-130,-110), Random.Range(30, 70), Random.Range(90, 120));
                    break;  
                }
            case 3:
                {
                    diskdata.color = Color.yellow;
                    diskdata.speed = -s;
                    float RanX = UnityEngine.Random.Range(-1f, 1f) < 0 ? -1 : 1;
                    diskdata.Direction = new Vector3(RanX, 1, 0);
                    diskdata.StartPoint = new Vector3(Random.Range(130, 110), Random.Range(30, 90), Random.Range(110, 140));
                    break;
                }
            case 4:
                {
                    diskdata.color = Color.red;
                    diskdata.speed = -s - 10;
                    float RanX = UnityEngine.Random.Range(-1f, 1f) < 0 ? -1 : 1;
                    diskdata.Direction = new Vector3(RanX, 1, 0);
                    diskdata.StartPoint = new Vector3(Random.Range(130, 110), Random.Range(30, 80), Random.Range(110, 130));
                    break;
                }
            case 5:
                {
                    diskdata.color = Color.black;
                    diskdata.speed = -s - 15;
                    float RanX = UnityEngine.Random.Range(-1f, 1f) < 0 ? -1 : 1;
                    diskdata.Direction = new Vector3(RanX, 1, 0);
                    diskdata.StartPoint = new Vector3(Random.Range(130, 110), Random.Range(30, 70), Random.Range(90, 120));
                    break;
                }
        }
        Used. Add (diskdata. GetInstanceID (), diskdata); // Add to use
        diskdata.name = diskdata.GetInstanceID().ToString();
        return diskdata;  
    }
}
  1. Last time I wrote about the motion separation version of the clergyman and the devil. This time I can reuse some of the motion separation code directly. The main modification is to rewrite MoveToAction to make Disk move like a free falling body with horizontal initial velocity (in fact, it can use rigid body attributes and then add gravity, here I have no choice to use for less modification of the code), and then rewrite it here. CCActionManager class in each scenario. Here is the rewritten code:

    • Rewritten MoveToAction class:
    public class CCMoveToAction : SSAction
        {
            public float speedx;
            public float speedy = 0;
    
            private CCMoveToAction() { }
            public static CCMoveToAction getAction(float speedx)
            {
                CCMoveToAction action = CreateInstance<CCMoveToAction>();
                action.speedx = speedx;
                return action;
            }
    
            public override void Update()
            {
                // Parabolic motion with simulated acceleration of 10
                this.transform.position += new Vector3(speedx*Time.deltaTime, -speedy*Time.deltaTime+(float)-0.5*10*Time.deltaTime*Time.deltaTime,0);
                speedy += 10*Time.deltaTime;
                If (transform. position. y <= 1)// clicked and moved to the ground all meet the recovery conditions.
                {
                    destroy = true;
                    CallBack.SSActionCallback(this);
                }
            }
    
            public override void Start()
            {
    
            }
        }
    • Rewritten CCActionManager class:
    using Interfaces;
        public class CCActionManager : SSActionManager, SSActionCallback
        {
            Int count = 0; // Record the number of all moving dishes
            public SSActionEventType Complete = SSActionEventType.Completed;
    
            public void MoveDisk(Disk Disk)
            {
                count++;
                Complete = SSActionEventType.Started;
                CCMoveToAction action = CCMoveToAction.getAction(Disk.speed);
                addAction(Disk.gameObject,action,this);
            }
    
            Public void SSAction Callback (SSAction source) // Callback function after the end of the event
            {
                count--;
                Complete = SSActionEventType.Completed;
                source.gameObject.SetActive(false);
            }
    
            Public bool IsAllFinished ()// Mainly to prevent the scene from having objects at the end of the game, but the GUI button has been loaded.
            {
                if (count == 0) return true;
                else return false;
            }
        }
  2. Rewrite the Interfaces and define the functions according to the needs of the interaction.

    namespace Interfaces
        {
            public interface ISceneController
            {
                void LoadResources();
            }
    
            public interface UserAction
            {
                Void Hit (Vector 3 pos); // Click events occur, and click locations are transmitted to the logbook.
                Void Restart (); // restart the game
                int GetScore();
                bool RoundStop();
                void RoundPlus();
            }
    
            public enum SSActionEventType : int { Started, Completed }
    
            public interface SSActionCallback
            {
                void SSActionCallback(SSAction source);
            }
        }
  3. Finally, the most important thing is to rewrite the recording. The director is the same as before.

        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        using Interfaces;
    
        public class FirstSceneController : MonoBehaviour, ISceneController, UserAction
        {
            int score = 0;
            int round = 1;
            int tral = 0;
            bool start = false;
            CCActionManager Manager;
            DiskFactory DF;
    
            void Awake()
            {
                SSDirector director = SSDirector.getInstance();
                director.currentScenceController = this;
                DF = DiskFactory.DF;
                Manager = GetComponent<CCActionManager>();
            }
    
            // Use this for initialization
            void Start () {
                
            }
    
            // Update is called once per frame
            int count = 0;
            void Update () {
                if(start == true)
                {
                    count++;
                    if (count >= 80)
                    {
                        count = 0;
    
                        if(DF == null)
                        {
                            Debug.LogWarning("DF is NUll!");
                            return;
                        }
                        tral++;
                        Disk d = DF.GetDisk(round);
                        Manager.MoveDisk(d);
                        if (tral == 10)
                        {
                            round++;
                            tral = 0;
                        }
                    }
                }
            }
    
            public void LoadResources()
            {
                
            }
    
            public void Hit(Vector3 pos)
            {
                Ray ray = Camera.main.ScreenPointToRay(pos);
    
                RaycastHit[] hits;
                hits = Physics.RaycastAll(ray);
                for (int i = 0; i < hits.Length; i++)
                {
                    RaycastHit hit = hits[i];
    
                    if (hit.collider.gameObject.GetComponent<Disk>() != null)
                    {
                        Color c = hit.collider.gameObject.GetComponent<Renderer>().material.color;
                        if (c == Color.yellow) score += 1;
                        if (c == Color.red) score += 2;
                        if (c == Color.black) score += 3;
                        
                        hit.collider.gameObject.transform.position = new Vector3(0, -5, 0);
                    }
    
                }
            }
    
            public int GetScore()
            {
                return score;
            }
    
            public void Restart()
            {
                score = 0;
                round = 1;
                start = true;
            }
            public bool RoundStop()
            {
                if (round > 3)
                {
                    start = false;
                    return Manager.IsAllFinished();
                }
                else return false;
            }
            public int GetRound()
            {
                return round;
            }
        }
  4. User interface GUI, this time GUI is relatively simple, mainly added to display scores, time, and round panels.
    public class InterfaceGUI : MonoBehaviour {
        UserAction UserActionController;
        public GameObject t;
        bool ss = false;
        float S;
        float Now;
        int round = 1;
        // Use this for initialization
        void Start () {
            UserActionController = SSDirector.getInstance().currentScenceController as UserAction;
            S = Time.time;
        }
        private void OnGUI()
        {
            if(!ss) S = Time.time;
            GUI.Label(new Rect(1000, 50, 500, 500),"Score: " + UserActionController.GetScore().ToString() + "  Time:  " + ((int)(Time.time - S)).ToString() + "  Round:  " + round );
            if (!ss && GUI.Button(new Rect(Screen.width / 2 - 30, Screen.height / 2 - 30, 100, 50), "Start"))
            {
                S = Time.time;
                ss = true;
                UserActionController.Restart();
            }
            if (ss)
            {
                round = UserActionController.GetRound();
                if (Input.GetButtonDown("Fire1"))
                {

                    Vector3 pos = Input.mousePosition;
                    UserActionController.Hit(pos);

                }
                if (round > 3)
                {
                    round = 3;
                    if (UserActionController.RoundStop())
                    {
                        ss = false;
                    }
                }
            }
        }
    }

GItHUB portal