3D遊戲設計 homework3

1、簡答並用程序驗證

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

遊戲對象運動本質就是在每一幀改變遊戲對象的空間屬性(位置、歐拉角、比例),並展現出來。

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

(1)修改Transform屬性:利用transform改變position來實現拋物線運動,根據物理的運動學知識,只要水平方向的移動速度是不變的,豎直方向有一定的加速度變化的,兩個方向的運動矢量相加便是拋物線運動。具體實現:

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

public class movement1 : MonoBehaviour
{
    public float speed;
    // Start is called before the first frame update
    void Start()
    {
        speed = 0;
    }

    // Update is called once per frame
    void Update()
    {
        this.transform.position += Vector3.left * Time.deltaTime * 4;
        this.transform.position += speed / 10 * Vector3.down * Time.deltaTime;
        ++speed;
    }
}

(2)使用向量Vector3:定義一個向量Vector3,同時定義該變量的值,其也是豎直方向上是一個均勻增加的數值,水平方向是一個保持不變的數值,然後將遊戲對象原本的position屬性與該向量相加。具體實現:

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

public class movement2 : MonoBehaviour
{
    public float speed;
    // Start is called before the first frame update
    void Start()
    {
        speed = 0;
    }

    // Update is called once per frame
    void Update()
    {
        Vector3 vec1= new Vector3(-Time.deltaTime * 4, -Time.deltaTime * (speed / 10), 0);
        ++speed;
        this.transform.position += vec1;
    }
}

(3)利用transform的Translate函數:傳入一個Vector3變量來改變position,該變量做拋物線運動。具體實現如下:

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

public class movement3 : MonoBehaviour
{
    public float speed;
    // Start is called before the first frame update
    void Start()
    {
        speed = 0;
    }

    // Update is called once per frame
    void Update()
    {
        Vector3 vec1= new Vector3(-Time.deltaTime * 4, -Time.deltaTime * (speed / 10), 0);
        ++speed;
        transform.Translate(vec1);
    }
}

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

(1)下載太陽系貼圖:
下載鏈接
圖片
(2)設置相對位置和貼圖:
相對位置
(3)實現自轉和公轉腳本:

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

public class move : MonoBehaviour
{
    public Transform center;    //各天體公轉的圓心
    public float globalSpeed;        //公轉速度
    public float selfSpeed;        //自轉速度
    public float ry, rz;        //公轉平面所在的軸

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        Vector3 axis = new Vector3(0, ry, rz);     //公轉軸
        this.transform.RotateAround(center.position, axis, globalSpeed * Time.deltaTime);   //公轉
        this.transform.Rotate(Vector3.up * selfSpeed * Time.deltaTime);       //自轉
    }
}

通過設置公轉軸使得不在同一個法平面公轉,調整速度使得公轉速度不一致。
效果

2、編程實踐

閱讀以下游戲腳本

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 )

(1)列出遊戲中提及的事物(Objects):

牧師,惡魔,船,河,兩岸

(2)用表格列出玩家動作表(規則表),注意,動作越少越好

動作 條件
開船 有人物在上面
上船 船在對應邊且船上有位置 (船隻有倆個位置)
下船 船在對應邊
殺人 在船上時牧師人數不大於惡魔或者岸邊時牧師人數小於惡魔

(3)請將遊戲中對象做成預製;在 GenGameObjects 中創建 長方形、正方形、球 及其色彩代表遊戲中的對象。

預製
預製對象的使用:參考官方文檔
使用 C# 集合類型 有效組織對象
編程要求:整個遊戲僅 主攝像機 和 一個 Empty 對象, 其他對象必須代碼動態生成!!! 。 整個遊戲不許出現 Find 遊戲對象, SendMessage 這類突破程序結構的 通訊耦合 語句。 違背本條準則,不給分
請使用課件架構圖編程,不接受非 MVC 結構程序
注意細節,例如:船未靠岸,牧師與魔鬼上下船運動中,均不能接受用戶事件!

(4)分別創建三個腳本Model、View、Controller,其中Model和Controller腳本掛在主攝像機下,View掛在empty下。

  1. Model:實現具體動作及邏輯,同時加載預製對象。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using mygame;

