四叉树在MMO中的应用

2023-11-04

四叉树是一种树状数据结构,在每一个节点上会有四个子区块。四叉树常应用于二维空间数据的分析与分类。 它将数据区分成为四个象限。数据范围可以是方形或矩形或其他任意形状。
四叉树在游戏中常常用于减少遍历的次数:

1.比如,地图上有N个怪,玩家砍了一刀,会砍到哪些怪?

2.再如,碰撞检测。

我们做个碰撞检测的对比测试。二维空间内有300对象,对象可以移动,可以碰撞。所以每帧要遍历300*300=90000次。使用四叉树后,可以减少这个遍历次数,以提高效率。

先看效果图:

每一幅图:四叉树深度为0,相当于没用四叉树。

遍历次数为:90000,fps为:25

第二幅图:四叉树深度为4,当前情况下的较为合理的深度

遍历次数约为:6500,fps为:60

 

以上对比结束已经说明了四叉树的用处。也可以自己手动对比:http://baaoo.com/qt/

下面上代码:

辅助类:四叉树包围盒

namespace GameBase.QT
{
    /// <summary>
    /// 四叉树的包围盒
    /// </summary>
    public struct AABB
    {
        public AABB(float xMin, float xMax, float yMin, float yMax)
        {
            this.xMin = xMin;
            this.xMax = xMax;
            this.yMin = yMin;
            this.yMax = yMax;
            this.xCenter = xMin + (xMax - xMin) * 0.5f;
            this.yCenter = yMin + (yMax - yMin) * 0.5f;
        }

        public float xMin;
        public float xMax;
        public float yMin;
        public float yMax;
        public float xCenter;
        public float yCenter;


        public bool Contains(AABB aabb)
        {
            if (aabb.xMin > this.xMin && aabb.xMax < this.xMax && aabb.yMin > this.yMin && aabb.yMax < this.yMax)
            {
                return true;
            }
            return false;
        }

        public bool Intersect(AABB aabb)
        {
            if (aabb.xMin > this.xMax || aabb.xMax < this.xMin || aabb.yMin > this.yMax || aabb.yMax < this.yMin)
            {
                return false;
            }
            return true;
        }

    }
}

辅助类:四叉树包围盒接口

namespace GameBase.QT
{
    public interface IAABB
    {
        AABB aabb { get; }
    }
}

 

核心类:四叉树

using System.Collections.Generic;

namespace GameBase.QT
{
    /// <summary>
    ///          y
    ///          ^
    ///          |
    ///          |
    ///      ②  |  ①
    /// ---------|-------->x
    ///      ③  |  ④
    ///          |
    ///          |
    ///  象限标识及坐标方向
    /// </summary>
    /// <typeparam name="T"></typeparam>

    public class QuadTree<T> where T : IAABB
    {
        #region 缓存节点(QuadTree)以减少GC,销毁时把所有的节点(QuadTree)存在root节点下。
        private Queue<QuadTree<T>> _cacheQTQueue;

        private QuadTree<T> CreateQT(QuadTree<T> parent, AABB aabb)
        {
            if(_root._cacheQTQueue != null && _root._cacheQTQueue.Count > 0)
            {
                QuadTree<T> result = _root._cacheQTQueue.Dequeue();
                result.Reset(parent, aabb, _depthMax);
                return result;
            }
            return new QuadTree<T>(parent, aabb, _depthMax);
        }

        private void DestroyQT(QuadTree<T> qt)
        {
            if (_root._cacheQTQueue == null)
            {
                _root._cacheQTQueue = new Queue<QuadTree<T>>();
            }
            _root._cacheQTQueue.Enqueue(qt);
        }
        #endregion

        private int _depthMax;

        public int DepthMax
        {
            get
            {
                return _depthMax;
            }
            set
            {
                _depthMax = value;
            }
        }

        private int _depth;

        private QuadTree<T> _parent;

        private QuadTree<T> _root;

        private QuadTree<T>[] _children;

        public QuadTree<T>[] Children
        {
            get
            {
                return _children;
            }
        }
        private AABB _aabb;
        public AABB aabb
        {
            get
            {
                return _aabb;
            }
        }
        private List<T> _objectList;
        public List<T> ObjectList
        {
            get
            {
                return _objectList;
            }
        }

        public QuadTree(QuadTree<T> parent, AABB aabb)
        {
            Reset(parent, aabb, 4);
        }

