3D游戏——unity实现打飞碟

  • 游戏内容要求:
    1. 游戏有 n 个 round,每个 round 都包括10 次 trial;
    2. 每个 trial 的飞碟的色彩、大小、发射位置、速度、角度、同时出现的个数都可能不同。它们由该 round 的 ruler 控制;
    3. 每个 trial 的飞碟有随机性,总体难度随 round 上升;
    4. 鼠标点中得分,得分规则按色彩、大小、速度不同计算,规则可自由设定。
  • 游戏的要求:
    • 使用带缓存的工厂模式管理不同飞碟的生产与回收,该工厂必须是场景单实例的!具体实现见参考资源 Singleton 模板类
    • 近可能使用前面 MVC 结构实现人机交互与游戏模型分离

UML图:

整体框架与牧师和魔鬼相似,这里只会讲述部分不一样的代码。

场景单实例的实现:

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

对于飞碟飞行的控制:

public class CCMoveToAction : SSAction
{
    public float Power = 10;// 初速度
    public float Angle = 45;// 初角度
    public float Gravity = -10;// 初重力值
    private Vector3 MoveSpeed;
    private Vector3 GritySpeed = Vector3.zero;
    private float dTime;
    private Vector3 currentAngle;
    public DiskFactory diskfactory;
    public UserGUI User;
    public static CCMoveToAction GetSSAction(float Power,float Angle,float Gravity,Vector3 MoveSpeed)
    {
        CCMoveToAction action = ScriptableObject.CreateInstance<CCMoveToAction>();
        action.Power = Power;
        action.Angle = Angle;
        action.Gravity = Gravity;
        action.MoveSpeed = MoveSpeed;
        return action;
    }
    public override void Update()
    {
        GritySpeed.y = Gravity * (dTime += Time.fixedDeltaTime);
        transform.position += (MoveSpeed + GritySpeed) * Time.fixedDeltaTime;
        currentAngle.z = Mathf.Atan((MoveSpeed.y + GritySpeed.y) / MoveSpeed.x) * Mathf.Rad2Deg;
        transform.eulerAngles = currentAngle;
        // 飞碟飞出场景,血量-1,回收飞碟    
        if(this.transform.position.y < -7 || this.transform.position.x > 15){
            if(this.transform.position.x > 0 && User.HP > 0) User.HP--;
            diskfactory.FreeDisk(this.gameobject);
            this.destroy = true;
            this.callback.SSActionEvent(this);
        }
    }

    public override void Start(){
        MoveSpeed = Quaternion.Euler(new Vector3(0, 0, Angle)) * Vector3.right * Power;
        currentAngle = Vector3.zero;
        diskfactory = Singleton<DiskFactory>.Instance;
        User = Singleton<UserGUI>.Instance;
    }
}

对飞碟飞行的管理,这个类会调用上面的CCMoveToAction使得飞碟具有飞行动作

public class MySceneActionManager : SSActionManager
{
    private CCMoveToAction diskfly;
    public Controll sceneController;
    
    protected new void Start()
    {
        sceneController = (Controll)SSDirector.GetInstance().CurrentScenceController;
        sceneController.actionManager = this;
    }

    public void fly_disk(Disk disk){
        diskfly = CCMoveToAction.GetSSAction(disk.Power,disk.Angle,disk.Gravity,disk.MoveSpeed);
        this.RunAction(disk.getGameObject(), diskfly, this);
    }
}

用户界面:

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

public class UserGUI : MonoBehaviour
{
    private IUserAction action;
    private bool gamestart = false;
    public int HP = 5;
    void Start()
    {
        action = SSDirector.GetInstance().CurrentScenceController as IUserAction;
    }

