MMORPG大型遊戲設計與開發(服務器 遊戲場景 搜索機)

雙十一註定是忙碌的日子,因此到了如今我纔將今天本身學習的內容拿出來跟你們分享。搜索機是我本身暫時取的名字,其實簡單的說就是場景裏提供搜索的一個工具,負責場景對象的範圍搜索和獲取。空洞的理論老是讓人一頭霧水,若是玩過遊戲的朋友不妨想想查看附近的玩家、選擇附近的玩家、點擊任務怪物名稱就能夠自動尋路打怪這些功能就大體有個印象了。node

一張截圖

搜索機

  一、數據

    1. 狀態

typedef enum operator_status_enum {
  kOperatorStatusContinue, //掃描繼續
  kOperatorStatusBreak, //中斷本次掃描,並進入下一次掃描
  kOperatorStatusStop, //中止掃描
} operator_status_t; //掃描狀態

    2. 基礎

typedef operator_base_struct {
  scene::Base *scene; //場景指針
  int32_t zoneid; //區域ID
  int32_t zoneradius; //搜索半徑
  bool scanhuman; //是否搜索玩家的列表
} operator_base_t; //基礎操做掃描數據結構

  二、實現 

     該實現爲通用的父類接口,具體的類型搜索在子類中實現。算法

    1. 初始化(init)

      初始化掃描控制器,主要是初始化基礎數據。數組

    2. 操做前回調(on before)

      回調須要在掃描以前的數據處理。網絡

    3. 判斷區域是否須要掃描(is need scan)

      根據區域ID判斷是否須要掃描,若是不須要則沒必要再掃描。數據結構

    4. 找到對象後的回調(on find object)

      找到了一個對象的返回值,判斷是否須要繼續掃描。函數

    5. 操做後的回調(on after)

      掃描完成後的邏輯數據處理。工具

  三、種類

    1. 附近活躍的隊友

typedef struct operator_active_teammates_strcut : public operator_base_struct {
  world_position_t position; //位置信息
  int16_t teamid; //隊伍ID
  uint32_t member_guid; //成員ID
  float radius; //搜索半徑
} operator_active_teammates_t; //活躍隊友的數據結構

    2. 面積有效狀態

      進入有效範圍則對象會得到該狀態。學習

typedef struct operator_AEimpact_struct : public operator_base_struct { 
  object::list_t *targets; //對象列表指針
  float radius; //搜索半徑
  int32_t count; //數量
  owner_impact impact; //擁有的特殊狀態
  impact_logic_t const *logic; //狀態邏輯對象指針
  object::Character *self; //本身的對象指針
  world_position_t center_point; //中心點位置
} operator_AEimpact_t; //面積有效狀態的數據結構

    3. 面積有效技能

      進入該技能左右範圍後對象會得到該技能的效果。ui

typedef struct operator_AEskill_struct : public operator_base_struct { 
  object::list_t *targets; //對象列表指針
  float radius; //搜索半徑
  skillinfo_t const *skillinfo; //技能信息對象指針
  object::Character *self; //本身的對象指針
  world_position_t center_point; //中心點位置
} operator_AEimpact_t; //面積有效技能的數據結構

    4. 合符聊天要求的對象

typedef struct operator_chat_struct : public operator_base_struct {
  packet::Base *packet; //網絡包指針
  int8_t chattype; //聊天的類型
  int16_t guildid; //幫會ID
  //其餘數據...
} operator_chat_t; //聊天對象的數據結構

    5. 附近的敵人

      好比任務中自動打怪的搜索。spa

typedef struct operator_enemy_struct : public operator_base_struct {
  object::Monster *monster; //怪物指針
  float radius; //搜索半徑
} operator_enemy_t; //敵人對象的數據結構

    6. 扇形掃描技能有效範圍

      進入扇形區域有效的範圍將被該技能做用。

