공부/Unity

[Unity] 컴포넌트 안쓰고 위치, 애니메이션 연동 (Oculus + Photon)

굴러다니다니 2025. 6. 24. 16:58

pun2 free + oculus integration (deprecated) 둘다 받아서 기본 설정 하기

xr setting, photon setting

 

넣고 main camera 삭제 

10 10 plain 넣기 > Ground

M_Ground 만들어서 넣어주기 (마음대로)

 

(Fantasy Chess RPG Character - Arthur 이용) > 불러오기 (model에 material 넣어줌 프리팹 왜 안쓰는진 모르겠음)

ㅎㅇ 아서 > scale 0.6으로 줄여줌

빈 오브젝트 > Player 자식 배치

카메라 3인칭 배치

만들어져있는 arthur animator 구경 (걷는중 공격 불가)

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;

public class PlayerMove : MonoBehaviourPun
{
    public float moveSpeed = 3.0f;
    public float rotSpeed = 200.0f;
    public GameObject cameraRig;
    public Transform myCharacter;
    public Animator anim;
    private void Update()
    {
        Move();
        Rotate();
    }
    private void Move()
    {
        //왼쪽 thumb stick의 방향값을 가져와 캐릭터의 이동 방향 정함
        Vector2 stickPos = OVRInput.Get(OVRInput.Axis2D.PrimaryThumbstick, OVRInput.Controller.LTouch);
        Vector3 dir = new Vector3(stickPos.x, 0, stickPos.y);
        dir.Normalize(); //정규화 why? 벡터의 방향을 유지하고 길이를 1로 만듦
        dir = cameraRig.transform.TransformDirection(dir);
        transform.position += dir * moveSpeed * Time.deltaTime;
        //왼쪽 thumb stick 기울이면 그 방향으로 캐릭터 회전
        float magnitude = dir.magnitude; //벡터의 길이(크기)
        if (magnitude > 0) //살짝이라도 기울였다면
        {
            myCharacter.rotation = Quaternion.LookRotation(dir);
        }
        anim.SetFloat("Speed", magnitude);
    }
    private void Rotate()
    { //컨트롤러로 카메라 좌우 회전
        float rotH = OVRInput.Get(OVRInput.Axis2D.PrimaryThumbstick, OVRInput.Controller.RTouch).x;
        cameraRig.transform.eulerAngles += new Vector3(0, rotH, 0) * rotSpeed * Time.deltaTime;
    }
}

PlayerMove.cs 생성 > 위에 코드 작성 > Player한테 넣어주기 > 인스펙터창에 연결

OVRInput.Get을 이용해 스틱의 방향을 가져올 수 있음

Player Resources 폴더에 넣기

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;

public class GameManager : MonoBehaviourPunCallbacks
{
    void Start()
    {
        Screen.SetResolution(960, 640, FullScreenMode.Windowed);
        PhotonNetwork.SendRate = 30; //데이터 송수신 빈도 매 초당 30회
        PhotonNetwork.SerializationRate = 30;
    }

    void Update()
    {
        
    }
}

GameManager.cs 생성 > 위에 코드 넣기

 

PlayerMove에 IPunObservable 추가 -> OnPhotonSerializeView로 연동하기 위해 (인터페이스 사용해서 필수로 써줘야됨)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;

public class PlayerMove : MonoBehaviourPun, IPunObservable
{
    public float moveSpeed = 3.0f;
    public float rotSpeed = 200.0f;
    public GameObject cameraRig;
    public Transform myCharacter;
    public Animator anim;

    //서버에서 받은 데이터를 저장할 변수들
    Vector3 setPos;
    Quaternion setRot;
    float dir_speed = 0;
    private void Start()
    {
        cameraRig.SetActive(photonView.IsMine); //로컬 카메라일때만 활성화 
    }
    private void Update()
    {
        Move();
        Rotate();
    }
    private void Move()
    {
        if (photonView.IsMine)
        {
            //왼쪽 thumb stick의 방향값을 가져와 캐릭터의 이동 방향 정함
            Vector2 stickPos = OVRInput.Get(OVRInput.Axis2D.PrimaryThumbstick, OVRInput.Controller.LTouch);
            Vector3 dir = new Vector3(stickPos.x, 0, stickPos.y);
            dir.Normalize(); //정규화 why? 벡터의 방향을 유지하고 길이를 1로 만듦
            dir = cameraRig.transform.TransformDirection(dir);
            transform.position += dir * moveSpeed * Time.deltaTime;
            //왼쪽 thumb stick 기울이면 그 방향으로 캐릭터 회전
            float magnitude = dir.magnitude; //벡터의 길이(크기)
            if (magnitude > 0) //살짝이라도 기울였다면
            {
                myCharacter.rotation = Quaternion.LookRotation(dir);
            }
            anim.SetFloat("Speed", magnitude);
        }
        else //원격 플레이어들의 좌표와 회전값으로 이동
        {
            transform.position = setPos;
            myCharacter.rotation = setRot;
            anim.SetFloat("Speed", dir_speed);
        }
    }
    private void Rotate()
    {
        if (photonView.IsMine)
        {
            //컨트롤러로 카메라 좌우 회전
            float rotH = OVRInput.Get(OVRInput.Axis2D.PrimaryThumbstick, OVRInput.Controller.RTouch).x;
            cameraRig.transform.eulerAngles += new Vector3(0, rotH, 0) * rotSpeed * Time.deltaTime;
        }
    }