public class Model : MonoBehaviour
{

    Stack<GameObject> start_priests = new Stack<GameObject>();
    Stack<GameObject> end_priests = new Stack<GameObject>();
    Stack<GameObject> start_devils = new Stack<GameObject>();
    Stack<GameObject> end_devils = new Stack<GameObject>();
    
    GameObject[] boat = new GameObject[2];
    GameObject boat_obj;
    public float speed = 50;

    SSDirector one;

    //對象的位置
    Vector3 boatStartPos = new Vector3(-2, 0.25f, 0);
    Vector3 boatEndPos = new Vector3(2, 0.25f, 0);
    Vector3 sideStartPos = new Vector3(-5, 0, 0);
    Vector3 sideEndPos = new Vector3(5, 0, 0);

    Vector3 riverPos = new Vector3(0, -0.25f, 0); 

    float gap = 1.1f;
    Vector3 priestsStartPos = new Vector3(-3.25f, 0.75f, 0);
    Vector3 priestsEndPos = new Vector3(3.25f, 0.75f, 0);
    Vector3 devilsStartPos = new Vector3(-5, 1, 0);
    Vector3 devilsEndPos = new Vector3(5, 1, 0);


    // Start is called before the first frame update
    void Start()
    {
        one = SSDirector.GetInstance();
        one.setModel(this);
        loadSrc();
    }

    // Update is called once per frame
    void Update()
    {
        setpositionS(start_priests, priestsStartPos);
        setpositionE(end_priests, priestsEndPos);
        setpositionS(start_devils, devilsStartPos);
        setpositionE(end_devils, devilsEndPos);

        if (one.state == State.SE_move)
        {
            boat_obj.transform.position = Vector3.MoveTowards(boat_obj.transform.position, boatEndPos, Time.deltaTime * speed);
            if (boat_obj.transform.position == boatEndPos)
            {
                one.state = State.End;
            }
            checkD();
        }
        else if (one.state == State.ES_move)
        {
            boat_obj.transform.position = Vector3.MoveTowards(boat_obj.transform.position, boatStartPos, Time.deltaTime * speed);
            if (boat_obj.transform.position == boatStartPos)
            {
                one.state = State.Start;
            }
            checkD();
        }
        checkV();
    }

    //加載遊戲對象
    void loadSrc()
    {
        //sides
        Instantiate(Resources.Load("side"), sideStartPos, Quaternion.identity);
        Instantiate(Resources.Load("side"), sideEndPos, Quaternion.identity);

        //river
        Instantiate(Resources.Load("river"), riverPos, Quaternion.identity);

        //boat
        boat_obj = Instantiate(Resources.Load("boat"), boatStartPos, Quaternion.identity) as GameObject;

        //prisets and devils
        for (int i = 0; i < 3; i++)
        {
            start_priests.Push(Instantiate(Resources.Load("Priest")) as GameObject);
            start_devils.Push(Instantiate(Resources.Load("Devil")) as GameObject);
        }
    }

    //設置起點對象位置
    void setpositionS(Stack<GameObject> aaa, Vector3 pos)
    {
        GameObject[] temp = aaa.ToArray();
        for (int i = 0; i < aaa.Count; i++)
        {
            temp[i].transform.position = pos + new Vector3(-gap * i * 0.5f, 0, 0);
        }
    }

    //設置終點對象位置
    void setpositionE(Stack<GameObject> aaa, Vector3 pos)
    {
        GameObject[] temp = aaa.ToArray();
        for (int i = 0; i < aaa.Count; i++)
        {
            temp[i].transform.position = pos + new Vector3(gap * i * 0.5f, 0, 0);
        }
    }

        //上船
        void getOnTheBoat(GameObject obj)
    {
        obj.transform.parent = boat_obj.transform;
        if (boatNum() != 0)
        {
            if (boat[0] == null)
            {
                boat[0] = obj;
                obj.transform.position = boat_obj.transform.position + new Vector3(0.3f, obj.transform.position.y - 0.25f, 0);
            }
            else
            {
                boat[1] = obj;
                obj.transform.position = boat_obj.transform.position + new Vector3(-0.3f, obj.transform.position.y - 0.25f, 0);
            }
        }
    }
    //判斷船上的空位數
    int boatNum()
    {
        int num = 0;
        for (int i = 0; i < 2; i++)
        {
            if (boat[i] == null)
            {
               ++ num;
            }
        }
        return num;
    }

