场景和副本是玩家活动的区域,场景在服务器启动的时候由mod_scene进程创建,场景信息保存在?ETS_SCENE中,并一直存在;副本在玩家请求进入的时候会创建副本服务进程,当玩家离开的时候会撤销进程。

场景:

场景相关的模块为mod_scene.erl和lib_scene.erl,
场景的基本要素:怪物,NPC,mask(包含场景可移动坐标信息),它们在场景初始化的时候被加载。

场景初始化过程:
1.服务器启动的时候,通过mod_scene.erl启动一个场景管理进程,将所有场景id载入
2.使用load_scene(SceneId)初始化场景:

load_scene(SceneId) ->S = data_scene:get(SceneId),case S#ets_scene.type =:= 2 orelse S#ets_scene.type =:= 3 oftrue -> %% 副本、帮会的原始场景,就不加载ok;false ->load_npc(S#ets_scene.npc, SceneId),load_mon(S#ets_scene.mon, SceneId),ets:insert(?ETS_SCENE, S#ets_scene{id = SceneId, mon=[], npc=[], mask=[]}),case data_mask:get(SceneId) of"" -> ?ERR("场景的坐标MASK为空:~w", [SceneId]);Mask1 -> load_mask(Mask1, 0, 0, SceneId)endend.

场景加载过程:

1.获取对应的配置信息,根据配置信息创建NPC和怪物,将场景插入ETS_SCENE表中。
2.获取每一个NPC配置信息,插入ETS_NPC表中
3.获取每一个怪物配置信息,开启一个怪物进程(gen_fsm),并打开怪物的战斗进程,将怪物信息保存到ETS_MON中。
4.获取场景坐标mask配置信息并加载,将可移动的坐标写入ETS_CENEN_POSES中

场景管理进程state:

-record(state, {auto_sid, auto_eid}).

其中auto_sid负责生成并返回每一个副本的唯一Id

九宫格广播系统:
当玩家状态发生变化时,需要将玩家状态的改变广播给场景中的其他玩家,(例如玩家进入新场景,需要告知旧场景的人玩家已离开,告知新场景的人玩家出现),而一个完整的场景可能有很多玩家。

为了减少不必要的广播,游戏设计了九宫格系统,将广播区域范围限定在以玩家为中心的九个格子当中。
使用lib_send:send_to_area_scene/4,获取玩家当前坐标九宫格和九宫格内其余玩家,并向他们广播消息

场景对应操作:

1.玩家在场景内走路:走路协议12001,修改玩家#player_status里的x和y坐标,将玩家的新坐标通过lib_scene:move_broadcast/9广播给场景内九宫格玩家
1.获取场景用户,场景怪物,场景NPC:使用ets:match/2进行搜索ETS_ONLINE,ETS_MON,ETS_NPC
2.进入/离开场景:leave_scene(Status)和enter_scene(Status),会通知场景九宫格内玩家
3.进入场景条件检查:check_enter(Status,SceneId)    检查进入副本的条件(等级,道具)
    进入普通场景:enter_normal_scene/2 返回新场景Id,X,Y坐标
    进入副本场景:检查玩家副本服务进程是否存在,不存在则创建,enter_normal_scene/2

玩家进入场景的判断:

