【Unity3D】缩放、平移、旋转场景

这篇具有很好参考价值的文章主要介绍了【Unity3D】缩放、平移、旋转场景。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1 前言

        场景缩放、平移、旋转有两种实现方案,一种是对场景中所有物体进行同步变换,另一种方案是对相机的位置和姿态进行变换。

        对于方案一,如果所有物体都在同一个根对象下(其子对象或孙子对象),那么只需要对根对象施加变换就可以实现场景变换;如果有多个根对象,那就需要对所有根对象施加变换。该方案实现简单,但是会破坏场景中对象的尺寸、位置、姿态,不符合现实世界的规则。如:对场景施加缩放变换后,又新增了一个对象,但是该对象不是放在同一个根目录下,就会让用户感觉新增对象的尺寸超出意外;如果有多个根对象,就会存在多个参考系(每个根对象一个参考系),增加场景中对象的控制难度。

        对于方案二,通过变换相机的位置和姿态,让用户感觉场景中所有对象在同步缩放、平移、旋转。该方案实现较困难,但是不会破环场景中对象的尺寸、位置、姿态,更贴近真实世界的规则,也不需要将所有对象都放在同一个根对象下。

        方案二明显优于方案一,本文将详细介绍其原理和实现。原理如下:

        1)场景缩放原理

        利用相机的透视原理(详见→透视变换原理),即相机拍摄到的图片呈现近大远小的效果,将相机靠近和远离场景,从而实现放大和缩小场景的效果。

        2)场景平移原理

        相机成像是在近平面上,如果扩展近平面的范围,相机拍摄的范围也就越大,将近平面平移到相机位置上,记为平面 S,将相机在 S 平面上平移,就会实现场景平移效果。

        3)场景旋转原理

        在 Unity3D Scene 窗口,通过按 Alt 键 + 鼠标拖拽,可以旋转场景。场景旋转包含两种情况,鼠标沿水平方向拖拽、鼠标沿竖直方向拖拽。

        当鼠标沿水平方向拖拽时,笔者通过多次实验观察,发现如下规律:当场景缩放到某个值时,旋转场景时,屏幕中心位置的物体(在相机的正前方)在场景旋转过程中始终处在屏幕中心,并且旋转轴的方向始终是 Y 轴方向。因此可以得出结论:旋转中心在相机正前方(forward),旋转轴沿 Y 轴方向。

        旋转中心的 y 值最好与地图的 y 值相等,如果场景中没有地图,可以取旋转中心为:cam.position + cam.forward * (nearPlan + 1 / nearPlan),当然,用户也可以取其他值。已知旋转中心的 y 值,可以按照以下公式推导出 x、z 值:

【Unity3D】缩放、平移、旋转场景

        当鼠标沿竖直方向拖拽时,旋转中心在相机位置,旋转轴沿相机的左边(-right)。

        本文代码资源见→缩放、平移、旋转场景。

2 代码实现

        SceneController.cs

using UnityEngine;
 
public class SceneController : MonoBehaviour {
    private Texture2D[] cursorTextures; // 鼠标样式: 箭头、小手、眼睛
    private Transform cam; // 相机
    private float nearPlan; // 近平面
    private Vector3 preMousePos; // 上一帧的鼠标坐标
    private int cursorStatus = 0; // 鼠标样式状态
    private bool isDraging = false; // 是否在拖拽中
 
    private void Awake() {
        string[] mouseIconPath = new string[]{"MouseIcon/0_arrow", "MouseIcon/1_hand", "MouseIcon/2_eye"};
        cursorTextures = new Texture2D[mouseIconPath.Length];
        for(int i = 0; i < mouseIconPath.Length; i++) {
            cursorTextures[i] = Resources.Load<Texture2D>(mouseIconPath[i]);
        }
        cam = Camera.main.transform;
        Vector3 angle = cam.eulerAngles;
        cam.eulerAngles = new Vector3(angle.x, angle.y, 0); // 使camp.right指向水平方向
        nearPlan = Camera.main.nearClipPlane;
    }
 
    private void Update() {
        cursorStatus = GetCursorStatus();
        // 更新鼠标样式, 第二个参数表示鼠标点击位置在图标中的位置, zero表示左上角
        Cursor.SetCursor(cursorTextures[cursorStatus], Vector2.zero, CursorMode.Auto);
        UpdateScene(); // 更新场景(Ctrl+Scroll: 缩放场景, Ctrl+Drag: 平移场景, Alt+Drag: 旋转场景)
    }
 
