Assignment 5

编写一个简单的鼠标打飞碟(Hit UFO)游戏。

示例视频:自制Unity打飞盘小游戏_单机游戏热门视频

源代码:GitHub - ShirohaBili/UnityGame

首先是介绍最重要的Controller类,在这个类中,实现了游戏运行的各项基本逻辑,先看代码:

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

public class Controller : MonoBehaviour, ISceneController, IUserAction
{
    // Start is called before the first frame update
    public DiskFactory factory = new DiskFactory();
    public BoomFactory boom_fac = new BoomFactory();
    public Score record = new Score();
    public UserGUI User_GUI = new UserGUI(); 
    public FlyActionManager action_manager;
    Vector3 startPos = new Vector3(0,-10f,0);
    private int count = 0;

    void Start()
    {
        SSDirector director = SSDirector.GetInstance();     
        director.CurrentScenceController = this;
        factory = Singleton<DiskFactory>.Instance;
        boom_fac = Singleton<BoomFactory>.Instance;
        record = Singleton<Score>.Instance;

        action_manager = gameObject.AddComponent<FlyActionManager>() as FlyActionManager;
        User_GUI = gameObject.AddComponent<UserGUI>() as UserGUI;
    }

    bool run = false;        //是否开始
    int trial = 0;            //回合内的计数器
    public int round = 1;    //回合数
    float BoomTimer = 0;    //计数器
    float maxTime = 2;    //爆炸特效回收前持续秒数
    // Update is called once per frame
    void Update()
    {   
        if (Input.GetButtonDown("Fire1")){
            Vector3 mp = Input.mousePosition;
            hit(mp);
        }

        // Debug.Log(record.getScore());

        if (run){
            count++;
            if (round == 1){    //第一回合
                if (count == 300){
                    float num = Random.Range(0f,1f);
                    if (num <= 0.75f) send(1);
                    else if (num > 0.75f && num < 0.95f) send(2);
                    else send(3);

                    count = 0;
                    trial++;

                    if (trial == 10) {
                        round++;
                        trial = 0;
                    }
                }
            }
            else if (round == 2){   //第二回合
                if (count == 250){
                    float num = Random.Range(0f,1f);
                    if (num <= 0.5f) send(1);
                    else if (num > 0.5f && num < 0.9f) send(2);
                    else send(3);

                    count =  0;
                    trial++;

                    if (trial == 10){
                        round++;
                        trial = 0;
                    }
                }
            }
            else if (round == 3){   //第三回合
                if (count == 200){
                    float num = Random.Range(0f,1f);
                    if (num <= 0.4f) send(1);
                    else if (num > 0.4f && num < 0.8f) send(2);
                    else send(3);

                    count = 0;
                    trial++;

                    if (trial == 10){
                        run = false;
                    }
                }
            }
            factory.FreeDisk();
        }

        if(!boom_fac.getEmpty()){    //回收爆炸特效
            BoomTimer +=Time.deltaTime;
            if (BoomTimer >= maxTime){
                boom_fac.OutList();
                BoomTimer = 0;
            }
        }
    }

    public void send(int type){    //发送飞盘
        GameObject curDisk = factory.GetDisk(type);
        
        //随机产生飞盘
        float ran_y = 0;
        float ran_x = Random.Range(-1f, 1f);
        if (ran_x < 0) ran_x = -1f;
        else ran_x = 1f;

        float power = 0;
        float angle = 0;

        if (type == 1){     //蓝色飞盘分数为1,简单一点,速度慢,倾角大
            ran_y = Random.Range(0f,2f);
            power = Random.Range(1f,2f);
            angle = Random.Range(20f,30f);
        }
        else if (type == 2){       //红色飞盘分数为2,稍难一点,速度稍快,倾角适中
            ran_y = Random.Range(0f,2f);
            power = Random.Range(2f,3f);
            angle = Random.Range(10f,20f);
        }
        else if (type == 3){     //绿色飞盘分数为3,最难,速度最快,倾角最小
            ran_y = Random.Range(0f,3f);
            power = Random.Range(3f,4f);
            angle = Random.Range(0f,10f);
        }
        curDisk.transform.position = new Vector3(ran_x*16f, ran_y, 0);
        action_manager.DiskFly(curDisk, angle, power);
    }

    public void hit(Vector3 pos){    //判断是否击中
        Ray ray = Camera.main.ScreenPointToRay(pos);

        RaycastHit hit;
        
        if (Physics.Raycast(ray,out hit)){
            if (hit.collider.gameObject.GetComponent<Disk>() !=null){
                Vector3 targetPos = hit.collider.gameObject.transform.position;
                boom_fac.getExp(targetPos);
                record.add(hit.collider.gameObject);
                hit.collider.gameObject.transform.position = startPos;
            }
        }
    }

    public void Reset(){    //重新开始
        run = true;
        round = 1;
        trial = 0;
        record.clear();
    }

    public float getScore(){
        return record.getScore();
    }

    public int getRound(){
        return round;
    }

    public int getTrial(){
        return trial;
    }
    public void clearTrial(){
        trial = 0;
    }
}

DiskFactory和BoomFactory分别是我按照工厂模式设计的负责生产和回收的类,分别负责飞碟和爆炸特效的生成和回收。

Score是我设计的负责计分的类,类似于现实游戏中的裁判。其中,击中蓝色飞盘记一分,击中红色飞盘记两分,击中绿色飞盘记三分。

UserGUI为用户接口,提供了界面UI与用户交互,提供了计分板和选择难度按钮供用户选择。

发射飞盘我采用了随机发射的方法,利用随机数来产生飞盘的初始位置、速度和倾角,按难度高低设置了不同的范围。


