Unity环绕物体的摄像机,添加了遮挡的适应

第三人人称摄像机

支持的功能

设定目标后使用鼠标可以环绕目标点旋转,且会进行遮挡的适配,当有遮挡的时候会移动差值移动到没有遮挡的位置。

使用方式

vThirdPersonCamera 挂在与摄像机上然后为target赋值。
如果有需要检测遮挡的层级可以修改:cullingLayer
vExtensions 创建并复制代码即可,工具类。
如果高度有偏移就修改:height

请添加图片描述

using Invector;
using UnityEngine;public class vThirdPersonCamera : MonoBehaviour
{#region inspector properties    public Transform target;[Tooltip("Leep的速度")]public float smoothCameraRotation = 12f;[Header("遮挡的层")] public LayerMask cullingLayer = 1 << 0;[Tooltip("Debug purposes, lock the camera behind the character for better align the states")]public bool lockCamera;[Header("Camera Input")]public string rotateCameraXInput = "Mouse X";public string rotateCameraYInput = "Mouse Y";[Header("向右偏移角度")]public float rightOffset = 0f;[Header("距离目标的距离")]public float defaultDistance = 2.5f;public float height = 1.4f;public float smoothFollow = 10f;public float xMouseSensitivity = 3f;public float yMouseSensitivity = 3f;public float yMinLimit = -40f;public float yMaxLimit = 80f;#endregion#region hide properties    [HideInInspector]public int indexList, indexLookPoint;[HideInInspector]public float offSetPlayerPivot;[HideInInspector]public string currentStateName;[HideInInspector]public Transform currentTarget;[HideInInspector]public Vector2 movementSpeed;private Transform targetLookAt;private Vector3 currentTargetPos;private Vector3 lookPoint;private Vector3 current_cPos;private Vector3 desired_cPos;private Camera _camera;private float distance = 5f;private float mouseY = 0f;private float mouseX = 0f;private float currentHeight;private float cullingDistance;private float checkHeightRadius = 0.4f;private float clipPlaneMargin = 0f;private float forward = -1f;private float xMinLimit = -360f;private float xMaxLimit = 360f;private float cullingHeight = 0.2f;private float cullingMinDist = 0.1f;#endregionprivate void Start(){Init();}private void Init(){if (target == null)return;_camera = GetComponent<Camera>();currentTarget = target;currentTargetPos = new Vector3(currentTarget.position.x, currentTarget.position.y + offSetPlayerPivot, currentTarget.position.z);targetLookAt = new GameObject("targetLookAt").transform;targetLookAt.position = currentTarget.position;targetLookAt.hideFlags = HideFlags.HideInHierarchy;targetLookAt.rotation = currentTarget.rotation;mouseY = currentTarget.eulerAngles.x;mouseX = currentTarget.eulerAngles.y;distance = defaultDistance;currentHeight = height;}private void FixedUpdate(){if (target == null || targetLookAt == null){return;}//收到鼠标的输入var Y = Input.GetAxis(rotateCameraYInput);var X = Input.GetAxis(rotateCameraXInput);//旋转摄像机RotateCamera(X, Y);CameraMovement();}private void SetMainTarget(Transform newTarget){target = newTarget;currentTarget = newTarget;mouseY = currentTarget.rotation.eulerAngles.x;mouseX = currentTarget.rotation.eulerAngles.y;Init();}/// <summary>/// Camera Rotation behaviour/// </summary>/// <param name="x"></param>/// <param name="y"></param>private void RotateCamera(float x, float y){// free rotation mouseX += x * xMouseSensitivity;mouseY -= y * yMouseSensitivity;movementSpeed.x = x;movementSpeed.y = -y;if (!lockCamera){mouseY = Invector.vExtensions.ClampAngle(mouseY, yMinLimit, yMaxLimit);mouseX = Invector.vExtensions.ClampAngle(mouseX, xMinLimit, xMaxLimit);}else{mouseY = currentTarget.root.localEulerAngles.x;mouseX = currentTarget.root.localEulerAngles.y;}}/// <summary>/// Camera behaviour/// </summary>    private void CameraMovement(){if (currentTarget == null)return;distance = Mathf.Lerp(distance, defaultDistance, smoothFollow * Time.deltaTime);cullingDistance = Mathf.Lerp(cullingDistance, distance, Time.deltaTime);var camDir = (forward * targetLookAt.forward) + (rightOffset * targetLookAt.right);camDir = camDir.normalized;var targetPos = new Vector3(currentTarget.position.x, currentTarget.position.y + offSetPlayerPivot, currentTarget.position.z);currentTargetPos = targetPos;desired_cPos = targetPos + new Vector3(0, height, 0);current_cPos = currentTargetPos + new Vector3(0, currentHeight, 0);RaycastHit hitInfo;ClipPlanePoints planePoints = _camera.NearClipPlanePoints(current_cPos + (camDir * (distance)), clipPlaneMargin);ClipPlanePoints oldPoints = _camera.NearClipPlanePoints(desired_cPos + (camDir * distance), clipPlaneMargin);//Check if Height is not blocked if (Physics.SphereCast(targetPos, checkHeightRadius, Vector3.up, out hitInfo, cullingHeight + 0.2f, cullingLayer)){var t = hitInfo.distance - 0.2f;t -= height;t /= (cullingHeight - height);cullingHeight = Mathf.Lerp(height, cullingHeight, Mathf.Clamp(t, 0.0f, 1.0f));}//Check if desired target position is not blocked       if (CullingRayCast(desired_cPos, oldPoints, out hitInfo, distance + 0.2f, cullingLayer, Color.blue)){distance = hitInfo.distance - 0.2f;if (distance < defaultDistance){var t = hitInfo.distance;t -= cullingMinDist;t /= cullingMinDist;currentHeight = Mathf.Lerp(cullingHeight, height, Mathf.Clamp(t, 0.0f, 1.0f));current_cPos = currentTargetPos + new Vector3(0, currentHeight, 0);}}else{currentHeight = height;}//Check if target position with culling height applied is not blockedif (CullingRayCast(current_cPos, planePoints, out hitInfo, distance, cullingLayer, Color.cyan)) distance = Mathf.Clamp(cullingDistance, 0.0f, defaultDistance);var lookPoint = current_cPos + targetLookAt.forward * 2f;lookPoint += (targetLookAt.right * Vector3.Dot(camDir * (distance), targetLookAt.right));targetLookAt.position = current_cPos;Quaternion newRot = Quaternion.Euler(mouseY, mouseX, 0);targetLookAt.rotation = Quaternion.Slerp(targetLookAt.rotation, newRot, smoothCameraRotation * Time.deltaTime);transform.position = current_cPos + (camDir * (distance));var rotation = Quaternion.LookRotation((lookPoint) - transform.position);transform.rotation = rotation;movementSpeed = Vector2.zero;}/// <summary>/// Custom Raycast using NearClipPlanesPoints/// </summary>/// <param name="_to"></param>/// <param name="from"></param>/// <param name="hitInfo"></param>/// <param name="distance"></param>/// <param name="cullingLayer"></param>/// <returns></returns>private bool CullingRayCast(Vector3 from, Invector.ClipPlanePoints _to, out RaycastHit hitInfo, float distance, LayerMask cullingLayer, Color color){bool value = false;if (Physics.Raycast(from, _to.LowerLeft - from, out hitInfo, distance, cullingLayer)){value = true;cullingDistance = hitInfo.distance;}if (Physics.Raycast(from, _to.LowerRight - from, out hitInfo, distance, cullingLayer)){value = true;if (cullingDistance > hitInfo.distance) cullingDistance = hitInfo.distance;}if (Physics.Raycast(from, _to.UpperLeft - from, out hitInfo, distance, cullingLayer)){value = true;if (cullingDistance > hitInfo.distance) cullingDistance = hitInfo.distance;}if (Physics.Raycast(from, _to.UpperRight - from, out hitInfo, distance, cullingLayer)){value = true;if (cullingDistance > hitInfo.distance) cullingDistance = hitInfo.distance;}return hitInfo.collider && value;}
}
using System;
using System.Collections.Generic;
using UnityEngine;namespace Invector
{public static class vExtensions{public static T[] Append<T>(this T[] arrayInitial, T[] arrayToAppend){if (arrayToAppend == null){throw new ArgumentNullException("The appended object cannot be null");}if ((arrayInitial is string) || (arrayToAppend is string)){throw new ArgumentException("The argument must be an enumerable");}T[] ret = new T[arrayInitial.Length + arrayToAppend.Length];arrayInitial.CopyTo(ret, 0);arrayToAppend.CopyTo(ret, arrayInitial.Length);return ret;}public static T[] vToArray<T>(this List<T> list){T[] array = new T[list.Count];if (list == null || list.Count == 0) return array;for (int i = 0; i < list.Count; i++){array[i] = list[i];}return array;}public static float ClampAngle(float angle, float min, float max){do{if (angle < -360)angle += 360;if (angle > 360)angle -= 360;} while (angle < -360 || angle > 360);return Mathf.Clamp(angle, min, max);}public static ClipPlanePoints NearClipPlanePoints(this Camera camera, Vector3 pos, float clipPlaneMargin){var clipPlanePoints = new ClipPlanePoints();var transform = camera.transform;var halfFOV = (camera.fieldOfView / 2) * Mathf.Deg2Rad;var aspect = camera.aspect;var distance = camera.nearClipPlane;var height = distance * Mathf.Tan(halfFOV);var width = height * aspect;height *= 1 + clipPlaneMargin;width *= 1 + clipPlaneMargin;clipPlanePoints.LowerRight = pos + transform.right * width;clipPlanePoints.LowerRight -= transform.up * height;clipPlanePoints.LowerRight += transform.forward * distance;clipPlanePoints.LowerLeft = pos - transform.right * width;clipPlanePoints.LowerLeft -= transform.up * height;clipPlanePoints.LowerLeft += transform.forward * distance;clipPlanePoints.UpperRight = pos + transform.right * width;clipPlanePoints.UpperRight += transform.up * height;clipPlanePoints.UpperRight += transform.forward * distance;clipPlanePoints.UpperLeft = pos - transform.right * width;clipPlanePoints.UpperLeft += transform.up * height;clipPlanePoints.UpperLeft += transform.forward * distance;return clipPlanePoints;}}public struct ClipPlanePoints{public Vector3 UpperLeft;public Vector3 UpperRight;public Vector3 LowerLeft;public Vector3 LowerRight;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/16424.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

早餐店燃气安全岂能马虎?探头选择与年检必须到位

在现代都市生活中&#xff0c;早餐店作为人们日常生活中的重要一环&#xff0c;其安全性问题日益受到人们的关注。其中&#xff0c;燃气泄漏引发的火灾和爆炸事故尤为令人担忧。 因此&#xff0c;点式可燃气体报警器在早餐店中的应用显得尤为重要。 在这篇文章中&#xff0c;…

AIGC产业链上下游解析及常见名词

文章目录 AIGC上游产业链 - 基础层AIGC中游产业链 - 大模型层与工具层AIGC下游产业链 - 应用层AIGC产业链常见的名词表 在上一章节为大家介绍了 “大模型的不足与解决方案” &#xff0c;这一小节呢为大家针对AIGC的相关产业进行一个拆解&#xff0c;以及相关的一些专业名词做出…

【编程题-错题集】奇数位丢弃(模拟 - 规律)

牛客对应题目链接&#xff1a;奇数位丢弃_牛客题霸_牛客网 (nowcoder.com) 一、分析题目 通过⼀两个例子的模拟&#xff0c;可以发现&#xff1a;每次起始删除的下标都是 2 的次方。根据这个规律&#xff0c;找到最后⼀次删除的起始位置的下标即可。 二、代码 #include <io…

感知觉训练:解锁独立生活的钥匙

在日新月异的科技时代&#xff0c;一款名为“蝙蝠避障”的辅助软件以其独到之处&#xff0c;为盲人朋友的日常生活平添了诸多便利&#xff0c;不仅实现了实时避障&#xff0c;还通过拍照识别功能扩展了信息获取的边界。然而&#xff0c;科技辅助之外&#xff0c;提升盲人朋友的…

Android 深入系统源码探讨 Activity、Window 和 View 的关系与实践

文章目录 1、概括2、Android Window 设计2.1、Window 类2.2、PhoneWindow2.3、WindowManager2.4、ViewRootImpl2.5、DecorView 3、Android Activity 设计3.1、Activity的基本概念3.2.、Activity的生命周期3.3、Activity的内部结构 4、Android View 设计4.1、View的基本概念4.2、…

LangChain 0.2 - 构建RAG应用

本文翻译整理自&#xff1a;Build a Retrieval Augmented Generation (RAG) App https://python.langchain.com/v0.2/docs/tutorials/rag/ 文章目录 一、项目说明什么是 RAG &#xff1f;概念索引检索和[生成 二、预览三、详细演练1.索引&#xff1a;加载2. 索引&#xff1a;拆…

陈丽:人工智能赋能教育创新发展

5月20日&#xff0c;在顾明远先生莅临科大讯飞考察指导高端咨询会暨“人工智能与未来教育”的主题研讨会上&#xff0c;北京师范大学原副校长、中国教育技术协会副会长陈丽教授作了题为《人工智能赋能教育创新发展》的主旨报告。 &#xff08;以下内容根据陈丽教授在研讨会上的…

期权课程之第三节【什么是ITM,ATM,OTM】

我们可以根据正股行权价和股价关系 对期权进行一个分类 ITM 全称为In-the-money&#xff1a;行权后可以获得收益的期权 根据call和put &#xff0c;又可以分为两类 ITM call &#xff1a; 行权价< 股价 &#xff0c;IMT Put &#xff1a;行权价>股价 比如这个时候&a…

分享免费的手机清理软件app,一款国外开发的手机清理神器,让手机再战两年!

手机内存越来越大&#xff0c;软件却越来越占地方&#xff0c;就像微信这家伙&#xff0c;轻轻松松就吃了十几个G&#xff01; 害得阿星8128G的手机&#xff0c;本来想换新的&#xff0c;结果用了这款Avast Cleanup软件&#xff0c;瞬间感觉手机还能再战两年&#xff01; 注意…

云上聚智共创未来 | 移动云的项目实战,10分钟让你获得高度可玩的个人博客网站

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引入 随着互联网的发展各种以前看起来离我们比较遥远的词越来越近了&#xff0c;比如 云服务、大数据、区块链、容器这些听起来…

VMware ESXi 7.0U3q macOS Unlocker OEM BIOS 集成网卡驱动和 NVMe 驱动 (集成驱动版)

VMware ESXi 7.0U3q macOS Unlocker & OEM BIOS 集成网卡驱动和 NVMe 驱动 (集成驱动版) ESXi 7 U3 标准版集成 Intel 网卡、Realtek USB 网卡 和 NVMe 驱动 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-esxi-7-u3-sysin/&#xff0c;查看最新版。原创作品…

Android network — 进程指定网络发包

Android network — 进程指定网络发包 0. 前言1. 进程绑定网络1.1 App进程绑定网络1.2 Native进程绑定网络 2. 源码原理分析2.1 申请网络requestNetwork2.2 绑定网络 BindProcessToNetwork 3. 总结 0. 前言 在android 中&#xff0c;一个app使用网络&#xff0c;需要在manifest…

X-CSV-Reader:一个使用Rust实现CSV命令行读取器

&#x1f388;效果演示 ⚡️快速上手 依赖导入&#xff1a; cargo add csv读取实现&#xff1a; use std::error::Error; use std::fs::File; use std::path::Path;fn read_csv<P: AsRef<Path>>(filename: P) -> Result<(), Box<dyn Error>> {le…

【Java面试】二、Redis篇(中)

文章目录 1、Redis持久化1.1 RDB1.2 AOF1.3 RDB与AOF的对比 2、数据过期策略&#xff08;删除策略&#xff09;2.1 惰性删除2.2 定期删除 3、数据淘汰策略4、主从复制4.1 主从全量同步4.2 增量同步 5、哨兵模式5.1 服务状态监控5.2 哨兵选主规则5.3 哨兵模式下&#xff0c;Redi…

商标注册申请名称的概率,多想名称选通过率好的!

近日给深圳客户申请的商标初审下来了&#xff0c;两个类别都下的初审&#xff0c;和当初的判断基本一致&#xff0c;普推知产老杨当时沟通说需要做担保申请注册也可以&#xff0c;后面选择了管家注册&#xff0c;最近大量的帮客户检索商标名称&#xff0c;分享下经验。 两个字基…

【PB案例学习笔记】-09滚动条使用

写在前面 这是PB案例学习笔记系列文章的第8篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gitee…

新书推荐:7.1 do while语句

本节必须掌握的知识点&#xff1a; 示例二十二 代码分析 汇编解析 ■do while语句其语法形式&#xff1a; do{ 语句块; }while(表达式) ■语法解析&#xff1a; ●执行do循环体内的语句块&#xff1b; ●判断while语句里的表达式&#xff0c;表达式为真继续下次循环&#…

stm32学习-串口收发(HEX/文本)数据包

串口收发HEX数据包 接线 TXDPA10RXDPA9按键PB1 配置流程 定义为固定包长&#xff0c;含包头包尾&#xff08;其中包头为0xFF&#xff0c;载荷数据固定为4字节&#xff0c;包围为0xFE&#xff09; 注意&#xff1a;固定包长/可变包长&#xff0c;或者包头包围是什么&#xf…

UI控件与视图层次:探索界面的无限可能

[OC]UI学习笔记 文章目录 [OC]UI学习笔记视图和视图层次结构CGRectUILabelUIButtonUIView控件UIView的层级关系UIWindow定时器和视图移动UISwitch进度条和滑动条控件步进器和分栏控件警告对话框与等待指示器UITextField 视图和视图层次结构 Objective-C中的UI编程主要围绕视图…

C++的哈希 哈希表 哈希桶

目录 Unordered系列关联式容器 什么是哈希 哈希表 闭散列 载荷因子α 扩容 查找 删除 字符串哈希算法 最终代码 开散列 插入 查找 删除 最终代码 完整代码 Unordered系列关联式容器 C98中&#xff0c;STL提供了底层为红黑树结构的一系列关联式容器&#xff0…