สร้างรายได้จากโลกของคุณ! การตั้งค่าผลิตภัณฑ์และสกุลเงิน
ZEPETO Mannequin คือแพ็กเกจ API หุ่นที่รองรับฟังก์ชันลองสวมใส่ / ซื้อคอสตูม
- ทำให้สามารถขายไอเทมคอสตูมที่ตัวเองสร้างและเผยแพร่ใน World ได้
- สามารถสร้างให้หุ่นสวมคอสตูมที่กำหนดบน NPC ตัวละคร ZEPETO ได้
- สามารถซื้อไอเทมคอสตูมได้จากการมีปฏิกิริยากับหุ่นหรือวัตถุ
STEP 1 : ติดตั้ง
Window → Package Manager → และติดตั้ง ZEPETO.World Package 1.21.15 หรือสูงกว่าก่อน
หลังจากนั้นกรุณาติดตั้งแพ็คเกจ ZEPETO.Module 1.0.8

❗️ คำเตือน
- ในส่วนของการปรับปรุงประสิทธิภาพ แพ็คเกจแบบสแตนด์อโลนได้ถูกรวมเข้ากับแพ็คเกจ ZEPETO.Module 1.0.8 และเวอร์ชันที่ใหม่กว่า
- โปรดลบแพ็คเกจ ZEPETO.Mannequin ที่ติดตั้งไว้ก่อนหน้านี้
- หลังจากลบแพ็คเกจที่มีอยู่แล้ว โปรดติดตั้งแพ็คเกจ ZEPETO.Module เวอร์ชัน 1.0.8 หรือใหม่กว่า
STEP 2 : ตรวจสอบ ID รายการที่จะตั้งค่าบนหุ่นจำลอง
👍 วิธีตรวจสอบ ID ไอเทม
- คุณสามารถตรวจสอบ URL ที่ปรากฏขึ้นเมื่อคุณคลิกที่สินค้ารายการใน ZEPETO Studio

- คัดลอก ID รายการในรูปแบบของตัวเลขสุ่ม วางลงใน Unity Editor และวาง 'CR_' ข้างหน้า

📘 หากเพิ่งสร้างไอเทมเป็นครั้งแรก กรุณาศึกษาจากแนวทางต่อไปนี้ [Create Your Item]
STEP 3 : เซ็ตหุ่น
สามารถสร้างวัตถุหุ่นได้ทั้งหมด 3 วิธี
❗️ คำเตือน
- แพ็กเกจ ZEPETO Mannequin สามารถใช้สำหรับเซ็ตและขายได้แค่ไอเทมที่ตัวเองสร้างและเผยแพร่ใน World เท่านั้น
- ไม่สามารถขายไอเทมที่ถูกตีกลับในขั้นตอนตรวจสอบได้
- เพราะฉะนั้น ต้องล็อกอิน Unity เท่านั้น จึงจะใช้ฟังก์ชันนี้ได้
- ตอนที่เผยแพร่แพ็กเกจที่มีฟังก์ชันหุ่น ก็ต้องล็อกอิน Editor ไว้เช่นกัน
- เมื่อใช้หุ่นของเทมเพลต ZEPETO หรือประเภทโมเดล ZEPETO จำนวนหุ่นอาจใช้ทรัพยากรมากเท่ากับจำนวนอักขระ ZEPETO ที่ป้อน ซึ่งอาจส่งผลต่อการปรับให้เหมาะสม
- หากคุณต้องการวางหุ่นจำนวนมากให้เป็นรูปร่างของตัวละคร Zepeto ในโลก เราขอแนะนำให้เลือกหุ่นแบบ Simple Type และใช้ ZEPETO BaseModel เป็นวัตถุ
STEP 3-1 : Simple Type

เป็นวิธีที่ง่ายที่สุด ซึ่งสามารถเพิ่มวัตถุที่กำหนดให้เป็น Mannequin ลูก เพื่อเซ็ตลักษณะภายนอกได้
- เพิ่มวัตถุที่จะโต้ตอบด้วย
- ต้องเพิ่ม Collider ลงในวัตถุเพื่อให้มีปฏิกิริยา
- ต้องเช็ก isTrigger หากไม่ได้เช็ก จะถูกประเมินเป็นทริกเกอร์อัตโนมัติ
- หากจะเพิ่ม Component ทั้งที่ไม่ได้เพิ่ม Collider จะมีหน้าต่างแจ้งเตือนปรากฏขึ้นดังด้านล่าง

