MMORPG大型游戏设计与开发(服务器 游戏场景 事件)
今天第星期天,知识是永远是学习不完的,所以今天这部分算比较轻松,同时也希望大家会有一个好的周末。场景事件即场景的回调,和别的事件一样是在特定的条件下产生的,前面也介绍过场景的各种事件,今天详细的说一说这些事件的具体作用。
游戏截图
场景事件
一个完整的对象一般都拥有事件,至于什么是事件在这里就不多解释了。在场景中的事件在天龙/武侠世界中的事件包括场景初始化、场景定时器、场景退出、玩家进入场景、角色升级、角色死亡、角色重生、场景通知、任务接受检查、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、基数排序(比较复杂)
数排序算法实现比较复杂,它是一种多关键字的排序算法,属于分类排序。因为基数序算法不需要过多比较,所以在数据较多的情况下,采用基数排序算法的效率要高于他的排序算法。
基数排序也是一种稳定的算法。
code.
#include <stdio.h> #include <stdint.h> #include <malloc.h> #include <inttypes.h> #include <math.h> #include <string.h> #include <stdlib.h> /** * 基数排序算法实现比较复杂,它是一种多关键字的排序算法,属于分类排序。因为基数 * 排序算法不需要过多比较,所以在数据较多的情况下,采用基数排序算法的效率要高于 * 其他的排序算法。 */ #define SIZEMAX 200 //待排序元素的最大个数 #define N 10 //待排序元素的实际个数 #define NUMBERKEY_MAX 6 //关键字项数的最大值 #define RADIX 10 //关键字基数,10表示十进制数字可以分为十组 typedef struct listcell_struct { int32_t key[NUMBERKEY_MAX]; //关键字 int32_t next; } listcell_t; //静态链表的节点,存放待排序的元素 typedef struct list_struct { listcell_t data[SIZEMAX]; //存储元素,data[0]为头节点 int32_t keynumber; //每个元素的当前关键字个数 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; //求字关键字的个数 (*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,作为关键字 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个关键字key[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]); //将关键字转换为数字 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成为按关键字非递减的静态链表,L.r[0]为头节点 void radixsort(list_t *L) { int32_t i; addr f, r; //由低位到高位一次对各关键字进行分配和收集 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); } }