    private int GetCursorStatus() { // 获取鼠标状态(0: 箭头, 1: 小手, 2: 眼睛)
        if (isDraging) {
            return cursorStatus;
        }
        if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.LeftControl)) {
            return 1;
        }
        if (Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.LeftAlt)) {
            return 2;
        }
        return 0;
    }
 
    private void UpdateScene() { // 更新场景(Ctrl+Scroll: 缩放场景, Ctrl+Drag: 平移场景, Alt+Drag: 旋转场景)
        float scroll = Input.GetAxis("Mouse ScrollWheel");
        if (!isDraging && cursorStatus == 1 && Mathf.Abs(scroll) > 0) { // 缩放场景
            ScaleScene(scroll);
        } else if (Input.GetMouseButtonDown(0)) {
            preMousePos = Input.mousePosition;
            isDraging = true;
        } else if (Input.GetMouseButtonUp(0)) {
            isDraging = false;
        } else if (Input.GetMouseButton(0)) {
            Vector3 offset = Input.mousePosition - preMousePos;
            if (cursorStatus == 1) { // 移动场景
                MoveScene(offset);
            } else if (cursorStatus == 2) { // 旋转场景
                RotateScene(offset);
            }
            preMousePos = Input.mousePosition;
        }
    }
 
    private void ScaleScene(float scroll) { // 缩放场景
        cam.position += cam.forward * scroll;
    }
 
    private void MoveScene(Vector3 offset) { // 平移场景
        cam.position -= (cam.right * offset.x / 100 + cam.up * offset.y / 100);
    }
 
    private void RotateScene(Vector3 offset) { // 旋转场景
        Vector3 rotateCenter = GetRotateCenter(0);
        cam.RotateAround(rotateCenter, Vector3.up, offset.x / 3); // 水平拖拽分量
        cam.LookAt(rotateCenter);
        cam.RotateAround(rotateCenter, -cam.right, offset.y / 5); // 竖直拖拽分量
    }
 
    private Vector3 GetRotateCenter(float planeY) { // 获取旋转中心
        if (Mathf.Abs(cam.forward.y) < float.Epsilon || Mathf.Abs(cam.position.y) < float.Epsilon) {
            return cam.position + cam.forward * (nearPlan + 1 / nearPlan);
        }
        float t = (planeY - cam.position.y) / cam.forward.y;
        float x = cam.position.x + t * cam.forward.x;
        float z = cam.position.z + t * cam.forward.z;
        return new Vector3(x, planeY, z);
    }
}

        说明:SceneController 脚本组件挂在相机下,鼠标图标如下,需要放在 Resouses/MouseIcon 目录下, 并且需要在 Inspector 窗口将其 Texture Type 属性调整为 Cursor。

【Unity3D】缩放、平移、旋转场景

3 运行效果

         通过 Ctrl+Scroll 缩放场景,Ctrl+Drag 平移场景,Alt+Drag 旋转场景 ,效果如下:

【Unity3D】缩放、平移、旋转场景

4 优化

        第 2 节中场景变换存在以下问题,本节将对这些问题进行优化。

  • 竖直方向平移场景时,会抬高或降低相机高度;
  • 竖直方向旋转场景时,如果相机垂直朝向地面,就会出现窗口急速晃动问题,因为旋转中心出现了跳变。

        针对问题一,将相机的上方向量(camera.up)投影到水平面上,再用投影向量计算相机前后平移的偏移量。

        针对问题二,使用一个全局变量实时保存并更新旋转中心的位置,并通过相机周转和自传(两者旋转角度和方向相等)实现水平和竖直方向旋转场景,避免使用 LookAt,因为相机不一定一直朝向旋转中心(如:相机焦点不在地图里)。

        SceneController.cs文章来源地址https://www.toymoban.com/news/detail-421192.html

using UnityEngine;

public class SceneController : MonoBehaviour {
    private const float MAX_HALF_EDGE_X = 5f; // 地图x轴方向半边长
    private const float MAX_HALF_EDGE_Z = 5f; // 地图z轴方向半边长
    private Texture2D[] cursorTextures; // 鼠标样式: 箭头、小手、眼睛
    private Transform cam; // 相机
    private float planeY = 0f; // 地面高度
    private Vector3 rotateCenter; // 旋转中心
    private Vector3 focusCenter; // 相机在地面上的焦点中心
    private bool isFocusInMap; // 相机焦点是否在地图里
    private Vector3 preMousePos; // 上一帧的鼠标坐标
    private int cursorStatus = 0; // 鼠标样式状态
    private bool isDraging = false; // 是否在拖拽中