    public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        if (stream.IsWriting) //송신
        {
            stream.SendNext(transform.position);
            stream.SendNext(myCharacter.transform.position);
            stream.SendNext(anim.GetFloat("Speed"));
        }
        else if (stream.IsReading)  //수신
        {
            setPos = (Vector3)stream.ReceiveNext();
            setRot = (Quaternion)stream.ReceiveNext();
            dir_speed = (float)stream.ReceiveNext();
        }
    }
}

*OnPhotonSerializeView를 사용해 연동하면, 가끔 네트워크 지연이 발생되면 그거만큼 끊겨서 반영됨. 그거 처리 해줘야됨

        else //원격 플레이어들의 좌표와 회전값으로 이동
        {
            /*
            transform.position = setPos;
            myCharacter.rotation = setRot;
            anim.SetFloat("Speed", dir_speed);
            */
            transform.position = Vector3.Lerp(transform.position, setPos, Time.deltaTime);
            myCharacter.rotation = Quaternion.Lerp(myCharacter.rotation, setRot, Time.deltaTime);
            anim.SetFloat("Speed", dir_speed);
        }

Move를 바로 대입이 아닌 Lerp를 이용해 처리하면 자연스러워진다

 

Player 자식으로 Canvas 생성 후 위와같이 설정 (World space와 transform)

 

Text 만들어서 알아서 설정

 

PlayerMove에 이름 Text 변수 만들고 

Start문으로 닉네임 연결

 

나는 녹색 나머지는 빨간색

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using UnityEngine.UI;

public class PlayerMove : MonoBehaviourPun, IPunObservable
{
    public float moveSpeed = 3.0f;
    public float rotSpeed = 200.0f;
    public GameObject cameraRig;
    public Transform myCharacter;
    public Animator anim;
    public Text nameText; //플레이어 이름 표시

    //서버에서 받은 데이터를 저장할 변수들
    Vector3 setPos;
    Quaternion setRot;
    float dir_speed = 0;
    private void Start()
    {
        cameraRig.SetActive(photonView.IsMine); //로컬 카메라일때만 활성화 
        nameText.text = photonView.Owner.NickName;
        if (photonView.IsMine) nameText.color = new Color(0.1179245f, 1, 0.2670011f, 1);
        else nameText.color = Color.red;
    }
    private void Update()
    {
        Move();
        Rotate();
    }
    private void Move()
    {
        if (photonView.IsMine)
        {
            //왼쪽 thumb stick의 방향값을 가져와 캐릭터의 이동 방향 정함
            Vector2 stickPos = OVRInput.Get(OVRInput.Axis2D.PrimaryThumbstick, OVRInput.Controller.LTouch);
            Vector3 dir = new Vector3(stickPos.x, 0, stickPos.y);
            dir.Normalize(); //정규화 why? 벡터의 방향을 유지하고 길이를 1로 만듦
            dir = cameraRig.transform.TransformDirection(dir);
            transform.position += dir * moveSpeed * Time.deltaTime;
            //왼쪽 thumb stick 기울이면 그 방향으로 캐릭터 회전
            float magnitude = dir.magnitude; //벡터의 길이(크기)
            if (magnitude > 0) //살짝이라도 기울였다면
            {
                myCharacter.rotation = Quaternion.LookRotation(dir);
            }
            anim.SetFloat("Speed", magnitude);
        }
        else //원격 플레이어들의 좌표와 회전값으로 이동
        {
            /*
            transform.position = setPos;
            myCharacter.rotation = setRot;
            anim.SetFloat("Speed", dir_speed);
            */
            transform.position = Vector3.Lerp(transform.position, setPos, Time.deltaTime);
            myCharacter.rotation = Quaternion.Lerp(myCharacter.rotation, setRot, Time.deltaTime);
            anim.SetFloat("Speed", dir_speed);
        }
    }
    private void Rotate()
    {
        if (photonView.IsMine)
        {
            //컨트롤러로 카메라 좌우 회전
            float rotH = OVRInput.Get(OVRInput.Axis2D.PrimaryThumbstick, OVRInput.Controller.RTouch).x;
            cameraRig.transform.eulerAngles += new Vector3(0, rotH, 0) * rotSpeed * Time.deltaTime;
        }
    }

