第十章:L2JMobius学习 – 进入游戏世界

在上一章节中,我们在服务器端已经实例化好了Player对象,接下来客户端就会向服务器端发送EnterWorld数据包,我们来看一下这个EnterWorld数据包类的run方法。这个方法中涉及的内容非常的多,所以,我们只简单介绍一些重点内容,如下所示

final Player player = client.getPlayer();
client.setConnectionState(ConnectionState.IN_GAME);

从我们的GameClient中获取Player对象,设置玩家角色为“进入游戏”状态。接下来,仍然是一个玩家角色昵称的检查,如果使用中文的话,这里会报错,需要修改一下。

// 设置玩家角色已上线
player.setOnlineStatus(true);
// 设置玩家角色为“跑”状态,发送ChangeMoveType数据包
player.setRunning();
// 设置玩家角色为“站立”状态,播放 idle 动画
player.standUp();
// 发送UserInfo数据包
player.broadcastKarma();

这里我们重点注意一下UserInfo数据包,该数据包中包含了玩家角色的大部分信息。包括角色的坐标,昵称,种族,职业,肖像特征,等级,身体属性,经验值,身体上装备,还有一些其他的血盟等等信息。接下来是一个GM的设置,

// 设置GM
enterGM(player);

什么条件决定游戏角色是GM呢?在存储角色的characters表中有一个“accesslevel”字段值。这个字段值在角色创建的时候,设置为0。角色是否GM是由该值来决定的。在工程的“config”目录下,有一个“AccessLevels.xml”的配置文件,打开之后,我们就会发现,里面定义了很多的“身份”,不同的“level”值对应不同的“身份”。默认情况下,当level > 50的时候,该“身份”就是GM了。

<access level="100" name="Master Access" 	isGm="true" />
<access level="90" name="Head GM" 			isGm="true" />
<access level="80" name="Event GM" 		    isGm="true" />
<access level="70" name="Support GM" 		isGm="true" />
<access level="60" name="General GM" 		isGm="true" />
<access level="50" name="Test GM" 			isGm="false" />
<access level="0" name="User" 				isGm="false" />

上面的“isGm”代表该身份是不是一个GM权限,true代表是,false代表不是。不同身份的GM拥有的命令是不一样的。我们可以查看“AdminCommands.xml”文件就知道了。例如,我们可以使用“admin_create_item”来制作任意的物品(装备)。接下来,比较重要的方法,就是“孵化”自己。

// 孵化自己,设置坐标位置,设置瓦片地图
player.spawnMe(player.getX(), player.getY(), player.getZ());

这个方法实际是Player对象的父类WorldObject里面的方法。这个WorldObject类是一个抽象类,它代表了游戏世界的“物体”,类似于Unity中的GameObject。它的子类可以是玩家,NPC,甚至是物品(装备)。这个类还是比较简单的,里面的成员变量如下

// 是否孵化
private boolean _isSpawned;
// 周围的WorldObject列表
private WorldObjectKnownList _knownList;
// 游戏对象名称
private String _name = "";
// 游戏对象ID
private int _objectId;
// 所在的瓦片地图
private WorldRegion _worldRegion;
// 位置信息(3D坐标)
private final Location _location = new Location(0, 0, -10000);

接下来,我们继续回到他的spawnMe方法中。

_location.setXYZ(spawnX, spawnY, z);
setWorldRegion(World.getInstance().getRegion(getLocation()));

上面的代码就是设置位置坐标和瓦片地图。什么是瓦片地图?这是游戏开发中经常使用的一种地图方式。对于非常大的地图而言,如果直接创建整体模型的话,加载的时候,就非常的消耗电脑性能资源。因此,我们将其拆分成一个一个的小地图,就是铺设房顶瓦片似的,最终组成一个完整的大地图。玩家角色需要知道自己处于哪个瓦片地图中,所以就有了上面方法。这个瓦片地图是一个WorldRegion类。这个WorldRegion类里面除了包含位置信息之外,还需要保存与周边瓦片地图的信息,这个周边瓦片地图就包含上下左右,左右斜上,左右斜下共计八个方向的瓦片地图信息。还有很多信息也要在WorldRegion类中保存,比如该瓦片地图中的游戏对象(Set<WorldObject> _visibleObjects)列表等等。那么,这些WorldRegion瓦片地图类是如何实例化的呢?这里需要介绍一个游戏世界World类。这个类从概念上理解,它是非常大的,它代表了整个游戏世界。他是一个单例,因为游戏世界只有一个。这个游戏世界World类除了初始化所有的瓦片地图之外,还包括所有的游戏对象Map<Integer, WorldObject> _allObjects集合。为了能够快速处理玩家角色,还会单独存储Map<String, Player> _allPlayers玩家角色集合。最后,我们再总结一下。

