제페토 월드 만들기 (회전 동기화)
Zepeto 2022. 4. 11. 17:20
이동중 회전하기
https://github.com/smilejsu82/ZepetoMultiTest
import {ZepetoScriptBehaviour} from 'ZEPETO.Script'
import {ZepetoWorldMultiplay} from 'ZEPETO.World'
import {Room, RoomData} from 'ZEPETO.Multiplay'
import {Player, State, Vector3} from 'ZEPETO.Multiplay.Schema'
import {CharacterState, SpawnInfo, ZepetoPlayers, ZepetoPlayer} from 'ZEPETO.Character.Controller'
import * as UnityEngine from "UnityEngine";
import PlayerController from './PlayerController'
import res_OnLookAtTarget from './res_OnLookAtTarget'
export default class Starter extends ZepetoScriptBehaviour {
public multiplay: ZepetoWorldMultiplay;
private room: Room;
private currentPlayers: Map<string, Player> = new Map<string, Player>();
private Start() {
this.multiplay.RoomCreated += (room: Room) => {
this.room = room;
this.room.AddMessageHandler('onLookAtTarget', (message)=>{
var res = message as res_OnLookAtTarget;
var fromId : string = res.fromId;
var toId : string = res.toId;
console.log(fromId, toId);
});
};
this.multiplay.RoomJoined += (room: Room) => {
room.OnStateChange += this.OnStateChange;
};
this.StartCoroutine(this.SendMessageLoop(0.1));
}
// 일정 Interval Time으로 내(local)캐릭터 transform을 server로 전송합니다.
private* SendMessageLoop(tick: number) {
while (true) {
yield new UnityEngine.WaitForSeconds(tick);
if (this.room != null && this.room.IsConnected) {
const hasPlayer = ZepetoPlayers.instance.HasPlayer(this.room.SessionId);
if (hasPlayer) {
const myPlayer = ZepetoPlayers.instance.GetPlayer(this.room.SessionId);
if (myPlayer.character.CurrentState != CharacterState.Idle)
{
this.SendTransform(myPlayer.character.transform);
//this.SendLookAt(fromId, toId);
}
}
}
}
}
private localZepetoPlayer : ZepetoPlayer;
private OnStateChange(state: State, isFirst: boolean) {
// 첫 OnStateChange 이벤트 수신 시, State 전체 스냅샷을 수신합니다.
if (isFirst) {
// [CharacterController] (Local)Player 인스턴스가 Scene에 완전히 로드되었을 때 호출
ZepetoPlayers.instance.OnAddedLocalPlayer.AddListener(() => {
this.localZepetoPlayer = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer;
var playerController = this.localZepetoPlayer.character.gameObject.AddComponent<PlayerController>();
playerController.Init(this.room.SessionId, this.room);
playerController.findTargetAction = (fromId, toId)=>{
ZepetoPlayers.instance.GetPlayer(fromId).character.gameObject.GetComponent<PlayerController>().SetTarget(toId);
};
this.localZepetoPlayer.character.OnChangedState.AddListener((cur, prev) => {
this.SendState(cur);
});
});
// [CharacterController] Player 인스턴스가 Scene에 완전히 로드되었을 때 호출
ZepetoPlayers.instance.OnAddedPlayer.AddListener((sessionId: string) => {
const isLocal = this.room.SessionId === sessionId;
if (!isLocal) {
const player: Player = this.currentPlayers.get(sessionId);
var zepetoPlayer = ZepetoPlayers.instance.GetPlayer(sessionId);
var playerController = zepetoPlayer.character.gameObject.AddComponent<PlayerController>();
playerController.Init(sessionId, null);
playerController.findTargetAction = (fromId, toId)=>{
ZepetoPlayers.instance.GetPlayer(fromId).character.gameObject.GetComponent<PlayerController>().SetTarget(toId);
// ZepetoPlayers.instance.GetPlayer(fromId).character.transform.LookAt(
// ZepetoPlayers.instance.GetPlayer(toId).character.transform
// );
};
// [RoomState] player 인스턴스의 state가 갱신될 때마다 호출됩니다.
player.OnChange += (changeValues) => this.OnUpdatePlayer(sessionId, player);
}
});
}
let join = new Map<string, Player>();
let leave = new Map<string, Player>(this.currentPlayers);
state.players.ForEach((sessionId: string, player: Player) => {
if (!this.currentPlayers.has(sessionId)) {
join.set(sessionId, player);
}
leave.delete(sessionId);
});
// [RoomState] Room에 입장한 player 인스턴스 생성
join.forEach((player: Player, sessionId: string) => this.OnJoinPlayer(sessionId, player));
// [RoomState] Room에서 퇴장한 player 인스턴스 제거
leave.forEach((player: Player, sessionId: string) => this.OnLeavePlayer(sessionId, player));
}
private * WaitForLoadLocalPlayer(callback){
while(true){
if(this.localZepetoPlayer){
break;
}
yield null;
}
callback();
}
private OnJoinPlayer(sessionId: string, player: Player) {
console.log(`[OnJoinPlayer] players - sessionId : ${sessionId}`);
this.currentPlayers.set(sessionId, player);
const spawnInfo = new SpawnInfo();
const position = this.ParseVector3(player.transform.position);
const rotation = this.ParseVector3(player.transform.rotation);
spawnInfo.position = position;
spawnInfo.rotation = UnityEngine.Quaternion.Euler(rotation);
const isLocal = this.room.SessionId === player.sessionId;
ZepetoPlayers.instance.CreatePlayerWithUserId(sessionId, player.zepetoUserId, spawnInfo, isLocal);
}
private OnLeavePlayer(sessionId: string, player: Player) {
console.log(`[OnRemove] players - sessionId : ${sessionId}`);
this.currentPlayers.delete(sessionId);
ZepetoPlayers.instance.RemovePlayer(sessionId);
}
private OnUpdatePlayer(sessionId: string, player: Player) {
console.log('OnUpdatePlayer');
const position = this.ParseVector3(player.transform.position);
const zepetoPlayer = ZepetoPlayers.instance.GetPlayer(sessionId);
zepetoPlayer.character.MoveToPosition(position);
if (player.state === CharacterState.JumpIdle || player.state === CharacterState.JumpMove)
zepetoPlayer.character.Jump();
}
private SendTransform(transform: UnityEngine.Transform) {
const data = new RoomData();
const pos = new RoomData();
pos.Add("x", transform.localPosition.x);
pos.Add("y", transform.localPosition.y);
pos.Add("z", transform.localPosition.z);
data.Add("position", pos.GetObject());
const rot = new RoomData();
rot.Add("x", transform.localEulerAngles.x);
rot.Add("y", transform.localEulerAngles.y);
rot.Add("z", transform.localEulerAngles.z);
data.Add("rotation", rot.GetObject());
this.room.Send("onChangedTransform", data.GetObject());
}
private SendLookAt(fromId:string, toId:string){
const data = new RoomData();
data.Add("fromId", fromId);
data.Add("toId", toId);
this.room.Send("onLookAtTarget", data.GetObject());
}
private SendState(state: CharacterState) {
const data = new RoomData();
data.Add("state", state);
this.room.Send("onChangedState", data.GetObject());
}
private ParseVector3(vector3: Vector3): UnityEngine.Vector3 {
return new UnityEngine.Vector3
(
vector3.x,
vector3.y,
vector3.z
);
}
}
import { ZepetoScriptBehaviour } from 'ZEPETO.Script'
import {Room, RoomData} from 'ZEPETO.Multiplay'
import { CharacterController, GameObject, Physics, Vector3, LayerMask } from 'UnityEngine';
import { ZepetoPlayers } from 'ZEPETO.Character.Controller';
import { Action$2 } from 'System'
export default class PlayerController extends ZepetoScriptBehaviour {
private room : Room;
private sessionId: string;
private targetSessionId : string;
private targetGo : GameObject;
private center : Vector3;
public findTargetAction : Action$2<string, string>;
public Init(sessionId: string, room : Room){
this.sessionId = sessionId;
this.room = room;
this.center = this.GetComponent<CharacterController>().bounds.center;
this.gameObject.layer = LayerMask.NameToLayer("Player");
}
public IsLocal(){
return this.room != null;
}
public GetSetssionId()
{
return this.sessionId;
}
public SetTarget(targetSessionId : string){
this.targetSessionId = targetSessionId;
var targetZepetoPlayer = ZepetoPlayers.instance.GetPlayer(this.targetSessionId);
this.targetGo = targetZepetoPlayer.character.gameObject;
}
Update(){
var colliders = Physics.OverlapSphere(this.center, 3.0, 1 << 21);
for(var i = 0; i<colliders.length; i++){
if( colliders[i]!= null && colliders[i].gameObject != this.gameObject){
this.findTargetAction(this.sessionId, colliders[i].gameObject.GetComponent<PlayerController>().GetSetssionId());
break;
}
}
}
LateUpdate(){
if(this.targetGo){
this.transform.LookAt(this.targetGo.transform);
}
}
}
import { ZepetoScriptBehaviour } from 'ZEPETO.Script'
export default class res_OnLookAtTarget{
public sessionId : string;
public fromId : string;
public toId : string;
}
import {Sandbox, SandboxOptions, SandboxPlayer} from "ZEPETO.Multiplay";
import {DataStorage} from "ZEPETO.Multiplay.DataStorage";
import {Player, Transform, Vector3} from "ZEPETO.Multiplay.Schema";
export default class extends Sandbox {
storageMap:Map<string,DataStorage> = new Map<string, DataStorage>();
constructor() {
super();
}
onCreate(options: SandboxOptions) {
// Room 객체가 생성될 때 호출됩니다.
// Room 객체의 상태나 데이터 초기화를 처리 한다.
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("onLookAtTarget", (client, message) => {
//const player = this.state.players.get(client.sessionId);
//player.state = message.state;
var res = {
sessionId : client.sessionId,
fromId : message.fromId,
toId : message.toId
};
this.broadcast('onLookAtTarget', res);
});
this.onMessage("onChangedState", (client, message) => {
const player = this.state.players.get(client.sessionId);
player.state = message.state;
});
}
async onJoin(client: SandboxPlayer) {
// schemas.json 에서 정의한 player 객체를 생성 후 초기값 설정.
console.log(`[OnJoin] sessionId : ${client.sessionId}, HashCode : ${client.hashCode}, userId : ${client.userId}`)
const player = new Player();
player.sessionId = client.sessionId;
if (client.hashCode) {
player.zepetoHash = client.hashCode;
}
if (client.userId) {
player.zepetoUserId = client.userId;
}
// [DataStorage] 입장한 Player의 DataStorage Load
const storage: DataStorage = client.loadDataStorage();
this.storageMap.set(client.sessionId,storage);
let visit_cnt = await storage.get("VisitCount") as number;
if (visit_cnt == null) visit_cnt = 0;
console.log(`[OnJoin] ${client.sessionId}'s visiting count : ${visit_cnt}`)
// [DataStorage] Player의 방문 횟수를 갱신한다음 Storage Save
await storage.set("VisitCount", ++visit_cnt);
// client 객체의 고유 키값인 sessionId 를 사용해서 Player 객체를 관리.
// set 으로 추가된 player 객체에 대한 정보를 클라이언트에서는 players 객체에 add_OnAdd 이벤트를 추가하여 확인 할 수 있음.
this.state.players.set(client.sessionId, player);
}
onTick(deltaTime: number): void {
// 서버에서 설정된 타임마다 반복적으로 호출되며 deltaTime 을 이용하여 일정한 interval 이벤트를 관리할 수 있음.
}
async onLeave(client: SandboxPlayer, consented?: boolean) {
// allowReconnection 설정을 통해 순단에 대한 connection 유지 처리등을 할 수 있으나 기본 가이드에서는 즉시 정리.
// delete 된 player 객체에 대한 정보를 클라이언트에서는 players 객체에 add_OnRemove 이벤트를 추가하여 확인 할 수 있음.
this.state.players.delete(client.sessionId);
}
}
'Zepeto' 카테고리의 다른 글
제페토 월드 모바일 접속 안될때 방화벽 해제 (0) | 2022.04.14 |
---|---|
유니티로 만드는 제페토 월드 (회전 동기화) (0) | 2022.04.13 |
찰칵 (0) | 2022.03.31 |
Zombie.ts (0) | 2022.03.31 |
zepeto world orientation horizontal (0) | 2022.03.25 |