Unity3d 简单实现 NGUI UI系统

首先定义两个配置表:
UILevel.txt

层级ID    层级名称    层级起始深度  锁定层级(1,锁定层级,不会自动更新Panel深度)  层级描述
UILeveID    UILevelName UILevelDepth    UILevelLock UILevelDes
1   PanelPanel  0   0   常用面板,如主界面,匹配面板等等
2   TipsPanel   1000    0   tips面板
3   FixedPanel  1500    0   loading界面等等,金币等等

将UI分为3个层级,用于显示UI层次

UIConfig.txt 定义UI相关属性

UIID    UI描述    UI类型    UI层级    UI是否会被销毁(0不会销毁,1会销毁)    UI存在时间  UI路径    UI名称    UI锁定层级(1不会自动修改层级)
UIID    UIDes   UIType  UILevel UICanDestroy    UILife  UIPath  UIName  UILockDepth
1001    登录UI    Login   1   0   0   UI/Parfabs/ LoginPanel  0
1002    服务器选择UI Login   1   0   0   UI/Parfabs/ ServerPanel 0
1003    角色选择界面  Login   3   0   0   UI/Parfabs/ CreateRolePanel 0
1004    加载界面    Game    3   0   0   UI/Parfabs/ LoadingPanel    0
2001    游戏大厅    Game    1   0   0   UI/Parfabs/ MainPanel   0
2002    顶部信息面板  Game    3   0   0   UI/Parfabs/ TopPanel    0
2003    商城界面    Game    1   0   0   UI/Parfabs/ MallPanel   0
2004    背包面板    Game    1   1   10  UI/Parfabs/ BagPanel    0
2005    机械厂界面   Game    1   1   10  UI/Parfabs/ FactoryPanel    0
2006    剧情界面    Game    1   1   10  UI/Parfabs/ CheckPointPanel 0
2007    地图编辑界面  Game    1   1   10  UI/Parfabs/ EditorMapPanel  0
2008    战斗UI    Game    1   1   10  UI/Parfabs/ FightPanel  0
2009    匹配模式选择界面    Game    1   1   10  UI/Parfabs/ MatchTypePanel  0
2010    匹配组选择界面 Game    1   1   10  UI/Parfabs/ MatchTypeGourpPanel 0
2011    组队界面    Game    1   1   10  UI/Parfabs/ TeamPanel   0
2012    匹配结果界面  Game    2   1   10  UI/Parfabs/ MatchResPanel   0
2013    英雄选择界面  Game    1   1   10  UI/Parfabs/ SelHeroPanel    0
2014    道具信息界面  Game    2   1   10  UI/Parfabs/ PropsInfoPanel  0
2015    战斗加载界面  Fight   1   1   10  UI/Parfabs/ FightLoadPanel  0

顶一个枚举类型,用来控制所有UIid

public enum UIType
{
    /// <summary>
    /// 登录注册面板
    /// </summary>
    LoginPanel = 1001,
    /// <summary>
    /// 服务器面板
    /// </summary>
    ServerPanel = 1002,

    /// <summary>
    /// 角色选择面板
    /// </summary>
    SelectRolePanel = 1003,

    /// <summary>
    /// 加载面板
    /// </summary>
    LoadingPanel = 1004,

    /// <summary>
    /// 游戏大厅
    /// </summary>
    MainPanel = 2001,
    /// <summary>
    /// 游戏模式面板
    /// </summary>
    TopPanel = 2002,
    /// <summary>
    /// 组队面板
    /// </summary>
    MallPanel = 2003,
    /// <summary>
    /// 背包面板
    /// </summary>
    BagPanel = 2004,
    /// <summary>
    /// 机械厂面板
    /// </summary>
    FactoryPanel = 2005,
    /// <summary>
    /// 剧情面板
    /// </summary>
    CheckPointPanel = 2006,
    /// <summary>
    /// 地图编辑面板
    /// </summary>
    EditorMapPanel = 2007,
    /// <summary>
    /// 战斗UI
    /// </summary>
    FightPanel = 2008,
    /// <summary>
    /// 匹配模式选择
    /// </summary>
    MatchTypePanel = 2009,
    /// <summary>
    /// 匹配模式组选择
    /// </summary>
    MatchTypeGroupPanel = 2010,
    /// <summary>
    /// 组队面板
    /// </summary>
    TeamPanel = 2011,
    /// <summary>
    /// 匹配结果面板
    /// </summary>
    MatchResPanel = 2012,
    /// <summary>
    /// 选择英雄面板
    /// </summary>
    SelHeroPanel = 2013,
    /// <summary>
    /// 道具信息面板
    /// </summary>
    PropsInfoPanel = 2014,
    /// <summary>
    /// 战斗服连接面板
    /// </summary>
    FightLoadPanel = 2015,
    /// <summary>
    /// 网络连接失败
    /// </summary>
    NetConnectFaildPanel = 5001,

}

