Game > GameAnvil > 클라이언트 개발 가이드 > 레퍼런스 클라이언트

다운받기

샘플 개발 환경

  • Unity3d : 2018.4.1f1
    • Unity Standalone으로 제작되었습니다. 본 샘플은 개발 참고용으로서 Editor 환경에서만 동작이 확인 되었습니다.
  • GameAnvil Connector : 1.1.0.0

Connector API doc - C

실행환경 설정 with Unity3d

  • Git 저장소에서 클론 한 프로젝트를 Unity3d로 실행
  • image.png
  • sample_game_client_unity 설정확인
    • GameAnvil Library - 필요한 라이브러리 확인
      • Assets/GameAnvil
        • image.png
        • GameAnvil
        • Google.Protobuf
        • K4os.Compression.LZ4
        • log4net
        • SingleServer
        • SuperSocket.ClientEngine
        • System.Buffers
        • System.Memory
        • System.Runtime.CompilerServices.Unsafe

클라이언트 실행 with Unity3d

  • Unity3d 실행
    • StartScenene 선랙으로 왼쪽아래 화면처럼 신을 선택 합니다.
      • image.png
    • Unity 프로젝트의 가운데 Play 버튼 으로 클라이언트 실행합니다.
  • 클라이언트 정상 동작 확인
    • Gamebase 연동 되어 있어서 시작하자마자 초기화와 게스트 로그인 처리가 되어 아래에 정보가 표시됩니다.
    • log console에 에러가 없으면 정상 동작

sample_game_client_unity 폴더 구조살펴보기

  • 게임개발에 참고 할 수있게 만든 GameAnvil샘플 서버와 연동하기 위한 클라이언트로 제작된 프로젝트 입니다.
    • 기능 사용성에 목적을 두어서 게임자체에대한 에러나 버그가 많을수 있습니다.
    • 참고하시고 불편하시거나 필요하거나 궁금한 부분은 언제든지 문의 해주세요.

Assets

GameAnvil : GameAnvil에서 사용 하는 Library 위치


GameAnvilSample : GameAnvil Sample 폴더

  • StartScene : 처음 시작하는 화면으로 기본 Gamebase초기화와 게스트로그인처리, 플랫폼테스트와 게임테스트 분기
  • image.png
  • LoadingScene : 화면이 변경될때마다 보이는 로딩
  • image.png
Data : 데이터성 객체 폴더
GameTest : 게임테스트 폴더
Scenes : 게임테스트 화면 폴더
  • GameLoginScene : 아이디 입력 받아 전체적인 로그인 처리하는 화면
  • image.png
  • GameLobbyScene  : 유저가 로그인하고나서의 화면, TapBird(1인), 멀티TapBird(4인), Snake(2인) 게임과, 랭킹, 유저 정보, 닉네임 변경를 확인
  • image.png
PlatformTest : GameAnvil API Test 폴더
Scenes : GameAnvil API 화면 폴더
  • AuthScene ; launching(rest), connect, auth, login 처리하는 화면
  • image.png image.png image.png image.png
  • LobbyScene : 로그인이후 게임 로비 화면, 싱글게임, 룸매치 멀티(4인), 유저매치(2), 랭킹, 셔플덱 가능 화면
  • image.png
  • MultiSnakeGameScene : 유저 매치 2인 게임 화면
  • image.png  image.png
  • MultiTapBirdGameScene : 룸매치 4명 게임 화면
  • image.png
  • SingleGameScene : 싱글룸 게임 화면
  • image.png image.png
Protocols : 서버와 통신할 프로토콜 폴더
Snake : Snake 게임 폴더, 유저 매치 게임, 2인 동시에 서버에서 보내준 food를 화면에보여주고, 유저의 이동값을 표시, food먹었을때의처리, 게임 end조건 판단
Scenes : Snake 게임 화면 폴더
  • image.png image.png