    public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        if (stream.IsWriting) //송신
        {
            stream.SendNext(transform.position);
            stream.SendNext(myCharacter.transform.rotation);
            stream.SendNext(anim.GetFloat("Speed"));
        }
        else if (stream.IsReading)  //수신
        {
            setPos = (Vector3)stream.ReceiveNext();
            setRot = (Quaternion)stream.ReceiveNext();
            dir_speed = (float)stream.ReceiveNext();
        }
    }
}

전체 코드

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Billboard : MonoBehaviour
{
    public Transform canvas;
    private void Update()
    {
        canvas.forward = Camera.main.transform.forward;
    }
}

플레이어 캔버스가 카메라 보게 하는 스크립트 Billboard.cs 작성 > Player에 붙여서 인스펙터창에 연결

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;

public class PlayerAttack : MonoBehaviourPun
{
    public Animator anim;
    private void Update()
    {
        if(OVRInput.GetDown(OVRInput.Button.PrimaryIndexTrigger, OVRInput.Controller.RTouch))
        {
            if (photonView.IsMine)
            {
                photonView.RPC("AttackAnimation", RpcTarget.AllBuffered);
            }
        }
    }
    [PunRPC]
    public void AttackAnimation() //photon animation view를 안써서 사용
    {
        anim.SetTrigger("Attack");
    }
}

Photon Animation View를 사용하지 않아서, 이를 구현하기 위해 PlayerAttack.cs을 위에처럼 만들어준다 > Player에 넣어주고 인스펙터 연결

 

*RpcTarget.All vs RpcTarget.AllBuffered 차이

All: 접속한 후에 실행한 이벤트만 보임

AllBuffered: 뒤늦게 접속한 상대에게도 서버에 저장되니 이벤트 발생 시간을 기점으로 함수 실행

 

Handle 없애고 Interactable 끈 HP Slider 만들기

플레이어 크기에 맞게 Collider 추가후 크기 알아서 맞게 설정

 

Rigidbody 추가하고 저렇게 고정

 

해당 무기가 존재하는 곳에 BoxCollider를 추가하고 IsTrigger 체크, 사이즈도 조절

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class WeaponCollider : MonoBehaviour
{
    public BoxCollider weaponCol; //검의 콜라이더
    private void Start()
    {
        DeActivateCollider(); //공격 모션 할 때만 키게
    }
    public void ActivateCollider()
    {
        weaponCol.enabled = true;
    }
    public void DeActivateCollider()
    {
        weaponCol.enabled = false;
    }
}

WeaponCollider.cs에 해당 내용 넣어서 생성 > Arthur_01에 스크립트 넣음 > Weapon boxcollider 인스펙터창에 연결

한 애니메이션에서도 검이 찔렀을 때에만 작동하게 하고 싶어서 스크립트로 뺐음

 

Attack 모션이 방패 막고 검으로 찌르는건데 저 26프레임에 콜라이더가 켜지면 좋겠는데요~

자세히 보면 작은 event로 Active Collider가 있는데

Animation clip 이 ReadOnly가 아니면 저 부분 눌러서 함수 연결로도 가능하다고 하는데 ReadOnly라 넘어가심

 

(내가 혼자 해 본 부분) Readonly 풀어보니까 이렇게 뜨는데

저기서 이렇게 연결하면 되는듯

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using UnityEngine.UI;

public class PlayerAttack : MonoBehaviourPun
{
    public Animator anim;
    public float maxHp = 10; //최대 체력
    public float attackPower = 2; //공격력
    public Slider hpSlider;
    public BoxCollider weaponCol;
    float curHp = 0; //현재 체력
    private void Start()
    {
        curHp = maxHp;
        hpSlider.value = curHp / maxHp;
    }
    private void Update()
    {
        if(OVRInput.GetDown(OVRInput.Button.PrimaryIndexTrigger, OVRInput.Controller.RTouch))
        {
            if (photonView.IsMine)
            {
                photonView.RPC("AttackAnimation", RpcTarget.AllBuffered);
            }
        }
    }
    [PunRPC]
    public void AttackAnimation() //photon animation view를 안써서 사용
    {
        anim.SetTrigger("Attack");
    }
    [PunRPC]
    public void Damaged(float pow)
    {
        curHp = Mathf.Max(0, curHp - pow);
        hpSlider.value = curHp / pow;
    }
    private void OnTriggerEnter(Collider other)
    {
        if (photonView.IsMine && other.gameObject.name.Contains("Player")){
            PhotonView pv = other.GetComponent<PhotonView>();
            pv.RPC("Damaged", RpcTarget.AllBuffered, attackPower);
            weaponCol.enabled = false;
        }
    }
}

