3D游戏编程作业——空间与运动

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/SYSU_yzt/article/details/100922778

3D游戏编程作业——空间与运动

C#自学

搜素 “C# 集合类型”, 了解List,HashTable的使用

List

List实现了很多接口

public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable

List的创建List<T> list = new List<T>(); List的元素添加list.Add(T()); List返回其长度list.Count List根据下标序号去除元素list.RemoveAt(x); 直接遍历Listforeach (T item in list)或者直接对其中各个元素进行某个函数操作list.ForEach(xxx;) List批量添加元素list.AddRange(new T[] {t1, t2, t3});

HashTable

HashTable类是一系列基于键的哈希代码组织起来的键值对,它根据键来访问集合中的元素。
Count属性用来获取HashTable中的键值对个数。IsFixedSize用来表示哈希表是否是固定大小的。IsReadOnly反映哈希表的读写权限。KeysValues成双成对。public virtual void Add( object key, object value ) 用来添加元素。public virtual void Clear()用来清空哈希表。public virtual bool ContainsKey( object key )用来判断是否有含此键的键值对。public virtual bool ContainsValue( object value )用来判断是否有含有此值的键值对。public virtual void Remove( object key )用来移除元素。

1.简答并用程序验证【建议做】

游戏对象运动的本质是什么?

答:就是使用矩阵变换(平移,旋转,缩放)改变游戏对象的控件属性。

请用三种方法以上方法,实现物体的抛物线运动。(如,修改Transform属性,使用向量Vector3的方法…)

答:方法1:用Vector3修改Transform属性

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

public class move : MonoBehaviour
{
    private float speedx;
    private float speedy;
    // Start is called before the first frame update
    void Start()
    {
        speedy = 10;
        speedx = 10;
    }

    // Update is called once per frame
    void Update()
    {
        speedy -= 9.8f * Time.deltaTime;
        Vector3 var = new Vector3(0, speedy, speedx) * Time.deltaTime;
        this.transform.position += var;
    }
}

方法2:使用平移方法Translate

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

public class move : MonoBehaviour
{
    private float speedx;
    private float speedy;
    // Start is called before the first frame update
    void Start()
    {
        speedy = 10;
        speedx = 10;
    }

    // Update is called once per frame
    void Update()
    {
        speedy -= 9.8f * Time.deltaTime;
        Vector3 var = new Vector3(0, speedy, speedx) * Time.deltaTime;
        this.transform.Translate(var);
    }
}

方法3:利用MoveTowards方法

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

public class move : MonoBehaviour
{
    private float speedx;
    private float speedy;
    // Start is called before the first frame update
    void Start()
    {
        speedy = 10;
        speedx = 10;
    }

    // Update is called once per frame
    void Update()
    {
        speedy -= 9.8f * Time.deltaTime;
        Vector3 var = new Vector3(0, speedy, speedx) * Time.deltaTime;
        this.transform.position = Vector3.MoveTowards(this.transform.position, var + this.transform.position, 100);
    }
}

写一个程序,实现一个完整的太阳系, 其他星球围绕太阳的转速必须不一样,且不在一个法平面上。

答:
在这里插入图片描述
项目结构:
在这里插入图片描述
实现代码

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

public class Script : MonoBehaviour
{
    public Transform Sun;
    public Transform Earth;
    public Transform Moon;
    public Transform Mercury;
    public Transform Venus;
    public Transform Mars;
    public Transform Jupiter;
    public Transform Saturn;
    public Transform Uranus;
    public Transform Neptune;
    // Start is called before the first frame update
    void Start()
    {
        Sun.position = Vector3.zero;
        Earth.position = new Vector3(15, 0, 0);
        Moon.position = new Vector3(18, 0, 0);
        Mercury.position = new Vector3(6, 0, 0);
        Venus.position = new Vector3(10, 0, 0);
        Mars.position = new Vector3(22, 0, 0);
        Jupiter.position = new Vector3(28, 0, 0);
        Saturn.position = new Vector3(34, 0, 0);
        Uranus.position = new Vector3(38, 0, 0);
        Neptune.position = new Vector3(46, 0, 0);
    }

