在Unity中通过Camera实现类似地图拉拽缩放的功能

本文介绍了在Unity中如何通过调整Camera的Size和位置,结合Input.GetAxis("Mouse ScrollWheel")实现地图的缩放,以及使用ScreenToWorldPoint()进行平移,通过限制摄像机移动范围达到平滑的地图拖拽效果。

在最近的工作中任务是制作一个导览图系统,需要用到的一个需求就是可以拖动和放大地图,参考网上有很多UI描点大法的实现,我想尝试在摄像机上去实现。

0de55f6632c044e09d7b26dfbe60a071.gif

由于地图偏平面,我使用的是用2D模式下的摄像机,将摄像机的Projection(投影)属性设置成,Orthographic。

我首先想到的是通过直接改变摄像机Size实现缩放效果,通过 Input.GetAxis("Mouse ScrollWheel")获取鼠标滚轮的滚动量来改变Size的大小


    public Camera me_camera;     // 参与变换的摄像机
    public Vector2 range_size = new Vector2(1, 5);     // 通过这个来控制Size缩放量
    // 位置缩放
    public void ScreenZoom()
    {
        float v = Input.GetAxis("Mouse ScrollWheel");   //获取鼠标滚轮方向
        float old_size = me_camera.orthographicSize;         // 计算摄像机原来的大小
        // 计算移动后的大小
        float size = Mathf.Clamp(old_size - v, range_size.x, range_size.y);
        me_camera.orthographicSize = size; 
    }

 不过这个效果emmm怎么说吧,通过和别人的地图作比较发现,还是差一些意思。

a3feec8887cb40459dbfd0e5f64da75f.gif

 

最后还是找到一个贴子参考,看了他人的文章一下子悟了,参考文章如下。

根据观察,我发现一个规律,别人的缩放是在视点缩放,在摄像机size缩小时位置会无限接近视点位置,而且的移动的长度(相对摄像机到视点的位置)和摄像机size缩放的倍数是相同的,比如摄像机size比原来缩小了 1/2 摄像机就会向视点位置移动原位置的 1/2 。

5440329484214646b65975208e64f50c.png

假设摄像机原来大小是oldSize、位置是pos,缩小后的大小是size、位置是newPos, 视点使用鼠标位置为mousePos,所以得到如下公式:

gif.latex?%5Cfn_jvn%20%5Csmall%20%5Cfrac%7BoldSize-size%7D%7BoldSize%7D%20%3D%20%5Cfrac%7B%28mousePos%20-%20pos%29%20-%28mousePos%20-%20newPos%29%20%7D%7BmousePos%20-%20pos%7D

 其中因为视点到摄像机的位置是在坐标系上的,要排除掉原点到摄像机的距离,因为其他条件是已知的,将公式简化则可以得到 newPos。

gif.latex?%5Cfn_jvn%20%5Csmall%20newPos%20%3D%20pos%20-%20%28mousePos%20-%20pos%20%29%20*%20%5Cfrac%7BoldSize%20-%20size%7D%7BoldSize%20%7D

代码修改如下:

    public Camera me_camera;     // 参与变换的摄像机
    public Vector2 range_size = new Vector2(1, 5);     // 通过这个来控制Size缩放量

    /// <summary>
    /// 根据视点缩放
    /// </summary>
    public void AnchorZoom()
    {
        float v = Input.GetAxis("Mouse ScrollWheel");
        // 计算摄像机原来的大小
        float old_size = me_camera.orthographicSize;
        // 计算移动后的大小
        float size = Mathf.Clamp(old_size - v, range_size.x, range_size.y);
        // 计算摄像机的缩放倍数 
        float pro = (old_size - size) / old_size;        
        // 公式
        Vector3 position = me_camera.transform.position + (me_camera.ScreenToWorldPoint(Input.mousePosition) - me_camera.transform.position) * pro;
        // 应用大小缩放
        me_camera.orthographicSize = size;
        //位置缩放
        me_camera.transform.position = new Vector3(position.x, position.y, me_camera.transform.position.z);
    }

其中,mousePosition 的值需要经过ScreenToWorldPoint( )  方法来转换成世界坐标去进行运算。

缩放搞定,然后是平移功能。摄像机平移功能比较简单,我直接采用的就是平移的方法。

    // 参与变换的摄像机
    public Camera me_camera;
    // 通过这个来控制画面缩放量
    public Vector2 range_size = new Vector2(2, 5);

    public void ScreenMove()
    {
        // 鼠标 左键
        float x = Input.GetAxis("Mouse X");
        float y = Input.GetAxis("Mouse Y");
        if (Input.GetMouseButton(0))
        {
            // 相机位置的偏移量(Vector3类型,实现原理是:向量的加法)
            Vector3 moveDir = (x * -me_camera.transform.right) + (y * -me_camera.transform.up);
            // 使平移速度被size影响 size 越小移动越慢 
            float speed = (me_camera.orthographicSize / range_size.y) * 0.5f;
            moveDir = moveDir * speed;
            // 限制y轴的偏移量
            me_camera.transform.position += moveDir;
        }
    }

      制作一个将摄像机的可移动画面划一个范围,超过范围慢慢会回来的效果。于是我将摄像机想象成一个矩形rect,在它的外面有一个更大的矩形rangeRect,然后你只需要判断rect是不是在rangeRect里面。在Unity中直接有Rect可以使用,只需要取分别的顶点在世界坐标里的位置就可以了,这样就可以算出宽和高。