%% 进入场景条件检查
check_enter(Status, Id) ->case get_data(Id) of[] ->{false, 0, 0, 0, <<"场景不存在!">>, 0, []};Scene ->case check_requirement(Status, Scene#ets_scene.requirement) of{false, Reason} -> {false, 0, 0, 0, Reason, 0, []};{true} ->case Scene#ets_scene.type of0 -> %% 普通场景enter_normal_scene(Id, Scene, Status);1 -> %% 普通场景enter_normal_scene(Id, Scene, Status);2 -> %% 副本场景case is_pid(Status#player_status.pid_dungeon) andalso is_process_alive(Status#player_status.pid_dungeon) oftrue ->enter_dungeon_scene(Scene, Status); %% 已经有副本服务进程false -> %% 还没有副本服务进程Pid = case is_pid(Status#player_status.pid_team) andalso is_process_alive(Status#player_status.pid_team) offalse -> %% 没有队伍,角色进程创建副本服务器mod_dungeon:start(0, self(), [{Status#player_status.id, Status#player_status.pid}]);true -> %% 有队伍,由队伍进程创建副本服务器mod_team:create_dungeon(Status#player_status.pid_team, self(), [Id, Status#player_status.id, Status#player_status.pid])end,case is_pid(Pid) offalse ->{false, 0, 0, 0, <<"你不是队长不能创建副本!">>, 0, []};true ->enter_dungeon_scene(Scene, Status#player_status{pid_dungeon = Pid})endendendendend.

判断是副本场景还是普通场景,普通场景直接进入,副本场景检查玩家是否拥有副本服务管理进程,将进入副本的操作发送到进程内继续执行

副本:

副本相关模块为mod_dungeon.erl
每一个副本由一个副本进程创建并维护,当玩家请求进入副本的时候,副本服务进程才会被创建

副本服务进程的state:

-record(state, {team_pid = 0,    %% 副本内队伍pidrl = [],           %% 副本服务器所属玩家dsrl = [],         %% 副本场景激活条件 [[SceneId, IsOpen::boolean, Request::Atom, NPCId, RequestNum, FinishNum]]dsl =[]            %% 副本服务器所拥有的场景 [{SceneId, IsOpen::boolean, Tips::string}]
}).

副本基本要素:副本场景激活条件,副本场景,在副本配置中可取,当达成一定的条件后可激活副本的新场景

通过mod_dungeon:start/3创建副本进程,在lib_scene:check_enter/2中,或者mod_team:create_dungeon/3中调用

start(TeamPid, From, RoleList) ->{ok, Pid} = gen_server:start(?MODULE, [TeamPid, RoleList], []),[clear(role, Id) || {Id, _} <- RoleList],[mod_player:set_dungeon(Rpid, Pid) || {_, Rpid} <- RoleList, Rpid =/= From],Pid.

创建完副本进程后,需要修改玩家#player_status.pid_dungeon为当前副本进程pid

一些常用副本操作:

mod_dungeon:join/2,玩家主动加入副本,清除玩家原来副本,副本玩家列表加入新玩家。用于在组队中非队长的玩家加入副本。
mod_dungeon:quit/2,out/2,将玩家踢出副本,调用send_out/1,获取 副本外场景,修改玩家#player_status的坐标,场景Pid,将玩家Id从副本进程玩家id列表中删去
mod_dungeon:clear/2,清除副本进程,调用mod_scene:clear_scene/1,清除场景内容(怪物,NPC,ETS记录),并停止副本进程。
kill_npc/2,副本杀怪,更新state中的dsrl,检查如果达到新的场景开放条件,则更新dsl,激活新的副本场景

玩家进入副本的判断:

handle_call({check_enter, SceneResId}, _From, State) ->   %% 这里的SceneId是数据库的里的场景id,不是唯一idcase lists:keyfind(SceneResId, 4, State#state.dsl) offalse ->{reply, {false, <<"没有这个副本场景">>}, State};   %%没有这个副本场景DS ->case DS#ds.enable offalse ->{reply, {false, DS#ds.tip}, State};    %%还没被激活true ->{SceneId, NewState} =case DS#ds.id =/= 0 oftrue -> {DS#ds.id, State};   %%场景已经加载过false -> create_scene(SceneResId, State)end,{reply, {true, SceneId}, NewState}endend;

0.根据#ets_scene.type判断是否副本场景。
1.检查玩家是否有副本服务进程pid_dungeon,如果没有则通过mod_dungeon:start/3创建。
2.检查该副本服务进程场景列表里是否有该副本场景,没有则返回失败。
3.副本服务进程场景列表有该场景,判断场景是否激活。
4.检查场景是否加载过,没加载过的话调用mod_scene:copy_scene/2复制一个副本场景,取代场景列表中原来的场景。此时场景的“唯一ID”代替了场景的资源id,同样被插入到?ETS_SCENE表中

将玩家传出副本:

send_out(R) when is_record(R, ets_online) ->case get_dungeon_id(lib_scene:get_res_id(R#ets_online.scene)) of0 -> scene_not_exist;  %% 不在副本场景Did -> %% 将传送出副本DD = data_dungeon:get(Did),[Sid, X, Y] = DD#dungeon.out,Player = gen_server:call(R#ets_online.pid, 'PLAYER'),lib_scene:leave_scene(Player),Player1 = Player#player_status{pid_dungeon = none, scene = Sid, x = X, y = Y},gen_server:cast(R#ets_online.pid, {'SET_PLAYER', Player1}),{ok, BinData} = pt_12:write(12005, [Sid, X, Y, <<>>, Sid]),lib_send:send_one(Player1#player_status.socket, BinData)end.

获取副本外场景Id, 修改#player_status的场景Id和坐标并回写玩家进程,并广播玩家切换场景的消息

英雄远征Erlang源码分析(7)-场景与副本相关推荐

  1. 英雄远征Erlang源码分析(1)-源码结构解析

    偶然得到了一份英雄远征的Erlang服务端源代码,想着通过对源代码的分析,来熟悉使用Erlang编程语言的游戏服务器的设计,游戏中关键逻辑的实现. 解压压缩文件后,在Idea内导入文件夹创建相关工程, ...

  2. 英雄远征Erlang源码分析(13)-总结 附上可执行的服务端和客户端代码

    总结:其实也没有什么好总结的......英雄远征这套源码虽然说体积并不大,麻雀虽小五脏俱全,对于MMORPG网游的一些基本系统都有完备的实现,虽然实现方法不一定是最好的.除去场景,战斗,组队,任务等那 ...

  3. 英雄远征Erlang源码分析(2)-网关服务器的启动

    上一篇文章解析了游戏源码的结构,我们知道该源码包含两个服务器的启动脚本:网关服务器和游戏服务器,其中网关服务器用于在玩家选择进入游戏服务器之前获取服务器列表,游戏服务器则处理玩家进入游戏服务器后的登录 ...

  4. 英雄远征Erlang源码分析(3)-游戏服务器的启动

    上一篇文件介绍了网关服务器的启动,其功能主要用于给客户端返回可选的游戏服务器列表,让客户端去连接.其实有些游戏的网关部分使用的是PHP搭建,代码的维护和Erlang是分开的.现在让我们来看游戏服务器的 ...

  5. 英雄远征Erlang源码分析(12)-任务模块解析

    与玩家任务相关的模块有:lib_task.erl(玩家任务相关操作),mod_task.erl(定时回写任务数据) 玩家任务的初始化: 在登录的时候调用lib_task:flush_role_task ...

  6. 英雄远征Erlang源码分析(5)-协议解析与玩家登录处理

    现在,客户端与服务器的连接算是正式建立了,此时用户需要做的第一件事就是登陆.不过在登录之前,我们要先研究下前后端通信的协议. 客户端与服务端建立连接后,通过提前制定好的协议进行交互.具体的协议文档在d ...

  7. 英雄远征Erlang源码分析(10)-队伍相关

    组队相关模块有mod_team.erl和lib_team.erl 通过24000协议请求,调用mod_team:start(Uid,Pid,Nick,TeamName)开启组队进程,创建队伍 组队st ...

  8. 英雄远征Erlang源码分析(6)-玩家进程初始化和玩家模块相关方法

    客户端发送登录请求后,服务器检查玩家登录需求,创建玩家进程以及进行相关的初始化工作,只有这些做完后,客户端操控的角色才算是和服务器的玩家进程建立了关联. 创建玩家进程调用mod_player:star ...

  9. 英雄远征Erlang源码分析(9)-战斗流程解析

    和战斗相关的模块有mod_battle.erl 当玩家进程和怪物进程被创建的时候都会通过mod_battle:start_link()创建一个战斗进程. 该战斗进程的state,用于保存玩家上次出手或 ...

最新文章

  1. 北京大兴要打造成未来科技新中心?
  2. http304缓存 php,通过http头设置http缓存
  3. C言语for轮回语句
  4. OpenCASCADE可视化:3D演示之图形基元
  5. SAP 几款容易令初学者混淆的 HANA 解决方案
  6. mysql主从同步面试题_面试被问MySQL 主从复制,怎么破?
  7. 项目实战-药品采购系统-day01
  8. 设计模式—工厂模式(思维导图)
  9. java me手机版,一个经典的 JAVA ME 手机程序入门级源码
  10. python实现链表的删除_删除链表中的元素,但是只能使用一个指针
  11. python实现485通讯_Python编程实现USB转RS485串口通信
  12. entity framework 动态条件
  13. 一文读懂「云上企业级存储」
  14. 自动驾驶 10-3: 全球导航卫星系统 (GNSS)The Global Navigation Satellite Systems
  15. 常用easyUI -icon 图标
  16. 通信原理教程chapter1
  17. Ubuntu解决火狐浏览器无法同步书签的问题
  18. P2921 [USACO08DEC]在农场万圣节
  19. JS基础到结束知识详细大汇总
  20. 跨域问题No ‘Access-Control-Allow-Origin‘ header is present on the requested resource.

热门文章

  1. 第二讲 系统研究手段
  2. 计算机毕业设计Java智能外包管理平台(源码+系统+mysql数据库+Lw文档)
  3. 数据库基础SQL知识面试题一
  4. 双离合档把上按钮作用_关于大众DSG双离合变速器,你不知道一些干货
  5. mac启动选项找不到linux,Mac升级10.10后开机引导不见了,无法进入Linux
  6. Unity基础(3)—— unity中的各种坐标系
  7. 一文解决MySQL突击面试,关键知识点总结
  8. UE4安装错误解决:MSB3644 framework “.NETFramework,Version=v4.6.2” were not found
  9. Fire Game FZU - 2150 (水搜索)
  10. java p12 ssl_从 p12 格式 SSL 证书解出 pem 格式公钥私钥给 Postman 使用