抽象类 UIBase.cs 是所有UI的基类,所有UI类均需要继承此类

using UnityEngine;
using System.Collections;

public abstract class UIBase : MonoBehaviour {

    /// <summary>
    /// UIID
    /// </summary>
    public UIType UIType { get; set; }
    /// <summary>
    /// UI配置信息
    /// </summary>
    public UIConfig UIConfig { get; set; }
    /// <summary>
    ///上一个UI数据
    /// </summary>
    private object[] PreUIData { get; set; }
    /// <summary>
    /// 上一个UI
    /// </summary>
    private UIBase PreUIBase { get; set; }
    /// <summary>
    /// 是否为打开状态
    /// </summary>
    public bool IsOpen { get; private set; }
    /// <summary>
    /// 上一次操作时间
    /// </summary>
    private System.DateTime OpenTime;

    /// <summary>
    /// 是否检测
    /// </summary>
    private bool isDetection = false;

    /// <summary>
    /// 打开UI
    /// </summary>
    /// <param name="data"></param>
    public void OpenUI(UIBase preUI,params object[] data)
    {
        IsOpen = true;
        this.gameObject.SetActive(true);
        PreUIBase = preUI;
        PreUIData = data;
        OpenTime = System.DateTime.Now;
        isDetection = false;
        OnOpenAni();
    }
    /// <summary>
    /// 打开UI动画
    /// </summary>
    /// <param name="data"></param>
    public virtual void OnOpenAni(params object[] data)
    {
        OnEnter(data);
    }

    /// <summary>
    /// 打开UI
    /// </summary>
    public void OpenUI()
    {
        this.gameObject.SetActive(true);
        OpenTime = System.DateTime.Now;
        isDetection = false;
        OnEnter(PreUIData);
    }
    /// <summary>
    /// 当更新
    /// </summary>
    /// <param name="data"></param>
    public void OnUpdate(params object[] data)
    {
        UpdateUI(data);
    }

    protected virtual void UpdateUI(params object[] data) {}

    /// <summary>
    /// 关闭UI动画
    /// </summary>
    public virtual void OnCloseAni()
    {
        OnClose();
    }
    /// <summary>
    /// 关闭UI
    /// </summary>
    protected void OnClose()
    {
        OnExit();
        IsOpen = false;
        isDetection = true;
        this.gameObject.SetActive(false);
    }


    /// <summary>
    /// 获取打开时间
    /// </summary>
    public System.DateTime GetOpenTime
    {
        get {
            return OpenTime;
        }
    }

    /// <summary>
    /// 初始化UI
    /// </summary>
    /// <param name="data"></param>
    public abstract void OnInit(params object[] data);

    /// <summary>
    /// 进入UI
    /// </summary>
    /// <param name="data"></param>
    protected abstract void OnEnter(params object[] data);

    /// <summary>
    /// 退出UI
    /// </summary>
    protected abstract void OnExit();

    /// <summary>
    /// 关闭UI
    /// </summary>
    protected void Close()
    {
        UIManager.Instance.CloseUI(UIType);
    }

    /// <summary>
    /// 打开上一层UI
    /// </summary>
    protected void OpenPreUI()
    {
        if(PreUIBase!=null)
            UIManager.Instance.OpenPreUI(PreUIBase,PreUIData);
    }
}

UIManager.cs 为UI管理类,包括打开UI,关闭UI等操作

using UnityEngine;
using System.Collections.Generic;
using System.Collections;
using System;

public enum UIFilter
{
    All,
    Open,
    Close,
}

public class UIManager : MonoBehaviour {

