Unity打飞碟小游戏(运动可变换版)制作

游戏机制:

打飞碟是一款益智游戏,玩家需要在界面中点击飞出来的飞碟以将其消除,不同颜色的飞碟飞行速度不一样,对应消除的得分也不一样。该游戏一共10轮,每轮持续10s,分为“运动学模式”和“重力学模式”两种模式。

UML图:

Controller制作代码:

DiskData.cs

飞碟参数

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

public class DiskData : MonoBehaviour
{
    public float speed;
    public Vector3 direction;
    public int score;
}

DiskFactory.cs

实现生成多个飞碟对象

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

public class DiskFactory : MonoBehaviour
{
    private List<DiskData> used;
    private List<DiskData> free;
    public GameObject disk;
    // Start is called before the first frame update
    void Start()
    {
        used = new List<DiskData>();
        free = new List<DiskData>();
        disk = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/UFO"));
        disk.SetActive(false);
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public GameObject GetDisk(int round){
        GameObject d;

        if(free.Count > 0){
            d = free[0].gameObject;
            free.Remove(free[0]);
        }
        else{
            d = GameObject.Instantiate<GameObject>(disk, Vector3.zero, Quaternion.identity);
            d.AddComponent<DiskData>();
        }
        d.gameObject.name = "Disk";

        // 根据不同的round设定不同的初始数值
        // round越大,难度越高,则可以通过设置不同的速度改变难度
        float uspeed = 2 + round;

        // scale
        Vector3 uscale = new Vector3(6, 1, 6);

        // 随机初始方向
        float dir_y = Random.Range(-0.5f, 0.5f);
        int dir_x = Random.Range(0, 2);

        // 随机颜色,不同的颜色有不同的速度
        int color = Random.Range(1, 5);

        // 红色
        if(color == 1){
            d.GetComponent<Renderer>().material.color = Color.red;
            d.GetComponent<DiskData>().speed = 0.4f * uspeed;
            d.GetComponent<DiskData>().direction = new Vector3(dir_x, dir_y, 0);
            d.GetComponent<DiskData>().score = 2;
            d.GetComponent<Transform>().localScale = 0.7f*uscale;

        }
        // 黄色
        else if(color == 2){
            d.GetComponent<Renderer>().material.color = Color.yellow;
            d.GetComponent<DiskData>().speed = 0.8f * uspeed;
            d.GetComponent<DiskData>().direction = new Vector3(dir_x, dir_y, 0);
            d.GetComponent<DiskData>().score = 4;
            d.GetComponent<Transform>().localScale = 0.6f*uscale;
        }
        // 蓝色
        else if(color == 3){
            d.GetComponent<Renderer>().material.color = Color.blue;
            d.GetComponent<DiskData>().speed = 1.2f * uspeed;
            d.GetComponent<DiskData>().direction = new Vector3(dir_x, dir_y, 0);
            d.GetComponent<DiskData>().score = 6;
            d.GetComponent<Transform>().localScale = 0.5f*uscale;
        }
        // 绿色
        else{
            d.GetComponent<Renderer>().material.color = Color.green;
            d.GetComponent<DiskData>().speed = 1.6f * uspeed;
            d.GetComponent<DiskData>().direction = new Vector3(dir_x, dir_y, 0);
            d.GetComponent<DiskData>().score = 8;
            d.GetComponent<Transform>().localScale = 0.4f*uscale;
        }
        used.Add(d.GetComponent<DiskData>());
        return d;
    }

    public void FreeDisk(GameObject d){
        foreach(DiskData dd in used){
            if(dd.gameObject.GetInstanceID() == d.GetInstanceID()){
                d.SetActive(false);
                used.Remove(dd);
                free.Add(dd);
                break;
            }
        }
    }


    public void Clear(){
        foreach(DiskData dd in used){//销毁在飞飞碟
            dd.gameObject.transform.position = new Vector3(-100, 0, 0);   // 将飞碟的位置设置为边界外,这样就能及时销毁
            Destroy(dd.gameObject);
        }
        foreach(DiskData dd in free){//销毁空闲飞碟
            dd.gameObject.transform.position = new Vector3(-100, 0, 0);   // 将飞碟的位置设置为边界外,这样就能及时销毁
            Destroy(dd.gameObject);
        }
        used.Clear();
        free.Clear();
    }
}

FirstController.cs

对游戏进行初始化和各项总控制

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

public class FirstController : MonoBehaviour, ISceneController, IUserAction
{
    // Start is called before the first frame update
    public DiskFactory diskFactory;
    public ScoreRecorder scoreRecorder;
    public FlyActionManager flyActionManager;
    public UserGUI userGUI;