TapBird : TapBird 게임 폴더, 싱글게임 & 4명까지 최고스코어를 기록하는 게임, 같이 게임하는 유저의 점수를 모두 표시
Scenes : TapBird 게임 화면 폴더
  • image.png image.png image.png
  • image.png image.png


Gamebase : Gamebase용 폴더


Plugins : IOS / Android 용 폴더


StreamingAssets : Gamebase 사용 폴더


NHN Cloud : Gamebase 사용 폴더


Sample Code

ConnectHandler : Assets/GameAnvilSample/

  • GameAnvil Connector초기화 밎 프로토콜 등록 Assets/GameAnvilSample/ConnectHandler.cs
connector = new GameAnvil.Connector(config);
            // 커넥터 로그 추가
            connector.Logger += (level, log) =>
            {
                Debug.Log(string.Format("Log[{0}]:{1}", level, log));
            };
            connector.LvNetLogger += (level, log) =>
            {
                Debug.Log(string.Format("Net[{0}]:{1}", level, log));
            };

            // 서버와 같은 순서로 프로토콜 등록
            GameAnvil.ProtocolManager.getInstance().RegisterProtocol(0, Com.Nhn.Gameanvil.Sample.Protocol.AuthenticationReflection.Descriptor);
            GameAnvil.ProtocolManager.getInstance().RegisterProtocol(1, Com.Nhn.Gameanvil.Sample.Protocol.GameMultiReflection.Descriptor);
            GameAnvil.ProtocolManager.getInstance().RegisterProtocol(2, Com.Nhn.Gameanvil.Sample.Protocol.GameSingleReflection.Descriptor);
            GameAnvil.ProtocolManager.getInstance().RegisterProtocol(3, Com.Nhn.Gameanvil.Sample.Protocol.ResultReflection.Descriptor);
            GameAnvil.ProtocolManager.getInstance().RegisterProtocol(4, Com.Nhn.Gameanvil.Sample.Protocol.UserReflection.Descriptor);

GameAnvil Connector 리스너 등록

  • Assets/GameAnvilSample/PlatformTest/Scripts/AuthUi.cs
        // 연결 끊기는 부분 처리 리스너 등록
        ConnectHandler.Instance.GetConnectionAgent().onDisconnectListeners += (SessionAgent sessionAgent, ResultCodeDisconnect result, bool force, Payload payload) =>
        {
            Debug.LogFormat("onDisconnect - {0}", result);
            UserInfo.Instance.MoveScene(Constants.SCENE_AUTH);
        };

Connect

  • Assets/GameAnvilSample/PlatformTest/Scripts/AuthUi.cs
        // 서버에 접속 시도.
        ConnectHandler.Instance.GetConnectionAgent().Connect(textIP.text, int.Parse(textPort.text),
            (SessionAgent sessionAgent, ResultCodeConnect result) =>
            {
                Debug.Log("Connect " + textID.text + ":" + textPort.text + " " + result);

                if (result == ResultCodeConnect.CONNECT_SUCCESS)
                {
                    // 성공시 입력 UI 처리
                    buttonConnect.gameObject.SetActive(false);
                    buttonAuth.gameObject.SetActive(true);
                    textID.gameObject.SetActive(true);
                }
                else
                {
                    // 서버 접속 실패 처리
                }
                buttonConnect.interactable = true;
            }
        );

Auth

  • Assets/GameAnvilSample/PlatformTest/Scripts/AuthUi.cs
            // 인증에 필요한 프로토콜 데이터 설정
            var authenticationReq = new Com.Nhn.Gameanvil.Sample.Protocol.AuthenticationReq
            {
                AccessToken = Constants.AUTH_ACCESS_TOKEN
            };
            Debug.Log("authenticationReq " + authenticationReq);

            // 서버에 인증 시도. 현재는 deviceid, id, pw 모두 uuid값으로 전달
            ConnectHandler.Instance.GetConnectionAgent().Authenticate(inputFieldUUID.text, inputFieldID.text, inputFieldID.text, new Payload().add(new Packet(authenticationReq)),
                (SessionAgent sessionAgent, ResultCodeAuth result, List<SessionAgent.LoginedUserInfo> loginedUserInfoList, string message, Payload payload) =>
                {
                    Debug.Log("Auth " + result);

                    // 성공인 경우 다음 단계로 진행.
                    if (result == ResultCodeAuth.AUTH_SUCCESS)
                    {
                        // 성공시 입력 UI 처리
                    }
                    else
                    {
                        // 실패시 처리
                    }
                }
            );

