空間與運動——作業與練習

簡答題

簡答並用程序驗證

遊戲對象運動的本質是什麼?

遊戲運動的本質是遊戲對象隨着時間幀的變化而做出相應的動作,包括座標位置的改變,旋轉和對相應的用戶時間做出反應等。

請用三種方法以上方法,實現物體的拋物線運動。(如,修改Transform屬性,使用向量Vector3的方法…)

修改Transform屬性
根據平拋運動公式來分別實現x和y方向的運動方式:


    public float speedX = 0.8f;
    public float sppedY = 0;
    public int gravity = 10;
	void Start () {
        Debug.Log("start!");
	}
	
	// Update is called once per frame
	void Update () {
 
        this.transform.position += Vector3.down * Time.deltaTime * speedY;
        this.transform.position += Vector3.right * Time.deltaTime * speedX;
        speedY += gravity * Time.deltaTime;
    }

使用Vector3

	void Update () {
 
        Vector3 vec = new Vector3(Time.deltaTime * speedX, -Time.deltaTime * speedY, 0);
        this.transform.position += vec;
        speedY += gravity * Time.deltaTime;
    }

使用translate()函數

    void Update()
    {
        Vector3 vec = new Vector3(Time.deltaTime * speedX, -Time.deltaTime * speedY, 0);
        this.transform.Translate(vec);
        speedY += gravity * Time.deltaTime;
    }

寫一個程序,實現一個完整的太陽系, 其他星球圍繞太陽的轉速必須不一樣,且不在一個法平面上。

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

public class SolarSystem : MonoBehaviour
{
    public GameObject venus;
    public GameObject earth;
    public GameObject sun;
    public GameObject saturn;
    public GameObject uranus;
    public GameObject mercury;
    public GameObject mars;
    public GameObject jupiter;
    public GameObject neptune;
    void Start()
    {
        sun = GameObject.Find("sun");
        mercury = GameObject.Find("mercury");
        venus = GameObject.Find("venus");
        earth = GameObject.Find("earth");
        mars = GameObject.Find("mars");
        jupiter = GameObject.Find("jupiter");
        saturn = GameObject.Find("saturn");
        uranus = GameObject.Find("uranus");
        neptune = GameObject.Find("neptune");

    }

    // Update is called once per frame
    void Update()
    {
        sun.transform.Rotate(Vector3.up * 3 * Time.deltaTime);
        mercury.transform.RotateAround(sun.transform.position, new Vector3(0, 3.5f, 1), 200* Time.deltaTime);
        mercury.transform.Rotate(new Vector3(0, 5, 1) * 5 * Time.deltaTime);
        venus.transform.RotateAround(sun.transform.position, new Vector3(0, 2.5f, 1), 100 * Time.deltaTime);
        venus.transform.Rotate(new Vector3(0, 2, 1) * Time.deltaTime);
        jupiter.transform.RotateAround(sun.transform.position, new Vector3(0, 9, 3), 30 * Time.deltaTime);
        jupiter.transform.Rotate(new Vector3(0, 10, 3) * 30 * Time.deltaTime);
        saturn.transform.RotateAround(sun.transform.position, new Vector3(0, 2, 1), 20 * Time.deltaTime);
        saturn.transform.Rotate(new Vector3(0, 3, 1) * 20 * Time.deltaTime);
        uranus.transform.RotateAround(sun.transform.position, new Vector3(0, 10, 1), 10 * Time.deltaTime);
        uranus.transform.Rotate(new Vector3(0, 10, 1) * 20 * Time.deltaTime);
        neptune.transform.RotateAround(sun.transform.position, new Vector3(0, 7.5f, 1), 5 * Time.deltaTime);
        neptune.transform.Rotate(new Vector3(0, 8, 1) * 30 * Time.deltaTime);
        earth.transform.RotateAround(sun.transform.position, Vector3.up, 50 * Time.deltaTime);
        earth.transform.Rotate(Vector3.up * 30 * Time.deltaTime);
        mars.transform.RotateAround(sun.transform.position, new Vector3(0, 12, 5), 40 * Time.deltaTime);
        mars.transform.Rotate(new Vector3(0, 12, 5) * 40 * Time.deltaTime);

    }
}


牧師與魔鬼(P&D)

遊戲腳本

Priests and Devils

Priests and Devils is a puzzle game in which you will help the Priests and Devils to cross the river within the time limit. There are 3 priests and 3 devils at one side of the river. They all want to get to the other side of this river, but there is only one boat and this boat can only carry two persons each time. And there must be one person steering the boat from one side to the other side. In the flash game, you can click on them to move them and click the go button to move the boat to the other direction. If the priests are out numbered by the devils on either side of the river, they get killed and the game is over. You can try it in many > ways. Keep all priests alive! Good luck!

