4일차 


https://doc.photonengine.com/ko-kr/pun/v2/demos-and-tutorials/pun-basics-tutorial/lobby-ui


해당 튜토리얼을 보고 유저네임 입력 UI와 스크립트를 만들었다




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Photon.Pun;
using Photon.Realtime;
 
namespace Com.MyCompany.MyGame
{
 
    [RequireComponent(typeof(InputField))]
    public class PlayerNameInputField : MonoBehaviour
    {
 
        const string playerNamePrefKey = "PlayerName";
        // Start is called before the first frame update
        void Start()
        {
            string defaultName = string.Empty;
            InputField _inputField = this.GetComponent<InputField>();
            if(_inputField != null)
            {
                if (PlayerPrefs.HasKey(playerNamePrefKey))
                {
                    defaultName = PlayerPrefs.GetString(playerNamePrefKey);
                    _inputField.text = defaultName;
                }
            }
 
            PhotonNetwork.NickName = defaultName;
        }
 
        // Update is called once per frame
        void Update()
        {
        
        }
 
        public void SetPlayerName(string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                Debug.LogError("Player Name is null or empty");
                return;
            }
            PhotonNetwork.NickName = value;
 
            PlayerPrefs.SetString(playerNamePrefKey, value);
        }
    }
}
cs


**InputField 의 on value changed 이벤트에 SetPlayName걸어도 null이 나가서 