        public QuadTree(QuadTree<T> parent, AABB aabb, int depthMax)
        {
            Reset(parent, aabb, depthMax);
        }

        private void Reset(QuadTree<T> parent, AABB aabb, int depthMax)
        {
            _parent = parent;
            _depthMax = depthMax;
            _root = _parent == null ? this : parent._root;
            if(_children == null)
            {
                _children = new QuadTree<T>[4];
            }
            else
            {
                if(_children[0] != null)
                {
                    _children[0] = null;
                    _children[1] = null;
                    _children[2] = null;
                    _children[3] = null;
                }
            }
            _aabb = aabb;
            _depth = parent != null ? parent._depth + 1 : 0;
            if(_objectList == null)
            {
                _objectList = new List<T>();
            }
            else
            {
                _objectList.Clear();
            }
        }

        private void Split()
        {
            _children[0] = CreateQT(this, new AABB(_aabb.xCenter, _aabb.xMax, _aabb.yCenter, _aabb.yMax));
            _children[1] = CreateQT(this, new AABB(_aabb.xMin, _aabb.xCenter, _aabb.yCenter, _aabb.yMax));
            _children[2] = CreateQT(this, new AABB(_aabb.xMin, _aabb.xCenter, _aabb.yMin, _aabb.yCenter));
            _children[3] = CreateQT(this, new AABB(_aabb.xCenter, _aabb.xMax, _aabb.yMin, _aabb.yCenter));
        }

        /// <summary>
        /// 返回第几象限,0代表1象限,1代表2象限,2代表3象限,3代表4象限 -1代码
        /// </summary>
        /// <param name="aabb"></param>
        /// <returns></returns>
        private int GetQuadrantIndex(AABB aabb)
        {
            int result;
            bool isLeft = aabb.xMax < _aabb.xCenter;
            bool isRight = aabb.xMin > _aabb.xCenter;
            bool isTop = aabb.yMin > _aabb.yCenter;
            bool isBottom = aabb.yMax < _aabb.yCenter;
            if (isTop && isRight)
            {
                result = 0;
            }
            else if (isTop && isLeft)
            {
                result = 1;
            }
            else if (isBottom && isLeft)
            {
                result = 2;
            }
            else if (isBottom && isRight)
            {
                result = 3;
            }
            else
            {
                result = -1;
            }
            return result;
        }

        public void Insert(T t)
        {
            if (_depth < _depthMax && _aabb.Contains(t.aabb))
            {
                int index = GetQuadrantIndex(t.aabb);
                if(index == -1)
                {
                    _objectList.Add(t);
                }
                else
                {
                    if (_children[0] == null)
                        Split();
                    _children[index].Insert(t);
                }
            }
            else
            {
                _objectList.Add(t);
            }
        }
        /// <summary>
        /// 只清除存储的对象,不清除子节点
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public bool Remove(T t)
        {
            if (t == null)
                return false;
            if(_objectList.Contains(t))
            {
                _objectList.Remove(t);
                return true;
            }
          
            if (_children[0] != null)
            {
                if (_children[0].Remove(t)) return true;
                if (_children[1].Remove(t)) return true;
                if (_children[2].Remove(t)) return true;
                if (_children[3].Remove(t)) return true;
            }
            return false;
        }
        /// <summary>
        /// 只清除存储的对象,不清除子节点
        /// </summary>
        public void RemoveAll()
        {
            _objectList.Clear();
            if (_children[0] != null)
            {
                _children[0].RemoveAll();
                _children[1].RemoveAll();
                _children[2].RemoveAll();
                _children[3].RemoveAll();
            }
        }
        /// <summary>
        /// 清除对象及子节点
        /// </summary>
        public void Clear()
        {
            _objectList.Clear();
            if (_children[0] != null)
            {
                _children[0].Clear();
                DestroyQT(_children[0]);
                _children[0] = null;

                _children[1].Clear();
                DestroyQT(_children[1]);
                _children[1] = null;

                _children[2].Clear();
                DestroyQT(_children[2]);
                _children[2] = null;

                _children[3].Clear();
                DestroyQT(_children[3]);
                _children[3] = null;
            }
        }
        public void Retrieve(List<T> result, AABB aabb)
        {
            Retrieve(result, aabb, null);
        }
        public void Retrieve(List<T> result, AABB aabb, System.Func<T, bool> filter)
        {
            int index = GetQuadrantIndex(aabb);
            if(_children[0] != null)
            {
                if (index == -1)
                {
                    _children[0].Retrieve(result, aabb, filter);
                    _children[1].Retrieve(result, aabb, filter);
                    _children[2].Retrieve(result, aabb, filter);
                    _children[3].Retrieve(result, aabb, filter);
                }
                else
                {
                    _children[index].Retrieve(result, aabb, filter);
                }
            }
            if(filter != null)
            {
                for(int i = 0, count = _objectList.Count; i < count; i++)
                {
                    T t = _objectList[i];
                    if (filter(t))
                    {
                        result.Add(t);
                    }
                }
            }
            else
            {
                result.AddRange(_objectList);
            }
        }

    }
}

 

