원격 네트워크 유저에게 총알을 발사하는 로직을 만들어 보자. 앞서 이동 로직에서 구현했듯이 총알 프리팹에 PhotonView컴포넌트를 추가해 생상하면 아주 간단하게 만들수 있겠지만 이는 잘못된 방법이다.

PhotonView 컴포넌트는 초당 20회 데이터를 전송하기 때문에 스테이지에 많은 총알을 생성하면 생성된 모든 총알에서 트래픽이 발생하기 때문에 이런 이벤트성 동작을 네트워크 유저와 공유할 때는 RPC(Remote Procedure Calls)를 통해 구현하는 것이 일방적인 방식이다.

RPC : 원격 프로시저 호출은 물리적으로 떨어져 있는 다른 디바이스의 함수를 호출하는 기능으로 RPC함수를 호출하면 네트워크를 통해 다른 사용자의 스크립트에서 해당 함수가 호출된다. 비슷한 개념으로  RMI(Remote Method Invocation)가 있다.

총알로 사용할 Bullet은 미리 만들어진 프리팹을 사용한다. 임포트한 AngryBotResorces/Prefabs 하위의 Bullet 프리팹을 씬뷰로 드래그해 확인해보자. Bullet프리팹은 Capsulre Collider와 Rigidbody 컴포넌트로 구성된다.  BULLET태그룰 생성하고 지정한다.

Bullet Prefab원본에 Bullet 스크립트를 연결한다. 

Start()함수에서 Force로 발사  되게 만들고 충돌시 충돌지점에 스파크이펙트를 생성하는 스크립트이다.

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

public class Bullet : MonoBehaviour {
    public GameObject effect;
    // 총알을 발사한 플레이어의 고유번호
    public int actorNumber;

    void Start() {
        GetComponent<Rigidbody>().AddRelativeForce(Vector3.forward * 1000.0f);
        // 일정시간이 지난 후 총알을 삭제
        Destroy(this.gameObject, 3.0f);
    }

    void OnCollisionEnter(Collision coll) {
        // 충돌 지점 추출
        var contact = coll.GetContact(0);
        // 충돌 지점에 스파크 이펙트 생성
        var obj = Instantiate(effect,
                              contact.point,
                              Quaternion.LookRotation(-contact.normal));
        Destroy(obj, 2.0f);
        Destroy(this.gameObject);
    }
}

public GameObject effect는 AngryBotResources/Particles/Prefab하위의 Bullet_Impact_Wall프리팹을 연결하고 저장한다.

 

유니티를 실행시키면 총알이 발사된다. 그런 하이라키뷰의 총알은 지운다. 이제 총알을 발사할 Fire 스크립트를 생성한다.

void Update() {
    // 로컬 유저여부와 마우스 왼쪽 버튼을 클릭했을 때 총알을 발사
    if (pv.IsMine && isMouseClick) {
        FireBullet(pv.Owner.ActorNumber);
        //RPC로 원격지에 있는 함수를 호출
        pv.RPC("FireBullet", RpcTarget.Others, pv.Owner.ActorNumber);
    }
}

포톤서버에서 일반적인 RPC호출은 PhtoneView.RPC(호출함수명, 호출대상, 전달할 데이터) 함수를 사용한다. 원격으로 호출할 함수명 인자는 string 타입으로 전달하고 호출 대상은 특정 플레이어를 지정하거나 RpcTarget옵션으로 전달 대상의 범위를 지정할 수 있다.

  • All : 자기자신을 포함한 모든 유저에게 함수를 뿌림
  • Others : 자신을 제외한 모든 유저에게 함수를 뿌림
  • MasterClient : 방장에게 함수를 뿌림
  • AllBuffered : 자신을 포함한 모든유저에게 함수를 뿌림, 또한 나중에 입장한 유저는 버퍼에 저장된 RPC를 전달받는다.
  • OtherBuffered : 자신을 제외한 모든유저에게 함수를 뿌림, 나중에 입장한 유저는 버퍼에 저장된 RPC를 전달받는다.
  • AllViaServer : 모든 네트워크 유저에게 거의 동일한 시간에 RPC를 전송하기위해 서버의 모든클라이언트에게 RPC를 동시전송
  • AllBufferedViaServer : AllViaServer와 동일하며, 버퍼에 저장된 RPC를 나중에 입장한 유저에게 전달