Login

  • Assets/GameAnvilSample/PlatformTest/Scripts/AuthUi.cs
        // 로그인에 필요한 프로토콜 데이터 설정
        var loginReq = new Com.Nhn.Gameanvil.Sample.Protocol.LoginReq
        {
            // 임의로 필요한 데이터 설정
            Uuid = inputFieldUUID.text,
            LoginType = Com.Nhn.Gameanvil.Sample.Protocol.LoginType.LoginGuest,
            AppVersion = Application.version,
            AppStore = "None",
            DeviceModel = SystemInfo.deviceModel,
            DeviceCountry = "KR",
            DeviceLanguage = "ko"

        };
        Debug.Log("loginReq " + loginReq);

        // 서버에 로그인
        ConnectHandler.Instance.CreateUserAgent(Constants.GAME_SPACE_NAME, string.Empty).Login(Constants.SPACE_USER_TYPE, string.Empty, new Payload().add(new Packet(loginReq)),
            (UserAgent userAgent, ResultCodeLogin result, UserAgent.LoginInfo loginInfo) =>
            {
                Debug.Log("Login " + result + ", " + loginInfo);

                // 성공시 다음 단계.
                if (result == ResultCodeLogin.LOGIN_SUCCESS)
                {
                    if (loginInfo.Payload.contains<Com.Nhn.Gameanvil.Sample.Protocol.LoginRes>())
                    {
                        // 로그인 응답 프로토콜 처리
                        Com.Nhn.Gameanvil.Sample.Protocol.LoginRes loginRes = Com.Nhn.Gameanvil.Sample.Protocol.LoginRes.Parser.ParseFrom(loginInfo.Payload.getPacket<Com.Nhn.Gameanvil.Sample.Protocol.LoginRes>().GetBytes());
                        Debug.Log("LoginRes " + loginRes);

                        // 서버에서 받은 게임 데이터 설정
                        UserInfo.Instance.Uuid = inputFieldUUID.text;
                        UserInfo.Instance.Nickname = loginRes.Userdata.Nickname;
                        UserInfo.Instance.Heart = loginRes.Userdata.Heart;
                        UserInfo.Instance.Coin = loginRes.Userdata.Coin;
                        UserInfo.Instance.Ruby = loginRes.Userdata.Ruby;
                        UserInfo.Instance.Level = loginRes.Userdata.Level;
                        UserInfo.Instance.Exp = loginRes.Userdata.Exp;
                        UserInfo.Instance.HighScore = loginRes.Userdata.HighScore;
                        UserInfo.Instance.CurrentDeck = loginRes.Userdata.CurrentDeck;

                        // 신전환신 버튼 리스너 모두 해재
                    }
                }
                else
                {
                    // 실패 처리
                }
            }
       );

GetUserAgent

  • Assets/GameAnvilSample/PlatformTest/Scripts/LobbyUi.cs
        // 커넥터로 부터 유저 객체 저장
        gameUser = ConnectHandler.Instance.GetUserAgent(Constants.GAME_SPACE_NAME, string.Empty);

CreateRoom : 싱글룸 생성및 입장

  • Assets/GameAnvilSample/PlatformTest/Scripts/LobbyUi.cs
        // 게임 싱글룸 생성
        gameUser.CreateRoom(Constants.SPACE_ROOM_TYPE_SINGLE, new Payload().add(new Packet(startGameReq)), (UserAgent userAgent, ResultCodeCreateRoom result, string roomId, Payload payload) =>
        {
            Debug.Log("CreateRoom " + result);

            if (result == ResultCodeCreateRoom.CREATE_ROOM_SUCCESS)
            {
                // 성공시 게임신으로 변경, 등록된 리스너 제거
            }
            else
            {
                // 실패 처리
            }
        });

