Unity 之 使用原生UGUI实现随手移动摇杆功能经典实例

Unity 之 使用原生UGUI实现随手移动摇杆功能
- 实现效果
- 一,实现思路
- 1.1 原理解析
- 1.2 思路概述
- 二,实现代码
- 2.1 随手落下
- 2.2 摇杆转动
- 三,源码分享
- 3.1 场景搭建
- 3.2 完整代码
- 3.3 实现效果
实现效果
本文最终实现效果:

一,实现思路
1.1 原理解析
做一个实验看一下使用ScrollRect组件实现摇杆的原理。
-
在
Hierarchy面板右键UI->Scroll View创建一个滚动视图,这个组件经常被应用于排行榜,选角色之类的可滑动的界面。

-
在
Scroll View->Viewport->Content添加一个Image组件

-
运行场景,鼠标点击并拖动中间部分,即可看到如下效果:

看到这里基本了解实现思路了吧,其实就是通过Scroll Rect组件的Context和Viewport的关系来进行模拟的。

更多关于ScrollRect的使用方法和实战应用,可以查看:Unity 之 UGUI Scroll Rect滚动矩形组件详解
1.2 思路概述
-
随手指落下位置
思路:其实就是根据手指第一次落下的屏幕坐标,修改摇杆的初始位置;手抬起时再将摇杆位置还原
知识点:获取手指按下和抬起的回调,将手指落下坐标转换为屏幕UI坐标 -
摇杆移动
思路:使用Scroll Rect的移动回调,来控制中间的虚拟摇杆进行位置变化
注意的点:使用OnDrag进行回调,并来控制虚拟摇杆的标移动位置不要超出背景 -
移动回调
思路:当使用摇杆时使用Update进行实时回调
注意的点:使用OnEndDrag进行回调,还原要个位置
二,实现代码
2.1 随手落下
通过锚点和RectTransformUtility坐标转换方法进行位置设置。
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;[RequireComponent(typeof(EventTrigger))]
public class JoystickTouch : ScrollRect
{ protected override void Start(){ EventTrigger trigger = GetComponent<EventTrigger>();EventTrigger.Entry entryPointerUp = new EventTrigger.Entry();entryPointerUp.eventID = EventTriggerType.PointerUp;entryPointerUp.callback.AddListener((data) => { OnPointerUp((PointerEventData)data); });trigger.triggers.Add(entryPointerUp);EventTrigger.Entry entryPointerDown = new EventTrigger.Entry();entryPointerDown.eventID = EventTriggerType.PointerDown;entryPointerDown.callback.AddListener((data) => { OnPointerDown((PointerEventData)data); });trigger.triggers.Add(entryPointerDown);}// 随手落下设置摇杆位置private void OnPointerDown(PointerEventData eventData){Vector2 LocalPosition;RectTransformUtility.ScreenPointToLocalPointInRectangle(this.GetComponent<RectTransform>(),eventData.position, eventData.pressEventCamera, out LocalPosition);this.viewport.localPosition = LocalPosition;}// 抬起还原位置private void OnPointerUp(PointerEventData eventData){this.viewport.anchoredPosition3D = Vector3.zero;}}
2.2 摇杆转动
还是在上面的类中重写OnDrag方法,进行虚拟摇杆中间位置的控制。
public class JoystickTouch : ScrollRect
{ // 半径 -- 控制拖拽区域private float mRadius;protected override void Start(){mRadius = this.content.sizeDelta.x * 0.5f;}public override void OnDrag(PointerEventData eventData){base.OnDrag(eventData);joyIsCanUse = true;//虚拟摇杆移动Vector3 contentPosition = this.content.anchoredPosition;if (contentPosition.magnitude > mRadius){contentPosition = contentPosition.normalized * mRadius;SetContentAnchoredPosition(contentPosition);}// 摇杆内部按钮旋转//if (content.anchoredPosition.y != 0)//{// content.eulerAngles = new Vector3(0, 0,// Vector3.Angle(Vector3.right, content.anchoredPosition) * content.anchoredPosition.y /// Mathf.Abs(content.anchoredPosition.y) - 90);//}}
}
三,源码分享
3.1 场景搭建
创建三个Image一个作为一个的子物体,依次为:接收点击背景面积,摇杆背景板,摇杆中的虚拟按钮。
第一个Image挂载新建脚本JoystickTouch
场景搭建如下:

3.2 完整代码
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;[RequireComponent(typeof(EventTrigger))]
public class JoystickTouch : ScrollRect
{/// <summary>/// 拖动差值/// </summary>public Vector2 offsetValue;// 半径 -- 控制拖拽区域private float mRadius;/// <summary>/// 移动中回调/// </summary>public System.Action<RectTransform> JoystickMoveHandle;/// <summary>/// 移动结束回调/// </summary>public System.Action<RectTransform> JoystickEndHandle;/// <summary>/// 摇杆是否处于可用状态/// </summary>public bool joyIsCanUse = false;protected override void Start(){mRadius = this.content.sizeDelta.x * 0.5f;EventTrigger trigger = GetComponent<EventTrigger>();EventTrigger.Entry entryPointerUp = new EventTrigger.Entry();entryPointerUp.eventID = EventTriggerType.PointerUp;entryPointerUp.callback.AddListener((data) => { OnPointerUp((PointerEventData)data); });trigger.triggers.Add(entryPointerUp);EventTrigger.Entry entryPointerDown = new EventTrigger.Entry();entryPointerDown.eventID = EventTriggerType.PointerDown;entryPointerDown.callback.AddListener((data) => { OnPointerDown((PointerEventData)data); });trigger.triggers.Add(entryPointerDown);}protected override void OnEnable(){joyIsCanUse = false;offsetValue = Vector2.zero;}public override void OnDrag(PointerEventData eventData){base.OnDrag(eventData);joyIsCanUse = true;//虚拟摇杆移动Vector3 contentPosition = this.content.anchoredPosition;if (contentPosition.magnitude > mRadius){contentPosition = contentPosition.normalized * mRadius;SetContentAnchoredPosition(contentPosition);}// 摇杆内部按钮旋转//if (content.anchoredPosition.y != 0)//{// content.eulerAngles = new Vector3(0, 0,// Vector3.Angle(Vector3.right, content.anchoredPosition) * content.anchoredPosition.y /// Mathf.Abs(content.anchoredPosition.y) - 90);//}}private void FixedUpdate(){if (joyIsCanUse){JoystickMoveHandle?.Invoke(this.content);offsetValue = this.content.anchoredPosition3D;}}public override void OnEndDrag(PointerEventData eventData){base.OnEndDrag(eventData);joyIsCanUse = false;offsetValue = Vector2.zero;JoystickEndHandle?.Invoke(this.content);}// 随手落下设置摇杆位置private void OnPointerDown(PointerEventData eventData){Vector2 LocalPosition;RectTransformUtility.ScreenPointToLocalPointInRectangle(this.GetComponent<RectTransform>(),eventData.position, eventData.pressEventCamera, out LocalPosition);this.viewport.localPosition = LocalPosition;}// 抬起还原位置private void OnPointerUp(PointerEventData eventData){this.viewport.anchoredPosition3D = Vector3.zero;}}
3.3 实现效果
按钮素材图片:


实现效果:

工程下载:源码和步骤都在上面分享过了,若还有什么不明白的,可以 点击链接下载 ,积分不够的童鞋关注下方卡片,回复:“摇杆” 即可获得开篇Demo源码~
相关文章:
Unity 之 使用原生UGUI实现随手移动摇杆功能经典实例
Unity 之 使用原生UGUI实现随手移动摇杆功能实现效果一,实现思路1.1 原理解析1.2 思路概述二,实现代码2.1 随手落下2.2 摇杆转动三,源码分享3.1 场景搭建3.2 完整代码3.3 实现效果实现效果 本文最终实现效果: 一,实现…...
Linux内核源代码概述
Linux内核源代码非常庞大,截止到2015年据统计代码总量就已经超过1500万行(LOC,Line of Code),看代码总量非常吓人,具体看这1500万行代码的大致分布情况如下图。 显然占比最大的drivers和arch目录下的代码合…...
Nginx 教程-动静分离
一、Nginx 动静分离理论1、概念今天学习和梳理Nginx动静分离,动静分离是将网站静态资源(HTML,JavaScript,CSS,img等文件)与后台应用分开部署,之所以要进行动静分离,其一为了提高前端…...
自己设计的网站,如何实现分页功能?(详细代码+注释)
目录 前言 实现分页功能 需求分析 客户端开发 服务器开发 前后端交互——两种前端得到 文章总页数 的方法,那种更合适? 前言 你在设计网站的时候是否有过这样的烦恼:“我设计的网站怎么就是从上到下一条线内容全部展开,一点都…...
STM32F407控制微型推拉式电磁铁(通过继电器)
1、继电器 继电器相当于开关,单片机通过io口高低电平的控制来控制继电器的开闭。采用继电器的好处除了能够用低电压控制高电压(如32单片机控制220V的电压)外,还可以防止电流反冲,弄烧单片机。 本文采用3.3v的电磁铁&am…...
VS Code工作区用法
背景VS Code可以通过"文件/打开文件夹"来打开本地项目,但是想要打开多个项目便需要来回切换,比较费劲。此时就可以使用工作区功能,将不同的项目放置到同一个工作区中,这样切换项目的时候就会非常方便。操作方法打开其中…...
Mybatis-Plus SQLFeatureNotSupportedException: getObject with type问题解决
问题描述: Error attempting to get column modify_time from result set. Cause: java.sql.SQLFeatureNotSupportedException: getObject with type ; getObject with type; nested exception is java.sql.SQLFeatureNotSupportedException: getObject with type…...
Unity | 发布Android的那些事儿
1.使用UnityWebRequest获取StreamingAssets中的json文件(1)直接根据不同平台指定url路径IEnumerator AITalPredZhanHui(){string url;string fileName "girl.json"; #if UNITY_EDITOR || UNITY_STANDALONEurl "file://" Applicat…...
git为什么要先commit,然后pull,最后再push?而不是commit完直接push?
情况是这样的,现在远程有一个仓库,分支就一个,是master。然后我本地的仓库是从远程的master上clone下来的。大家都是clone下来,再在自己本地改好,再commit然后pull然后push,大家都是这么做的。那么现在问题…...
若依框架----源码分析(@RateLimiter)
若依作为最近非常火的脚手架,分析它的源码,不仅可以更好的使用它,在出错时及时定位,也可以在需要个性化功能时轻车熟路的修改它以满足我们自己的需求,同时也可以学习人家解决问题的思路,提升自己的技术水平…...
页面的重排和重绘?
思路: 网页渲染HTML文件到浏览器的过程->定义->如何优化网页渲染HTML文件到浏览器的过程HTML 文件通过HTML解析器解析生成DOM树;CSS文件通过CSS解析器生成CSSOM树;DOM树和CSSOM树生成渲染树(render tree)&#x…...
人脸检测-python和c++实现
人脸检测是计算机视觉领域中的一个重要应用,其目的是从图像或视频中自动检测出其中的人脸,并对其进行识别、跟踪等操作。人脸检测技术已经广泛应用于安防、人机交互、娱乐等领域,具有广泛的应用前景。 人脸检测的基本思路可以分为以下几个步骤: 图像预处理:首先需要对输入…...
PowerJob源码环境搭建
一、IEDA导入PowerJob源码 gitgithub.com:PowerJob/PowerJob.gitPowerJob 由调度服务器(powerjob-server)和执行器(powerjob-worker)两部分组成 powerjob-server 负责提供 Web 服务和完成任务的调度powerjob-worker 则负责执行用…...
天梯赛刷题小记 —— L2
最近在重刷 天梯赛,浅浅记录一下,进入L2阶段了 L2-001 紧急救援 解题思路:典型的dijkstra模板题,带路径记录与权重,方案数记录,解析出过 Dijkstra(兼路径) #include <bits/stdc.h> #define inf…...
Prometheus监控实战系列十九:监控Kubernetes集群(上)
Kuberentes是一款开源的容器编排产品,由Google开发后发布到社区,并在2015年将该项目捐献给了云原生基金会(Cloud Native Computing Foundation)。从2014年第一个版本发布以来,Kubernetes便迅速获得开源社区的追捧&…...
番茄学习法——亲测超级好用
今天给大家分享下我最近使用的学习方法,真的非常好用!大家用起来! 在日常的学习和工作中,我们经常会遇到一些难以克服的问题:分心、效率低下、焦虑等。为了帮助人们更好地学习和工作,一些学习方法和工具应运…...
vue 项目中使用高德地图
一、账号准备 首先,需要注册并登录高德地图开放平台,申请密钥。操作指引:高德地图开放平台 二、安装高德地图加载器 npm 安装: npm i amap/amap-jsapi-loader --save或者 yarn 安装: yarn add amap/amap-jsapi-loa…...
【每日一题】病人排队
题目描述小理是个热爱生活的孩子。病人登记看病,小理想编写一个程序,将登记的病人按照以下原则排出看病的先后顺序:1. 老年人(年龄 ≥≥ 60岁)比非老年人优先看病。2. 老年人按年龄从大到小的顺序看病,年龄…...
【数据结构】链表OJ题
目录面试题 02.04 分割链表剑指 Offer II 027 回文链表160 相交链表141 环形链表142 环形链表 II138 复制带随机指针的链表面试题 02.04 分割链表 定义lesshead和greaterhead链接小于和大于等于k的值分别设置哨兵位和尾节点指针最后将两表去除哨兵位再链接 struct ListNode* p…...
冒泡 VS 插入 VS 选择——谁更胜一筹?(附排序源码)
文章目录什么样的“排序算法”更加优质?排序算法的执行效率排序算法的内存消耗排序算法的稳定性冒泡排序(Bubble Sort)插入排序(Insertion Sort)选择排序(Selection Sort)最终的胜利者…...
从HTTP到gRPC:etcd v2与v3 API调用差异及Postman实战解析
1. etcd v2与v3 API的核心差异解析 第一次接触etcd时,你可能和我一样被网上的v2教程坑过——照着文档发送HTTP请求却总是返回404错误。这其实是因为etcd v3默认关闭了v2 API支持,而大多数中文教程还在用陈旧的v2示例。让我们先理清这两个版本的本质区别&…...
AMD显卡专属优化:Ollama-for-amd本地大模型部署终极指南
AMD显卡专属优化:Ollama-for-amd本地大模型部署终极指南 【免费下载链接】ollama-for-amd Get up and running with Llama 3, Mistral, Gemma, and other large language models.by adding more amd gpu support. 项目地址: https://gitcode.com/gh_mirrors/ol/ol…...
从零开始:使用VSCode + CMake + Ninja + GCC构建高效MCU开发环境
1. 为什么需要这套开发环境? 作为一名在嵌入式领域摸爬滚打多年的开发者,我深知传统IDE的痛点。记得刚入行时,公司清一色使用某商业IDE,直到某天收到法务部的紧急通知——需要立即处理软件版权问题。这让我意识到,基于…...
Analog离线引擎:从原理到实践的抗断网解决方案
Analog离线引擎:从原理到实践的抗断网解决方案 【免费下载链接】analog Meet the calendar that changes everything 项目地址: https://gitcode.com/gh_mirrors/analog4/analog 在数字化办公环境中,日程管理工具的网络依赖性常常成为效率瓶颈。远…...
IIC驱动进阶:实现非连续寄存器批量配置的模块化设计
1. IIC总线驱动开发的痛点与挑战 第一次接触摄像头模块配置时,我被密密麻麻的寄存器列表吓到了。OV5640摄像头需要配置上百个寄存器,地址从0x3100到0x5FFF不等,每个寄存器都有特定功能。更麻烦的是,这些寄存器地址完全不连续&…...
Java函数计算部署实战:从本地调试到生产环境上线的7个关键步骤(含阿里云/华为云/AWS对比)
第一章:Java函数计算部署全景概览Java函数计算是云原生场景下轻量级、事件驱动型服务的重要实现方式。它将传统Java应用的部署范式从虚拟机/容器迁移至按需执行、自动扩缩的无服务器架构,显著降低运维复杂度与资源闲置成本。开发者只需聚焦业务逻辑&…...
智能CPU性能优化工具:释放处理器潜能的系统级解决方案
智能CPU性能优化工具:释放处理器潜能的系统级解决方案 【免费下载链接】CPUDoc 项目地址: https://gitcode.com/gh_mirrors/cp/CPUDoc 当你在游戏中遭遇帧率骤降、视频渲染耗时过长,或是多任务处理时系统响应迟滞,这些问题的根源往往…...
MissionPlanner地面站调试Pixhawk:除了基础校准,你的F450还能设置这些高级功能
MissionPlanner地面站进阶指南:解锁Pixhawk飞控的隐藏潜力 当你已经能够熟练完成F450无人机的基础校准,让四轴稳稳升空只是起点而非终点。MissionPlanner作为Pixhawk飞控的瑞士军刀,藏着许多被普通教程忽略的进阶功能——这些功能往往决定着你…...
ES10(ES2019)新特性完整指南
ES10(ES2019)新特性发布时间:2019年6月 ES10 新增了数组扁平化、对象转换、字符串修剪等实用方法。1. Array.prototype.flat() 将嵌套数组"拉平",返回一个新数组: 基本用法 [1, 2, [3, 4]].flat(); //…...
零门槛视频创作:OpenCut高效替代方案全解析
零门槛视频创作:OpenCut高效替代方案全解析 【免费下载链接】OpenCut The open-source CapCut alternative 项目地址: https://gitcode.com/gh_mirrors/ap/OpenCut 在数字内容创作爆炸的时代,视频编辑工具的选择直接影响创作效率与作品质量。Open…...
