像写诗一样制作可交互模型
本项目名为SK Model
Workspace(模型工作空间),旨在通过简单的方式,创建可交互,可复用的模型。同时具有丰富的接口和较强的可拓展性
由于作者一直在咕咕咕,导致该项目有很多坑没有填,如果你正在香草图书馆浏览本页面,可以点这里访问该文章的原始页面,文章将在原始页面继续保持更新,之后会添加更多有用的功能
之后我也会制作一些基于该数据包的原版家具,(然而我并不会建模,所以做的不怎么好)
- 运作方式:原版游戏,数据包
- 支持版本:1.21.8
本文将详细介绍该数据包的功能,并且提供一些案例教程方便读者理解
数据包下载
依赖关系
- (数据包) SK Model Workspace
- (前置数据包) SK API
什么是“可交互模型”呢
玩家对模型进行一定操作,模型对操作进行反馈,具备这种特征的模型可以称作“可交互模型”,比方说有一个椅子,玩家左键点击即可将其破坏,右键点击可以坐到上面。其中“左键点击”和“右键点击”即为操作,“破坏”和“坐”即为反馈,此时,这个椅子就是一个“可交互模型”
展示实体与交互实体在1.19.4版本被加入,为原版开发者们提供了诸多便利,也为找到“可交互模型”的简单实现方式带来了可能性,这一领域目前已有许多优秀的作品:
该如何实现呢
在SK Model Workspace中,每一个可交互模型都由一个Marker,一个或多个展示实体与交互实体组成,其中交互实体用于接收玩家的操作,然后Marker将作为执行者执行事先设定好的事件,最终展示实体给予一定反馈
交互实体接收到玩家的操作以后,需要告诉Marker让其作为执行者,但是如何让交互实体找到Marker呢,一种方法是让交互实体作为Marker的乘客,交互实体可以使用execute on vehicle找到Marker,但是这样做很显然有一个问题:假如有不止一个展示实体,让它们都作为Marker的乘客,那么这些展示实体就无法分别设定自己的坐标。显然这是不合适的
另外一种方法是,在模型被创建的之时,将展示实体和Marker的UUID存入storage中,展示实体只需要查找表即可找到Marker
现在Marker成为了执行者,它可以操作所有的交互实体,这又该如何实现呢?其实也不难,我们将一个模型中所有的展示实体与交互实体称为该模型的元素(element),为每一个元素设定一个不重复的元素ID,然后在Marker中存储所有元素的元素ID和UUID,Marker可以通过给定元素ID来查找该元素的UUID,从而对该元素执行操作
sequenceDiagram 玩家->>交互实体: 玩家点击交互实体 交互实体->>Marker: 查找表,找到Marker的UUID Marker-->>展示实体1: 通过元素ID查找UUID Marker-->>展示实体2: 通过元素ID查找UUID
此外,SK Model Workspace还支持给模型配置方块,模型本身是没有碰撞体积的。可以配置屏障方块来给模型添加碰撞体积。也可以配置光源方块,让模型发光。 同时为了不影响世界中已经存在的方块,在模型被创建时,会检查目标位置的方块是否为空气,如果是,配置好的方块才会被放置
定义模型
秉持可复用原则,需要先定义模型,再创建模型
定义模型
1 | data modify storage skmws reg.model.<模型ID> set value <模型数据> |
<模型ID> 模型的ID,这是唯一的
<模型数据> 一个包含该模型所有数据的复合标签,格式如下
(根标签)
elements 元素列表
(一个元素)
type 实体类型,可选值为item_display,text_display,block_display,interaction
当type:"interaction"时id 元素ID,元素列表中所有元素的ID不能重复
events 事件列表
merge (可选)合并数据
position (可选)相对位置
当type:"item_display"或type:"text_display"或type:"block_display"时id 元素ID,元素列表中所有元素的ID不得重复
merge (可选)合并数据
position (可选)相对位置
rotation (可选)相对旋转角
marker (可选)标记实体配置
merge 合并数据
blocks (可选)方块列表
(一个方块)
position 方块的相对位置
block 方块ID
events 事件列表
align_position (可选)对齐坐标,若无该项则不进行坐标对齐,列表中有三个数,对应XYZ三轴,实际坐标为不大于当前坐标且能被该值整除的最大数字,填入-1则表示不对该轴坐标进行对齐,示例:[1,-1,1]表示XZ轴对齐方块网格,Y轴不进行对齐
align_rotation (可选)约束偏航角,若无该项则不进行偏航角约束,示例:输入90代表实际偏航角被约束至东南西北四个方向之一,输入45代表实际偏航角被约束至八个基本方向
lock_rotation (可选)锁定旋转角,让旋转角恒为指定值,如果该项与align_rotation同时存在,则优先使用该项
事件列表
事件列表的执行者为该模型的Marker
可以使用@a
[tag=skmws.s]来指定正在执行交互操作的玩家
(一个事件列表)
(一个事件)
type 事件类型
当type:"remove"时,移除该模型,同时触发on_remove事件列表(无需提供额外参数)
当type:"destroy"时,破坏该模型,同时触发on_remove事件列表sound 破坏音效
particle 破坏粒子
当type:"cooldown"时,设置交互冷却时间,在该时间段内模型不接受任何操作time 冷却时间
当type:"sit"时,让执行交互操作的玩家坐在该模型上id 元素ID,指定让玩家坐到哪个元素上
当type:"call"时,执行另一事件列表,完毕后接着执行当前事件列表event 事件列表ID
当type:"cmd"时,执行指定命令cmd 要执行的命令
当type:"execute"时,让指定元素执行指定命令id 元素ID,作为执行者
cmd 执行的命令
当type:"merge"时,合并数据至指定元素id 元素ID
data 合并数据
当type:"element_append"时,添加元素id 元素ID
data (一个元素)
当type:"element_remove"时,移除元素id 元素ID
当type:"block_append"时,添加方块position 放置位置
block 方块ID
当type:"block_remove"时,移除方块position 要被移除的方块的位置
此外这些事件还有其对应的函数,{大括号里是函数的参数呢}
可以在type:"cmd"事件执行的命令中使用这些函数,不能在其他的上下文中使用
skmws:event/remove
skmws:event/destroy {sound, particle}
skmws:event/cooldown {time}
skmws:event/sit {id}
skmws:event/call {event}
skmws:event/cmd {cmd}
skmws:event/execute {id, cmd}
skmws:event/merge {id, data}
skmws:event/element_append {id, data}
skmws:event/element_remove {id}
skmws:event/block_append {position, block}
skmws:event/block_remove {position}
创建模型
模型被定义后,需要“创建”才能将模型安放在世界中
创建模型1 | function skmws:construct |
执行位置 模型的创建位置
执行旋转角 模型创建时的旋转角
参数
(根标签)
id 模型ID
例程:装饰模型
装饰模型,就是字面意思,用于装饰的模型,需要实现的功能也很简单,如下
- 展示自定义模型(使用物品展示实体)
- 模型可交互(使用交互实体)
- 左键点击时破坏模型(在leftclick事件列表中加入destroy事件)
- 让模型对齐方块网格(添加align_position参数)
- 让模型偏航角约束至八个基本方向(添加align_rotation参数)
- 拥有一格高的碰撞箱(配置屏障方块)
定义如下
1 | data modify storage skmws reg.model.test01 set value |
使用如下命令创建该模型 1
/function skmws:construct {id:"test01"}
技术细节
- 交互实体的宽和高设定为1.01(比1稍大),为了防止玩家点到屏障
- 自定义模型需要用到custom_model_data组件
效果展示
例程:更丝滑的门
原版MC的门没有开关门动画,现在来制作一个带有开关门动画的门
- 使用方块展示实体显示门的模型,门分为上下两部分,所以需要用两个方块展示实体
- 展示实体插值实现门的开关动画
- 左键点击门即可破坏
- 右键点击门可以打开或关闭门
- 门关闭时,放置屏障阻止玩家通行;门打开时,移除屏障
定义如下
1 | data modify storage skmws reg.model.door set value |
在刚才的定义中,Marker中存储了一项名为door的数据,这个表示门的开关状态,0表示关闭,1表示打开
然后还需要单独写一个函数来处理右键点击时开关门动作
这些功能并不由SK Model
Workspace数据包提供,而是在实际开发中按照需求自行编写,这体现了该数据包的可拓展性
1 | # skm:door/_update |
1 | # skm:door/_open |
1 | # skm:door/_close |
效果展示