第三次作业 空间与运动

1. 简答并用程序验证(建议做)

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

游戏对象运动的本质是游戏对象Position、Rotate、Scale属性数值的变化。

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

  • 修改transform.position属性:

    public float xSpeed = 2.0f;
    public float ySpeed = 0;
    public int t = 0;
    
    void Update()
    {
       t += Time.deltaTime;
       float distance_x,distance_y;
       distance_x = xSpeed * t;
       distance_y = 0.5 * 10 * t * t;
       transform.position += Vector3.right * distance_x;
       transform.position += Vector3.down * distance_y;
    }
    
  • 使用transform.Translate:

    public float xSpeed = 2.0f;
    public float ySpeed = 0;
    public int t = 0;
    void Update()
    {
       t += Time.deltaTime;
       transform.Translate(Vector3.right * xSpeed * t + Vector3.down * 0.5 * 10 * t * t);
    }
    
  • 使用向量Vector3:

    public float xSpeed = 2.0f; // x方向上的初始速度
    public float ySpeed = 0; // y方向上的初始速度
    public int t = 0; // 时间
    
    void Update()
    {
    	t += (float)Time.deltaTime;
    	float distance_x,distance_y;
    	distance_x = xSpeed * t;
    	distance_y = 0.5 * 10 * t * t;
        this.transform.position += new Vector3(distance_x, distance_y, 0);
    }
    

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

  • 首先,创建9个Spheer,分别命名为sun和其他八大行星,将八大行星设置为sun的子对象;

  • 设置太阳和八大行星的位置、大小;

  • 为各个对象添加材质包;

  • 编写脚本,代码如下:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class SolarSystem : MonoBehaviour
    {
    	public float revolutionSpeed; // 公转速度
    	public float rotationSeed; // 自转速度
    	public Vector3 axis; //角度
     	// Start is called before the first frame update
    	void Start()
    	{
        
    	}
    
     	// Update is called once per frame
    	void Update()
    	{
        	transform.RotateAround(transform.parent.position, axis, revolutionSpeed * Time.deltaTime); // 公转函数
        	transform.Rotate(Vector3.up * rotationSeed * Time.deltaTime); // 自转函数
    	}
    }
    
  • 将脚本挂载到八个行星下,分别设置每个行星的revolutionSpeed, rotationSpeed, axis;

  • 添加每个行星的运行轨迹:双击对象-Effects-Trail,设置每个轨迹的时间和颜色;

  • 运行游戏,效果如下:
    在这里插入图片描述

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)

    3个牧师
    3个魔鬼
    1艘小船
    2个河岸
    
  • 用表格列出玩家动作表(规则表),注意,动作越少越好
    | 玩家动作 | 动作条件|
    |–|--|
    | 船从一侧开往另一侧| 船上有牧师或者魔鬼 |
    | 从右边上岸| 船的右边有人|
    | 从左边上岸| 船的左边有人|
    | 游戏结束| 任何一侧的恶魔多于天使|

  • 请将游戏中对象做成预制

  • 在场景控制器 LoadResources 方法中加载并初始化 长方形、正方形、球 及其色彩代表游戏中的对象。

  • 使用 C# 集合类型 有效组织对象

  • 整个游戏仅 主摄像机 和 一个 Empty 对象, 其他对象必须代码动态生成!!! 整个游戏不许出现 Find 游戏对象, SendMessage 这类突破程序结构的 通讯耦合 语句。 违背本条准则,不给分

  • 请使用课件架构图编程,不接受非 MVC 结构程序

MVC是界面人机交互程序设计的一种架构模式。它把程序分为三个部分:
模型(Model):数据对象及关系 游戏对象、空间关系
控制器(Controller):接受用户事件,控制模型的变化 一个场景一个主控制器 至少实现与玩家交互的接口(IPlayerAction)
实现或管理运动 界面(View):显示模型,将人机交互事件交给控制器处理 处收 Input 事件 渲染 GUI ,接收事件

  • 注意细节,例如:船未靠岸,牧师与魔鬼上下船运动中,均不能接受用户事件!

项目结构

  • 游戏对象树:
    在这里插入图片描述
    仅主摄像机和一个Empty对象,其他对象动态生成。
  • 资源树:
    在这里插入图片描述
    由Materials、Resources、Scripts、Scenes四部分组成。
    Materials部分是自己创建的材料,Resources部分是预制,Scripts是脚本。
  • 将对象做成预制:
    在这里插入图片描述
    四种预制分别表示河岸、船、恶魔和牧师。
  • MVC结构
    Scripts下分别创建Model、Controller和View脚本:
    在这里插入图片描述

Model.cs:

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;
                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;
                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);
                Check();
            }
            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());
            Check();
        }
        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);
                    }
                }
                Check();
                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);
                    }
                }
                Check();
            }
        }
    }
    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);
                }
            }
            Check();
        }
    }
}

Controller.cs:

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()
    {

    }
}

View.cs

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)
        {
            GUI.TextField(new Rect(500, 50, 100, 25), "You Win!");
            if(GUI.Button(new Rect(700, 25, 50, 50), "restart"))
            {
                ssd.Restart();
            }
        }
        else if (ssd.state == State.Lose)
        {
            GUI.TextField(new Rect(500, 50, 100, 25), "You Lose!");
            if (GUI.Button(new Rect(700, 25, 50, 50), "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(500, 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()
    {

    }
}
  • 效果:
    在这里插入图片描述

  • 各个按钮功能
    游戏开始时,右边河岸有两个按钮:PriestDevil,点击Priest时牧师上船,点击Devil时魔鬼上船;另外两个按钮:OFF_LeftOFF_Right,点击OFF_Left时站在船左边的人下船(上岸),点击OFF_Right时站在船右边的人下船(上岸)。点击Move时小船移动。

  • 游戏视频

    PriestAndDevil

  • 项目源代码
    不知为何上传不了gitee和github,于是我把它放到了百度网盘中:
    链接:https://pan.baidu.com/s/1N7-juwjw3kak8XJ5-ISqag
    提取码:x50j

3. 思考题

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

  • Rotate:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MyRotate : 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;
   }
}
  • RotateAround
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class myRotateAround : MonoBehaviour
{
    public Transform t; // 
    
	 // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        Quaternion rotation = Quaternion.AngleAxis(100 * Time.deltaTime, Vector3.up);
        Vector3 direction = transform.position - t.position;
        direction = rotation * direction;
        transform.position = t.position + direction;
        transform.rotation *= rotation;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值