    int round;
    float current_time;
    float index_time;
    int round_disk_count;

    static float round_time = 10;
    static int max_disk = 30;
    void Start()
    {
        SSDirector.GetInstance().CurrentSceneController = this;
        gameObject.AddComponent<DiskFactory>();
        gameObject.AddComponent<CCActionManager>();
        gameObject.AddComponent<PhysicalActionManager>();
        userGUI = gameObject.AddComponent<UserGUI>();
        diskFactory = Singleton<DiskFactory>.Instance;
        flyActionManager = Singleton<CCActionManager>.Instance;

        LoadResources();
    }

    // Update is called once per frame
    void Update()
    {
        if(userGUI.status == 1){
            if(round <= 10){
                current_time += Time.deltaTime;
                index_time += Time.deltaTime;
                if(current_time < round_time){   // 当前轮次没有结束
                    if(index_time > 1){
                        index_time = 0;
                        userGUI.round = round;

                        int disk_count = Random.Range(1,4);
                        while(disk_count > 0){
                            float x = Random.Range(-1f, 1f) < 0 ? -1 : 1;
                            float y = Random.Range(0f, 8f);
                            
                            SendDisk(x, y);
                            round_disk_count++;
                            disk_count--;
                            if(round_disk_count == max_disk){
                                break;
                            }
                        }
                    }
                    
                }
                else{
                    round_disk_count = 0;
                    current_time = 0;
                    index_time = 0;
                    round++;
                    userGUI.round = round;
                }
            }
            if(round >= 11){
                userGUI.status = 2;
            }
        }
    }

    public void LoadResources(){
        scoreRecorder = new ScoreRecorder();
        scoreRecorder.Reset();

        round = 1;
        current_time = 0;
        index_time = 0;
        round_disk_count = 0;
    }

    public void SendDisk(float x, float y){
        GameObject disk = diskFactory.GetDisk(round);
        // float rand_y = Random.Range(0f, 8f);
        // float rand_x = Random.Range(-1f, 1f) < 0 ? -1 : 1;
        disk.transform.position = new Vector3(x, y, 0);
        disk.SetActive(true);
        
        flyActionManager.FlyDisk(disk, disk.GetComponent<DiskData>().speed, disk.GetComponent<DiskData>().direction);
    }

    public void GetHit(Vector3 position){
        Ray ray = Camera.main.ScreenPointToRay(position);
        RaycastHit[] hits = Physics.RaycastAll(ray);

        for(int i = 0; i < hits.Length; i++){
            RaycastHit hit = hits[i];
            if (hit.collider.gameObject.GetComponent<DiskData>() != null){
                GameObject disk = hit.collider.gameObject;
                disk.transform.position = new Vector3(0, -20, 0);
                diskFactory.FreeDisk(disk);
                scoreRecorder.Record(disk);
                userGUI.score = scoreRecorder.score;
            }
        }
    }

    public void ClearAll(){
        GameObject[] gameObjects = FindObjectsOfType(typeof(GameObject)) as GameObject[];  // 获取所有的GameObject
        foreach(GameObject gmobj in gameObjects){
            if(gmobj.name == "Disk"){
                gmobj.gameObject.SetActive(false);
            }
        }

        diskFactory.Clear();
    }

