空间与运动 -- MVC架构学习

码途紫霄狩
• 阅读 1206

空间与运动 -- MVC架构学习

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

  • 游戏对象运动的本质是什么?
  • 请用三种方法以上方法,实现物体的抛物线运动。(如,修改Transform属性,使用向量Vector3的方法…)
  • 写一个程序,实现一个完整的太阳系, 其他星球围绕太阳的转速必须不一样,且不在一个法平面上。

解答:

  • 运动的本质就是改变 transform 三个属性的(Position, Rotation, Scale)值,使物体进行运动

抛物线运动实现的三种方法:

  • 改变物体 position 的方法,由于要实现抛物线运动,我们知道在unity的界面中,垂直方向的改变也就是对y轴进行改变,根据抛物线的公式 $y=v_0t+\frac{1}{2}at^2$,抛物线是向下运动就可以在公式中添加负号,即可实现物体的想下运动

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    public class NewBehaviourScript : MonoBehaviour
      {
          public float vx = 0.5f; // x轴方向上的速度
          public float v0 = 0; // y轴方向上的初始速度
          const float a = 9.8f; //加速度a
          // Start is called before the first frame update
          void Start()
          {
          }
      
      // Update is called once per frame
      void Update()
      {
          this.transform.position += new Vector3(vx * Time.deltaTime, (float)(-v0 * Time.deltaTime - 0.5 * a * (Time.deltaTime) * (Time.deltaTime)), 0);
          v0 += a * Time.deltaTime; // 每一帧改变的时候v0的速度是不一样的
      }
    }
    • 使用transform的translate函数,此函数使得物体的position移动相应的位置,具体的公式还是抛物线的公式:$y=v_0t+\frac{1}{2}at^2$
      using System.Collections;
      using System.Collections.Generic;
      using UnityEngine;
      
      public class NewBehaviourScript : MonoBehaviour
      {
          public float vx = 0.5f; // x轴方向上的速度
          public float v0 = 0; // y轴方向上的初始速度
          const float a = 9.8f; //加速度a
          // Start is called before the first frame update
          void Start()
          {
      
          }
      
          // Update is called once per frame
          void Update()
          {
              this.transform.Translate(new Vector3(vx * Time.deltaTime, (float)(-v0 * Time.deltaTime - 0.5 * a * (Time.deltaTime) * (Time.deltaTime)), 0));
              v0 += a * Time.deltaTime;
          }
      }
    • transform中可以调用一个函数 SetPositionAndRotation,此函数可以设置物体的position和rotation,rotation可以不需要设置,position设置的数值和上面基本一致,但是此时需要额外设置时间的变化,以及,初始物体的高度也就是y值
      using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class NewBehaviourScript : MonoBehaviour
    {
        public float vx = 0.5f; // x轴方向上的速度
        const float a = 9.8f; //加速度a
        public float t = 0;
        public float y = 10.0f; // 初始y的位置
        // Start is called before the first frame update
        void Start()
        {
            
        }
    
        // Update is called once per frame
        void Update()
        {
            this.transform.SetPositionAndRotation(new Vector3(vx * t, y - (float)(0.5 * a * t * t), 10), this.transform.rotation);
            t += Time.deltaTime;
        }
    }
    • 做太阳系,首先从老师给的网址太阳系贴图,将所有行星的图片都拖到桌面,然后再拖入unity中,即可在assets中使用,然后将这些图片拖到大小不一的球星就够,就可以形成各大行星

      空间与运动 -- MVC架构学习

      根据老师上课给的代码,进行一定的修改,即可完成整个太阳系的行星围绕太阳转动的景象

      空间与运动 -- MVC架构学习

      也可以看到几大行星并不是在一个法平面上,所以完成了要求

      using System.Collections;
      using System.Collections.Generic;
      using UnityEngine;
      
      public class Rotate : MonoBehaviour
      {
          public int v; // 旋转的速度
          public Vector3 e;
          float x, y; // 法向量
          // Start is called before the first frame update
          void Start()
          {
              // 由于在不同的法平面所以生成的法向量应该是随机的
              x = Random.Range(1, 10); y = Random.Range(10, 20);
              e = new Vector3(x, y, 0);
          }
          // Update is called once per frame
          void Update()
          {
              Quaternion q = Quaternion.AngleAxis(v * Time.deltaTime, e);
              this.transform.localPosition = q * this.transform.localPosition;
          }
      }
      

      上面代码的法向量是随机生成的故不在同一个法平面,速度可以自己设置故可以行星绕着太阳旋转的速度不一样

      空间与运动 -- MVC架构学习

    编程实践

    • 阅读以下游戏脚本
    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/gam... )
    • 列出游戏中提及的事物(Objects)
    • 用表格列出玩家动作表(规则表),注意,动作越少越好
    • 请将游戏中对象做成预制
    • 在场景控制器 LoadResources 方法中加载并初始化长方形、正方形、球及其色彩代表游戏中的对象。
    • 使用 C# 集合类型有效组织对象
    • 整个游戏仅主摄像机和一个 Empty 对象, 其他对象必须代码动态生成!!。 整个游戏不许出现 Find 游戏对象, SendMessage 这类突破程序结构的 通讯耦合 语句。 违背本条准则,不给分
    • 请使用课件架构图编程,不接受非 MVC 结构程序
    • 注意细节,例如:船未靠岸,牧师与魔鬼上下船运动中,均不能接受用户事件!

    游戏规则

    牧师与魔鬼:这是一款经典的游戏,游戏很简单,玩家需要操控船只、牧师和魔鬼,然后使得一个岸边的三个牧师和三个魔鬼都移动到另一个岸边。并且需要在游戏限定的时间60秒内进行操作。

    注意

    • 要想使船移动,船上必须有牧师或魔鬼
    • 船上最多有两个人物
    • 不管哪个岸上,只要魔鬼数大于牧师数游戏失败(数量包括船只停靠时船上的人物数量)

    游戏资源

    github地址

    游戏截图和视频

    由于游戏过程比较长,所以只截取部分过程图,具体游戏的过程在视频链接中

    视频链接
    空间与运动 -- MVC架构学习
    空间与运动 -- MVC架构学习
    空间与运动 -- MVC架构学习

    游戏Assets结构

    设计的结构完全是按照标准的结构构建的,如下图所示

    空间与运动 -- MVC架构学习

    • Materials:存放游戏中的Material
    • Resources:存放游戏中的对象的预制
    • Scenes:存放游戏的场景
    • Scripts:存放游戏的c#代码
    • Texture:存放关于物体的一些图片

    游戏中的事物

    空间与运动 -- MVC架构学习

    空间与运动 -- MVC架构学习

    • Water:水,也就是牧师与魔鬼需要渡过的,由长方体构成,并挂上网上找的图片
    • Devil:魔鬼,由黑色的长方体构成
    • Priest:牧师,由绿色的长方体构成
    • ground:两个岸边,由长方体构成,并挂上图片
    • Boat:木船,由长方体构成,并挂上图片

    MVC结构编程

    在进行编程前,需要了解MVC的大体结构

    空间与运动 -- MVC架构学习

    MVC是界面人机交互程序设计的一种架构模式。它把程序分为三个部分:

    • 模型(Model):数据对象及关系

      • 游戏对象、空间关系
    • 控制器(Controller):接受用户事件,控制模型的变化

      • 一个场景一个主控制器
      • 至少实现与玩家交互的接口(IPlayerAction)
      • 实现或管理运动
    • 界面(View):显示模型,将人机交互事件交给控制器处理

      • 处收 Input 事件
      • 渲染 GUI ,接收事件

    本实验也是严格按照了MVC结构进行编写代码,代码的大致构成,如下图所示:

    空间与运动 -- MVC架构学习

    类中的变量由于权限不同设置成了不同的类型,public,private,readonly(只读变量不可以对它的值进行修改)

    接下来分别介绍各个代码实现的具体功能:

    • Director:属于最高层的控制器,保持运行时始终有一个实例,这样方便了类与类之间的通信。

      主要的职责有:

      • 获取当前游戏的场景
      • 控制场景运行、切换、入栈与出栈
      • 暂停、恢复、退出
      • 管理游戏全局状态
      • 设定游戏的配置
      • 设定游戏全局视图

    可以通过一个抽象的场景接口访问不同场景的控制器

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    // 负责实例化
    public class Director : System.Object {
        private static Director _instance;
        public SceneController currentSceneController { get; set; }
    
        public static Director getInstance() {
            if (_instance == null)  _instance = new Director ();
            return _instance;
        }
    }
    • SceneController:场景控制器:

      职责:

      • 管理本次场景所有的游戏对象
      • 协调游戏对象(预制件级别)之间的通讯
      • 响应外部输入事件
      • 管理本场次的规则(裁判)
      • 各种杂务

    可以看到它是由interface实现的,说明不能直接来创建对象,我们通过之前的学习可以知道,interface的使用需要一个类去继承它,这样才可以使用。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    // 场景管理:加载所有的资源
    public interface SceneController {
        void LoadResources ();
    }
    • RoleController:由类的名字就可以知道,这是对游戏人物的控制器,可以控制人物的移动,并且可以得到人物的相关信息,比如该游戏人物是牧师还是魔鬼,在船上还是在岸上,该游戏人物的名字是什么等等。不光需要以上的功能,由于人物是可以操控的所以我们还需要监控鼠标对人物的点击功能。
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    // 对游戏人物的控制器
    public class RoleController {
        readonly GameObject obj;
        readonly Moving mov;
        readonly ClickGUI clickGUI;
        readonly int PorD; // 判断是牧师(0)还是魔鬼(1)
    
        bool _isOnBoat;
        GroundController gController;
    
        public RoleController(string r) {
            
            if (r == "priest") {
                obj = Object.Instantiate (Resources.Load ("Perfabs/Priest", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
                PorD = 0;
            } else {
                obj = Object.Instantiate (Resources.Load ("Perfabs/Devil", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
                PorD = 1;
            }
            mov = obj.AddComponent (typeof(Moving)) as Moving;
    
            clickGUI = obj.AddComponent (typeof(ClickGUI)) as ClickGUI;
            clickGUI.setController (this);
        }
    
        public void setName(string n) {
            obj.name = n;
        }
    
        public void setPosition(Vector3 p) {
            obj.transform.position = p;
        }
    
        public void Movingto(Vector3 dest) {
            mov.setDestination(dest);
        }
    
        public int getRole() {
            return PorD;
        }
    
        public string getName() {
            return obj.name;
        }
    
        public void getOnBoat(BoatController b) {
            gController = null;
            obj.transform.parent = b.getGameobj().transform;
            _isOnBoat = true;
        }
    
        public void getGround(GroundController coastCtrl) {
            gController = coastCtrl;
            obj.transform.parent = null;
            _isOnBoat = false;
        }
    
        public bool isOnBoat() {
            return _isOnBoat;
        }
    
        public GroundController getGroundController() {
            return gController;
        }
    
        public void reset() {
            mov.reset ();
            gController = (Director.getInstance ().currentSceneController as FirstController).g1;
            getGround (gController);
            setPosition (gController.getEmptyPosition ());
            gController.getGround (this);
        }
    }
    • GroundController:对地面的控制器,两个只读变量sPosition、ePosition记录了两个地面的位置, 然后pos数组记录了陆地上的可存放人物的位置;上陆地和下陆地的动作对变量的修改;有上陆地之前,需要判断陆地上的空位置的索引,然后才可以修改变量的内容
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    // 陆地的控制器
    public class GroundController {
        readonly GameObject ground;
        readonly Vector3 sPosition = new Vector3(9, 1, 0);
        readonly Vector3 ePosition = new Vector3(-9, 1, 0);
        readonly Vector3[] pos;
        readonly int st_pos;
    
        RoleController[] roles;
    
        public GroundController(string ss) {
            pos = new Vector3[] {new Vector3(6.5F,2.25F,0), new Vector3(7.5F,2.25F,0), new Vector3(8.5F,2.25F,0), 
                new Vector3(9.5F,2.25F,0), new Vector3(10.5F,2.25F,0), new Vector3(11.5F,2.25F,0)};
    
            roles = new RoleController[6];
    
            if (ss == "from") {
                ground = Object.Instantiate (Resources.Load ("Perfabs/Ground", typeof(GameObject)), sPosition, Quaternion.identity, null) as GameObject;
                ground.name = "from";
                st_pos = 1;
            } else {
                ground = Object.Instantiate (Resources.Load ("Perfabs/Ground", typeof(GameObject)), ePosition, Quaternion.identity, null) as GameObject;
                ground.name = "to";
                st_pos = -1;
            }
        }
    
        public int getEmptyIndex() {
            for (int i = 0; i < roles.Length; i++) {
                if (roles [i] == null) return i;
            }
            return -1;
        }
    
        public Vector3 getEmptyPosition() {
            Vector3 p = pos [getEmptyIndex ()];
            p.x *= st_pos;
            return p;
        }
    
        public void getGround(RoleController r) {
            int ii = getEmptyIndex ();
            roles [ii] = r;
        }
    
        public RoleController getOffGround(string pname) {    // 0->priest, 1->devil
            for (int i = 0; i < roles.Length; i++) {
                if (roles [i] != null && roles [i].getName () == pname) {
                    RoleController r = roles [i];
                    roles [i] = null;
                    return r;
                }
            }
            return null;
        }
    
        public int get_st_pos() {
            return st_pos;
        }
    
        public int[] getRoleNum() {
            int[] cnt = {0, 0};
            for (int i = 0; i < roles.Length; i++) {
                if (roles [i] == null) continue;
    
                if (roles [i].getRole () == 0) cnt[0]++;
                else cnt[1]++;
            }
            return cnt;
        }
    
        public void reset() {
            roles = new RoleController[6];
        }
    }
    • BoatController:对船的控制器,有两个只读变量sPosition,ePosition,来表示船的起始和终止位置,并且通过变量st_pos来判断是起点还是终点,还需要对船进行控制运动,所以需要监听鼠标的点击功能。还需要判断该船是否为空,还有空的位置所对应的索引,还需要得到上船下船等动作的通知;该类的实现和上面的陆地控制器的实现相似,所以设计起来也比较简单
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    // 船的控制器
    public class BoatController {
        readonly GameObject boat;
        readonly Moving mov;
        readonly Vector3 sPosition = new Vector3 (5, 1, 0); // 起始位置
        readonly Vector3[] sPositions;
        readonly Vector3 ePosition = new Vector3 (-5, 1, 0); // 到达位置
        readonly Vector3[] ePositions;
    
        int st_pos; // 起始点还是终点:-1:终点 1:起点
        RoleController[] member = new RoleController[2];
    
        public BoatController() {
            st_pos = 1;
    
            sPositions = new Vector3[] { new Vector3 (4.5F, 1.5F, 0), new Vector3 (5.5F, 1.5F, 0) };
            ePositions = new Vector3[] { new Vector3 (-5.5F, 1.5F, 0), new Vector3 (-4.5F, 1.5F, 0) };
    
            boat = Object.Instantiate (Resources.Load ("Perfabs/Boat", typeof(GameObject)), sPosition, Quaternion.identity, null) as GameObject;
            boat.name = "boat";
    
            mov = boat.AddComponent (typeof(Moving)) as Moving;
            boat.AddComponent (typeof(ClickGUI));
        }
    
    
        public void Move() {
            if (st_pos == -1) {
                mov.setDestination(sPosition);
                st_pos = 1;
            } else {
                mov.setDestination(ePosition);
                st_pos = -1;
            }
        }
    
        public int getEmptyIndex() {
            for (int i = 0; i < member.Length; i++) {
                if (member [i] == null) return i;
            }
            return -1;
        }
    
        public bool isEmpty() {
            for (int i = 0; i < member.Length; i++) {
                if (member [i] != null) return false;
            }
            return true;
        }
    
        public Vector3 getEmptyPosition() {
            Vector3 p;
            int ii = getEmptyIndex ();
            if (st_pos == -1) p = ePositions[ii];
            else p = sPositions[ii];
            
            return p;
        }
    
        public void GetOnBoat(RoleController r) {
            int ii = getEmptyIndex ();
            member [ii] = r;
        }
    
        public RoleController GetOffBoat(string member_name) {
            for (int i = 0; i < member.Length; i++) {
                if (member [i] != null && member [i].getName () == member_name) {
                    RoleController r = member [i];
                    member [i] = null;
                    return r;
                }
            }
            return null;
        }
    
        public GameObject getGameobj() {
            return boat;
        }
    
        public int get_st_pos() {
            return st_pos;
        }
    
        public int[] getRoleNum() {
            int[] cnt = {0, 0};
            for (int i = 0; i < member.Length; i++) {
                if (member [i] == null) continue;
    
                if (member [i].getRole () == 0) cnt[0]++;
                else cnt[1]++;
            }
            return cnt;
        }
    
        public void reset() {
            mov.reset ();
            if (st_pos == -1) Move ();
            
            member = new RoleController[2];
        }
    }
    • Moving:是一个关于对象运动的类,该类可以控制物体的移动速度speed,还可以设置物体的目的地,以达到物体可以移动。由于岸上的人物要想到达船上需要经过两个步骤,否则可能会穿模,也就是人物会穿过地表面,这样会不严谨,所以我设置了 cur 变量来判断当前物体运行的位置,然后再根据位置设置相应的目的地(mid, dest),来解决此类问题。
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    // 关于对象运动的实现
    public class Moving: MonoBehaviour {
            
        readonly float speed = 20;
    
        int cur; // 当前运行的位置
        Vector3 dest, mid; // 设置一个中间位置,使得运动不会穿模
    
        public void setDestination(Vector3 d) {
            dest = d; mid = d;
    
            if (d.y == transform.position.y) cur = 2;
            else if (d.y < transform.position.y) mid.y = transform.position.y;
            else mid.x = transform.position.x;
            
            cur = 1;
        }
    
        public void reset() {
            cur = 0;
        }
    
        void Update() {
            if (cur == 1) {
                transform.position = Vector3.MoveTowards (transform.position, mid, speed * Time.deltaTime);
                if (transform.position == mid) cur = 2;
            } else if (cur == 2) {
                transform.position = Vector3.MoveTowards (transform.position, dest, speed * Time.deltaTime);
                if (transform.position == dest) cur = 0;
            }
        }
    }
    • FirstController:高一层的控制器,控制着这个场景中的所有对象,包括其加载、通信、用户输入。

      继承了SceneControllerUserAction,说明该控制器实现了对两个接口的继承并实现;该控制器还实现了加载游戏资源和加载游戏人物,并且有控制人物运动和船只的运动;游戏的运行时间的控制;判断游戏是否结束,和游戏结束的条件,如果结束了进行重置游戏,重新设置各个变量

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    // 总控制器
    public class FirstController : MonoBehaviour, SceneController, UserAction {
    
        readonly Vector3 p_water = new Vector3(0,0.5F,0); // 水的位置
        
        UserGUI uGUI;
    
        public GroundController g1;
        public GroundController g2;
        public BoatController boat;
        private RoleController[] roles; 
        private float time; // 游戏运行的时间
    
        void Awake() {
            Director d = Director.getInstance ();
            d.currentSceneController = this;
            uGUI = gameObject.AddComponent <UserGUI>() as UserGUI;
            roles = new RoleController[6];
            LoadResources();
            time = 60;
        }
    
        // 游戏时间的运行
        void Update() {
            time -= Time.deltaTime;
            this.gameObject.GetComponent<UserGUI>().time = (int) time;
            uGUI.isWin = isfinished ();
        }
    
        private void loadRole() {
            for (int i = 0; i < 3; i++) {
                RoleController r = new RoleController ("priest");
                r.setName("priest" + i);
                r.setPosition (g1.getEmptyPosition ());
                r.getGround (g1); g1.getGround (r);
    
                roles [i] = r;
            }
    
            for (int i = 0; i < 3; i++) {
                RoleController r = new RoleController ("devil");
                r.setName("devil" + i);
                r.setPosition (g1.getEmptyPosition ());
                r.getGround (g1); g1.getGround (r);
    
                roles [i+3] = r;
            }
        }
    
        public void LoadResources() {
            GameObject water = Instantiate (Resources.Load ("Perfabs/Water", typeof(GameObject)), p_water, Quaternion.identity, null) as GameObject;
            water.name = "water";
    
            g1 = new GroundController ("from");
            g2 = new GroundController ("to");
            boat = new BoatController ();
    
            loadRole ();
        }
    
        public void MoveBoat() {
            if (boat.isEmpty ()) return;
            boat.Move ();
            uGUI.isWin = isfinished ();
        }
    
        public void MoveRole(RoleController r) {
            if (r.isOnBoat ()) {
                GroundController which_g;
                if (boat.get_st_pos () == -1) which_g = g2;
                else which_g = g1;
                
                boat.GetOffBoat (r.getName());
                r.Movingto (which_g.getEmptyPosition ());
                r.getGround (which_g);
                which_g.getGround (r);
            } else {                                    
                GroundController which_g = r.getGroundController ();
    
                if (boat.getEmptyIndex () == -1) return; // 船是空的        
                if (which_g.get_st_pos () != boat.get_st_pos ()) return;
    
                which_g.getOffGround(r.getName());
                r.Movingto (boat.getEmptyPosition());
                r.getOnBoat (boat);
                boat.GetOnBoat (r);
            }
            uGUI.isWin = isfinished ();
        }
    // 判断是否结束 0:没有结束 1:输 2:赢
        int isfinished() {    
            if (time < 0) return 1;
            int p1 = 0; int d1 = 0; // 起始点牧师与魔鬼数量
            int p2 = 0; int d2 = 0; // 终点牧师与魔鬼数量
    
            int[] cnt1 = g1.getRoleNum (); // 起始点的人数
            p1 += cnt1[0]; d1 += cnt1[1];
    
            int[] cnt2 = g2.getRoleNum (); // 终点的人数
            p2 += cnt2[0]; d2 += cnt2[1];
    
            if (p2 + d2 == 6) return 2;
    
            int[] cnt3 = boat.getRoleNum (); // 船上人的数量
            if (boat.get_st_pos () == -1) {    
                p2 += cnt3[0]; d2 += cnt3[1];
            } else {    
                p1 += cnt3[0]; d1 += cnt3[1];
            }
    
            if (p1 < d1 && p1 > 0) return 1;
            if (p2 < d2 && p2 > 0) return 1;
            return 0;            
        }
    
        public void restart() {
            time = 60;
            boat.reset ();
            g1.reset (); g2.reset ();
            for (int i = 0; i < roles.Length; i++) roles [i].reset ();
        }
    }
    
    • UserAction:接口,定义了玩家可以进行的操作MoveBoat() 移动船只、MoveRole 移动人物、restart重新开始游戏。该接口通过别的类来继承,这样就可以实现得到用户的输入然后作出反应
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    // 用户或玩家可以进行的操作
    public interface UserAction {
        void MoveBoat();
        void MoveRole(RoleController r);
        void restart();
    }
    • UserGUI:用户交互,来设置整个用户界面,判断游戏是否胜利,如果胜利则显示Win,并有一个按钮表示是否需要重新玩这个游戏;如果失败则会显示GameOver,然后也可以进行游戏重置。在此我还设置了字体的大小和按钮的大小,方便观看,也不失美感
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    public class UserGUI : MonoBehaviour {
        private UserAction u;
        public int isWin = 0;// 1:Gameover 2:Win
        public int time; // 游戏运行时间
        GUIStyle ssize, buttons, tsize;
    
        void Start() {
            u = Director.getInstance ().currentSceneController as UserAction;
    
            // 设置字体大小
            ssize = new GUIStyle(); tsize = new GUIStyle();
            ssize.fontSize = 45; tsize.fontSize = 20;
            ssize.alignment = TextAnchor.MiddleCenter;
    
            // 设置按钮大小
            buttons = new GUIStyle("button");
            buttons.fontSize = 30;
    
            // 设置游戏的时长
            time = 60;
        }
        // 判断是否胜利或失败,然后重置
        void OnGUI() {
            GUI.Label(new Rect(0, 0, 100, 50), "Time:  " + time, tsize);
            if (isWin == 1) {
                GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 80, 100, 50), "Gameover!", ssize);
                if (GUI.Button(new Rect(Screen.width / 2-65, Screen.height / 2, 140, 70), "Restart", buttons)) {
                    isWin = 0; u.restart ();
                }
            } else if(isWin == 2) {
                GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 80, 100, 50), " Win!", ssize);
                if (GUI.Button(new Rect(Screen.width / 2 - 65, Screen.height / 2, 140, 70), "Restart", buttons)) {
                    isWin = 0; u.restart ();
                }
            }
        }
    }
    
    • ClickGUI:用来监听鼠标的点击功能,并且调用RoleController实现对人物的控制
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    // 鼠标点击的控制
    public class ClickGUI : MonoBehaviour {
        UserAction u;
        RoleController roleController;
    
        public void setController(RoleController rc) {
            roleController = rc;
        }
    
        void Start() {
            u = Director.getInstance ().currentSceneController as UserAction;
        }
    
        void OnMouseDown() {
            if (gameObject.name == "boat") u.MoveBoat ();
            else u.MoveRole (roleController);
        }
    }

    思考题【选做】

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

    解答:

    • Rotate:

      Rotates the object around the given axis by the number of degrees defined by the given angle.

      Rotate has an axis, angle and the local or global parameters. The rotation axis can be in any direction.

      需要实现绕v轴旋转角度x,得到了旋转之后,就可以改变t的position和rotation

      void Rotate(Transform t, Vector3 axis, float angle)
      {
          var r = Quaternion.AngleAxis(angle, axis);
          t.position = r * t.position; t.rotation *= r;
      }
    • RotateAround:

      Rotates the transform about axis passing through point in world coordinates by angle degrees.

      此函数是围绕中心旋转,我们需要得到物体的位移,求完之后,将它进行旋转然后加上中心的位移即可。

      void RotateAround(Transform t, Vector3 center, Vector3 axis, float angle)
    {
        var r = Quaternion.AngleAxis(angle, axis);
        var dis = t.position - center;
        dis *= r; t.position = center + dis; t.rotation *= r ;
    }
    点赞
    收藏
    评论区
    推荐文章
    红橙Darren 红橙Darren
    4年前
    NDK开发前奏 - 实现支付宝人脸识别功能
    1.基于AndroidStudio的opencv配置与使用先推荐一本书《计算机视觉算法与应用》,相信用过OpenCV的哥们都知道这是用来干啥的,这里我就不再啰嗦。只说一下他的应用领域:人机互动、物体识别、图像分割、人脸识别、动作识别、运动跟踪、机器人、运动分析、机器视觉、结构分析、汽车安全驾驶等等。这次我们主要用它来做人脸识别,注意人脸
    Karen110 Karen110
    4年前
    人工智能数学基础-线性代数3:线性空间、线性相关及基
    一、向量空间(线性空间)及基域线性空间是在考察了大量的数学对象(如几何学与物理学中的向量,代数学中的n元向量、矩阵、多项式,分析学中的函数等)的本质属性后抽象出来的数学概念。1.1、详细定义向量空间也称线性空间,设V是一个非空集合,P是一个数域。若:1.在V中定义了一种运算,称为加法,即对V中任意两个元素α与β都按某一法则对应于V内惟一确定的一个元素α
    Wesley13 Wesley13
    3年前
    2020微信运动一键刷步数,让你重回排行榜第一
    微信运动很久之前就有了,不过我是最近才开始玩的,你的微信运动排行榜里是不总有人长期霸占第一,他应该不是每天都在运动,很可能是使用了工具刷的,今天就分享下。在线刷微信运动步数首先在应用商店下载一个乐心健康app ,使用手机号注册,并且设置一个登陆密码,然后在数据共享里开启第三方同步(包含微信运动,支付宝运动,阿里体育,需要关注他们的公
    Wesley13 Wesley13
    3年前
    unity仿微信飞机大战项目
    开发路线:1,游戏背景(连续播放)2,添加主角3,设置游戏主角的动画4,添加两种子弹并设置子弹的运动5,添加三种子弹设置子弹的自动生成和运动6,添加两种奖励物品设置奖励物品的自动生成和运动7,设置主角的控制
    Wesley13 Wesley13
    3年前
    ubuntu18.04环境下为UR3配置MoveIt!运动学插件IKFAST(二)
    前言昨天已经将OpenRAVE配置好了,接下来就是配置IKFast插件了。ikfast,机器人运动学的编译器,在RosenDiankovOpenRAVE运动规划软件提供,是一个强大的逆运动学求解器。不像大多数的逆运动学求解,ikfast可以求解任意复杂运动链的运动学方程,并产生特定语言的文件(如C)后供使用。最终的结果是非常稳定的解决方
    Wesley13 Wesley13
    3年前
    Video Object Detection with an Aligned Spatial
    摘要:  本文针对视频目标检测问题提出时空记忆网络(STMN)。它的核心是时空记忆模块,作为一种递归计算单元去建模长时间目标外观和运动信息。STMN可以用一个预训练的CNNbackbone进行初始化,这对提高检测精度非常重要。本文为了建模目标运动提出匹配变换去对齐帧到帧的特征。本文的方法在VID数据集上获得了stateoftheart的结
    Wesley13 Wesley13
    3年前
    unity2D以最小的角度旋转到目标方向(y方向为角色的主方向)
    一.使用向量原理转换到目标方向为了让角色的自身y转向目标方向,并且以最小角度旋转,要点是获得当前方向与目标方向的叉值,从而判断应该旋转的方向floatrotateSpeed;//相对目标位置运动voidtrackPosition02(Vector3tarPosition){Vector3targetDirtarPo
    Wesley13 Wesley13
    3年前
    JS操控CSS样式完成小球自由落体运动,和大家分享一下制作心得。
       这篇心得本应该在一个月之前和大家一起分享的,由于本人比较懒,也几乎没有写博客的习惯,所以迟了一些。有一些内容只是一些个人的废话,可看可不看,毕竟在国内的应试教育下,大家基础物理知识都是很扎实的:  (废话)(背景:下面讨论的物体运动默认为宏观角度)一般情况下,物体在三维空间所发生的位移都可以解析为若干连续的在二维空间所发生的位移的和,同理,物
    Wesley13 Wesley13
    3年前
    Unity 2D人物运动不协调的检查方法(本人专用)
    运用Rigibody2D的时候:  1:注意人物移动代码是否和跳跃代码有重叠的地方(导致Rigibody2d.velocity.y数值一直不变?)。!(https://img2018.cnblogs.com/blog/1373948/201907/1373948201907021946527621011120816.png)  2:人
    人脸识别闸机惊艳美国运动员 背后的黑科技是如何实现的?
    全世界瞩目的冰雪赛事刚刚闭幕,运动健儿们奋力拼搏,在一场场速度与激情的较量中留给我们诸多精彩瞬间,同时此次赛事中的许多“黑科技”也给人留下了深刻印象,比如人脸识别闸机就让不少国外的运动员们大为惊叹。为了确保疫情防控,此次赛事中应用了人脸识别门禁,不用摘口罩和帽子就可以精准识别运动员身份信息,实现秒速通行。来自美国的运动员专门在社交平台上分享了这一技术,他特意
    迁移学习(Transfer Learning)
    1.深入了解神经网络的组成、训练和实现,掌握深度空间特征分布等关键概念;2.掌握迁移学习的思想与基本形式,了解传统迁移学习的基本方法,对比各种方法的优缺点;3.握深度迁移学习的思想与组成模块,学习深度迁移学习的各种方法;4.掌握深度迁移学习的网络结构设计、目标函数设计的前沿方法,了解迁移学习在PDA、SourceFreeDA上的应用;5.掌握深度迁移学习在