MMORPG大型游戏设计与开发(server 游戏场景 事件)
游戏截图
场景事件
一个完整的对象一般都拥有事件,至于什么是事件在这里就不多解释了。
在场景中的事件在天龙/武侠世界中的事件包含场景初始化、场景定时器、场景退出、玩家进入场景、角色升级、角色死亡、角色重生、场景通知、任务接受检查、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);
}
}
MMORPG大型游戏设计与开发(server 游戏场景 事件)的更多相关文章
- MMORPG大型游戏设计与开发(游戏服务器 游戏场景 概述 updated)
我们在玩游戏的时候,我们进入游戏后第一眼往往都是看到游戏世界中的场景,当然除了个别例外,因为那些游戏将游戏场景隐藏了起来,如文字游戏中的地点一样.既然我们接触了游戏世界的核心,那么作为核心的场景又包括 ...
- MMORPG大型游戏设计与开发(服务器 AI 事件)
AI中的事件与场景中的事件大致相同,都是由特定的条件触发的.只不过AI的事件与其他事件不同的是,对于AI的事件往往是根据不同的AI类型,和动态的触发条件下才产生的.其实不管AI多么智能,它对应的触发条 ...
- MMORPG大型游戏设计与开发(概述)updated
1.定义 MMORPG,是英文Massive(或Massively)Multiplayer Online Role-PlayingGame的缩写,即大型多人在线角色扮演游戏. 2.技术与知识 在这系列 ...
- MMORPG大型游戏设计与开发(服务器 AI 概述)
游戏世界中我们拥有许多对象,常见的就是角色自身以及怪物和NPC,我们可以见到怪物和NPC拥有许多的行为,比如说怪物常常见到敌对的玩家就会攻击一样,又如一些NPC来游戏世界中走来走去,又有些怪物和NPC ...
- MMORPG大型游戏设计与开发(服务器 游戏场景 核心详述)
核心这个词来的是多么的高深,可能我们也因为这个字眼望而却步,也就很难去掌握这部分的知识.之所以将核心放在最前面讲解,也可以看出它真的很重要,希望朋友们不会错过这个一直以来让大家不熟悉的知识,同我一起进 ...
- MMORPG大型游戏设计与开发(客户端架构 part8 of vegine)
脚本模块是游戏设计中争论比较多的话题,那是因为作为脚本本身所带来的利弊.其实这都无关紧要,取舍是人必须学会的一项技能,如果你不会取舍那么就让趋势给你一个满意的答复.自从魔兽世界以及传奇(世界)问世以来 ...
- MMORPG大型游戏设计与开发(服务器 游戏场景 事件)
今天第星期天,知识是永远是学习不完的,所以今天这部分算比较轻松,同时也希望大家会有一个好的周末.场景事件即场景的回调,和别的事件一样是在特定的条件下产生的,前面也介绍过场景的各种事件,今天详细的说一说 ...
- MMORPG大型游戏设计与开发(UI SYSTEM SHOW)
接下来一段时间,这些文件可能不再更新,期间我会学习和掌握一些前端知识.虽然我非常欣赏剑侠网络版叁和九阴真经的画面,但是那是一个庞大的游戏引擎,一般人是无法窥伺的,除非你是天才而且要拥有机器毫无中断的毅 ...
- MMORPG大型游戏设计与开发(part1 of net)
网络模块的设计,是大型多人在线游戏中比较重要的一部分.我之所以将网络模块放到最前面,是因为许许多多的开发者面对这一块的时候充满了疑惑,而且也觉得很神秘和深奥.这些我们面对到的困难,其实是由于我们对这方 ...
随机推荐
- 10-XML
今日知识 1. xml * 概念 * 语法 * 解析 xml概念 1. 概念:Extensible Markup Language 可扩展标记语言 * 可扩展:标签都是自定义的. <user&g ...
- python 微信红包
def redbags(money, num=10): import random choice = random.sample(range(1, money * 100), num - 1) cho ...
- jumpserver install
本文来源jumpserver官网 一步一步安装 环境 系统: CentOS 7 IP: 192.168.244.144 关闭 selinux 和防火墙 # CentOS 7 $ setenforce ...
- js两个页面之间URL传递参数中文乱码
- css3小叮当(转载)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- day28 re(正则)模块
目录 re模块有什么用? re模块的基础使用 元字符 终极 贪婪模式 非贪婪模式 re模块高级 comple match和search re.split() sub和subn 分组 re模块有什么用? ...
- nginx日志按日期自动切割脚本
#!/bin/bash #nginx日志切割脚本 #author:setevn #设置日志文件存放目录 logs_path="/usr/local/nginx/logs/" #设置 ...
- Spring Boot 项目学习 (四) Spring Boot整合Swagger2自动生成API文档
0 引言 在做服务端开发的时候,难免会涉及到API 接口文档的编写,可以经历过手写API 文档的过程,就会发现,一个自动生成API文档可以提高多少的效率. 以下列举几个手写API 文档的痛点: 文档需 ...
- [luogu2765 网络流24题] 魔术球问题 (dinic最大流)
传送门 题目描述 «问题描述: 假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3,...的球. (1)每次只能在某根柱子的最上面放球. (2)在同一根柱子中,任何2个相邻球的编号之 ...
- 记录python爬取猫眼票房排行榜(带stonefont字体网页),保存到text文件,csv文件和MongoDB数据库中
猫眼票房排行榜页面显示如下: 注意右边的票房数据显示,爬下来的数据是这样显示的: 网页源代码中是这样显示的: 这是因为网页中使用了某种字体的缘故,分析源代码可知: 亲测可行: 代码中获取的是国内票房榜 ...