typedef struct operator_sector_skill_struct : public operator_base_struct { 
  object::list_t *targets; //對象列表指針
  float radius; //搜索半徑 
  int32_t count; //最大能夠搜索的對象數量
  skillinfo_t const *skillinfo; //搜索主體的技能信息對象指針 
  object::Character *self; //本身的對象指針 
  world_position_t center_point; //中心點位置
} operator_sector_skill_t; //以扇形區域搜索技能範圍的結構

    7. 附近的隊友

      查詢附近的隊友信息。

typedef struct operator_teammates_struct : public operator_base_struct {  
  object::Monster *monster; //怪物對象指針
  float radius; //搜索半徑
  int32_t count; //最大能夠搜索的對象數量 
  int8_t type; //類型
  bool only_noenemy; //是否只搜索沒有敵人的隊員
  bool scan_allmonster; //是否掃描全部敵人
} operator_teammates_t; //隊伍搜索結構

    8. 附近的玩家

      查看附近的玩家列表(名稱、狀態、等級等)。

typedef struct operator_character_struct : public operator_base_struct {  
  object::list_t *targets; //對象列表指針
  float radius; //搜索半徑
  int32_t count; //最大能夠搜索的對象數量 
  object::Special *self; //搜索主體
  world_position_t center_point; //中心點位置
} operator_character_t; //特殊對象玩家搜索結構

    9. 附近的陷阱

      若是附近有陷阱,則對象在陷阱有效範圍裏將被陷阱做用。

typedef struct operator_trap_struct : public operator_base_struct {  
  object::list_t *targets; //對象列表指針
  float radius; //搜索半徑
  int32_t count; //最大能夠搜索的對象數量 
  object::Special *self; //搜索主體
  world_position_t center_point; //中心點位置
} operator_trap_t; //特殊對象搜索陷阱的結構

算法(樹查找和哈希查找)

  一、基於二叉排序樹的查找

    叉排序樹定義性質:

      1 若是二叉樹的左子樹不爲空,則左子樹上的每個節點的元素值都小於其對應的根節點元素的值。

      2 若是二叉樹的右子樹不爲空,則右子樹上的每個節點的元素值都大於其對應的根節點元素的值。

      3 時二叉樹的左子樹和右子樹同時知足一、2兩項特性,即左子樹和右子樹都是一棵二叉樹。

    基於二叉排序樹的查找算法分爲插入操做和查找操做的兩個部分。

    插入操做不須要移動節點,僅須要移動節點指針。

    code.

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <malloc.h>

/**
 * 二叉排序樹定義性質:
 * 1 若是二叉樹的左子樹不爲空,則左子樹上的每個節點的元素值都小於其對應的根節點元素的值。
 * 2 若是二叉樹的右子樹不爲空,則右子樹上的每個節點的元素值都大於其對應的根節點元素的值。
 * 3 同時二叉樹的左子樹和右子樹同時知足一、2兩項特性,即左子樹和右子樹都是一棵二叉樹。
 */

/**
 * 基於二叉排序樹的查找算法分爲插入操做和查找操做的兩個部分。
 * 插入操做不須要移動節點,僅須要移動節點指針。
 */
typedef struct node_struct {
  int32_t data;
  struct node_struct *left, *right;
} node_t, *nodepointer_t; //二叉樹的查找的結構

//二叉樹查找
nodepointer_t binarytree_search(nodepointer_t tree, int32_t x);
//二叉樹插入。若是樹中不存在元素x,則將x插入到正確的位置並返回1,不然返回0
int32_t binarytree_insert(nodepointer_t *trees, int32_t x);
//中序遍歷二叉排序樹
void in_ordertraverse(nodepointer_t tree);

int32_t main(int32_t argc, char *argv[]) {
  nodepointer_t tree = NULL, pointer;
  int32_t table[] = {32, 13, 23, 56, 53, 67, 65, 88, 25, 36};
  int32_t length = sizeof(table) / sizeof(table[0]);
  int32_t x, i;
  //插入並生成二叉排序樹
  for (i = 0; i < length; ++i)
    binarytree_insert(&tree, table[i]);
  printf("in order traverse list is: \n");
  in_ordertraverse(tree);
  printf("\nplease input a number you want search: ");
  scanf("%d", &x);
  pointer = binarytree_search(tree, x);
  if (pointer != NULL) {
    printf("%d is a member of array\n", x);
  } else {
    printf("%d is not a member of array\n", x);
  }
  return 0;
}