以上是整个四叉树的代码

下面上个用法的代码:

物体类:要实现 IAABB接口

using UnityEngine;

namespace GameBase.QT.Demo
{
    public class QTElement : MonoBehaviour, IAABB
    {
        private float halfW;
        private float halfH;
        public static QTElement CreateQTElement(float x, float y, float width, float height)
        {
            UnityEngine.GameObject go = UnityEngine.Object.Instantiate(QTDemo.Instance.qtElementModel);
            go.SetActive(true);
            go.transform.localScale = new UnityEngine.Vector3(width, height, 1f);
            go.transform.localPosition = new UnityEngine.Vector3(x, y, 0);
            go.transform.SetParent(QTDemo.Instance.transform);
            QTElement qtElement = go.GetComponent<QTElement>();
            qtElement.halfW = width * 0.5f;
            qtElement.halfH = height * 0.5f;
            qtElement._aabb = new AABB(x - qtElement.halfW, x + qtElement.halfW, y - qtElement.halfH, y + qtElement.halfH);
            return qtElement;
        }
        private AABB _aabb;
        public AABB aabb
        {
            get
            {
                Vector2 pos = transform.localPosition;
                _aabb.xMin = pos.x - halfW;
                _aabb.xMax = pos.x + halfW;
                _aabb.yMin = pos.y - halfH;
                _aabb.yMax = pos.y + halfH;
                return _aabb;
            }
        }

        private const float SPEED_MIN = 0.05f;
        private const float SPEED_MAX = 0.20f;

        private Vector2 _speed;

        private void Awake()
        {
            float speedX = UnityEngine.Random.Range(SPEED_MIN, SPEED_MAX);
            float speedY = UnityEngine.Random.Range(SPEED_MIN, SPEED_MAX);

            if(UnityEngine.Random.Range(0, 2) > 0)
            {
                speedX = -speedX;
            }
            if (UnityEngine.Random.Range(0, 2) > 0)
            {
                speedY = -speedY;
            }
            _speed = new Vector2(speedX, speedY);
        }

        private Material _mat;
        public void SetColor(Color color)
        {
            if(_mat == null)
                _mat = GetComponent<MeshRenderer>().material;
            _mat.color = color;
        }

        public void ResetColor()
        {
            if (_mat == null)
                _mat = GetComponent<MeshRenderer>().material;
            _mat.color = Color.white;
        }

        private void Update()
        {
            Vector2 pos = transform.localPosition;
            Vector2 newPos = pos + _speed;
            if (newPos.x - halfW < QTDemo.X_MIN || newPos.x + halfW > QTDemo.X_MAX)
            {
                _speed = new Vector2(-_speed.x, _speed.y);
            }
            if (newPos.y - halfH < QTDemo.Y_MIN || newPos.y + halfH > QTDemo.Y_MAX)
            {
                _speed = new Vector2(_speed.x, -_speed.y);
            }

            newPos = pos + _speed;
            transform.localPosition = newPos;
        }
    }
}

 

用法类:具体怎么用这个QT

using System.Collections.Generic;
using UnityEngine;

namespace GameBase.QT.Demo
{
    public class QTDemo : MonoBehaviour
    {

        public const float X_MIN = 0;
        public const float X_MAX = 120;
        public const float Y_MIN = 0;
        public const float Y_MAX = 80;

        private QuadTree<QTElement> _qt;
        private List<QTElement> _allElement;

        public GameObject qtElementModel;

        private static QTDemo _instance;

        public static QTDemo Instance
        {
            get
            {
                return _instance;
            }
        }

