热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

可直接使用的unity第三人称自由视角相机脚本

使用方法:将要控制的角色拖到TargetBody,将相机的焦点拖到CamerPivot,,建议CameraPivot是一个放在TargetBody下的子物体,并且位置应该是在Tar

使用方法:


将要控制的角色拖到TargetBody,将相机的焦点拖到CamerPivot,,建议CameraPivot是一个放在TargetBody下的子物体,并且位置应该是在TargetBody的头部.

注意:此脚本自动忽略"Ignore Raycast"层和"Mob"对相机视野的遮挡,也就是说,当相机被带有这两个层之一的物体遮挡时,相机不会自动移动到遮挡物之前,这是用于设置一些不应该触发相机防遮挡的物体用的,比如说怪物和玻璃等.




包含的功能有:

            1.控制人物转向,按下WASD按键时可直接控制目标物体(角色)插值转向.

            2.垂直视角限制,避免发生相机翻转的问题

            3.相机防遮挡


原理说明:

            1.通过四元数插值控制人物转向

            2.在unity中,水平视角的x欧拉角为0,向上直至垂直的欧拉角实际上为(0-90),而向下直至垂直的欧拉角实际上是(360-270).脚本中maxEvelation为最大仰角,maxDepression为最大俯角,假设此处最大俯角和最大仰角都为80,则在最大仰角达到80以上时,判断鼠标的输入,只有当鼠标输入为向下滑动时(此时鼠标的输入Input.getAxis("Mouse Y")>0)才允许滑动.同理,当最大俯角为80(360-80&#61;280)时,此时实际上的欧拉角为280-270,所以当x<280时,则只允许向上滑动(此时鼠标的输入Input.getAxis("Mouse Y")<0))

如图所示:


代码:

float eulerX &#61; transform.localEulerAngles.x;//相机的x欧拉角,也就是垂直方向.float inputY &#61; Input.GetAxis("Mouse Y");//垂直视野限制transform.RotateAround(CameraPivot.transform.position, CameraPivot.transform.up, rotateSpeed * Input.GetAxis("Mouse X"));if (eulerX > maxEvelation && eulerX <90){if (inputY > 0)transform.RotateAround(CameraPivot.transform.position, transform.right, -rotateSpeed * inputY);}else if (eulerX <360-maxDepression && eulerX > 270){if (inputY <0)transform.RotateAround(CameraPivot.transform.position, transform.right, -rotateSpeed * inputY);}else{transform.RotateAround(CameraPivot.transform.position, transform.right, -rotateSpeed * inputY);}

            3.相机防遮挡


先提前检测(自己)相机的应该在位置,从相机目标向相机发射一条射线,如果碰撞了,说明即将碰撞.则直接把位置调到碰撞点位置,但此时的位置正好在平面里,相机的一部分视野会透视,所以应该在再向相机靠近一点.

transform.position &#61; CameraPivot.transform.position &#43; (wallHit - CameraPivot.transform.position) * 0.8f;

注意,这里是提前判断,如果是直接更新相机位置后发现被遮挡再把位置移动到碰撞点,相机就会在碰撞点和应在位置来回切换.效果自然是有问题的.

最后说明相机的位置更新方法:

        相机的旋转通过RotateAround()来以目标相机焦点为中心进行旋转.旋转后根据当前位置和相机焦点位置相减得到方向向量,然后用相机焦点位置&#43;方向向量的单位向量*设置的距相机焦点的距离.

代码表示:

           offset &#61; transform.position - CameraPivot.transform.position;offset &#61; offset.normalized * freeDistance;transform.position &#61; CameraPivot.transform.position &#43; offset;



完整代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class FreeTPSCamera : MonoBehaviour {[Header("相机距离")]public float freeDistance &#61; 2;[Header("相机最近距离")]public float minDistance &#61; 0.5f;[Header("相机最远距离")]public float maxDistance &#61; 20;[Header("是否可控制相机距离(鼠标中键)")]public bool canControlDistance&#61;true;[Header("更改相机距离的速度")]public float distanceSpeed &#61; 1;[Header("视角灵敏度")]public float rotateSpeed &#61; 1;[Header("物体转向插值(灵敏度,取值为0到1)")]public float TargetBodyRotateLerp &#61; 0.3f;[Header("需要转向的物体")]public GameObject TargetBody;//此脚本能操作转向的物体[Header("相机焦点物体")]public GameObject CameraPivot;//相机焦点物体 [Header("&#61;&#61;&#61;锁敌&#61;&#61;&#61;")]public GameObject lockTarget&#61;null;public float lockSlerp&#61;1;public GameObject lockMark;private bool marked;[Header("是否可控制物体转向")]public bool CanControlDirection &#61; true;[Header("俯角(0-89)")]public float maxDepression&#61;80;[Header("仰角(0-89)")]public float maxEvelation&#61;80;private Vector3 PredictCameraPosition;private Vector3 offset;private Vector3 wallHit;private GameObject tmpMark;// Use this for initializationvoid Start () {offset &#61; transform.position - CameraPivot.transform.position;if (TargetBody &#61;&#61; null){TargetBody &#61; GameObject.FindGameObjectWithTag("Player");Debug.Log("未绑定目标物体&#xff0c;默认替换为Player标签的物体");}if (!CameraPivot){Debug.LogError("未绑定相机焦点物体");}}void LockTarget(){if(lockTarget){lockTarget &#61; null;marked &#61; false;Destroy(tmpMark);return;}Vector3 top &#61; transform.position &#43; new Vector3(0, 1, 0)&#43;transform.forward*5;LayerMask mask &#61; (1 < maxDepression && eulerX <90)//当向上角度越界时{if (inputY > 0)//如果鼠标时在向下滑动transform.RotateAround(CameraPivot.transform.position, Vector3.right, -rotateSpeed * inputY);//允许滑动}else if (eulerX <360-maxEvelation && eulerX > 270){if (inputY <0)transform.RotateAround(CameraPivot.transform.position, Vector3.right, -rotateSpeed * inputY);}else//角度正常时{transform.RotateAround(CameraPivot.transform.position, Vector3.right, -rotateSpeed * inputY);}}if (lockTarget){offset &#61; CameraPivot.transform.position - (lockTarget.transform.position);}else{offset &#61; transform.position - CameraPivot.transform.position;//以上方向发生了变化,记录新的方向向量}offset &#61; offset.normalized * freeDistance;///在一次FixedUpdate中,随时记录新的旋转后的位置,然后得到方向,然后判断是否即将被遮挡,如果要被遮挡,将相机移动到计算后的不会被遮挡的位置///如果不会被遮挡,则更新位置为相机焦点位置&#43;方向的单位向量*距离///if (Inwall())//预测会被遮挡{//print("Inwall");transform.position &#61; CameraPivot.transform.position &#43; (wallHit - CameraPivot.transform.position) * 0.8f;return;}else{transform.position &#61; CameraPivot.transform.position &#43; offset;}}// Update is called once per framevoid FixedUpdate () {FreeCamera();if(lockTarget){if (!marked){tmpMark &#61; Instantiate(lockMark, lockTarget.transform.position &#43; new Vector3(0, 2.5f, 0), transform.rotation);tmpMark.transform.forward &#61; -Vector3.up;marked &#61; true;}else{tmpMark.transform.position &#61; lockTarget.transform.position &#43; new Vector3(0, 2.5f, 0);//tmpMark.transform.forward&#61; -transform.up;tmpMark.transform.Rotate(Vector3.up *30* Time.fixedDeltaTime,Space.World);}}}private void Update(){if(Input.GetKeyDown(KeyCode.F)){LockTarget();}}
}



推荐阅读
  • 本文详细介绍了 `org.apache.tinkerpop.gremlin.structure.VertexProperty` 类中的 `key()` 方法,并提供了多个实际应用的代码示例。通过这些示例,读者可以更好地理解该方法在图数据库操作中的具体用途。 ... [详细]
  • 本文探讨了如何通过Service Locator模式来简化和优化在B/S架构中的服务命名访问,特别是对于需要频繁访问的服务,如JNDI和XMLNS。该模式通过缓存机制减少了重复查找的成本,并提供了对多种服务的统一访问接口。 ... [详细]
  • 在Effective Java第三版中,建议在方法返回类型中优先考虑使用Collection而非Stream,以提高代码的灵活性和兼容性。 ... [详细]
  • 本文介绍了如何使用 VBScript 脚本在 IE7 上安装 Windows 序列号的方法。对于使用超级解霸的用户,如果遇到 .vbs 文件无法正常运行的问题,文中也提供了相应的解决办法。 ... [详细]
  • 本文探讨了如何在游戏启动画面中移除广告,特别是在游戏数据加载期间(大约5-6秒)广告会短暂显示的问题。通过调整XML布局和代码逻辑,可以实现广告的延迟加载或完全移除。 ... [详细]
  • 长期从事ABAP开发工作的专业人士,在面对行业新趋势时,往往需要重新审视自己的发展方向。本文探讨了几位资深专家对ABAP未来走向的看法,以及开发者应如何调整技能以适应新的技术环境。 ... [详细]
  • 机器学习(ML)三之多层感知机
    深度学习主要关注多层模型,现在以多层感知机(multilayerperceptron,MLP)为例,介绍多层神经网络的概念。隐藏层多层感知机在单层神经网络的基础上引入了一到多个隐藏 ... [详细]
  • RTThread线程间通信
    线程中通信在裸机编程中,经常会使用全局变量进行功能间的通信,如某些功能可能由于一些操作而改变全局变量的值,另一个功能对此全局变量进行读取& ... [详细]
  • php三角形面积,335宝石大全
    php三角形面积,335宝石大全 ... [详细]
  • 在Java开发中,如何利用ProcessBuilder类调用外部程序是一个常见的需求。本文将详细介绍ProcessBuilder类的使用方法,并提供示例代码帮助你更好地理解和应用。 ... [详细]
  • importjava.io.*;importjava.util.*;publicclass五子棋游戏{staticintm1;staticintn1;staticfinalintS ... [详细]
  • 本题要求计算一组正整数的最小公倍数(LCM)。输入包括多组测试数据,每组数据首先给出一个正整数n,随后是n个正整数。 ... [详细]
  • 本文档详细介绍了软通动力Java开发工程师职位的笔试题目,涵盖了Java基础、集合框架、JDBC、JSP等内容,并提供了详细的答案解析。 ... [详细]
  • C# 中创建和执行存储过程的方法
    本文详细介绍了如何使用 C# 创建和调用 SQL Server 存储过程,包括连接数据库、定义命令类型、设置参数等步骤。 ... [详细]
  • 在Java开发中,保护代码安全是一个重要的课题。由于Java字节码容易被反编译,因此使用代码混淆工具如ProGuard变得尤为重要。本文将详细介绍如何使用ProGuard进行代码混淆,以及其基本原理和常见问题。 ... [详细]
author-avatar
mobiledu2502917563
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有