    public void Restart(){
        round = 1;
        current_time = 0;
        index_time = 0;
        round_disk_count = 0;
        scoreRecorder.Reset();
        userGUI.score = scoreRecorder.score;
        userGUI.round = round;
        ClearAll();
    }

    public void SetMode(int mode){
        if(mode == 0){
            flyActionManager = Singleton<CCActionManager>.Instance;
        }
        else if(mode == 1){
            flyActionManager = Singleton<PhysicalActionManager>.Instance;
        }
    }
}

ISceneController.cs

场景资源加载分离

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

public interface ISceneController
{
    void LoadResources();
}

IUserAction.cs

用户操作分离

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

public interface IUserAction {
    // void MoveBoat();
    // void MoveRole(Role roleModel);
    // void Check();
    void Restart();
    void SetMode(int mode);
    void GetHit(Vector3 position);
}

ScoreRecorder.cs

分数记录器

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

public class ScoreRecorder : System.Object
{
    public int score;
    // Start is called before the first frame update
    void Start()
    {
        score = 0;
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public void Record(GameObject disk){
        score += disk.GetComponent<DiskData>().score;
    }

    public void Reset(){
        score = 0;
    }
}

Singleton.cs

单实例模板

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

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

SSDirector.cs

场景设置分离

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

public class SSDirector : System.Object
{
    static SSDirector _instance;
    public ISceneController CurrentSceneController {get; set;}
    public static SSDirector GetInstance() {
        if (_instance == null) {
            _instance = new SSDirector();
        }
        return _instance;
    }
}

Actions制作代码:

CCActionManager.cs

运动动作管理

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

public class CCActionManager : SSActionManager, ISSActionCallback, FlyActionManager
{

    public FirstController controller;
    public CCFlyAction fly;
    // Start is called before the first frame update
    public void Start()
    {
        controller = (FirstController)SSDirector.GetInstance().CurrentSceneController;
    }

    // Update is called once per frame
    // public void Update()
    // {
        
    // }

    public void FlyDisk(GameObject disk, float speed, Vector3 direction){
        fly = CCFlyAction.GetSSAction(direction, speed);
        RunAction(disk, fly, this);
    }

    public void SSActionEvent(SSAction source,
        SSActionEventType events = SSActionEventType.Competeted,
        int intParam = 0,
        string strParam = null,
        Object objectParam = null){
            controller.diskFactory.FreeDisk(source.gameObject);
        }
}

CCFlyAction.cs

飞行动作

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

public class CCFlyAction : SSAction
{
    Vector3 direction;
    float speed;
    public static CCFlyAction GetSSAction(Vector3 direction, float speed){
        CCFlyAction index = ScriptableObject.CreateInstance<CCFlyAction>();
        index.direction = direction;
        index.speed = speed;
        return index;
    }
    // Start is called before the first frame update
    public override void Start()
    {
        // gameObject.AddComponent<Rigidbody>();
        gameObject.GetComponent<Rigidbody>().isKinematic = true;
    }

    // Update is called once per frame
    public override void Update()
    {
        transform.Translate(direction*speed*Time.deltaTime);
        if(this.transform.position.x < -35 || this.transform.position.x > 35 ||
            this.transform.position.y < -10 || this.transform.position.y > 30){
                this.destroy = true;
                this.enable = false;
                this.callback.SSActionEvent(this);
            }
    }
}

CCMoveToAction.cs

移动后消除判断

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

public class CCMoveToAction : SSAction
{
    public Vector3 target;   // 移动后的目标位置
    public float speed;

    // Update is called once per frame
    public override void Update()
    {
        this.transform.localPosition = Vector3.MoveTowards(this.transform.localPosition, target, speed * Time.deltaTime);
        // 如果游戏对象不存在或者当前位置已在目标位置上,则不移动
        if(this.transform.localPosition == target || this.gameObject == null){
            this.destroy = true;    // 标记为销毁
            this.callback.SSActionEvent(this);   // 回调函数
            return;
        }
    }

