3Dgame作業6:打飛碟修改版

大練功狂發表於2020-11-09

簡單的打飛碟遊戲

設計結構

本次採用了MVC結構,在Driect.cs檔案中定義了Direct,UserAction,ISceneController三個類,對於

MVC結構的作用是重要的:

程式碼如下;

Direct.cs

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

public class Direct : System.Object
{
    private static Direct instance;
    public ISceneController currentScene{get;set;}
    public static Direct getInstance(){
        if (instance==null)
            instance=new Direct();
        return instance;
    }

}

public interface ISceneController{
    
}
public interface UserAction{
    void getScore(int n);
    void getLost(int n);

}

public interface PAction{
    void addGravity();
    
}

Direct類用於管理當前的場記類,做一些傳參(用instance指明瞭現在的場記),還有管理一些重要功能等。

ISceneController提供了每個場記必須要有的函式,這裡是空的,ISceneController主要起一個標記作用,這樣在其他物體的類裡面,就可以通過Direct找到當前的Controller了。

UserAction規定了使用者可能會發生的事情,在複雜的情況,可以一個場記,一個Action介面。這裡的GetScore是玩家獲得得分的事件,Getlost是玩家丟掉分數的事件(沒打到飛碟)。

新加內容

PAction介面是新加的,用於物理引擎的操作。在飛碟的管理類中實現addGravity函式給飛碟施加恆定的重力。

FirstController.cs

場記用於處理在該場景下的一切事務,包括資源的載入,物體的排程,事件的處理等。其他的許多物體類,也都收到FirstController類的管理。

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

public class FirstController : MonoBehaviour,ISceneController,UserAction
{
    // Start is called before the first frame update
    public GameObject[] terrain;
    public UFOController uc;
    public int score;
    public int lost;
    public yesyes[] yes;

    public GUIStyle style;

    public int round;



    void Start()
    {
        Direct.getInstance().currentScene=this;
        loadResources();
        Random.InitState((int)System.DateTime.Now.Ticks);
        uc.StartRound();
        score=0; lost=0;
        round=1;


    }

    // Update is called once per frame
    
    void Update()
    {
        if (round>=4) return;
        uc.UFOUpdate();

        if (uc.isOver()==1){
            round++;
            if (round>=4) 
                return;
            uc.setDifficulty(round);
            uc.StartRound();
        }  

    }

    void FixedUpdate(){
        uc.UFOFixedUpdate();
      
    }

    void OnGUI(){
        if (round>=4){
            style.alignment=TextAnchor.MiddleCenter;
            style.normal.textColor=Color.black;
            style.fontSize=250;
            if (score>lost){
                GUI.Label(new Rect(0,0,Screen.width,Screen.height),"你贏了",style);
            }else{
                GUI.Label(new Rect(0,0,Screen.width,Screen.height),"你輸了",style);
            }
        }

        style.alignment=TextAnchor.MiddleLeft;
        style.normal.textColor=Color.black;
        style.fontSize=40;
        GUI.Label(new Rect(0,Screen.height-100,Screen.width,100),"白 1 分      藍 2 分      紅 3 分"+"                                     Score : "+score.ToString()+"       Lost : "+lost.ToString()+"              Round "+round.ToString(),style);
    
        for(int i=1;i<=33;++i){
            if (yes[i].isOn==1){
                if (Time.time-yes[i].stime>=3){
                    yes[i].isOn=0;
                    continue;
                }
                else{
                    if (yes[i].color=="white") style.normal.textColor=Color.gray;
                    if (yes[i].color=="blue") style.normal.textColor=Color.blue;
                    if (yes[i].color=="red") style.normal.textColor=Color.red;

                    GUI.Label(new Rect(yes[i].x,yes[i].y,400,200),"YesYes",style);
                }
            }
        }

    }