- เพิ่ม Conponent หุ่นลงในวัตถุ
- ในตัวตรวจสอบให้ตั้งค่าดังนี้:
- icon: จะแสดงไอคอนไม้แขวนเสื้อเป็นค่าเริ่มต้น แต่สามารถเปลี่ยนเป็นไอคอนคัสตอมได้
- icon position: หากไม่มี ไอคอนจะปรากฏบนตำแหน่งวัตถุที่มี Component หุ่นเป็นค่าเริ่มต้น
- เพิ่ม position object จากนั้นป้อนค่าลงไปเพื่อให้ปรากฏในตำแหน่งที่ต้องการ
- ids: กรอก ID ไอเทมคอสตูมที่ต้องการขาย

- เพิ่ม Empty Object แล้วตั้งชื่อวัตถุว่า MannequinScript
- ZEPETO > เพิ่ม Typescript แล้วตั้งชื่อสคริปต์ว่า MannequinScript
- เขียนสคริปต์ดังด้านล่าง
import { ZepetoScriptBehaviour } from 'ZEPETO.Script';
import { ZepetoPlayers, SpawnInfo } from 'ZEPETO.Character.Controller';
import { WorldService } from 'ZEPETO.World';
import { ItemContentsRequest, Mannequin, MannequinComponent, MannequinInteractable, MannequinPreviewer } from 'ZEPETO.Mannequin';
import { Object } from 'UnityEngine';
export default class MannequinScript extends ZepetoScriptBehaviour {
private _previewer: MannequinPreviewer;
Start() {
// Code that creates a ZEPETO character based on the logged in ID
// ZepetoPlayers.instance.CreatePlayerWithUserId(WorldService.userId, SpawnInfo.Default, true);
ZepetoPlayers.instance.OnAddedLocalPlayer.AddListener(() => {
const character = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer.character;
// Add Mannequin Interactable Component
character.gameObject.AddComponent<MannequinInteractable>();
});
// Find all Mannequin components
const mannequins = Object.FindObjectsOfType<MannequinComponent>();
mannequins.forEach(m => {
// Enter the Collider
m.onActive.AddListener(contents => {
Mannequin.OpenUI(contents);
const zepetoContext = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer.character.Context;
this._previewer = new MannequinPreviewer(zepetoContext, contents);
this._previewer.PreviewContents();
});
// Exit the Collider
m.onCancel.AddListener(() => {
Mannequin.CloseUI();
this._previewer?.ResetContents();
});
});
}
}
- หลังจากเพิ่มสคริปต์ไปที่วัตถุแล้ว ให้กดปุ่ม [▶︎(play)] เพื่อเรียกใช้

STEP 3-2 : ZEPETO Model Type

สามารถเพิ่ม ID ไอเทมลงใน NPC ตัวละคร ZEPETO เพื่อเซ็ตลักษณะภายนอกของหุ่นได้
- สร้าง Empty Object แล้วตั้งชื่อว่า Mannequin
- ต้องเพิ่ม Collider ลงในวัตถุเพื่อให้มีปฏิกิริยา
- ต้องเช็ก isTrigger หากไม่ได้เช็ก จะถูกประเมินเป็นทริกเกอร์อัตโนมัติ
- เพิ่ม Conponent หุ่นลงในวัตถุ
- ในตัวตรวจสอบให้ตั้งค่าดังนี้:
- icon: จะแสดงไอคอนไม้แขวนเสื้อเป็นค่าเริ่มต้น แต่สามารถเปลี่ยนเป็นไอคอนคัสตอมได้
- icon position: หากไม่มี ไอคอนจะปรากฏบนตำแหน่งวัตถุที่มี Component หุ่นเป็นค่าเริ่มต้น
- เพิ่ม position object จากนั้นป้อนค่าลงไปเพื่อให้ปรากฏในตำแหน่งที่ต้องการ
- ids: กรอก ID ไอเทมคอสตูมที่ต้องการขาย
- pose: สามารถเลือกโพสแล้วเซ็ตได้
- ในอนาคตจะจัดโพสที่ต้องการให้ในรูปแบบ ID