49369aba0e5042409ee850bdd0dd21a9.png

 加上限制的平移代码如下:


    // 参与变换的摄像机
    public Camera me_camera;
    // 通过这个来控制画面缩放量
    public Vector2 range_size = new Vector2(2, 5);
    // 限制运动范围矩形
    Rect rect, rangeRect;

    void Start()
    {
        Camera.main.orthographicSize = range_size.y;

        // 在这里定义矩形限制的范围
        var Start_leftTop = Camera.main.ScreenToWorldPoint(new Vector3(0, Screen.height, 0));
        var Start_rightButtom = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, 0, 0));
        rangeRect = new Rect(Start_leftTop.x, Start_leftTop.y, Mathf.Abs(Start_rightButtom.x - Start_leftTop.x), Mathf.Abs(Start_rightButtom.y - Start_leftTop.y));

        Camera.main.orthographicSize = range_size.x;
    }

    void Update()
    {
        ScreenMove();
    }

    public void ScreenMove()
    {
        // 鼠标 左键
        float x = Input.GetAxis("Mouse X");
        float y = Input.GetAxis("Mouse Y");
        if (Input.GetMouseButton(0))
        {
            // 相机位置的偏移量(Vector3类型,实现原理是:向量的加法)
            Vector3 moveDir = (x * -me_camera.transform.right) + (y * -me_camera.transform.up);
            // 使平移速度被size影响 size 越小移动越慢 
            float speed = (me_camera.orthographicSize / range_size.y) * 0.5f;
            moveDir = moveDir * speed;
            // 限制y轴的偏移量
            me_camera.transform.position += moveDir;
        }
        else //限制摄像机运动范围   
        {         
            UpdateNowCameraRect();
            //是否出顶
            if (rect.y > rangeRect.y) 
            {
                float d = rect.y - rangeRect.y;
                me_camera.transform.position -= new Vector3(0, d * 0.05f, 0);
            }
            // 底低于底部
            if (rect.y - rect.height < rangeRect.y - rangeRect.height)
            {
                float d = (rangeRect.y - rangeRect.height) - (rect.y - rect.height);
                me_camera.transform.position -= new Vector3(0, -d * 0.05f, 0);
            }
            // 低于最左边
            if (rect.x < rangeRect.x)
            {
                float d = rangeRect.x - rect.x;
                me_camera.transform.position -= new Vector3(-d * 0.05f, 0, 0);
            }
            // 高于最右边
            if (rect.x + rect.width > rangeRect.x + rangeRect.width)
            {
                float d = (rect.x + rect.width) - (rangeRect.x + rangeRect.width);
                me_camera.transform.position -= new Vector3(d * 0.05f, 0, 0);
            }
        }
    }

    /// <summary>
    /// 更新当前摄像机的矩形
    /// </summary>
    void UpdateNowCameraRect()
    {
        var Now_leftTop = Camera.main.ScreenToWorldPoint(new Vector3(0, Screen.height, 0));
        var Now_rightButtom = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, 0, 0));
        rect.Set(Now_leftTop.x, Now_leftTop.y, Mathf.Abs(Now_rightButtom.x - Now_leftTop.x), Mathf.Abs(Now_rightButtom.y - Now_leftTop.y));
    }

 平移和缩放都完成,最终效果如下。

e9ed2f065ce14fa58606490d15f13e7f.gif

 最后附上所有代码,里面有触控屏上的移动方法,直接下载用就行,因为太长我就不再这里展示啦。

https://download.csdn.net/download/HunagJingLiao/87267232

 