    private static UIManager _instance;
    public static UIManager Instance
    {
        get {
            return _instance;
        }
    }

    /// <summary>
    /// UI缓存
    /// </summary>
    private Dictionary<UIType, UIBase> uiCache = new Dictionary<UIType, UIBase>();
    /// <summary>
    /// 当前显示的主UI
    /// </summary>
    public UIBase CurUiBase { get;private set; }
    /// <summary>
    /// loading 界面
    /// </summary>
    public LoadingPanel LoadPanel { get; private set; }
    /// <summary>
    /// UI层级
    /// </summary>
    private Dictionary<int, Transform> uiLevel = new Dictionary<int, Transform>();


    void Awake()
    {
        _instance = this;
    }

    void Start()
    {
        List<UILevel> levelInfo = UILevel.TryGet();
        for (int i = 0; i < levelInfo.Count; i++)
        {
            Transform obj = CreateLevel(string.Format("{0} - {1} -{2}", levelInfo[i].UILevelID,levelInfo[i].UILevelName,levelInfo[i].UILevelDepth));
            uiLevel.Add(levelInfo[i].UILevelID,obj);
        }

        StartCoroutine(DetectionUI());
        if (LoadPanel == null)
        {
            LoadPanel = (LoadingPanel)GetUIBase(UIType.LoadingPanel);
            LoadPanel.CloseLoading();
        }

        OpenUI( UIType.LoginPanel);
        //OpenUI( UIType.MainPanel);
        //OpenTips(UIType.TopPanel);
        //MusicControl.Instance.PlayBackMusic("LoginAudio");
    }

    /// <summary>
    /// UI销毁计时器
    /// </summary>
    /// <returns></returns>
    IEnumerator DetectionUI()
    {
        yield return new WaitForSeconds(360);
        if (uiCache.Count > 0)
        {
            List<UIBase> bases = new List<UIBase>(uiCache.Values);
            for (int i = 0; i < bases.Count; i++)
            {
                if (!bases[i].UIConfig.UIisDestroy) continue;
                System.TimeSpan ts = System.DateTime.Now - bases[i].GetOpenTime;
                if (ts.TotalMinutes >= bases[i].UIConfig.UILife)
                {
                    Destroy(bases[i].gameObject);
                    uiCache.Remove(bases[i].UIType);
                }
            }
        }
    }

    /// <summary>
    /// 清楚UI
    /// </summary>
    /// <param name="type"></param>
    public void ClearScene(SceneType type)
    {
        List<UIBase> bases = new List<UIBase>(uiCache.Values);
        for (int i = 0; i < bases.Count; i++)
        {
            if (bases[i].UIConfig.UIType.Equals(type.ToString()))
            {
                Destroy(bases[i].gameObject);
                uiCache.Remove(bases[i].UIType);
            }
        }
    }

    /// <summary>
    /// 打开UI
    /// </summary>
    /// <param name="uiType"></param>
    /// <param name="data"></param>
    public void OpenUI(UIType uiType,params object[] data)
    {
        if (uiCache.ContainsKey(uiType))
        {
            if(CurUiBase!=null)
                CurUiBase.OnCloseAni();
            uiCache[uiType].OpenUI(CurUiBase, data);
            CurUiBase = uiCache[uiType];
            return;
        }
        UIBase uiBase = GetUIBase(uiType);
        if (uiBase == null) return;
        if (CurUiBase != null)
            CurUiBase.OnCloseAni();
        uiBase.OnInit();
        uiBase.OpenUI(CurUiBase, data);
        CurUiBase = uiBase;
    }

    /// <summary>
    /// 打开上一层UI
    /// </summary>
    /// <param name="uiBase"></param>
    /// <param name="data"></param>
    public void OpenPreUI(UIBase uiBase, params object[] data)
    {
        if (CurUiBase != null)
            CurUiBase.OnCloseAni();
        uiBase.OpenUI();
        CurUiBase = uiBase;
    }

    /// <summary>
    /// 打开tips
    /// </summary>
    /// <param name="uiType"></param>
    /// <param name="data"></param>
    public void OpenTips(UIType uiType, params object[] data)
    {
        if (uiCache.ContainsKey(uiType))
        {
            uiCache[uiType].OpenUI(CurUiBase, data);
            return;
        }
        UIBase uiBase = GetUIBase(uiType);
        if (uiBase == null) return;
        uiBase.OnInit();
        uiBase.OpenUI(CurUiBase, data);
    }