    private void Awake() {
        string[] mouseIconPath = new string[] { "MouseIcon/0_arrow", "MouseIcon/1_hand", "MouseIcon/2_eye" };
        cursorTextures = new Texture2D[mouseIconPath.Length];
        for (int i = 0; i < mouseIconPath.Length; i++) {
            cursorTextures[i] = Resources.Load<Texture2D>(mouseIconPath[i]);
        }
        cam = Camera.main.transform;
        Vector3 angle = cam.eulerAngles;
        cam.eulerAngles = new Vector3(angle.x, angle.y, 0); // 使camp.right指向水平方向
        rotateCenter = new Vector3(0, planeY, 0);
        focusCenter = new Vector3(0, planeY, 0);
    }

    private void Update() {
        cursorStatus = GetCursorStatus();
        // 更新鼠标样式, 第二个参数表示鼠标点击位置在图标中的位置, zero表示左上角
        Cursor.SetCursor(cursorTextures[cursorStatus], Vector2.zero, CursorMode.Auto);
        UpdateScene(); // 更新场景(Ctrl+Scroll: 缩放场景, Ctrl+Drag: 平移场景, Alt+Drag: 旋转场景)
    }

    private int GetCursorStatus() { // 获取鼠标状态(0: 箭头, 1: 小手, 2: 眼睛)
        if (isDraging)
        {
            return cursorStatus;
        }
        if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.LeftControl))
        {
            return 1;
        }
        if (Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.LeftAlt))
        {
            return 2;
        }
        return 0;
    }

    private void UpdateScene() { // 更新场景(Ctrl+Scroll: 缩放场景, Ctrl+Drag: 平移场景, Alt+Drag: 旋转场景)
        float scroll = Input.GetAxis("Mouse ScrollWheel");
        if (!isDraging && cursorStatus == 1 && Mathf.Abs(scroll) > 0) { // 缩放场景
            ScaleScene(scroll);
        }
        else if (Input.GetMouseButtonDown(0)) {
            preMousePos = Input.mousePosition;
            UpdateRotateCenter();
            isDraging = true;
        }
        else if (Input.GetMouseButtonUp(0)) {
            isDraging = false;
        }
        else if (Input.GetMouseButton(0)) {
            Vector3 offset = Input.mousePosition - preMousePos;
            if (cursorStatus == 1) { // 移动场景
                MoveScene(offset);
            }
            else if (cursorStatus == 2) { // 旋转场景
                RotateScene(offset);
            }
            preMousePos = Input.mousePosition;
        }
    }

    private void ScaleScene(float scroll) { // 缩放场景
        cam.position += cam.forward * scroll;
    }

    private void MoveScene(Vector3 offset) { // 平移场景
        Vector3 horVec = Vector3.ProjectOnPlane(cam.right, Vector3.up).normalized;
        Vector3 verVec = Vector3.ProjectOnPlane(cam.up, Vector3.up).normalized;
        cam.position -= (horVec * offset.x / 100 + verVec * offset.y / 100);
    }

    private void RotateScene(Vector3 offset) { // 旋转场景
        float hor = offset.x / 3;
        float ver = -offset.y / 5;
        cam.RotateAround(rotateCenter, Vector3.up, hor); // 相机绕旋转中心水平旋转
        cam.RotateAround(rotateCenter, cam.right, ver); // 相机绕旋转中心竖直旋转
        // 由于transform.RotateAround方法中已经进行了物体姿态调整, 因此以下语句是多余的
        // cam.RotateAround(cam.position, Vector3.up, hor); // 相机自转, 使其朝向旋转中心
        // cam.RotateAround(cam.position, cam.right, ver); // 相机自转, 使其朝向旋转中心
    }

    private void UpdateRotateCenter() { // 更新旋转中心
        UpdateFocusStatus();
        if (!isFocusInMap) {
            return;
        }
        rotateCenter.x = Mathf.Clamp(focusCenter.x, -MAX_HALF_EDGE_X, MAX_HALF_EDGE_X);
        rotateCenter.z = Mathf.Clamp(focusCenter.z, -MAX_HALF_EDGE_Z, MAX_HALF_EDGE_Z);
    }

    private void UpdateFocusStatus() { // 更新焦点状态
        isFocusInMap = true;
        Vector3 vec1 = new Vector3(0, planeY - cam.position.y, 0);
        Vector3 vec2 = cam.forward;
        if (Mathf.Abs(vec1.y) < float.Epsilon || Mathf.Abs(vec2.y) < float.Epsilon) {
            isFocusInMap = false;
            return;
        }
        float angle = Vector3.Angle(vec1, vec2);
        if (angle >= 90) { // 相机在地面以上并且朝天, 或在地面以下并且朝下
            isFocusInMap = false;
            return;
        }
        float t = (planeY - cam.position.y) / vec2.y;
        focusCenter.x = cam.position.x + t * vec2.x;
        focusCenter.z = cam.position.z + t * vec2.z;
        if (Mathf.Abs(focusCenter.x) > MAX_HALF_EDGE_X || Mathf.Abs(focusCenter.z) > MAX_HALF_EDGE_Z) { // 相机焦点不在地图区域内
            isFocusInMap = false;
        }
    }
}

