热门标签 | 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();}}
}



推荐阅读
  • 本文深入探讨了HTTP请求和响应对象的使用,详细介绍了如何通过响应对象向客户端发送数据、处理中文乱码问题以及常见的HTTP状态码。此外,还涵盖了文件下载、请求重定向、请求转发等高级功能。 ... [详细]
  • XNA 3.0 游戏编程:从 XML 文件加载数据
    本文介绍如何在 XNA 3.0 游戏项目中从 XML 文件加载数据。我们将探讨如何将 XML 数据序列化为二进制文件,并通过内容管道加载到游戏中。此外,还会涉及自定义类型读取器和写入器的实现。 ... [详细]
  • PHP 编程疑难解析与知识点汇总
    本文详细解答了 PHP 编程中的常见问题,并提供了丰富的代码示例和解决方案,帮助开发者更好地理解和应用 PHP 知识。 ... [详细]
  • 本文详细介绍了Java中org.neo4j.helpers.collection.Iterators.single()方法的功能、使用场景及代码示例,帮助开发者更好地理解和应用该方法。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • 深入理解Java泛型:JDK 5的新特性
    本文详细介绍了Java泛型的概念及其在JDK 5中的应用,通过具体代码示例解释了泛型的引入、作用和优势。同时,探讨了泛型类、泛型方法和泛型接口的实现,并深入讲解了通配符的使用。 ... [详细]
  • 在Java中,this是一个引用当前对象的关键字。如何通过this获取并显示其所指向的对象的属性和方法?本文详细解释了this的用法及其背后的原理。 ... [详细]
  • Qt QTableView 内嵌控件的实现方法
    本文详细介绍了在 Qt QTableView 中嵌入控件的多种方法,包括使用 QItemDelegate、setIndexWidget 和 setIndexWidget 结合布局管理器。每种方法都有其适用场景和优缺点。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文详细介绍了如何使用 Yii2 的 GridView 组件在列表页面实现数据的直接编辑功能。通过具体的代码示例和步骤,帮助开发者快速掌握这一实用技巧。 ... [详细]
  • 前言--页数多了以后需要指定到某一页(只做了功能,样式没有细调)html ... [详细]
  • 本文深入探讨了 Java 中的 Serializable 接口,解释了其实现机制、用途及注意事项,帮助开发者更好地理解和使用序列化功能。 ... [详细]
  • 本文介绍如何使用 Android 的 Canvas 和 View 组件创建一个简单的绘图板应用程序,支持触摸绘画和保存图片功能。 ... [详细]
  • 社交网络中的级联行为 ... [详细]
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社区 版权所有