    // Update is called once per frame
    void Update()
    {
        Earth.RotateAround(Sun.position, Vector3.up, 10 * Time.deltaTime);
        Earth.Rotate(Vector3.up, 30 * Time.deltaTime);
        Mercury.RotateAround(Sun.position,new Vector3(1, 1, 0), 30 * Time.deltaTime);
        Mercury.Rotate(new Vector3(1, 1, 0), 600 * Time.deltaTime);
        Venus.RotateAround(Sun.position, new Vector3(1, 1, 0.5f), 20 * Time.deltaTime);
        Venus.Rotate(new Vector3(1, 1, 0.5f), 400 * Time.deltaTime);
        Mars.RotateAround(Sun.position, new Vector3(1, 0, 0.5f), 8 * Time.deltaTime);
        Mars.Rotate(new Vector3(1, 0, 0.5f), 330 * Time.deltaTime);
        Jupiter.RotateAround(Sun.position, new Vector3(1, 0, 1), 5 * Time.deltaTime);
        Jupiter.Rotate(new Vector3(1, 0, 1), 280 * Time.deltaTime);
        Saturn.RotateAround(Sun.position, new Vector3(1, 2, 1), 4.5f * Time.deltaTime);
        Saturn.Rotate(new Vector3(1, 2, 1), 250 * Time.deltaTime);
        Uranus.RotateAround(Sun.position, new Vector3(1, 3, 1), 3.5f * Time.deltaTime);
        Uranus.Rotate(new Vector3(1, 3, 1), 200 * Time.deltaTime);
        Neptune.RotateAround(Sun.position, new Vector3(1, 7, 1), 2.5f * Time.deltaTime);
        Neptune.Rotate(new Vector3(1, 7, 1), 100 * Time.deltaTime);
        Moon.RotateAround(Earth.position, Vector3.up, 359 * 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
  • 列出游戏中提及的事物(Objects)
  • 用表格列出玩家动作表(规则表),注意,动作越少越好
  • 请将游戏中对象做成预制
  • 在 GenGameObjects 中创建 长方形、正方形、球 及其色彩代表游戏中的对象。
  • 使用 C# 集合类型 有效组织对象
  • 整个游戏仅 主摄像机 和 一个 Empty 对象, 其他对象必须代码动态生成!!! 。 整个游戏不许出现 Find 游戏对象,
    SendMessage 这类突破程序结构的 通讯耦合 语句。 违背本条准则,不给分
  • 请使用课件架构图编程,不接受非 MVC 结构程序
  • 注意细节,例如:船未靠岸,牧师与魔鬼上下船运动中,均不能接受用户事件!

游戏效果图:
在这里插入图片描述
在这里插入图片描述
项目结构图:
在这里插入图片描述
github地址
游戏对象:船(一个实体和一个Stack类),三个恶魔,三个牧师,两岸(均为Stack)
规则表:

状态 可执行的动作
船停靠在右边 右边的人上船,下船,出发(要求船非空)
船向左开 无,但是在船靠岸后会进行输赢判断
船停靠在左边 左边的人上船,下船,出发(要求船非空)
船向右开 无,但是在船靠岸后会进行输赢判断
重开游戏
重开游戏

在本游戏中,我使用build函数来实现题目要求中的GenGameObjects!!!

MVC结构分为三部分:
Model:

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

using PriestsAndDevils;

public class Model : MonoBehaviour
{
    Stack<GameObject> RightPriests = new Stack<GameObject>();
    Stack<GameObject> RightDevils = new Stack<GameObject>();
    Stack<GameObject> LeftPriests = new Stack<GameObject>();
    Stack<GameObject> LeftDevils = new Stack<GameObject>();
    Stack<GameObject> Boat = new Stack<GameObject>();
    SSDirector ssd;
    GameObject Boat_obj;
    // Start is called before the first frame update
    void Start()
    {
        ssd = SSDirector.GetInstance();
        ssd.SetModel(this);
        Build();
    }

    // Update is called once per frame
    void Update()
    {
        Correct(RightPriests, new Vector3(4.6f, 0.8f, 10));
        Correct(RightDevils, new Vector3(6.2f, 0.8f, 10));
        Correct(LeftPriests, new Vector3(-5.6f, 0.8f, 10));
        Correct(LeftDevils, new Vector3(-7.6f, 0.8f, 10));
        if (ssd.state != State.RightToLeft && ssd.state != State.LeftToRight)
        {
            if (ssd.state == State.StopAtRight)
            {
                Correct(Boat, new Vector3(3, 0.8f, 10));
            }
            if (ssd.state == State.StopAtLeft)
            {
                Correct(Boat, new Vector3(-3, 0.8f, 10));
            }
        }
        if (ssd.state == State.RightToLeft)
        {
            Boat_obj.transform.position = Vector3.MoveTowards(Boat_obj.transform.position, new Vector3(-3, 0, 10), 10f * Time.deltaTime);
            for(int i = 0; i < Boat.Count; i++)
            {
                Boat.ToArray()[i].transform.position= Vector3.MoveTowards(Boat.ToArray()[i].transform.position, new Vector3(-3 + i*0.6f, 0.8f, 10), 10f * Time.deltaTime);
            }
            if(Boat_obj.transform.position==new Vector3(-3, 0, 10))
            {
                ssd.state = State.StopAtLeft;
                while (Boat.Count != 0)
                {
                    GameObject t = Boat.Pop();
                    Debug.Log(t.name);
                    if (t.name == "Priest(Clone)")
                    {
                        LeftPriests.Push(t);
                    }
                    else if (t.name == "Devil(Clone)")
                    {
                        LeftDevils.Push(t);
                    }
                }
                Check();
            }
        }
        else if (ssd.state == State.LeftToRight)
        {
            Boat_obj.transform.position = Vector3.MoveTowards(Boat_obj.transform.position, new Vector3(3, 0, 10), 10f * Time.deltaTime);
            for (int i = 0; i < Boat.Count; i++)
            {
                Boat.ToArray()[i].transform.position = Vector3.MoveTowards(Boat.ToArray()[i].transform.position, new Vector3(3 + i * 0.6f, 0.8f, 10), 10f * Time.deltaTime);
            }
            if (Boat_obj.transform.position == new Vector3(3, 0, 10))
            {
                ssd.state = State.StopAtRight;
                while (Boat.Count != 0)
                {
                    GameObject t = Boat.Pop();
                    Debug.Log(t.name);
                    if (t.name == "Priest(Clone)")
                    {
                        RightPriests.Push(t);
                    }
                    else if (t.name == "Devil(Clone)")
                    {
                        RightDevils.Push(t);
                    }
                }
                Check();
            }
        }
    }
    void Build()
    {
        Instantiate(Resources.Load("Prefabs/Bank"), new Vector3(6, 0, 10), Quaternion.identity);
        Instantiate(Resources.Load("Prefabs/Bank"), new Vector3(-6, 0, 10), Quaternion.identity);

        Boat_obj = Instantiate(Resources.Load("Prefabs/Boat"), new Vector3(3, 0, 10), Quaternion.identity) as GameObject;

        for (int i = 0; i < 3; i++)
        {
            RightPriests.Push(Instantiate(Resources.Load("Prefabs/Priest")) as GameObject);
            RightDevils.Push(Instantiate(Resources.Load("Prefabs/Devil")) as GameObject);
        }
    }
    void Correct(Stack<GameObject> gos,Vector3 pos)  //这里参照了网上的定位方法
    {
        for(int i = gos.Count - 1; i >= 0; i--)
        {
            gos.ToArray()[i].transform.position = pos + new Vector3(0.6f * (gos.Count - 1 - i), 0, 0);
        }
    }
    public void PriestOn()
    {
        if (ssd.state == State.StopAtRight)
        {
            if (RightPriests.Count != 0 && Boat.Count < 2)
            {
                Boat.Push(RightPriests.Pop());
            }
        }
        else if(ssd.state == State.StopAtLeft)
        {
            if (LeftPriests.Count != 0 && Boat.Count < 2)
            {
                Boat.Push(LeftPriests.Pop());
            }
        }
    }
    public void DevilOn()
    {
        if (ssd.state == State.StopAtRight)
        {
            if (RightDevils.Count != 0 && Boat.Count < 2)
            {
                Boat.Push(RightDevils.Pop());
            }
        }
        else if (ssd.state == State.StopAtLeft)
        {
            if (LeftDevils.Count != 0 && Boat.Count < 2)
            {
                Boat.Push(LeftDevils.Pop());
            }
        }
    }
    public void Move()
    {
        if (ssd.state == State.StopAtRight && Boat.Count != 0)
        {
            ssd.state = State.RightToLeft;
        }
        if (ssd.state == State.StopAtLeft && Boat.Count != 0)
        {
            ssd.state = State.LeftToRight;
        }
    }
    public void Check()
    {
        if ((LeftPriests.Count!=0&&LeftDevils.Count > LeftPriests.Count) || (RightDevils.Count > RightPriests.Count&&RightPriests.Count!=0))
        {
            ssd.state = State.Lose;
        }
        else if (LeftPriests.Count == 3 && LeftDevils.Count == 3)
        {
            ssd.state = State.Win;
        }
    }
    public void Restart()
    {
        ssd.state = State.StopAtRight;
        while (Boat.Count != 0)
        {
            GameObject t = Boat.Pop();
            Debug.Log(t.name);
            if (t.name == "Priest(Clone)")
            {
                RightPriests.Push(t);
            }
            else if (t.name == "Devil(Clone)")
            {
                RightDevils.Push(t);
            }
        }
        while (LeftPriests.Count != 0)
        {
            RightPriests.Push(LeftPriests.Pop());
        }
        while (LeftDevils.Count != 0)
        {
            RightDevils.Push(LeftDevils.Pop());
        }
        Boat_obj.transform.position = new Vector3(3, 0, 10);
    }
    public void Offleft()
    {
        if (Boat.Count >= 1)
        {
            if (Boat.Count == 2)
            {
                GameObject t1 = Boat.Pop();
                GameObject t2 = Boat.Pop();
                if (t2.name == "Devil(Clone)")
                {
                    if (ssd.state == State.StopAtRight)
                    {
                        RightDevils.Push(t2);
                    }
                    else
                    {
                        LeftDevils.Push(t2);
                    }
                }
                else if (t2.name == "Priest(Clone)")
                {
                    if (ssd.state == State.StopAtRight)
                    {
                        RightPriests.Push(t2);
                    }
                    else
                    {
                        LeftPriests.Push(t2);
                    }
                }
                Boat.Push(t1);
            }
            else
            {
                GameObject t = Boat.Pop();
                if (t.name == "Devil(Clone)")
                {
                    if (ssd.state == State.StopAtRight)
                    {
                        RightDevils.Push(t);
                    }
                    else
                    {
                        LeftDevils.Push(t);
                    }
                }
                else if (t.name == "Priest(Clone)")
                {
                    if (ssd.state == State.StopAtRight)
                    {
                        RightPriests.Push(t);
                    }
                    else
                    {
                        LeftPriests.Push(t);
                    }
                }
            }
        }
    }
    public void Offright()
    {
        if (Boat.Count == 2)
        {
            GameObject t = Boat.Pop();
            if (t.name == "Devil(Clone)")
            {
                if (ssd.state == State.StopAtRight)
                {
                    RightDevils.Push(t);
                }
                else
                {
                    LeftDevils.Push(t);
                }
            }
            else if (t.name == "Priest(Clone)")
            {
                if (ssd.state == State.StopAtRight)
                {
                    RightPriests.Push(t);
                }
                else
                {
                    LeftPriests.Push(t);
                }
            }
        }
    }
}

View:

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

using PriestsAndDevils;

public class View : MonoBehaviour
{
    // Start is called before the first frame update
    SSDirector ssd;
    void Start()
    {
        ssd = SSDirector.GetInstance();
    }
    public void OnGUI()
    {
        if (ssd.state == State.Win)
        {
            if (GUI.Button(new Rect(250, 400, 500, 50), "You Win! (click to restart)"))
            {
                ssd.Restart();
            }
        }
        else if(ssd.state == State.Lose)
        {
            if(GUI.Button(new Rect(250, 400, 500, 50), "You Lose! (click to restart)"))
            {
                ssd.Restart();
            }
        }
        else if(ssd.state == State.StopAtRight)
        {
            if(GUI.Button(new Rect(700, 200, 50, 50), "Priest"))
            {
                ssd.PriestOn();
            }
            if(GUI.Button(new Rect(800, 200, 50, 50), "Devil"))
            {
                ssd.DevilOn();
            }
            if(GUI.Button(new Rect(400, 400, 50, 50), "Move")){
                ssd.Move();
            }
            if(GUI.Button(new Rect(300, 100, 150, 50), "OFF_Left"))
            {
                ssd.Offleft();
            }
            if (GUI.Button(new Rect(500, 100, 150, 50), "OFF_Right"))
            {
                ssd.Offright();
            }
        }
        else if(ssd.state == State.StopAtLeft)
        {
            if (GUI.Button(new Rect(200, 200, 50, 50), "Priest"))
            {
                ssd.PriestOn();
            }
            if (GUI.Button(new Rect(100, 200, 50, 50), "Devil"))
            {
                ssd.DevilOn();
            }
            if (GUI.Button(new Rect(400, 400, 50, 50), "Move"))
            {
                ssd.Move();
            }
            if (GUI.Button(new Rect(300, 100, 150, 50), "OFF_Left"))
            {
                ssd.Offleft();
            }
            if (GUI.Button(new Rect(500, 100, 150, 50), "OFF_Right"))
            {
                ssd.Offright();
            }
        }
        else if(ssd.state == State.RightToLeft)
        {

        }
        else if(ssd.state== State.LeftToRight)
        {

        }
    }

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

Controller:

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

namespace PriestsAndDevils
{
    public enum State { LeftToRight,RightToLeft,StopAtLeft,StopAtRight,Win,Lose};

    public class SSDirector : System.Object
    {
        private static SSDirector _instance;
        public State state;
        private Model model;

        public static SSDirector GetInstance()
        {
            if (_instance == null)
            {
                _instance = new SSDirector();
                _instance.state = State.StopAtRight;
            }
            return _instance;
        }
        public Model GetModel()
        {
            return model;
        }
        public void SetModel(Model m)
        {
            model = m;
        }
        public void PriestOn()
        {
            model.PriestOn();
        }
        public void Move()
        {
            model.Move();
        }
        public void Restart()
        {
            model.Restart();
        }
        public void DevilOn()
        {
            model.DevilOn();
        }
        public void Offleft()
        {
            model.Offleft();
        }
        public void Offright()
        {
            model.Offright();
        }
    }
}

public class Controller : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        SSDirector.GetInstance();
    }

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

3、思考题【选做】

使用向量与变换,实现并扩展 Tranform 提供的方法,如 Rotate、RotateAround 等

自旋转

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

public class RotateBeh : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        Quaternion q = Quaternion.AngleAxis(100 * Time.deltaTime, Vector3.up);
        transform.localRotation *= q;
    }
}

使物体定向旋转至某方向(参考了网上博客)

Quaternion T = Quaternion.LookRotation(new Vector3(55,5,5) - transform.position, Vector3.up);
transform.rotation = Quaternion.Slerp(transform.rotation, T, Time.deltaTime * 5f);

猜你喜欢

转载自blog.csdn.net/SYSU_yzt/article/details/100922778