[game]十字鏈表的AOI算法實現


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. 完整代碼實例

#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;
		}
	}
};

// --------------------------------------------
void main()
{
	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();
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM