一、简介

  马三从上一家公司离职了,最近一直在出去面试,忙得很,所以这一篇博客拖到现在才写出来。马三在上家公司工作的时候,曾处理了一个UGUI不规则区域点击的问题,制作过程中也有一些收获和需要注意坑,因此记录成博客与大家分享。众所周知在UGUI中,响应点击通常是依附在一张图片上的,而图片不管美术怎么给你切,导进Unity之后都是一个矩形,如果要做其他形状,最多只能旋转一下,或者自己做一些处理。而为了美术效果,很多时候我们不得不需要特定形状的UI,并且让它们实现精准的响应点击。例如下图就是一个不规则的点击区域。

      图1:UGUI不规则点击区域示意图

  下面是处理了不规则区域点击后的演示效果,当点击按钮的时候,会对点击次数进行累加并且打印到控制台。可以看到进行了不规则区域点击处理以后,对我们原来的普通矩形Sprite的点击不会产生到影响,而不规则区域的表现效果也符合我们的预期。

图2:规则区域与不规则区域点击效果对比

二、针对UGUI不规则区域点击的两种处理方法

  针对UGUI的不规则区域响应点击,一般来说有两种处理办法:

  1.精灵像素检测:该方法是指通过读取精灵(Sprite)在某一点的像素值(RGBA),如果该点的像素值中的Alpha小于一定的阈值(比如0.5)则表示该点处是透明的,即用户点击的位置在精灵边界以外,否则用户点击的位置在精灵边界内部。

  2.通过算法计算碰撞区域:通过一定的算法,手动计算出碰撞区域,然后在判断用户是点击在了精灵上面,还是点击在精灵外部。

1.精灵像素检测法

  首先来说下精灵像素检测法,因为它实现起来比较简单也好理解。uGUI在处理控件是否被点击的时候,主要是根据IsRaycastLocationValid这个方法的返回值来进行判断的,而这个方法用到的基本原理则是判断指定点对应像素的RGBA数值中的Alpha是否大于某个指定临界值。例如,我们知道半透明通常是指Alpha=0.5,而对一个后缀名为png格式的图片来说半透明或者完全透明的区域理论上不应该被响应的,所以根据这个原理,我们只需要设定一个透明度的临界值,然后对当前鼠标位置对应的像素进行判断就可以了,因此这种方法叫做精灵像素检测。对于上面的这个IsRaycastLocationValid接口,我们可以通过下载UGUI源码或者反编译的方式看到它的实现:

  1. public virtual bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
  2. {
  3. //当透明度>=1.0时,表示点击在可响应区域返回true
  4. if(this.m_EventAlphaThreshold >= 1f){
  5. return true;
  6. }
  7.  
  8. //当没有指定精灵时返回true,因为不指定Spirte的时候,Unity将其区域填充为默认的白色,全部区域都是可以响应点击的
  9. Sprite overrideSprite = this.overrideSprite;
  10. if(overrideSprite == null){
  11. return true;
  12. }
  13.  
  14. //坐标系转换
  15. Vector2 local;
  16. RectTransformUtility.ScreenPointToLocalPointInRectangle(base.rectTransform, screenPoint, eventCamera, ref local);
  17. Rect pixelAdjustedRect = base.GetPixelAdjustedRect ();
  18. local.x += base.rectTransform.get_pivot ().x * pixelAdjustedRect.get_width ();
  19. local.y += base.rectTransform.get_pivot ().y * pixelAdjustedRect.get_height ();
  20. local = this.MapCoordinate(local, pixelAdjustedRect);
  21. Rect textureRect = overrideSprite.get_textureRect ();
  22. Vector2 vector = new Vector2(local.x / textureRect.get_width (), local.y / textureRect.get_height ());
  23.  
  24. //计算屏幕坐标对应的UV坐标
  25. float num = Mathf.Lerp(textureRect.get_x (), textureRect.get_xMax (), vector.x) / (float)overrideSprite.get_texture().get_width();
  26. float num2 = Mathf.Lerp(textureRect.get_y (), textureRect.get_yMax (), vector.y) / (float)overrideSprite.get_texture().get_height();
  27. bool result;
  28.  
  29. //核心方法:像素检测
  30. try{
  31. result = (overrideSprite.get_texture().GetPixelBilinear(num, num2).a >= this.m_EventAlphaThreshold);
  32. }catch(UnityException ex){
  33. Debug.LogError("Using clickAlphaThreshold lower than 1 on Image whose sprite texture cannot be read. " + ex.Message + " Also make sure to disable sprite packing for this sprite.", this);
  34. result = true;
  35. }
  36.  
  37. return result;
  38. }

  可以看到大概的思路就是经过一系列的坐标转换之后,将一个UV坐标的Alpha值与临界值作比较。基于这个像素这个思路我们又可以衍生出两种解决方案,一是直接更改临界值,二是在像素检测的思路上进行拓展与重写,定制我们自己的像素检测方法。

  先来看下第一种直接更改阈值的方法,Unity在Image组件中为我们暴露出了一条属性alphaHitTestMinimumThreshold。关于它的含义我们可以参考Unity的官方文档:

   图3:alphaHitTestMinimumThreshold属性文档

  大概的意思就是点击的时候会将该像素的Alpah值与该阈值进行比较,Alpha小于该阈值的部分的点击事件会被忽略掉,意思也就是某一像素的Alpha只有大于设定的阈值,你才能接到响应事件。当值为1的时候,表示只有完全不透明的部分才能响应。默认值为0,即一个Image不管透明不透明的部分,都会参与事件的响应。为了能够让alphaHitTestMinimumThreshold这个属性生效和工作,我们需要把Advance选项中的Read/Writeable属性勾选上

  因此我们将alphaHitTestMinimumThreshold值设置为一个合理的范围就可以实现不规则区域的点击了,代码如下:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.UI;
  5.  
  6. /// <summary>
  7. /// 不规则区域Button
  8. /// </summary>
  9. [RequireComponent(typeof(RectTransform))]
  10. [RequireComponent(typeof(Image))]
  11. public class IrregulaButton : MonoBehaviour
  12. {
  13. [Tooltip("设定Sprite响应的Alpha阈值")]
  14. [Range(, 0.5f)]
  15. public float alpahThreshold = 0.5f;
  16.  
  17. private void Awake()
  18. {
  19. var image = this.GetComponent<Image>();
  20. if (null != image)
  21. {
  22. image.alphaHitTestMinimumThreshold = alpahThreshold;
  23. }
  24. }
  25. }

  第二种基于像素检测的解决方案是自己重写IsRaycastLocationValid接口里面像素检测方法,将屏幕坐标转换为UI坐标,然后再根据Sprite的类型做一些处理,最后根据x,y坐标取出像素的Alpha值与我们的阈值进行比较,具体代码如下:

  1. using UnityEngine;
  2. using UnityEngine.UI;
  3.  
  4. /// <summary>
  5. /// 不规则区域图形检测组件
  6. /// </summary>
  7. [RequireComponent(typeof(RectTransform))]
  8. [RequireComponent(typeof(Image))]
  9. public class IrregularRaycastMask : MonoBehaviour, ICanvasRaycastFilter
  10. {
  11. private Image _image;
  12. private Sprite _sprite;
  13.  
  14. [Tooltip("设定Sprite响应的Alpha阈值")]
  15. [Range(, 0.5f)]
  16. public float alpahThreshold = 0.5f;
  17.  
  18. void Start()
  19. {
  20. _image = GetComponent<Image>();
  21. }
  22.  
  23. /// <summary>
  24. /// 重写IsRaycastLocationValid接口
  25. /// </summary>
  26. /// <param name="sp"></param>
  27. /// <param name="eventCamera"></param>
  28. /// <returns></returns>
  29. public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
  30. {
  31. _sprite = _image.sprite;
  32.  
  33. var rectTransform = (RectTransform)transform;
  34. Vector2 localPositionPivotRelative;
  35. RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform)transform, sp, eventCamera, out localPositionPivotRelative);
  36.  
  37. // 转换为以屏幕左下角为原点的坐标系
  38. var localPosition = new Vector2(localPositionPivotRelative.x + rectTransform.pivot.x * rectTransform.rect.width,
  39. localPositionPivotRelative.y + rectTransform.pivot.y * rectTransform.rect.height);
  40.  
  41. var spriteRect = _sprite.textureRect;
  42. var maskRect = rectTransform.rect;
  43.  
  44. var x = ;
  45. var y = ;
  46. // 转换为纹理空间坐标
  47. switch (_image.type)
  48. {
  49.  
  50. case Image.Type.Sliced:
  51. {
  52. var border = _sprite.border;
  53. // x 轴裁剪
  54. if (localPosition.x < border.x)
  55. {
  56. x = Mathf.FloorToInt(spriteRect.x + localPosition.x);
  57. }
  58. else if (localPosition.x > maskRect.width - border.z)
  59. {
  60. x = Mathf.FloorToInt(spriteRect.x + spriteRect.width - (maskRect.width - localPosition.x));
  61. }
  62. else
  63. {
  64. x = Mathf.FloorToInt(spriteRect.x + border.x +
  65. ((localPosition.x - border.x) /
  66. (maskRect.width - border.x - border.z)) *
  67. (spriteRect.width - border.x - border.z));
  68. }
  69. // y 轴裁剪
  70. if (localPosition.y < border.y)
  71. {
  72. y = Mathf.FloorToInt(spriteRect.y + localPosition.y);
  73. }
  74. else if (localPosition.y > maskRect.height - border.w)
  75. {
  76. y = Mathf.FloorToInt(spriteRect.y + spriteRect.height - (maskRect.height - localPosition.y));
  77. }
  78. else
  79. {
  80. y = Mathf.FloorToInt(spriteRect.y + border.y +
  81. ((localPosition.y - border.y) /
  82. (maskRect.height - border.y - border.w)) *
  83. (spriteRect.height - border.y - border.w));
  84. }
  85. }
  86. break;
  87. case Image.Type.Simple:
  88. default:
  89. {
  90. // 转换为统一UV空间
  91. x = Mathf.FloorToInt(spriteRect.x + spriteRect.width * localPosition.x / maskRect.width);
  92. y = Mathf.FloorToInt(spriteRect.y + spriteRect.height * localPosition.y / maskRect.height);
  93. }
  94. break;
  95. }
  96.  
  97. // 如果texture导入过程报错,则删除组件
  98. try
  99. {
  100. return _sprite.texture.GetPixel(x, y).a > alpahThreshold;
  101. }
  102. catch (UnityException e)
  103. {
  104. Debug.LogError("Mask texture not readable, set your sprite to Texture Type 'Advanced' and check 'Read/Write Enabled'" + e.Message);
  105. Destroy(this);
  106. return false;
  107. }
  108. }
  109. }

  最后为了验证我们的组件是否生效,可以在按钮上挂载一个ButtonClickCounter 脚本,当接收到点击事件的时候,记录点击次数并打印到控制台方便观察,具体代码如下:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.UI;
  5.  
  6. /// <summary>
  7. /// 按钮点击次数计数器
  8. /// </summary>
  9. public class ButtonClickCounter : MonoBehaviour
  10. {
  11. private int count = ;
  12. private string btnName;
  13.  
  14. void Start()
  15. {
  16. var text = this.transform.Find("Text").GetComponent<Text>();
  17. btnName = text.text;
  18. }
  19.  
  20. public void Click()
  21. {
  22. count++;
  23. Debug.Log(string.Format("{0}点击了{1}次!", btnName, count));
  24. }
  25. }

  我们只要简单地直接把组件挂载到Image上面便可以生效了,具体截图如下:

