[Unity] 컴포넌트 안쓰고 위치, 애니메이션 연동 (Oculus + Photon)
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();
}
}
}