首页 > 技术文章 > MMORPG大型游戏设计与开发(server 游戏场景 事件)

slgkaifa 2017-06-25 20:16 原文

游戏截图

  

场景事件

  一个完整的对象一般都拥有事件,至于什么是事件在这里就不多解释了。

在场景中的事件在天龙/武侠世界中的事件包含场景初始化、场景定时器、场景退出、玩家进入场景、角色升级、角色死亡、角色重生、场景通知、任务接受检查、NPC对话默认事件、NPC事件列表事件。

  1、场景初始化(scene init)

    场景初始化事件,负责副本场景的数据维护、负责副本定时器的开启、负责城市入口的挂接、调用脚本初始化函数。

  2、场景定时器(scene timer)

    负责定时器数据的处理,通常会调用到脚本的对应函数。

  3、场景退出(scene quit)

    场景退出通常是清理数据的作用,首先调用脚本的场景退出函数。然后移除全部对象。移除的对象包含玩家、怪物、宠物、操作台、掉落包。

  4、玩家进入(player enter)

    一个玩家进入场景产生的事件,一般调用脚本函数处理该事件。

  5、玩家升级(player level up)

    玩家升级后的回调。试想一下玩家升级后会有哪些数据的改变?场景中有哪些数据须要更新?是不是这里的玩家升级事件也就包含了其它数据的改变?

  6、玩家死亡(player death)

    玩家死亡一般伴随着很多数据的改变。如常见的金钱掉落,物品掉落等等,还有玩家死亡可能会触发任务状态的改变,或一个剧情等等。

  7、玩家复活(player relive)

    玩家的复活事件。是不是玩家复活的时候常见的数据直接在逻辑中就实现了,还是要放到事件函数中?我们这里常说的事件函数一般都是指调用脚本,调用脚本事实上为了可以频繁的修改数据。

  8、玩家断线(player disconnect)

    断线是一个痛苦的事情,特别是在我们玩的正开心的时候,可是怎么也比只是一些实在的数据处理的时候,如天龙八部中离线事件中把玩家交易的全然处理放到了这里。

  9、场景通知(scene notify)

    一開始一听这个事件的时候我也曾认为非常迷茫。可是看了详细代码的时候才知道在游戏中是为副本场景来服务的,仅仅有当副本已经初始化完毕才会收到该消息用来传送玩家到副本中。

  10、任务接受检查事件(mission accept check)

    要接受一个任务会有条件的。如人物等级的限制、所在场景、事件限制等等。

  11、NPC默认对话框事件(npc default dialog)

    假设是能够交互的NPC则会有对话框来表现这样的交互,假设还有默认的操作则将默认操作的选项显示出来。

  12、NPC默认事件列表(npc default event list)

    默认事件列表是指NPC默认的一些事件,这些事件在经过该回调正确的检查之后才显示正确的选项。

算法(归并和基数排序)

  1、归并排序

    并排序算法实现复杂。由于二路归并算法须要的暂时空间较大,所以经常常使用在外部序中。

(其核心的思想为将两个或两个以上的元素有序序列合并为一个有序序列)

    并算法是一种稳定的排序算法。

    code.

复制代码
#include <stdio.h>
#include <stdint.h>
#include <malloc.h>
#include <inttypes.h>

/**
 * 归并排序算法实现复杂,由于二路归并算法须要的暂时空间较大,所以经常常使用在外部
 * 排序中。(其核心的思想为将两个或两个以上的元素有序序列合并为一个有序序列)
 * 归并算法是一种稳定的排序算法。
 */

//将source数组中的元素拷贝到dest数组中,当中。length为长度。first是目标数组的起始位置
void copyarray(int32_t source[], int32_t dest[], int32_t length, int32_t first);
//归并排序
void mergesort(int32_t array[], int32_t left, int32_t right);
//合并两个子序列中的元素
void merge(int32_t array[], int32_t left, int32_t right);
//数组打印
void displayarray(int32_t array[], int32_t length);

