Photon을 이용한 멀티 TPS
- 프로젝트 명: Photon TPS
- 개발 기간: 약 2주
- Photon을 사용하여 멀티 플레이 게임 구현해보기
게임 조작
- WASD로 캐릭터를 이동한다.
- 마우스로 조준 및 사격한다.
- Space Bar로 점프할 수 있다.
구조
-
일반적으로 시작 > 로비 > 방 > 게임 순서를 갖고 있다.
-
시작 시 닉네임을 입력한다.
- 로비에서 플레이어는 방을 개설하거나 참여할 수 있다.
- 사용자 1의 시점
-
사용자 1이 방을 개설한 후 사용자 2의 시점
-
방에서는 플레이어간 채팅이 가능하고 게임을 시작할 수 있다.
- 게임 중에는 체력, 탄창 및 사격을 구현하였다.
코드
- 시작: 포톤에서 제공하는 콜백 함수를 사용하여 멀티 플레이를 시작한다.
public void Connect()
{
if (nicknameField.text.Equals(string.Empty))
{
PhotonNetwork.NickName = "User " + Random.Range(0, 1000);
}
PhotonNetwork.NickName = nicknameField.text;
PhotonNetwork.ConnectUsingSettings();
}
// 포톤 서버에 접속 후 호출되는 콜백 함수
public override void OnConnectedToMaster()
{
PhotonNetwork.JoinLobby();
}
// 로비에 접속 후 호출되는 콜백 함수
public override void OnJoinedLobby()
{
PhotonNetwork.LoadLevel("Lobby");
}
- 로비: 플레이어가 개설한 방을 로비에서 확인할 수 있도록 갱신하는 함수를 ILobbyCallbacks를 사용하여 구현한다.
public override void OnRoomListUpdate(List<RoomInfo> roomList)
{
int roomCount = roomList.Count;
for (int i = 0; i < roomCount; i++)
{
if (!roomList[i].RemovedFromList)
{
if (!_myList.Contains(roomList[i])) _myList.Add(roomList[i]);
else _myList[_myList.IndexOf(roomList[i])] = roomList[i];
}
else if (_myList.IndexOf(roomList[i]) != -1) _myList.RemoveAt(_myList.IndexOf(roomList[i]));
}
MyListRenewal();
}
- 방: 플레이어간 채팅이 가능하도록 RPC를 이용하여 구현한다.
public void Send()
{
if (sendField.text == "")
{
return;
}
pv.RPC("ChatRPC", RpcTarget.All, "<color=green>" + PhotonNetwork.NickName + "</color>" + " : " + sendField.text);
sendField.text = "";
}
[PunRPC] // RPC는 플레이어가 속해있는 방 모든 인원에게 전달한다
void ChatRPC(string msg)
{
bool isInput = false;
for (int i = 0; i < text.Length; i++)
if (text[i].text == "")
{
isInput = true;
text[i].text = msg;
break;
}
if (!isInput) // 꽉차면 한칸씩 위로 올림
{
for (int i = 1; i < text.Length; i++) text[i - 1].text = text[i].text;
text[text.Length - 1].text = msg;
}
}
RPC란?
원격 클라이언트에 있는 메소드를 호출 하는 것입니다. [PunRPC] 속성을 적용해야 합니다. RPC로 표기된 함수를 호출하려면 PhotonView 컴포넌트가 필요합니다.
출처
- 게임: 캐릭터 조작은 Input System을 통해 구현하고, 사격과 재장전은 RPC로 선언하여 모든 플레이어가 확인할 수 있도록 한다.
public void OnShot(InputAction.CallbackContext context)
{
if (!photonView.IsMine) return;
if (_isRunning) return;
if (context.performed) // Action type이 "Button"일 경우 키가 눌렸는지 확인함
{
_animator.SetBool("isFire", true);
pv.RPC("Fire", RpcTarget.All);
}
else
{
_animator.SetBool("isFire", false);
}
}
[PunRPC]
private void Fire()
{
if (playerInfo.curAmmo > 0)
{
playerInfo.curAmmo--;
GameObject spawnedBullet = Instantiate(bullet, muzzle.position, muzzle.rotation);
spawnedBullet.GetComponent<Rigidbody>().AddForce(aimPoint.forward * 2500f);
}
}
public void OnReload(InputAction.CallbackContext context)
{
if (!photonView.IsMine) return;
if (_isRunning) return;
pv.RPC("Reload", RpcTarget.All);
}
[PunRPC]
public void Reload()
{
if (playerInfo.remainedAmmo > 0)
{
if (playerInfo.curAmmo >= playerInfo.MaxAmmo) return;
int insertAmmo = playerInfo.MaxAmmo - playerInfo.curAmmo;
if (playerInfo.remainedAmmo <= insertAmmo)
{
playerInfo.curAmmo += playerInfo.remainedAmmo;
playerInfo.remainedAmmo = 0;
}
else
{
playerInfo.curAmmo += insertAmmo;
playerInfo.remainedAmmo -= insertAmmo;
}
_animator.SetTrigger("isReload");
}
}
마치며
Photon을 이용하여 실시간 채팅과 멀티 플레이를 구현하였다.