• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

               C++ 技術中心

               :: 首頁 :: 聯系 ::  :: 管理
              160 Posts :: 0 Stories :: 87 Comments :: 0 Trackbacks

            公告

            鄭重聲明:本BLOG所發表的原創文章,作者保留一切權利。必須經過作者本人同意后方可轉載,并注名作者(天空)和出處(CppBlog.com)。作者Email:coder@luckcoder.com

            留言簿(27)

            搜索

            •  

            最新隨筆

            最新評論

            評論排行榜

            AOI主要有九宮格、燈塔和十字鏈表的算法實現。本文闡述十字鏈表的實現和嘗試。

            1. 基本原理

            根據二維地圖,將其分成x軸和y軸兩個鏈表。如果是三維地圖,則還需要維護多一個z軸的鏈表。將對象的坐標值按照大小相應的排列在相應的坐標軸上面。

            2. 基本接口

            對對象的操作主要有以下三個接口:

            • add:對象進入地圖;
            • leave:對象離開地圖;
            • move:對象在地圖內移動。

            2. 算法實現

            既然是鏈表,很自然地想到用線性表來實現。因為存在向前和向后找的情況,所以使用雙鏈表實現。其實實現也是非常簡單,就是兩個雙鏈表(這里以二維地圖舉例)。那么我們的節點需要四個指針,分布為x軸的前后指針,y軸的前后指針。

            // 雙鏈表(對象)
            class DoubleNode
            {
            public:
                DoubleNode(string key, int x, int y)
                {
                    this->key = key;
                    this->x = x;
                    this->y = y;
                    xPrev = xNext = NULL;
                };
                
                DoubleNode * xPrev;
                DoubleNode * xNext;
                
                DoubleNode * yPrev;
                DoubleNode * yNext;
                
                string key;  // 只是一個關鍵字
                int x; // 位置(x坐標)
                int y; // 位置(y坐標)

            private:

            };

            下面是地圖場景信息和接口。這里的實現比較粗略,是帶頭尾的的雙鏈表,暫時不考慮空間占用的問題。類Scene有分別有一個頭尾指針,初始化的時候會為其賦值,主要用DoubleNode類的指針來存儲x軸和y軸的頭尾。初始化的時候,將_head的next指針指向尾_tail;將_tail的prev指針指向_head
            // 地圖/場景
            class Scene
            {
            public:
                Scene()
                {
                    this->_head = new DoubleNode("[head]", 0, 0); // 帶頭尾的雙鏈表(可優化去掉頭尾)
                    this->_tail = new DoubleNode("[tail]", 0, 0);
                    _head->xNext = _tail;
                    _head->yNext = _tail;
                    _tail->xPrev = _head;
                    _tail->yPrev = _head;
                };

                // 對象加入(新增)
                DoubleNode * Add(string name, int x, int y);

                // 對象離開(刪除)
                void Leave(DoubleNode * node);

                // 對象移動
                void Move(DoubleNode * node, int x, int y);

                // 獲取范圍內的AOI (參數為查找范圍)
                void PrintAOI(DoubleNode * node, int xAreaLen, int yAreaLen);

            private:
                DoubleNode * _head;
                DoubleNode * _tail;
            };

            2.1. add(進入地圖)

            DoubleNode對象插入到十字鏈表中。x軸和y軸分別處理,處理方法基本一致。其實就是雙鏈表的數據插入操作,需要從頭開始遍歷線性表,對比相應軸上的值的大小,插入到合適的位置。

            void _add(DoubleNode * node)
            {
                // x軸處理
                DoubleNode * cur = _head->xNext;
                while(cur != NULL)
                {
                    if((cur->x > node->x) || cur==_tail) // 插入數據
                    {
                        node->xNext = cur;
                        node->xPrev = cur->xPrev;
                        cur->xPrev->xNext = node;
                        cur->xPrev = node;
                        break;
                    }
                    cur = cur->xNext;
                }

                // y軸處理
                cur = _head->yNext;
                while(cur != NULL)
                {
                    if((cur->y > node->y) || cur==_tail) // 插入數據
                    {
                        node->yNext = cur;
                        node->yPrev = cur->yPrev;
                        cur->yPrev->yNext = node;
                        cur->yPrev = node;
                        break;
                    }
                    cur = cur->yNext;
                }
            }

            假設可視范圍為x軸2以內,y軸2以內,則運行:

            1. 分別插入以下數據a(1,5)、f(6,6)、c(3,1)、b(2,2)、e(5,3),然后插入d(3,3),按照x軸和y軸打印其雙鏈表結果;
            2. 插入d(3,3)數據,求其可視AOI范圍(如圖,除了f(6,6),其它對象都在d的可視范圍內)。

            控制臺結果(前8行):

            步驟1結果圖示:

            步驟2結果圖示:

            2.2. leave(離開地圖)和move(移動)

            其實都是雙鏈表的基本操作,斷掉其相應的指針就好了。按理,是需要

            move和leave操作如圖,move是將d(3,3)移動到(4,4),然后再打印其AOI范圍。

            控制臺結果:

            移動后AOI范圍圖示:


            3. 完整代碼實例
            // ConsoleApplication2.cpp : 定義控制臺應用程序的入口點。
            //

            #include "stdafx.h"


            #include "stdafx.h"
            #include "stdio.h"
            #include <iostream>
            #include <string>

            using namespace std;

            // 雙鏈表(對象)
            class DoubleNode
            {
            public:
                DoubleNode(string key, int x, int y)
                {
                    this->key = key;
                    this->x = x;
                    this->y = y;
                    xPrev = xNext = NULL;
                };

                DoubleNode * xPrev;
                DoubleNode * xNext;

                DoubleNode * yPrev;
                DoubleNode * yNext;

                string key;
                int x; // 位置(x坐標)
                int y; // 位置(y坐標)

            private:

            };




            // 地圖/場景
            class Scene
            {
            public:

                Scene()
                {
                    this->_head = new DoubleNode("[head]", 0, 0); // 帶頭尾的雙鏈表(可優化去掉頭尾)
                    this->_tail = new DoubleNode("[tail]", 0, 0);
                    _head->xNext = _tail;
                    _head->yNext = _tail;
                    _tail->xPrev = _head;
                    _tail->yPrev = _head;
                };

                // 對象加入(新增)
                DoubleNode * Add(string name, int x, int y)
                {

                    DoubleNode * node = new DoubleNode(name, x, y);
                    _add(node);
                    return node;
                };

                // 對象離開(刪除)
                void Leave(DoubleNode * node)
                {
                    node->xPrev->xNext = node->xNext;
                    node->xNext->xPrev = node->xPrev;
                    node->yPrev->yNext = node->yNext;
                    node->yNext->yPrev = node->yPrev;

                    node->xPrev = NULL;
                    node->xNext = NULL;
                    node->yPrev = NULL;
                    node->yNext = NULL;
                };

                // 對象移動
                void Move(DoubleNode * node, int x, int y)
                {
                    Leave(node);
                    node->x = x;
                    node->y = y;
                    _add(node);
                };

                // 獲取范圍內的AOI (參數為查找范圍)
                void PrintAOI(DoubleNode * node, int xAreaLen, int yAreaLen)
                {
                    cout << "Cur is: " << node->key << "(" << node->x << "," << node->y << ")" << endl;
                    cout << "Print AOI:" << endl;

                    // 往后找
                    DoubleNode * cur = node->xNext;
                    while (cur != _tail)
                    {
                        if ((cur->x - node->x) > xAreaLen)
                        {
                            break;
                        }
                        else
                        {
                            int inteval = 0;
                            inteval = node->y - cur->y;
                            if (inteval >= -yAreaLen && inteval <= yAreaLen)
                            {
                                cout << "\t" << cur->key << "(" << cur->x << "," << cur->y << ")" << endl;
                            }
                        }
                        cur = cur->xNext;
                    }

                    // 往前找
                    cur = node->xPrev;
                    while (cur != _head)
                    {
                        if ((node->x - cur->x) > xAreaLen)
                        {
                            break;
                        }
                        else
                        {
                            int inteval = 0;
                            inteval = node->y - cur->y;
                            if (inteval >= -yAreaLen && inteval <= yAreaLen)
                            {
                                cout << "\t" << cur->key << "(" << cur->x << "," << cur->y << ")" << endl;
                            }
                        }
                        cur = cur->xPrev;
                    }
                };

                // 調試代碼
                void PrintLink()  // 打印鏈表(從頭開始)
                {
                    // 打印x軸鏈表
                    DoubleNode * cur = _head->xNext;
                    while (cur != _tail)
                    {
                        cout << (cur->key) << "(" << (cur->x) << "," << (cur->y) << ") -> ";
                        cur = cur->xNext;
                    }
                    cout << "end" << endl;

                    // 打印y軸鏈表
                    cur = _head->yNext;
                    while (cur != _tail)
                    {
                        cout << (cur->key) << "(" << (cur->x) << "," << (cur->y) << ") -> ";
                        cur = cur->yNext;
                    }
                    cout << "end" << endl;
                };

            private:
                DoubleNode * _head;
                DoubleNode * _tail;

                void _add(DoubleNode * node)
                {
                    // x軸處理
                    DoubleNode * cur = _head->xNext;
                    while (cur != NULL)
                    {
                        if ((cur->x > node->x) || cur == _tail) // 插入數據
                        {
                            node->xNext = cur;
                            node->xPrev = cur->xPrev;
                            cur->xPrev->xNext = node;
                            cur->xPrev = node;
                            break;
                        }
                        cur = cur->xNext;
                    }

                    // y軸處理
                    cur = _head->yNext;
                    while (cur != NULL)
                    {
                        if ((cur->y > node->y) || cur == _tail) // 插入數據
                        {
                            node->yNext = cur;
                            node->yPrev = cur->yPrev;
                            cur->yPrev->yNext = node;
                            cur->yPrev = node;
                            break;
                        }
                        cur = cur->yNext;
                    }
                }
            };

            // --------------------------------------------

            int _tmain(int argc, _TCHAR* argv[])
            {
                Scene scene = Scene();
                // 增加
                scene.Add("a", 1, 5);
                scene.Add("f", 6, 6);
                scene.Add("c", 3, 1);
                scene.Add("b", 2, 2);
                scene.Add("e", 5, 3);
                DoubleNode * node = scene.Add("d", 3, 3);

                scene.PrintLink();
                scene.PrintAOI(node, 2, 2);

                // 移動
                cout << endl << "[MOVE]" << endl;
                scene.Move(node, 4, 4);
                scene.PrintLink();

                scene.PrintAOI(node, 2, 2);

                // 刪除
                cout << endl << "[LEAVE]" << endl;
                scene.Leave(node);
                scene.PrintLink();

            }

            算法的大概思想如下.每個場景維護兩個鏈表,分別為X軸和Y軸的坐標按序排列好的鏈表,也就是比如在X軸鏈表上,越在前的對象,X坐標越小,Y軸鏈表同理.這樣,每次需要更新狀態的時候,只需要在這個鏈表上向前或者向后遍歷結點就知道該通知誰了.

            這里假設有三個API:Add(向場景中添加一個對象),Leave(某對象離開場景),Move(某對象在場景中移動).

            來一個一個看.

            Add:根據新增對象的X,Y坐標,依次遍歷X,Y軸坐標鏈表,這里有兩個目的,一個是獲得這個新增對象的坐標在X,Y軸坐標的位置,另一方面獲得該通知哪些結點.通知的范圍,每個對象可以自己定制自己的通知范圍.但是為了簡單起見,在這里我們假設每個結點X,Y坐標相差1,而通知的范圍是2.比如原有的X軸坐標為:
            a->b->c->d->e->f->g->h
            假設新增一個對象z,它最終所在的位置是c和d之間,根據前面的假設,它只需要通知b,c,e,f四個結點就好了.所以,新增一個結點的時候,并不需要遍歷完鏈表的.
            但是這里還需要注意的是,一個結點,必須X,Y坐標同時都在通知范圍內才可以進入通知集合.

            Leave:在添加了對象之后,對象身上掛了兩個結點,分別存放在X,Y軸坐標鏈表上的位置,那么在這個對象要離開場景時,也只需要向前向后探索一定的范圍,就可以得到需要通知該對象要離開的集合了.

            Move:移動操作比較麻煩,但是也可以比較漂亮的解決.在更新位置之前,同樣根據前面的算法得到要通知的結點集合,稱為old_set;更新位置之后,再獲取另一個通知集合,稱為new_set;然后,old_set和new_set的交集,稱為move_set,在此集合中的結點,在移動前后都在通知范圍,因此要向這個集合的結點發送該對象移動的消息;而old_set-move_set的集合,在移動之后已經離開了視野,因此要向它們發送該對象離開的消息;最后,new_set-move_set是移動之后才看見該結點的結點集合,這個集合的結點,要發送該結點進入場景的消息.

            posted on 2017-03-07 15:09 C++技術中心 閱讀(1865) 評論(0)  編輯 收藏 引用 所屬分類: 游戲開發
            久久婷婷五月综合97色直播| 91精品国产色综合久久| 66精品综合久久久久久久| 欧美性大战久久久久久| 人人狠狠综合久久88成人| 热久久国产精品| 久久精品国产久精国产一老狼| 国产亚洲欧美成人久久片| 午夜精品久久久久久毛片| 2021国内久久精品| 久久精品国产一区二区电影| 丰满少妇人妻久久久久久| 亚洲精品国产字幕久久不卡| 久久久久99精品成人片试看| 欧美亚洲国产精品久久| 99久久无色码中文字幕| 亚洲国产成人久久综合碰| 久久国产精品免费一区| 亚洲国产成人久久精品影视| 亚洲国产成人精品女人久久久| 久久婷婷五月综合国产尤物app| AAA级久久久精品无码区| 99久久免费国产精品| 色综合久久无码中文字幕| 久久久久亚洲AV无码专区网站 | 日本福利片国产午夜久久| 久久久久久综合网天天| 精品久久久久久无码人妻热 | 九九热久久免费视频| 久久婷婷五月综合97色一本一本| 国产精品乱码久久久久久软件| 精品无码人妻久久久久久| 国产毛片久久久久久国产毛片 | 久久成人精品| 国产亚州精品女人久久久久久| 国产精品久久久久久久| 国产成人精品久久二区二区| 精品综合久久久久久888蜜芽| 精品久久8x国产免费观看| 久久香蕉国产线看观看精品yw| 99久久99久久精品国产片果冻|