当前位置: 首页 > news >正文

Unity组件开发--相机跟随角色和旋转

1.相机跟随组件,节点:

2.相机跟随组件脚本:

using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Burst.Intrinsics;
using UnityEngine;
using UnityEngine.UI;public class CameraFollow : Singleton<CameraFollow> {public Transform firstAngleTarget; //第一人称跟随的目标public Transform threeAngleTarget; //第三人称跟随的目标public float radius;public float polarDeg;public float elevationDeg;private Transform target;public bool isLookAt;public float lerpSpeed = 0;//是否第一人称视角private bool isFirstAngle = false;LayerMask mask;/// <summary>/// 极坐标转换成笛卡尔坐标/// </summary>/// <param name="radius"></param>/// <param name="angle"></param>/// <returns></returns>/// private bool isDriven;private void Awake() {EventManager.Instance.AddListener(EventName.PlayerDriving, (s, e) => {  //开车事件触发var arg = e as PlayerDrivingEventArgs;if (arg.driveObj && arg.driveObj.GetComponent<VehicleBase>() && arg.driveObj.GetComponent<VehicleBase>().driveCam) {gameObject.SetActive(false);}else {isDriven = true;}});EventManager.Instance.AddListener(EventName.PlayerDown, (s, e) => {  //开车事件触发var arg = e as PlayerDrivingEventArgs;                                              //isDriven = false;if (arg.driveObj && arg.driveObj.GetComponent<VehicleBase>() && arg.driveObj.GetComponent<VehicleBase>().driveCam) {gameObject.SetActive(true);}else {isDriven = false;}});}private void Start(){mask.value = (1 << LayerMask.NameToLayer("Ground")) | (1 << LayerMask.NameToLayer("Wall"));this.target = this.threeAngleTarget;EventManager.Instance.AddListener(EventName.ChangeAngle, changeAngle);var offset = SphericalToCartesian(radius, DegreeToRadian(polarDeg), DegreeToRadian(elevationDeg));//transform.position = Vector3.Lerp(transform.position, target.position + offset, Time.deltaTime * 10);transform.position = target.position + offset;}private void changeAngle(object sender, EventArgs e) {var data = e as AngleChangeEventArgs;if (data != null) {if (data.angleIndex == 1) {this.target = this.firstAngleTarget;this.radius = 0;isFirstAngle = true;transform.position = target.position;transform.forward = target.forward;} else if (data.angleIndex == 3) {this.target = this.threeAngleTarget;this.radius = 6;isFirstAngle = false;}}Debug.Log("摄像机视角改变" + e);}public Vector2 PolarToCartesian(float radius, float angle) {float x = radius * Mathf.Cos(angle);float y = radius * Mathf.Sin(angle);return new Vector2(x, y);}public static float DegreeToRadian(float degree) {return degree * Mathf.Deg2Rad;}public static Vector3 SphericalToCartesian(float radius, float polar, float elevation) {float a = radius * Mathf.Cos(elevation);float x = a * Mathf.Cos(polar);float y = radius * Mathf.Sin(elevation);float z = a * Mathf.Sin(polar);return new Vector3(x, y, z);}public static void CartesianToSpherical(Vector3 cartesian, out float radius, out float polar, out float elevation) {radius = Mathf.Sqrt(Mathf.Pow(cartesian.x, 2) + Mathf.Pow(cartesian.y, 2) + Mathf.Pow(cartesian.z, 2));polar = Mathf.Atan2(cartesian.z, cartesian.x);elevation = Mathf.Asin(cartesian.y / radius);}void LateUpdate() {if (isDriven) {var offset = SphericalToCartesian(6f, DegreeToRadian(270), DegreeToRadian(-15));transform.position = Vector3.Lerp(transform.position, target.TransformPoint(target.localPosition + offset), Time.deltaTime * 3);//transform.position = target.TransformPoint(target.localPosition + offset);transform.LookAt(target);return;}if (isFirstAngle) {//var offset = SphericalToCartesian(radius, DegreeToRadian(polarDeg), DegreeToRadian(elevationDeg));target.eulerAngles = new Vector3(elevationDeg, -polarDeg, 0);transform.forward = target.forward;transform.position = target.position;if (PlayerController.Instance.animator) {var euler = PlayerController.Instance.animator.transform.eulerAngles;PlayerController.Instance.animator.transform.eulerAngles = new Vector3(euler.x, target.eulerAngles.y, euler.z);}}else {Ctrl_Cam_Move();CtrThird();}}void CtrThird() {var offset = SphericalToCartesian(radius, DegreeToRadian(polarDeg), DegreeToRadian(elevationDeg));//更新相机位置//transform.position = target.position + offset;transform.position = target.position + offset;//TODO:做成CAMERA NEAR OBJ,进行隐藏if (PlayerController.Instance.animator != null) PlayerController.Instance.animator.gameObject.SetActive(true);//计算完位置之后处理让镜头不会穿墙Vector3 direction = transform.position - target.position;float distance = direction.magnitude;direction.Normalize();RaycastHit hit;if (Physics.Raycast(target.transform.position, direction, out hit, distance, mask.value)) {var dstPos = hit.point - distance * distance * direction * 0.01f;var offsetDis = target.position - dstPos;CartesianToSpherical(offsetDis, out var compareRadius, out _, out _);if (compareRadius < 1f)  {if (PlayerController.Instance.animator != null)  PlayerController.Instance.animator.gameObject.SetActive(false);}transform.position = dstPos;}transform.eulerAngles = target.eulerAngles;if (isLookAt) {transform.LookAt(target);}}public void Ctrl_Cam_Move(){if (EditorModel.Instance.CurrentUnlock != null) { //解锁其他物体的时候,镜头不动, 要前后移动的是物体return;}if (Input.GetAxis("Mouse ScrollWheel") > 0){//transform.Translate(Vector3.forward * 1f);//速度可调  自行调整radius = Math.Clamp(radius - 1.0f, 2, 15);}if (Input.GetAxis("Mouse ScrollWheel") < 0){//transform.Translate(Vector3.forward * -1f);//速度可调  自行调整radius = Math.Clamp(radius + 1.0f, 2, 15);}}
}

