本文系作者原创,转载请注明出处

入门级的笔者想了一上午才搞懂那个欧拉角的Camera旋转..=.=

在调试场景的时候,每次都本能的按下W想前进,但是这是不可能的(呵呵)

于是便心血来潮想顺便添加个KeyMove事件给摄像机,来实现 伪”漫游“场景 吧。

笔者之前看到过一个叫First Person Controller的Prefab,直接就实现了第一人称的场景漫游(即是不能到处乱飞,只能在一个固定高度进行场景浏览,模拟人行走的意思)

盗张图嘿嘿

但是介于刚刚入门不知道哪儿去找,所以只能自己嗨几个脚本出来自己测....勿喷...

进入正题吧

众所周知 transfrom.Translate平移 里面提供了一个函数

  1. function Translate (x : float, y : float, z : float, relativeTo : Space = Space.Self) : void
  2.  
  3. Description
    描述
  4.  
  5. Moves the transform by x along the x axis, y along the y axis, and z along the z axis.
  6.  
  7. 移动变换
    x沿着x轴,y沿着y轴,z沿着z
  8.  
  9. If relativeTo is left out or set to Space.Self the movement is applied relative to the transform's local axes.
    (the x, y and z axes shown when selecting the object inside the Scene View.)
    If relativeTo is Space.World the movement is applied relative to the world coordinate system.
  10.  
  11. 如果relativeTo留空或者设置为Space.Self,移动被应用相对于变换的自身轴(当在场景视图选择物体时,x、y和z轴显示)
    如果相对于Space.World 移动被应用相对于世界坐标系统

函数的第四个形参relativeTo可以不加,默认为Space.Self

--------------------------------------------

那么什么是Space.World和Space.Self?

官方文档已经说了,Space.World使实体相对于世界坐标轴进行平移变换,Space.Self使实体相对于自己的坐标轴进行平移变换

举个栗子:

当不填写relativeTo(使用Space.Self)的时候,用以下移动一个物体

  1. if (Input.GetKey(KeyCode.W))
  2. {
  3. transform.Translate(, , * Time.deltaTime * moveSpeedForDebug);
  4. }
  5. if (Input.GetKey(KeyCode.S))
  6. {
  7. transform.Translate(, , - * Time.deltaTime * moveSpeedForDebug);
  8. }
  9. if (Input.GetKey(KeyCode.A))
  10. {
  11. transform.Translate(- * Time.deltaTime * moveSpeedForDebug, , );
  12. }
  13. if (Input.GetKey(KeyCode.D))
  14. {
  15. transform.Translate( * Time.deltaTime * moveSpeedForDebug, , );
  16. }

会以物体当前的Z轴正方向作为”前面“,即向着Z轴的实时朝向进行平移变换(前进后退左移右移等)。如果你朝向正前面按下W,就会往前面平移;向天上按下W就会向天上平移

即你可以到达三维空间中任何一个点

但是使用Space.World的时候不一样了

我们假设世界轴Z轴朝向北方

你继续以上的动作你会发现,不管你的视角面向哪儿,你按下W向着世界轴Z轴进行平移变换,你始终是往北边移动的

这个感觉就像你坐在汽车上,你可以踩油门但是不能摸方向盘,于是你踩着油门一直往前开。当前面遇到一个十字路口你想左转,于是你本能地看向左边

但是你只能踩油门,于是虽然你看向左边了,但是汽车还是开向了前方(如果还不懂的话就去自己试一试吧moew)

--------------------------------------------

于是我想到,可以新建一个camera,用GetComponent获取,然后直接用不写relativeTo的Translate函数对相机进行平移变换实现这种二维平面的移动

但是问题来了,就像上面说的,Space.Self平移变换是使用物体当前的Z轴正方向作为”前面“的,也就是说要实现在一定高度的场景预览,摄像机的X轴不能旋转

但是这不现实啊,哪儿有人去看风景的时候一直盯着前面不看天上不看地下的=.=这样的话Space.Self平移变换就不能实现二维平面的移动

用Space.World呢?也不行!因为实体的平移变换不会根据你的视角发生变化,也就是说实体一直往一个方向平移,不会”看哪儿走哪儿“,所以不能实现有指向的二维平面的移动

但是也只有Space.World能够控制实体不在空间的Y轴上乱动了,这个怎么办呢?

如果相机的移动朝向一直有左右没有上下(只有Yaw旋转没有Pitch旋转),但是相机的查看却能有左右和上下(包含Yaw与Pitch),那就可以直接用relativeTo Space.Self参数

进行一个有指向的二维平面移动了!

于是乎,利用父级子级的方法来实现!

也就是说可以将Camera绑在一个Cube上面。

Cube添加只有Yaw旋转的查看以及relativeTo Space.Self平移变换,那么Cube只能在xOz平面内进行平移,而因为相机是Cube的子级,平移和旋转照样适用

也就是说我们需要做的,只是在给相机添加一个只有Pitch旋转的查看就行了!

