tps视角 unity3d_unity tps - CSDN
  • 在游戏开发中,第三人称相机(FPS)是一个很常见的需求,我们今天来实现该功能。 这种相机跟随,是第三人称角度看向对象的,也就是一直看向对象的后面,如一直显示玩家的后背。

    在游戏开发中,第三人称相机(FPS)是一个很常见的需求,我们今天来实现该功能。

    这种相机跟随,是第三人称角度看向对象的,也就是一直看向对象的后面,如一直显示玩家的后背。

    挂载到相机上即可,代码如下:

    using UnityEngine;
    using System.Collections;
    //相机一直拍摄主角的后背
    public class CameraFlow_TPS : MonoBehaviour {
    	//目标对象
    	public Transform target;
    	//垂直方向偏移
    	public float distanceUp=15f;
    	//水平方向偏移
    	public float distanceAway = 10f;
    	//位置平滑值
    	public float posSmooth = 2f;
    	//Fov平滑值
    	public float fovSmooth = 5f;
    	void Update () {
    		// 鼠标轴控制相机的远近
    		if ((Input.mouseScrollDelta.y < 0 && Camera.main.fieldOfView >= 3) || Input.mouseScrollDelta.y > 0 && Camera.main.fieldOfView <= 80)
    		{
    			Camera.main.fieldOfView += Input.mouseScrollDelta.y * fovSmooth * Time.deltaTime;
    		}
    	}
    	void LateUpdate()
    	{
    		//相机的位置
    		Vector3 targetPos = target.position + Vector3.up * distanceUp - target.forward * distanceAway;
    		transform.position=Vector3.Lerp(transform.position,targetPos,Time.deltaTime*posSmooth);
    		//相机的角度
    		transform.LookAt(target.position);
    	}
    }


    展开全文
  • 本课程以一款小型的第三人称射击游戏为案例,手把手教你如何搭建一款3D射击游戏的雏形。麻雀虽小,五脏俱全,这款小游戏难度不大,但第三人称射击游戏该有的模块、功能它都有涉及。通过案例游戏的开发,学习者可以对...
    Unity3D实战入门之第三人称射击游戏(TPS)—327人已学习 
    课程介绍    
    201804171007053416.jpg
        这是一套第三人称射击游戏开发的入门基础课程。 本课程以一款小型的第三人称射击游戏为案例,手把手教你如何搭建一款3D射击游戏的雏形。麻雀虽小,五脏俱全,这款小游戏难度不大,但第三人称射击游戏该有的模块、功能它都有涉及。通过案例游戏的开发,学习者可以对第三人称射击游戏入门,对后续复杂大中型3D游戏的开发有实际的借鉴意义。 这套课程,对初次接触3D射击类游戏的学习者而言,是一块极好的跳板。
    课程收益
        本课程从零开始,带领学习者逐步完成一款小型的第三人称射击游戏。向3D射击类游戏的初学者展示完整项目的制作流程和设计思路,对该类游戏的常用知识点、必备功能模块、代码结构进行详细讲解,让学习者能够快速搭建一款3D射击游戏的雏形,为后续中、大型游戏的开发打下良好基础。
    讲师介绍
        伍晓波更多讲师课程
        6年程序开发经验,精通C/C++/C#编程。 曾担任过Unity3d游戏开发主程和Unity3d游戏开发讲师,熟悉Unity3d的UI系统、物理引擎、动画系统、粒子系统等,对小型、中型游戏有丰富的项目经验,益智类、敏捷类、塔防、RPG均有广泛涉猎。
    课程大纲
        1.3D游戏场景的创建  23:35
        2.玩家模型的导入和动画片段的分割  17:41
        3.玩家的移动控制  26:49
        4.玩家移动的动画切换及摄像机跟随  17:31
        5.玩家的朝向和旋转控制  19:13
        6.线渲染组件(LineRenderer)的基本属性和使用方法  33:19
        7.使用线渲染组件(LineRenderer)实现玩家普通攻击的激光效果  18:48
        8.玩家枪口和射击点粒子效果的添加  28:44
        9.怪物模型的导入和动画片段的分割  18:31
        10.Unity3D的寻路导航(Navigation)系统(一)  30:38
        11.Unity3D的寻路导航(Navigation)系统(二)  22:45
        12.玩家对怪物普通攻击的逻辑实现  25:09
        13.怪物对玩家攻击的逻辑实现  16:39
        14.玩家和怪物血条的添加  17:17
        15.怪物的生成控制  16:33
        16.玩家特殊攻击的实现(一)  28:19
        17.玩家特殊攻击的实现(二)  19:19
        18.屏幕小地图的制作  20:00
        19.简易游戏手柄的制作(一)  29:27
        20.简易游戏手柄的制作(二)  10:31
        21.移动设备上多点触控的实现(一)  18:38
        22.移动设备上多点触控的实现(二)  13:38
    大家可以点击【查看详情】查看我的课程
    展开全文
  • 参考了https://www.cnblogs.com/idemo/p/9595935.html的代码,用自己的思路修改了一下,注释还算详细 效果: 代码如下: ...using System.Collections;...using System.Collections.Generic;... public Tran

    参考了https://www.cnblogs.com/idemo/p/9595935.html的代码,用自己的思路修改了一下,注释还算详细

    效果:

    代码如下:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class TPSFreeLookCamara : MonoBehaviour
    {
        [Header("跟随的目标对象")]
        public Transform followTarget = null;
    
        [Header("旋转角度参数")]
        public Vector2 rotate;
    
        [Header("相机旋转速度")]
        public float rotateSpeed = 2;
    
        [Header("相机跟随移动速度")]
        public float moveSpeed = 10;
    
        [Header("仰角Y最大值")]
        public float maxY = 80;
    
        [Header("仰角Y最小值")]
        public float minY = -80;
    
        [Header("视口大小")]
        public float viewSize = 60;
    
        [Header("默认角度")]
        public float defaultAngle = -135;
    
        [Header("离目标对象的距离")]
        public float radius = 3;
    
        [Header("离目标对象的高度")]
        public float height = 1.5f;
    
        [Header("鼠标是否可见")]
        public bool visiable = false;
        [Header("鼠标限制范围")]
        public CursorLockMode lockMode = CursorLockMode.Confined;
    
    
        private Camera controlCamara;// 控制的相机
    
    
        void Start()
        {
            controlCamara = this.GetComponent<Camera>();
        }
    
        void FixedUpdate()
        {
            float inputX = Input.GetAxis("Mouse X");
            float inputY = Input.GetAxis("Mouse Y");
            rotate.x += inputX * rotateSpeed;
            rotate.y += inputY * rotateSpeed;
            viewSize += -Input.mouseScrollDelta.y * 3;
    
    
    
            // 一些约束,不用管
            if (viewSize < 1)
            {
                viewSize = 1;
            }
            else if (viewSize > 60)
            {
                viewSize = 60;
            }
    
            // 让这个x值不是很大
            if (rotate.x >= 360 || rotate.x <= -360)
            {
                rotate.x = 0;
            }
    
            // 仰角和俯角的角度限制
            if (rotate.y < minY)
            {
                rotate.y = minY;
            }
            else if (rotate.y > maxY)
            {
                rotate.y = maxY;
            }
            controlCamara.fieldOfView = viewSize;
            Cursor.visible = visiable;
            Cursor.lockState = lockMode;
    
    
        }
    
        void LateUpdate()
        {
            Transform self = controlCamara.transform;
            Vector3 startPosition = self.position;// 相机开始位置
            Vector3 endPosition;// 相机最终位置
    
            Vector3 targetPos = followTarget.position;
            targetPos.y += height;
    
            //旋转y轴,左右滑动
            Vector2 v1 = CalcAbsolutePoint(rotate.x, radius);
            endPosition = targetPos + new Vector3(v1.x, 0, v1.y);
    
            //相机的观察点
            Vector2 v2 = CalcAbsolutePoint(rotate.x + defaultAngle, 1);
            Vector3 viewPoint = new Vector3(v2.x, 0, v2.y) + targetPos;
            //计算2点之间的距离
            float dist = Vector3.Distance(endPosition, viewPoint);
            Vector2 v3 = CalcAbsolutePoint(rotate.y, dist);
            endPosition += new Vector3(0, v3.y, 0);
    
            // 防相机穿墙检测
            // 定义一条射线
            RaycastHit hit;
            if (Physics.Linecast(targetPos, endPosition, out hit))
            {
                string name = hit.collider.gameObject.tag;
                if (name != "MainCamera" || name != "Player")
                {
                    //如果射线碰撞的不是相机,那么就取得射线碰撞点到玩家的距离
                    endPosition = hit.point - (endPosition - hit.point).normalized * 0.2f;
                }
            }
            //self.position = endPosition;
            self.position = Vector3.Lerp(startPosition, endPosition, Time.deltaTime * moveSpeed);
    
            Quaternion rotateQ = Quaternion.LookRotation(viewPoint - endPosition);
            self.rotation = Quaternion.Slerp(transform.rotation, rotateQ, Time.deltaTime * moveSpeed);
            //self.rotation = rotateQ;
        }
    
        // 用角度计算圆的XY
        public static Vector2 CalcAbsolutePoint(float angle, float dist)
        {
            // 弧度 等于 角度*(PI/180)
            float radian = -angle * (Mathf.PI / 180);
            float x = dist * Mathf.Cos(radian);
            float y = dist * Mathf.Sin(radian);
            return new Vector2(x, y);
        }
    }
    

     

    展开全文
  • 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

    展开全文
  • 基于Unity3D魔方实现   一、背景:  魔方,英文名为Rubik's Cube,又叫魔术方块,也称鲁比克方块。是匈牙利布达佩斯建筑学院厄尔诺·鲁比克教授在1974年发明的。三阶魔方系由富有弹性的硬塑料制成的6面正方体...
  • Cinemachine作为Unity官方的解决方案,致力于帮助开发者高效的实现游戏中的相机系统。下面就就给大家介绍下Cinemachine的官方实例。 1、2D Camera 搭建一个快速场景,MainCamera选择Orthographic。在...
  • Unity面试题》 版本 作者 参与者 完成日期 备注 Unity_JobView_V01_1.0 严立钻   2018.07.26  ...
  • FPS相机

    2015-11-07 21:37:40
    提要FPS常用于FPS游戏中,Minecraft默认的视角也是第一人称的。在网上招了半天也没有找到比较好的现成的,还是自己写好了。相机建模不管是FPS相机还是TPS的相机,都包含了相机的两个操作,Yaw和PitchRoll在一些特殊...
  • Unity最大的优点:可视化的工作流和跨平台的支持。Unity基于component的设计,使得一个component能被重复的使用。 Unity的缺点有:查找功能不够强大,有时候在项目中查找脚本比较麻烦;不支持链接到第三方库,要...
  • 同学们在游戏入行过程中需要在技术上的准备,包括必修的知识准备和编程功底准备,以及加分项的玩游戏以及游戏项目和领域知识方面的准备。
  • 模型和骨骼动画仅仅是开启3D游戏的敲门砖,置入基于摄像机的场景设计方能呈现最完美的3D游戏。本节,我们依旧从简单着手,一步步创建基于模型的3D游戏场景。 《XNA4.0学习指南(中文)》是一本绝对值得一看的好书,...
  • 还记得2018年所有3D小游戏加起来才200来款,2019年刚刚过去,小编已体验了近千款3D小游戏(超过90%基于LayaAir引擎研发),相信还有不少3D游戏小编还没有体验到。在3D小...
  • 程序员11月书讯

    2016-11-14 15:20:07
    10月书讯中奖名单:_iorilan、极简、逆流的鱼yuiop、mlcjq、Marksinoberg。...下期书讯更新时,会在本期的书讯评论中选出若干优秀评论,获奖者可任选图灵书讯中的图书一本。11月有13本新书上市,有重磅推荐3本,还有...
  • 计算机图书

    2016-11-25 09:37:38
    10月书讯中奖名单:_iorilan、极简、逆流的鱼yuiop、mlcjq、Marksinoberg。 ...下期书讯更新时,会在本期的书讯评论中选出若干优秀评论,获奖者可任选图灵书讯中的图书一本。 11月有13本新书上市,有重磅推荐3本,...
  • 昨天参加了网易游戏学院的公开日《技术进步引发的灵感革命》,干货十足,期间做了一些笔记,分享一下。 在广州好几年了,也是第一次到科韵路信息港,下午去的早了些就在周围逛了逛,熟悉了一下地形和周边的小吃...
  • 就在近期的Unite Europe 2017 中,有两个talk比较有意思,分别是The AAA graphics of Spellsouls: achieving 60FPS on mobile 和 The Real-time VFX of Spellsouls,恰好最近在做一些优化方面的工作,就一起学习了...
  • 1. 负责移动游戏产品的测试⼯作,涉及功能、性能、兼容等纬度; 2. 负责游戏测试⽤例的编写、review、持续优化,拓展测试⼴度和深度; 3. 负责运营期游戏项⽬的质量反馈收集、分析和跟进;...
1 2
收藏数 24
精华内容 9
关键字:

tps视角 unity3d