    void OnGUI()
    {   GUIStyle fontStyle2 = new GUIStyle();
        fontStyle2.fontSize = 40;
        fontStyle2.normal.textColor= new Color(1, 1, 1);
        GUIStyle fontStyle1 = new GUIStyle();
        fontStyle1.fontSize = 20;
        GUIStyle fontStyle3 = new GUIStyle();
        fontStyle3.fontSize = 50;
        if(GUI.Button (new Rect (Screen.width-100, Screen.height-100, 60, 30), "退出")){
            Application.Quit();
        }
        if(gamestart == false){
            GUI.Label(new Rect(Screen.width / 2 - 300, 200, 420, 50), "游戏目标:尽可能的射击多的飞碟", fontStyle2);
            GUI.Label(new Rect(Screen.width / 2 - 200, 300, 420, 50), "Ps:游戏难度会随分数的增加而增加", fontStyle1);
            if (GUI.Button(new Rect(Screen.width / 2 - 80, Screen.height-250, 90, 40), "来,弄")){
                action.Startgame();
                gamestart = true;
            }
        }
        else{
            GUI.Label(new Rect(Screen.width / 2 - 350, 50, 420, 50), "分数:", fontStyle1);
            GUI.Label(new Rect(Screen.width / 2 - 300, 50, 420, 50), action.Getscore().ToString(), fontStyle1);
            GUI.Label(new Rect(Screen.width / 2 - 200, 50, 420, 50), "剩余血量:", fontStyle1);
            GUI.Label(new Rect(Screen.width / 2 - 100, 50, 420, 50), HP.ToString(), fontStyle1);
        }
        if (Input.GetButtonDown("Fire1")) {
            action.Hit();
		}
        if (HP <= 0){
            GUI.Label(new Rect(Screen.width / 2 - 100, 150, 60, 40), "游戏结束!", fontStyle3);
            action.Stopgame();
            if (GUI.Button(new Rect(Screen.width / 2 - 40, Screen.height-200, 70, 40), "重新开始")){
                action.Restart();
                HP = 5;
            }
        }  
    }
}

飞碟类:有四种飞碟,具体差异体现在预制体的制作,这里可以随意制作预制体

public class Disk
    {
        public GameObject disk;
        public float Power = 3;
        public float Angle = 15;
        public float Gravity = -0.5f;
        public Vector3 MoveSpeed;
        public Disk(string name){
            if(name == "disk1"){
                disk = Object.Instantiate(Resources.Load("disk1", typeof(GameObject)), new Vector3(-10,0,0), Quaternion.Euler(0, -90, 0)) as GameObject;
            }
            else if(name == "disk2"){
                disk = Object.Instantiate(Resources.Load("disk2", typeof(GameObject)), new Vector3(-10,0,0), Quaternion.Euler(0, -90, 0)) as GameObject;
            }
            else if(name == "disk3"){
                disk = Object.Instantiate(Resources.Load("disk3", typeof(GameObject)), new Vector3(-10,0,0), Quaternion.Euler(0, -90, 0)) as GameObject;
            }
            else if(name == "disk4"){
                disk = Object.Instantiate(Resources.Load("disk4", typeof(GameObject)), new Vector3(-10,0,0), Quaternion.Euler(0, 90, 0)) as GameObject;
            }
        }
        public GameObject getGameObject() { return disk; }
    }

飞碟工厂:用于新建disk到游戏中和回收disk资源

public class DiskFactory : MonoBehaviour
{
    public List<Disk> used = new List<Disk>();   //正在被使用的飞碟列表
    public List<Disk> free = new List<Disk>();   //空闲的飞碟列表

    public Disk GetDisk()
    {
        Disk disk_prefab = null;
        int rand = Random.Range(0, 99);
        if (free.Count > 10){
            disk_prefab = free[0];
            disk_prefab.getGameObject().SetActive(true);
            free.Remove(free[0]);
        }
        else {
            if (rand % 4 == 0){
                disk_prefab = new Disk("disk1");
            }
            else if(rand % 4 == 1){
                disk_prefab = new Disk("disk2");
            }
            else if(rand % 4 == 2){
                disk_prefab = new Disk("disk3");
            }
            else{
                disk_prefab = new Disk("disk4");
            }
        }
        used.Add(disk_prefab);
        return disk_prefab;
    }

    //回收飞碟

    public void ResetDisk(Disk disk){
        disk.Power = 0;
        disk.Angle = 0;
        disk.Gravity = 0;
    }

    public void FreeDisk(GameObject disk)
    {
        for(int i = 0;i < used.Count; i++)
        {
            if (disk.GetInstanceID() == used[i].getGameObject().GetInstanceID())
            {
                ResetDisk(used[i]);
                used[i].getGameObject().SetActive(false);
                free.Add(used[i]);
                used.Remove(used[i]);
                break;
            }
        }
    }
}

控制类:

用于获取游戏状态来调整游戏难度,游戏每达到10分难度就会往上提升一档,当游戏得分突破40分后,游戏会同时发送两个飞碟。