    public void loadResources(){
        terrain=new GameObject[4];
        terrain[0]=Instantiate(Resources.Load<GameObject>("Terrain"),new Vector3(0,0,0),Quaternion.identity);
        terrain[1]=Instantiate(terrain[0],new Vector3(-1000,0,0),Quaternion.identity);
        terrain[2]=Instantiate(terrain[0],new Vector3(-1000,0,-1000),Quaternion.identity);
        terrain[3]=Instantiate(terrain[0],new Vector3(0,0,-1000),Quaternion.identity);

        uc=new UFOController();
        yes=new yesyes[34];
        for (int i=0;i<=33;++i){
            yes[i]=new yesyes();
        }
    }

    public void getScore(int n){
        score+=n;

        for (int i=1;i<=33;++i){
            if (yes[i].isOn==0){
                yes[i].isOn=1;
                yes[i].x=Input.mousePosition.x;
                yes[i].y=Screen.height-Input.mousePosition.y;
                yes[i].stime=Time.time;

                if (n==1) yes[i].color="white";
                if (n==2) yes[i].color="blue";
                if (n==3) yes[i].color="red";

                break;
            }
        }
    }

    public void getLost(int n){
        lost+=n;
    }


}


public class yesyes{
    public int isOn;
    public float x,y;
    public float stime;
    public string color;

    public yesyes(){
        isOn=0;
        x=-200;y=-200;
        stime=-100;
        color="white";
    }

    public void over(){
        isOn=0;
    }
}

在FirstController裡面首先在Start中確立了自己和Direct類的關係。然後載入自己的資源。

在Update函式中呼叫UFOController類裡的函式UFOUpdate,這個是下一個檔案中的類。用於管理所有的UFO,減輕場記的程式碼負擔和混亂度。在Update中還判斷了遊戲結束的條件。因為遊戲分為3個round(關卡),隨著round的提高,遊戲的難度會變高。在Update函式中要處理這些變化。

FirstController還實現了介面UserAction的函式定義,獲取或者失去得分。

新加內容

FixedUpdate函式是新加的,用於物理引擎。FixUpdate函式會在每次運算物理引擎之前先進行運算。也就是在這個函式中,施加力或者改變速度等。可以和物理運算保持同步。

UFOController.cs

負責管理UFO的生成,移動,銷燬,等所有事物。在此檔案中還定義了有關UFO的其他類,記錄UFO的資訊或者管理UFO的點選事件。

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

public class UFOController :MonoBehaviour,PAction
{
    
    public GameObject white,blue,red;
    public UFO[] ufos;
   
    public int diff;

    public float stime;

    private float lastCreateTime;
    public void UFOUpdate(){
        int number=getNumber();
        
        if (Time.time-stime>=9.5f){
            
        }else{
            if (Time.time-lastCreateTime>=1){
                lastCreateTime=Time.time;

                for (int i=1;i<=diff;++i){
                    CreateUFO();
                }
            }
        }

        for(int i=1;i<=33;++i){
            if (ufos[i].obj!=null){
                //ufos[i].obj.transform.position=Vector3.MoveTowards(ufos[i].obj.transform.position,ufos[i].target,ufos[i].speed);
                if (ufos[i].obj.transform.position==ufos[i].target || ufos[i].obj.transform.position.y<=4.5){
                    ufos[i].obj.GetComponent<UFOClass>().getOut();
                    number--;
                }
            }
        }
        
    }
    public void UFOFixedUpdate(){
        addGravity();
    }

    public void addGravity(){
        for(int i=1;i<=33;++i){
            if (ufos[i].obj!=null){
                ufos[i].obj.GetComponent<Rigidbody>().AddForce(new Vector3(0,-10,0));
            }
        }
    }

    public void StartRound(){
        stime=Time.time;
    }

    public int isOver(){
        if (getNumber()==0 && Time.time-stime>=10){
            return 1;
        }
        return 0;
    }