👍 หากคุณติดตั้งสคริปต์ Mannequin ในฉากของคุณแล้ว คุณสามารถข้ามขั้นตอนต่อมาได้
- เพิ่ม Empty Object แล้วตั้งชื่อวัตถุว่า MannequinScript
- ZEPETO > เพิ่ม Typescript แล้วตั้งชื่อสคริปต์ว่า MannequinScript
- เขียนสคริปต์ดังด้านล่าง
import { ZepetoScriptBehaviour } from 'ZEPETO.Script';
import { ZepetoPlayers, SpawnInfo } from 'ZEPETO.Character.Controller';
import { WorldService } from 'ZEPETO.World';
import { ItemContentsRequest, Mannequin, MannequinComponent, MannequinInteractable, MannequinPreviewer } from 'ZEPETO.Mannequin';
import { Object } from 'UnityEngine';
export default class MannequinScript extends ZepetoScriptBehaviour {
private _previewer: MannequinPreviewer;
Start() {
// Code that creates a ZEPETO character based on the logged in ID
// ZepetoPlayers.instance.CreatePlayerWithUserId(WorldService.userId, SpawnInfo.Default, true);
ZepetoPlayers.instance.OnAddedLocalPlayer.AddListener(() => {
const character = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer.character;
// Add Mannequin Interactable Component
character.gameObject.AddComponent<MannequinInteractable>();
});
// Find all Mannequin components
const mannequins = Object.FindObjectsOfType<MannequinComponent>();
mannequins.forEach(m => {
// Enter the Collider
m.onActive.AddListener(contents => {
Mannequin.OpenUI(contents);
const zepetoContext = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer.character.Context;
this._previewer = new MannequinPreviewer(zepetoContext, contents);
this._previewer.PreviewContents();
});
// Exit the Collider
m.onCancel.AddListener(() => {
Mannequin.CloseUI();
this._previewer?.ResetContents();
});
});
}
}- หลังจากเพิ่มสคริปต์ไปที่วัตถุแล้ว ให้กดปุ่ม [▶︎(play)] เพื่อเรียกใช้

STEP 3-3 : ZEPETO Template Type

หากป้อน ZEPETO ID จะมีหุ่นที่สวมชุดที่คนนั้นสวมใส่อยู่ปรากฏขึ้น แต่หากไม่ใช่ชุดที่สร้างขึ้นเองก็จะไม่ปรากฏ
- สร้าง Empty Object แล้วตั้งชื่อว่า Mannequin
- ต้องเพิ่ม Collider ลงในวัตถุเพื่อให้มีปฏิกิริยา
- ต้องเช็ก isTrigger หากไม่ได้เช็ก จะถูกประเมินเป็นทริกเกอร์อัตโนมัติ
- เพิ่ม Conponent หุ่นลงในวัตถุ
- ในตัวตรวจสอบให้ตั้งค่าดังนี้:
- icon: จะแสดงไอคอนไม้แขวนเสื้อเป็นค่าเริ่มต้น แต่สามารถเปลี่ยนเป็นไอคอนคัสตอมได้
- icon position: หากไม่มี ไอคอนจะปรากฏบนตำแหน่งวัตถุที่มี Component หุ่นเป็นค่าเริ่มต้น
- เพิ่ม position object จากนั้นป้อนค่าลงไปเพื่อให้ปรากฏในตำแหน่งที่ต้องการ
- zepeto id: ป้อน ZEPETO ID
- pose: สามารถเลือกโพสแล้วเซ็ตได้
- ในอนาคตจะจัดโพสที่ต้องการให้ในรูปแบบ ID

👍 หากคุณติดตั้งสคริปต์ Mannequin ในฉากของคุณแล้ว คุณสามารถข้ามขั้นตอนต่อมาได้
- เพิ่ม Empty Object แล้วตั้งชื่อวัตถุว่า MannequinScript
- ZEPETO > เพิ่ม Typescript แล้วตั้งชื่อสคริปต์ว่า MannequinScript
- เขียนสคริปต์ดังด้านล่าง
import { ZepetoScriptBehaviour } from 'ZEPETO.Script';
import { ZepetoPlayers, SpawnInfo } from 'ZEPETO.Character.Controller';
import { WorldService } from 'ZEPETO.World';
import { ItemContentsRequest, Mannequin, MannequinComponent, MannequinInteractable, MannequinPreviewer } from 'ZEPETO.Mannequin';
import { Object } from 'UnityEngine';
export default class MannequinScript extends ZepetoScriptBehaviour {
private _previewer: MannequinPreviewer;
Start() {
// Code that creates a ZEPETO character based on the logged in ID
// ZepetoPlayers.instance.CreatePlayerWithUserId(WorldService.userId, SpawnInfo.Default, true);
ZepetoPlayers.instance.OnAddedLocalPlayer.AddListener(() => {
const character = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer.character;
// Add Mannequin Interactable Component
character.gameObject.AddComponent<MannequinInteractable>();
});
// Find all Mannequin components
const mannequins = Object.FindObjectsOfType<MannequinComponent>();
mannequins.forEach(m => {
// Enter the Collider
m.onActive.AddListener(contents => {
Mannequin.OpenUI(contents);
const zepetoContext = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer.character.Context;
this._previewer = new MannequinPreviewer(zepetoContext, contents);
this._previewer.PreviewContents();
});
// Exit the Collider
m.onCancel.AddListener(() => {
Mannequin.CloseUI();
this._previewer?.ResetContents();
});
});
}
}- หลังจากเพิ่มสคริปต์ไปที่วัตถุแล้ว ให้กดปุ่ม [▶︎(play)] เพื่อเรียกใช้