nodepointer_t binarytree_search(nodepointer_t tree, int32_t x) {
  node_t *pointer = NULL;
  if (tree != NULL) {
    pointer = tree;
    while (pointer != NULL) {
      if (pointer->data == x) { //若是找到,則返回指向該節點的指針
        return pointer;
      } else if (x < pointer->data) { //若是關鍵字小於pointer指向的節點的值,則在左子樹中查找
        pointer = pointer->left;
      } else if (x > pointer->data) { //若是關鍵字大於pointer指向的節點的值,則在右子樹中查找
        pointer = pointer->right;
      }
    }
  }
  return NULL;
}

int32_t binarytree_insert(nodepointer_t *trees, int32_t x) {
  node_t *pointer = NULL, *current = NULL, *parent = NULL;
  current = *trees;
  while (current != NULL) {
    if (current->data == x) //若是二叉樹中存在元素爲x的節點,則返回0
      return 0;
    parent = current; //parent指向current的前驅節點
    if (x < current->data) { //若是關鍵字小於pointer指向節點的值,則在左子樹中查找
      current = current->left;
    } else { //若是關鍵字大於pointer指向節點的值,則在右子樹中查找
      current = current->right;
    }
  }
  pointer = (node_t *)malloc(sizeof(node_t)); //生成節點
  if (NULL == pointer) return 0; //內存不足
  pointer->data = x;
  pointer->left = NULL;
  pointer->right = NULL;
  if (!parent) { //若是二叉樹爲空,則第一節點成爲根節點
    *trees = pointer;
  } else if (x < parent->data) { //若是x小於parent指向的節點元素,則x成爲parent的左節點數據
    parent->left = pointer;
  } else { //若是x大於parent所指向的節點元素,則x成爲parent的右節點數據
    parent->right = pointer;
  }
  return 1;
}

void in_ordertraverse(nodepointer_t tree) {
  if (tree) {
    in_ordertraverse(tree->left); //中序遍歷左子樹
    printf("%4d", tree->data); //訪問根節點
    in_ordertraverse(tree->right); //中序遍歷右子樹
  }
}

    result.

  二、哈希查找

    哈希表的查找方法與前面的基於線性和樹形的查找算法不一樣,哈希表直接定位了元素所在位置,基本不須要逐個比較元素(除了衝突)。

    該算法須要解決的兩個問題:構造哈希表和處理衝突。

    最經常使用的構造哈希表的方法是除留餘數法,最爲經常使用的處理衝突的方法是開放定址法和鏈地址法。

    code.

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <malloc.h>
#include <stdlib.h>

/**
 * 哈希表的查找方法與前面的基於線性和樹形的查找算法不一樣,哈希表直接定位了元素所在
 * 的位置,基本不須要逐個比較元素(除了衝突)。
 * 該算法須要解決的兩個問題:構造哈希表和處理衝突。
 * 最經常使用的構造哈希表的方法是除留餘數法,最爲經常使用的處理衝突的方法是開放定址法和鏈地址法。
 */ 

typedef struct datatype_struct {
  int32_t value; //元素值
  int32_t repeatcount; //重複次數
} datatype_t; //元素類型結構

typedef struct hashtable_struct {
  datatype_t *data;
  int32_t length; //長度
  int32_t number; //個數
} hashtable_t; //哈希表的結構

//構造哈希表並處理衝突
void create_hashtable(hashtable_t *hashtable, 
                      int32_t m, 
                      int32_t p, 
                      int32_t hash[], 
                      int32_t length);