MatchRoom : 멀티 룸매치

  • Assets/GameAnvilSample/PlatformTest/Scripts/LobbyUi.cs
        // 만들어 져있는 방에 들어가는 룸매치 요청 - 혼자서도 플레이가 가능하다. 최대 인원수 까지 모두 입장
        gameUser.MatchRoom(Constants.SPACE_ROOM_TYPE_MULTI_ROOM_MATCH, true, false, (UserAgent userAgent, ResultCodeMatchRoom result, string roomId, Payload payload) =>
        {
            Debug.Log("MatchRoom " + result);
            if (result == ResultCodeMatchRoom.MATCH_ROOM_SUCCESS)
            {
                // 성공시 게임신으로 변경, 등록된 리스너 제거
            }
            else
            {
                // 실패 처리
            }
        });

MatchUserStart : 멀티 유저 매치

  • Assets/GameAnvilSample/PlatformTest/Scripts/LobbyUi.cs
        // 유저 매치 두명이서 방을 만들어서 동시에 입장해서 게임진행
        gameUser.MatchUserStart(Constants.SPACE_ROOM_TYPE_MULTI_USER_MATCH, (UserAgent userAgent, ResultCodeMatchUserStart result, Payload payload) =>
        {
            Debug.Log("MatchUser " + result);
            if (result == ResultCodeMatchUserStart.MATCH_USER_START_SUCCESS)
            {
                // 성공시 게임신으로 변경, 등록된 리스너 제거
            }
            else
            {
                // 실패 처리
            }
        });
  • 유저가 매치될때 서버로 부터 onMatchUserDone이 호출된다.
        // 타이밍 이슈상 리스너를 미리 등록,  유저가 게임방에 들어 갔을때 게임 레디 flag설정
        gameUser.onMatchUserDoneListeners += (UserAgent userAgent, ResultCodeMatchUserDone result, bool created, string roomId, Payload payload) =>
        {
            Debug.Log("onMatchUserDoneListeners!!!!!! " + userAgent.GetUserId());

            if (result == ResultCodeMatchUserDone.MATCH_USER_DONE_SUCCESS)
            {
                UserInfo.Instance.gameState = UserInfo.GameState.Wait;
            }
        };
  • 유저 매치 타임아웃시 onMatchUserTimeout이 호출된다. : Assets/GameAnvilSample/PlatformTest/Scripts/MultiSnakeGameUi.cs
        // 유저 매치 요청 타임 아웃 리스너
        snakeGameUser.onMatchUserTimeoutListeners += (UserAgent userAgent) =>
        {
            Debug.Log("onMatchUserTimeoutListeners!!!!!! " + userAgent.GetUserId());

            // 로비신으로 이동
        };