STEP 4 : ใช้ฟังก์ชันซื้อหุ่น
หากเซ็ตหุ่นได้ถูกต้อง ไอคอนสำหรับมีปฏิกิริยากับหุ่นเมื่อเข้าไปในขอบเขต Collider ของวัตถุหุ่นจะปรากฏขึ้น
หากคลิกที่ไอคอน จะมีหน้าต่างให้ซื้อสินค้าและไอเทมที่เซ็ตไว้ในหุ่นปรากฏขึ้น

สามารถลองสวมใส่ได้โดยคลิกที่ไอคอน
- ไอเทมที่มีอยู่แล้วจะปรากฏเป็นเครื่องหมายเช็กถูกแทนที่ราคา หากออกจากขอบเขต Collider ของวัตถุหุ่น จะถูกยกเลิกการลองสวมใส่และเปลี่ยนกลับไปเป็นชุดเดิม

ต่อไปเป็นรายละเอียดการดำเนินการซื้อตาม Test Scenario
Test Environment | การดำเนินการซื้อ |
|---|---|
ซื้อบน Unity Editor | เมื่อซื้อได้สำเร็จระหว่าง Play คอสตูมจะถูกเปลี่ยน แต่หากสิ้นสุดการ Play ก็จะกลับไปเป็นดังเดิม |
ซื้อบน QR Code Mobile Test Environment | ในกรณีนี้ เป็นการทดสอบโดยเข้าผ่านเซิร์ฟเวอร์จริง ดังนั้นเมื่อซื้อสำเร็จแล้ว คอสตูมจะถูกเปลี่ยน และแม้จะออกจาก World แล้ว คอสตูมที่เปลี่ยนก็จะยังคงอยู่ นอกจากนี้ หากการซื้อสำเร็จ รายการจะถูกซื้อจากเซิร์ฟเวอร์จริง ดังนั้นอัญมณีที่ถือโดยบัญชีจริงจะถูกหักออกและคุณจะมีรายการเครื่องแต่งกาย เสื้อผ้าที่ซื้อแล้วไม่สามารถทิ้งหรือลบทิ้งได้โดยพลการ ดังนั้นโปรดซื้ออย่างระมัดระวังเมื่อทำการทดสอบ |
ซื้อโดยการเข้าผ่านลิงก์ทดสอบหลังจากบันทึกแพ็กเกจลงใน ZEPETO Studio | ในกรณีนี้ เป็นการทดสอบโดยเข้าผ่านเซิร์ฟเวอร์จริง ดังนั้นเมื่อซื้อสำเร็จแล้ว คอสตูมจะถูกเปลี่ยน และแม้จะออกจาก World แล้ว คอสตูมที่เปลี่ยนก็จะยังคงอยู่ นอกจากนี้ หากการซื้อสำเร็จ รายการจะถูกซื้อจากเซิร์ฟเวอร์จริง ดังนั้นอัญมณีที่ถือโดยบัญชีจริงจะถูกหักออกและคุณจะมีรายการเครื่องแต่งกาย เสื้อผ้าที่ซื้อแล้วไม่สามารถทิ้งหรือลบทิ้งได้โดยพลการ ดังนั้นโปรดซื้ออย่างระมัดระวังเมื่อทำการทดสอบ |
STEP 5 : ซิงก์ Mannequin Multiplay
Multiplay จะต้องซิงก์กับคอสตูมที่ผู้เล่นอื่นสวมใส่ผ่านการใช้ฟังก์ชันหุ่น
👍 ต้องตั้งค่า Multiplay พื้นฐานให้เสร็จเรียบร้อยก่อนสร้างตามรายการด้านล่าง
- เพิ่มสคริปต์ Client โดยเลือก ZEPETO > Typescript จากนั้นตั้งชื่อสคริปต์ว่า MannequinController
- เขียนตามด้านล่าง
import { ZepetoScriptBehaviour } from 'ZEPETO.Script';
import { Room } from 'ZEPETO.Multiplay';
import { ClothesPreviewer, ItemContent, ItemContentsRequest, Mannequin, MannequinComponent, MannequinInteractable, MannequinPreviewer } from 'ZEPETO.Mannequin';
import { ZepetoContext } from 'Zepeto';
import { ZepetoPlayer, ZepetoPlayers } from 'ZEPETO.Character.Controller';
import { GameObject, Object, Canvas, WaitForSecondsRealtime, LayerMask } from 'UnityEngine';
import { Player } from 'ZEPETO.Multiplay.Schema';
import { ZepetoWorldMultiplay } from 'ZEPETO.World';
class CharacterItem {
property: string;
id: string;
}
class ChangedItem {
sessionId: string;
characterItems: CharacterItem[];
}
export default class MannequinController extends ZepetoScriptBehaviour {
private MESSAGE_TYPE = {
OnChangedItem: "OnChangedItem",
SyncChangedItem: "SyncChangedItem",
CheckChangedItem: "CheckChangedItem"
}
private multiplay: ZepetoWorldMultiplay;
private _room: Room;
private _previewer: MannequinPreviewer
private _context: ZepetoContext
private _userZepetoContexts: Map<string, ZepetoContext> = new Map<string, ZepetoContext>();
private _currentMannequinComponent: MannequinComponent = null;
private _selectMannequinComponent: MannequinComponent = null;
private _isOpenMannequinUI: boolean = false;
private _basicClothString = "basicCloth" as const;
Start() {
this.multiplay = Object.FindObjectOfType<ZepetoWorldMultiplay>();
Mannequin.OnOpenedShopUI.AddListener((item) => {
// When you click OpenShopButton
this.OpenedShopUI(item)
});
Mannequin.OnClosedShopUI.AddListener(() => {
// When you click CloseShopButton
this.ClosedShopUI()
});
Mannequin.OnSelectedItem.AddListener((itemcontent: ItemContent, select: boolean) => {
// Action when item is selected
});
ZepetoPlayers.instance.OnAddedLocalPlayer.AddListener(() => {
const myPlayer = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer;
// Mannequin
const character = myPlayer.character;
character.gameObject.AddComponent<MannequinInteractable>();
console.log("local _context set");
this._context = character.Context;
const mannequins = Object.FindObjectsOfType<MannequinComponent>()
mannequins.forEach(mannequin => {
// When you enter mannequin collider
mannequin.onActive.AddListener(contents => {
if (this._currentMannequinComponent != null && this._currentMannequinComponent == mannequin) {
return;
}
if (contents == null || contents.length == 0) {
console.log("No mannequin data");
return;
}
if (this._isOpenMannequinUI) {
this.BreakMannequin();
}
this._selectMannequinComponent = mannequin;
Mannequin.OpenUI(contents);
console.log("onActive");
});
// [Option] When you leave mannequin collider
mannequin.onCancel.AddListener( () => {
if (this._currentMannequinComponent == null || this._currentMannequinComponent != mannequin) {
return;
}
this.BreakMannequin();
console.log("onCancel");
});
let iconCanvas = mannequin.gameObject.GetComponentInChildren<Canvas>(true);
if (iconCanvas != null) {
iconCanvas.gameObject.layer = LayerMask.NameToLayer("UI");
}
});
});
ZepetoPlayers.instance.OnAddedPlayer.AddListener((sessionId: string) => {
if (ZepetoPlayers.instance.GetPlayer(sessionId).isLocalPlayer) {
return;
}
const userContext = ZepetoPlayers.instance.GetPlayer(sessionId).character.Context;
this._userZepetoContexts.set(sessionId, userContext);
this._room.Send(this.MESSAGE_TYPE.CheckChangedItem, sessionId);
});
this.multiplay.RoomJoined += (room: Room) => {
this._room = room;
this.AddMessageHandler();
};
}
private AddMessageHandler() {
// [Option] Synchronize each player's clothes
this._room.AddMessageHandler<ChangedItem>(this.MESSAGE_TYPE.SyncChangedItem, message => {
console.log("syncChangedItem");
if (message == null) {
return;
}
if (false == this._userZepetoContexts.has(message.sessionId)) {
return;
}
let itemContents: ItemContent[] = [];
for (const characterItem of message.characterItems) {
let itemContent: ItemContent = new ItemContent();
itemContent.id = characterItem.id;
itemContent.property = parseInt(characterItem.property);
if (itemContent.id == this._basicClothString) {
itemContent.id = "";
}
itemContents.push(itemContent);
}
let clothesPreviewer:ClothesPreviewer = new ClothesPreviewer(this._userZepetoContexts.get(message.sessionId),itemContents);
clothesPreviewer.PreviewContents();
});
}
private ClosedShopUI() {
this._currentMannequinComponent = null;
this._isOpenMannequinUI = false;
}
private OpenedShopUI(items: ItemContent[]) {
this._isOpenMannequinUI = true;
this._currentMannequinComponent = this._selectMannequinComponent;
this._previewer = new MannequinPreviewer(this._context, items);
this._previewer.OnChanged.AddListener((changeValues) => {
let characterItems: CharacterItem[] = [];
for (const changeValue of changeValues) {
let characterItem: CharacterItem = new CharacterItem();
characterItem.id = changeValue.id;
characterItem.property = changeValue.property.toString();
if (characterItem.id == "") {
characterItem.id = this._basicClothString;
}
characterItems.push(characterItem);
}
this._room.Send(this.MESSAGE_TYPE.OnChangedItem, characterItems);
});
this._previewer.PreviewContents();
}
public BreakMannequin() {
Mannequin.CloseUI();
if (this._previewer) {
this._previewer.ResetContents();
this._previewer = null;
}
this._currentMannequinComponent = null;
this._isOpenMannequinUI = false;
}
}- เลือก Hierarchy > Empty Object เพื่อสร้างวัตถุ แล้วตั้งชื่อว่า MannequinController
- เพิ่มสคริปต์ที่เพิ่งสร้างลงใน MannequinController