int32_t main(int32_t argc, char *argv[]) {
  int32_t array[] = {100, 35, 23, 6, 81, 33, 125, 378, 199};
  int32_t length = sizeof(array) / sizeof(array[0]);
  printf("before sort: ");
  displayarray(array, length);
  mergesort(array, 0, length - 1);
  printf("after sort: ");
  displayarray(array, length);
  return 0;
}

void copyarray(int32_t source[], int32_t dest[], int32_t length, int32_t first) {
  int32_t i, j = first;
  for (i = 0; i < length; ++i) {
    dest[j] = source[i];
    ++j;
  }
}

void mergesort(int32_t array[], int32_t left, int32_t right) {
  int32_t i;
  if (left < right) {
    i = (left + right) / 2;
    mergesort(array, left, i);
    mergesort(array, i + 1, right);
    merge(array, left, right);
  }
}

void merge(int32_t array[], int32_t left, int32_t right) {
  int32_t begin1, begin2, middle, k = 0, length;
  int32_t *pointer = NULL;
  begin1 = left;
  middle = (left + right) / 2;
  begin2 = middle + 1;
  length = right - left + 1;
  pointer = (int32_t *)malloc(length * sizeof(int32_t));
  if (NULL == pointer) return;
  while (begin1 <= middle && begin2 <= right) {
    if (array[begin1] < array[begin2]) {
      pointer[k++] = array[begin1++];
    } else {
      pointer[k++] = array[begin2++];
    }
  }
  while (begin1 <= middle) pointer[k++] = array[begin1++];
  while (begin2 <= right) pointer[k++] = array[begin2++];
  copyarray(pointer, array, length, left);
  if (pointer != NULL) free(pointer);
  pointer = NULL;
}

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

    result.

  2、基数排序(比較复杂)

    数排序算法实现比較复杂,它是一种多keyword的排序算法,属于分类排序。由于基数序算法不须要过多比較,所以在数据较多的情况下,採用基数排序算法的效率要高于他的排序算法。

    基数排序也是一种稳定的算法。

    code.

复制代码
#include <stdio.h>
#include <stdint.h>
#include <malloc.h>
#include <inttypes.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>