图4:不规则区域检测组件使用

2.通过算法计算碰撞区域法

  对于这种实现不规则碰撞区域的方法,马三并没有进行深入地研究,因为马三觉得挑选一个可靠的检测碰撞算法不是很容易,既要考虑到它的精准性又要考虑当图形复杂以后的计算效率,因此从易用性上面来讲,不如第一种实现方案好。关于这种方法的实现和原理,马三也是从网上搜集的一些资料进行整理的,感兴趣的读者可以深入研究一下哈,下面很多内容都是马三搜集整理网上大神的文章的资料得来的,其中给出了许多链接,大家可以直接参看链接里面的内容。

  该方法是指给精灵(Sprite)添加一个多边形碰撞器(Rolygon Collider)组件,利用该组件来标记精灵的边界,这样通过比较鼠标位置和边界可以判断点击是否发生在精灵内部。关于这个算法与实现,PayneQin大神已经在他的博客中做了很详细的解析和说明,大家可以直接去看他的博客。知乎上关于判断一个点是否在多边形内部也有很多算法地讨论,具体可以看这里。其中这篇文献提供了判断一个点是否在任意多边形内部的两种方法,分别为Corssing Number和Winding Number。这两种方法在理论层面的相关细节请大家自行阅读这篇文章PayneQin大神选择的是前者实现,其基本思想是计算从该点引出的射线与多边形边界相交的次数,当其为奇数时表示该点在多边形内部,当其为偶数时表示在多边形外部。马三在网上找到了相关的实现(偷懒):

  1. bool ContainsPoint2(Vector2[] polyPoints,Vector2 p)
  2. {
  3. //统计射线和多边形交叉次数
  4. int cn = ;
  5.  
  6. //遍历多边形顶点数组中的每条边
  7. for(int i=; i<polyPoints.Length-; i++)
  8. {
  9. //正常情况下这一步骤可以忽略这里是为了统一坐标系
  10. polyPoints [i].x += transform.GetComponent<RectTransform> ().position.x;
  11. polyPoints [i].y += transform.GetComponent<RectTransform> ().position.y;
  12.  
  13. //从当前位置发射向上向下两条射线
  14. if(((polyPoints [i].y <= p.y) && (polyPoints [i + ].y > p.y))
  15. || ((polyPoints [i].y > p.y) && (polyPoints [i + ].y <= p.y)))
  16. {
  17. //compute the actual edge-ray intersect x-coordinate
  18. float vt = (float)(p.y - polyPoints [i].y) / (polyPoints [i + ].y - polyPoints [i].y);
  19.  
  20. //p.x < intersect
  21. if(p.x < polyPoints [i].x + vt * (polyPoints [i + ].x - polyPoints [i].x))
  22. ++cn;
  23. }
  24. }
  25.  
  26. //实际测试发现cn为0的情况即为宣雨松算法中存在的问题
  27. //所以在这里进行屏蔽直接返回false这样就可以让透明区域不再响应
  28. if(cn == )
  29. return false;
  30.  
  31. //返回true表示在多边形外部否则表示在多边形内部
  32. return cn % == ;
  33. }

  基于上面算法制作的多边形碰撞器实现的不规则按钮,以正五边形举例(PayneQin大神实现,马三只是搬运工):

  1. /*
  2. * 基于多边形碰撞器实现的不规则按钮
  3. * 作者:PayneQin
  4. * 日期:2016年7月9日
  5. */
  6.  
  7. using UnityEngine;
  8. using System.Collections;
  9. using UnityEngine.UI;
  10. using UnityEngine.EventSystems;
  11.  
  12. public class UnregularButtonWithCollider : MonoBehaviour,IPointerClickHandler
  13. {
  14. /// <summary>
  15. /// 多边形碰撞器
  16. /// </summary>
  17. PolygonCollider2D polygonCollider;
  18.  
  19. void Start()
  20. {
  21. //获取多边形碰撞器
  22. polygonCollider = transform.GetComponent<PolygonCollider2D>();
  23. }
  24.  
  25. public void OnPointerClick(PointerEventData eventData)
  26. {
  27. //对2D屏幕坐标系进行转换
  28. Vector2 local;
  29. local.x = eventData.position.x - (float)Screen.width / 2.0f;
  30. local.y = eventData.position.y - (float)Screen.height / 2.0f;
  31. if(ContainsPoint(polygonCollider.points,local))
  32. {
  33.  
  34. Debug.Log ("这是一个正五边形!");
  35. }
  36.  
  37. }
  38.  
  39. /// <summary>
  40. /// 判断指定点是否在给定的任意多边形内
  41. /// </summary>
  42. bool ContainsPoint(Vector2[] polyPoints,Vector2 p)
  43. {
  44. //统计射线和多边形交叉次数
  45. int cn = ;
  46.  
  47. //遍历多边形顶点数组中的每条边
  48. for(int i=; i<polyPoints.Length-; i++)
  49. {
  50. //正常情况下这一步骤可以忽略这里是为了统一坐标系
  51. polyPoints [i].x += transform.GetComponent<RectTransform> ().position.x;
  52. polyPoints [i].y += transform.GetComponent<RectTransform> ().position.y;
  53.  
  54. //从当前位置发射向上向下两条射线
  55. if(((polyPoints [i].y <= p.y) && (polyPoints [i + ].y > p.y))
  56. || ((polyPoints [i].y > p.y) && (polyPoints [i + ].y <= p.y)))
  57. {
  58. //compute the actual edge-ray intersect x-coordinate
  59. float vt = (float)(p.y - polyPoints [i].y) / (polyPoints [i + ].y - polyPoints [i].y);
  60.  
  61. //p.x < intersect
  62. if(p.x < polyPoints [i].x + vt * (polyPoints [i + ].x - polyPoints [i].x))
  63. ++cn;
  64. }
  65.  
  66. }
  67.  
  68. //实际测试发现cn为0的情况即为宣雨松算法中存在的问题
  69. //所以在这里进行屏蔽直接返回false这样就可以让透明区域不再响应
  70. if(cn == )
  71. return false;
  72.  
  73. //返回true表示在多边形外部否则表示在多边形内部
  74. return cn % == ;
  75. }

三、需要注意的坑

  在像素检测法实现UGUI不规则碰撞区域的过程中,马三也遇到了很多需要注意的问题,在这里和大家分享一下:

1.图片需要开启Read/Writeable属性

  如果选择使用像素检测法实现的话,需要注意开启Texture的Read/Writeable属性(我们需要读写该Texture的像素值),而且他必须是Advance类型。这样这张图片就不能打进我们的图集里面了,必须以散图的形式存在于工程当中,不利于统一管理。而且开启了Read/Writeable属性属性的话,在程序运行的时候,它会在内存中多复制出来一份,必然会影响到游戏的运行效率。所以尽量还是减少游戏中这种不规则UI的出现。

2.像素检测有偏移,不准确的问题

  马三在实际操作的过程中,发现实际点击的时候经常会有偏移(经常偏下一些),有的透明的地方可以点击,而明明是不透明的地方却不能点击。刚开始马三还以为是图片格式或者是图片本身有什么问题,反反复复确认了好多次。直到后来马三在unity论坛上找到了这篇文章,才找到问题的症结所在。

  对于如下图所示的这种周围有空白区域的图片,我们需要在Unity图片导入设置的时候,将Mesh Type格式设置为Full Rect,而unity导入时默认帮我们设置的是Tight模式。

         

图5:周围有空白的图片                                                                                     图6:正确的导入设置

  那么,它们有什么区别呢?关于它们的区别,Unity官方是这样解释的:

图7:Full Rect和Tight两种Mesh Type的官方解释

  总的来说就是,用Tight模式的话,如果你的图片周围有空白像素,它会帮你压缩掉减小面积,以减少DrawCall,但是会增加Sprite的面数。如果用Full Rect模式不会压缩,也不会增加面数,直接创建一个quab,然后把图片扔上去。如果尺寸小于32x32的话,Unity默认使用Full Rect格式导入,否则使用Tight格式导入。因此如果我们不对Mesh Type进行设置的话,原来的一些空白区域就相当于裁剪掉了,这样相对于左下角的坐标来说,一些像素坐标就发生了偏移,而我们使用的是像素检测方法,必然也会导致偏移误差。

四、总结

  通过本篇博客,马三和大家一起学习了如何在Unity中实现UGUI不规则区域的点击,希望本篇博客能为大家的工作过程中带来一些帮助与启发。

  本篇博客中的样例工程已经同步至Github:https://github.com/XINCGer/Unity3DTraining/tree/master/SomeTest/IrregularButton,欢迎大家Fork!

  如果觉得本篇博客对您有帮助,可以扫码小小地鼓励下马三,马三会写出更多的好文章,支持微信和支付宝哟!

       

参考资料:

  • https://blog.csdn.net/qinyuanpei/article/details/51868638
  • https://blog.csdn.net/shenmifangke/article/details/53504036
  • https://www.zhihu.com/question/26551754?f3fb8ead20=b6b9d1289bcc893ff2fa0abd1e65fc52

作者:马三小伙儿
出处:https://www.cnblogs.com/msxh/p/9283266.html
请尊重别人的劳动成果,让分享成为一种美德,欢迎转载。另外,文章在表述和代码方面如有不妥之处,欢迎批评指正。留下你的脚印,欢迎评论!

【Unity游戏开发】UGUI不规则区域点击的实现的更多相关文章

  1. 【Unity游戏开发】记一次解决 LuaFunction has been disposed 的bug的过程

    一.引子 RT,本篇博客记录的是马三的一次解决 LuaFunction has been disposed 的bug的全过程,事情还要从马三的自研框架 ColaFrameWork 说起.最近,马三在业 ...

  2. 喵的Unity游戏开发之路 - 玩家控制下的球的滑动

  3. 2017年Unity游戏开发视频教程(入门到精通)

    本文是我发布的一个Unity游戏开发的学习目录,以后我会持续发布一系列的游戏开发教程,都会更新在这个页面上,适合人群有下面的几种: 想要做独立游戏的人 想要找游戏开发相关工作的人 对游戏开发感兴趣的人 ...

  4. Unity游戏开发常用的一些函数用法

    Unity游戏开发常用函数 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分享. ...

  5. Unity 游戏开发技巧集锦之创建透明的材质

    Unity 游戏开发技巧集锦之创建透明的材质 Unity创建透明的材质 生活中不乏透明或者半透明的事物.例如,擦的十分干净的玻璃,看起来就是透明的:一些塑料卡片,看起来就是半透明的,如图3-23所示. ...

  6. Unity 游戏开发技巧集锦之使用忍者飞镖创建粒子效果

    Unity 游戏开发技巧集锦之使用忍者飞镖创建粒子效果 使用忍者飞镖创建粒子效果 游戏中,诸如烟.火.水滴.落叶等粒子效果,都可以使用粒子系统(particle system)来实现.例如,<明 ...

  7. Unity 游戏开发技巧集锦之制作一个望远镜与查看器摄像机

    Unity 游戏开发技巧集锦之制作一个望远镜与查看器摄像机 Unity中制作一个望远镜 本节制作的望远镜,在鼠标左键按下时,看到的视图会变大:当不再按下的时候,会慢慢缩小成原来的视图.游戏中时常出现的 ...

  8. 【Unity游戏开发】不接SDK也能在游戏内拉起加QQ群操作?

    一.引子 一般在游戏进行对外测试的时候都会有一个玩家QQ群,方便玩家反馈问题.交流游戏心得等.那么为了增加玩家加QQ群的欲望,可能会在游戏里面设计一个小功能,点击一下可以直接拉起手Q加群的操作,加了Q ...

  9. 喵的Unity游戏开发之路 - 推球:游戏中的物理

    很多童鞋没有系统的Unity3D游戏开发基础,也不知道从何开始学.为此我们精选了一套国外优秀的Unity3D游戏开发教程,翻译整理后放送给大家,教您从零开始一步一步掌握Unity3D游戏开发. 本文不 ...

随机推荐

  1. winserver-查看登陆日志

    Abstract 先要开启登陆审核,在查看登陆日志. 开启审核 运行 secpol.msc 日志查看 windowslog 下的security 管理员成功登陆后的eventid:4776,4648, ...

  2. 为什么CynosDB叫真正的云原生数据库?

    本文由腾讯云数据库发表 注:本文摘自2018年11月22日腾讯云数据库CynosDB新品发布会的演讲实录.随着互联网信息的发展,大家也对云这个词汇也不是特别陌生了,作为全球首选的云服务厂商之一的腾讯云 ...

  3. 【原】使用IDEA创建Maven工程时提示"...xxx/pom.xml already exists in VFS"的解决

    问题:使用IDEA创建Maven工程时提示"...xxx/pom.xml already exists in VFS",怎么办? 解决:如果只是删除工程,还会有这样的提示.说到底, ...

  4. lua_local变量在new时不会被清空

    前言 我的运行环境 Lua5.3 按照我们以往的Java或C#编程经验,如果一个class被new,那么这个class中所有成员变量的值都是默值或是构造函数中赋的值,但在Lua中的local变量却并不 ...

  5. 基于CAS在.NET中实现SSO单点登录

    单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一.SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统. 单点登录原理 ...

  6. RocketMQ4.3.X关于设置useEpollNativeSelector = true报错问题

    前一阵子刚整理完RocketMQ4.3.x版本的相关配置的工作,接下来就来测试一下改变参数会带来什么好的结果 首先我就选中了useEpollNativeSelector 这个参数 默认这个参数是 fa ...

  7. 解释型语言VS编译型语言

    前言 计算机不能直接理解除机器语言以外的语言,所以只有把程序员编写的程序翻译成机器语言,计算机才能够执行程序. 将其他语言翻译成机器语言的工具,被称之为:编译器. 编译器的翻译方式有两种:编译和解释. ...

  8. C#二维码与条形码的生成

    二维码 using Gma.QrCodeNet.Encoding;using Gma.QrCodeNet.Encoding.Windows.Render; string str = "Htt ...

  9. Elastic Stack-Elasticsearch使用介绍(四)

    一.前言     上一篇说了一下查询和存储机制,接下来我们主要来说一下排序.聚合.分页: 写完文章以后发现之前文章没有介绍Coordinating Node,这个地方补充说明下Coordinating ...

  10. [蓝桥杯]2015蓝桥省赛B组题目及详解

    /*——————————————————————————————————————————————————————————— [结果填空题]T1 题目:奖券数目 有些人很迷信数字,比如带“4”的数字,认 ...