인자를 InputField로 변경 후  value.text로 접근하니 접근이 가능했다.

 ** public void SetPlayerName(InputField value){ value.text ...........

원인을  찾아보자

추가

위에 Dynamic string 쪽에 선택을 했어야했다 ... 글을 대충읽지 말자




[RequireComponent(typeof(컴포넌트))]

스크립트가 달린 오브젝트에 해당 컴포넌트가 없다면 추가를 요청한다


C#에서 [] 연산자 용법중 어트리뷰트 부분을 참고해보자

https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/concepts/attributes/index


PlayerPrefs 클래스는 로컬에 간단한 자료를 저장 할 수있게 해준다

https://docs.unity3d.com/ScriptReference/PlayerPrefs.html





3일차 


공식 홈페이지에 튜토리얼이 한글로 잘 나와있다

참조 : https://doc.photonengine.com/ko-kr/pun/v2/demos-and-tutorials/pun-basics-tutorial/intro



유니티 에셋스토에서 PUN을 받는다







 PUN 을 임포트하고 포톤 클라우드의 애플리케이션 ID를 입력하면 된다

 




씬을 하나 만든 후 빈 오브젝트에 아래 스크립트를 추가해 보자


https://doc-api.photonengine.com/ko-kr/pun/current/index.html 

API 도 한글로 잘 되어있다


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
 
namespace Com.MyCompany.MyGame
{  
    public class Launcher : MonoBehaviour
    {
 
        string gameVersion = "1";
 
        private void Awake()
        {
            PhotonNetwork.AutomaticallySyncScene = true;
        }
 
        void Start()
        {
            Connect();
        }
   
        void Update()
        {
        
        }
        public void Connect()
        {
            if (PhotonNetwork.IsConnected)
            {
                PhotonNetwork.JoinRandomRoom();
            }
            else
            {
                PhotonNetwork.GameVersion = gameVersion;
                PhotonNetwork.ConnectUsingSettings();
            }
        }
 
    }
}



PhotomNetwork가 사용중이니


https://doc-api.photonengine.com/ko-kr/pun/current/class_photon_network.html

관련 API를 보자




Window -> Photon Unity Networking ->PUN Wizard

PUN 관련 세팅을 할 수 있다





Logging 을 full로 설정하면 전체 로그를 확인 할 수 있다









포톤  회원가입

https://dashboard.photonengine.com/ko-KR/account/SignUp



메일 주소 입력


가입한 메일에 비밀번호 설정 URL이 온다

outlook 메일을 사용했더니 정크메일 보관함들어간다

메일이 보이지않으면 스팸메일함을 찾아보자




비밀번호를 설정 후 포톤클라우드 관리화면을 보면 

20 CCU 짜리 무료 플랜을 볼 수 있다

 CCU( Concurrent connected User ) : 동시 접속자수 




어플리케이션 ID을 확인해 놓자


PUN 사용은 다음에 해보자


2일차


멀티 플레이를 위해 서버 엔진을 찾아 나서자


직접 만들기에는 어렵다


어떤 친철한 분이 서버 엔진을 이쁘게 정리한 것을 찾았다 


구축 방식으로 나눠보자


무료 라이센스와 자료가 많아 보이는   ProudNet , Photon Cloud 를 두고 고민을 해보자


포톤 클라우드 -  클라우드 환경을 제공하여 별도의 서버구축이 필요하지 않다. 서버 커스텀이 불가능하고 릴레이 서버로 빠른 반응이 필요한 곳에 적합하                        지 않다는 평이있다 포톤 서버라고 self-hosting 제품도 있다. 유니티 프로젝트에서 많이쓰는듯하다.


프라우드넷 - 국내 업체에서 제작해서인지 한글 문서가 잘되어있다. 하지만 서버를 직접 구축해야함. 홀펀칭 p2p도 지원을 하는듯해서 빠른 반응이 필요한 경우에도 사용 할 수 있을듯하다. 


기왕 하게된거 둘다 써보자 



츨처: http://www.gamecodi.com/board/zboard.php?id=GAMECODI_Talkdev&no=4749









1일차

 

유니티를 설치해보자


https://store.unity.com/kr


돈이없으니 Personal을 설치하자




모바일이나 웹 버전을 빌드 할 수도있니 일단 설치하자




적당히 다음 다음


설치 끝



Sub Test()




Application.FileDialog(msoFileDialogOpen).AllowMultiSelect = True

'파일 오픈 다이얼로그에 여러파일을 허가한다.

'그 외 다른 다이얼로그 존재

'MsoFileDialogType Enumeration 

'msoFileDialogFilePicker 3 File picker dialog box.

'msoFileDialogFolderPicker 4 Folder picker dialog box.

'msoFileDialogOpen 1 Open dialog box.

'msoFileDialogSaveAs 2 Save As dialog box.


fileChoi = Application.FileDialog(msoFileDialogOpen).Show

'리턴값이 0 일경우 다이얼로그 취소.

'            -1 정상적으로 다이얼로그 사용시.


Dim fileNum As Integer

Dim dataLine As String


Dim strArr() As String



If fileChoi <> 0 Then             '파일을 선택하였을때


    maxLine = 0

    findFlag = False

    

    For i = 1 To Application.FileDialog(msoFileDialogOpen).SelectedItems.Count  '선택된 파일개수

      strPath = Application.FileDialog(msoFileDialogOpen).SelectedItems(i)          '파일경로

    

      fileNum = FreeFile()

        Open strPath For Input As #fileNum          

       

 j = 1

        

        While Not EOF(fileNum)

                   

            Line Input #fileNum, dataLine

        

            strArr() = Split(dataLine, vbTab)

                    

            If UBound(strArr) = 0 Then

                

            Else     

                'Cells(i + 1, 1) = strArr(0)

                itemName = strArr(0)   '이름

                itemNo = strArr(3)

                

                For k = 1 To maxLine

                   

                    If Cells(k, 1) = itemName Then

                        

                        Cells(k, i + 1) = itemNo  '기존목록에있으면 개수만추가

                        

                        fineFlag = True

                        Exit For

                    

                    End If

                

                Next k

                

                If fineFlag = False Then

                    maxLine = maxLine + 1

                    Cells(maxLine, 1) = itemName '없으면  추가

                   Cells(maxLine, i + 1) = itemNo

                    

                End If

                

                

                'Cells(j, i) = strArr(0)

                

            End If

            

            

            fineFlag = False

            'MsgBox (LBound(strArr))

            'MsgBox (strArr(0))

            'MsgBox (dataLine)

            j = j + 1

       Wend                             '아이템 하나 탐색 종료

    

    Next i                             '파일 하나 탐색 종료

    

End If


'For i = 1 To filePath.Size


    'MsgBox (filePath(i - 1))

'Next i

    


End Sub










참고: https://msdn.microsoft.com/en-us/vba/excel-vba/articles/application-filedialog-property-excel

       http://software-solutions-online.com/excel-vba-open-file-dialog/

       https://www.exceltrick.com/formulas_macros/vba-split-function/

참고페이지 : http://lua-users.org/wiki/BuildingLuaInWindowsForNewbies

너무 길어서 실전압축 


우선 TDM-GCCLua source 를 다운받는다.

TDM-GCC은  tdm64-gcc-5.1.0-2 를 다운받았습니다.


GCC설치를 할때 환경변수 설정 항목이있는데 체크를 해줍시다.


lua 소스파일을 적당한 곳에 풀어줍니다.

그리고 윈도우 콘솔창으로 lua소스파일이 있는 곳으로 이동 후


mingw32-make PLAT=mingw


을 치시면 src폴더 안에 


lua.exe

lua53.dll

luac.exe   가 생깁니다.


파일 위치로 가서 실행을 시켜보면 잘 작동하는 것을 볼 수 있습니다.


이제 이것들을 적당한 곳에 옮겨서 환경변수에 추가하면 

간편하게 lua파일을 돌릴 수 있습니다.



실험에 사용 할 도메인은 Employee class


@SessionAttributes(value="employee") 


SessionAttributes alias은 employee 


출력시 어떤 스코프에 있는지 확인을 위해 RequestScope와 SessionScope 둘다 출력 하도록 하였다.


출력페이지 코드


결과 페이지 <br/>

RequestScope : ${requestScope.employee.name } <br/>

SessionScope : ${sessionScope.employee.name }


실험 1.

세션에 아무것도 없을 때


@Controller

@RequestMapping(value="test/*") 

@SessionAttributes(value="employee")

public class TestController {

@RequestMapping(value="test01")

public String test() {

return "forward:resultpage.jsp";

}

}

 

결과





실험 2.

세션에 데이터가 존재 할 때


@Controller

@RequestMapping(value="test/*") 

@SessionAttributes(value="employee")

public class TestController {

@RequestMapping(value="createEmployee")

public String createSession(HttpSession session) {

Employee emp=new Employee();

emp.setName("created Employee");

session.setAttribute("Employee", emp );

return "forward:resultpage.jsp";

}


@RequestMapping(value="test02")

public String test() {

return "forward:resultpage.jsp";

}

}


결과



전통적인 방법으로 세션에 데이터를 입력하였고 결과는 예상대로였다.



세션에 데이터가 있을 경우 실험1과 동일한 요청을 하였을때 

예상과 다르게 requestScope에도 데이터가 있는 것을 확인하였다.


그래서 결과 페이지를 직접 요청해 보았다.

RequestScope에는 존재하지 않는다.


@SessionAttributes를 이용 할 때에 RequestScope에 데이터가 입력된다.




실험3 


실험2와 동일한 조건에서 Model에 같은 이름으로 설정을 해보자.


@Controller

@RequestMapping(value="test/*") 

@SessionAttributes(value="employee")

public class TestController {

@RequestMapping(value="createEmployee")

public String createSession(HttpSession session) {

Employee emp=new Employee();

emp.setName("created Employee");

session.setAttribute("Employee", emp );

return "forward:resultpage.jsp";

}


@RequestMapping(value="test03")

public String test(Model model) {

Employee emp=new Employee();

emp.setName("new Employee");

model.addAttribute("employee",emp);

return "forward:resultpage.jsp";

}

}



결과


세션에 아무것도 없을 경우이다.



세션에 데이터가 입력되는 것을 확인 할 수 있었다.


세션에 데이터가 입력되는 것을 확인 할 수 있었다.



그렇다면 세션에 데이터가 있는 경우는 어떨까

참고로 createEmployee를 먼저 요청 후 test03을 요청하였을때 결과이다.




세션에 데이터가 있던 없던 별 차이가 없는 듯한 결과가 나왔다.



실험4


메소드 인자로 @ModelAttribute를 사용 할 경우.


@Controller

@RequestMapping(value="test/*") 

@SessionAttributes(value="employee")

public class TestController {

@RequestMapping(value="createEmployee")

public String createSession(HttpSession session) {

Employee emp=new Employee();

emp.setName("created Employee");

session.setAttribute("Employee", emp );

return "forward:resultpage.jsp";

}


@RequestMapping(value="test04") 

 public String  test (@ModelAttribute(value="employee")Employee employee) {

     

return "forward:resultpage.jsp";


 }

}


결과

세션에 데이터가 없을 경우

org.springframework.web.HttpSessionRequiredException: Expected session attribute 'employee'


익셉션이 발생한다.


세션에 데이터가 있을 경우





세션에 데이터가 있고 요청에 name을 추가했을 때

?name=test04


세션에 데이터가 없을 경우 익셉션이 발생 하는 것 말고는  

실험3과 같은 것 같다.


실험5


@ModelAtttibute를 메소드 위에 썻을 경우

실험1 부터 같은 조건을 해보았다.


@Controller

@RequestMapping(value="test/*") 

@SessionAttributes(value="employee")

public class TestController {

@ModelAttribute("employee")

public Employee employee() {

Employee emp=new Employee();

emp.setName("default create Employee");

return emp;

}


@RequestMapping(value="createEmployee")

public String createSession(HttpSession session) {

Employee emp=new Employee();

emp.setName("created Employee");

session.setAttribute("Employee", emp );

return "forward:resultpage.jsp";

}


@RequestMapping(value="test01")

public String test() {

return "forward:resultpage.jsp";

}

}


결과

세션에 데이터가 없을 때


@ModelAttribute 가 달린 메소드가 호출 되는 것 같다.


세션에 데이터가 있을 때를 위해 createEmployee를 요청 하였다.



세션을 만드는 메소드가 같은 컨트롤러에 있어서 그런지 위와 같은 결과가 나왔다.

그래서 컨트롤러를 따로 만들어 세션에 데이터 입력 후 다시해 보았다.



 세션에 데이터가 있을 경우는 앞선 실험들과 같은 결과가 나왔다.



실험6

앞선 실험들을 실험5의 @ModelAttribute가 달린 메소드를 추가해서 다시 해보았다.


결과

실험1 부터 3 까지는 같은 결과나왔다. 


※다만 실험4의 세선에 데이터가 없고 메소드인자로 @ModelAttributes가 사용 된 경우 익셉션이 발생하지 않았다. 




총 정리는 이해가 되면 그 때 추가예정.






추가 내용


org.springframework.web.bind.annotation

Annotation Type SessionAttributes



  • @Target(value=TYPE)
     @Retention(value=RUNTIME)
     @Inherited
     @Documented
    public @interface SessionAttributes
    Annotation that indicates the session attributes that a specific handler uses.

    This will typically list the names of model attributes which should be transparently stored in the session or some conversational storage, serving as form-backing beans. Declared at the type level, applying to the model attributes that the annotated handler class operates on.

    NOTE: Session attributes as indicated using this annotation correspond to a specific handler's model attributes, getting transparently stored in a conversational session. Those attributes will be removed once the handler indicates completion of its conversational session. Therefore, use this facility for such conversational attributes which are supposed to be stored in the session temporarily during the course of a specific handler's conversation.

    For permanent session attributes, e.g. a user authentication object, use the traditional session.setAttribute method instead. Alternatively, consider using the attribute management capabilities of the generic WebRequest interface.

    NOTE: When using controller interfaces (e.g. for AOP proxying), make sure to consistently put all your mapping annotations — such as @RequestMapping and @SessionAttributes — on the controller interfacerather than on the implementation class.

@SessionAttributes를 사용하였을때 requestescope에도 데이터가 남아있었는데 위에 저 부분이 해당사항인 것으로 추정 되어진다.

그리고 사용자 인증 같은 경우에는 맨날쓰던 session.setAttribute를 쓰라고 권고 하는 듯하다.

총평
애매하다 싶으면 다같이 그냥 쓰던거 쓰자.


잘 못 된 부분이나 더 자세한 정보를 가지고 계신분은 댓글 남겨주시면 감사하겠습니다.



.abcRioButton.abcRioButtonLightBlue { margin: 0 auto;}



Window -> Preferences -> General -> Editors -> Text Editors 
Show whitespace characters 체크




적용을 하면 탭, 스페이스 , 엔터가 표시가 된다.



+ Recent posts