    public static CCMoveToAction GetSSAction(Vector3 target, float speed){
        CCMoveToAction action = ScriptableObject.CreateInstance<CCMoveToAction>();
        action.target = target;
        action.speed = speed;
        return action;
    }
}

CCSequenceAction.cs

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

public class CCSequenceAction : SSAction, ISSActionCallback
{
    public List<SSAction> sequence;
    public int repeat = -1;
    public int start = 0;
    // Start is called before the first frame update
    public override void Start()
    {
        // 初始化列表中的动作
        foreach(SSAction action in sequence){
            action.gameObject = this.gameObject;
            action.transform = this.transform;
            action.callback = this;
            action.Start();
        }
    }

    // Update is called once per frame
    public override void Update()
    {
        if(sequence.Count <= 0){
            return;
        }
        if(sequence.Count > 0 && start < sequence.Count){
            sequence[start].Update();
        }
        else{
            return;
        }
    }

    public static CCSequenceAction GetSSAction(int repeat, int start, List<SSAction> sequence){
        CCSequenceAction action = ScriptableObject.CreateInstance<CCSequenceAction>();
        action.repeat = repeat;
        action.start = start;
        action.sequence = sequence;
        return action;
    }

    public void SSActionEvent(SSAction source, 
                    SSActionEventType events = SSActionEventType.Competeted, 
                    int intParam = 0, string strParam = null, Object objectParam = null){
            source.destroy = false;
            this.start++;

            if(this.start >= sequence.Count){
                this.start = 0;
                if(this.repeat > 0){
                    this.repeat--;
                }
                else{
                    this.destroy = true;
                    this.callback.SSActionEvent(this);
                }
            }
    }

    void OnDestroy(){

    }
}

FlyActionManager.cs

动作总管理

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

public interface FlyActionManager
{
    void FlyDisk(GameObject disk, float speed, Vector3 direction);
}

ISSActionCallback.cs

动作回调

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum SSActionEventType : int { Started, Competeted } 
public interface ISSActionCallback
{
    public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted, 
        int intParam = 0, string strParam = null, Object objectParam = null);
}

PhysicalActionManager.cs

物理学动作管理

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

public class PhysicalActionManager : SSActionManager, ISSActionCallback, FlyActionManager
{
    public FirstController controller;
    public PhysicalFlyAction fly;
    // Start is called before the first frame update
    public void Start()
    {
        controller = (FirstController)SSDirector.GetInstance().CurrentSceneController;
    }

    // Update is called once per frame
    // void Update()
    // {
        
    // }

    public void FlyDisk(GameObject disk, float speed, Vector3 direction){
        fly = PhysicalFlyAction.GetSSAction(direction, speed);
        RunAction(disk, fly, this);
    }

    public void SSActionEvent(SSAction source,
        SSActionEventType events = SSActionEventType.Competeted,
        int intParam = 0,
        string strParam = null,
        Object objectParam = null){
            controller.diskFactory.FreeDisk(source.gameObject);
        }
}

PhysicalFlyAction.cs

物理学动作

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

public class PhysicalFlyAction : SSAction
{
    Vector3 direction;
    float speed;
    public static PhysicalFlyAction GetSSAction(Vector3 direction, float speed){
        PhysicalFlyAction index = ScriptableObject.CreateInstance<PhysicalFlyAction>();
        index.direction = direction;
        index.speed = speed;
        return index;
    }
    // Start is called before the first frame update
    public override void Start()
    {
        // gameObject.AddComponent<Rigidbody>();
        gameObject.GetComponent<Rigidbody>().isKinematic = false;
        gameObject.GetComponent<Rigidbody>().velocity = speed*direction;   // 水平初速度
    }

    // Update is called once per frame
    public override void Update()
    {
        if(transform != null){
            transform.Translate(direction*speed*Time.deltaTime);
        if(this.transform.position.y < -10){
            this.destroy = true;
            this.enable = false;
            this.callback.SSActionEvent(this);
        }
        }
    }
}

SSAction.cs

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

public class SSAction : ScriptableObject
{
    public bool enable = true;
    public bool destroy = false;