총알 발사 로직에서 로컬 FireBullet함수를 호출하지 않고 다음과 같이 RpcTarget.All 옵션을 사용해도 동일한 결과를 볼 수 있다. RpcTarget.All은 Rpc함수를 룸에 입장한 모든 네트워크 유저에 대해 호출하고 로컬 유저는 해당 함수를 즉시 호출한다.

pv.RPC("FireBullet",RpcTarget.All, null);

RPC로 호출할때는 반드시 [PunRPC] 어트리뷰트를 함수앞에 명시해야한다.

[PunRPC]
void FireBullet(int actorNo) {
    // 총구화염 효과가 실행 중이 아닌 경우에 총구 화염효과 실행
    if (!muzzleFlash.isPlaying) muzzleFlash.Play(true);

    GameObject bullet = Instantiate(bulletPrefab,
                                    firePos.position,
                                    firePos.rotation);
    bullet.GetComponent<Bullet>().actorNumber = actorNo;
}

 

Fire.cs 전체코드

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

public class Fire : MonoBehaviour
{
    public Transform firePos;
    public GameObject bulletPrefab;
    private ParticleSystem muzzleFlash;

    private PhotonView pv;
    // 왼쪽 마우스 버튼 클릭 이벤트 저장
    private bool isMouseClick => Input.GetMouseButtonDown(0);

    void Start()
    {
        // 포톤뷰 컴포넌트 연결
        pv = GetComponent<PhotonView>();  
        // FirePos 하위에 있는 총구 화염 효과 연결  
        muzzleFlash = firePos.Find("MuzzleFlash").GetComponent<ParticleSystem>();
    }

    void Update()
    {
        // 로컬 유저여부와 마우스 왼쪽 버튼을 클릭했을 때 총알을 발사
        if (pv.IsMine && isMouseClick)
        {
            FireBullet(pv.Owner.ActorNumber);
            //RPC로 원격지에 있는 함수를 호출
            pv.RPC("FireBullet", RpcTarget.Others, pv.Owner.ActorNumber);
        }        
    }

    [PunRPC]
    void FireBullet(int actorNo)
    {
        // 총구화염 효과가 실행 중이 아닌 경우에 총구 화염효과 실행
        if (!muzzleFlash.isPlaying) muzzleFlash.Play(true);

        GameObject bullet = Instantiate(bulletPrefab,
                                        firePos.position,
                                        firePos.rotation);
        bullet.GetComponent<Bullet>().actorNumber = actorNo;
    }
}

작성된 스크립트를 Resources폴더에 있는 Player프리팹에 추가한다. Resources폴더에 있는 Player 프리팹을 선택한 다음 인스펙터 뷰에서 [Open Prefab] 버튼을 클릭한다. 프리팹 에디터 뷰로 진입하면 하이라키뷰에 있는 Gun/Fire Pos속성에 연결한다. Bullet Prefab 속성에는 프로젝트뷰의 AngryBotResources/Prefabs하위에 있는 Bullet프리팹을 연결한다.

* RPC호출 목적으로만 사용하려면 PhotonView컴포넌트의 Synchronization 속성을 Off로 설정해야 한다.

총알의 발사는 자신의 캐릭터에서만 동작해야 하기 때문에 PhotonView.IsMine 속성을 체크해야 한다. 자신의 경우 로컬 FireBullet함수를 호출하고 원격유저의 캐릭터는 RPC함수를 사용해 원격 FireBullet 함수를 호출한다.

게임을 빌드후 실행해 서로 총알을 발사해 확인해보자.

 

 

+ Recent posts