LeaveRoom : 룸에서 나갈때 호출

  • Assets/GameAnvilSample/PlatformTest/Scripts/MultiSnakeGameUi.cs
  • Assets/GameAnvilSample/PlatformTest/Scripts/MultiTapBirdGameUI.cs
  • Assets/GameAnvilSample/PlatformTest/Scripts/SingleGameUi.cs
        // 게임종료 프로토콜 정의
        var endGameReq = new Com.Nhn.Gameanvil.Sample.Protocol.EndGameReq
        {
            EndType = gameEndType
        };

        // 게임룸 나가는 요청
        tapBirdUser.LeaveRoom(new Payload().add(new Packet(endGameReq)), (UserAgent userAgent, ResultCodeLeaveRoom result, bool force, string roomId, Payload payload) =>
        {
            Debug.Log("LeaveRoom " + result);

            if (result == ResultCodeLeaveRoom.LEAVE_ROOM_SUCCESS)
            {
                if (payload.contains<Com.Nhn.Gameanvil.Sample.Protocol.EndGameRes>())
                {
                    Com.Nhn.Gameanvil.Sample.Protocol.EndGameRes endGameRes = Com.Nhn.Gameanvil.Sample.Protocol.EndGameRes.Parser.ParseFrom(payload.getPacket<Com.Nhn.Gameanvil.Sample.Protocol.EndGameRes>().GetBytes());

                    UserInfo.Instance.Heart = endGameRes.UserData.Heart;
                    UserInfo.Instance.TotalScore = endGameRes.TotalScore;

                    UserInfo.Instance.Coin = endGameRes.UserData.Coin;
                    UserInfo.Instance.CurrentDeck = endGameRes.UserData.CurrentDeck;
                    UserInfo.Instance.Exp = endGameRes.UserData.Exp;
                    UserInfo.Instance.HighScore = endGameRes.UserData.HighScore;
                    UserInfo.Instance.Level = endGameRes.UserData.Level;
                    UserInfo.Instance.Nickname = endGameRes.UserData.Nickname;
                    UserInfo.Instance.Ruby = endGameRes.UserData.Ruby;

                    Debug.Log("GameResult " + endGameRes);
                }

                // 로비신으로 이동
            }
            else
            {
                // 실패시 처리
            }
        });


게임에서 정의한 프로토콜 처리

  • Send : Assets/GameAnvilSample/PlatformTest/Scripts/SingleGameUi.cs : 응답을 대기하지않고 서로보 패킷을 보내고 끝난다.
        // 전송할 패킷
        var tapMsg = new Com.Nhn.Gameanvil.Sample.Protocol.TapMsg
        {
            Combo = tapCount,
            SelectCardName = UserInfo.Instance.CurrentDeck + "_0" + 1,
            TapScore = 100
        };

        // 응답없이 서버로 데이터 성으로 전달하는 패킷
        tapBirdUser.Send(new Packet(tapMsg));
  • Request : Assets/GameAnvilSample/PlatformTest/Scripts/LobbyUi.cs : 서버로 요청을 보내고 응답을 받을때까지 대기
        // 게임유저가 서버로 request로 response를 받아 처리 한다.
        gameUser.Request<Com.Nhn.Gameanvil.Sample.Protocol.ShuffleDeckRes>(shuffleDeckReq, (userAgent, shuffleDeckRes) =>
        {
            Debug.Log("shuffleDeckRes" + shuffleDeckRes);

            if (shuffleDeckRes.ResultCode == Com.Nhn.Gameanvil.Sample.Protocol.ErrorCode.None)
            {
                // 서버에 셔플 성공 하고 응답값 갱신
                UserInfo.Instance.CurrentDeck = shuffleDeckRes.Deck;
                UserInfo.Instance.Coin = shuffleDeckRes.BalanceCoin;
                UserInfo.Instance.Ruby = shuffleDeckRes.BalanceRuby;

                // 유저 정보 UI 갱신
            }
            else
            {
                // 실패시 처리
            }
        });
  • 리스너 등록 : Assets/GameAnvilSample/PlatformTest/Scripts/MultiSnakeGameUi.cs : 서버 --> 클라이언트로 오는 패킷 리스너 등록
        snakeGameUser.AddListener((UserAgent userAgent, Com.Nhn.Gameanvil.Sample.Protocol.SnakeFoodMsg msg) =>
        {
            if (msg != null)
            {
                Debug.Log("<<<< SnakeFoodMsg!!!!!! : " + msg);

                if (msg.IsDelete)
                {
                    foreach (var currentFood in SnakeGameInfo.Instance.FoodList)
                    {
                        if (msg.FoodData.Idx == currentFood.transform.position.z)
                        {
                            Debug.Log("Delete Food !!!!! : " + currentFood.transform.position.z + ":" + currentFood.transform.position.x + ", " + currentFood.transform.position.y);
                            Destroy(currentFood);
                            SnakeGameInfo.Instance.FoodList.Remove(currentFood);
                            break;
                        }
                    }
                }
                else
                {
                    textFoodList.text = msg.FoodData.ToString();
                }
            }
        });
TOP