这样的话,相机能够实现”到处看“的查看,也能实现只在xOz平面内进行的平移

于是首先建个简单的场景吧

然后建立一个Cube实体,并在Cube的子级建立一个Camera,我把这个起导向作用的Cube实体命名为了GuideCube

向导方块2333

接下来做的是把Cube移到地面上来,再把相机放进Cube中间,关闭Cube的渲染

建立一个脚本(我的是MouseLook)在Update()函数里面添加如下片段(变量的定义我就不说了,详细点这里,另一篇文章里面给出了MouseLook的源码)

把脚本添加到Cube上,实现只有Yaw旋转的查看

  1. if (axes == RotationAxes.MouseX)
  2. {
  3. //获取鼠标的X移动增量
  4. //希望摄像机移动Yaw即Y轴
  5. deltaMouseRotationX += Input.GetAxis("Mouse X") * sensitivityX * Damping;
  6. //比较X旋转角度与最大、最小旋转角度限制
  7. deltaMouseRotationX = Mathf.Clamp(deltaMouseRotationX, minimumX, maximumX);
  8. transform.localEulerAngles = , deltaMouseRotationX, );
  9. Debug.Log("左右看!");
  10. }

再将刚刚的代码写入另一个脚本(我的是KeyMove)添加上去

(并没有写完,但是具有基本功能,比如private enum movementLimit与mainCamera = GetComponentInChildren<Camera>();都还用不上)

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4.  
  5. [AddComponentMenu("Camera-Control/Key Move")]
  6. public class KeyMove : MonoBehaviour {
  7.  
  8. private Camera mainCamera;
  9.  
  10. private enum movementLimit
  11. {
  12. noLimit =,
  13. limitYaxisMovement =
  14. }
  15. private movementLimit isMovementLimited = movementLimit.limitYaxisMovement;
  16.  
  17. public float moveSpeedForDebug = 0.5f;
  18.  
  19. // Use this for initialization
  20. void Start ()
  21. {
  22. mainCamera = GetComponentInChildren<Camera>();
  23. if (mainCamera == null)
  24. Debug.LogWarning("Warning:\nError Finging camera!!");
  25. }
  26.  
  27. // Update is called once per frame
  28. void Update ()
  29. {
  30.  
  31. if (isMovementLimited == movementLimit.limitYaxisMovement)
  32. {
  33. if (Input.GetKey(KeyCode.W))
  34. {
  35. transform.Translate(, , * Time.deltaTime * moveSpeedForDebug);
  36. }
  37. if (Input.GetKey(KeyCode.S))
  38. {
  39. transform.Translate(, , - * Time.deltaTime * moveSpeedForDebug);
  40. }
  41. if (Input.GetKey(KeyCode.A))
  42. {
  43. transform.Translate(- * Time.deltaTime * moveSpeedForDebug, , );
  44. }
  45. if (Input.GetKey(KeyCode.D))
  46. {
  47. transform.Translate( * Time.deltaTime * moveSpeedForDebug, , );
  48. }
  49. }
  50. }
  51. }

之后给相机添加如下,源码还是在MouseLook里面,实现只有Pitch旋转的查看

  1. if (axes == RotationAxes.MouseY)
  2. {
  3. //获取鼠标的Y移动增量
  4. //希望摄像机移动Pitch即X轴
  5. deltaMouseRotationY += Input.GetAxis("Mouse Y") * sensitivityY * Damping;
  6. //比较Y旋转角度与最大、最小旋转角度限制
  7. deltaMouseRotationY = Mathf.Clamp(deltaMouseRotationY, minimumY, maximumY);
  8. transform.localEulerAngles = , );
  9. Debug.Log("上下看!");
  10. }

这样的话,鼠标的X轴偏移会变成Cube的Yaw旋转,鼠标的Y轴偏移变成Camera的Pitch旋转,又Camera跟着父级GuideCube进行Yaw旋转

那么在我们眼中看起来就只是Camera在作”四处查看“的动作啦,而GuideCube也能实现只在xOz平面上的漫游

。。是不是有点麻烦。。

注,我写的MouseLook源码里面能设置如下

也就是规定添加了此Component的实体能够XY旋转还是只能X还是只能Y旋转,方便了这个文章的实现