    public GameObject gameObject { get; set; }
    public Transform transform { get; set; }
    public ISSActionCallback callback { get; set;}

    protected SSAction() {}

    // Start is called before the first frame update
    public virtual void Start()
    {
        throw new System.NotImplementedException();
    }

    // Update is called once per frame
    public virtual void Update()
    {
        throw new System.NotImplementedException();
    }
}

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>();

    // Start is called before the first frame update
    protected void Start()
    {
        
    }
    // Update is called once per frame
    protected void Update()
    {
        foreach(SSAction ac in waitingAdd){
            actions[ac.GetInstanceID()] = ac;
        }
        waitingAdd.Clear();

        foreach(KeyValuePair<int, SSAction> kv in actions){
            SSAction ac = kv.Value;
            if(ac.destroy){
                waitingDelete.Add(ac.GetInstanceID());
            }
            else if(ac.enable){
                ac.Update();
            }
        }
        foreach(int key in waitingDelete){
            SSAction ac = actions[key];
            actions.Remove(key);
            Destroy(ac);
        }
        waitingDelete.Clear();
    }

    public void RunAction(GameObject gameObject, SSAction action, ISSActionCallback manager){
        action.gameObject = gameObject;
        action.transform = gameObject.transform;
        action.callback = manager;
        waitingAdd.Add(action);
        action.Start();
    }
}

View制作代码:

UserGUI.cs

加载GUI界面

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

public class UserGUI : MonoBehaviour
{
    GUIStyle topicStyle;
    GUIStyle dataStyle;
    public int round;
    public int score;
    public int status;
    public int mode;
    private string[] modeStrings = {"运动学模式", "重力学模式"};
    private IUserAction userAction;
    // Start is called before the first frame update
    void Start()
    {
        round = 1;
        score = 0;
        status = 0;
        mode = 0;
        userAction = SSDirector.GetInstance().CurrentSceneController as IUserAction;

        SetStyle();
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    void SetStyle(){
        topicStyle = new GUIStyle();
        topicStyle.normal.textColor = Color.black;
        topicStyle.fontSize = 60;

        dataStyle = new GUIStyle();
        dataStyle.normal.textColor = Color.black;
        dataStyle.fontSize = 30;
    }

    void OnGUI(){
        GUI.Label(new Rect(300, 20, 50, 40), "Hit UFO", topicStyle);

        if(status == 0){
            if(GUI.Button(new Rect(10, 10, 100, 40), "Start Game")){
                status = 1;
                userAction.Restart();
            }
            // GUI.Box(new Rect(100, 100, 300, 200), "模式:");
            // mode = 0;
            mode = GUI.Toolbar(new Rect (10, 60, 200, 30), mode , modeStrings);
            
            userAction.SetMode(mode);
        }

        else if(status == 1){
            GUI.Label(new Rect(20, 20, 100, 50), "score: "+ score, dataStyle);
            GUI.Label(new Rect(20, 80, 100, 50), "round: "+ round, dataStyle);
            if(Input.GetButtonDown("Fire1")){
                userAction.GetHit(Input.mousePosition);
            }
            if(GUI.Button(new Rect(20, 160, 100, 40), "Restart")){
                status = 0;
                userAction.Restart();
            }
        }

        else if(status == 2){
            GUI.Label(new Rect(320, 100, 50, 40), "Game Over", dataStyle);
            GUI.Label(new Rect(320, 200, 50, 40), "total score: " + score, dataStyle);
            if (GUI.Button(new Rect(330, 330, 100, 40), "Restart"))
            {
                status = 0;
                userAction.Restart();
            }
        }
    }
}

 游戏游玩视频:

Unity打飞碟小游戏(运动可变换版)游玩_哔哩哔哩_bilibili

猜你喜欢

转载自blog.csdn.net/qq3098320650/article/details/134355429
今日推荐