        private void Awake()
        {
            if (_instance != null)
                throw new System.Exception("QTDemo must be singleton");
            _instance = this;
            _qt = new QuadTree<QTElement>(null, new AABB(X_MIN, X_MAX, Y_MIN, Y_MAX));
            _allElement = new List<QTElement>();
            Application.targetFrameRate = 60;

            int count = 100;
            while (count-- > 0)
            {
                AddElement();
            }
        }

        private bool _showQTGrid = true;
        private bool _showObjOwner = true;
        private int _maxDepth;
        private int _cycleCount;


        private void OnGUI()
        {
            UnityEngine.GUILayout.BeginVertical();
            UnityEngine.GUILayout.BeginHorizontal();
            if (UnityEngine.GUILayout.Button("AddElement"))
            {
                AddElement();
            }

            if (UnityEngine.GUILayout.Button("AddElement10"))
            {
                int count = 10;
                while (count-- > 0)
                {
                    AddElement();
                }
            }

            if (UnityEngine.GUILayout.Button("AddElement100"))
            {
                int count = 100;
                while (count-- > 0)
                {
                    AddElement();
                }
            }
            UnityEngine.GUILayout.EndHorizontal();
            UnityEngine.GUILayout.BeginHorizontal();
            if (UnityEngine.GUILayout.Button("Remove"))
            {
                if (_allElement.Count <= 0)
                    return;
                RemoveElement();
            }

            if (UnityEngine.GUILayout.Button("Remove10"))
            {
                int count = 10;

                while (count-- > 0)
                {
                    if (_allElement.Count <= 0)
                        break;
                    RemoveElement();
                }
            }

            if (UnityEngine.GUILayout.Button("Remove100"))
            {
                int count = 100;

                while (count-- > 0)
                {
                    if (_allElement.Count <= 0)
                        break;
                    RemoveElement();
                }
            }
            UnityEngine.GUILayout.EndHorizontal();
            UnityEngine.GUILayout.BeginHorizontal();

            if (UnityEngine.GUILayout.Button("Clear"))
            {
                _qt.Clear();
                for (int i = 0, count = _allElement.Count; i < count; i++)
                {
                    UnityEngine.Object.Destroy(_allElement[i].gameObject);
                }
                _allElement.Clear();
            }

            UnityEngine.GUILayout.EndHorizontal();

            _showQTGrid = GUILayout.Toggle(_showQTGrid, "显示网格:");
            _showObjOwner = GUILayout.Toggle(_showObjOwner, "显示对象所在网格:");
            GUILayout.BeginHorizontal();
            _maxDepth = (int)GUILayout.HorizontalSlider(_maxDepth, 0f, 10.1f);
            if (UnityEngine.GUILayout.Button("confirm"))
            {
                _qt.DepthMax = _maxDepth;
            }
            GUILayout.EndHorizontal();

            GUILayout.Space(50);

            GUILayout.Label("当前对像数:" + _allElement.Count.ToString());
            GUILayout.Label("四叉树深度:" + _qt.DepthMax.ToString());
            GUILayout.Label("  遍历次数:" + _cycleCount.ToString());

            UnityEngine.GUILayout.EndVertical();
        }

        List<QTElement> _possibleElementList = new List<QTElement>();
        HashSet<QTElement> _intersectSet = new HashSet<QTElement>();
        private int calCycleCountFrame = 0;
        private bool calCycleCount = true;
        private void LateUpdate()
        {
            if (calCycleCountFrame-- < 0)
            {
                calCycleCountFrame = 30;
                calCycleCount = true;
            }
            else
            {
                calCycleCount = false;
            }

            _qt.Clear();
            for (int i = 0, count = _allElement.Count; i < count; i++)
            {
                _qt.Insert(_allElement[i]);
            }

            _intersectSet.Clear();
            if (calCycleCount) _cycleCount = 0;
            for (int i = 0, count = _allElement.Count; i < count; i++)
            {
                QTElement qtEleA = _allElement[i];
                qtEleA.ResetColor();
                QTElement qtEleB;
                _possibleElementList.Clear();
                //_qt.Retrieve(_possibleElementList, qtEleA.aabb, (qtEle) => { return qtEle != qtEleA; });
                _qt.Retrieve(_possibleElementList, qtEleA.aabb);
                for (int j = 0, countJ = _possibleElementList.Count; j < countJ; ++j)
                {
                    qtEleB = _possibleElementList[j];
                    if (calCycleCount) _cycleCount++;
                    if (qtEleA == qtEleB)
                        continue;
                    if (qtEleA.aabb.Intersect(qtEleB.aabb))
                    {
                        _intersectSet.Add(qtEleB);
                        _intersectSet.Add(qtEleA);
                    }
                }
            }
            foreach (var e in _intersectSet)
            {
                e.SetColor(Color.red);
            }

        }

