Unity记录7.4-界面-UI的缩放、菜单、菜单关闭、预制体UI

汇总:Unity 记录

开源地址:asd123pwj/asdGame

摘要:UI的缩放、右键菜单、菜单失焦关闭,以及从预制体中加载UI。

UI缩放-2024/05/21

  • 最好的缩放方式肯定是Windows那样的,然而UI位置、UI尺寸、鼠标位置的坐标系不是同一个,我转换不来。
  • 所以妥协了一下,只能缩放两个方向,向右和向下。
  • 代码如下
    • 一个关键点是,UI的RectTransform.pivot得是(0, 1),即左上角。
    • 和拖动不同,缩放用的鼠标位置是ScreenPointToLocalPointInRectangle
public class UIResizeButtom: UIComponentBase{
    public UIResizeButtom(GameObject parent): base(parent, "ResizeButtom"){
        _image_name = "ResizeButtom";
        _set_UIPos = _set_UIPos_RightBottom;
        _sizeDelta = new (50, 50);
        // _rotation = Quaternion.Euler(0, 0, 90);
    }

    public override void _init_eventInteraction(){
        // ----- set top
        _Cfg._Event._event_PointerDown.Add(_Cfg._Interact._set_top);

        // ----- resize right
        _Cfg._Event._event_PointerDown.Add(_Cfg._Interact._update_mouseDown_mousePos);
        _Cfg._Event._event_PointerDown.Add(_Cfg._Interact._update_mouseDown_size);
        _Cfg._Event._event_Drag.Add(_Cfg._Interact._update_mouseHold_mousePos);
        _Cfg._Event._event_Drag.Add(_Cfg._Interact._resize);
    }
}
    public void _update_mouseDown_size(PointerEventData eventData){ 
        mouseDown_size = rt.sizeDelta; 
    }
    public void _update_mouseDown_mousePos(PointerEventData eventData){ 
        RectTransformUtility.ScreenPointToWorldPointInRectangle(rt, eventData.position, eventData.pressEventCamera, out mouseDown_mousePos_world); 
        RectTransformUtility.ScreenPointToLocalPointInRectangle(rt, eventData.position, eventData.pressEventCamera, out mouseDown_mousePos_local); 
    }
    public void _update_mouseHold_mousePos(PointerEventData eventData){ 
        RectTransformUtility.ScreenPointToWorldPointInRectangle(rt, eventData.position, eventData.pressEventCamera, out mouseHold_mousePos_world); 
        RectTransformUtility.ScreenPointToLocalPointInRectangle(rt, eventData.position, eventData.pressEventCamera, out mouseHold_mousePos_local); 
    }

    public void _resize(PointerEventData eventData){ 
        if (!_check_downL(eventData)) return; 
        rt.sizeDelta = new(mouseDown_size.x + mouseHold_mousePos_local.x - mouseDown_mousePos_local.x, mouseDown_size.y - mouseHold_mousePos_local.y + mouseDown_mousePos_local.y); 
    }

右键打开子菜单-2024/05/21

  • 实现起来较简单,只要把rt.anchoredPosition设为鼠标点击时的ScreenPointToLocalPointInRectangle即可。
  • 此外,子菜单的pivot需要和UI一致,我这里都是左上角。
public class UIRightMenu: UIComponentBase{
    public UIRightMenu(GameObject parent, Vector2 position): base(parent, "RightMenu"){
        _image_name = "RightMenu";
        _set_UIPos_LeftTop();
        _anchoredPosition = position;
        _sizeDelta = new (300, 200);
    }

    public override void _init_eventInteraction(){
        // ----- set top
        _Cfg._Event._event_PointerDown.Add(_Cfg._Interact._set_top);
    }
}
    public void _open_rightMenu(PointerEventData eventData) { 
        if (!_check_downR(eventData)) return; 
        UIRightMenu rightMenu = new(Cfg._parent, mouseDown_mousePos_local); 
    }

失焦关闭菜单-2024/05/21

  • 这需要用到deselect事件,
    • deselect事件又需要有一个可选择的组件的navigation
    • 因此,在生成右键菜单组件时,同时给该组件附加一个按钮buttom
    • 由于按钮的选中状态会改变外观,因此需要设置buttom.transition = Selectable.Transition.None
  • deselect事件设置一个关闭方法,上篇的Close一样,设置GameObject.SetActive(false)
    • 这样,当鼠标按下菜单外的任何位置时,将关闭该菜单。
  • 此外,每次打开菜单应该是打开同一个,
    • 因此,仅需在第一次时创建右键菜单,
    • 后续打开时,先将菜单挪至鼠标位置,再启用GameObject.SetActive(true)
  • 此外,我修改为pointerClick事件触发,
    • 即按下并松开后,打开菜单
    • 因此,使用的位置是松开时的鼠标位置,或是拖动时的鼠标位置,
    • 由于我是给背景添加的右键菜单,背景同时还记录的拖动位置,因此我用拖动位置。