    /// <summary>
    /// 更新UI
    /// </summary>
    /// <param name="uiType"></param>
    /// <param name="data"></param>
    public void UpdateUI(UIType uiType, params object[] data)
    {
        UIBase uiBase = GetUI(uiType);
        if (uiBase != null)
            uiBase.OnUpdate(data);
    }


    /// <summary>
    /// 获取标签
    /// </summary>
    /// <param name="uiType"></param>
    /// <returns></returns>
    public UIBase GetUI(UIType uiType, UIFilter filter = UIFilter.Open)
    {
        switch (filter)
        {
            case UIFilter.Open:
                if (uiCache.ContainsKey(uiType) && uiCache[uiType].IsOpen)
                    return uiCache[uiType];
                break;
            case UIFilter.Close:
                if (uiCache.ContainsKey(uiType) && !uiCache[uiType].IsOpen)
                    return uiCache[uiType];
                break;
            case UIFilter.All:
                if (uiCache.ContainsKey(uiType))
                    return uiCache[uiType];
                break;
        }
        return null;
    }

    /// <summary>
    /// 获取UI
    /// </summary>
    /// <param name="uiType"></param>
    /// <returns></returns>
    private UIBase GetUIBase(UIType uiType)
    {
        UIConfig config = UIConfig.TryGet(((int)uiType).ToString());
        if (config == null)
        {
            Debug.Log(string.Format("获取ui:{0}-{1} 数据失败", uiType.ToString(), (int)uiType));
            return null;
        }
        GameObject uiObj = GameObject.Instantiate(Resources.Load(config.UIPath + config.UIName)) as GameObject;
        if (uiObj == null)
        {
            Debug.Log(string.Format("获取ui:{0}-{1} 模型失败", uiType.ToString(), (int)uiType));
            return null;
        }

        uiObj.transform.parent = uiLevel[config.UILevel];
        uiObj.transform.localPosition = Vector3.zero;
        uiObj.transform.localScale = Vector3.one;
        uiObj.transform.localEulerAngles = Vector3.zero;
        //如果锁定UI层级
        if(!config.UILockDepth)
            ChangePanel(config.UILevel,uiObj.transform);
        UIBase uiBase = uiObj.GetComponent<UIBase>();
        uiBase.UIConfig = config;
        uiBase.UIType = uiType;
        uiCache.Add(uiType, uiBase);
        return uiBase;
    }

    /// <summary>
    /// 关闭UI
    /// </summary>
    /// <param name="uiType"></param>
    public void CloseUI(UIType uiType)
    {
        if (uiCache.ContainsKey(uiType))
            uiCache[uiType].OnCloseAni();
    }

    /// <summary>
    /// 关闭所有UI
    /// </summary>
    public void CloseAll()
    {
        List<UIBase> bs = new List<UIBase>(uiCache.Values);
        for (int i = 0; i < bs.Count; i++)
        {
            if (bs[i].IsOpen)
                bs[i].OnCloseAni();
        }
    }
    /// <summary>
    /// 过滤关闭所有
    /// </summary>
    /// <param name="filter"></param>
    public void CloseAll(UIType filter)
    {
        List<UIBase> bs = new List<UIBase>(uiCache.Values);
        for (int i = 0; i < bs.Count; i++)
        {
            if (bs[i].IsOpen&&bs[i].UIType!=filter)
                bs[i].OnCloseAni();
        }
    }


    /// <summary>
    /// 创建层级
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    private Transform CreateLevel(string name)
    {
        GameObject go = new GameObject(name);
        go.transform.parent = this.transform;
        go.transform.localPosition = Vector3.zero;
        go.transform.localScale = Vector3.one;
        go.transform.localEulerAngles = Vector3.zero;
        return go.transform;
    }

    private void ChangePanel(int level,Transform obj)
    {
        //如果锁定层级深度则跳过
        UILevel levelInfo = UILevel.TryGet(level.ToString());
        if (levelInfo.UILevelLock) return;
        int curMaxLevel = GetMax(levelInfo);
        UIPanel[] panels = obj.GetComponentsInChildren<UIPanel>();
        for (int i = 0; i < panels.Length; i++)
        {
            panels[i].depth = ++curMaxLevel;
        }
    }