    public UFOController(){
        white=Resources.Load<GameObject>("whiteUFO");
        blue=Resources.Load<GameObject>("blueUFO");
        red=Resources.Load<GameObject>("redUFO");

        Rigidbody rb=new Rigidbody();

        white.AddComponent<Rigidbody>();
        white.GetComponent<Rigidbody>().detectCollisions=false;
        white.GetComponent<Rigidbody>().useGravity=false;
        white.GetComponent<Rigidbody>().mass=1;
        white.GetComponent<Rigidbody>().drag=0;
        white.GetComponent<Rigidbody>().angularDrag=10;

        blue.AddComponent<Rigidbody>();
        blue.GetComponent<Rigidbody>().detectCollisions=false;
        blue.GetComponent<Rigidbody>().useGravity=false;
        blue.GetComponent<Rigidbody>().mass=1;
        blue.GetComponent<Rigidbody>().drag=0;
        blue.GetComponent<Rigidbody>().angularDrag=10;

        red.AddComponent<Rigidbody>();
        red.GetComponent<Rigidbody>().detectCollisions=false;
        red.GetComponent<Rigidbody>().useGravity=false;
        red.GetComponent<Rigidbody>().mass=1;
        red.GetComponent<Rigidbody>().drag=0;
        red.GetComponent<Rigidbody>().angularDrag=10;

        ufos=new UFO[34];
        for (int i=0;i<34;++i){
            ufos[i]=new UFO();
        }
        diff=1;
        stime=-100;
        lastCreateTime=-100;
    }
    public void setDifficulty(int d){
        diff=d;
        
    }


    public int getNumber(){
        int ans=0;
        for (int i=1;i<=33;++i){
            if (ufos[i].obj!=null)
                ans++;
        }
        return ans;
    }

    public void CreateUFO(){
        UFO u=new UFO();
        for (int i=1;i<=33;++i){
            if (ufos[i].obj==null){
                u=ufos[i];
                break;
            }
        }


        u.init(diff);

        int kind;
        kind=Random.Range(0,100);
        if (kind<=33){
            u.obj=Instantiate(white,u.pos,Quaternion.identity);
            u.obj.name="white";
        }
        else if (kind<=66){
            u.obj=Instantiate(blue,u.pos,Quaternion.identity);
            u.obj.name="blue";
        }
        else{
            u.obj=Instantiate(red,u.pos,Quaternion.identity);
            u.obj.name="red";
        }

        u.initVelocity(diff);
        u.obj.AddComponent(typeof(UFOClass));
    }
}

public class UFO {
    public float speed;
    public Vector3 target;
    public GameObject obj;
    public Vector3 pos;
   
    public UFO(){

    }

    public void init(int diff){
        Object.Destroy(obj);
        float y=Random.Range(60,120f);
        float x=150;
        if (Random.Range(0,2)==0){
            x=-x;
        }
        pos=new Vector3(x,y,80);

        x=-x;
        y=Random.Range(60f,120f);
        target=new Vector3(x,y,80);
        

        speed=Random.Range(0.04f,0.06f);
        speed=speed*diff;


    }

    public void initVelocity(int diff){
        Vector3 dir=target-pos;
        dir=dir/dir.magnitude;
        obj.GetComponent<Rigidbody>().velocity=dir*diff*50;
    }
}


public class UFOClass:MonoBehaviour{
    private UserAction action;

    public UFOClass(){
        action=Direct.getInstance().currentScene as UserAction;
    }

    void OnMouseDown(){
        string s=gameObject.name;
        if (s=="white"){
            action.getScore(1);
        }
        if (s=="blue"){
            action.getScore(2);
        }
        if (s=="red"){
            action.getScore(3);
        }
        Destroy(gameObject);
        
    }

    public void getOut(){
        string s=gameObject.name;
        if (s=="white"){
            action.getLost(1);
        }
        if (s=="blue"){
            action.getLost(2);
        }
        if (s=="red"){
            action.getLost(3);
        }
        Destroy(gameObject);        
    }
}