/**
 * 基数排序算法实现比較复杂。它是一种多keyword的排序算法,属于分类排序。

由于基数 * 排序算法不须要过多比較。所以在数据较多的情况下。採用基数排序算法的效率要高于 * 其它的排序算法。 */ #define SIZEMAX 200 //待排序元素的最大个数 #define N 10 //待排序元素的实际个数 #define NUMBERKEY_MAX 6 //keyword项数的最大值 #define RADIX 10 //keyword基数,10表示十进制数字能够分为十组 typedef struct listcell_struct { int32_t key[NUMBERKEY_MAX]; //keyword int32_t next; } listcell_t; //静态链表的节点,存放待排序的元素 typedef struct list_struct { listcell_t data[SIZEMAX]; //存储元素,data[0]为头节点 int32_t keynumber; //每一个元素的当前keyword个数 int32_t length; //静态链表的当前长度 } list_t; //静态链表,存放元素序列 typedef int32_t addr[RADIX]; //指针数组的类型。用来指向每一个链表的第一个节点和最后一个节点 void displaylist(list_t L); //输出链表中的元素 void display_staticlist(list_t L); //以静态链表的形式输出元素 void initlist(list_t *list, int32_t dest[], int32_t length); int32_t transchar(char _char); //将字符转换为数字 void distribute(listcell_t data[], int32_t i, addr f, addr r); //分配 void collect(listcell_t data[], addr f, addr r); //收集 void radixsort(list_t *L); int32_t main(int32_t argc, char *argv[]) { int32_t array[N] = {131, 23, 56, 34, 2, 11, 19, 38, 22, 33}; list_t L; initlist(&L, array, N); printf("need sort number is %d, key number is %d\n", L.length, L.keynumber); printf("before sort static list: "); display_staticlist(L); printf("before sort list: \n"); displaylist(L); radixsort(&L); printf("after sort: \n"); displaylist(L); return 0; } //按数组序号形式输出静态链表 void displaylist(list_t L) { int32_t i, j; printf("no key addr \n"); for (i = 1; i <= L.length; ++i) { printf("%2d ", i); for (j = L.keynumber - 1; j >= 0; --j) { printf("%c", L.data[i].key[j]); } printf(" %d\n", L.data[i].next); } } //按链表形式输出静态链表 void display_staticlist(list_t L) { int32_t i = L.data[0].next, j; while (i) { for (j = L.keynumber - 1; j >= 0; --j) printf("%c", L.data[i].key[j]); printf(" "); i = L.data[i].next; } printf("\n"); } //初始化静态链表L void initlist(list_t *L, int32_t array[], int32_t length) { char _char1[NUMBERKEY_MAX] , _char2[NUMBERKEY_MAX]; int32_t i, j; int32_t max = array[0]; for (i = 1; i < length; ++i) { //将最大的元素存入max if (max < array[i]) max = array[i]; } (*L).keynumber = (int32_t)(log10(max)) + 1; //求字keyword的个数 (*L).length = length; //待排序个数 for (i = 1; i <= length; ++i) { //itoa(array[i - 1], _char1 , 10); //将整型转换为字符,并存入_char sprintf(_char1, "%d", array[i - 1]); //假设_char的长度<max的位数,则在_char前补'0' for (j = strlen(_char1); j < (*L).keynumber; ++j) { strcpy(_char2, "0"); strcat(_char2, _char1); strcpy(_char1, _char2); } //将每一个元素的个位数存入key。作为keyword for (j = 0; j < (*L).keynumber; ++j) { (*L).data[i].key[j] = _char1[(*L).keynumber - 1 - j]; } } for (i = 0; i < (*L).length; ++i) (*L).data[i].next = i + 1; (*L).data[(*L).length].next = 0; } int32_t transchar(char _char) { return _char - '0'; } //为data数组中的第i个keywordkey[i]建立radix个子表。使同一子表中元素的key[i]同样 //f[0...radix - 1]和r[0...radix - 1]分别指向各个子表中第一个和最后一个元素 void distribute(listcell_t data[], int32_t i, addr f, addr r) { int32_t j, p; for (j = 0; j < RADIX; ++j) f[j] = 0; //初始化各个子表 for (p = data[0].next; p; p = data[p].next) { j = transchar(data[p].key[i]); //将keyword转换为数字 if (!f[j]) { //f[j]是空表。则f[j]指示第一个元素 f[j] = p; } else { data[r[j]].next = p; } r[j] = p; //将p所指的节点插入第j个子表中 } } //收集,按key[i]将f[0...radix - 1]所指各子表依次连接成一个静态链表 void collect(listcell_t data[], addr f, addr r) { int32_t i; int32_t temp; for (i = 0; !f[i]; ++i); //找第一个非空子表。为求兴许函数 data[0].next = f[i]; temp = r[i]; //r[0].next 指向第一个非空子表中的第一个节点 while (i < RADIX - 1) { for (i = i + 1; i < RADIX - 1 && !f[i]; ++i); //查找下一个非空子表 if (f[i]) { data[temp].next = f[i]; temp = r[i]; } } data[temp].next = 0; //temp指向最后一个非空子表中的最后一个节点 } //基数排序,使得L成为按keyword非递减的静态链表。L.r[0]为头节点 void radixsort(list_t *L) { int32_t i; addr f, r; //由低位到高位一次对各keyword进行分配和收集 for (i = 0; i < (*L).keynumber; ++i) { distribute((*L).data, i, f, r); //第i次分配 collect((*L).data, f, r); //第i次收集 printf("the %d times collect result: ", i + 1); display_staticlist(*L); } }

推荐阅读