# 代码编写基础

本节将主要介绍接口和事件之间的区别、文档的查阅以及客户端和服务端之间的通信。

# 接口与事件

这里的接口与事件和逻辑编辑器的接口和事件定义一致,如果有遗忘的同学可以去回顾

那么在逻辑编辑器中,所有事件,都体现为监听:xxxxx,而在零件开发中,监听事件一般都是定义一个函数。

如果需要查阅所有可以监听的事件,可以在这里进行查询。

例如在PartBase中定义以下函数,就视为监听了ServerChatEvent

def ServerChatEvent(self, args):
    pass

而在零件开发中调用接口,都是以调用函数的形式来调用的。

还是这个事件,GetParentSetEntityAttrValueSetEntityAttrMaxValue,都是调用了PartBase的接口。

所有可以使用的接口都可以在这里进行查询。

    def ServerChatEvent(self, args):
        if args["message"] != "更新血量":
            return
        parent = self.GetParent()
        entityId = parent.GetEntityId()
        if args["playerId"] != entityId:
            return
        self.SetEntityAttrValue(entityId, AttrType.HEALTH, self.health)
        self.SetEntityAttrMaxValue(entityId, AttrType.HEALTH, self.maxHealth)

为什么PartBase中没有找到上方代码使用的GetParent?

因为我们是基于面向对象技术,进行预设和零件开发的。PartBase继承了SdkInterface和TransformObject对象,自然就可以调用来自父类的函数。

实际上我们调用的GetParent接口,是来自TransformObject的。这样GetParent,因为零件挂载玩家预设上,就能得到玩家。

# 服务器客户端通信

在之前的逻辑编辑器界面制作中,我们就已经稍微接触了一些服务器和客户端之间的通信。

当时的界面,将客户端中的内容,发送到了服务端。服务端监听,并执行命令。

这样就是一个客户端往服务端的通信。但是实际上,客户端和服务端之间,是可以双向通信的。下面将会详细介绍通信的使用方法。

# 客户端->服务端

客户端向服务端的通信主要需要在客户端调用NotifyToServer接口,而服务端需要在初始化的时候调用ListenSelfEvent接口,来监听这个事件。

# NotifyToServer

文档说明:点我

参数名 数据类型 说明
eventName str 事件名称
eventData object 事件数据

这是一个客户端接口。eventName是事件名称。事件名称可以理解为这个事件的具体含义。客户端中使用这个事件名称发送到服务端,那么服务端相应的也需要使用这个事件名称来监听。eventData是事件的具体数据,即一般事件中的args,一般传入一个字典。

# ListenSelfEvent

文档说明:点我

参数名 数据类型 说明
eventName str 事件名称
target object 目标
func object 回调函数

这个接口,双端通用。

eventName就是我们NotifyToServer时所定义的事件名称。target为监听哪个对象的事件,一般填写self。func为回调函数,定义一个函数,参数为args,然后在这里传入function类型的值。

例子:

class TestPartPart(PartBase):
    def InitServer(self):
        print "InitServer"
        self.ListenSelfEvent("TestEvent", self, self.OnTestEvent)

    def OnTestEvent(self, args):
        print "收到来自 {} 的客户端事件".format(args["playerId"])

    def SendToServer(self):
        self.NotifyToServer("TestEvent", {"playerId": self.GetLocalPlayerId()})

这样如果在客户端调用SendToServer函数,服务端就会相应的收到事件,并打印消息。

# 服务端->客户端

服务端向客户端的通信主要需要在服务端调用NotifyToClient接口,而客户端需要在初始化的时候调用ListenSelfEvent接口,来监听这个事件。

# NotifyToClient

文档说明:点我

参数名 数据类型 说明
playerId str 玩家ID
eventName str 事件名称
eventData object 事件数据

这是一个服务端接口。playerId为需要发送到的玩家id。其他的参数和NotifyToServer用法一致。

如果需要广播到所有玩家的客户端,可以使用BroadcastToAllClient

# ListenSelfEvent

ListenSelfEvent和客户端向服务端通信中的使用方法一致。

文档说明:点我

参数名 数据类型 说明
eventName str 事件名称
target object 目标
func object 回调函数