按照我設計的演算法,遊戲中最多可能出現33個UFO,所以用一個33的陣列就能存放下全部的內容。(不想用連結串列)

	public UFO[] ufos;

CreateUFO用於建立UFO,根據難度值,對UFO的速度進行調整。
StartRound用於重新開始一個回合
SetDifficulty用於設定難度,一般用在每個回合開始前。
getNumber統計當前UFO的個數。
isOver判斷當前回合有沒有結束。如果飛碟數為0,並且已經開始了一段時間(防止剛開始就結束),那麼此回合就結束了。將有FirstController開啟下一回合。
UFOUpdate在FirstController中的Update中被呼叫,兩者的呼叫是同步進行的。UFOUpdate主要用於管理飛碟的移動,判斷建立新飛碟的條件,並且判斷飛碟出界或者到達目的地的條件,是這個類的核心。

下面的UFO和UFOClass兩個類:

UFO類是為了記錄每一個飛碟的資訊,比如初始位置,速度,目的地等,還有一個obj作為指標指向飛碟的GameObject。內部inti函式會根據當前的難度進行重新構造,完成了資源的重新利用。

UFOClass主要用於AddComponent函式,給飛碟的GameObject加入元件。該元件用於處理點選事件。裡面的action變數是利用MVC結構的特性,從而完成了對場記中玩家事件函式的呼叫。

程式碼部分至此介紹完畢。

新加物理引擎部分

UFOFixedUpdate函式。在每次FirstController執行FixedUpdate時,一起執行這裡的UFOFixedUpdate函式。在這裡主要進行給飛碟加重力的操作:

    public void UFOFixedUpdate(){
        addGravity();
    }
    public void addGravity(){
        for(int i=1;i<=33;++i){
            if (ufos[i].obj!=null){
                ufos[i].obj.GetComponent<Rigidbody>().AddForce(new Vector3(0,-10,0));
            }
        }
    }

另外,新加部分還有為每個飛碟新增剛體屬性,並調配引數。

在建構函式裡面。

        white.AddComponent<Rigidbody>();
        white.GetComponent<Rigidbody>().detectCollisions=false;
        white.GetComponent<Rigidbody>().useGravity=false;
        white.GetComponent<Rigidbody>().mass=1;
        white.GetComponent<Rigidbody>().drag=0;
        white.GetComponent<Rigidbody>().angularDrag=10;

        blue.AddComponent<Rigidbody>();
        blue.GetComponent<Rigidbody>().detectCollisions=false;
        blue.GetComponent<Rigidbody>().useGravity=false;
        blue.GetComponent<Rigidbody>().mass=1;
        blue.GetComponent<Rigidbody>().drag=0;
        blue.GetComponent<Rigidbody>().angularDrag=10;

        red.AddComponent<Rigidbody>();
        red.GetComponent<Rigidbody>().detectCollisions=false;
        red.GetComponent<Rigidbody>().useGravity=false;
        red.GetComponent<Rigidbody>().mass=1;
        red.GetComponent<Rigidbody>().drag=0;
        red.GetComponent<Rigidbody>().angularDrag=10;

成果

在這裡插入圖片描述

附加功能

說說下面這個yesyes類

位置: FirstController.cs裡面。額外定義的一個類。

作用:在滑鼠點選飛碟後,我希望螢幕上能顯示出一個與飛碟同顏色的“yesyes”字樣來提醒打到了。

引數:記錄了每一個yesyes字樣的應有顏色,位置,以及是否應該被顯示,開始顯示的時間是什麼。顯示時間超過三秒,就不再顯示了。這些都在OnGui函式中進行了判斷。

效果
在這裡插入圖片描述
打掉的飛碟都變成了yesyes

斯卡文腐蝕+1

專案連結

程式碼連結
專案下載

專案使用方法

下載Asserts.zip解壓,用我的Asserts覆蓋你專案的Asserts。重新整理你的專案,雙擊UFOScene,點選執行。

相關文章