PlayerAttack.cs 위에처럼 수정

 

PlayerAttack에 인스펙터 연결, Player에 Photon view 추가 > Player Prefab override Apply all

Player 하이어라키창에서 삭제

Create Empty > GameManager 스크립트 연결

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;

public class LobbyManager : MonoBehaviourPunCallbacks
{
    // Start is called before the first frame update
    void Start()
    {
        PhotonNetwork.GameVersion = "0.1";
        int num = Random.Range(0, 1000);
        PhotonNetwork.NickName = "Player" + num;
        PhotonNetwork.AutomaticallySyncScene = true;
        PhotonNetwork.ConnectUsingSettings();
    }
    public override void OnConnectedToMaster()
    {
        PhotonNetwork.JoinLobby(TypedLobby.Default);
    }
    public override void OnJoinedLobby()
    {
        RoomOptions ro = new RoomOptions()
        {
            IsVisible = true,
            IsOpen = true,
            MaxPlayers = 8
        };
        PhotonNetwork.JoinOrCreateRoom("NetTest", ro, TypedLobby.Default);
    }
    public override void OnJoinedRoom()
    {
        Vector2 originPos = Random.insideUnitSphere*2f;
        PhotonNetwork.Instantiate("Player", new Vector3(originPos.x, 0,
            originPos.y), Quaternion.identity);
    }
    // Update is called once per frame
    void Update()
    {
        
    }
}

Create Empty > LobbyManager > 저거 넣어주기

 

* 오류때문에 임시로 주석 해제

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using UnityEngine.UI;

public class PlayerMove : MonoBehaviourPun, IPunObservable
{
    public float moveSpeed = 3.0f;
    public float rotSpeed = 200.0f;
    public GameObject cameraRig;
    public Transform myCharacter;
    public Animator anim;
    public Text nameText; //플레이어 이름 표시

    //서버에서 받은 데이터를 저장할 변수들
    Vector3 setPos;
    Quaternion setRot;
    float dir_speed = 0;
    private void Start()
    {
        cameraRig.SetActive(photonView.IsMine); //로컬 카메라일때만 활성화 
        nameText.text = photonView.Owner.NickName;
        if (photonView.IsMine) nameText.color = new Color(0.1179245f, 1, 0.2670011f, 1);
        else nameText.color = Color.red;
    }
    private void Update()
    {
        Move();
        Rotate();
    }
    private void Move()
    {
        if (photonView.IsMine)
        {
            //왼쪽 thumb stick의 방향값을 가져와 캐릭터의 이동 방향 정함
            Vector2 stickPos = OVRInput.Get(OVRInput.Axis2D.PrimaryThumbstick, OVRInput.Controller.LTouch);
            Vector3 dir = new Vector3(stickPos.x, 0, stickPos.y);
            dir.Normalize(); //정규화 why? 벡터의 방향을 유지하고 길이를 1로 만듦
            dir = cameraRig.transform.TransformDirection(dir);
            transform.position += dir * moveSpeed * Time.deltaTime;
            //왼쪽 thumb stick 기울이면 그 방향으로 캐릭터 회전
            float magnitude = dir.magnitude; //벡터의 길이(크기)
            if (magnitude > 0) //살짝이라도 기울였다면
            {
                myCharacter.rotation = Quaternion.LookRotation(dir);
            }
            anim.SetFloat("Speed", magnitude);
        }
        else //원격 플레이어들의 좌표와 회전값으로 이동
        {
            transform.position = setPos;
            myCharacter.rotation = setRot;
            anim.SetFloat("Speed", dir_speed);
            /*
            transform.position = Vector3.Lerp(transform.position, setPos, Time.deltaTime * 20f);
            myCharacter.rotation = Quaternion.Lerp(myCharacter.rotation, setRot, Time.deltaTime * 20f);
            anim.SetFloat("Speed", dir_speed);
            */
        }
    }
    private void Rotate()
    {
        if (photonView.IsMine)
        {
            //컨트롤러로 카메라 좌우 회전
            float rotH = OVRInput.Get(OVRInput.Axis2D.PrimaryThumbstick, OVRInput.Controller.RTouch).x;
            cameraRig.transform.eulerAngles += new Vector3(0, rotH, 0) * rotSpeed * Time.deltaTime;
        }
    }

    public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        if (stream.IsWriting) //송신
        {
            stream.SendNext(transform.position);
            stream.SendNext(myCharacter.transform.rotation);
            stream.SendNext(anim.GetFloat("Speed"));
        }
        else if (stream.IsReading)  //수신
        {
            setPos = (Vector3)stream.ReceiveNext();
            setRot = (Quaternion)stream.ReceiveNext();
            dir_speed = (float)stream.ReceiveNext();
        }
    }
}
728x90
반응형