        private void AddElement()
        {
            QTElement element = QTElement.CreateQTElement(UnityEngine.Random.Range(X_MIN + 5, X_MAX - 5), UnityEngine.Random.Range(Y_MIN + 5, Y_MAX - 5), UnityEngine.Random.Range(1, 3), UnityEngine.Random.Range(1, 3));
            _allElement.Add(element);
            _qt.Insert(element);
        }

        private void RemoveElement()
        {
            if (_allElement.Count <= 0)
                return;
            QTElement qte = _allElement[_allElement.Count - 1];
            _qt.Remove(qte);
            UnityEngine.Object.Destroy(qte.gameObject);
            _allElement.RemoveAt(_allElement.Count - 1);
        }


        private void OnDrawGizmos()
        {
            DrawQuadTree(_qt);
        }

        private void DrawQuadTree(QuadTree<QTElement> quadTree)
        {
            if (quadTree == null)
                return;

            DrawAABB(quadTree.aabb);
            Gizmos.color = Color.black;
            for (int i = 0, count = quadTree.Children.Length; i < count; i++)
            {
                DrawQuadTree(quadTree.Children[i]);
                if (!_showObjOwner)
                    continue;
                if (quadTree.ObjectList.Count > 0)
                {
                    for (int j = 0, countJ = quadTree.ObjectList.Count; j < countJ; j++)
                    {
                        var qtEle = quadTree.ObjectList[j];
                        Vector3 center = new Vector3(qtEle.aabb.xMin + (qtEle.aabb.xMax - qtEle.aabb.xMin) * 0.5f, qtEle.aabb.yMin + (qtEle.aabb.yMax - qtEle.aabb.yMin) * 0.5f);
                        Gizmos.DrawLine(center, new Vector3(quadTree.aabb.xMin, quadTree.aabb.yMin));
                        Gizmos.DrawLine(center, new Vector3(quadTree.aabb.xMax, quadTree.aabb.yMin));
                        Gizmos.DrawLine(center, new Vector3(quadTree.aabb.xMin, quadTree.aabb.yMax));
                        Gizmos.DrawLine(center, new Vector3(quadTree.aabb.xMax, quadTree.aabb.yMax));
                    }
                }
            }
        }

        private void DrawAABB(AABB aabb)
        {
            if (!_showQTGrid)
                return;
            Gizmos.color = Color.cyan;
            Gizmos.DrawLine(new Vector3(aabb.xMin, aabb.yMin), new Vector3(aabb.xMax, aabb.yMin));
            Gizmos.DrawLine(new Vector3(aabb.xMax, aabb.yMin), new Vector3(aabb.xMax, aabb.yMax));
            Gizmos.DrawLine(new Vector3(aabb.xMax, aabb.yMax), new Vector3(aabb.xMin, aabb.yMax));
            Gizmos.DrawLine(new Vector3(aabb.xMin, aabb.yMax), new Vector3(aabb.xMin, aabb.yMin));
        }
    }
}

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

四叉树在MMO中的应用 的相关文章