3.相机跟随角色视角旋转:


using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;public class CameraRotate : MonoBehaviour
{public float speedH;//public float speedVertical;public bool isMobile;//[Range(0f, 1f)]//public float damping;CameraFollow cameraFollow;private float currentHorizontal;private float currentVertical;private Vector3 lastMousePosition;private bool isDragging;float time;private float velocityY;private float velocityX;PointerEventData eventDataCurrentPosition;//private bool isDragging;public static CameraRotate instance;private void Awake() {instance = this;//UI交互要禁用玩家的控制cameraFollow = Camera.main.GetComponent<CameraFollow>();currentHorizontal = cameraFollow.polarDeg;time = Time.realtimeSinceStartup;}// Start is called before the first frame updatevoid Start() {}public void SetPolarDeg(float degree) {currentHorizontal = degree;cameraFollow.polarDeg = degree;}private bool IsPointerOverUIObject() {//判断是否点击的是UI,有效应对安卓没有反应的情况,true为UIif (eventDataCurrentPosition == null) {eventDataCurrentPosition = new PointerEventData(EventSystem.current);}eventDataCurrentPosition.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);List<RaycastResult> results = new List<RaycastResult>();EventSystem.current.RaycastAll(eventDataCurrentPosition, results);return results.Count > 0;}// Update is called once per framevoid LateUpdate() {//if (IsPointerOverUIObject()) {//    lastMousePosition = Input.mousePosition;//    return;//点击到UI不处理//}// 检查鼠标左键是否按下如果按下的那一下是在UI之上,则不让其旋转if (PlayerData.Instance.isRunningPC){if (Input.GetMouseButtonDown(0) && IsPointerOverUIObject() == false){// 记录鼠标点击位置lastMousePosition = Input.mousePosition;isDragging = true;}else if (Input.GetMouseButtonUp(0)){// 如果鼠标抬起,则需要重新按下鼠标来记录新的点击位置isDragging = false;}}else {if (Input.GetMouseButtonDown(0)){// 记录鼠标点击位置lastMousePosition = Input.mousePosition;isDragging = true;}else if (Input.GetMouseButtonUp(0)){// 如果鼠标抬起,则需要重新按下鼠标来记录新的点击位置isDragging = false;}}if (isDragging) {float speedHorizontal = speedH;float speedVertical = 0.1f;// 计算鼠标移动的增量Vector3 deltaMousePosition = Input.mousePosition - lastMousePosition;//Debug.Log("deltaMousePosition.x:" + deltaMousePosition.x + "deltaMousePosition.y:" + deltaMousePosition.y);// 计算水平旋转角度// 计算水平旋转角度float deltaHorizontal = speedHorizontal * deltaMousePosition.x;var newHorizontal = currentHorizontal - deltaHorizontal;//newHorizontal = Mathf.SmoothDamp(currentHorizontal, newHorizontal,ref velocityX, Time.unscaledDeltaTime);// 更新摄像机跟随脚本的旋转角度cameraFollow.polarDeg = newHorizontal;//Debug.Log("cameraFollow.polarDeg" + cameraFollow.polarDeg);currentHorizontal = newHorizontal;float deltaVertical = speedVertical * deltaMousePosition.y;var newVertical = currentVertical - deltaVertical;//newHorizontal = Mathf.SmoothDamp(currentVertical, newVertical, ref velocityY, Time.unscaledDeltaTime);// 更新摄像机跟随脚本的旋转角度cameraFollow.elevationDeg = Mathf.Clamp(newVertical, -90f, 89);currentVertical = newVertical;lastMousePosition = Input.mousePosition;}else {}}}

相关文章:

Unity组件开发--相机跟随角色和旋转

1.相机跟随组件&#xff0c;节点&#xff1a; 2.相机跟随组件脚本&#xff1a; using System; using System.Collections; using System.Collections.Generic; using Unity.Burst.Intrinsics; using UnityEngine; using UnityEngine.UI;public class CameraFollow : Singleton&…...

JavaScript系列——Proxy(代理)

文章目录 概要Proxy 语法handler 对象的方法Proxy 示例常用handler 对象的方法的参数handler.get()语法示例 handler.set()语法示例 使用场景验证值修正及附加属性 小结 概要 Proxy 用于创建一个对象的代理&#xff0c;将对原对象上的操作&#xff08;属性获取、赋值、函数调用…...

QT第三天

使用QT完成水果计价界面和功能&#xff0c;如下图&#xff1a; 运行结果&#xff1a; 代码&#xff1a; widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QListWidgetItem>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_N…...

Jetpack Compose -> 声明式UI Modifier

前言 本章主要介绍下 Compose 的声明式 UI 以及初级写法&#xff1b; 什么是声明式UI 传统UI 传统 UI 方式来声明UI <androidx.appcompat.widget.LinearLayoutCompat android:layout_width"match_parent" android:layout_height"match_parent&quo…...

windows10 装docker和docker compose

一.windows环境准备 开启过程中的问题&#xff0c;进入bios修复 二.docker下载安装 1.下载 Docker Desktop: The #1 Containerization Tool for Developers | Docker 下载最新版有问题&#xff0c;下载老版本试试 Docker Desktop release notes | Docker Docs 2.安装 三.do…...

第二次面试总结 - 宏汉科技 - Java后端开发

&#x1f9f8;欢迎来到dream_ready的博客&#xff0c;&#x1f4dc;相信您对专栏 “本人真实面经” 很感兴趣o (ˉ▽ˉ&#xff1b;) 专栏 —— 本人真实面经&#xff0c;更多真实面试经验&#xff0c;中大厂面试总结等您挖掘 目录 总结 (非详细) 面试内容(提问内容) - 带答案…...

GPT-4:人工智能的新纪元与未来的无限可能

在人工智能的发展史上&#xff0c;GPT-4的问世标志着一个新的里程碑。作为最新一代的自然语言处理模型&#xff0c;GPT-4不仅在技术上取得了突破&#xff0c;更在应用层面展现了前所未有的潜力。本文将探讨GPT-4的核心技术、应用场景以及它对未来社会的潜在影响。 GPT-4的技术…...

2.右值引用和移动语义

文章目录 右值引用和移动语义&&的特性右值引用优化性能&#xff0c;避免深拷贝移动(move )语义forward 完美转发emplace_back 减少内存拷贝和移动unordered container 无序容器map和unordered_map的差别内部实现机理不同优缺点以及适用处 小结优缺点以及适用处 小结 代…...

深入浅出线程原理

Linux 中的线程本质 线程接口由 Native POSIX Thread Library 提供&#xff0c;即&#xff1a;NPTL 库函数 线程被称为轻量级进程 (Light Weight Process) 每一个线程在内核中都对应一个调度实体&#xff0c;拥有独立的结构体 (task_struct) 内核设计&#xff1a;一个进程对…...

openssl3.2 - 官方demo学习 - saccept.c

文章目录 openssl3.2 - 官方demo学习 - saccept.cEND openssl3.2 - 官方demo学习 - saccept.c 建立TLSServer(使用了证书, 和证书中的私钥), 接收客户端的连接, 并将客户端发来的信息打印到屏幕 笔记 /*! \file saccept.c */ /*! \brief 建立TLSServer(使用了证书, 和证书中…...

JavaScript基础(26)_dom增删改练习

<!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8"><title>DOM增删改练习</title><link rel"stylesheet" href"../browser_default_style/reset.css"><style>table {borde…...

mac上部署单体hbase

1. 简介 HBase 是一个开源的、分布式的、版本化的典型非关系型数据库。它是 Google BigTable 的开源实现&#xff0c;并且是 Apache 基金会的 Hadoop 项目的一部分1。HBase 在 Hadoop Distributed File System (HDFS) 上运行&#xff0c;作为一个列式存储非关系数据库管理系统…...

【RV1126 学习】SDK/ U-Boot/kernel/rootfs 编译学习

文章目录 RV1126芯片介绍rv1126 模块代码目录相关说明 SDK 包下的脚本使用build.sh 脚本使用envsetup.sh 脚本使用mkfirmware.sh 脚本使用rkflash.sh 脚本使用 U-Boot 编译和配置uboot 的配置修改编译操作 kernel 的修改编译rootfs 编译和配置buildroot 配置busybox 配置 RV112…...

Golang 使用 AST 获取方法和参数名以及应用举例

背景 在做一些自动生成的代码工作时&#xff0c;有时需要知道方法以及对应的参数名 如果仅是方法&#xff0c;利用反射机制就可以解决 而参数名&#xff0c;程序编译后&#xff0c;已经丢失 可以通过 AST 事先获取方法的参数名 有了方法、参数名&#xff0c;加上反射&…...

DC-DC变换集成电路芯片B34063——工作电压范围宽,静态电流小

B34063为一单片DC-DC变换集成电路&#xff0c;内含温度补偿的参考电压源(1.25V)、比较器、能有效限制电流及控制工作周期的振荡器,驱动器及大电流输出开关管等&#xff0c;外配少量元件&#xff0c;就能组成升压、降压及电压反转型DC-DC变换器。 主要特点&#xff1a; ● 工作…...

强力推荐:本地文件加密软件—超详细加密步骤来了!

在数字化时代&#xff0c;数据安全问题日益受到人们的关注。 为了保护个人和企业的重要信息不被泄露&#xff0c;越来越多的人开始使用文件加密软件。 尤其是常常会有数据泄露风险的企业更是需要一款非常给力的加密工具来保护企业数据安全。 一、选择合适的加密软件 在选择加…...

在qml中,ListModel可以与WorkerScript一起使用,从多个线程访问列表模型

在QML中&#xff0c;您可以使用ListModel和WorkerScript一起实现多线程访问列表模型。以下是一个简单的例子&#xff0c;演示了如何在QML中使用这两个元素&#xff1a; import QtQuick 2.15 import QtQuick.Controls 2.15ApplicationWindow {visible: truewidth: 400height: 3…...

rocketmq实现延迟消息

SpringBoot整合RocketMQ发送延时消息 springboot rocketmq 延迟消息 Windows下RocketMQ安装及可视化界面搭建 Java 客户端 RocketMQ延迟消息 项目背景 项目中有延时消息的需求&#xff0c;综合考量RocketMQ比较适合。 RocketMQ支持多维度的延迟级别 支持多种消息类型 基…...

vue倒计时60秒改变按钮状态效果demo(整理)

你可以使用Vue的计时器和绑定状态的方法来实现这个功能。 首先&#xff0c;在data中添加一个计时器countdown&#xff0c;初始值为0。 data() {return {countdown: 0} }<template><div><button click"startCountdown" :disabled"countdown > …...

多区域isis配置实验

一、预习&#xff1a; IS-IS&#xff1a;Intermediate System to Intermediate System&#xff0c;中间系统到中间系统&#xff0c;是ISO为它的CLNP&#xff08;ConnectionLess Network Protocol&#xff09;设计的一种动态路由协议&#xff0c;后来为了提供对IP路由的支持&…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

【解密LSTM、GRU如何解决传统RNN梯度消失问题】

解密LSTM与GRU&#xff1a;如何让RNN变得更聪明&#xff1f; 在深度学习的世界里&#xff0c;循环神经网络&#xff08;RNN&#xff09;以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而&#xff0c;传统RNN存在的一个严重问题——梯度消失&#…...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

代码规范和架构【立芯理论一】(2025.06.08)

1、代码规范的目标 代码简洁精炼、美观&#xff0c;可持续性好高效率高复用&#xff0c;可移植性好高内聚&#xff0c;低耦合没有冗余规范性&#xff0c;代码有规可循&#xff0c;可以看出自己当时的思考过程特殊排版&#xff0c;特殊语法&#xff0c;特殊指令&#xff0c;必须…...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...

Python 训练营打卡 Day 47

注意力热力图可视化 在day 46代码的基础上&#xff0c;对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...

springboot 日志类切面,接口成功记录日志,失败不记录

springboot 日志类切面&#xff0c;接口成功记录日志&#xff0c;失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...

xmind转换为markdown

文章目录 解锁思维导图新姿势&#xff1a;将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件&#xff08;ZIP处理&#xff09;2.解析JSON数据结构3&#xff1a;递归转换树形结构4&#xff1a;Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...

【Linux】Linux安装并配置RabbitMQ

目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的&#xff0c;需要先安…...