- เขียนโค้ดเซิร์ฟเวอร์ใน World.Multiplay > index.ts โดยอ้างอิงตามด้านล่าง
import { Sandbox, SandboxOptions, SandboxPlayer } from 'ZEPETO.Multiplay';
import { Player, Transform, Vector3 } from 'ZEPETO.Multiplay.Schema';
class CharacterItem {
property: string;
id: string;
}
class ChangedItem {
sessionId: string;
characterItems: CharacterItem[];
}
enum Cloth {
TOP = "19",
BOTTOM = "20" ,
DRESS = "22"
}
export default class extends Sandbox {
private MESSAGE_TYPE = {
OnChangedItem: "OnChangedItem",
SyncChangedItem: "SyncChangedItem",
CheckChangedItem: "CheckChangedItem"
}
// Map<sessionId, Map<CharacterItem.property, CharacterItem.id>>
private ChangedItems: Map<string, Map<string, string>>;
onCreate(options: SandboxOptions) {
// Called when the Room object is created.
// Handle the state or data initialization of the Room object.
this.onMessage("onChangedTransform", (client, message) => {
const player = this.state.players.get(client.sessionId);
const transform = new Transform();
transform.position = new Vector3();
transform.position.x = message.position.x;
transform.position.y = message.position.y;
transform.position.z = message.position.z;
transform.rotation = new Vector3();
transform.rotation.x = message.rotation.x;
transform.rotation.y = message.rotation.y;
transform.rotation.z = message.rotation.z;
player.transform = transform;
});
this.onMessage("onChangedState", (client, message) => {
const player = this.state.players.get(client.sessionId);
player.state = message.state;
player.subState = message.subState; // Character Controller V2
});
// Mannequin server code
this.ChangedItems = new Map<string, Map<string, string>>();
this.onMessage<CharacterItem[]>(this.MESSAGE_TYPE.OnChangedItem, (client, message) => {
// Overwrite clothes and set new parts
if (this.ChangedItems.has(client.userId)) {
const changedItemMap = this.ChangedItems.get(client.userId);
for (const characterItem of message) {
if (characterItem.property == Cloth.DRESS) {
// In the case of a dress (22), remove the top (19) and bottom (20)
if (changedItemMap.has(Cloth.TOP)) {
changedItemMap.delete(Cloth.TOP);
}
if (changedItemMap.has(Cloth.BOTTOM)) {
changedItemMap.delete(Cloth.BOTTOM);
}
} else if (characterItem.property == Cloth.TOP || characterItem.property == Cloth.BOTTOM) {
// Remove the dress if it is a top (19) or bottom (20)
if (changedItemMap.has(Cloth.DRESS)) {
changedItemMap.delete(Cloth.DRESS);
}
}
changedItemMap.set(characterItem.property,characterItem.id);
console.log(`OnChangedItem old ${client.userId} : ${characterItem.property} // ${characterItem.id}`);
}
} else {
// Initial registration
let changedItemMap: Map<string,string> = new Map<string, string>();
for (const characterItem of message) {
changedItemMap.set(characterItem.property,characterItem.id);
}
this.ChangedItems.set(client.sessionId,changedItemMap);
}
let changedItem: ChangedItem = new ChangedItem();
changedItem.sessionId = client.sessionId;
changedItem.characterItems = message;
console.log(`OnChangedItem : ${changedItem.sessionId}`);
for (const characterItem of changedItem.characterItems) {
console.log(` ::: ${characterItem.property} - ${characterItem.id} `);
}
this.broadcast(this.MESSAGE_TYPE.SyncChangedItem, changedItem, {except: client});
});
this.onMessage<string>(this.MESSAGE_TYPE.CheckChangedItem,(client, message) => {
if (false == this.ChangedItems.has(message)) {
return;
}
let changedItem: ChangedItem = new ChangedItem();
changedItem.sessionId = client.sessionId;
changedItem.characterItems = [];
for (const property of this.ChangedItems.get(message).keys()) {
let characterItem: CharacterItem = new CharacterItem();
characterItem.property = property;
characterItem.id = this.ChangedItems.get(message).get(property);
changedItem.characterItems.push(characterItem);
}
client.send<ChangedItem>(this.MESSAGE_TYPE.SyncChangedItem, changedItem );
});
}
onJoin(client: SandboxPlayer) {
// Create the player object defined in schemas.json and set the initial value.
console.log(`[OnJoin] sessionId : ${client.sessionId}, HashCode : ${client.hashCode}, userId : ${client.userId}`)
const player = new Player();
player.sessionId = client.sessionId;
if (client.userId) {
player.zepetoUserId = client.userId;
}
// Manage the Player object using sessionId, a unique key value of the client object.
// The client can check the information about the player object added by set by adding the add_OnAdd event to the players object.
this.state.players.set(client.sessionId, player);
}
async onLeave(client: SandboxPlayer, consented?: boolean) {
// By setting allowReconnection, it is possible to maintain connection for the circuit, but clean up immediately in the basic guide.
// The client can check the information about the deleted player object by adding the add_OnRemove event to the players object.
this.state.players.delete(client.sessionId);
if (this.ChangedItems.has(client.sessionId)) {
this.ChangedItems.delete(client.sessionId);
}
}
}- เปิดเซิร์ฟเวอร์ Multiplay เป็น On แล้วทดสอบ
ฟังก์ชันอีเวนต์
สามารถใช้ฟังก์ชันอีเวนต์ได้ตั้งแต่ ZEPETO Mannequin เวอร์ชัน 1.1.0 เป็นต้นไป
ZEPETO.Mannequin.Mannequin
การทำงาน | รายละเอียด |
|---|---|
public static OnSelectedItem: UnityEngine_Events.UnityEvent$2<ItemContent, boolean>; | จะถูกเรียกเมื่อเลือกไอเทมที่กำหนดในหน้าต่างซื้อคอสตูมบนหุ่น ItemContent ของไอเทมที่เลือกและสถานะการเลือกไอเทมจะเปลี่ยนไปเป็นค่า boolean |
public static OnSucceededPurchaseItems: UnityEngine_Events.UnityEvent$1<ItemContent[]>; | จะถูกเรียกเมื่อซื้อคอสตูมสำเร็จ ลิสต์ ItemContent ของข้อมูลไอเทมที่ซื้อจะถูกย้าย |
public static OnFailedPurchaseItems: UnityEngine_Events.UnityEvent$1<ItemContent[]>; | จะถูกเรียกเมื่อซื้อคอสตูมไม่สำเร็จ ลิสต์ ItemContent ของข้อมูลไอเทมที่ซื้อไม่สำเร็จจะถูกย้าย |
public static OnAppliedItems: UnityEngine_Events.UnityEvent$1<ItemContent[]>; | จะถูกเรียกใช้เมื่อเลือกว่าจะใส่ไอเทมที่ซื้อหลังจากซื้อคอสตูมสำเร็จ ลิสต์ ItemContent ของข้อมูลไอเทมที่สวมใส่จะถูกย้าย |
public static OnOpenedShopUI: UnityEngine_Events.UnityEvent$1<ItemContent[]>; | จะถูกเรียกเมื่อเปิดหน้าต่างซื้อคอสตูมบนหุ่น ลิสต์ ItemContent ของข้อมูลไอเทมในหน้าต่างซื้อจะถูกย้าย |
public static OnClosedShopUI: UnityEngine_Events.UnityEvent; | จะถูกเรียกเมื่อหน้าต่างซื้อคอสตูมบนหุ่นปิด |
ZEPETO.Mannequin.BasePreviewer
การทำงาน | รายละเอียด |
|---|---|
public OnChanged: UnityEngine_Events.UnityEvent$1<ZEPETO_Mannequin_BasePreviewer.ChangedValue[]>; | จะถูกเรียกเสมอ หากข้อมูลไอเทมที่กำลังสวมใส่เปลี่ยนไปจากการกดที่หุ่น รวมถึงลิสต์ OnChagedValue จะถูกย้าย |
ข้อมูลตัวแปร Class Member มีดังต่อไปนี้
public property : ZepetoPropertyFlag | ข้อมูลส่วนประกอบของคอสตูม |
|---|---|
public id : string | item ID |
Mannequin.WorldCamera
หากไม่ตั้งค่า Mannequin.WorldCamera ไว้ ไอคอน mannequin interaction และกล้องที่เชื่อมอยู่จะถูกจับเป็นกล้องที่มี Depth ตํ่า
หากต้องการตั้งค่ากล้องด้วยตนเอง สามารถใช้ Mannequin.WorldCamera ได้
Variable | รายละเอียด |
|---|---|
Mannequin.WorldCamera | ตัวแปรที่ช่วยให้สามารถตั้งค่ากล้องของไอคอน mannequin interaction ด้วยตัวเองได้ |
ตัวอย่างการตั้งค่า Mannequin.WorldCamera ใน MannequinScript เดิม
import { ZepetoScriptBehaviour } from 'ZEPETO.Script';
import { ZepetoPlayers, SpawnInfo } from 'ZEPETO.Character.Controller';
import { WorldService } from 'ZEPETO.World';
import { ItemContentsRequest, Mannequin, MannequinComponent, MannequinInteractable, MannequinPreviewer } from 'ZEPETO.Mannequin';
import { Object } from 'UnityEngine';
export default class MannequinScript extends ZepetoScriptBehaviour {
private _previewer: MannequinPreviewer;
Start() {
// Code that creates a ZEPETO character based on the logged in ID
// ZepetoPlayers.instance.CreatePlayerWithUserId(WorldService.userId, SpawnInfo.Default, true);
ZepetoPlayers.instance.OnAddedLocalPlayer.AddListener(() => {
const character = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer.character;
// Add Mannequin Interactable Component
character.gameObject.AddComponent<MannequinInteractable>();
// Code to set up the Mannequin Icon World Canvas Camera as the ZEPETO Camera
Mannequin.WorldCamera = ZepetoPlayers.instance.LocalPlayer.zepetoCamera.camera;
});
// Find all Mannequin components
const mannequins = Object.FindObjectsOfType<MannequinComponent>();
mannequins.forEach(m => {
// Enter the Collider
m.onActive.AddListener(contents => {
Mannequin.OpenUI(contents);
const zepetoContext = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer.character.Context;
this._previewer = new MannequinPreviewer(zepetoContext, contents);
this._previewer.PreviewContents();
});
// Exit the Collider
m.onCancel.AddListener(() => {
Mannequin.CloseUI();
this._previewer?.ResetContents();
});
});
}
}OnContentsLoaded API
A callback has been added that is triggered when all clothing items are fully loaded while using the Mannequin API to equip clothing items.
public OnContentsLoaded: UnityEngine_Events.UnityEvent$1<ZEPETO_Mannequin_BasePreviewer.ChangedValue[]>;An example of setting OnContentsLoaded in the existing MannequinScript is as follows.
import { ZepetoScriptBehaviour } from 'ZEPETO.Script';
import { ZepetoPlayers, SpawnInfo } from 'ZEPETO.Character.Controller';
import { WorldService } from 'ZEPETO.World';
import { ItemContentsRequest, Mannequin, MannequinComponent, MannequinInteractable, MannequinPreviewer } from 'ZEPETO.Mannequin';
import { Object } from 'UnityEngine';
export default class MannequinScript extends ZepetoScriptBehaviour {
private _previewer: MannequinPreviewer;
Start() {
// Code that creates a ZEPETO character based on the logged in ID
// ZepetoPlayers.instance.CreatePlayerWithUserId(WorldService.userId, SpawnInfo.Default, true);
ZepetoPlayers.instance.OnAddedLocalPlayer.AddListener(() => {
const character = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer.character;
// Add Mannequin Interactable Component
character.gameObject.AddComponent<MannequinInteractable>();
});
// Find all Mannequin components
const mannequins = Object.FindObjectsOfType<MannequinComponent>();
mannequins.forEach(m => {
// Enter the Collider
m.onActive.AddListener(contents => {
Mannequin.OpenUI(contents);
const zepetoContext = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer.character.Context;
this._previewer = new MannequinPreviewer(zepetoContext, contents);
// Adding listener to OnContentsLoaded event
this._previewer.OnContentsLoaded.AddListener((changedValues)=>{
console.log('Contents loaded:', changedValues);
});
this._previewer.PreviewContents();
});
// Exit the Collider
m.onCancel.AddListener(() => {
Mannequin.CloseUI();
this._previewer?.ResetContents();
});
});
}
}