# MC的对象化

插件开发角度下的MC世界, 正是一个由许多对象构成的世界.
下面简述几个常用的类型. BukkitAPI中还有更多的类型. 这只是冰山一角.

# 玩家(Player)

服务器内的每一个玩家都有一个Player对象.
如果你想获取这样的一个对象, 例如, 玩家PlayerName的Player对象, 你可以这样获取:

Bukkit.getPlayerExact("PlayerName");  

如果你翻看API, 你会发现存在Bukkit.getPlayer("PlayerName");这样的方法来获取.
但是这个方法会“模糊地”获取玩家.
假如服务器内有abc和ab这两个玩家, 如果你想获取ab的Player对象, 万一ab不在线, 你Bukkit.getPlayer("ab");返回的Player对象, 很有可能是abc的, 而不是ab的.

PlayerEntity的子类. 这意味着Player对象是一个确切的实体,那么如果玩家下线,它在服务器内对应的实体便消失,也就不存在所谓的Player对象了.
因此,对一个下线的玩家使用getPlayergetPlayerExact方法获取Player对象是无意义的. 这会得到null.
如果要表示一个确切的玩家,无论其是否在线,可以使用OfflinePlayer对象.
Bukkit中给出了getOfflinePlayer方法,你可以通过该方法获得一个OfflinePlayer对象.

# 实体(Entity)

在MC中, 所有的生物, 例如一只羊, 乃至一个僵尸, 又或者是玩家, 都是生物, 他们都是Entity类型的对象.
这个概念还可以更加进一步的扩充, 一个被点燃的TNT, 实际上, 它也是一个实体(TNTPrimed).

# Material、ItemStack

提醒: Bukkit 1.13中, Material枚举发生了翻天覆地的改动, 导致以前的插件无法更好兼容1.13, 以前的教程可能与实际环境有出入.

在Bukkit当中, 存在一个Enum(枚举)类型的Material, Material中含有各种物品和方块的种类.
值得注意的是, 某些物品的Material和该物品放置后的方块的Material不同.

例如, MC中一个苹果的种类是Material.APPLE; 石头方块的种类是Material.STONE;

特殊的是, 某些物品与其对应的方块Material不一致, 例如红石比较器.
红石比较器物品的种类是Material.REDSTONE_COMPARATOR, 而放置后的方块种类又分为Material.REDSTONE_COMPARATOR_ON(开启状态), Material.REDSTONE_COMPARATOR_OFF(关闭状态)两种, 红石比较器方块的种类不能用Material.REDSTONE_COMPARATOR来表示.

ItemStack用于反应一种描述物品堆叠的方式.
一个ItemStack的实例, 囊括了物品的种类(其对应的Material)和数量等信息.
例如, 玩家手中拿着三个苹果. 玩家手中的这三个苹果, 实质上是一个ItemStack, 它包括了这三个苹果的种类(Material.APPLE)、数量(3)与其他的一些信息.

下面的例子中我们创建了几个ItemStack:

ItemStack item1 = new ItemStack(Material.DIAMOND); //一个钻石
ItemStack item2 = new ItemStack(Material.DIAMOND,2); //两个钻石
ItemStack item3 = new ItemStack(Material.WOOL,1); //一个羊毛(白色的)
ItemStack item4 = new ItemStack(Material.WOOL,1,15); //一个羊毛(黑色的)

item3.setDurability(0); //设置item3的耐久度为0, 对于羊毛、木板等就是设置颜色的 "子ID", 对于钻石剑那就是耐久度了.
item1.setAmount(20); //设置item1数量为20
Material m = item1.getType(); //得到item1的类型, 也就是Material.DIAMOND

Lore是物品的描述信息, 请看下图:

该物品的Lore为:

§8材质: §b铜锌合金
§8电荷量: §b10.0J

我们来给item1也设置这样的Lore.

ItemMeta im1 = item1.hasItemMeta()?item1.getItemMeta():Bukkit.getItemFactory().getItemMeta(item1.getType());
List<String> loreList = new ArrayList<>();
loreList.add("§8材质: §b铜锌合金");
loreList.add("§8电荷量: §b10.0J");
im1.setLore(loreList); //设置lore
// 这样设置名称: im1.setDisplayName("蓄电池 - I");
item1.setItemMeta(im1);

//下面这个是错误示范, 必须把im1 get得到后再set回去, 否则诸如下面这样无法生效
item1.getItemMeta().setLore(loreList);

# Location

任何一个坐标都可由一个Location代表.

常见的实体对象是Entity的子类,故都提供了getLocation方法,返回的Location代表着它们的坐标位置.
值得一提的是,如果应用getLocation获取实体位置,那么获取的位置是它的脚. 例如Player.getLocation()所获取的是玩家的脚的位置.
对于这些实体对象,如果想修改他们所在的坐标位置,Bukkit没有提供setLocation方法,而是提供了teleport方法. 通过teleport方法可以传送某个实体.

Location对象的基本使用也十分简单.

BlockLocation
Location中提供了getBlockLocation()getBlockX()getBlockY()getBlockZ()四个方法.
对于一个方块而言,其坐标的XYZ值均为整数,所以这些方法所获取的是此Location对应的最精确方块的坐标.
通俗的理解,可以认为获取的是将XYZ四舍五入后的坐标值.
getBlock()获取的此Location对应的最精确的方块的Block对象.

坐标运算
Location提供add(加)、subtract(减)方法.

两点间距离 Location提供distance方法,参数为另一个Location,返回值为double,代表两点间距离.
Location还提供distanceSquared方法,代表两点间的方块距离,遵循四舍五入.

# Block

任何一个方块都可以用Block对象表示.

在BukkitAPI中,任一世界里有且仅有一个方块对应着三个确切的XYZ值. 也就是一个坐标(XYZ都是整数)只对应一个方块.

getTypesetType方法可以设置方块的类型.
在1.9之前的版本还令设getTypeIdsetTypeId方法. 随着Mojang官方废弃物品ID,BukkitAPI也废弃了这两个方法,并最终删除.

# World

在Minecraft中,每一个世界都是一个World对象. 一个World以若干Chunk(区块)组成.

# Inventory

对于玩家背包、箱子里存放的所有ItemStack对象, 我们可以认为他们都储存在了一个Inventory对象里.
也就是意味着, 一个箱子、一个玩家都对应他们专属的Inventory对象, 用来储存它们存放着的物品.

我们先以操作玩家背包为例, 练习Inventory的使用.

//假设p是一个Player对象的变量
Inventory inv = p.getInventory();
inv.addItem(new ItemStack(Material.DIAMOND));

这样就给玩家背包里加了一个钻石, 如果玩家背包已满, 什么都不会发生.

每一个Inventory是一个储存物品GUI的体现, 每一个储存物品的格子都有自己的编号(index). 以玩家背包为例:

这也就意味着:

inv.setItem(36,new ItemStack(Material.DIAMOND));

就是在玩家物品栏第一个位置放置了一个钻石, 或者代表原来在该处放置的物品被删除且替换成了一个钻石.

还有这些常用操作:

inv.setItem(36,null); //把36号格子清空
inv.removeItem(item1); //删除item1
if(inv.contains(item1)){ //判断inv里有没有item1
	System.out.println("lala");
}
inv.clear(); //清空整个inventory
// 这样也就意味着:
// p.getInventory().clear(); 是清空玩家背包