内容概要:本文提出了一种基于改进扩散模型的高海拔地区新能源高波动出力场景生成方法,并提供了完整的Python代码实现。该方法针对高海拔地区风能、光伏等新能源出力波动剧烈、不确定性高的特点,通过优化扩散模型的结构训练策略,有效捕捉历史数据的概率分布特征时序相关性,从而生成高质量、多样化的出力场景。文中详细阐述了模型的数学推导、网络架构设计、损失函数优化及采样算法改进,并通过实验证明其在拟合精度、场景多样性稳定性方面优于传统生成模型,为电力系统在高比例新能源接入下的规划、调度风险评估提供了可靠的场景输入支持。; 适合人群:具备一定Python编程能力和机器学习基础,从事新能源发电预测、电力系统分析、智能优化、场景生成等方向研究的科研人员、高校研究生及工程技术人员。; 使用场景及目标:①用于高海拔地区风电、光伏出力的不确定性建模多场景生成;②支撑含高渗透率新能源的电力系统随机优化调度、鲁棒决策风险评估;③为相关学术研究、论文复现算法改进提供可运行的技术方案代码基础; 阅读建议:建议读者结合所提供的完整资源(代码、数据集、说明文档)进行实践操作,重点关注扩散模型的前向加噪反向去噪过程的设计细节,以及如何将其适配于新能源时序数据的生成任务,通过参数调优对比实验深入理解模型的生成机制性能边界。
内容概要:本文围绕基于静态约束法的配电网电动汽车接入容量评估展开研究,提出了一种在新型电力系统背景下评估主动配电网对电动汽车承载能力的方法。研究通过构建数学模型,结合潮流计算关键约束条件(如电压越限、线路过载等),量化分析配电网可承受的最大电动汽车充电负荷容量,旨在识别规模化电动汽车接入带来的潜在运行风险,并为电网规划运行提供科学依据。文中配套提供了完整的Matlab代码实现,便于仿真验证结果复现。此外,该研究分布式光伏承载力评估、电动汽车可调能力分析等方向形成技术联动,展现了多主题协同的研究体系。; 适合人群:具备电力系统分析基础理论知识及Matlab编程能力的高校研究生、科研机构研究人员,以及从事新能源并网、智能配电网规划运行等相关领域的工程技术人员。; 使用场景及目标:①用于学术研究中的模型复现论文撰写支撑;②评估实际配电网中电动汽车大规模接入的可行性安全边界,指导充电基础设施布局;③作为高校教学案例,帮助学生深入理解电网承载力评估的核心原理、建模方法仿真技术; 阅读建议:建议结合文中提及的相关研究方向(如二阶锥规划、多面体聚合方法等)进行对比学习,充分利用所提供的Matlab代码网盘资料开展仿真实验,重点关注约束条件的设定逻辑潮流计算模块的实现细节,以深化对评估模型机理工程应用价值的理解。
内容概要:本文围绕“考虑隐私保护的分布式联邦学习电力负荷预测研究”展开,提出了一种基于Python实现的联邦学习框架,旨在解决居民或行业电力负荷预测中用户电表数据隐私泄露的风险。该研究通过构建分布式机器学习模型,使各参方在不共享原始数据的前提下协同训练全局模型,有效实现了数据“可用不可见”。文中详细阐述了联邦学习的整体架构设计、本地模型训练流程、参数加密传输安全聚合机制,并结合差分隐私等技术进一步增强系统的隐私保护能力。同时,研究利用真实电力负荷数据集进行了实验验证,展示了方法在预测精度隐私保障之间的良好平衡,并提供了完整的代码实例复现指南,便于后续研究应用拓展。; 适合人群:具备一定机器学习基础和电力系统背景知识,从事智慧能源、隐私计算或人工智能相关方向研究的研究生、科研人员及工程技术人员。; 使用场景及目标:① 实现跨区域、跨主体的电力负荷协同预测,打破数据孤岛;② 在确保用户用电数据隐私安全的前提下提升负荷预测准确性;③ 推动联邦学习在智能电网、需求响应、虚拟电厂等场景中的实际部署应用。; 阅读建议:建议结合文中提供的Python代码网盘资料进行动手实践,重点关注联邦学习的通信轮次设计、模型聚合算法(如FedAvg)的实现细节以及差分隐私噪声添加策略,深入理解其对模型性能隐私强度的影响,为进一步优化创新奠定基础。
VDA_Band_19.1_3rd edition_2026 English Inspection of Technical Cleanliness 内容概要:本文档为德国汽车工业协会(VDA)发布的第三版《技术清洁度检验:功能相关汽车部件的颗粒污染检测》(VDA 19.1),系统规范了汽车行业中零部件技术清洁度的检测方法流程。文件涵盖从取样、提取、过滤到分析的全流程标准化操作,重点更新了干法提取(如 Stamp Test 和刷吸法)、小于50µm颗粒的检测、光学子系统和SEM/EDX标准分析方法,并引入统一材料分类体系以提升结果可比性。同时明确了“标准分析”“自由检验”的区别,前者用于高兼容性检测,后者允许客户供应商协商定制参数。文档还强化了对非可测组件的技术清洁保障、测量不确定度评估及方法验证的要求,并提供了多个实际案例支持应用落地。; 适合人群:适用于汽车制造业中从事质量控制、工艺开发、供应商管理及相关检测实验室的技术人员和管理人员,尤其适合具备一定质量管理或洁净度检测基础的专业人员。; 使用场景及目标:①用于制定和执行零部件清洁度检测标准;②指导 incoming/outgoing 检验及生产过程监控;③支持失效分析质量改进项目;④作为企业内部审核和技术交流的依据; 阅读建议:建议结合VDA 19.2及其他相关标准配套使用,重点关注各章节中的起始参数设定、方法选择逻辑及附录中的检查表示例,在实际操作中同步开展方法验证人员培训,确保检测结果的有效性和可追溯性。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值