到了这里,关于【Unity3D】缩放、平移、旋转场景的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity3D 脚本3(旋转)

    一、物体的旋转 给物体调转一个旋转角度。 1、Quaternion四元组(x,y,z,w) transfrom.rotation()=... 不方便操作,官方不建议用 2、Euler Angle 欧拉角 transfrom.eulerAngles = new Vector(0,45,0); transfrom.LocalE ulerAngles = new Vector(0,45,0); 这样就可以让物体旋转45度。 在Update 中修改角度持续旋转 Vecto

    2023年04月16日
    浏览(46)
  • unity3D 鼠标滚轮实现物体的大小缩放

    鼠标滚轮响应函数是Input.GetAxis(\\\"Mouse ScrollWheel\\\"),函数返回值类型是float,向前滚是返回正数,向后滚是返回负数,且鼠标滚轮滑动单次函数返回值为0.1 利用返回值修改模型transform.localscale,实现模型缩放 鼠标滚轮一直向后滚,会看见模型逐渐变小,当变到很小到消失的时候,

    2024年02月08日
    浏览(49)
  • Unity3D相机围绕物体自由旋转

    写了一个相机围绕物体360°自由旋转的脚本,比较实用,分享出来给大家使用,配置如下: Hierarchy界面  LookPovit:为物体Auto Aircraft的中心空对象; AroundPovit:用于相机的空对象,作为父类,主要作用是为了保持与被观察物体的位置一致,可防止Auto Aircraft出现抖动而影响相机

    2024年02月11日
    浏览(43)
  • Unity3D 控制物体移动且自动旋转

    直接上代码(改脚本挂载到游戏物体上) 第三人称摄像机移动

    2024年02月15日
    浏览(53)
  • Unity3D实现键盘控制小车左右方向旋转

    注:本文章是在已学课程的基础上实现的标题效果! 以下是学习控制物体旋转的简单方法: 代码在updata()函数中实现;  transform:指本脚所挂载的物体的位置信息,包含Position(位置)、Rotation(旋转)、Scale(缩放),此处通过\\\".\\\"来调用Rotate方法; Rotate:unity手册中描述如下,参数一:

    2024年01月18日
    浏览(38)
  • Unity3D-场景中3D物体添加点击事件

    Unity3D - 场景中3D物体添加鼠标点击事件 鼠标点击3D物体触发,Unity从本质上来说有两种:一种是通过事件(event)触发,一种是通过射线(ray)判断穿过的物体触发。这两种触发的原理是不同的,不论哪种触发都必须满足触发的要求才可以,既然原理不同,触发的要求也不一样

    2024年02月08日
    浏览(38)
  • unity3d场景怎么添加天空盒子?

    unity3d场景中想要添加天空盒子,该怎么添加呢?下面我们就来看看详细的教程。 1、先下载组成天空盒子的天空图片 2、把天空图片拖进 unity3d 中创建的文件夹 3、新建一个 材质球 4、把材质球的 shader修改成天空盒选项 5、在天空盒的六个方向(前,后,左,右,上,下)添加

    2024年02月11日
    浏览(33)
  • Unity3D:提升场景画面品质&平衡性能优化

    在日常开发中,提升场景画面品质的同时平衡性能优化一直是很重要且头疼的一部分,尤其是当场景类型不同及效果需求不一样时。 所以怎么同时提升场景画面品质和平衡性能优化呢,下面介绍一些适用度高且实用性好的设置。 介绍之前先说一下结论:怎么样才能说的上是

    2024年02月08日
    浏览(34)
  • Unity3D学习记录02——PloyBrush场景搭建

    首先在Window-Package Manager里面搜索Poly Brush,下载后将URP的Shader样例导入  导入后Asset文件夹下会有Sample的文件夹,在菜单栏 Tools-PolyBrush-PolyBrush Window 打开窗口 这个窗口最上面的五个,第一个是用来调整地形高低的,第二个是进行柔化场景的,第三个是调整颜色的, 第四个可以

    2024年02月08日
    浏览(33)
  • Unity3D摄像机,键盘控制前后左右上下移动,鼠标控制旋转、放缩

    Unity3D中运行场景时,实现摄像机的前、后、左、右、上、下,以及鼠标滚轮的放缩,鼠标右键的旋转操作。亲测有效,可供参考。 按键功能介绍:W——前;S——后;A——左;D——右;Q——下降;E——上升;鼠标右键——旋转;鼠标滚轮——放缩。 Tourcamera脚本需要挂在摄

    2024年02月11日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包