    /// <summary>
    /// 获取当前面板最大层级
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    private int GetMax(UILevel level)
    {
        int maxDepth = level.UILevelDepth;
        UIPanel[] panels = uiLevel[level.UILevelID].GetComponentsInChildren<UIPanel>();
        for (int i = 0; i < panels.Length; i++)
        {
            if (panels[i].depth > maxDepth)
                maxDepth = panels[i].depth;
        }
        return maxDepth;
    }
}

打开一个UI
UIManager.Instance.OpenUI( UIType.LoginPanel);

运行后UI效果:
这里写图片描述

最终:
这里写图片描述

注意如果需要加入UI动画,需要在UI类中重写OnOpenAni()和OnCloseAni()方法,动画结束后不要忘了调用OnOpen()和OnClose()方法

有关配置表解析部分可以查看我的文章 “Unity3d txt配置表读取” 或 自行解析
http://blog.csdn.net/qq_18192161/article/details/79220695

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Unity 3D NGUI实战教程 完整版 第1章 初识NGUI 1.1 游戏UI开发介绍 1.1.1 什么是游戏UI 1.1.2 UI为何如此重要 1.1.3 UI开发的流程 1.1.4 UI开发的难点 1.2 什么是NGUI 1.2.1 NGUI插件介绍 1.2.2 NGUI的强大优势 第2章 NGUI基础 2.1 导入NGUI插件 2.1.1 NGUI版本介绍 2.1.2 NGUI的下载和购买 2.1.3 导入NGUI插件应用 2.1.4 导入常见问题 2.2 认识基本的UI资源 2.2.1 什么是UI精灵(Sprite) 2.2.2 什么是UI图集(Atlas) 2.2.3 什么是UI贴图(Texture) 2.2.4 什么是UI标签(Label) 2.2.5 什么是UI字体(Font) 2.3 制作第一个UI图集 2.3.1 学会解剖UI的资源结构 2.3.2 如何导入切好的美术资源 2.3.3 用Atlas Maker制作图集 2.4 制作第一个UI字体 2.4.1 为什么要制作UI字体 2.4.2 静态字体和动态字体 2.4.3 制作静态字体介绍 2.4.4 制作动态字体介绍 2.5 创建第一个UI 2.5.1 创建一个2D UI 2.5 .2 创建一个3D UI 2.5.3 了解UIRoot、UIPanel和UICamera组件 2.6 2DUI和3DUI的工作原理 2.6.1 2DUI的工作原理 2.6.2 3DUI的工作原理 2.6.3 如何判断该选择哪一种UI 2.7 深度(Depth)概念 2.7.1 强化对深度的理解 2.7.2 小心相机的深度 第3章 核心组件 3.1 什么是UI控件 3.2 制作精灵(UISprite) 3.2.1 怎样判断是否应该使用精灵 3.2.2 创建精灵 3.2.3 Sprite组件的设置 3.3 制作标签(Label) 3.3.1 怎样判断是否应当使用标签 3.3.2 创建标签 3.3.3 Label的文字设置 3.4 制作UI纹理(UITexture) 3.4.1 什么情况下使用UITexture 3.4.2 创建纹理 3.4.3 纹理的设置 3.5 制作按钮(Button) 3.5.1 怎样判断应该使用按钮 3.5.2 创建按钮 3.5.3 核心组件BoxCollider 3.5.4 核心组件UIButton 3.5.5 制作按钮的放缩动画 3.5.6 制作按钮的偏移动画 3.5.7 制作按钮的旋转动画 3.5.8 添加按钮单击音效 3.5.9 任何事物都可以变成按钮,不仅仅是UI 3.6 制作进度条(UISlider) 3.6.1 怎样判断是否应当使用进度条 3.6.2 创建进度条 3.6.3 核心组件UISlider设置 3.6.4 进度条的BoxCollider说明 3.7 制作输入框(Input) 3.7.1 怎样判断是否应当使用输入框 3.7.2 创建输入框 3.7.3 核心组件Input设置 3.7.4 输入框使用的一些注意事项 3.8 制作滚动视图(ScrollView) 3.8.1 怎样判断是否应当使用滚动视图 3.8.2 创建滚动视图 3.8.3 滚动视图核心组件UIPanel 3.8.4 滚动视图核心组件UIScrollView 3.8.5 创建一个拖动条 3.8.6 拖动条说明 3.8.7 让视图内的内容可以被拖动 3.8.8 制作滚动视图时的注意事项 3.9 制作复选框(Toggle) 3.9.1 怎样判断是否应当使用复选框 3.9.2 创建复选框 3.9.3 复选框的核心组件UIToggle 3.10 制作下拉菜单(PopupList) 3.10.1 怎样判断是否应当使用下拉菜单 3.10.2 创建下拉菜单 3.10.3 显示当前选中的选项 3.10.4 下拉菜单核心组件PopupList 3.10.5 制作下拉菜单的注意事项 第4章 UI动画 4.1 常见的两种UI动画介绍 4.1.1 要区分UI动画和UI特效两个概念 4.1.2 关于Tween动画 4.1.3 关于Animation动画 4.2 渐隐渐现动画(透明度动画) 4.2.1 透明度动画的介绍和应用 4.2.2 使用透明度动画TweenAlpha 4.2.3 使用透明度动画的注意点 4.3 颜色变化动画(变色动画) 4.3.1 变色动画的介绍和应用 4.3.2 使用颜色动画TweenColor 4.3.3 使用颜色动画的注意点 4.4 位置变换动画(位移动画) 4.4.1 位移动画的介绍和应用 4.4.2 使用位移动画TweenPosition 4.4.3 使用位移动画的注意点 4.5 旋转变化动画(旋转动画) 4.5.1 旋转动画的介绍和应用 4.5.2 使用旋转动画TweenRotation 4.5.3 使用旋转动画的注意点 4.6 大小变化动画(放缩动画) 4.6.1 放缩动画的介绍和应用 4.6.2 使用放缩动画TweenScale 4.6.3 使用放缩动画的注意点 4.7 Tween动画总结 4.8 动画控制组件UIPlayTween 4.8.1 为什么要用UIPlayTween 4.8.2 动画核心组件UIPlayTween讲解 4.8.3 使用UIPlayTween的注意事项 4.9 动画控制组件UIPlayAnimation 4.9.1 为什么要用UIPlayAnimation 4.9.2 为UI添加Animation组件 4.9.3 动画核心组件UIPlayAnimation讲解 4.9.4 使用UIPlayAnimation注意事项 第5章 其他组件 5.1 使用Toggle制作页签 5.1.1 页签的工作原理 5.1.2 一个完整的页签界面 5.1.3 制作两个页签按钮 5.1.4 使用ToggleObjects来记录页签内容 5.1.5 制作页签注意事项 5.2 拖动摄像机来浏览超大界面 5.2.1 拖动相机功能的介绍和应用 5.2.2 核心原理和组件介绍 5.2.3 拖动相机浏览超大界面的注意事项 5.3 使用Grid自动排列UI 5.3.1 自动排列UI的应用 5.3.2 自动排列UI核心组件Grid介绍 5.4 使用DragObject直接拖动物体 5.5 让玩家通过拖动自由改变控件大小 5.6 制作序列帧精灵动画(SpriteAnimation) 5.6.1 什么是序列帧精灵动画 5.6.2 SpriteAnimation组件 第6章 NGUI实战进阶 6.1 UI开发核心问题--UI随屏幕自适应 6.1.1 屏幕分辨率对UI适配的影响 6.1.2 主流设备的屏幕分辨率 6.1.3 自适应核心组件Anchor的使用 6.1.4 使用Anchor的注意事项 6.1.5 正式开发UI之前必须明确的几个问题 6.2 UI元素的相对自适应 6.2.1 什么是UI元素的相对自适应 6.2.2 Anchors的介绍及使用 6.2.3 使用Anchors的范例:背景图的全屏适配 6.2.4 使用Anchors的注意事项 6.3 多摄像机同时协作运行 6.3.1 摄像的渲染层的概念 6.3.2 多摄像机协作的应用范围 6.3.3 如何创建多个UI摄像机 6.3.4 多摄像机协作的注意事项 6.4 巧用九宫格以减少UI资源量 6.4.1 项目安装包大小对项目的影响

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘建宁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值