游戏世界World类是最高级的类,它代表了游戏世界。里面包含所有的瓦片地图WorldRegion类,同时还存储着所有的游戏对象allObjects集合以及玩家角色allPlayers集合。

在瓦片地图WorldRegion类中,除了自己的位置信息之后,还包含了周围瓦片地图的信息,同时还存储着当前瓦片地图中的所有游戏对象visibleObjects集合。

游戏对象WorldObject类,是所有玩家角色,NPC,以及物品(装备)的父类,里面包含了位置信息和瓦片地图信息,还有周围游戏对象knownList /_knownObjects集合。

接下来,我们继续回到他的spawnMe方法中,还得继续介绍spawnMe方法,

// 将当前游戏对象放入到游戏世界World的_allobjects集合中
World.getInstance().storeObject(this);

// 将当前游戏对象放入到瓦片地图WorldRegion的_visibleObjects集合中
final WorldRegion region = getWorldRegion();
region.addVisibleObject(this);

// 查询当前角色周围的游戏对象,放入到 _knownList/_knownObjects 集合中
World.getInstance().addVisibleObject(this, region, null);

// 调用Creature类的onSpawn方法
onSpawn();

上面的代码注释已经非常清除了,剩下的内容,我们就不再详细介绍了。最后,我们说明一下Player对象的继承关系。Player对象的父类是Playable抽象类,而Playable抽象类的父类是Creature抽象类,而Creature抽象类的父类就是WorldObject抽象类,如下所示:

Player -> Playable -> Creature -> WorldObject

这里要注意的是Creature类,他是一个基础角色类,它不仅仅为Player服务,还可以为NPC类服务。所以Creature类中的很多方法是共享于玩家和NPC的。比如说角色移动。接下来,我们来说明一下Player对象实例化过程,这里面涉及到几个重要类。首先是Player类的构造方法,代码如下:

super(objectId, template);
getKnownList();
getStat();
getStatus();
_ai = new PlayerAI(new AIAccessor());

除了第一行代码调用父类的构造方法之外,剩余的四行代码都是对应了四个重要的类。

PlayerKnownList –> PlayableKnownList –> CreatureKnownList -> WorldObjectKnownList

上面是PlayerKnownList类的继承关系,主要就是存储当前角色附近的游戏对象。区别在于不同的角色在添加周围游戏对象的时候,会增加一些额外的操作,也不是单纯的将这些游戏对象放入到集合列表中。后期我们讲解具体使用的时候,就知道了。

PlayerStat -> PlayableStat -> CreatureStat

上面是PlayerStat类的继承关系,主要处理玩家角色经验值(exp),技能点(sp)和等级(level),里面涉及到很多的算法。

PlayerStatus -> PlayableStatus -> CreatureStatus

上面是PlayerStatus 类的继承关系,主要处理玩家的Hp,Mp,Cp

PlayerAI -> CreatureAI -> AbstractAI -> Ctrl

上面是PlayerAI 类的继承关系,主要处理玩家的自动化(可以理解为状态机),比如自动攻击等等。我们可以发现,上面三种类的继承关系是非常相似的,与玩家Player的继承关系一致。

Player -> Playable -> Creature -> WorldObject

这里面非常重要的是“Player”和“Creature”两个类。Player代表的是玩家角色,而Creature代表的就是普通角色(包括玩家或NPC)。后面讲解npc的时候,大家就会明白,从“Creature”类开始,就会出现非玩家类“Npc”,“ NpcStat”,“ NpcStatus”以及“NpcWalkerAI”等类。本章节,我们就简单到这里,下一个章节我们介绍角色如何移动。这个就涉及到PlayerAI了。

本章节涉及的内容均已上传百度网盘:

https://pan.baidu.com/s/1XdlcCFPvXnzfwFoVK7Sn7Q?pwd=avd4

欢迎加企鹅交流裙:874700842(裙文件里面也可以下载所有内容)。

猜你喜欢

转载自blog.csdn.net/konkon2012/article/details/131659766