編程要求

  • play the game (
    http://www.flash-game.net/game/2535/priests-and-devils.html )

  • 列出遊戲中提及的事物(Objects)
    用表格列出玩家動作表(規則表),注意,動作越少越好

  • 請將遊戲中對象做成預製

  • 在 GenGameObjects 中創建 長方形、正方形、球 及其色彩代表遊戲中的對象。

  • 使用 C# 集合類型 有效組織對象

  • 整個遊戲僅 主攝像機 和 一個 Empty 對象, 其他對象必須代碼動態生成!!! 。 整個遊戲不許出現 Find 遊戲對象, SendMessage 這類突破程序結構的 通訊耦合 語句。 違背本條準則,不給分

  • 請使用課件架構圖編程,不接受非 MVC 結構程序
    注意細節,例如:船未靠岸,牧師與魔鬼上下船運動中,均不能接受用戶事件!

遊戲中的對象
  • boat(船)
  • land(陸地)
  • river(河流)
  • devil(魔鬼)
  • priest(牧師)
  • skybox(天空盒)
玩家動作表
動作 結果 約束
點擊牧師 牧師移動(陸地–船 or 船–陸地) 牧師與船在同側;船或牧師沒有在移動過程中;遊戲未結束
點擊魔鬼 魔鬼移動(陸地–船 or 船–陸地) 魔鬼與船在同側;船或魔鬼沒有在移動過程中;遊戲未結束
點擊船 船移動 船未在移動過程中;船上有乘客;遊戲未結束
主要模塊

MVC架構:

主要的幾個類:


遊戲對象搭建:

一開始我使用unity自帶的3DObject搭建對象,後來覺得不太美觀,於是就從asset store上下載了一些預設,這樣看起來會稍微好一些:
devil:

priest:

然後因爲在之後需要對象捕捉到鼠標的點擊動作,所以要爲它們分別添加一個capsule collider組件,不然鼠標點擊了沒有反應。

對於boat、land和river,就索性用了cube,然後改變下參數,加上不同的material:

剛好上節課學了天空盒的設置,在這裏就順便加上了一個:

大體看上去是這樣子:

關鍵模塊

SSDirector

SSDirector獲取當前遊戲的場景,掌握着遊戲整體運行的節奏,管理遊戲全局狀態,並設定相關的配置,是遊戲的整體指揮者。

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

兩個接口ISceneController和UserAction分別提供場景加載和遊戲對象響應函數的接口:

public interface ISceneController
    {
        void loadResources();
    }

    public interface UserAction
    {
        void moveBoat(); //move boat
        void restart(); //restart game
        void objectClicked(chCtrl.CharacterController ch);
    }

CharacterControlller

實現對遊戲角色的加載和運動控制,通過構造函數進行初始化,然後實現了一些角色的運動函數,如setPosition(), movePos(), getOnBoat()等,並提供一些角色的狀態信息,以便FirstController來對角色進行總體地調度。

public CharacterController(string ch_name)
        {
            if (ch_name == "priest")
            {

                obj = Object.Instantiate(Resources.Load("prefabs/priest", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
                obj.transform.Rotate(Vector3.up, -90);
                objMark = 0;
            }
            if (ch_name == "devil")
            {
                obj = Object.Instantiate(Resources.Load("prefabs/devil", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
                obj.transform.Rotate(Vector3.up, -90);
                objMark = 1;
            }
            move = obj.AddComponent(typeof(Move)) as Move;
            clickGUI = obj.AddComponent(typeof(ClickGUI)) as ClickGUI;
            clickGUI.setController(this);
        }
        public void reset()
        {
            move.reset();
            landController = (SSDirector.getInstance().currentSceneController as FirstController).startLand;
            getOnLand(landController);
            setPosition(landController.getEmptyPlace());
            landController.addObj(this);
        }
        public int getMark() { return objMark; }
        public string getName() { return obj.name; }
        public LandController getLandController() { return landController; }
        public bool isOnBoat() { return onBoat; }
        public void setName(string name) { obj.name = name; }
        public void setPosition(Vector3 pos) { obj.transform.position = pos; }
        public void movePos(Vector3 dest) { move.setDest(dest); }
        public void getOnBoat(BoatController bo)
        {
            landController = null;
            obj.transform.parent = bo.getGameobj().transform;
            onBoat = true;
        }
        public void getOnLand(LandController la)
        {
            landController = la;
            obj.transform.parent = null;
            onBoat = false;
        }

LandController/BoatController

作用和CharacterController類似,都是對相應的對象進行控制,在LandController中會提供給FirstController陸地上的狀態信息,例如空餘的位置,在陸地上的角色信息,還有上岸離岸的操作。BoatController控制船的移動,提供船上角色信息,以及上岸上船操作。

    public LandController(string _state)
    {
        passenger = new chCtrl.CharacterController[6];
        pos = new Vector3[] {new Vector3(6.5F,0.8f,0), new Vector3(7.5F,0.8F,0),
                new Vector3(8.5F,0.8F,0),
                new Vector3(9.5F,0.8F,0), new Vector3(10.5F,0.8F,0),
                new Vector3(11.5F,0.8F,0)
        }; //object position

        if (_state == "from") //from
        {
            land = Object.Instantiate(Resources.Load("prefabs/land", typeof(GameObject)), startPos, Quaternion.identity, null) as GameObject;
            land.name = "from";
            state = 1;
        }
        else //to
        {
            land = Object.Instantiate(Resources.Load("prefabs/land", typeof(GameObject)), endPos, Quaternion.identity, null) as GameObject;
            land.name = "to";
            state = -1;
        }
    }
    public int getEmptyNum()
    {
        for (int i = 0; i < passenger.Length; i++)
        {
            if (passenger[i] == null) return i;
        }
        return -1;
    }
    public Vector3 getEmptyPlace()
    {
        int index = getEmptyNum();
        Vector3 v = pos[index];
        v.x *= state;
        return v;
    }
    public int getState()
    {
        return state;
    }
    public int[] getObjNum()
    {
        int[] count = { 0, 0 };
        for (int i = 0; i < passenger.Length; i++)
        {
            if (passenger[i] == null) continue;
            if (passenger[i].getMark() == 0) count[0]++; //p
            else count[1]++; //d
        }
        return count;
    }
    public void addObj(chCtrl.CharacterController ch) //get on land
    {
        int index = getEmptyNum();
        passenger[index] = ch;
    }
    public chCtrl.CharacterController removeObj(string name) //get off land
    {
        for (int i = 0; i < passenger.Length; i++)
        {
            if (passenger[i] != null && passenger[i].getName() == name)
            {
                chCtrl.CharacterController ch = passenger[i];
                passenger[i] = null;
                return ch;
            }
        }
        return null;
    }
  public BoatController()
    {
        state = 1;
        startPoss = new Vector3[] { new Vector3(4.5F, 0.8f, 0), new Vector3(5.5F, 0.8f, 0) };
        endPoss = new Vector3[] { new Vector3(-5.5F, 0.8f, 0), new Vector3(-4.5F, 0.8f, 0) };
        boat = Object.Instantiate(Resources.Load("prefabs/boat", typeof(GameObject)), startPos, Quaternion.identity, null) as GameObject;
        boat.name = "boat";
        move = boat.AddComponent(typeof(Move)) as Move;
        boat.AddComponent(typeof(ClickGUI));
    }
    public void reset()
    {
        move.reset();
        if (state == -1) Move();
        passenger = new chCtrl.CharacterController[2];
    }
    public GameObject getGameobj() { return boat; }
    public int getState() { return state; }
    public int[] getObjNum() //return obj index
    {
        int[] count = { 0, 0 };
        for (int i = 0; i < passenger.Length; i++)
        {
            if (passenger[i] == null)
                continue;
            if (passenger[i].getMark() == 0)
            {
                count[0]++;
            }
            else
            {
                count[1]++;
            }
        }
        return count;
    }
    public void Move() //boat move
    {
        if (state == -1) //to
        {
            move.setDest(startPos);
            state = 1;
        }
        else //from
        {
            move.setDest(endPos);
            state = -1;
        }
    }
    public int getEmptyNum()
    {
        for (int i = 0; i < passenger.Length; i++)
        {
            if (passenger[i] == null)
            {
                return i;
            }
        }
        return -1;
    }
    public Vector3 getEmptyPlace()
    {
        Vector3 pos = new Vector3();
        int index = getEmptyNum();
        if (state == -1) //to
        {
            pos = endPoss[index];
        }
        if (state == 1) //from
        {
            pos = startPoss[index];
        }
        return pos;
    }
    public bool isEmpty()
    {
        for (int i = 0; i < passenger.Length; i++)
        {
            if (passenger[i] != null)
            {
                return false;
            }
        }
        return true;
    }
    public void getOnBoat(chCtrl.CharacterController ch)
    {
        int index = getEmptyNum();
        passenger[index] = ch;
    }
    public chCtrl.CharacterController getOffBoat(string name)
    {
        for (int i = 0; i < passenger.Length; i++)
        {
            if (passenger[i] != null && passenger[i].getName() == name)
            {
                chCtrl.CharacterController ch = passenger[i];
                passenger[i] = null;
                return ch;
            }
        }
        return null;
    }

FirstController

實現對所有遊戲對象的控制,根據各個controller提供的信息來進行相關函數調用。同時實現了ISceneController和UserAction的函數接口。

    public void loadResources()
    {
        GameObject river = Instantiate(Resources.Load("prefabs/river", typeof(GameObject)), riverPos, Quaternion.identity, null) as GameObject;
        river.name = "river";
        startLand = new LandController("from");
        endLand = new LandController("to");
        boat = new BoatController();

        for (int i = 0; i < 3; i ++)
        {
            chCtrl.CharacterController _ch = new chCtrl.CharacterController("priest");
            _ch.setName("priest[" + i + "]");
            _ch.setPosition(startLand.getEmptyPlace());
            _ch.getOnLand(startLand);
            startLand.addObj(_ch);
            ch[i] = _ch;
        }
        for (int i = 0; i < 3; i++)
        {
            chCtrl.CharacterController _ch = new chCtrl.CharacterController("devil");
            _ch.setName("devil[" + i + "]");
            _ch.setPosition(startLand.getEmptyPlace());
            _ch.getOnLand(startLand);
            startLand.addObj(_ch);
            ch[i + 3] = _ch;
        }
    }
    public void moveBoat()
    {
        if (boat.isEmpty()) return;
        boat.Move();
        userGUI.status = check();
    }
    public void objectClicked(chCtrl.CharacterController _ch)
    {
        if (_ch.isOnBoat()) //obj is on boat
        {
            LandController land;
            if (boat.getState() == -1) land = endLand;
            else land = startLand;
            boat.getOffBoat(_ch.getName());
            _ch.movePos(land.getEmptyPlace());
            _ch.getOnLand(land);
            land.addObj(_ch);
        }
        else //obj is on land
        {
            LandController land = _ch.getLandController();
            if (boat.getEmptyNum() == -1) return; //full boat
            if (land.getState() != boat.getState()) return; //different side
            land.removeObj(_ch.getName());
            _ch.movePos(boat.getEmptyPlace());
            _ch.getOnBoat(boat);
            boat.getOnBoat(_ch);
        }
        userGUI.status = check();
    }
    int check()
    {
        int start_p = 0, start_d = 0, end_p = 0, end_d = 0;
        int[] start_count = startLand.getObjNum();
        start_p += start_count[0];
        start_d += start_count[1];
        int[] end_count = endLand.getObjNum();
        end_p += end_count[0];
        end_d += end_count[1];
        if (end_p + end_d == 6) return 2;

        int[] boatCount = boat.getObjNum();
        if (boat.getState() == -1) //to
        {
            end_p += boatCount[0];
            end_d += boatCount[1];
        }
        if (boat.getState() == 1) //from
        {
            start_p += boatCount[0];
            start_d += boatCount[1];
        }
        if (start_p < start_d && start_p > 0)
        {       
            return 1;
        }
        if (end_p < end_d && end_p > 0)
        {
            return 1;
        }
        return 0;
    }
    public void restart()
    {
        boat.reset();
        startLand.reset();
        endLand.reset();
        for (int i = 0; i < ch.Length; i++) ch[i].reset();
    }

UserGUI

加載了遊戲界面信息,包括按鈕和標題等。

  void Start()
    {
        userAction = SSDirector.getInstance().currentSceneController as UserAction;
        
       
    }

    void OnGUI()
    {
        GUIStyle fontstyle = new GUIStyle();
        fontstyle.normal.background = null;
        fontstyle.normal.textColor = new Color(255, 192, 203);
        fontstyle.fontSize = 50;

        style = new GUIStyle()
        {
            fontSize = 50
        };
        style.normal.textColor = new Color(0, 0, 0);
        buttonStyle = new GUIStyle("button")
        {
            fontSize = 10
        };
        GUI.Label(new Rect(250, 15, 100, 100), "Priest & Devil", fontstyle); //title
        if (status == 1)
        {
            GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 85, 100, 50), "Gameover!", style);
            if (GUI.Button(new Rect(Screen.width / 2 - 70, Screen.height / 2, 140, 70), "Restart", buttonStyle))
            {
                status = 0;
                userAction.restart();
            }
        }
        else if (status == 2)
        {
            GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 85, 100, 50), "You win!", style);
            if (GUI.Button(new Rect(Screen.width / 2 - 70, Screen.height / 2, 140, 70), "Restart", buttonStyle))
            {
                status = 0;
                userAction.restart();
            }

        }
    }

除此之外,還有Move和ClickGUI等輔助類,定義物體的運動和鼠標點擊後的事件等,具體代碼見github。
github

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章