剩下的就是各项游戏对象的具体实现方法了,因为采用了动作分离,所以类会有点多,这里就选其中一些最重要的类进行介绍。

首先是DIsk基类,规定了DIsk应具备的基本性质,再按照工厂模式设计了一个DiskFactory类,用于产生和回收飞碟,代码如下:

Disk.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//飞碟基类,Disk只需要记录三个特征即可
public class Disk : MonoBehaviour
{
    public int type = 1;
    public Color color = Color.black;
    public int score = 0;
}

DiskFactory.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 工厂
public class DiskFactory : MonoBehaviour {
    private List<Disk> used = new List<Disk>();
    private List<Disk> free = new List<Disk>();
    Vector3 startPos = new Vector3(0,-10f,0);

    public GameObject GetDisk(int type) {
        GameObject curdisk = null;
        if (free.Count>0) {
            for(int i = 0; i < free.Count; i++) {
                if(!(free[i].type==1||free[i].type==2||free[i].type==3))
                    Debug.Log("error");
                if (free[i].type == type) {
                    curdisk = free[i].gameObject;
                    free.Remove(free[i]);
                    break;
                }
            }     
        }
        //Debug.Log(curdisk == null);
        if(curdisk == null) {
            if (type == 1) {
                curdisk = Instantiate(Resources.Load<GameObject>("Prefabs/BlueDisk"),startPos, Quaternion.identity);
            }            
            else if(type == 2) {
                curdisk = Instantiate(Resources.Load<GameObject>("Prefabs/RedDisk"),startPos, Quaternion.identity);
            }
            else if (type == 3){
                curdisk = Instantiate(Resources.Load<GameObject>("Prefabs/GreenDisk"),startPos, Quaternion.identity);
            }
            curdisk.GetComponent<Renderer>().material.color = curdisk.GetComponent<Disk>().color;
        }

        used.Add(curdisk.GetComponent<Disk>());
        curdisk.SetActive(true);
        return curdisk;
    }

    public void FreeDisk() {
        // Debug.Log("before");
        // Debug.Log(used.Count);
        // Debug.Log(free.Count);
        for(int i=0; i<used.Count; i++) {
            if (used[i].gameObject.transform.position.y <= -9f) {
                Disk t = used[i];
                free.Add(t);
                used.Remove(used[i]);
            }
        }      
        // Debug.Log("after");
        // Debug.Log(used.Count);
        // Debug.Log(free.Count);    
    }

    public void Reset() {
        FreeDisk();
    }

}

在这之前,我利用Unity内自带的3DGameObject对象生成了三个DIsk的预设,分别为BlueDisk、RedDisk和GreenDisk,它们各自有不同的分数,速度也不一样。


在生成了Disk之后,有如下用于控制飞碟飞行的DiskFly类,代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 具体动作的实现,继承于SSAction动作基类
public class DiskFlyAction : SSAction {
    public float gravity = -0.5f;                                 
    private Vector3 start_vector;                              
    private Vector3 gravity_vector = Vector3.zero;             
    private Vector3 current_angle = Vector3.zero;              
    private float time;                                        

    private DiskFlyAction() { }
    public static DiskFlyAction GetSSAction(int lor, float angle, float power) {

        DiskFlyAction action = CreateInstance<DiskFlyAction>();
        if (lor == -1) {
            action.start_vector = Quaternion.Euler(new Vector3(0, 0, -angle)) * Vector3.left * power;
        }
        else {
            action.start_vector = Quaternion.Euler(new Vector3(0, 0, angle)) * Vector3.right * power;
        }
        return action;
    }

    public override void Update() {

        time += Time.fixedDeltaTime;
        gravity_vector.y = gravity * time * 0.1f;
        transform.position += (start_vector + gravity_vector) * Time.fixedDeltaTime;
        
        current_angle.z = Mathf.Atan((start_vector.y + gravity_vector.y) / start_vector.x) * Mathf.Rad2Deg;
        transform.eulerAngles = current_angle;

        if (this.transform.position.y < -10) {
            this.destroy = true;
            this.callback.SSActionEvent(this);      
        }
    }

    public override void Start() { }
}

完成之后,还需要一个Manager去管理这个动作,代码如下:

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

public class FlyActionManager : SSActionManager {
    public DiskFlyAction fly;  
    public Controller scene_controller;           

    protected void Start() {
        scene_controller = (Controller)SSDirector.GetInstance().CurrentScenceController;
        scene_controller.action_manager = this;     
    }

    //飞碟飞行
    public void DiskFly(GameObject disk, float angle, float power) {
        int lor = 1;
        if (disk.transform.position.x > 0) lor = -1;
        fly = DiskFlyAction.GetSSAction(lor, angle, power);
        this.RunAction(disk, fly, this);
    }
}

lor的作用在于判断是左飞还是往右飞,方向会有不同的变化。


接下来的是管理爆炸特效相关的“爆炸工厂”,代码如下:

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

public class BoomFactory : MonoBehaviour
{
    public Queue<GameObject> que = new Queue<GameObject>();
    // Start is called before the first frame update
    public void getExp(Vector3 pos){
        GameObject effect = Instantiate(Resources.Load("Prefabs/Boom") as GameObject, pos, Quaternion.identity);
        que.Enqueue(effect);
    }

    public void OutList(){
        GameObject curEff = que.Dequeue();
        Destroy(curEff);
    }

    public bool getEmpty(){
        bool judge = false;
        if (que.Count == 0)  judge = true; 
        return judge;
    }
}

剩下的一些类就和上一次作业中第一题的动作分离的类差不太多了,虽然有一些细微的改变,但总体上的逻辑并没有很大的变化,所以就略过不讲,具体的代码可去我上传的GIthub仓库内查看。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值