//在哈希表中查找值爲x的元素
int32_t hash_search(hashtable_t hashtable, int32_t x);
//求哈希表的平均查找長度
void hashASL(hashtable_t hashtable, int32_t m);
//哈希表打印
void displayhash(hashtable_t hashtable, int32_t m);
//數組打印
void displayarray(int32_t array[], int32_t length);

int32_t main(int32_t argc, char *argv[]) {
  int32_t hash[] = {45, 35, 23, 17, 83, 27, 91, 33, 56, 78, 99};
  hashtable_t hashtable;
  int32_t m = 11, p = 11;
  int32_t length = sizeof(hash) / sizeof(hash[0]);
  int32_t position, x;
  create_hashtable(&hashtable, m, p, hash, length);
  displayhash(hashtable, m);
  printf("please a number you want search: ");
  scanf("%d", &x);
  position = hash_search(hashtable, x);
  printf("%d in array position: %d\n", x, position);
  hashASL(hashtable, m);
  return 0;
}

void create_hashtable(hashtable_t *hashtable, 
                      int32_t m, 
                      int32_t p, 
                      int32_t hash[], 
                      int32_t length) {
  int32_t i, k = 1;
  int32_t sum, addr, di;
  (*hashtable).data = (datatype_t *)malloc(m * sizeof(datatype_t));
  if (NULL == (*hashtable).data) return; //not enough memory
  (*hashtable).number = length; //元素個數
  (*hashtable).length = m; //哈希表長度
  for (i = 0; i < m; ++i) { //哈希表初始化
    (*hashtable).data[i].value = -1;
    (*hashtable).data[i].repeatcount = 0;
  }
  for (i = 0; i < length; ++i) { //構造哈希表並初始化
    sum = 0; //sum 記錄衝突次數
    addr = hash[i] % p; //利用除留餘數法求哈希函數地址
    di = addr;
    //若是不衝突則將元素存儲在表中
    if (-1 == (*hashtable).data[addr].value) {
      (*hashtable).data[addr].value = hash[i];
      (*hashtable).data[addr].repeatcount = 1;
    } else { //用線性探測再散列法處理衝突
      do {
        di = (di + k) % m;
        sum += 1;
      } while ((*hashtable).data[di].value != -1);
      (*hashtable).data[di].value = hash[i];
      (*hashtable).data[di].repeatcount = sum + 1;
    }
  }
}

int32_t hash_search(hashtable_t hashtable, int32_t x) {
  int32_t d, d1, m;
  m = hashtable.length;
  d = d1 = x % m;
  while (hashtable.data[d].value != -1) {
    if (hashtable.data[d].value == x) { //若是找到x,則返回其所在位置
      return d;
    } else { //若是沒有找到,則繼續向後查找
      d = (d + 1) % m;
    }
    //若是已經遍歷完全部位置仍是沒有找到,則返回0
    if (d == d1) return 0;
  }
  return 0;
}

void hashASL(hashtable_t hashtable, int32_t m) {
  float avg = 0;
  int32_t i;
  for (i = 0; i < m; ++i)
    avg = avg + hashtable.data[i].repeatcount;
  avg = avg / hashtable.number;
  printf("avg search length ASL: %2.f", avg);
  printf("\n");
}

void displayhash(hashtable_t hashtable, int32_t m) {
  int32_t i;
  printf("hash table address: ");
  for (i = 0; i < m; ++i) //輸出哈希表的地址
    printf("%-5d", i);
  printf("\n");
  printf("member value: ");
  for (i = 0; i < m; ++i) //輸出哈希表的元素值
    printf("%-5d", hashtable.data[i].value);
  printf("\n");
  printf("repeat times: ");
  for (i = 0; i < m; ++i) //衝突次數
    printf("%-5d", hashtable.data[i].repeatcount);
  printf("\n");
}

void displayarray(int32_t array[], int32_t length) {
  int32_t i;
  for (i = 0; i < length; ++i)
    printf("%4d", array[i]);
  printf("\n");
}

    result.

相關文章
相關標籤/搜索