[Unity3D]巧妙利用父级子级实现Camera场景平面漫游的更多相关文章

  1. delphi 选中的展开0级 子级不展开

    TreeView1.Selected.Expand(False); //选中的展开0级 子级不展开 TreeView1.Selected.Expand(True); //全部展开 来自为知笔记(Wiz ...

  2. JS获取节点的兄弟,父级,子级元素的方法(js获取子级获取到换行与空格元素-FF)

    先说一下JS的获取方法,其要比JQUERY的方法麻烦很多,后面以JQUERY的方法作对比. JS的方法会比JQUERY麻烦很多,主要则是因为FF浏览器,FF浏览器会把你的换行也当最DOM元素 < ...

  3. mysql oracle sql获取树 父级 子级 及自己

    select * from ( select t.*,d.TABLE_NAME,d.QUERY_SQL,d.data_control_col,d.id table_id,d.where_sql fro ...

  4. 递归方式---通过子级id,获取子级和父级Name

    #region 递归--返回 父级|子级 名称 #region --返回 父级|子级 名称 public string RetrurnTypeNames(string TypeId) { String ...

  5. vue结合Ant Design实现后台系统的权限分配(支持无限子级嵌套)

    最近公司的业务需要,要做一个后台管理系统的管理系统类似于这样子 功能需求如下: 左边是权限菜单,右边对应的是具体权限. 1.父级权限菜单选中,父级权限菜单的权限包括其中所有子级权限菜单的权限也要选中, ...

  6. WPF利用VisualTreeHelper遍历寻找对象的子级对象或者父级对象

    原文:WPF利用VisualTreeHelper遍历寻找对象的子级对象或者父级对象 简介 本文将完整叙述我利用VisualTreeHelper实现题述功能的全部过程,想直接看函数实现的朋友可以跳到函数 ...

  7. ThinkPHP 关联模型中查询某条记录的父级(非查询子级)

    数据表 id      cat_name      cat_pid 76     手机.数码     0 84     手机配件        76 86     蓝牙耳机        84 从属关 ...

  8. 子级Repeater获取 父级Repeater

    第一种方法,子级Repeater中绑定父级的某个字段: <%# DataBinder.Eval((Container.NamingContainer.NamingContainer as Rep ...

  9. <转载>如何解决子级用float浮动父级div高度不能自适应的问题

    转载:http://www.kwstu.com/ArticleView/divcss_2013101582430202 解决子级对象使用css float浮动 而父级div不能自适应高度,不能被父级内 ...

随机推荐

  1. OpenCASCADE Job - Shoe Doctor

    鞋博士 鞋博士经过8年沉淀,在鞋类工业4.0全流程平台上积累了相当的技术实力,获投资商亲睐. 新的一年,在投资商协助下,将踏上新的征途,因此诚邀您加盟顶层技术合伙人. 如果您具备以下实力,我们期待您的 ...

  2. ASP.NET Core中的依赖注入(3): 服务的注册与提供

    在采用了依赖注入的应用中,我们总是直接利用DI容器直接获取所需的服务实例,换句话说,DI容器起到了一个服务提供者的角色,它能够根据我们提供的服务描述信息提供一个可用的服务对象.ASP.NET Core ...

  3. .NET MVC Razor模板预编译(二)

    在前面一片文章:<.NET MVC4 Razor视图预编译(一)> 里面我采用的是PrecompiledMvcViewEngineContrib组件进行预编译视图的虚拟地址注册,但是这个组 ...

  4. 设计模式(一):“穿越火线”中的“策略模式”(Strategy Pattern)

    在前段时间呢陆陆续续的更新了一系列关于重构的文章.在重构我们既有的代码时,往往会用到设计模式.在之前重构系列的博客中,我们在重构时用到了“工厂模式”.“策略模式”.“状态模式”等.当然在重构时,有的地 ...

  5. HBase框架学习之路

    1 背景知识 1.1 解决问题 解决HDFS不支持单条记录的快速查找和更新的问题. 1.2 适用情况 存在亿万条记录的数据库,只有千万或者百万条记录使用RDBMS更加合适 确保你的应用不需要使用RDB ...

  6. VMware-存储断网之后无法添加vmx到清单

    由于发生了单点故障,笔者最近处理了一个case,其中一些经验非常希望和大家分享. 问题原因: Technorati 标签: VMware,虚拟机,vmx,锁定,干货 某环境使用VMware的ESXi5 ...

  7. 增强学习(三)----- MDP的动态规划解法

    上一篇我们已经说到了,增强学习的目的就是求解马尔可夫决策过程(MDP)的最优策略,使其在任意初始状态下,都能获得最大的Vπ值.(本文不考虑非马尔可夫环境和不完全可观测马尔可夫决策过程(POMDP)中的 ...

  8. 读书笔记--SQL必知必会19--存储过程

    不同的DBMS对存储过程的实现不同,差异巨大,这里不涉及具体的DBMS,仅仅说明存储过程的简单含义. 19.1 存储过程 简单来说,存储过程就是为以后使用而保存的一条或多条SQL语句. 可以将存储过程 ...

  9. 新年抢红包效果(New Year Red Packet)

    新年抢红包效果(New Year Red Packet) 晓娜的文章(微信公众号:migufe) 2016即将过去,我们将迎来新的一年2017,这里小编提前祝大家新年快乐!万事如意!那我们新年最开心的 ...

  10. flask+sqlite3+echarts2+ajax数据可视化

    前提: 准备Python + Flask+Sqlite3的平台环境(windows系统) 前面一节介绍flask怎么安装了,剩下sqlite3下载后解压,然后环境变量添加解压路径就行了 附加下载地址: ...