    //船移動
    public void moveBoat()
    {
        if (boatNum() != 2)
        {
            if (one.state == State.Start)
            {
                one.state = State.SE_move;
            }
            else if (one.state == State.End)
            {
                one.state = State.ES_move;
            }
        }
    }

    //下船
    public void getOffTheBoat(int flag)
    {
        if (flag == 0 || flag == 2)
        {
            for (int i = 0; i < 2; ++i)
            {
                if (boat[i] != null && boat[i].tag == "Priest")
                {
                    Debug.Log("enter1");
                    if (flag == 0)
                    {
                        start_priests.Push(boat[i]);
                    }
                    else
                    {
                        end_priests.Push(boat[i]);
                    }
                    boat[i] = null;
                    break;
                }
            }
        }
        else if (flag == 1 || flag == 3)
        {
            for (int i = 0; i < 2; ++i)
            {
                if (boat[i] != null && boat[i].tag == "Devil")
                {
                    if (flag == 1)
                    {
                        start_devils.Push(boat[i]);
                    }
                    else
                    {
                        end_devils.Push(boat[i]);
                    }
                    boat[i] = null;
                    break;
                }
            }
        }
        else
        {
            for (int i = 0; i < 2; ++i)
            {
                if (boat[i] != null)
                {   
                    if (boat[i].tag == "Devil")
                    {
                        start_devils.Push(boat[i]);
                    }
                    else
                        start_priests.Push(boat[i]);
                    boat[i] = null;
                }
            }
        }
    }

    //檢查是否失敗
    void checkD()
    {
        int bp = 0, bd = 0;
        for (int i = 0; i < 2; i++)
        {
            if (boat[i] != null && boat[i].tag == "Priest")
            {
                bp++;
            }
            else if (boat[i] != null && boat[i].tag == "Devil")
            {
                bd++;
            }
        }

        int sp = start_priests.Count, sd = start_devils.Count, ep = end_priests.Count, ed = end_devils.Count;
        if (one.state == State.Start)
        {
            int sum = sp + bp;
            if ((sum != 0 && sum < (sd + bd)) || (ep != 0 && ep < ed))
                one.state = State.Lose;
        }
        else
        {
            int sum = ep + bp;
            if ((sp != 0 && sp < sd) || (sum != 0 && sum < ed + bd))
                one.state = State.Lose;
        }
    }

    //檢查是否勝利
    void checkV()
    {
        if (end_devils.Count == 3 && end_priests.Count == 3)
        {
            one.state = State.Win;
            return;
        }
    }

    //遊戲對象從岸上到船上的變化
    public void priS()
    {
        if (start_priests.Count != 0 && boatNum() != 0 && one.state == State.Start)
        {
            getOnTheBoat(start_priests.Pop());
        }
    }
    public void priE()
    {
        if (end_priests.Count != 0 && boatNum() != 0 && one.state == State.End)
        {
            getOnTheBoat(end_priests.Pop());
        }
    }
    public void delS()
    {
        if (start_devils.Count != 0 && boatNum() != 0 && one.state == State.Start)
        {
            getOnTheBoat(start_devils.Pop());
        }
    }
    public void delE()
    {
        if (end_devils.Count != 0 && boatNum() != 0 && one.state == State.End)
        {
            getOnTheBoat(end_devils.Pop());
        }
    }

    //重置遊戲
    public void Reset()
    {
        boat_obj.transform.position = boatStartPos;
        one.state = State.Start;

        int num1 = end_devils.Count, num2 = end_priests.Count;
        for (int i = 0; i < num1; i++)
        {
            Debug.Log(i);
            start_devils.Push(end_devils.Pop());
        }

        for (int i = 0; i < num2; i++)
        {
            start_priests.Push(end_priests.Pop());
        }
        if (boatNum() != 2)
        {
            getOffTheBoat(4);
        }
    }
}

  1. View:處收 Input 事件,渲染 GUI ,接收事件。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using mygame;

