游戏简介
游戏中随机出现飞碟,玩家在其坠地之前点击到即可得分,每个飞碟的分数由其速度、大小决定,在50秒内取得50分即可进入下一轮,否则结束游戏、结算分数。随着轮数的增加,飞碟的平均速度逐渐增加,平均大小逐渐减小,从而提高难度。

游戏演示视频
【3d游戏编程作业5——打飞碟小游戏】 https://www.bilibili.com/video/BV1ew411T7CF/?share_source=cop...

游戏代码
https://github.com/Kmbls/hit-UFO

新增技术
1、使用了对象池技术,UFOFactory类负责飞碟的生成与回收。通过复用已生成的对象,可以节约内存。
对象池伪代码如下:

getUFO(UFOData) 
BEGIN
    IF (free list has UFO) THEN
    a_UFO = remove one from list
    ELSE
    a_UFO = clone from Prefabs
    ENDIF
    Set UFOData of a_UFO
    Add a_UFO to used list
    Return a_UFO
END
FreeDisk(ufo)
BEGIN
    Find ufo in used list
    IF (not found) THEN THROW exception
    Move ufo from used to free list
END

2、使用物理引擎控制飞碟移动。通过给飞碟预制件添加刚体组件,并在发射飞碟时给予其速度矢量,可模拟飞碟被发射后的真实运动轨迹。

3、场景单实例。使用Singleton<T>.Instance,可在任何地方获取继承自MonoBehavior的单实例对象。

游戏框架
image.png
FirstController与ISceneController、SSDirector等类的关系依旧不变,在此不再画出,详见之前作业报告。

重要代码
1、Singleton<T>

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

2、UFOFactory

using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices.WindowsRuntime;
using UnityEngine;

struct Rule {

}

public class UFOFactory : MonoBehaviour
{
    private List<GameObject> uselist;
    private List<GameObject> freelist;
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("UFOFactory Start!");
        uselist = new List<GameObject>();
        freelist = new List<GameObject>();
    }

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

    public GameObject GetUFO(UFOData data){
        GameObject ufo = null;
        bool found = false;
        if(freelist.Count != 0){
            foreach (GameObject o in freelist){
                if(o.tag == data.color){
                    ufo = o;
                    freelist.Remove(ufo);
                    found = true;
                    break;
                }
            }
        }
        if(found == false){
            ufo = Instantiate<GameObject>(
                Resources.Load<GameObject>("prefabs/" + data.color),
                Vector3.up,Quaternion.Euler(0,0,180f));
        }
        ufo.tag = data.color;
        ufo.name = data.score.ToString();
        ufo.GetComponent<Rigidbody>().mass = data.mass;
        ufo.transform.localScale = new Vector3(data.size,data.size,data.size);
        uselist.Add(ufo);
        return ufo;
    }

    public void FreeUFO(GameObject ufo){
        foreach(GameObject o in uselist){
            if(o.GetInstanceID() == ufo.GetInstanceID()){
                o.SetActive(false);
                uselist.Remove(o);
                freelist.Add(o);
                break;
            }
        }
    }
}

使用两个List维护对象池,在原需要释放对象时,改为存入空闲列表。需要调用对象时,先在空闲列表中寻找有无满足条件的对象,有则拿出使用,从而减少了对象的重复生成与销毁,减少内存占用。

3、UFOData

public struct UFOData
{
    public int size;
    public string color;
    public float mass;
    public int score;
}

4、RoundController

public class RoundController : MonoBehaviour
{
    public int round;
    private float shootdelay;
    
    public float time;
    public float total_time;

    public bool ongame;
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("RoundController Start!");
        round = 1;
        shootdelay = 2;
        ongame = true;
        time = 0;
        total_time = 0;
    }

    // Update is called once per frame
    void Update()
    {
        if(ongame){
            time += Time.deltaTime;
            total_time += Time.deltaTime;
            if(time > 1)
                Singleton<UserGUI>.Instance.UpdateTime();
            if(time > shootdelay){
                int times = Random.Range(1, 5);
                for (int i = 0;i<times;i++)
                    createUFO();
                time = 0;
            }
            if(total_time > 50){
                Singleton<FirstController>.Instance.GameOver();
            }
        }
    }

    public void createUFO(){
        UFOData data = new UFOData();
        data.size = Random.Range(2,5);
        int color = Random.Range(0,3);
        switch(color){
            case 0:
                data.color = "Red";break;
            case 1:
                data.color = "Blue";break;
            case 2:
                data.color = "White";break;
        }
        data.mass = Random.Range(1,2);
        float speedfactor = (float)Random.Range(1 + (float)(0.5*round < 3.5 ? 0.5*round : 3.5),5);
        data.score = (int)(speedfactor * 5 / data.size);
        GameObject ufo = Singleton<UFOFactory>.Instance.GetUFO(data);
        Singleton<UFOActionManager>.Instance.setMove(ufo,speedfactor);
    }
}

按照一定规则生成UFOdata,并交由UFOFactory生成对象。取得对象后,再将对象交由UFOActionManager赋予动作。
同时维护游戏时间,超时则游戏结束。

5、ScoreController

public class ScoreController : MonoBehaviour
{
    public int total_score;
    public int now_score;
    public int score_to_next;
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("ScoreController Start!");
        now_score = 0;
        score_to_next = 50;
    }

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

    public void AddScore(GameObject ufo){
        int add_score;
        int.TryParse(ufo.name,out add_score);
        now_score += add_score;
        total_score += add_score;
        if(now_score >= score_to_next){
            Singleton<FirstController>.Instance.Brake();
        }
        Singleton<UserGUI>.Instance.UpdateScore();
    } 
}

添加、记录得分,并在满足得分要求后进入下一轮。

6、FlyAction

public class FlyAction : SSAction
{
    private Vector3 velocity;
    private Vector3 spawnpos;
    private Rigidbody rb;
    // Start is called before the first frame update
    public static FlyAction GetFlyAction(Vector3 velocity, Vector3 spawnpos){
        FlyAction action = ScriptableObject.CreateInstance<FlyAction> ();
        action.velocity = velocity;
        action.spawnpos = spawnpos;
        return action;
    }
    public override void Start()
    {
        gameobject.transform.position = spawnpos;
        gameobject.SetActive(true);
        rb = gameobject.GetComponent<Rigidbody>();
        //rb.MovePosition(spawnpos);
        rb.velocity = velocity;
    }

    public override void Update()
    {
        if(gameobject.transform.position.y <= 0){
            Debug.Log(gameobject.transform.position.y);
            Singleton<UFOFactory>.Instance.FreeUFO(gameobject);
            this.destory = true;
        }
    }
}

赋予刚体组件以初速度和初位置,从而交由物理引擎管理物体动作。


礼貌的羽毛球
1 声望0 粉丝