สร้างโลกของคุณเอง
การโต้ตอบกับวัตถุ
การโต้ตอบกับวัตถุ
15นาที
สร้างปุ่มโต้ตอบที่ปรากฏเมื่ออักขระ zepeto เข้าใกล้วัตถุ ขั้นตอนที่ 1 ตั้งค่าสภาพแวดล้อม คุณสามารถดาวน์โหลดแอนิเมชันและทรัพยากรปุ่มที่ใช้ในตัวอย่างการโต้ตอบและคู่มือจากลิงก์ด้านล่างนี้ 📘 ตัวอย่างการโต้ตอบ zepeto โมดูลการโต้ตอบ zepeto https //github com/naverz/zepeto multiplay example/tree/main/assets/zepeto%20interaction%20module สร้างโค้ดการสร้างอักขระ zepeto ในฉากเป็นค่าเริ่มต้น 📘 กรุณาอ้างอิงจากคู่มือต่อไปนี้ \[ ผู้เล่น zepeto docid\ qei1clrldk5lxxgpv1yqu ] ขั้นตอนที่ 2 การตั้งค่าอ็อบเจ็กต์ ตั้งค่าอ็อบเจ็กต์เพื่อให้มีปฏิสัมพันธ์กับตัวละคร zepeto 1\) วางอ็อบเจ็กต์ที่ตัวละคร zepeto จะมีปฏิสัมพันธ์ด้วย 2\) สร้างลำดับชั้น > สร้างอ็อบเจ็กต์ว่างและเปลี่ยนชื่อเป็น dockpoint นี่คือจุดที่ตัวละคร zepeto จะมีปฏิสัมพันธ์ด้วย ปรับตำแหน่งของอ็อบเจ็กต์ ตรวจสอบว่าปุ่มสลับ gizmo การแปลงที่ด้านบนของ unity editor เป็น local และหมุนแกน z (ลูกศรสีน้ำเงิน) ไปด้านนอกของวัตถุ หลังจากเพิ่มคอมโพเนนต์ collider ให้ตรวจสอบ istrigger ปรับขนาดของ collider ให้ตรงกับช่วงที่ผู้เล่นสามารถโต้ตอบกับวัตถุได้ 3\) สร้าง hierarchy > สร้างวัตถุเปล่าเป็นลูกของ dockpoint และเปลี่ยนชื่อเป็น iconpos ขั้นตอนที่ 3 การตั้งค่า ui 1\) สร้าง hierachy > ui > canvas เป็นลูกของวัตถุที่ตัวละคร zepeto จะโต้ตอบด้วยและเปลี่ยนชื่อเป็น preficoncanvas ตั้งค่า render mode เป็น world space ตั้งค่า width และ height เป็น 1 ตามลำดับ ยกเลิกการเลือกตัวเลือก ignore reversed graphics ในส่วนของ graphic raycaster 2\) สร้าง hierachy > ui > button เป็นลูกของ 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(); } } ลำดับการทำงานของสคริปต์มีดังนี้ update() เรียกใช้ฟังก์ชัน updateiconrotation() ที่กำหนดเองเพื่อหมุนผ้าใบไอคอนให้ตรงกับการหมุนของกล้อง ontriggerenter(), ontriggerexit() เมื่อคุณเข้าไปในพื้นที่ collider และตรวจจับการกระตุ้น ให้เรียกใช้ฟังก์ชัน showicon() ที่กำหนดเองเพื่อเปิดใช้งานไอคอน เมื่อคุณออกจากพื้นที่ collider ให้เรียกใช้ฟังก์ชัน hideicon() ที่กำหนดเองเพื่อปิดการใช้งานไอคอน 3\) หลังจากสร้างสคริปต์เสร็จแล้ว ให้เพิ่มสคริปต์ไปยังวัตถุ dockpoint 4\) กำหนด pref icon canvas, icon position จาก inspector ขั้นตอนที่ 4 2 gestureinteraction 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 มาใช้ เริ่มต้น coroutine snapbone() และแนบ bodybone ของตัวละคร zepeto ไปยัง targettranform เริ่มต้น coroutine waitforexit() เมื่อตัวละคร zepeto กระโดดหรือเคลื่อนที่ หรือออกจากพื้นที่ collider ให้ยกเลิกท่าทางและเปิดใช้งานไอคอน เปิดใช้งานไอคอนเมื่อความจุที่นั่งเต็ม ถ้า issnapbone ไม่ถูกเลือก นำท่าทางที่กำหนดให้กับ animationclip มาใช้ เริ่มต้น coroutine waitforexit() 3\) หลังจากสร้างสคริปต์เสร็จแล้ว ให้เพิ่มสคริปต์ไปยังวัตถุ dockpoint 4\) กำหนด animation clip, is snap bone, body bone, และ allow overlap ใน inspector กำหนด animation clip นี่คือท่าทางที่ต้องทำเมื่อมีการโต้ตอบ ตรวจสอบ is snapbone ตรวจสอบให้แน่ใจว่าส่วนที่กำหนดให้กับ body bone ถูกวางใน dockpoint ตั้งค่า body bone เป็น hips ตรวจสอบให้แน่ใจว่าสะโพกถูกวางใน dockpoint เพราะมันจะเป็นท่าทางนั่ง allow overlap ช่วยให้คุณกำหนดได้ว่าหลายคนสามารถนั่งในที่นั่งเดียวกันได้หรือไม่ ขั้นตอนที่ 5 เล่น ปุ่มจะปรากฏเมื่ออักขระ zepeto เข้าใกล้วัตถุ และจะหายไปเมื่อมันเคลื่อนออกไป หากท่าทางที่คุณตั้งไว้เล่นเมื่อเข้าใกล้และโต้ตอบกับปุ่ม นั่นคือความสำเร็จ นอกจากท่าทางแล้ว เหตุการณ์ต่างๆ สามารถนำไปใช้ได้หลังจากการโต้ตอบ ต่อไปนี้คือตัวอย่างการนำเหตุการณ์ไปใช้ที่สร้างรายการหลังจากการโต้ตอบ https //www youtube com/watch?v=ooazdb4 lgo https //www youtube com/watch?v=ooazdb4 lgo 📘 ตัวอย่าง zepeto world ตัวอย่างการโต้ตอบบทที่ 3 https //github com/naverz/zepeto world sample/tree/main/assets/chapter3 https //github com/naverz/zepeto world sample/tree/main/assets/chapter3