用射线来判断飞碟是否被鼠标点中。

扫描二维码关注公众号,回复: 17613458 查看本文章
public class Controll : MonoBehaviour,ISceneController,IUserAction
{
    private int gamestate = 0;                                              // 0 游戏未开始,1 游戏进行 ,2 游戏开始
    private int round = 1;
    private int score = 0;
    private float time = 2f;
    private float i = 0;
    public UserGUI User;
    public MySceneActionManager actionManager;
    //public Disk disk;
    public DiskFactory diskfactory;
    void Start()
    {
        SSDirector director = SSDirector.GetInstance();
        director.CurrentScenceController = this;
        User = gameObject.AddComponent<UserGUI>() as UserGUI;
        diskfactory = Singleton<DiskFactory>.Instance;
        actionManager = gameObject.AddComponent<MySceneActionManager>() as MySceneActionManager;
        //LoadResources();
    }
    
    void Update(){
        if (gamestate == 2){
            gamestate = 1;
        }
        
        if (gamestate == 1){
            if (i >= time){   
                if (score > 10 && round == 1){
                    time -= 0.3f;
                    round = 2;
                }
                else if(score > 20 && round == 2){
                    time -= 0.5f;
                    round = 3;
                }
                else if(score > 30 && round == 3){
                    time += 0.3f;
                    round = 4;
                }
                SendDisk();
                i = 0;
            }
            i += Time.deltaTime;
        }
        
    }
    public void LoadResources(){
        
    }
    public void SendDisk(){
        Disk disk = diskfactory.GetDisk();
        if (round == 1){
            disk.Power = Random.Range(2.5f, 3.5f);
            disk.Angle = Random.Range(10f, 30f);
            disk.Gravity = Random.Range(-0.3f, -0.5f);
            disk.disk.transform.position = new Vector3(-10,Random.Range(-3f, 2f),0);
        }
        else if (round == 2){
            disk.Power = Random.Range(3.5f, 4.5f);
            disk.Angle = Random.Range(5f, 35f);
            disk.Gravity = Random.Range(-0.5f, -1f);
            disk.disk.transform.position = new Vector3(-10,Random.Range(-3f, 1f),0);
        }
        else if (round == 3){
            disk.Power = Random.Range(5.0f, 7.0f);
            disk.Angle = Random.Range(5f, 35f);
            disk.Gravity = Random.Range(-1f, -1.5f);
            disk.disk.transform.position = new Vector3(-10,Random.Range(-4f, 3f),0);
        }
        else if (round == 4){
            disk.Power = Random.Range(7f, 10f);
            disk.Angle = Random.Range(5f, 35f);
            disk.Gravity = Random.Range(-2f, -2.5f);
            disk.disk.transform.position = new Vector3(-10,Random.Range(-5f, 2f),0);
            Disk disk2 = diskfactory.GetDisk();
            disk2.Power = Random.Range(6f, 8f);
            disk2.Angle = Random.Range(5f, 35f);
            disk2.Gravity = Random.Range(-1f, -3f);
            disk2.disk.transform.position = new Vector3(-10,Random.Range(-4.5f, 1f),0);
            actionManager.fly_disk(disk2);
        }
        actionManager.fly_disk(disk);
    }
    public void Restart(){
        score = 0;
        gamestate = 2;
    }
    public void Stopgame(){
        gamestate = 0;
        round = 1;
        time = 2f;
    }

    public void Startgame(){
        gamestate = 2;
    }

    public bool Hit(){
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit)) {
            if(hit.collider.gameObject.transform.position.x < 3 && gamestate > 0)score+=2;
            else if(hit.collider.gameObject.transform.position.x >= 3 && gamestate > 0) score+=1;
            hit.collider.gameObject.transform.position = new Vector3(-10, -11, 0);
            diskfactory.FreeDisk(hit.collider.gameObject);
            return true;
		}
        return false;
    }
    public int Getscore(){
        return score;
    }
}

游戏运行:只要在游戏场景新建两个空对象,一个挂载controll,一个挂载DiskFactory即可。

游戏演示视频:打飞碟演示视频_哔哩哔哩_bilibili

代码链接:WuyqSYSU/HitDisk

猜你喜欢

转载自blog.csdn.net/yq22331103/article/details/143796005