随机推荐

  • 运营入门——全栈市场人

    运营入门 全栈市场人 该系列文章系个人读书笔记及总结性内容 任何组织和个人不得转载进行商业活动 特别说明 本文内容部分摘自 全栈市场人 互联网市场营销入门通用宝典 Lydia所著 如摘抄内容存在不妥或版权问题 请联系博客作者予以删除 阅读目
  • 02FFMPEG的AVInputFormat结构体分析

    02FFMPEG的AVInputFormat结构体分析 概述 该结构体位于libavformat库中的avformat h中 1 AVInputFormat结构体 对于FFmpeg没给注释的 我尽量找对应的注释 确实没有的证明我们不需要知道
  • 【单目标优化算法】蜣螂优化算法(Dung beetle optimizer,DBO)(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 文献来源 4 Matlab代码实现 1 概述 本文提出了一种新的基于种群的技术 称为粪甲
  • 多益网络前端笔试

    1 常用的http请求方式有 https www cnblogs com yinrw p 10694474 html 2
  • 超实用!JavaScript修改CSS变量,达到动态修改样式的目的

    在网页开发中 我们通常使用CSS来设置网页的样式 但是 在开发过程中 有时候我们需要根据不同的条件来动态修改样式 这时候就需要使用JavaScript来实现 在CSS中 有一种变量的概念 可以使用变量来定义颜色 字体大小等样式属性 在本篇文
  • as报错:Gradle 7.2 requires Java 8 or later to run. Your build is currently configured to use Java 7

    android studio创建项目时报错 Gradle 7 2 requires Java 8 or later to run Your build is currently configured to use Java 7 意思是说我现
  • [计算机网络]HTTP、UDP、TCP协议

    个人主页 你帅你先说 欢迎点赞 关注 收藏 既选择了远方 便只顾风雨兼程 欢迎大家有问题随时私信我 版权 本文由 你帅你先说 原创 CSDN首发 侵权必究 为您导航 1 HTTP协议 1 1URL 1 2HTTP协议格式 1 2 1HTTP
  • 正则表达式与Regex函数

    影响使用正则表达式的因素 C语言中使用strstr或strcmp函数 需要确定字符串中含有此子串 进行字符串匹配 匹配效率较低 且两个函数均为绝对匹配 灵活性较差 正则表达式通过数据的规则 匹配提取数据 正则表达式的匹配方式 工作流程 正则
  • 【DL】第 9 章:新兴的神经网络设计

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • TypeScript基本类型的了解

    数据类型 number string boolean跟js类同 字面量 它就是值本身及值本身的类型 let a 10 a 10 a 125 a 123 any表示任何类型 可以任意赋值 一个变量赋值为any后相当于对该变量关闭了ts的类型检
  • 用cmake将源代码编译成可运行的工程项目

    网上下载的完整源代码 比如 深入理解OpenCV 实用计算机视觉项目解析 想要用来学习 首先得把源码编译到自己使用的平台上 比如我的是VS2010 一般源代码给出的是 C Source C C Header 文件 以及CmakeLists
  • JS之对象-对象增强语法(ES6)

    ES6对JS的对象提供一些封装的语法糖 用来简化某些操作 1 属性值简写 给对象添加变量时 同名属性可以简写 let name zhangsan let Obj name test lalala console log name zhang
  • AI时代带来的图片造假危机,该如何解决

    一 前言 当今 图片造假问题非常泛滥 已经成为现代社会中一个严峻的问题 随着AI技术不断的发展 人们可以轻松地通过图像编辑和AI智能生成来篡改和伪造图片 使其看起来真实而难以辨别 之前就看到过一对硕士夫妻为了骗保竟篡改结婚证 离婚证等信息
  • 常用的maven命令

    常用maven命令总结 mvn v 查看版本 mvn archetype create 创建 Maven 项目 mvn compile 编译源代码 mvn test compile 编译测试代码 mvn test 运行应用程序中的单元测试
  • C++ 自带Sort(快速排序)所需的头文件和操作方法

    需要的头文件 include
  • Promise实现串行执行

    第二种写法 const arr 1 2 3 4 5 6 arr reduce async prs cur idx gt const t await prs const time idx 0 0 1000 idx 1 500 return n
  • 脚本重启电信天翼网关

    背景 家里光猫经常断网 重启才能恢复 光猫位置拔电源不方便 telnet登录重启需破解有风险 因此采用curl模拟登录web重启 最后通过其他脚本监测 实现断网时自动执行重启 bin sh a 1 loginfo curl s L http
  • MySQL与Oracle主键冲突解决方式

    1 MySQL主键冲突 当主键冲突时 可以选择更新或替换 1 1 主键冲突 主键冲突 mysql gt desc my class Field Type Null Key
  • Windows子系统安装

    Windows子系统安装 1 Windows配置修改 1 开启开发者模式 在设置中找到更新与安全 选择开发者选项将开发者模式打开 2 开启子系统 打开控制面板选择程序 按图依次点击勾选适用于Linux的Windows子系统 然后进行重启电脑
  • 四叉树在MMO中的应用

    四叉树是一种树状数据结构 在每一个节点上会有四个子区块 四叉树常应用于二维空间数据的分析与分类 它将数据区分成为四个象限 数据范围可以是方形或矩形或其他任意形状 四叉树在游戏中常常用于减少遍历的次数 1 比如 地图上有N个怪 玩家砍了一刀