这个接口,双端通用。

eventName就是我们NotifyToServer时所定义的事件名称。target为监听哪个对象的事件,一般填写self。func为回调函数,定义一个函数,参数为args,然后在这里传入function类型的值。

例子:

class TestPartPart(PartBase):
    def InitClient(self):
        print "InitClient"
        self.ListenSelfEvent("TestEvent", self, self.OnTestEvent)

    def OnTestEvent(self, args):
        print "收到来自服务端的事件 {}".format(args)

    def SendToServer(self,playerId):
        self.NotifyToClient(playerId, "TestEvent", {"msg": "test"})

这样如果在客户端调用SendToServer函数,服务端就会相应的收到事件,并打印消息。

# 课后作业

# 辨认接口和事件

辨别下方的代码是调用接口还是监听事件

  1. self.SetCommand()
  2. def OnCommandOutputServerEvent(self, args):

答案

  1. 调用接口
  2. 监听事件

# 零件开发实际操作

使用零件开发来编写一个爆炸箭的功能:所有射出的弓箭,在击中目标时都会产生爆炸。

并且使用自定义属性面板,设置爆炸范围。

# 操作步骤

  1. 创建一个玩家预设、空零件。零件命名为ExplosionArrow
  2. 将零件挂接到玩家预设上。
  3. 接下来使用PyCharm打开ExplosionArrowPart.py,编辑代码。
  4. __init__下定义一个爆炸半径成员变量,方便后面制作自定义属性。
  5. 监听ProjectileDoHitEffectEvent,并获取对应位置,再调用CreateExplosion,创建爆炸,其中爆炸半径使用成员变量,随后删除箭的实体。

代码参考:

@registerGenericClass("ExplosionArrowPart")
class ExplosionArrowPart(PartBase):
    def __init__(self):
        PartBase.__init__(self)
        self.explosionRadius = 5
        self.name = "爆炸弓零件"

    def ProjectileDoHitEffectEvent(self, args):
        self.CreateExplosion((args["x"], args["y"], args["z"]), self.explosionRadius, True, True, args["srcId"], args["srcId"])
        self.DestroyEntity(args["id"])

接下来设置元数据,打开ExplosionArrowPartMeta.py,修改PROPERTIES,添加一个爆炸半径变量。

代码参考:

@sunshine_class_meta
class ExplosionArrowPartMeta(PartBaseMeta):
    CLASS_NAME = "ExplosionArrowPart"
    PROPERTIES = {
        "explosionRadius": PInt(text="爆炸半径", sort=1000, default=5, group="爆炸箭头")
    }

这样就修改完成。打开编辑器,选中爆炸弓零件,就可以看到相应的设置。

# 通信

利用客户端和服务端的通信系统,将玩家聊天的内容发送到客户端,并打印到日志窗口中。

# 操作步骤

  1. 新建一个空零件,命名为NotifyTest,并挂接在玩家预设上。
  2. 编辑NotifyTestPart.py,监听ServerChatEvent,获取消息内容和玩家ID,使用NotifyToClient将其发送给客户端,事件名为ChatToClient,数据是一个dict{"msg": 消息内容}
  3. 在InitClient中调用ListenSelfEvent,监听ChatToClient事件,再定义一个函数叫做OnRecvChat,接收args作为参数,打印args["msg"],作为回调函数。

代码参考:

@registerGenericClass("NotifyTestPart")
class NotifyTestPart(PartBase):
    def __init__(self):
        PartBase.__init__(self)
        self.name = "通信测试"

    def InitClient(self):
        self.ListenSelfEvent("ChatToClient", self, self.OnRecvChat)

    def OnRecvChat(self, args):
        print "从服务端发来的聊天信息: {}".format(args["msg"])

    def ServerChatEvent(self, args):
        message = args["message"]
        playerId = args["playerId"]
        self.NotifyToClient(playerId, "ChatToClient", {"msg": message})

这样每次发消息,在日志窗口都会由客户端打印收到的消息内容。

进阶

20分钟

接口与事件

服务器客户端通信

客户端->服务端

服务端->客户端

课后作业

辨认接口和事件

零件开发实际操作

通信