public class View : MonoBehaviour
{

    SSDirector one;
    UserAction action;

     // Start is called before the first frame update
    void Start()
    {
        one = SSDirector.GetInstance();
        action = SSDirector.GetInstance() as UserAction;
    }

    private void OnGUI()
    {
        GUI.skin.label.fontSize = 30;
        if (one.state == State.Win)
        {
            if (GUI.Button(new Rect(650, 140, 300, 100), "WIN\n(click here to reset)"))
            {
                action.reset();
            }
        }
        if (one.state == State.Lose)
        {
            if (GUI.Button(new Rect(650, 140, 300, 100), "LOSE\n(click here to reset)"))
            {
                action.reset();
            }
        }

        if (GUI.Button(new Rect(700, 80, 100, 50), "GO"))
        {
            action.moveBoat();
        }
        if (GUI.Button(new Rect(550, 250, 90, 50), "LPriestsOFF"))
        {
            action.offBoatLP();
        }
        if (GUI.Button(new Rect(950, 250, 90, 50), "RPriestsOFF"))
        {
            action.offBoatRP();
        }
        if (GUI.Button(new Rect(650, 250, 90, 50), "LDevilsOFF"))
        {
            action.offBoatLD();
        }
        if (GUI.Button(new Rect(850, 250, 90, 50), "RDevilsOFF"))
        {
            action.offBoatRD();
        }
        if (GUI.Button(new Rect(350, 130, 75, 50), "PriestsON"))
        {
            action.priestSOnSide();
        }
        if (GUI.Button(new Rect(500, 130, 75, 50), "DevilsON"))
        {
            action.devilSOnSide();
        }
        if (GUI.Button(new Rect(1050, 130, 75, 50), "DevilsON"))
        {
            action.devilEOnSide();
        }
        if (GUI.Button(new Rect(1200, 130, 75, 50), "PriestsON"))
        {
            action.priestEOnSide();
        }
    }
}
  1. Controller:接受用戶事件,控制模型的變化。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using mygame;

namespace mygame
{
    public enum State {Start, SE_move, ES_move, End, Win, Lose };

    public interface UserAction
    {
        void priestSOnSide();
        void priestEOnSide();
        void devilSOnSide();
        void devilEOnSide();
        void moveBoat();
        void offBoatLP();
        void offBoatRP();
        void offBoatLD();
        void offBoatRD();
        void reset();
    }

    public class SSDirector : System.Object, UserAction
    {
        private static SSDirector _instance;

        public Controller currentScenceController;
        public State state = State.Start;
        private Model game_obj;

        public static SSDirector GetInstance()
        {
            if (_instance == null)
            {
                _instance = new SSDirector();
            }
            return _instance;
        }

        public Model getModel()
        {
            return game_obj;
        }

        internal void setModel(Model someone)
        {
            if (game_obj == null)
            {
                game_obj = someone;
            }
        }

        public void priestSOnSide()
        {
            game_obj.priS();
        }
        public void priestEOnSide()
        {
            game_obj.priE();
        }
        public void devilSOnSide()
        {
            game_obj.delS();
        }
        public void devilEOnSide()
        {
            game_obj.delE();
        }
        public void moveBoat()
        {
            game_obj.moveBoat();
        }
        public void offBoatLP()
        {
            game_obj.getOffTheBoat(0);
        }
        public void offBoatRP()
        {
            game_obj.getOffTheBoat(2);
        }
        public void offBoatLD()
        {
            game_obj.getOffTheBoat(1);
        }
        public void offBoatRD()
        {
            game_obj.getOffTheBoat(3);
        }
        public void reset()
        {
            game_obj.Reset();
        }
    }
}

public class Controller : MonoBehaviour
{

    // Start is called before the first frame update
    void Start()
    {
        SSDirector one = SSDirector.GetInstance();
    }

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

    }
}

最終效果:
效果
視頻鏈接

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