精华内容
下载资源
问答
  • Unity第三人称视角

    2019-01-13 21:07:37
    Unity3D第三人称视角,和博客是同步的,有需要请下载。
  • 实现了按下wsad角色自动转向的功能,当相机偏向一个方向后依然有效,可以作为新手学习untiy 3d相机的例子。另外有向量叉乘的实际运用,可以判断旋转的方位。
  • Unity的一款很好用的插件 第三人称摄像机 内附案例场景 可自行研究学习
  • Unity 第三人称射击游戏视角控制与武器瞄准-附件资源
  • unity 第三人称相机跟随脚本
  • Unity第人称

    2017-06-18 20:14:05
    Unity第人称
  • Unity第三人称角色视角系统,方便好用,只需添加两个脚本就能完成你想要的功能
  • 视角变化的联合国角色控制 此仓库包含一个示例,用于处理角色和相机在不同...在第一人称视角的时候角色跟随相机方向旋转在第三人称视角的时候角色跟着随输入旋转 去做 添加跳跃跳跃和自由落体自由落体(或dodge翻滚)
  • UNITY 第三人称 人物移动代码详解 UNITY 第三人称 人物移动代码详解
  • 第三人称是视角意味着摄像机跟随着角色移动而不是在角色内移动,模拟阴影,像实时阴影和光照贴图,改善图像。
  • Unity 第三人称 摄像机

    千次阅读 2019-06-25 15:50:37
    今天主要是简单的模拟下吃鸡游戏的第三人称摄像机的实现。 观察 我们玩过吃鸡手游的都知道,吃鸡的人物跟随的摄像机有两种状态 1. 滑动屏幕的时候,摄像机左右上下移动,人物也会跟随着左右移动,上下抬头低头...

    今天主要是简单的模拟下吃鸡游戏的第三人称摄像机的实现。

    观察

    我们玩过吃鸡手游的都知道,吃鸡的人物跟随的摄像机有两种状态

    1. 滑动屏幕的时候,摄像机左右上下移动,人物也会跟随着左右移动,上下抬头低头(播放相应的动画,但是不上下移动)。摄像机始终保持着观察人物背面。左右可以360度旋转,上下会有个最大值和最小值的限制,无法达到0度往上看或90度往下看。

    2. 按住拖动右上角小眼睛按钮,摄像机左右上下移动,人物不会跟随着左右转动,头会左右上下摆动。几乎可以360度观察人物。上下左右的限制与情况1相同。

    在不考虑头部转动的情况下,可以总结出两种情况摄像机的转动围绕人物的规则都是一样的,即以一个固定半径形成的球面上移动,除去了球面的最上和最下的部分,视角始终看向球心。而当滑动屏幕的情况下,人物也随之左右自转,保持摄像机观察人物的正背面。

    分析

    根据表现的现象,我们可以分析出我们需要实现的逻辑。我们可以想象人物的正后面有一条直线line,方向为(0,0,-1),摄像机离人物距离为distance,初始值摄像机的位置为(0,0,-distance)

    当摄像机进行360度移动的时候,我们可以把移动操作拆分为两个步骤,首先是摄像机垂直移动,即以人物为中心,半径为distance的圆上移动。设垂直角度为α(0<min<α<max<90),则摄像机的高度y轴为distance*sin α,离人物的z轴距离为distance*cos α。此时摄像机的位置为(0,distance*sin α,-distance*cos α)

    然后在之前的基础上,我们对摄像头进行水平移动,即以人物头顶高度distance*sin α的点为中心,半径为distance*cos α的圆上移动。设水平角度为β,则摄像机x轴为distance*cos α * sin β,z轴距离则更新为distance*cos α * cos β。

    所以我们可以用两个角度α,β,来控制摄像机的转动,上下移动更新α值,左右移动更新β值,摄像机的位置为(distance*cos α * sin β, distance*sin α, -distance*cos α * cos β),摄像机始终看向(LookAt)人物

    接着我们要区分两种情况的人物状态,当滑动屏幕的时候,人物会随之一起反方向转动,从而保证摄像机始终观察着人物背面。所以当摄像机水平转了β度时,人物的转向应该为(0, -β, 0)。而按住小眼睛的时候,人物不需要随之移动,但是当松开小眼睛的时候,摄像头需要回归原始位置,所以在按下的时候,我们需要纪录按下时摄像机的偏移角度,以供松开时复原。

    实现

    Demo中我们一共有四个cs文件,一个控制小眼睛按钮,一个控制屏幕滑动,一个控制人物移动,最后一个就是控制摄像机了。

    还有需要搭建一个简单的测试场景,里面有一个方块模拟角色,一个plane做地面,然后添加一个UGUI的Button做小眼睛按钮。

    首先是人物移动的控制器PlayerController,就是做了简单的位移

    using UnityEngine;
    
    namespace Test
    {
        public class PlayerController : MonoBehaviour
        {
            //速度转换
            const float SPEED_CONVERTER = 0.3f;
    
            void Update()
            {
                float inputV = Input.GetAxis("Vertical");
                float inputH = Input.GetAxis("Horizontal");
    
                this.transform.position += this.transform.forward * inputV * SPEED_CONVERTER;
                this.transform.position += this.transform.right * inputH * SPEED_CONVERTER;
            }
        }
    }

    接着是我们的摄像机控制器ThridPlayerCameraController,对摄像机的转动进行操作

    using UnityEngine;
    
    namespace CameraTool
    {
        [RequireComponent(typeof(Camera))]
        public class ThridPlayerCameraController : MonoBehaviour
        {
            [SerializeField] Transform m_target;
            //相机与人物距离
            [SerializeField] float m_distance = 5;
            //初始化的偏移角度,以人物的(0,0,-1)为基准
            [SerializeField] float m_offsetAngleX = 0;
            [SerializeField] float m_offsetAngleY = 45;
    
            //相机与人物的坐标的偏移量
            Vector3 m_offsetVector;
            //纪录偏移角度用于复原
            float m_recordAngleX;
            float m_recordAngleY;
            //相机是否在旋转,旋转中需要一直重新计算 m_offsetVector
            bool m_isRotateing = false;
    
            //弧度,用于Mathf.Sin,Mathf.Cos的计算
            const float ANGLE_CONVERTER = Mathf.PI / 180;
    
            //相机上下的最大最小角度
            const float MAX_ANGLE_Y = 80;
            const float MIN_ANGLE_Y = 10;
    
            Transform m_trans;
            public Transform mineTransform
            {
                get
                {
                    if (m_trans == null)
                    {
                        m_trans = this.transform;
                    }
                    return m_trans;
                }
            }
    
            GameObject m_go;
            public GameObject mineGameObject
            {
                get
                {
                    if (m_go == null)
                    {
                        m_go = this.gameObject;
                    }
                    return m_go;
                }
            }
    
            void Start()
            {
                CalculateOffset();
            }
    
            void LateUpdate()
            {
                //相机坐标 = 人物坐标 + 偏移坐标
                mineTransform.position = m_target.position + m_offsetVector;
                mineTransform.LookAt(m_target);
            }
    
            void Update()
            {
                if (m_isRotateing)
                {
                    CalculateOffset();
                }
            }
    
            //计算偏移,可以想象成在一个球面转,m_distance为半径,m_offsetAngleY决定了相机的高度y
            //高度确定后,就是在一个圆面上转,根据m_offsetAngleX计算出x与z
            void CalculateOffset()
            {
                m_offsetVector.y = m_distance * Mathf.Sin(m_offsetAngleY * ANGLE_CONVERTER);
                float newRadius = m_distance * Mathf.Cos(m_offsetAngleY * ANGLE_CONVERTER);
                m_offsetVector.x = newRadius * Mathf.Sin(m_offsetAngleX * ANGLE_CONVERTER);
                m_offsetVector.z = -newRadius * Mathf.Cos(m_offsetAngleX * ANGLE_CONVERTER);
            }
    
            //开始旋转,纪录当前偏移角度,用于复原
            public void StartRotate()
            {
                m_isRotateing = true;
    
                m_recordAngleX = m_offsetAngleX;
                m_recordAngleY = m_offsetAngleY;
            }
    
            //旋转,修改偏移角度的值,屏幕左右滑动即修改m_offsetAngleX,上下滑动修改m_offsetAngleY
            public void Rotate(float x, float y)
            {
                if (x != 0)
                {
                    m_offsetAngleX += x;
                }
                if (y != 0)
                {
                    m_offsetAngleY += y;
                    m_offsetAngleY = m_offsetAngleY > MAX_ANGLE_Y ? MAX_ANGLE_Y : m_offsetAngleY;
                    m_offsetAngleY = m_offsetAngleY < MIN_ANGLE_Y ? MIN_ANGLE_Y : m_offsetAngleY;
                }
            }
    
            //旋转结束,如需要复原镜头则,偏移角度还原并计算偏移坐标
            public void EndRotate(bool isNeedReset = false)
            {
                m_isRotateing = false;
    
                if (isNeedReset)
                {
                    m_offsetAngleY = m_recordAngleY;
                    m_offsetAngleX = m_recordAngleX;
                    CalculateOffset();
                }
            }
        }
    }

    接着就是两个UI控制器UIController 和 SmallEyeController,实现IDragHandler, IBeginDragHandler, IEndDragHandler接口

    using UnityEngine;
    using UnityEngine.EventSystems;
    
    namespace Test
    {
        public class UIController : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler
        {
            [SerializeField] Transform m_target;
            [SerializeField] CameraTool.ThridPlayerCameraController m_thridCamera;
    
            //滑动的偏移转换为角度偏移的系数
            const float DRAG_TO_ANGLE = 0.5f;
            Vector2 m_previousPressPosition;
            float m_angleX, m_angleY;
    
            public void OnBeginDrag(PointerEventData eventData)
            {
                m_previousPressPosition = eventData.position;
                m_thridCamera.StartRotate();
            }
    
            public void OnDrag(PointerEventData eventData)
            {
                //滑动屏幕,人物和摄像机一起旋转(人物只转Y轴,即左右旋转)
                m_angleX = (eventData.position.x - m_previousPressPosition.x) * DRAG_TO_ANGLE;
                m_angleY = (eventData.position.y - m_previousPressPosition.y) * DRAG_TO_ANGLE;
                m_thridCamera.Rotate(-m_angleX, -m_angleY);
                m_target.Rotate(new Vector3(0, m_angleX, 0));
                m_previousPressPosition = eventData.position;
            }
    
            public void OnEndDrag(PointerEventData eventData)
            {
                m_thridCamera.EndRotate();
            }
        }
    }
    using UnityEngine;
    using UnityEngine.UI;
    using UnityEngine.EventSystems;
    
    namespace Test{
    	public class SmallEyeController : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler
        {
            [SerializeField] CameraTool.ThridPlayerCameraController m_thridCamera;
    
            const float DRAG_TO_ANGLE = 0.5f;
            Vector2 m_previousPressPosition;
            float m_angleX, m_angleY;
            Image m_image;
    
            void Start()
            {
                m_image = GetComponent<Image>();
            }
    
            public void OnBeginDrag(PointerEventData eventData)
            {
                m_previousPressPosition = eventData.position;
                m_thridCamera.StartRotate();
                m_image.color = Color.gray;
            }
    
            public void OnDrag(PointerEventData eventData)
            {
                //滑动小眼睛按钮,只旋转摄像机
                m_angleX = (eventData.position.x - m_previousPressPosition.x) * DRAG_TO_ANGLE;
                m_angleY = (eventData.position.y - m_previousPressPosition.y) * DRAG_TO_ANGLE;
                m_thridCamera.Rotate(-m_angleX, -m_angleY);
                m_previousPressPosition = eventData.position;
            }
    
            public void OnEndDrag(PointerEventData eventData)
            {
                m_image.color = Color.white;
                m_thridCamera.EndRotate(true);
            }
    	}
    }

    效果图

    展开全文
  • 包含一个较为完整的UNITY第人称射击游戏的教程,及其使用到的素材包,含角色武器模型。 《我的Unity 3D之旅》博客配套美术资源
  • 动手实现了一个人称视角的编写方式,以前一直都用的unity自带的standardassets里面的人称视角预制体,现在也能自己写出来这种视角了,根据里面的文档来写每个人都可以做到,文档还介绍了一些简单的知识点,...
  • Unity3D人称FPX,通过下载FPS_人称.Unitypackage资源包导入到Unity工程中(版本2017.3.1以上),会有一个Prefabs预制体文件夹,直接拖入Scene场景中使用
  • unity3d的playmaker插件中如何使用第三人称控制器的简易教程示范。
  • Unity 第三人称射击游戏视角控制与武器瞄准

    千次阅读 多人点赞 2020-02-11 18:36:02
    1.使用Final IK来控制武器的瞄准 2.第三人称视角控制

    ===================更新一下源工程链接:
    链接:https://pan.baidu.com/s/15bxH-MPregp2ZIN92fK7XA 提取码:e7bp
    ===================(与本文相比有修改)

    效果视频演示:
    https://www.bilibili.com/video/av88249417
    完整代码在最下面

    最近练习Unity,想做一个第三人称射击游戏的Demo。首先来做一个武器瞄准效果,即让枪能指向目标。

    查了很久不知道怎么实现,后来查到了IK这个概念,IK就是反向动力学,按我的理解是让骨骼中的下层带动上层运动,与FK(前向动力学)相反,比如伸手去触摸物体时带动整个身体前倾。所以武器的瞄准就可以用通过IK来做,让手和武器瞄准目标,带动身体旋转,这样就很自然。

    如图:
    在这里插入图片描述
    Unity自带的IK实在是不好用,连这个功能都很难实现,于是我用了一个插件:Final IK,用里面一个叫AimIK的组件就可以解决了。
    附上Final IK1.6的链接:
    链接:https://pan.baidu.com/s/1zY4bluDzi8xSSWPtJBLGWQ
    提取码:eovc
    教程参考这篇博客:
    https://blog.csdn.net/weixin_38239050/article/details/101831392
    Final IK的使用不是本文的重点。

    用代码来设置瞄准的思路是:从摄像机的位置向摄像机的forward方向发出一定距离的射线,如果射线打到物体就让把它设为瞄准目标,否则瞄准射线的终点。注意射线可能需要屏蔽玩家本身碰撞体的干扰
    设置AimIK的target位置的代码为:

    aimIK.solver.target.position = targetPos;
    

    例如射线打到地面:
    在这里插入图片描述

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

    然后是视角控制,虽然Unity有相应的插件,但是直接用就没意思了。本来以为比较简单,没想到还是搞了几天(太菜了)
    实现的功能如下:
    1.摄像机平滑跟随玩家
    2.摄像机绕玩家旋转,并限制角度
    3.往上看时随角度增大而拉近摄像机,往下看时随角度增大而拉远摄像机
    4.瞄准时拉近摄像机,停止瞄准时回到默认位置
    5.碰到墙壁遮挡时拉近摄像机

    下面依次介绍,完整代码在最下面

    1.摄像机平滑跟随

    记录摄像机与人物的位置偏移向量

    playerOffset = player.position - transform.position;
    

    当摄像机旋转后需要更新playerOffset,再次计算上面这句代码。

    更新playerOffset后需要在下一帧使用插值让摄像机移动到player.position - playerOffset这个位置

    transform.position = Vector3.Lerp(transform.position, player.position - playerOffset, moveSpeed * Time.deltaTime);
    

    2.摄像机绕玩家旋转,并限制角度

    主要使用transform.RotateAround函数,让摄像机绕人物旋转。由于人物可以移动,而摄像机的位置是使用插值改变的,当人物移动时,playerOffset这个向量的长度和旋转角度会发生改变,人物走的越远,偏差会越大,不符合要求。

    我的办法是当摄像机绕玩家旋转后,让playerOffset向量同步旋转相同的角度,更新playerOffset。旋转后playerOffset的长度不变,这样可保证player.position-playerOffset始终是摄像机的最终位置。

    如下图示意:
    在这里插入图片描述
    在这里插入图片描述
    RotateAround的用法为:

    RotateAround(Vector3 point, Vector3 axis, float angle);
    

    point:要围绕的点;
    axis:要围绕的轴,如x,y,z
    angel:旋转的角度

    所以摄像机水平和垂直绕玩家旋转为:(注意旋转的轴不同

    transform.RotateAround(player.position, Vector3.up, axisX);
    transform.RotateAround(player.position, transform.right, -axisY);
    

    其中axisX和axisY为对应鼠标的偏移量xTime.deltatimex旋转速度:

    float axisX = Input.GetAxis("Mouse X") * rotateSpeed * Time.deltaTime;
    float axisY = Input.GetAxis("Mouse Y") * rotateSpeed * Time.deltaTime;
    

    而让playerOffset向量绕一个点旋转则要先获取水平和垂直旋转的四元数:(同样注意绕的轴

    Quaternion rotX = Quaternion.AngleAxis(axisX, Vector3.up);
    Quaternion rotY = Quaternion.AngleAxis(-axisY, transform.right);
    

    然后让这两个四元素与向量相乘就可以旋转向量而长度不变:(注意这里相乘不具有交换性,不要改变顺序或是简写

    playerOffset = rotX * rotY * playerOffset;
    

    这里获取向量旋转看不懂可以参考文章:
    https://gameinstitute.qq.com/community/detail/127450

    要限制角度,我们要先让它旋转,然后获取旋转后垂直方向的欧拉角:

    float x = (transform.rotation).eulerAngles.x;
    

    但是欧拉角的范围是0-360度循环的,不利于判断,要转换范围成-180度-180度,向上为负,向下为正。然后判断它是否在我们给定的范围内如果超出范围则还原摄像机在垂直方向的旋转,并且让playerOffset只进行水平旋转;否则让playerOffset进行水平和垂直的旋转。

    //欧拉角范围为0~360,这里要转为-180~180方便判断
    if (x > 180) x -= 360;
    
    if (x < minAngle || x > maxAngle)//超出角度
    {
    	//还原位置和旋转
    	transform.position = posPre;
    	transform.rotation = rotPre;
    
    	//更新offset向量,offset与本物体同步旋转
    	//我们需要通过这offset去计算本物体(包括摄像机)应该平滑移向的位置
    	//如果仅仅使用RotateAround函数,当人物在移动时会出现误差
    	playerOffset = rotX*playerOffset;   
    }
    else//垂直视角符合范围的情况
    {
    	//更新offset向量,offset与本物体同步旋转
    	playerOffset = rotX * rotY * playerOffset;
    }
    

    最高视角:(根据minAngle,根据需要设定,我设为-40)
    在这里插入图片描述
    最低视角:(根据maxAngle,根据需要设定,我设为50)
    在这里插入图片描述

    3.往上看时随角度增大而拉近摄像机,往下看时随角度增大而拉远摄像机

    摄像机与人物的距离需要时可变的,比如在瞄准时应该将摄像机拉近人物,但是如果直接改变摄像机的位置又会破坏跟随玩家和自由视角的功能。

    为了使摄像机在能够根据需要偏移的同时又不影响平滑跟随玩家,我的办法是把上面的控制代码挂在一个空物体上,将摄像机作为这个空物体的子物体,这样空物体跟随玩家时,摄像机也会做相同的位移。而让摄像机的位置产生偏移只需要改变它的localPosition,也就是它与父物体的相对位置。

    如图:TPSCameraParent为空物体
    在这里插入图片描述
    摄像机偏移是改变TPSCamera的localPosition.z,让TPSCamera相对于TPSCameraParent前后移动。定义一个总的偏移量:

    float localOffset = 0;
    

    这个偏移量的影响因素有三个:
    1.垂直视角角度
    2.是否瞄准
    3.是否有遮挡

    下面来看垂直视角角度的影响:

    上面我们获取到了垂直方向旋转欧拉角float x,就可以根据x与我们给定的最大角度的比值来设定摄像机的前后偏移:

    //更据角度设置摄像机位置偏移
                if (x < 0)//往上角度为负
                {
                    //往上看时距离拉近
                    localOffsetAngle = (x / minAngle) * localOffsetAngleUp;
                }
                else
                {
                    //往下看时距离拉远
                    localOffsetAngle = -(x / maxAngle) * localOffsetAngleDown;
                }
    

    其中localOffsetAngle为根据角度计算的偏移量,localOffsetAngleUp和localOffsetAngleDown分别为向上看和向下看时这个偏移量的最大值。当x=0时即摄像机平视前方时,这个偏移量=0。

    那怎么让摄像机能够前后偏移呢?让总偏移量加上它:

    localOffset+=localOffsetAngleMax;
    

    在最后使摄像机平滑移动到偏移位置:

    Vector3 offsetPos = new Vector3(0, 0, localOffset);//这是相机应该移向的位置
    //使相机平滑移动到这个位置
    cam.transform.localPosition = Vector3.Lerp(cam.transform.localPosition, offsetPos, localOffsetSpeed * Time.deltaTime);
    

    俯视时拉远摄像机:
    在这里插入图片描述
    仰视时拉近摄像机:
    在这里插入图片描述

    4.瞄准时拉近摄像机,停止瞄准时回到默认位置

    规定当鼠标右键按住时瞄准,松开时停止瞄准。

    再定义一个偏移量localOffsetAim和一个bool值isAiming

    public float localOffsetAim = 2;//根据是否瞄准而产生的偏移量,表示瞄准时摄像机应该前进多远距离,根据需要设值
    private bool isAiming = false;//是否正在瞄准
    

    然后每帧判断鼠标事件:

    if (Input.GetMouseButtonDown(1))//鼠标右键按下为瞄准
       {
       	isAiming = true;
       }
    if (Input.GetMouseButtonUp(1))//鼠标右键松开停止瞄准
    {
       	isAiming = false;
    }
    

    接着根据isAiming来决定是否让localOffset加上这个偏移量:

    //根据是否瞄准而调整
    if (isAiming)
    {
    	localOffset += localOffsetAim;
    }
    

    瞄准时拉近摄像机:
    在这里插入图片描述
    正常距离:
    在这里插入图片描述

    5.碰到墙壁遮挡时拉近摄像机

    当人物身后有墙壁或是其他物体靠近时,会遮挡视线:
    在这里插入图片描述
    这时候需要将摄像机拉近到合适距离,所以再定义一个偏移量:

    float localOffsetCollider = 0;
    

    然后在一帧中逐渐增加这个偏移量去试探有没有遮挡,试探的方法还是利用射线检测,看是否能打到除玩家外的碰撞体,我这里玩家身上挂的是CapsuleCollider:

    private bool CheckView(Vector3 checkPos)
        {
            //发出射线来检测碰撞
            RaycastHit hit;
            //射线终点为玩家物体的中间位置
            Vector3 endPos = player.position + player.up * player.GetComponent<CapsuleCollider>().height * 0.5f;
    
            Debug.DrawLine(checkPos,endPos, Color.blue);
    
            //从checkPos发射一条长度为起点到终点距离的射线
            if (Physics.Raycast(checkPos,endPos-checkPos,out hit,(endPos-checkPos).magnitude)){
                if (hit.transform == player)//如果射线打到玩家说明没有遮挡
                    return true;
                else//如果射线打击到其他物体说明有遮挡
                    return false;
            }
            return true;//如果射线没有打到任何物体也说明没有遮挡
        }
    

    根据是否有遮挡而调整localOffsetCollider

            Vector3 checkPos = transform.position + cam.transform.forward * localOffset;//这是没有调整前相机应该移向的位置
            for(localOffsetCollider=0; !CheckView(checkPos);localOffsetCollider+=0.2f)//让localOffset递增直至没有遮挡
            {
                //更新checkPos为我们想要移动到的位置,再去试探
                checkPos = transform.position + cam.transform.forward * (localOffset+localOffsetCollider);
            }
    

    再让localOffset加上这个试探出的localOffsetCollider。

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

    完整代码及使用

    分为两个脚本:
    1.TPSCamera.cs

    using UnityEngine;
    
    public class TPSCamera : MonoBehaviour
    {
        public static TPSCamera _instance;//用作单例模式
        public Camera cam;//摄像机,是本物体下的子物体
        public Transform player;//玩家物体的Transform
        public Vector3 playerOffset;//本物体与玩家位置的偏移向量
    
        public float rotateSpeed;//控制旋转速度
        public float moveSpeed;//控制跟随的平滑度
    
        public float minAngle;//垂直视角的最小角度值
        public float maxAngle;//垂直视角的最大角度值
    
        public float localOffsetSpeed = 8;//控制相机与父物体偏移时的平滑度
        public float localOffsetAim = 2;//根据是否瞄准而产生的偏移量,表示瞄准时摄像机应该前进多远距离,根据需要设值
        private float localOffsetAngle = 0;//根据垂直视角角度而产生的偏移量
        public float localOffsetAngleUp = 1.5f;//根据向上的角度而产生的偏移量的最大值
        public float localOffsetAngleDown = 1.5f;//根据向下的角度而产生的偏移量的最大值
        private float localOffsetCollider = 0;//根据玩家与摄像机间是否有遮挡而产生的偏移量
    
        private bool isAiming = false;//是否正在瞄准
    
        private void Awake()
        {
            _instance = this;
            player = GameObject.Find("Player").transform;//根据名字找到玩家物体
            playerOffset = player.position - transform.position;//初始化playerOffset
            cam = transform.GetComponentInChildren<Camera>();//获取子物体的Camera组件
        }
    
        private void Update()
        {
            if (Input.GetMouseButtonDown(1))//鼠标右键按下为瞄准
            {
                isAiming = true;
            }
            if (Input.GetMouseButtonUp(1))//鼠标右键松开停止瞄准
            {
                isAiming = false;
            }
            SetPosAndRot();//设置视角旋转后的位置和朝向
            Cursor.visible = false;//隐藏鼠标
        }
    
        /// <summary>
        /// 上下移动鼠标时,相机围绕玩家旋转,并且限制旋转角度
        /// </summary>
        public void SetPosAndRot()
        {
            //更新本物体的position,相机会和本物体做相同的位移,使相机平滑跟随玩家
            transform.position = Vector3.Lerp(transform.position, player.position - playerOffset, moveSpeed * Time.deltaTime);
            
            //获取鼠标移动量
            float axisX = Input.GetAxis("Mouse X") * rotateSpeed * Time.deltaTime;
            float axisY = Input.GetAxis("Mouse Y") * rotateSpeed * Time.deltaTime;
    
            //计算水平和垂直的旋转角
            Quaternion rotX = Quaternion.AngleAxis(axisX, Vector3.up);
            Quaternion rotY = Quaternion.AngleAxis(-axisY, transform.right);
    
            //摄像机在水平方向绕玩家旋转
            transform.RotateAround(player.position, Vector3.up, axisX);
            
            //保存未旋转垂直视角前的position和rotation
            Vector3 posPre = transform.position;
            Quaternion rotPre = transform.rotation;
    
            //先垂直绕玩家旋转,注意这里旋转的轴为transform.right
            transform.RotateAround(player.position, transform.right, -axisY);
    
            //判断垂直角度是否符合范围
            float x = (transform.rotation).eulerAngles.x;
            //欧拉角范围为0~360,这里要转为-180~180方便判断
            if (x > 180) x -= 360;
            if (x < minAngle || x > maxAngle)//超出角度
            {
                //还原位置和旋转
                transform.position = posPre;
                transform.rotation = rotPre;
    
                //更新offset向量,offset与本物体同步旋转
                //我们需要通过这offset去计算本物体(包括摄像机)应该平滑移向的位置
                //如果仅仅使用RotateAround函数,当人物在移动时会出现误差
                playerOffset = rotX*playerOffset;   
            }
            else//垂直视角符合范围的情况
            {
                //更新offset向量,offset与本物体同步旋转
                playerOffset = rotX * rotY * playerOffset;
    
                //更据角度设置摄像机位置偏移
                if (x < 0)//往上角度为负
                {
                    //往上看时距离拉近
                    localOffsetAngle = (x / minAngle) * localOffsetAngleUp;
                }
                else
                {
                    //往下看时距离拉远
                    localOffsetAngle = -(x / maxAngle) * localOffsetAngleDown;
                }
            }
    
            //设置摄像机与父物体的偏移,三个影响因素
            SetLocalOffset(); 
        }
    
        /// <summary>
        /// 根据是否瞄准、垂直视角和是否有遮挡来调整摄像机与父物体的偏移
        /// </summary>
        public void SetLocalOffset()
        {
            float localOffset = 0;//摄像机与父物体(即本脚本所在的空物体)的偏移
            //根据垂直视角调整
            localOffset += localOffsetAngle;
            //根据是否瞄准而调整
            if (isAiming)
            {
                localOffset += localOffsetAim;
            }
    
            //根据是否有遮挡而调整
            Vector3 checkPos = transform.position + cam.transform.forward * localOffset;//这是没有调整前相机应该移向的位置
            for(localOffsetCollider=0; !CheckView(checkPos);localOffsetCollider+=0.2f)//让localOffset递增直至没有遮挡
            {
                //更新checkPos为我们想要移动到的位置,再去试探
                checkPos = transform.position + cam.transform.forward * (localOffset+localOffsetCollider);
            }
            localOffset += localOffsetCollider;//加上这个试探出的偏移量
    
            Vector3 offsetPos = new Vector3(0, 0, localOffset);//这是调整后相机应该移向的位置
            //使相机平滑移动到这个位置
            cam.transform.localPosition = Vector3.Lerp(cam.transform.localPosition, offsetPos, localOffsetSpeed * Time.deltaTime);
        }
    
        /// <summary>
        /// 检查玩家与摄像机之间是否有碰撞体遮挡
        /// </summary>
        /// <param name="checkPos">假设相机的位置</param>
        /// <returns></returns>
        private bool CheckView(Vector3 checkPos)
        {
            //发出射线来检测碰撞
            RaycastHit hit;
            //射线终点为玩家物体的中间位置
            Vector3 endPos = player.position + player.up * player.GetComponent<CapsuleCollider>().height * 0.5f;
    
            Debug.DrawLine(checkPos,endPos, Color.blue);
    
            //从checkPos发射一条长度为起点到终点距离的射线
            if (Physics.Raycast(checkPos,endPos-checkPos,out hit,(endPos-checkPos).magnitude)){
                if (hit.transform == player)//如果射线打到玩家说明没有遮挡
                    return true;
                else//如果射线打击到其他物体说明有遮挡
                    return false;
            }
            return true;//如果射线没有打到任何物体也说明没有遮挡
        }
    
    }
    
    

    这个脚本挂在TPSCameraParent上,即摄像机的父物体上
    相关参数根据需要设定,我的如下,供参考:
    在这里插入图片描述

    2.ShootControl.cs

    using RootMotion.FinalIK;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class ShootControl : MonoBehaviour
    {
        public TPSCamera tpsCamera;//控制相机视角的脚本
        public Camera cam;//摄像机
        public float range;//射线距离
        private float offsetDis;//摄像机与玩家的距离
        public Vector3 targetPos;//目标位置
        private AimIK aimIK;//对应的final ik组件
        public float speed;//移动速度
        public float rotateSpeed;//旋转速度
    
        private void Awake()
        {
            tpsCamera = GameObject.Find("TPSCameraParent").GetComponent<TPSCamera>();//获取相机的父物体
            cam = tpsCamera.GetComponentInChildren<Camera>();//相机为tpsCamera的子物体
            aimIK = GetComponent<AimIK>();//获取AimIk组件
            offsetDis = Vector3.Distance(transform.position, cam.transform.position);//初始化offsetDis
        }
    
        private void Update()
        {
            SetTarget();//设置瞄准的目标位置
            OnKeyEvent();//处理按键响应
        }
    
        /// <summary>
        /// 设置瞄准的目标
        /// 从摄像机位置向摄像机正方向发射射线(即从屏幕视口中心发出)
        /// 射线的长度=range,可以近似设为子弹的射程
        /// 若射线打到非玩家的物体则将该物体设为目标
        /// 若射线没有打到物体则将目标设为射线的终点
        /// </summary>
        public void SetTarget()
        {
            //从摄像机位置向摄像机正方向发射射线(即从屏幕视口中心发出)
            RaycastHit hit;
            if (Physics.Raycast(cam.transform.position, cam.transform.forward, out hit, range))
            {
                //若射线打到非玩家的物体则将该物体设为目标
                //我这里并没有进行判断该物体是否是玩家,因为我设置的玩家位于屏幕的偏左下位置,射线不会穿过玩家
                //需要的话,可以给玩家设定layer,然后让射线屏蔽这个layer
                targetPos = hit.point;
            }
            else
            {
                //若射线没有打到物体则将目标设为射线的终点
                targetPos = cam.transform.position + (cam.transform.forward * range);
            }
            //画出射线便于观察(不会显示在game中)
            Debug.DrawRay(cam.transform.position, cam.transform.forward * range, Color.green);
    
            //按下鼠标右键时开启AimIK,进入瞄准状态
            if (Input.GetMouseButtonDown(1))
            {
                aimIK.enabled = true;
            }
    
            //按住鼠标右键时为瞄准状态,人物身体始终朝向摄像机的前方
            if (Input.GetMouseButton(1))
            {
                RotateBodyToTarget();
            }
            else//松开右键时为自由视角状态,关闭AimIK,不进行瞄准
            {
                //注意这里使用Disale(),不要直接enabled=false,原因不清楚
                aimIK.Disable();
            }
        }
    
        /// <summary>
        /// 旋转玩家身体,使玩家朝向摄像机的水平前方
        /// </summary>
        private void RotateBodyToTarget()
        {
            Vector3 rotEulerAngles = cam.transform.eulerAngles;//获取摄像机的旋转的欧拉角
            rotEulerAngles.x = 0;//垂直方向不进行旋转
            //使用插值让玩家平滑转向
            transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(rotEulerAngles), rotateSpeed * Time.deltaTime);
            SetAimIKTarget();//更新AimIK的target的位置
        }
    
        /// <summary>
        /// 更新AimIK的target的位置
        /// </summary>
        private void SetAimIKTarget()
        {
            //将AimIK的target位置设为之前射线检测到的位置
            aimIK.solver.target.position = targetPos;
        }
    
        /// <summary>
        /// 管理键盘的响应,这里只用来控制玩家移动,不重要,可以忽略
        /// </summary>
        private void OnKeyEvent()
        {
            //Horizontal和Vertical的默认按键为ad←→和ws↑↓
            float h = Input.GetAxis("Horizontal");
            float v = Input.GetAxis("Vertical");
            if (h != 0 || v != 0)
            {
                Vector3 moveDir = new Vector3(h, 0, v);
                transform.Translate(moveDir * speed * Time.deltaTime);
                RotateBodyToTarget();
            }
        }
    }
    

    挂在玩家身上,玩家名为“Player”,参数参考:
    在这里插入图片描述
    玩家身上还需挂载的组件:
    在这里插入图片描述
    AimIK参数:(建议先看一下AimIK的使用,如果不需要瞄准功能的话可以不用ShootControl和AimIK组件)
    在这里插入图片描述
    其中Target为一个任意的空物体,拖进去,FirePos为武器的空子物体,位于枪口位置。
    如图:
    在这里插入图片描述
    初始时将需要跟随的人物放在摄像机视线的左下角,否则需要在ShootControl.cs中的射线检测那里屏蔽掉玩家所在的层。
    在这里插入图片描述
    另外准星绘制参考这个博客:
    https://blog.csdn.net/xboxbin/article/details/88069638

    展开全文
  • Unity 第三人称人物移动

    千次阅读 2020-09-05 21:29:53
    要实现第三人称的人物移动首先就是需要控制人物的移动: 1.创建一个简单的物体当作人物: 2,为物体添加移动代码:本次使用的方式A、D键控制前后运动,W、S键控制转向 transform.Translate(0, 0, speed * Input....

    1,创建一个简单的物体作为角色
    在这里插入图片描述
    2,为物体添加移动代码:本次使用的方式A、D键控制前后运动,W、S键控制转向

    	transform.Translate(0, 0, speed * Input.GetAxis("Vertical"));//Translate可以控制物体移动
        transform.Rotate(0, speed *20* Input.GetAxis("Horizontal"),0);//Rotate可以控制物体转向
    

    3,物体需要跳跃:当按下空格键,AddForce可以给物体一个力,而Vector.up可以控制力的方向向上

    if(Input.GetKeyDown(KeyCode.Space))
            {
                this.GetComponent<Rigidbody>().AddForce(Vector3.up * 6000);
            }
    

    4,为了使得物体不摔倒,使用eulerAngles给物体一个角度判断`

    if ((this.transform.eulerAngles.x > 90 && this.transform.eulerAngles.x < 270) || (this.transform.eulerAngles.z > 90 && this.transform.eulerAngles.z < 270))
            {
                transform.eulerAngles = new Vector3(0, 180, 0);
            }
    

    5,给物体添加一个跟随相机,首先创建一个相机,然后把这个脚本赋给相机,player为玩家,通过玩家的位置,来为相机创建一个跟随位置。
    第二行的lerp是一个插值运算方法,可以使得相机平滑的跟随着人物来运动。
    为了使得相机在人物背后,可以使用LookAt来注视玩家。

    
    	Vector3 camera_felow = player.position + Vector3.up * speed_one - first.forward * speed_two;
        transform.position = Vector3.Lerp(transform.position, camera_felow, Time.deltaTime * smooth);       
       	transform.LookAt(first.position);//lookat为注视物体的效果
    

    最后附上完整的两个代码:
    玩家:脚本直接添加在需要控制移动的角色身上

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class three_run : MonoBehaviour
    {
        public  float speed = 0.1f; //定义一个速度
        void Update()
        {
        	//控制物体的移动
            transform.Translate(0, 0, speed * Input.GetAxis("Vertical"));
            transform.Rotate(0, speed *20* Input.GetAxis("Horizontal"),0);
            //实现跳跃的效果
            if(Input.GetKeyDown(KeyCode.Space))
            {
                this.GetComponent<Rigidbody>().AddForce(Vector3.up * 6000);
            }
            //防止物体倒下(如果是角色控制器可以直接面板调整)
            if ((this.transform.eulerAngles.x > 90 && this.transform.eulerAngles.x < 270) || (this.transform.eulerAngles.z > 90 && this.transform.eulerAngles.z < 270))
            {
                transform.eulerAngles = new Vector3(0, 180, 0);
            }    
        }
    }
    

    相机:脚本添加到相机上面, 并将跟随的角色拖入

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class three_camera : MonoBehaviour
    {
        public Transform player;
        public float speed_one=10f;  
        public float speed_two=10f; //使用speed_one,speed_two可以调整玩家和相机的距离
        private float smooth = 1f;  
        void LateUpdate()
        {
            Vector3 camera_felow = player.position + Vector3.up * speed_one - player.forward * speed_two;
            transform.position = Vector3.Lerp(transform.position, camera_felow, Time.deltaTime * smooth);
            transform.LookAt(player.position);
        }
    }
    
    
    展开全文
  • using System.Collections; using System.Collections.Generic; using UnityEngine; public class CameraControl : MonoBehaviour { private GameObject Fcam; private GameObject Tcam;... Fcam = GameObject.Find(...
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class CameraControl : MonoBehaviour
    {
        private GameObject Fcam;
        private GameObject Tcam;
        
        private void Awake()
        {
            Fcam = GameObject.Find("FCamera");
            Tcam = GameObject.Find("TCamera");
            Fcam.SetActive(false);
        }
        void Update()
        {
            if(Input.GetKeyDown(KeyCode.F))
            {
              
                Fcam.SetActive(true);
                Tcam.SetActive(false);
            }
            if(Input.GetKeyDown(KeyCode.T))
            {
                Fcam.SetActive(false);
                Tcam.SetActive(true);
            }
        }
        // Start is called before the first frame update
       
    }
    

    如果使用FixedUpdate的话切换会出现按下按键不会响应的情况,暂时不清楚是为什么。

      void Update()
        {
            if(Input.GetKeyDown(KeyCode.Space))
            {
                flag = !flag;
                Fcam.SetActive(flag);
                Tcam.SetActive(!flag);
            }
            
        }
    
    update函数更改 变成单个按键来回切换
    
    展开全文
  • Unity第三人称的实现思路(一)实现步骤角色模型层级设置输入检测模型动画角色移动摄像机自由视角转向角色转向 实现步骤 在做角色控制器的时候脑海里应该明白一点,与外界做交互的是角色身上的碰撞器(不可见),...
  • 在开始使用Unity设计开发第三人称游戏之前,我们需要先获得合适的角色模型。Unity支持多种格式的模型,而当我们使用具有动画的模型时,往往选择fbx格式的模型 选择好模型后,就可以将模型导入到Unity中,然后我们需
  • unity第人称RPG游戏工程模版,使用此模版可以快速的制作出一款第一人称rpg游戏。资源真实有效,不蒙人!!
  • unity 人称相机的实现

    千次阅读 2020-09-06 20:36:55
    3,为人物添加控制代码 3.1首先就是控制人物的行走代码:使用角色控制器中的Move方法来实现控制 float _horizontal = Input.GetAxis("Horizontal"); float _vertical = Input.GetAxis("Vertical"); if (player....
  • 这是以前写的人物控制器(包含第一人称第三人称、漫游视角),这里做个记录,方便后续参考使用. 实现 模型使用官方的,操作使用鼠标右键控制视角,键盘WASD、Shift控制人物移动. 切换到漫游视角(God)时,有BUG,...
  • Unity3D 第三人称射击游戏代码
  • Unity实现第三人称视角

    千次阅读 2019-01-13 22:46:12
    最近耍了一波巫师3,突然想到能不能做个简单的第三人称视角的摄像机控制。不过这个摄像机控制目前还没有被卡视角的功能(就是那种根据地形调整摄像机距离)准备在后面几天实现卡视角功能。不过先来做个简单的第三...
  • Unity第三人称视角、移动控制改进

    千次阅读 2019-03-28 10:22:23
    public Vector3 temp = new Vector3(0, -0.7f, 0); private void CameraDefend() { RaycastHit hit; //从角色向相机发射一条射线 if (Physics.Linecast(this.target.position + temp, transform.position, out ...
  • 键盘WSAD控制行走 最简单的就是使用 Input.GetKeyDown() 函数接收键盘 值输入,但在这里还有更...Unity学习笔记10——旋转(四元数和欧拉角) 。 转载于:https://www.cnblogs.com/ChanWunsam/p/10018315.html
  • 使用Unity制作的简单的第三人称相机

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,378
精华内容 1,351
关键字:

unity第三人称