public class UIRightMenu: UIComponentBase{
    public UIRightMenu(GameObject parent, Vector2 position): base(parent, "RightMenu"){
        _image_name = "RightMenu";
        _set_UIPos_LeftTop();
        _anchoredPosition = position;
        _sizeDelta = new (300, 200);
        _enableNavigation = true;
    }

    public override void _init_eventInteraction(){
        // ----- set top
        _Cfg._Event._event_PointerDown.Add(_Cfg._Interact._set_top);
        // ----- Close
        _Cfg._Event._event_Deselect.Add(_Cfg._Interact._closeSelf);
    }
}
    // ---------- Interacton ----------
    public void _enableSelf(){ _Cfg._self.SetActive(true); }
    public void _enableSelf(Vector2 pos){
        _Cfg._self.GetComponent<RectTransform>().position = pos;
        _Cfg._self.SetActive(true); 
    }

    void set_navigation(){
        if (!_enableNavigation) return;
        Button btm = _self.AddComponent<Button>();
        btm.transition = Selectable.Transition.None;
        EventSystem.current.SetSelectedGameObject(_self);
    } 

滚动窗口-2024/05/25

  • Unity的UI-Scroll View即滚动窗口
    • 创建后,在Scroll View的子对象Viewport的子对象Content中,添加Grid Layout Group组件,即可网格式排列子对象。
    • 其排列的内容,为Content的子对象。
  • Grid的大小与间隔空间、Scroll View的RectTransform宽高、滚动条ScrollBar的宽高是一个坐标系。
    • 例如,Grid大小为100,间隔为10,ScrollView宽度为1200时,11个Grid刚好填满
    • 1200 = (100+10) (11-1) + 100 1
    • 当存在滚动条时,滚动条默认宽度为20,则ScrollView宽度为1220时,11个Grid刚好填满
    • 1220 = (100+10) (11-1) + 100 1 + 20
    • 这意味着能够自动调整最适应的网格行列数。
  • 内容的添加后续实现,因为从空GameObject创建一个滚动窗口麻烦,所以打算从预制体来。

从预制体加载UI-2024/05/26

  • 这个步骤和"Unity记录6.2-动作-角色生成"几乎一致,因为都是加载prefab,不像上篇7.3,是加载图片。
{
    "version": "0.0.1",
    "UIPrefabs":{
        "ScrollView":{
            "name": "ScrollView",
            "path": "Assets/Resources_addressable/Prefab/UI/ScrollView.prefab"
        }
    }
}
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using Cysharp.Threading.Tasks;
using Unity.VectorGraphics;

public struct UIPrefabInfo{
    public string name;
    public string path;
}

public struct UIPrefabsInfo{
    public string version;
    public Dictionary<string, UIPrefabInfo> UIPrefabs;
}

public class UIPrefabList{
    GameConfigs _game_configs;
    public UIPrefabsInfo _UIPrefabs_info;
    public Dictionary<string, GameObject> _name2UIPrefab = new();

    public UIPrefabList(GameConfigs game_configs){
        _game_configs = game_configs;
        load_UIPrefabs();
    }

    public bool _check_UIPrefab_loaded(string name){
        return _name2UIPrefab.ContainsKey(name);
    }

    public GameObject _get_UIPrefab(string name){
        return _name2UIPrefab[name];
    }

    async UniTaskVoid load_UIPrefab(string name){
        string UIPrefab_path = _UIPrefabs_info.UIPrefabs[name].path;
        AsyncOperationHandle<GameObject> handle = Addressables.LoadAssetAsync<GameObject>(UIPrefab_path);
        handle.Completed += action_UIPrefab_loaded;
        await UniTask.Yield();
    }

    void load_UIPrefabs(){
        string jsonText = File.ReadAllText(_game_configs.__UIPrefabsInfo_path);
        _UIPrefabs_info = JsonConvert.DeserializeObject<UIPrefabsInfo>(jsonText);
        foreach (var object_kv in _UIPrefabs_info.UIPrefabs){
            load_UIPrefab(object_kv.Key).Forget();
        }
    }

    void action_UIPrefab_loaded(AsyncOperationHandle<GameObject> handle){
        if (handle.Status == AsyncOperationStatus.Succeeded) _name2UIPrefab.Add(handle.Result.name, handle.Result);
        else Debug.LogError("Failed to load prefab: " + handle.DebugName);
        Addressables.Release(handle);
    }
}

版权声明:
作者:MWHLS
链接:https://mwhls.top/5024.html
来源:无镣之涯
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
< <上一篇
下一篇>>
文章目录
关闭
目 录