Unity UGUI Selectable部分源码浅析

2024-01-30 09:44

本文主要是介绍Unity UGUI Selectable部分源码浅析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

private readonly List<CanvasGroup> m_CanvasGroupCache = new List<CanvasGroup>();protected override void OnCanvasGroupChanged(){//判断父节点中是否允许交互var groupAllowInteraction = true;Transform t = transform;while (t != null){t.GetComponents(m_CanvasGroupCache);bool shouldBreak = false;for (var i = 0; i < m_CanvasGroupCache.Count; i++){//如果父节点中的canvasgroup不允许交互,那么断开循环,设置falseif (!m_CanvasGroupCache[i].interactable){groupAllowInteraction = false;shouldBreak = true;}// 如果此节点设置了“忽略父设置”,那么也直接断开if (m_CanvasGroupCache[i].ignoreParentGroups)shouldBreak = true;}if (shouldBreak)break;t = t.parent;}if (groupAllowInteraction != m_GroupsAllowInteraction){m_GroupsAllowInteraction = groupAllowInteraction;OnSetProperty();}}

在canvasGroup属性改变时调用OnCanvasGroupChanged

public virtual bool IsInteractable(){return m_GroupsAllowInteraction && m_Interactable;}

物体的可交互属性需要一起判断m_GroupsAllowInteraction 和 m_Interactable,即自身或父物体上的canvasGroup是否允许交互,以及自身是否允许交互

protected override void OnEnable(){base.OnEnable();if (s_IsDirty)RemoveInvalidSelectables();  //如果标记为脏,就从可选择物体数组中移除m_WillRemove = false;if (s_SelectableCount == s_Selectables.Length)   //如果可选择物体数组满了,则扩容两倍{Selectable[] temp = new Selectable[s_Selectables.Length * 2];Array.Copy(s_Selectables, temp, s_Selectables.Length);s_Selectables = temp;}s_Selectables[s_SelectableCount++] = this;isPointerDown = false;DoStateTransition(currentSelectionState, true);  //设置初始状态}
protected override void OnTransformParentChanged(){base.OnTransformParentChanged();// 如果父物体有改变,需要判断自身和父物体的canvasGroup属性是否有改变OnCanvasGroupChanged();}
private void OnSetProperty(){
#if UNITY_EDITORif (!Application.isPlaying)DoStateTransition(currentSelectionState, true);else
#endifDoStateTransition(currentSelectionState, false);}

设置状态属性,如果是Editor模式,如果正在运行,那么立即改变,没有过渡,如果非Editor模式,那么有过渡

        protected override void OnDisable(){m_WillRemove = true;s_IsDirty = true;InstantClearState();  //重置各种状态base.OnDisable();}

取消激活时,将自身标记为脏

private static void RemoveInvalidSelectables(){for (int i = s_SelectableCount - 1; i >= 0; --i){// Swap last element in array with element to be removedif (s_Selectables[i] == null || s_Selectables[i].m_WillRemove)s_Selectables[i] = s_Selectables[--s_SelectableCount];}s_IsDirty = false;}

将元素从可选择物体数组列表中移除,同时标记为脏

protected SelectionState currentSelectionState{get{if (!IsInteractable())return SelectionState.Disabled;if (isPointerDown)return SelectionState.Pressed;if (hasSelection)return SelectionState.Selected;if (isPointerInside)return SelectionState.Highlighted;return SelectionState.Normal;}}

获取当前的选择状态

protected virtual void InstantClearState(){string triggerName = m_AnimationTriggers.normalTrigger;isPointerInside = false;isPointerDown = false;hasSelection = false;switch (m_Transition){case Transition.ColorTint:StartColorTween(Color.white, true);break;case Transition.SpriteSwap:DoSpriteSwap(null);break;case Transition.Animation:TriggerAnimation(triggerName);break;}}

即时清除状态,将Selectable的状态还原

protected virtual void DoStateTransition(SelectionState state, bool instant){if (!gameObject.activeInHierarchy)return;Color tintColor;Sprite transitionSprite;string triggerName;switch (state){case SelectionState.Normal:tintColor = m_Colors.normalColor;transitionSprite = null;triggerName = m_AnimationTriggers.normalTrigger;break;case SelectionState.Highlighted:tintColor = m_Colors.highlightedColor;transitionSprite = m_SpriteState.highlightedSprite;triggerName = m_AnimationTriggers.highlightedTrigger;break;case SelectionState.Pressed:tintColor = m_Colors.pressedColor;transitionSprite = m_SpriteState.pressedSprite;triggerName = m_AnimationTriggers.pressedTrigger;break;case SelectionState.Selected:tintColor = m_Colors.selectedColor;transitionSprite = m_SpriteState.selectedSprite;triggerName = m_AnimationTriggers.selectedTrigger;break;case SelectionState.Disabled:tintColor = m_Colors.disabledColor;transitionSprite = m_SpriteState.disabledSprite;triggerName = m_AnimationTriggers.disabledTrigger;break;default:tintColor = Color.black;transitionSprite = null;triggerName = string.Empty;break;}switch (m_Transition){case Transition.ColorTint:StartColorTween(tintColor * m_Colors.colorMultiplier, instant);break;case Transition.SpriteSwap:DoSpriteSwap(transitionSprite);break;case Transition.Animation:TriggerAnimation(triggerName);break;}}

状态切换方法

public Selectable FindSelectable(Vector3 dir){dir = dir.normalized;Vector3 localDir = Quaternion.Inverse(transform.rotation) * dir;Vector3 pos = transform.TransformPoint(GetPointOnRectEdge(transform as RectTransform, localDir));float maxScore = Mathf.NegativeInfinity;Selectable bestPick = null;if (s_IsDirty)RemoveInvalidSelectables();for (int i = 0; i < s_SelectableCount; ++i){Selectable sel = s_Selectables[i];if (sel == this)continue;if (!sel.IsInteractable() || sel.navigation.mode == Navigation.Mode.None)continue;#if UNITY_EDITOR除了运行时使用,FindSelectable被自定义编辑器用来在不同的可选项之间绘制箭头。对于场景视图摄像机,只考虑同一阶段的可选对象。if (Camera.current != null && !UnityEditor.SceneManagement.StageUtility.IsGameObjectRenderedByCamera(sel.gameObject, Camera.current))continue;
#endifvar selRect = sel.transform as RectTransform;Vector3 selCenter = selRect != null ? (Vector3)selRect.rect.center : Vector3.zero;Vector3 myVector = sel.transform.TransformPoint(selCenter) - pos;//值就是沿着这个方向的距离float dot = Vector3.Dot(dir, myVector);//跳过方向错误或距离为0的元素这也确保了下面的评分公式不会有除数为零的误差if (dot <= 0)continue;// This scoring function has two priorities:// - Score higher for positions that are closer.// - Score higher for positions that are located in the right direction.// This scoring function combines both of these criteria.// It can be seen as this://   Dot (dir, myVector.normalized) / myVector.magnitude// The first part equals 1 if the direction of myVector is the same as dir, and 0 if it's orthogonal.// The second part scores lower the greater the distance is by dividing by the distance.// The formula below is equivalent but more optimized.//// If a given score is chosen, the positions that evaluate to that score will form a circle// that touches pos and whose center is located along dir. A way to visualize the resulting functionality is this:// From the position pos, blow up a circular balloon so it grows in the direction of dir.// The first Selectable whose center the circular balloon touches is the one that's chosen.float score = dot / myVector.sqrMagnitude;if (score > maxScore){maxScore = score;bestPick = sel;}}return bestPick;}

此方法用来寻找临近的可选择对象

public virtual void OnMove(AxisEventData eventData){switch (eventData.moveDir){case MoveDirection.Right:Navigate(eventData, FindSelectableOnRight());break;case MoveDirection.Up:Navigate(eventData, FindSelectableOnUp());break;case MoveDirection.Left:Navigate(eventData, FindSelectableOnLeft());break;case MoveDirection.Down:Navigate(eventData, FindSelectableOnDown());break;}}

当焦点移动到另一个可选择对象时,调用此方法,确定选择的对象

protected bool IsHighlighted(){if (!IsActive() || !IsInteractable())return false;return isPointerInside && !isPointerDown && !hasSelection;}

判断是否高亮,即鼠标移上去的时候,需判断三个状态,鼠标在UI范围内,没有点下,没有选择

protected bool IsPressed(){if (!IsActive() || !IsInteractable())return false;return isPointerDown;}

是否按下

private void EvaluateAndTransitionToSelectionState(){if (!IsActive() || !IsInteractable())return;DoStateTransition(currentSelectionState, false);}

执行状态改变

public virtual void OnPointerDown(PointerEventData eventData){if (eventData.button != PointerEventData.InputButton.Left)  //如果不是鼠标左键,returnreturn;//如果可以交互,导航模式不为空,且EventSystem存在,那么设置EventSystem的当前选择物体if (IsInteractable() && navigation.mode != Navigation.Mode.None && EventSystem.current != null)EventSystem.current.SetSelectedGameObject(gameObject, eventData);isPointerDown = true;EvaluateAndTransitionToSelectionState();}

鼠标按下回调

public virtual void OnPointerUp(PointerEventData eventData){if (eventData.button != PointerEventData.InputButton.Left)return;isPointerDown = false;EvaluateAndTransitionToSelectionState();}

鼠标抬起回调

public virtual void OnPointerEnter(PointerEventData eventData){isPointerInside = true;EvaluateAndTransitionToSelectionState();}

鼠标进入回调

public virtual void OnPointerExit(PointerEventData eventData){isPointerInside = false;EvaluateAndTransitionToSelectionState();}

鼠标离开回调

public virtual void OnSelect(BaseEventData eventData){hasSelection = true;EvaluateAndTransitionToSelectionState();}

选择回调

public virtual void OnDeselect(BaseEventData eventData){hasSelection = false;EvaluateAndTransitionToSelectionState();}

取消选择回调

public virtual void Select(){if (EventSystem.current == null || EventSystem.current.alreadySelecting)return;EventSystem.current.SetSelectedGameObject(gameObject);}

直接确定EventSystem的选择物体

这篇关于Unity UGUI Selectable部分源码浅析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/659838

相关文章

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

poj 2976 分数规划二分贪心(部分对总体的贡献度) poj 3111

poj 2976: 题意: 在n场考试中,每场考试共有b题,答对的题目有a题。 允许去掉k场考试,求能达到的最高正确率是多少。 解析: 假设已知准确率为x,则每场考试对于准确率的贡献值为: a - b * x,将贡献值大的排序排在前面舍弃掉后k个。 然后二分x就行了。 代码: #include <iostream>#include <cstdio>#incl

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除

red5-server源码

red5-server源码:https://github.com/Red5/red5-server