创造你的世界
与物体互动
与物体互动
15 分
实现一个交互按钮,当zepeto角色接近一个物体时出现。 步骤 1 环境设置 您可以从以下链接下载用于交互示例和指南的动画和按钮资源。 📘 zepeto 交互示例 zepeto 交互模块 https //github com/naverz/zepeto multiplay example/tree/main/assets/zepeto%20interaction%20module 在场景中实现zepeto角色创建代码作为默认。 📘 请参考以下指南。 \[ zepeto 玩家 docid\ veqpnpsmnkxkrdhcmuqtl ] 步骤 2 设置对象 设置与 zepeto 角色交互的对象。 1\) 放置 zepeto 角色将与之交互的对象。 2\) 创建一个层级 > 创建空对象并将其重命名为 dockpoint。 这是 zepeto 角色将与之交互的点。调整对象的位置。 检查unity编辑器顶部的变换小工具切换按钮是否为本地,并将z轴(蓝色箭头)旋转到物体外部。 添加collider组件后,检查istrigger。 调整collider的大小以匹配玩家可以与物体交互的范围。 3\) 创建层级 > 创建空对象作为dockpoint的子对象,并将其重命名为iconpos。 步骤 3 设置用户界面 1\) 创建层级 > 用户界面 > 画布,作为 zepeto 角色将要交互的对象的子项,并将其重命名为 preficoncanvas。 将渲染模式设置为世界空间。 将宽度和高度分别设置为 1。 在图形射线投射器组件上取消选中忽略反向图形选项。 2\) 创建层级 > 用户界面 > 按钮,作为 preficoncanvas 的子项。 3\) 设置完成后,将其制作成prefab,并删除层级中的剩余preficoncanvas。 步骤 4 编写脚本 步骤 4 1 interactionicon 1\) 创建项目 > 创建 > zepeto > typescript 并将其重命名为 interactionicon。 2\) 编写如下示例脚本。 import { zepetoscriptbehaviour } from 'zepeto script'; import { camera, canvas, collider, gameobject, transform, object } from 'unityengine'; import { button } from 'unityengine ui'; import { unityevent } from 'unityengine events'; import { zepetoplayers } from 'zepeto character controller'; export default class interactionicon extends zepetoscriptbehaviour { // 图标 @header("\[图标]") @serializefield() private preficoncanvas gameobject; @serializefield() private iconposition transform; // unity 事件 @header("\[unity 事件]") public onclickevent unityevent; public ontriggerenterevent unityevent; public ontriggerexitevent unityevent; private button button; private canvas canvas; private cachedworldcamera camera; private isiconactive boolean = false; private isdonefirsttrig boolean = false; private update() { if (this isdonefirsttrig && this canvas? gameobject activeself) { this updateiconrotation(); } } private ontriggerenter(coll collider) { if (coll != zepetoplayers instance localplayer? zepetoplayer? character getcomponent\<collider>()) { return; } this showicon(); this ontriggerenterevent? invoke(); } private ontriggerexit(coll collider) { if (coll != zepetoplayers instance localplayer? zepetoplayer? character getcomponent\<collider>()) { return; } this hideicon(); this ontriggerexitevent? invoke(); } public showicon(){ if (!this isdonefirsttrig) { this createicon(); this isdonefirsttrig = true; } else { this canvas gameobject setactive(true); } this isiconactive = true; } public hideicon() { this canvas? gameobject setactive(false); this isiconactive = false; } private createicon() { if (this canvas === undefined) { const canvas = gameobject instantiate(this preficoncanvas, this iconposition) as gameobject; this canvas = canvas getcomponent\<canvas>(); this button = canvas getcomponentinchildren\<button>(); this canvas transform position = this iconposition position; } this cachedworldcamera = object findobjectoftype\<camera>(); this canvas worldcamera = this cachedworldcamera; this button onclick addlistener(() => { this onclickicon(); }); } private updateiconrotation() { this canvas transform lookat(this cachedworldcamera transform); } private onclickicon() { this onclickevent? invoke(); } } 脚本的流程如下: 更新() 调用 updateiconrotation() 自定义函数以旋转图标画布以匹配相机旋转。 ontriggerenter(), ontriggerexit() 当你进入碰撞体区域并检测到触发时,调用 showicon() 自定义函数以激活图标。 当你离开碰撞体区域时,调用 hideicon() 自定义函数以禁用图标。 3\) 完成脚本创建后,将脚本添加到 dockpoint 对象中。 4\) 从检查器中分配 pref 图标画布和图标位置。 步骤 4 2 手势交互 1\) 创建项目 > 创建 > zepeto > typescript 并将其重命名为 gestureinteraction。 2\) 写一个类似下面的示例脚本。 import { animationclip, animator, humanbodybones, physics, transform, vector3, waitforendofframe} from 'unityengine'; import { zepetoscriptbehaviour } from 'zepeto script'; import { zepetoplayers, zepetocharacter } from "zepeto character controller"; import interactionicon from ' /interactionicon'; export default class gestureinteraction extends zepetoscriptbehaviour { @serializefield() private animationclip animationclip; @serializefield() private issnapbone boolean = true; @serializefield() private bodybone humanbodybones; @serializefield() private allowoverlap boolean = false; private interactionicon interactionicon; private isfirst boolean = true; private localcharacter zepetocharacter; private outposition vector3; private playergestureposition vector3; private start() { this interactionicon = this transform getcomponent\<interactionicon>(); zepetoplayers instance onaddedlocalplayer addlistener(() => { this localcharacter = zepetoplayers instance localplayer zepetoplayer character; }); this interactionicon onclickevent addlistener(()=> { // 当点击交互图标时 this interactionicon hideicon(); this dointeraction(); }); } private dointeraction() { this outposition = this transform position; if (this issnapbone) { // 是否位置为空 if (this allowoverlap || this findotherplayernum() < 1) { this localcharacter setgesture(this animationclip); this startcoroutine(this snapbone()); this startcoroutine(this waitforexit()); } else { // 座位已满。 this interactionicon showicon(); } } else { this localcharacter setgesture(this animationclip); this startcoroutine(this waitforexit()); } } private snapbone() { const animator animator = this localcharacter zepetoanimator; const bone transform = animator getbonetransform(this bodybone); let idx = 0; while(true) { const distance = vector3 op subtraction(bone position, this localcharacter transform position); const newpos vector3 = vector3 op subtraction(this transform position, distance); this playergestureposition = newpos; this localcharacter transform position = this playergestureposition; this localcharacter transform rotation = this transform rotation; yield new waitforendofframe(); idx++; // 在5帧动画期间校准位置。 if (idx > 5) { return; } } } // 确切的方法必须通过服务器代码, // 但它是由本地客户端计算以优化服务器。 private findotherplayernum() { const hitinfos = physics overlapsphere(this transform position, 0 1); let playernum = 0; if (hitinfos length > 0) { hitinfos foreach((hitinfo) => { if (hitinfo transform getcomponent\<zepetocharacter>()) { playernum ++; } }); } return playernum; } private waitforexit() { if (this localcharacter) { while (true) { if (this localcharacter tryjump || this localcharacter trymove) { this localcharacter cancelgesture(); this transform position = this outposition; this interactionicon showicon(); break; } else if(this issnapbone && this playergestureposition != this localcharacter transform position){ this interactionicon showicon(); break; } yield; } } } } 脚本的流程如下: 开始() 当图标被点击时,它会停用并调用 dointeraction() 自定义函数。 dointeraction() 如果 issnapbone 被选中, 如果座位是空的(allowoverlap 被选中,或者 findotherplayernum() 自定义函数返回值小于 1) 获取分配给 animationclip 的手势。 启动 snapbone() 协程并将 zepeto 角色的 bodybone 附加到 targettranform。 启动 waitforexit() 协程。 当 zepeto 角色跳跃或移动,或离开碰撞体区域时,取消手势并激活图标。 当座位容量满时激活图标。 如果 issnapbone 没有被选中, 获取分配给 animationclip 的手势。 启动 waitforexit() 协程。 findotherplayernum() 查找存在于附加脚本的对象位置的 zepeto 角色,并返回有多少个。 您可以检查座位是否为空。 3\) 完成脚本创建后,将脚本添加到 dockpoint 对象中。 4\) 在检查器中分配动画剪辑、是否为快照骨骼、身体骨骼和允许重叠。 分配动画剪辑。这些是与交互相关的手势。 检查是否为快照骨骼。确保分配给身体骨骼的部分被定位为停靠点。 将身体骨骼设置为臀部。确保臀部被定位在停靠点,因为这将是一个坐姿手势。 允许重叠可以让你决定是否可以让多个人坐在一个座位上。 步骤 5 玩 当 zepeto 角色接近物体时,按钮会出现,当它远离时,按钮会消失。 如果您设置的手势在接近和与按钮交互时播放,那就是成功。 除了手势外,还可以实现各种事件在交互后发生。 以下是实现一个事件的示例,该事件在交互后创建一个物品。 https //www youtube com/watch?v=ooazdb4 lgo https //www youtube com/watch?v=ooazdb4 lgo 📘 zepeto 世界示例 第 3 章 交互示例 https //github com/naverz/zepeto world sample/tree/main/assets/chapter3 https //github com/naverz/zepeto world sample/tree/main/assets/chapter3