반응형

I am tired of clicking through the hierarchy trying to find where X script is so I can look at it's public variables when testing things out. Especially from scene to scene when placement might be different. How do you stay organized? Find in scene wasn't that useful. Guess I might have to look into editing the inspector. I hope it's easy enough to do. Maybe an asset could help?

 

 

 

Answer by Neamtzu · 

As somebody already stated, you can right click on a script in the project tab, click on "Find References in Scene". Depending on the size of the project, this may take a bit and Unity may seem unresponsive until the search is completed. An alternative to this is to search the type you need in Hierarchy search tab with by typing t:TheTypeYou'reLookingFor. The results, if any, appear instantly and you can search for Unity classes too (t:camera - will return all camera scripts in the scene).

An useful tool for hierarchy management is QHierarchy.

반응형
반응형

 

 

 

 

 

 

 

 

 

 

 

하단 메일로 투자/제안/문의 주시면 감사하겠습니다

 

보내는 방법

  1. 메일 제목에 [투자, 제안, 프로젝트, 문의, 그 밖에] 중 한가지를 선택 하셔서 메일로 보내주시면 됩니다
    3dmpengines@gmail.com 

  2. 카톡으로 [투자, 제안, 프로젝트, 문의, 그 밖에]  의 문의로 보내주시면 됩니다

 

https://pf.kakao.com/_uxabzK

문의 카톡 아이디 : 게임프로그래밍 과외

 

 

 

약력 : https://3dmpengines.tistory.com/1682

 

3DMP & Contact

2012/10/21 ~ Now 약력 3DMP : (3D Mathematics Physics) Game engine 게임 엔진 프로그래머 (Nexon, Netmarble, NHN Games etc..) 자기개발 수강, 이직, 공채등으로 하셨던 분들 : EA, Nexon, NC, Netmarble, Kr..

3dmpengines.tistory.com

 

 

 

 

 

 

 

 

반응형
반응형

유니티 안드로이드 로그캣으로 디버깅 하기 - Unity Android Logcat Debugging.

유니티 에디터에서는 문제가 없이 잘 돌아 가는데 

꼭 핸드폰에 올리면 죽거나 퍼즈 되는 경우가 있다.

이럴 땐 안드로이드 logcat를 이용 하여

출력된 디버깅 정보를 확인 하고 에러를 해결 할 수 있다. 

 

기존 방법

 

1. 핸드폰을 개발자 모드로 활성화 하고 USB 디버깅 모드 사용으로 설정.

( 설정->휴대전화정보 -> 소프트웨어 정보->빌드번호

-> 빌드번호 창을 7회 클릭 -> 설정으로 이동

-> 개발자 옵션 생김 -> USB 디버깅 사용으로 전환 )

 

2. 안드로이드 SDK 설치 된 경로에서 명령창을 열어 다음 명령으로 로그 출력

( 기본 C:\Users\[유저이름]\AppData\Local\Android\Sdk\platform-tools\adb.exe )

  adb logcat -s Unity

 

adb logcat : 전체 로그 출력

adb logcat -s Unity : 유니티 태그로 발생 되는 로그 출력

 

유니티 패키시 이용 방법

 

 위에서 2번의 adb.exe 이용 방법 외에

지금은 유니티에서

Android Logcat 

이라는 패키지를 설치 하여 로그캣을 gui로 유니티 에디터에서 볼 수 있다. 

설치: 유니티 -> Window -> Package Manager -> Android Logcat 검색 -> 설치 

 

Android logcat

 

확인: Window->Analysis -> Android Logcat를 클릭 하면 창을 볼 수 있음.

android logcat

 

유니티 앱에서 발생 되는 로그만 보기 원한다면 

상단의 Tag 항목을 클릭 -> Unity를 선택 하면 된다. 

 

 

ref : https://dragontory.tistory.com/410

반응형
반응형

페이로드(payload)는 전송되는 데이터를 의미합니다. 데이터를 전송할 때, 헤더와 메타데이터, 에러 체크 비트 등과 같은 다양한 요소들을 함께 보내어, 데이터 전송의 효율과 안정성을 높히게 됩니다. 이 때, 보내고자 하는 데이터 자체를 의미하는 것이 바로 페이로드입니다. 우리가 택배 배송을 보내고 받을 때, 택배 물건이 페이로드이고, 송장이나 박스, 뾱뾱이와 같은 완충재 등등은 부가적인 것이기 때문에 페이로드가 아닙니다.

추가적으로 위키피디아에 아주 이해하기 좋은 예시가 아래와 같이 나와있어서 첨부합니다.

페이로드(payload)라는 단어는 운송업에서 비롯하였는데, 지급(pay)해야 하는 적화물(load)을 의미합니다. 예를 들어, 유조선 트럭이 20톤의 기름을 운반한다면 트럭의 총 무게는 차체, 운전자 등의 무게 때문에 그것보다 더 될 것이다. 이 모든 무게를 운송하는데 비용이 들지만, 고객은 오직 기름의 무게만을 지급(pay)하게 된다. 그래서 ‘pay-load’란 말이 나온 것이다

json으로 보는 실제 데이터에서의 payload는 아래의 json에서 “data”입니다. 그 이외의 데이터들은 전부 통신을 하는데 있어 용이하게 해주는 부차적인 정보들입니다.


ref

반응형
반응형
웹 통신에서 HTTPS 트래픽을 처리하기 위해 클라이언트와 서버간 SSL 세션을 맺는 HTTPS 핸드쉐이크 과정 다들 아시지요?

 

! 아래 그림을 참고해 보겠습니다.
 
.. 일반적인 HTTPS 핸드쉐이크 과정입니다. 위 과정이 모두 완료되어야 클라이언트-서버간 암호화된 데이터를 주고 받을 수 있습니다.
 
그런데 여러분..
데이터를 보내려고 할때마다 위와 같이 인증서를 확인하고.. 암호화 방식을 결정하며.. 데이터 암호화에 사용할 키를 생성하고 서로 교환하는 과정을 반복한다면.. 얼마나 비효율적일까요?
 
클라이언트와 서버간 Latency  50ms 라고 한다면.. 실제 데이터를 교환하기 위해 매번 200 ms(클라이언트 기준) 의 핸드쉐이크 과정이 포함되어야 합니다.
가뜩이나 암/복호화로 인해 서버의 응답 지연시간이 HTTP 보다 느려지는데.. 핸드쉐이크 과정까지 매번 수행하게 되면.. 속도측면에서 얼마나 만족스러운 결과를 기대할 수 있을까요?
 
그렇습니다. 우리가 지금 고민하고 있듯이.. SSL 프로토콜을 만드신 분들께서도 이런 사항을 고민하셨겠지요~..
HTTPS 핸드쉐이크의 과정을 대폭 개선한 간소화된 핸드쉐이크 과정이 이미 나와있습니다.
크게 보면.. 2가지네요~.. 
 
 
그 중 먼저 첫 번째 두둥~
 
 
1.     SSL Identifier 메커니즘
 
일단, 최초로 서버와의 통신을 시도하는 클라이언트는 어쩔 수 없습니다.
Full 핸드쉐이크 과정을 처음엔 거쳐야 합니다.
 
먼저 클라이언트는 최초 접속이기 때문에 Client Hello 메시지에 session id 값이 없습니다.
아래 2번과정에서 서버는 Server Hello 메시지에 Session Identifier 를 포함하여 전달합니다
 
 
 
클라이언트는 서버에서 발행해 준 Session Identifier 정보를 로컬에 저장하게 되는데요~..
클라이언트는 다음 HTTPS 세션을 맺을 때 이 값을 사용하게 됩니다
저장해 둔 Session Identifier 값을 Client Hello 메시지에 포함하여 전달합니다
 
서버는 클라이언트가 보내준 Session Identifier 정보를 확인하고, 이를 다시 사용해도 된다고 판단이 되면, Server Hello 메시지에 같은 Session IDs 값을 포함하여 전달합니다. ( 2번 과정)
 
이러면.. 다음 과정들이 생략됩니다.
 
ㄱ.   서버 인증서 확인
ㄴ.   암복호화에 사용할 Cipher Sutes 결정
 
ㄷ.   암복호화에 사용할 암호화 키 교환
 
이전에 사용했던 Cipher Suite 와 암호화 키를 다시 재활용하게 되므로 Full 핸드쉐이크 과정보다 2 Step 정도가 간소화 되네요~.. 
처음과 같이 하나의 과정의 Latency 를 50ms 로 가정한다면.. 100ms 정도를 개선하는 효과가 생깁니다.
 
클라이언트-서버간 주고 받아야 할 트랜잭션이 많을수록 효과는 크게 됩니다
 
 

2.     Session Ticket 메커니즘

 
두번째 방법은 Session Ticket 메커니즘입니다.
 
이 역시도 최초에는 Full 핸드쉐이크 과정을 수행해야 합니다. 가장 마지막 과정에서 서버는 “New Session Ticket”메시지를 클라이언트에게 전달합니다. 여기에는 암복호화에 사용할 마스터 키와 Cipher Sutes 알고리즘이 포함되어 있습니다.
 
세션 티켓의 정보는 서버만 알고 있는 키로 안전하게 암호화 하여 처리합니다.
세션 티켓 메커니즘은 TLS 확장 옵션입니다. 해당 메커니즘은 클라이언트와 서버가 모두 지원해야만 사용할 수 있습니다.
 
클라이언트는 Client Hello 메시지의 확장 옵션부분에 값이 없는 Session Ticket 을 같이 포함하여 전달함으로써 Session Ticket 메커니즘을 지원함을 알립니다. 서버 역시 Server Hello 메시지에 같은 방식으로 지원여부를 알립니다
 
 
 
클라이언트가 다시 서버에 HTTPS 통신을 시도하게 되면 다음과 같이 간소화된 절차를 거치게 됩니다.
 
클라이언트나 서버 둘 중 하나도 이를 지원하지 않는 경우에는 Session Identifier 메커니즘을 사용하게 됩니다.
 
Session Ticket 메커니즘이 상세히 기술된 RFC 5077 에는 Session Identifier 보다 Session Ticket 메카니즘의 사용을 더 권장하고 있습니다.
 
이유는 두가지인데요~
 
첫째, 서버의 메모리 사용이 적습니다. Session Identifier 는 서버가 발행한 Session IDs 값들을 모두 메모리에 기억하고 있어야 합니다
따라서 접속사용자가 많은 경우에는 서버 메모리 자원에 영향을 줄 수 있습니다
반면, Session Ticket 메커니즘은 클라이언트가 해당 내용을 기억하고 서버로 전달하는 방식이라 서버의 메모리 자원에 영향이 적습니다.
 
둘째, L4에 의한 부하분산 환경에서는 Session Identifier 메커니즘을 사용하는데 문제가 될 수 있습니다.
L4 의 부하분산 방식이 사용자의 IP를 참고하지 않는 방식의 경우 사용자를 동일한 서버로 분산하지 않기 때문에 SSL 세션 재활용 효율이 적습니다
이는 서버가 정보를 저장하고 이용하기 때문인데, 반면 클라이언트가 정보를 전달하는 방식인 Session Ticket을 사용할 경우에는 L4의 환경에 영향을 전혀 받지 않습니다
 
 
반응형

'서버(Server)' 카테고리의 다른 글

Photon.Pun not found  (0) 2022.06.14
페이로드(payload)란? 개념 설명  (0) 2022.06.02
반응형

Making games is more accessible than it ever has been. There's now a myriad of game engines to choose from, most of them for free, and an equally important accessibility of storefronts in the PC space. Between Steam, Epic Games Store and Itch.io, among others, the barriers to publishing a game have been hugely reduced.

And not only are games simpler to make and publish, but the range of creations you can easily make is wider than ever. For a long time, aspiring developers would perfect their craft with 2D platformers and Flash games, but now the improved accessibility of tools means they can be much more ambitious, much earlier. PlayerUnknown's Battlegrounds is a good example of that evolution, initially created by Brendan Greene because he found the multiplayer games he was playing too repetitive.

But there's one aspect of making games that has still a long way to go in terms of its accessibility: building backend services, managing multiplayer features, maintaining servers, creating matchmaking services, hosting and analysing players data, and everything that goes into running a live, multiplayer game.

As noted by Linode's Will Blew in a recent GamesIndustry.biz Academy guide to building your back-end in the cloud: "Making the right choices around how you run and support your game over time can be daunting, but there are lots of options available to help."

 

 

 

One of those options is PlayFab, a back-end live ops tech company acquired by Microsoft exactly three years ago. Now operating as Azure PlayFab (Azure being Microsoft's cloud platform), it's keen to conquer more of the industry.

"PlayFab has grown almost ten times since [the acquisition], in terms of both the number of monthly active players, [and] the number of games on the platform," says James Gwertzman, PlayFab co-founder and now Microsoft's general manager for cloud gaming. "All of Microsoft's first party studio games are now on Playfab. We get to host Minecraft, Halo, Gears of War and the new Flight Simulator, and I literally get a visceral thrill every time I see a game using one of our services.

"How do we ultimately deliver that platform that Phil [Spencer, head of Xbox] sold me on, to help the world's game developers be more successful with their games? I'm now responsible not just for PlayFab, but also for Azure, and frankly any [Microsoft] service in the game development space. My job is basically to figure out what game developers need, work across all of Microsoft to deliver it, whether it's working with Playfab, Xbox, Azure, Dynamics, Teams, and stitch together end-to-end solutions from across Microsoft to the games industry."

Gwertzman is keen to advocate for both Microsoft's cloud and its dev tools, reintroducing its basics to the GamesIndustry.biz Academy, and show how it's bloomed since the acquisition.

The challenges facing Azure PlayFab

Despite running some of the biggest titles in the world, Azure is still somehow less known in the gaming space than Amazon Web Services for instance, which is a challenge Gwertzman will have to tackle as he works on growing the cloud.

"I joke all the time that Azure is the world's best cloud for gaming you've never thought of. I think Azure has a reputation as being a cloud that is really focused on the enterprise. If you're an insurance company, or a big bank, or maybe an agriculture company, then I'm sure Azure is a great cloud, but if you're a game developer, you might think Azure's not the cloud for you.

 

 

"And to be honest with you, that's probably going to be the single biggest headwind that we're going to be working on now that I'm in this new role, because I think Microsoft's cloud kind of does have that reputation. And it has a lot to do with where we came from."

He points out that clouds like Amazon's initially targeted startups and entrepreneurs -- early tech adopters that typically include game developers -- and is now trying to figure out how to tackle enterprises and big companies. Microsoft came from the other direction.

 

....

 

 

 

ref : https://www.gamesindustry.biz/articles/2021-02-01-azure-playfab-the-worlds-biggest-dev-tools-youve-never-thought-of

 

반응형

'서버(Server) > Playfab' 카테고리의 다른 글

플레이펩(playfab?) 이란 (Azure)  (0) 2022.05.16
반응형
Question :
왜 all you have to do is 동사원형 인가요?
 

한 회화책에서 all you have(got) to do is 동사원형 이라고 나와있는데요

 

예문을 보자면  All you have to do is sit here. 이라는데

왜 오로지 all you have to do is 동사원형만 가능한가요?

 

all you have to do is to find your son 이런건 안되나요?

 

 

 

 

Answer : 

 

all <you have(got) to do> is (to) 동사원형.

to도 가능하고 생략도 가능합니다.

이처럼 생략할 수 있는 경우는 다음과 같습니다.

 

①[②] All <I did>/ was [(to) press this button].(LEG305)

①[③] The best <that you can do>/ is [keep quiet].

①[②] My favorite thing <to do> / is [play tennis].

①[②] [What you do] / is [(to) mix the eggs with flour].(LEG305)

 

 

 

ref : https://kin.naver.com/qna/detail.naver?d1id=11&dirId=11080302&docId=239635588&qb=c2hvdWxkIGRvIGlzIOuPmeyCrOybkO2YlQ==&enc=utf8&section=kin.ext&rank=2&search_sort=0&spq=0

반응형

'영어 > 영문법' 카테고리의 다른 글

분화 복수는 복수취급  (0) 2022.09.10
불가산 명사는 단수 information  (0) 2022.08.28
all you have to do is 동사가 두개?  (0) 2021.11.04
대동사  (0) 2021.03.24
2020년에 가장 많이 검색된 영단어 Top 5  (0) 2021.03.23
반응형

플레이펩은 마이크로소프트 Azure 의 플레이펩인 빽엔드 플랫폼이고 프로토버전에서 사용이 유용할수있고 상용버전들에서도 사용되는 빽엔드 플랫폼이다

 

빽엔드 플랫폼인데 Javascript 로 어느정도 커스터마이징이 가능한 빽엔드 플랫폼이지만

전체다 컨트롤이 가능하지 않고 커스터마이징의 제한은 플레이어 종속되어 있는 형태이다

(이부분이 살짝 아쉬움, 전체다 컨트롤 하면 좋을것 같음 : 가능하게 해주세요!)

하지만 볼륨이 큰작업이라.. 이게 진행될진 모르겠지만 이것이 된다면 타 빽엔드 플랫폼과는 확실히 다른 차별성을 갖을 수 있을 거라 봅니다

 

그냥사용하기에는 좀 아쉬움이 남지만 그 외의 부분들은 대체적으로 훌룡함

 

 

 

유저가 가입되고 로그인되는 처리를 할수 있고

 

사용자를 눌러 사용자당 다음과 같은 정보들을 볼수 있습니다

 

 

즉 왠만한 빽엔드에서 구현하는건 상당수 되어 있다

 

 

 

좌측에 보면 랭킹, 상점 등에 대한 구현도 고려해볼수 있습니다

 

 

 

자동화 => 수정버전(레거시) 버전이 스크립트로 편집 가능한 부분들..

 

 

 

위는 테스트를 아주 짧게 해본 코드

 

 

 

포톤과도 인테그레이션이 가능하다는 것을 알수 있다

기본적으로 함수가 하나의 API 형식이 되는 꼴이다 사용성은 편하다

 

 

테스트해본결과 기본 유저당 데이터들도 클라와 서버간의 전송도 가능하다

클라와 서버와의 직접적인 데이터 전송은 위험하기에 이런 스크립트를 제공하는 듯 하다

방향성은 괜찮다고 보여짐

 

 

 

 

그외에 제공되는 기능들은 대략적으로 다음들이다

 

 

  1. Multiplayer Services를 통해 더 빠르게 빌드하고 좀 더 비용 효율적인 게임 시작
  2. Analytics로 게임 최적화
  3. LiveOps를 통해 게임을 서비스로 실행하고 플레이어가 더 많은 것을 위해 게임을 지속하도록 유도

Multiplayer Servers

대기 시간이 짧고 신뢰성이 높은 실시간 멀티 플레이어 게임 플레이를 제공하도록 전용 Multiplayer Servers를 동적으로 스케일링합니다.

파티 네트워킹 및 채팅

Azure Cognitive Services에서 지원하는 PlayFab 파티의 원활한 플레이어 간 소통을 통해 플레이어가 관계를 형성하고 커뮤니티를 구축하도록 도와줍니다.

매치 메이킹 및 그룹

플레이어가 새로운 친구와 경쟁자를 찾도록 돕고, 더 흥미롭고 더 나은 보상이 따르는 플레이를 위해 모든 수준의 플레이어에게 더 빠르고 우수한 매치를 제공합니다.

순위표 및 통계

토너먼트, 순위표, 상품을 통해 플레이어의 성공을 추적, 비교, 보상합니다.

네트워크 간 ID 및 데이터

사용 플랫폼에서 플레이어 요구를 충족하고 계정을 연결하여 Xbox, PSN, Nintendo 등에서 로밍하도록 허용합니다.

 

 

 

 

ref : https://azure.microsoft.com/ko-kr/services/playfab/#overview

 

반응형
반응형

반응형
반응형

현업 C/C++/STL/3D게임프로그래밍/3D수학/Shader/엔진 통합과정 과외출처(마감)

 

 

3DMP

 

 

수학/프로그램은 샤프를 들기 이전에 상상에서부터 시작되어야 합니다!!

 

현업 3D 게임엔진 프로그래머가 전수해드리는 고농축 고퀄리티의 과외를 만나 보실 수 있습니다

 

 

 

 

이력 ]

  • 넥슨, NHN, 넷마블 다수 동종/타종 근무 경험 및 다년간 현업 개발자
  • 3D 게임 엔진 프로그래머/ 3D,2D 게임/ 언리얼 엔진/ 엔진 프로그래머/ 메인 PC 게임 개발 / 모바일 게임/엔진 개발
  • 미들웨어(엔진)/ 일반 프로그램 프로젝트 제작
  • 고차원 수학/ 물리등의 알고리즘과 관련된 특수 프로그램 제작
  • 게임업계 대기업 넥슨, NC 외 P사, 중견 B사 등등의 기업에 합격시킨 노하우
  • 다수, 다양한 분들에 대한 다수의 과외 경험으로 축적된 노하우 보유
  • 기타 일반 프로젝트 경험
  • 과외 등록 완료, 남,  성범죄 "완전 무(無)"!!

 

 

[ 수강하셨던 분들 ]

  • 동종 현업 3N 사 포함/비 동종업계 현업 프로그래머 분들 다수 수강
  • 해외 글로벌 대기업 E* 사, 국내 일반/게임업계 대기업 현업 프로그래머, TA, 아티스트, 기획자
  • 국내 현업 게임 및 비게임 업계 종사자 대기업, 중견, 벤처 회사원과 기업가, 사업가(CEO)
  • 대학생(취준생), SKY 포함 상위 25% 이상 대학생, 유학생
  • 전공자/컴퓨터 관련 전공자가 아니지만 게임이나 프로그램 쪽으로 진로를 희망하시는 분들
    (비전공자 일지라도 많은 상당수의 분들이 원하는 성과를 이루고 가셨습니다)
  • 전문 프로그래머로 진로를 정하시거나 또는 희망하시는 분들
  • 프로그램을 혼자 하기엔 막연하여 그룹(단체) 배워보고자 하시는 분들
  • 초심자, 프로그램 배워보려 했지만 진입 장벽 때문에 포기하신 분들
     

[이직/신입 취직하신 분들]

이직/합격/수강하신 분들의 주요 회사 목록 

https://3dmpengines.tistory.com/2105?category=804913

 

 

  [ 통합 진행 과목 ]

 

방향에 따른 과목들은 상담을 통해 설정됩니다 
추가 과목에 대한 추가 비용은 들지 않으며 방향에 따라 과목들이 정해집니다

  • 일반 수학, 2D수학 미들웨어, STL 기반 자료구조와 알고리즘,
  • 최적화 알고리즘, 설계-디자인(Design Pattern)
  • C언어/C++,C++ 11~, 현업 디버깅 스킬 , 시스템 베이스
  • 기초 수학, 3D 실전 수학이론, 수학적용을 위한 실전 코드베이스 작업
  • DirectX9 / DX12,  DX를 통한 실전 수학 활용 병행
  • 멀티쓰레드 프로그래밍, 기초 물리(Physics), 캐릭터, 지형 엔진 & Tools,
  • Graphics : 3D 애니메이션, Shader, DX shader(HLSL), Unity shader
  • 게임 엔진 : Unreal 4, Unity3D
    카테고리 중 원하시는 목록을 상담 시 적어주셔도 됩니다 개인별 맞춤형 진행
  • 진행 사항에 관한 자세한 내용은 하단 참조
    [주의] 단 과외를 통한 외주나 과제 제작은 받지 않습니다

 

   [ 과외 대상 ]

  • 견고하면서 제대로 된 스킬을 배우고 싶으신 분
  • 실력을 더 쌓아가고 싶은 현업 프로그래머
  • 다른 교육원에서 실질적인 배움을 채워주지 못한 부분에 갈증을 느끼시는 분 또는 취준생
  • 현업에서 사용되는 3D or 2D 온라인 게임 프로그래밍의 스킬을 쌓고 싶은 분.
  • 만드는 것에만 열중해 설계를 보지 못하시는 분이나 문제는 푸나 실질적인 적용을 못하는 분
  • 전공생/비전공생/유학생/컴퓨터 전공 관련 또는 프로그래밍에 관심이 있는 분
  • 3D 그래픽스 수학을 배우고자 하는 분(기초부터 현업 수준까지)
    수학적 개념이 어느 정도 있고 응용은 하지만 원하는 결과를 잘 못 만들어내는 분
    일정 수준의 수학 실력은 되긴 하나 좀 더 심도 있는 프로그램적 수학 스킬을 원하시는 분 
  • 초심자 or 기초가 부족하거나 또는 약간의 기초는 있지만 어떻게 공부해야 할지 막연하신 분

 

 

[ 과외 기본 방향성 : 개인 맞춤형 최적화 설정]

 

수업은 수강자분의 현재 상태에 맞는 최적화된 맞춤 방식으로 진행되는 것을 원칙으로 하며 모든 과목은

최종적으로는 혼자만의 생각으로 수학/프로그램을 만들어 나가는 것을 진행 시 가장 우선적인 목표 진행합니다

현업/면접 또는 프로젝트에서 중요한 부분 또한 조언 및 컨설팅이 같이 병행됩니다

 

  •  과목은 위에 나열한 과목들이 있지만 원하는 과목 또는 수준에 따라 먼저 시작하게 되는 과목이
    변경되니 과목 란에서 이런 과목이 있다는 것만 참고하시면 됩니다
  • 철저히 수강생 맞춤형으로 진행됩니다(수강자 분의 수준이 고려된 최적의 선에서 시작합니다)
  • 진도는 기본적으로 수강자 분들의 현재 수준을 고려하여 사전 상담을 통하여 
    수강자의 현재 실력 기준으로부터 부족한 부분은 채워 나가고 수강생의 목표에 따라 추가해야 할 부분은
    추하여 진행하는 방향으로 진행됩니다(과목 추가에는 별도의 비용이 추가되지 않습니다)
  • 수업은 수강자가 직접 코드를 작성하면서 진행됩니다
  • 과외 기본 목표 : 혼자만의 힘으로 희망하는 수준까지의 프로그램 실력을 쌓는 것을 기본 목표로 합니다
  • 도서는 따로 존재하지 않으면 도서 위주로 진행되지 않습니다!
    수강생의 독립적인 실력을 최우선으로 목표로 하기 때문에 선생님에게 편한 방식인 일괄적인 도서를 선정하지 않고 현재 수강자의 수준을 고려하여 진행합니다 (참고가 될만한 도서는 동반될 수도 있습니다)
    책을 중심으로 나가진 않는 또 다른 이유는 서적을 통해 배울 수 없는 실질적인 현업의 견고한
    고급 스킬적인 부분들을 가르쳐드리기 때문입니다
    진도만을 위한 도서 위주의 수업 또는 깊은 이해가 동반되지 않는 방식 즉 겉핥기로는 게임/엔진 프로그래머의 문턱에 조차 도달할 수 없습니다는 점을 기억해주시면 될 것 같습니다

 

 

 [ 과외 안내 ]

  • 예약 대기 : 예약 신청 시 공석이 생길 경우 우선적으로 연락을 드리고 있습니다
    신청자가 많은 경우 대기 기간이 발생할 수 있습니다

   진행 방식 :  개인 과외 1:1 진행 방식 / 1:N 방식

  • 1 Section 기준 = 4주/4회, 추가 섹션은 및 진행은 별도 문의주시면 답변해드립니다
  • 1 회당(1 섹션) 시간 : 1:00, 섹션당 추가에 따른 누적 할인 적용
  • 수강 기간 : 한 달 단위로 수강, 수강생이 희망하는 달까지 진행되며 한달 단위 결제방식입니다
  • 과외 장소/방식 : 코로나로 인하여 온라인으로만(원격, 카메라 Off ) 진행하고 있습니다
    온라인은 오프라인과 거의 동일하게 진행되며 다년간 온라인과 오프라인의 노하우로
    둘 방식에는  큰 차이가 없다고 보시면 됩니다
    해외에 거주하신 분들의 경우 네트워크 속도에 유의해 주시기 바랍니다(사전 네트워크 테스트 가능)
  • 납부 방식 : 한 달 단위, 해외에 계신 분들에 한하여 페이팔로 결제 가능
  • 그룹 과외  : 그룹(2인 이상은) 별도 문의 , 추가 할인 적용

 

[수강 특전]  : 3DMP 네트워크

  • 현업 게임 프로그래머들과 함께 지속적인 '3DMP 네트워크 데이' 에서 정보 교류활동의 기회

 

 

[문의]

상담 시 최대한 오픈 마인으로 임해주시면 좋은 시너지 효과가 나올 수 있습니다!

상담 전용 카톡 플러스 친구 (별도의 친구 추가 없이 상담 가능)

https://pf.kakao.com/_uxabzK

 

상담 전용 카톡 아이디 : 게임프로그래밍 과외

반응형
반응형

병합 요청(MR)은 한 브랜치를 다른 브랜치로 병합하기 위한 요청이다. 병합 요청을 사용하여 소스 코드에 대해 제안된 변경사항을 시각화하고 협업한다.

개요

  • 병합 요청(일명 "MR")은 제안된 변경사항에 대한 많은 정보를 표시한다. MR의 본문에는 위젯(CI/CD 파이프라인이 있는 경우, 관련 정보 표시)과 함께 설명이 포함되며, 그 뒤에 해당 MR로 협업하는 사람들의 토론 스레드가 표시된다.
  • MR에는 스레드에서 발생하는 토론, 커밋 목록, 파이프라인 및 Job 목록, 코드 변경 및 인라인 코드 리뷰를 볼 수 있는 내비게이션 탭도 포함되어 있다.

사용 사례

A. 팀에서 일하는 소프트웨어 개발자일 경우

  1. 새 브랜치를 체크아웃하고 병합 요청을 통해 변경사항을 제출한다.
  2. 팀에서 피드백을 수집한다.
  3. 코드 품질 보고서로 코드를 최적화하는 구현 작업을 수행한다.
  4. GitLab CI/CD의 단위 테스트 보고서로 변경사항을 검증한다.
  5. 라이선스 준수 보고서를 통해 라이선스가 프로젝트와 호환되지 않는 종속성을 사용하지 않도록 한다. (ULIMATE)
  6. 관리자에게 승인을 요청한다. (STARTER)
  7. 관리자 :
    a. 최종 리뷰와 함께 커밋을 푸시한다.
    b. 병합 요청을 승인한다. (STARTER)
    c. 파이프라인이 성공하면 병합되도록 설정한다.
  8. 변경사항은 GitLab CI/CD에 대한 수동 작업을 통해 프로덕션에 배포된다.
  9. 구현이 성공적으로 고객에게 전달된다.

B. 회사의 웹 사이트를 위한 웹페이지를 작성하는 웹 개발자일 경우

  1. 새 브랜치를 체크아웃하고 병합 요청을 통해 새 페이지를 제출한다.
  2. 리뷰어(Reviewers)로부터 피드백을 수집한다.
  3. Review Apps로 변경사항을 미리 볼 수 있다.
  4. 웹 디자이너에게 구현을 요청한다.
  5. 관리자에게 승인을 요청한다. (STARTER)
  6. 승인되면, 병합 요청이 스쿼시(Squash) 및 병합되고, GitLab Pages를 사용하여 스테이징(Staging)에 배포된다.
  7. 프로덕션 팀이 프로덕션으로 병합 커밋을 체리 픽(Cherry-pick) 한다.

상단의 병합 요청 내비게이션 탭

  • 병합 요청에 대한 설명을 포함하는 새 Overview탭이 병합 요청의 맨 위에 위치한다. Overview 옆에서 Commits, Pipelines 및 Changes를 찾을 수 있다.

Merge Request 만들기

  • 모든 병합 요청은 브랜치를 만드는 것으로 시작된다. Git CLI 애플리케이션을 통해 명령 줄로 로컬에서 수행하거나 GitLab UI를 통해 할 수 있다.
  • 새 병합 요청을 시작하면, 방법에 관계없이, New Merge Request 페이지로 이동하여 병합 요청에 대한 정보를 채운다.
  • 방법에 관계없이 새 브랜치를 GitLab에 푸시하면, Create Merge Request 버튼을 클릭하고 거기에서 병합 요청을 시작한다.

New Merge Request 페이지

  • New Merge Request 페이지에서, 병합 요청에 대한 제목과 설명을 작성하여 시작한다. 브랜치에 이미 커밋이 있는 경우 제목은 첫 번째 커밋 메시지의 첫 번째 라인으로 미리 채워지고, 설명은 커밋 메시지의 추가 라인으로 미리 채워진다. 제목은 모든 경우에 필수인 유일한 필드이다.
  • 여기에서, 정보(제목, 설명, 담당자, 마일스톤, 레이블, 승인자)를 입력하고 Create Merge Request 버튼을 클릭할 수 있다.

Create Merge Request 버튼

  • 새 브랜치를 GitLab에 푸시했으면, GitLab의 저장소를 방문하여 화면 상단에서 Create Merge Request 버튼을 클릭할 수 있는 call-to-action(CTA, 행동 유도 또는 클릭 유도)을 확인한다.

 

 

ref : https://velog.io/@leyuri/Gitlab-Merge-Request-%EA%B8%B0%EB%8A%A5%EC%9D%B4%EB%9E%80

반응형
반응형
I have a base class Media and several derived classes, namely DVD, Book, etc... The base class is written as:
class Media{
    private:
        int id;
        string title;
        int year;
    public:
        Media(){ id = year = 0; title = ""; }
        Media(int _id, string _title, int _year): id(_id), title(_title), year(_year) {}
//      virtual ~Media() = 0;
        void changeID(int newID){ id = newID; }
        virtual void print(ostream &out);
};

The thing is: without the destructor, GCC gives me a bunch of warnings class has virtual functions but non-virtual destructor, but still compiles and my program works fine. Now I want to get rid of those annoying warnings so I satisfy the compiler by adding a virtual destructor, the result is: it doesn't compile, with the error:

undefined reference to `Media::~Media()`

Making the destructor pure virtual doesn't solve the problem. So what has gone wrong?

 

 

 

 

 

 

What you have commented out is a pure-virtual declaration for a destructor. That means the function must be overridden in a derived class to be able to instantiate an object of that class.

What you want is just a definition of the destructor as a virtual function:

virtual ~Media() {}

In C++ 11 or newer, it's generally preferable to define this as defaulted instead of using an empty body:

virtual ~Media() = default;
answered Apr 5, 2012 at 8:05

 

Jerry Coffin
453k76 gold badges597 silver badges

 

 

 

ref : https://stackoverflow.com/questions/10024796/c-virtual-functions-but-no-virtual-destructors

반응형
반응형

 

It just that i have to write a long line and then declare a function in the header and implement it in the cpp file only to make a simple delay.
In javascript for example you can do something like that:

setTimeout(function()
{
...
},300);

But when i tried to use lambda within the timer function it says it couldn’t convert what he needs to lambda.
Is it really have to be such a mess only to make a simple delay ?

 

 

 

FTimerDelegate TimerCallback;
TimerCallback.BindLambda([]
{
	// callback;
});

FTimerHandle Handle;
GetWorld()->GetTimerManager().SetTimer(Handle, TimerCallback, 5.0f, false);

 

Inrate 에 대한 설명  == /** Time between set and fire, or repeat frequency if looping. */

 

 

 

Is this what you’re looking for?

 

 

 

 

If you want an inline callback, you can do the following:

GetWorld()->GetTimerManager().SetTimer(Handle, FTimerDelegate::CreateLambda([] { /* callback; */ }), 5.0f, false);

 

 

 

ref : https://forums.unrealengine.com/t/can-i-use-lambda-with-timers/30620

 

Can i use lambda with timers?

It just that i have to write a long line and then declare a function in the header and implement it in the cpp file only to make a simple delay. In javascript for example you can do something like that: setTimeout(function() { ... },300); But when i tried

forums.unrealengine.com

 

 

33님 댓글 

GetWorldTimerManager().SetTimer(FTimerhandle,[this]{ bla bla bal },InRate,Loop);



반응형

'게임엔진(GameEngine) > Unreal4' 카테고리의 다른 글

블루프린트에서의 멀티플레이어  (0) 2022.07.13
UE5 특징  (0) 2022.06.16
비히클 셋업  (0) 2022.03.16
check(표현식);  (0) 2022.03.03
문자 변환  (0) 2022.02.10
반응형

유니티는 C# 6.0을 지원한다. 2018 버전부터는 7.0도 지원한다고 알고있다. async, await 키워드는 C# 5.0부터 추가된 기능이므로 유니티에서 사용 가능하다.

굉장히 오버헤드가 큰 작업을 해야하는데 실시간성을 확보하고 싶을때가 있다. 맵을 로딩하는 동안 로딩 아이콘이 끊김 없이 뱅글뱅글 돌게 하고 싶은 경우가 좋은 예시. 보통 이럴 땐 일회용 스레드를 생성하고 그 안에서 작업을 수행한다. 이런 작업을 위해서 있는 편의 기능이 Task인데 aysnc와 await는 이 Task와 함께 사용하는 키워드이다.

async, await의 사용법을 모를 때는 유니티에서 일회용 스레드를 만들고 싶으면 아래와 같이 쓰곤 했었다.

using UnityEngine;
using System.Collections;
using System.Threading;

public class AsyncTest_Coroutine : MonoBehaviour
{
	void Start()
	{
		StartCoroutine(Run(10));
	}

	IEnumerator Run(int count)
	{
		int result = 0;
		bool isDone = false;

		// 이렇게 하면 변수 스코프를 공유할 수 있는 장점이 있다.
		// 스레드 내에서 result와 isDone 변수에 접근할 수 있다.
		(new Thread(() =>
		{
			for (int i = 0; i < count; ++i)
			{
				Debug.Log(i);
				result += i;
				Thread.Sleep(1000);
			}

			// 작업이 끝났음을 알린다.
			isDone = true;
		}))
		.Start();

		// isDone == true 가 될 때까지 대기한다.
		while (!isDone) yield return null;

		Debug.Log("Result : " + result);
	}
}

프로그램을 block 시키지 않고 10초간 비동기 작업이 실행되는 코드이다.

이렇게 스레드 객체를 만들고 람다식으로 함수를 정의하면 나름 보기 좋은 코드가 나온다. 새로운 스레드를 팠지만 그냥 위에서 아래로 순차적으로 읽으면 동작을 이해할 수 있어서 개인적으로 굉장히 좋은 코드라고 생각한다. 하지만 매번 코루틴을 사용해야 하는 단점이 있고 그마저도 유니티이기 때문에 가능한 것이었다.

그런데 async, await 키워드를 이용하면 더 간단하고 유니티 코루틴의 도움 없이도 동일한 동작을 하는 코드를 작성할 수 있다. 다음은 async, await를 사용하는 예제이다.

using System.Threading.Tasks;
using System.Threading;
using UnityEngine;

public class AsyncTest : MonoBehaviour
{
	void Start()
	{
		Debug.Log("Run() invoked in Start()");
		Run(10);
		Debug.Log("Run() returns");
	}

	void Update()
	{
		Debug.Log("Update()");
	}

	async void Run(int count)
	{
		// 새로 만들어진 태스크 스레드에서 CountAsync()를 실행한다.
		var task = Task.Run(() => CountAsync(10));

		// 함수를 리턴하고 태스크가 종료될 때까지 기다린다.
		// 따라서 바로 "Run() returns" 로그가 출력된다.
		// 태스크가 끝나면 result 에는 CountAsync() 함수의 리턴값이 저장된다.
		int result = await task;

		// 태스크가 끝나면 await 바로 다음 줄로 돌아와서 나머지가 실행되고 함수가 종료된다.
		Debug.Log("Result : " + result);
	}

	int CountAsync(int count)
	{
		int result = 0;

		for (int i = 0; i < count; ++i)
		{
			Debug.Log(i);
			result += i;
			Thread.Sleep(1000);
		}

		return result;
	}
}

await 키워드가 들어있는 메소드는 반드시 async 키워드를 붙여야 한다. Task 객체 앞에 await를 붙이면 해당 태스크가 종료될 때까지 기다리게 된다. await를 만나면 즉시 함수를 리턴하고 해당 스레드는 다음 작업을 계속 수행할 수 있다. 태스크가 종료되면 다시 await가 있던 곳으로 돌아와 나머지가 실행된다. 물론 함수가 호출됐던 동일한 스레드에서 실행된다.

이제 람다식을 사용해서 첫 번째 예제처럼 수정하면 다음과 같다.

using System.Threading.Tasks;
using System.Threading;
using UnityEngine;

public class AsyncTest_Lambda : MonoBehaviour
{
	void Start()
	{
		Debug.Log("Run() invoked in Start()");
		Run(10);
		Debug.Log("Run() returns");
	}

	void Update()
	{
		Debug.Log("Update()");
	}

	async void Run(int count)
	{
		int result = 0;

		await Task.Run(() =>
		{
			for (int i = 0; i < count; ++i)
			{
				Debug.Log(i);
				result += i;
				Thread.Sleep(1000);
			}
		});

		Debug.Log("Result : " + result);
	}
}

 

ref : https://velog.io/@anzsoup/%EC%9C%A0%EB%8B%88%ED%8B%B0%EC%97%90%EC%84%9C-async-await-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

반응형
반응형

자동차 바퀴는 Kinematic 으로 설정하고 콜리전 같은 효과를 주기위해서 대신 Ray cast 를 사용하여 월드와 상호작용하도록 처리한다

 

 

 

 

비히클용으로 아주 기본적인 최소한의 아트 셋업은, 스켈레탈 메시 입니다. 비히클 유형에 따라 얼마나 복잡한 아트 셋업이 필요할 것인지가 결정되며, 서스펜션에 특별한 관심을 쏟아야 할 수 있습니다. 탱크라면 서스펜션이 거의 항상 보이지 않을 테니 별달리 신경쓸 필요가 없겠지만, 비히클 게임 에서 볼 수 있는 것과 같은 모래언덕용 소형차라면 노출된 부품이 좀 더 그럴싸하게 움직이도록 하기 위해서는 조인트가 추가로 필요할 것입니다.

기본

비히클 메시는 양수 X 를 아래로 향하고 있어야 합니다.

언리얼 엔진 4 에서 사용할 휠의 반경은 센티미터 단위로 측정해야 합니다.

위 그림에서는 Maya의 Distance Measurement (거리 측정) 툴을 사용하여 휠 서로 반대편의 두 버텍스 사이의 거리에 따라 휠의 지름을 측정합니다. 휠의 반경은 이 값의 절반이 됩니다.

3DsMax 의 경우 Helpers 섹션에도 비슷한 툴이 있습니다.

조인트

4륜 비히클에 필요한 최소한의 조인트 갯수는 루트에 하나, 휠마다 하나씩 총 5 개입니다. 휠과 루트 조인트는 X 가 앞쪽, Z 가 위쪽으로 맞춰져 있어야 합니다:

그래야 Y 축 중심으로 회전하면서 Z 축으로 스티어링됩니다.

다른 모든 조인트는 원하는 대로 배치 가능하지만, 애니메이션 블루프린트  Look At 노드같은 것들은 X 가 앞쪽이라 가정한다는 점 참고하시기 바랍니다.

시각적으로 어색해 보이는 것을 방지하기 위해, 휠의 조인트는 중앙을 정확히 맞춰야 합니다. 콜리전 감지에 시각적인 메시가 사용되지는 않을 것이지만, 휠 메시가 중앙을 벗어난 경우 휠이 망가진 것처럼 보일 것이기에, 모션 블러로 인해 정말 눈에 띨 것입니다.

바인딩

Maya의 경우 표준 스무드 바인드고, 3DS Max 의 경우 스킨 모디파이어 입니다. 휠은 하나의 조인트에만 웨이팅이 있어야 이상한 변형 없이 자유로운 스핀이 가능합니다. 쇼크 앱소버와 스트럿 서스펜션의 경우 복잡한 스키닝으로 해도 가능하지만, 언리얼 에디터 측에서는 좀 더 생각을 많이 해야 할 것입니다.

익스포트

비히클은 단순히 스켈레탈 메시 로 익스포트되며, 별달리 고려할 것은 없습니다.

임포트

스켈레탈 메시에 대한 표준 FBX 임포트입니다. 임포터가 피직스 애셋 을 생성하도록 하는 것이 좋을 것입니다.

피직스 애셋

피직스 애셋 은 비히클에 엄청나게 중요하므로, 간과하거나 생략할 수 없습니다. 처음 언리얼 엔진 4 로 피직스 애셋 을 생성할 때, 아마도 거의 이와 같아 보일 것입니다:

그 이유는 피직스 애셋 툴 (PhAT) 이 조인트에 스키닝된 버텍스를 가능한 만큼 래핑 시도하기 때문입니다. 그리고 솔직히, 그 전부 지우고 다시 만들게 될 것입니다. 왜냐구요? 모든 피직스 바디를 결합시킬 컨스트레인트 재생성을 제대로 처리할 수 있는 방법이, 현재 PhAT 에는 없기 때문입니다. 즉 중간 피직스 바디를 삭제하면 상위 계층구조에 새로운 컨스트레인트를 만들지 않습니다. 그러나 모든 피직스 바디를 삭제하고 루트 조인트부터 쌓아가기 시작하면 모든 컨스트레인트가 제대로 생성될 것입니다.

계층구조 패널에서 모든 것을 Shift 선택한 다음 Delete 키를 누릅니다. 그러면 애셋에서 모든 피직스 바디가 제거됩니다.

이제 루트 조인트부터 시작해서 비히클 조인트에 피직스 바디를 만들기 시작합니다. 염두에 둘 것은, 물리 시뮬레이션을 하거나 비히클의 바운드에 영향을 끼칠 필요가 있는 조인트에만 피직스 바디가 필요하다는 것입니다.

여기 우리 버기의 경우, 루트에 대한 박스나 휠에 대한 구체가 시작하기 딱 좋을 것이며, 원하는 동작을 내기 위해서는 여러가지 다른 피직스 바디가 필요할 것입니다.

바디 콜리전 생성

비히클 루트에 대한 박스를 생성하려면:

  1. 계층구조 패널에서 루트 조인트에 우클릭합니다.
  2. 바디 추가 를 선택합니다.
  3. 콜리전 지오메트리  박스 로 설정합니다.
  4. "OK" 를 클릭합니다.
  5. 그 후 새로운 피직스 바디 를 이동, 회전, 스케일 조절하여 비히클의 모양을 더욱 잘 추정해 낼 수 있습니다.

휠 콜리전 생성

휠에 쓸 구체를 생성하려면:

  1. 계층구조 패널에서 휠 조인트에 우클릭합니다.
  2. 바디 추가 를 선택합니다.
  3. 콜리전 지오메트리  구체 로 설정합니다.
  4. "OK" 를 클릭합니다.
  5. 디테일 패널에서 Physics Type 프로퍼티를 찾아 Kinematic 으로 설정해 줍니다.비히클의 바운드에 영향을 끼치려는 휠에는 필수인데, 그림자와 컬링이 정상 작동하도록, 또 게임이 시작되면 비히클에서 휠이 떨어져 나오지 않도록 하기 위해서입니다.
  6.  피직스 바디 는 절대 실제 콜리전에 사용되지 않습니다.
    현재 휠은 레이 캐스트를 사용하여 월드와의 상호작용을 처리합니다.

더 나아가서

간단한 셋업은 이렇습니다. '비히클 게임' 내 '비히클' 의 '피직스 애셋' 을 살펴보면 이렇습니다:

휠 외에 모든 콜리전 바디는 비히클의 루트 조인트상에 존재합니다. 휠이 특정 게임 요소에 걸리지 않도록 하고, 휠의 메시가 벽이나 철책에 잘리지 않도록 하기도 합니다.

 

ref : https://docs.unrealengine.com/4.27/ko/InteractiveExperiences/Vehicles/VehicleContentCreation/

반응형

'게임엔진(GameEngine) > Unreal4' 카테고리의 다른 글

UE5 특징  (0) 2022.06.16
lambda with timers  (1) 2022.04.24
check(표현식);  (0) 2022.03.03
문자 변환  (0) 2022.02.10
언리얼4 Android용 Visual Studio 디버깅 가능해지다!!  (0) 2021.08.20
반응형

 

 

You should be using either

LayerMask.GetMask("A", "B")

or

(1 << LayerMask.NameToLayer("A")) | (1<< LayerMask.NameToLayer("B"))

or if you wanna make life easy, just add a public LayerMask field and set in in the inspector

public LayerMask layerMask
반응형
반응형

멤버 선언에 예기치 않은 한정이 있습니다. 이 경고를 해결하려면 식별자에서 한정자를 제거합니다.

기본적으로 이 경고는 해제되어 있습니다. /Wall 또는 /wN4596을 사용하여 명령줄에서 수준 N 경고로 사용하도록 설정할 수 있습니다. 또는 소스 파일에서 #pragma 경고(N:4596)를 사용합니다. 자세한 내용은 기본적으로 해제되어 있는 컴파일러 경고를참조하세요. 일부 버전의 컴파일러는 /permissive-아래에만 이 경고를 생성합니다.

이 경고는 Visual Studio 2015 업데이트 3부터 사용할 수 있습니다. 이전 버전의 컴파일러에서 경고 없이 컴파일된 코드는 이제 C4596을 생성할 수 있습니다. 특정 컴파일러 버전 이상에 도입된 경고를 사용하지 않도록 설정하는 방법에 대한 자세한 내용은 컴파일러 버전별 컴파일러 경고를참조하세요.

 

 

 

이 샘플에서는 C4596을 생성하고 이를 해결하는 방법을 보여줍니다.

 

// C4596.cpp
// compile with: /w14596 /c

struct A {
    void A::f() { } // error C4596: illegal qualified name in member
                    // declaration.
                    // Remove redundant 'A::' to fix.
};

 

 

ref : https://docs.microsoft.com/ko-kr/cpp/error-messages/compiler-warnings/c4596?view=msvc-170

반응형
반응형
 
#define SOL_ALL_SAFETIES_ON 1

#include <sol/sol.hpp>
#include <assert.hpp>

int main() {
	sol::state lua;
	int x = 0;
	lua.set_function("beep", [&x]{ ++x; });
	lua.script("beep()");
	c_assert(x == 1);

	sol::function beep = lua["beep"];
	beep();
	c_assert(x == 2);

	return 0;
}

 

#define SOL_ALL_SAFETIES_ON 1

#include <sol/sol.hpp>
#include <assert.hpp>

struct vars {
	int boop = 0;

	int bop () const {
		return boop + 1;
	}
};

int main() {
	sol::state lua;
	lua.new_usertype<vars>("vars", 
		"boop", &vars::boop,
		"bop", &vars::bop);
	lua.script("beep = vars.new()\n"
		"beep.boop = 1\n"
		"bopvalue = beep:bop()");

	vars& beep = lua["beep"];
	int bopvalue = lua["bopvalue"];

	c_assert(beep.boop == 1);
	c_assert(lua.get<vars>("beep").boop == 1);
	c_assert(beep.bop() == 2);
	c_assert(bopvalue == 2);

	return 0;
}

 

 

ref : https://sol2.readthedocs.io/en/latest/

반응형
반응형

std::move 와 std::forward 는 lvalue, rvalue 의 개념과 엮여서 조금 헷갈린다. 어떻게 동작하는지, 심지어 왜 필요한지 등. 아래의 코드로 간단히 정리해본다.

표면적으로는 std::move 는 주어진 입력인자를 rvalue 로 변환해주는 역할을 한다. 아래의 코드에서와 같이 a 라는 객체는 lvalue 이고 std::move 가 a 를 받고 b 에 리턴하는 것은 rvalue 이다. 이게 다 이다. 이 동작과 move 라는 이름이 무슨 관계란 말인가?

 

이는 rvalue 라는 것과 연관지어서 생각해봐야한다. 클래스는 copy constructor 와 move constructor 를 가질 수 있다. 기본 내장형 또는 custom 형태 든. 보통 copy constructor 는 간단히, 클래스 인스턴스가 가진 모든 것을 복사한다고 생각하면 된다. 따라서 복사 비용이 크다. 반면에 move constructor 는 이렇게 모든 것을 복사할 필요가 없고 따라서 복사비용 (공간 및 시간) 을 효율적으로 해야할 때 사용되는데, 이 move constructor 가 호출되기 위해서는 rvalue 가 필요하다. 그래서 std::move 가 무언가를 ravlue 로 만들어주고 이것이 해당 객체를 'movable' 하도록 만들어주기 때문에 이름이 std::move 인 것이다.

다시한번 생각해봐도 이유가 타당한 것 같다. 어떤 객체가 rvalue 라면, 다시말해 const 의 속성을 가지고 있고 값이 변하지 않는 객체라면, 굳이 비싼 비용을 수반해서 모든 걸 복사할 필요가 없지 않은가?

std::forward 의 경우는 move 와 비슷하나 약간 다르다. std::move 는 입력으로 받은 객체를 무조건 rvalue 로 만들어준다. 그러나 std::forward 는 조건이 맞을 때만 rvalue 로 만들어준다.

※ 참고

- https://www.geeksforgeeks.org/move-constructors-in-c-with-examples/

 

 

Move Constructors in C++ with Examples - GeeksforGeeks

A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

www.geeksforgeeks.org

 

 

ref : https://blog.naver.com/sheld2/222654277182

반응형
반응형

td::forward가 가장 많이 쓰이는 곳은 보편 참조 매개변수로 받아서 처리하는 로직입니다.

// lvalue를 캐치하는 함수
void Catch(Person& p, const char* name)
{
    cout << name << "lvalue catch" << endl;
}

// rvalue를 캐치하는 함수
void Catch(Person&& p, const char * name)
{
    cout << name << "rvalue catch" << endl;
}

// 전달받은 obj를 std::forward를 통해서 catch 함수로 전달합니다.
template<typename T>
void ForwardingObj(T&& obj, const char* name)
{
    Catch(std::forward<T>(obj), name);
}

int _tmain(int argc, _TCHAR* argv[])
{
    Person p1("ahn", 1985);
    ForwardingObj(p1, "p1\t\t=\t");
    ForwardingObj(std::move(p1), "std::move(p1)\t=\t");

    return 0;
}

수행결과

 

ref : https://jungwoong.tistory.com/53

반응형
반응형

이 매크로는 표현식을 실행한 뒤, 어서트 결과가 false 이면 실행을 중지시킵니다. 표면식은 매크로가 빌드에 컴파일되는 경우에만 실행됩니다 (DO_CHECK=1). 가장 간단한 형태의 check() 매크로입니다.

예:

check(Mesh != nullptr);
check(bWasInitialized && "Did you forget to call Init()?");

 

checkf(표현식, ...);

checkf() 매크로는 표현식이 true 가 아니면 디버깅에 도움이 되는 추가 정보를 출력하는 것이 가능합니다. 컴파일 면에 있어서는 check() 와 똑같습니다.

예:

checkf(WasDestroyed, TEXT( "Failed to destroy Actor %s (%s)"), *Actor->GetClass()->GetName(), *Actor->GetActorLabel());
checkf( TCString<ANSICHAR>::Strlen( Key ) >= KEYLENGTH( AES_KEYBITS ), TEXT( "AES_KEY needs to be at least %d characters" ), KEYLENGTH( AES_KEYBITS ) );

 

 

 

ref : https://docs.unrealengine.com/4.27/ko/ProgrammingAndScripting/ProgrammingWithCPP/Assertions/

반응형

'게임엔진(GameEngine) > Unreal4' 카테고리의 다른 글

lambda with timers  (1) 2022.04.24
비히클 셋업  (0) 2022.03.16
문자 변환  (0) 2022.02.10
언리얼4 Android용 Visual Studio 디버깅 가능해지다!!  (0) 2021.08.20
모바일 Joystick 을 PC 에서도 입력되도록  (0) 2021.06.28
반응형

Reliable UDP (이하 RUDP)는 신뢰성을 갖는 UDP를 의미합니다.

일반적으로 TCP는 신뢰성을 갖는 대신 느리고, UDP는 신뢰성이 없고 빠르다고 알려져있죠.

여기에 또 하나의 특징은, TCP는 서버 (Listener)와, 클라이언트 (Connector) 관계가 성립한다는 점입니다.

즉, 서버건 클라이언트건 연결 관리가 필요하다는 것이죠.

RUDP의 필요성은, 주로 클라이언트 끼리의 통신에서 대두되었습니다.

우선 일반적인 클라이언트/서버 구조에서의 클라이언트 끼리의 통신은 서버를 경유해서 데이터를 전송함으로써 신뢰성을 갖추는데, UDP보다 느리고 서버에 부하를 주기 때문에 클라이언트 끼리의 통신에서도 TCP의 장점은 신뢰성과, UDP의 장점은 속도를 모두 갖춘 Reliable UDP가 등장하게 된 것이죠.

TCP는 한쪽이 서버가 되어서 대기 하고 있어야하지만, UDP는 그럴 필요가 없이 바로 통신이 가능하다는 점도 또 하나의 장점입니다.

보통 RUDP는 Relay Server와 연동되어서 구현이 되는데요, 모든 상황에서 UDP 통신이 가능한 것이 아니기 때문에, UDP 통신이 실패했을 때 신뢰성 갖춘 통신을 위해 Relay Server를 통한 데이터 전송을 해주기 위해서입니다.

우선 UDP가 Reliable 해지기 위한 방법부터 알아봅시다.

TCP가 UDP와 다른 점은 신뢰성이 있다고 얘기했었죠? TCP가 신뢰성을 갖추고 있는 이유를 먼저 얘기해보겠습니다.

1. 데이터의 순서를 보장해줍니다. 보낸 순서와 받는 순서가 일치하게 해준다는 의미입니다.
2. 데이터의 도착을 보장해줍니다. 보낸 데이터가 반드시 도착한다는 것을 보장해준다는 의미입니다.
3. 데이터의 무결성을 보장해줍니다. 즉, 보낸 데이터와 받는 데이터가 일치 하다는 것을 보장한다는 의미입니다.

위의 세가지 조건을 만족하기에 TCP가 신뢰성을 갖추고 있다고 말하는 것이고, RUDP도 마찬가지로 위 세가지 조건을 UDP를 통해 만족하도록 구현함으로써 신뢰성을 갖게 되는 것이죠.

순서를 보장하는 방법은 패킷에 번호를 붙이고, 번호순서대로 패킷이 도착할때까지 기다렸다가 패킷이 모두 모이면 그때 패킷을 풀면됩니다.

도착을 보장하는 방법은 패킷에 번호를 붙이고, 해당 번호의 패킷이 도착할때까지 재전송하면 됩니다.

보내는 입장에서 재전송을 하는 이유는, UDP이기에 받는 입장에서는 자신에게 보내려는 패킷이 있었는지 알 방법이 없기 때문이죠.

무결성을 보장하는 방법은 체크섬을 통해서, 데이터가 손실되지 않았는지 검증합니다.

RUDP사용시 주의사항은, UDP로 연결을 시도하는 중에도 릴레이 서버를 통한 패킷 전달은 지속적으로 이루어 져야 한다는 점입니다.

그리고 UDP연결이 성공했을 때 UDP를 통한 송신을 시작해야 합니다.

UDP 송수신 중에는 연결 유지를 위해 일정 시간 간격으로 HeartBeat 패킷을 보내고, 일정 시간동안 해당 패킷이 도착하지 않는다면  UDP전송이 다시금 불가능해진 것으로 판단하여, 릴레이 서버를 이용하도록 하면서 지금껏 전송 확인이 안된 패킷들을 릴레이를 통해 전달하면 됩니다. 이렇게 해야 패킷의 지연은 있을 수 있으나 패킷의 소실은 발생하지 않습니다.


ref : 

https://elky.tistory.com/258

반응형
반응형

 

OhPontonInstantiate
    Start
        같은 Gameobjecdt 들의 Start 호출
    OnPhotonSerializeView
    순으로 호출됨
    이후에 SerializeView 는 계속 호출된다


    OnPhotonSerializeView 는 photonview 에서 Observed Components 로 연결되어 있어야 호출 된다

반응형
반응형

FName

콘텐츠 브라우저에서 새 애셋 이름을 지을 때, 다이내믹 머티리얼 인스턴스의 파라미터를 변경할 때, 스켈레탈 메시에서 본에 접근할 때, 모두 FName 을 사용합니다. FName 은 문자열 사용에 있어서 초경량 시스템을 제공하는데, 주어진 문자열이 재사용된다 해도 데이터 테이블에 한 번만 저장되는 것입니다. FName 은 대소문자를 구분하지 않습니다. 변경도 불가능하여, 조작할 수 없습니다. 이처럼 FName 의 정적인 속성과 저장 시스템 덕에 찾기나 키로 FName 에 접근하는 속도가 빠릅니다. FName 서브시스템의 또다른 특징은 스트링에서 FName 변환이 해시 테이블을 사용해서 빠르다는 점입니다.

FText

In Unreal Engine 4 (UE4) the primary component for text localization is the FText class. All user-facing text should use this class, as it supports text localization by providing the following features:

FText also features the AsCultureInvariant function (or the INVTEXT macro), which creates non-localized, or "culture invariant" text. This is useful for things like converting a player name from an external API into something you can display in your user interface.

You can create a blank FText using either FText::GetEmpty(), or by using just FText().

FString

FName 이나 FText 와는 달리, FString 은 조작이 가능한 유일한 스트링 클래스입니다. 대소문자 변환, 부분문자열 발췌, 역순 등 사용가능한 메서드는 많습니다. FString 은 검색, 변경에 다른 스트링과의 비교도 가능합니다. 그러나 바로 그것이 FString 이 다른 불변의 스트링 클래스보다 비싸지는 이유입니다.

변환

에서로예제

FName FString TestHUDString = TestHUDName.ToString();
FName FText TestHUDText = FText::FromName(TestHUDName);
FName -> FText 는 가능한 경우도 있지만, FName 의 내용이 FText 의 "자동 현지화" 혜택을 받지 못할 수 있음에 유념해 주시기 바랍니다.

FString FName TestHUDName = FName(*TestHUDString);
FString -> FName 은 손실성 변환이라 위험합니다. FName 은 대소문자를 구분하지 않기 때문입니다.

FString FText TestHUDText = FText::FromString(TestHUDString);
FString -> FText 은 가능한 경우도 있지만, FString 의 내용이 FText 의 "자동 현지화" 혜택을 받지 못할 수 있음에 유념해 주시기 바랍니다.

FText FString TestHUDString = TestHUDText.ToString();
FText -> FString 은 안전하지 않습니다. 일부 언어에서는 변환시 손실될 위험이 있습니다.

FText FName FText 에서 FName 으로의 직접 변환은 없습니다. 대신, FString 을 거친 다음 FName 으로 변환하는 방법이 있습니다.
FText -> FString -> FName 은 손실성 변환이라 위험합니다. FName 은 대소문자를 구분하지 않기 때문입니다.

인코딩

일반적으로 스트링 변수 리터럴 설정시에는 TEXT() 매크로를 사용해야 합니다. TEXT() 매크로를 지정하지 않으면, 리터럴은 ANSI 를 사용해서 인코딩되기에, 지원되는 글자가 크게 제한됩니다. FString 에 전달되는 ANSI 리터럴은 TCHAR (네이티브 유니코드 인코딩)으로의 변환을 겪어야 하기에, TEXT() 를 사용하는 편이 보다 효율적입니다.

인코딩 관련 상세 정보는 캐릭터 인코딩 문서를 참고해 주시기 바랍니다.

 

ref : https://docs.unrealengine.com/4.27/ko/ProgrammingAndScripting/ProgrammingWithCPP/UnrealArchitecture/StringHandling/

반응형
반응형

Custom Instantiation Data

You can send some initial custom data when the instantiation call. Just make use of the last parameter in the PhotonNetwork.Instantiate* method.

This has two main advantages:

  • save traffic by avoiding extra messages: we don't have to use a separate RPC or RaiseEvent call for synchronizing this kind of information
  • timing: the data exchanged is available on the time of the prefab instantiation which could be useful to do some initialization

The instantiation data is an object array (object[]) of anything Photon can serialize.

Example:

Instantiate with custom data:

object[] myCustomInitData = GetInitData();
PhotonNetwork.Instantiate("MyPrefabName", new Vector3(0, 0, 0), Quaternion.identity, 0, myCustomInitData);

Receive custom data:

public void OnPhotonInstantiate(PhotonMessageInfo info)
{
    object[] instantiationData = info.photonView.InstantiationData;
    // ...
}

 

 

ref : https://doc.photonengine.com/en-us/pun/current/gameplay/instantiation

반응형
반응형

event, Action은 꽤 많이 사용하는 편인데

가끔씩 초기화 코드가 중복되면서 이벤트가 중복 호출되는 문제가 발생하기 쉽다. 

그런데 이런 휴먼에러가 발생하면 이벤트 쪽은 이원화되어 있기 떄문에 오류를 발견하기 어렵다..

 

 

1. 방법 1 : 명시적으로 제거하기

using System.Linq;

private EventHandler foo;
public event EventHandler Foo
{
    add
    {
        if (foo == null || !foo.GetInvocationList().Contains(value))
            foo += value;
    }
    remove
    {
        foo -= value;
    }
}

 

2. 방법 2 : 언제나 초기화 전에 제거하기

중요한 사실. -=은 발견되지 않아도 예외가 발생하지 않는다.

때문에 많은 개발자들이 체인을 걸기 전에 빼는 방식으로 구현한다고 함.

private void OnEnable()
{
    Value.onValueChanged -= OnValueCallback;
    Value.onValueChanged += OnValueCallback;
}

private void OnDisable()
{
    Value.onValueChanged -= OnValueCallback;
}

다음과 같이 이벤트 핸들러 내부에서 처리하는 방법도 존재한다!

private EventHandler _foo;
public event EventHandler Foo 
{
    add 
    {
        _foo -= value;
        _foo += value;
    }
    remove 
    {
        _foo -= value;
    }
}

 

3. 단일 이벤트라면 그냥 대입연산자 쓰기

- 다른 이벤트가 체인될게 아니라면 그냥 대입연산자를 써버리는 방법도 있다.

  연결되어있던 모든 이벤트들의 레퍼런스가 사라지며, 새로 대입한 레퍼런스로 바뀐다.

- 버튼은 일반적으로 한 개의 이벤트만 가지기 때문에, 주로 버튼에 이벤트 체인걸 때 사용하게 된다.

private void OnEnable()
{
    Value.onValueChanged = OnValueCallback;
}

private void OnDisable()
{
    Value.onValueChanged -= OnValueCallback;
}

 

 

마치며..

사실 이벤트 order를 가진 프로퍼티를 만들어볼까 하고 만지작거리고 있었는데,

이벤트는 이원화되어야하며, 순서를 가진다면 당신은 무언가 잘못 짠 것입니다.

라길래 순서 없는 버전까지만 하는걸로!

 

 

 

ref : https://mentum.tistory.com/602

반응형
반응형
private GameObject playerUiPrefab;
 GameObject _uiGo =  Instantiate(playerUiPrefab);
    _uiGo.SendMessage ("SetTarget", this, SendMessageOptions.RequireReceiver);

수신자가 필요하며 이 의미는 SetTarget이 응답할 컴포넌트를 찾지 못했을 때 경고를 받게 된다는 것 입니다. 

 

 인스턴스로 부터 PlayerUI 컴포넌트를 받기 위한 방식중의 하나는 SetTarget 을 직접 호출 하는 것 입니다. Component 들을 직접 사용하는 것이 일반적으로 권장 되지만 다양한 방식으로 동일한 사항을 할 수 있다는 것을 알아두는 것도 좋습니다.

SendMessageOptions.RequireReceiver

 

Description

SendMessage에 대한 수신자가 필요한 경우에 사용합니다.

수신자를 찾을 수 없는 경우에, 콘솔에 오류가 출력됩니다. (기본 설정값입니다.) See Also: GameObject.SendMessage

 

 

ref : https://docs.unity3d.com/kr/530/ScriptReference/SendMessageOptions.RequireReceiver.html

반응형
반응형

PlayerPrefs

 

 

PlayerPrefs 클래스는 유니티에서 제공해주는 데이터 관리 클래스이다.

해당 클래스는 int, float, string, bool 타입의 변수를 저장하고 로드하는 기능을 제공한다.

 

 

PlayerPrefs 은 (엑셀에서 두 개의 컬럼으로 구성되어 있는 것과 같이) key와 Value 의 쌍으로 묶여진 항목들의 리스트의 룩업 리스트 입니다. 

그리고 각 플랫폼별 알아서 저장해줍니다

Description

게임 세션(session)사이에 플레이어 preference를 저장하고, preference에 접근합니다.

 

Mac OS X 에서 PlayerPrefs는 ~/Library/Preferences 폴더에 unity.[company name].[product name].plist의 파일이름으로 저장되며,

On Mac OS X PlayerPrefs are stored in ~/Library/Preferences folder, in a file named unity.[company name].[product name].plist, where company and product names are the names set up in Project Settings. Windows 독립 플레이어에서 PlayerPrefs는 @@HKCU
Software
[company name]
[product name]@@ 키 아래의 레지스트리에 저장되며 standalone players.

웹 플레이어에서 PlayerPrefs는 Mac OS X에서 ~/Library/Preferences/Unity/WebPlayerPrefs아래에 HKCU\Software\[company name]\[product name] key, where company and product names are 웹 플레이어 URL 당, 한개의 preference 파일이 존재하며, 파일 크기는 1메가 바이트로 제한됩니다. 파일 크기가 이 제한 값을 초과하는 경우,

On Linux, PlayerPrefs can be found in ~/.config/unity3d/[CompanyName]/[ProductName] again using the company and product names specified in the Project Settings.

On Windows Store Apps, Player Prefs can be found in %userprofile%\AppData\Local\Packages\[ProductPackageId]>\LocalState\playerprefs.dat

On Windows Phone 8, Player Prefs can be found in application's local folder, See Also: Windows.Directory.localFolder

On Android data is stored (persisted) on the device. The data is saved in SharerPreferences. C#/JavaScript, Android Java and Native code can all access the PlayerPrefs data. The PlayerPrefs data is physically stored in /data/data/pkg-name/shared_prefs/pkg-name.xml.

WebPlayer

On Web players, PlayerPrefs are stored in binary files in the following locations:

Mac OS X: ~/Library/Preferences/Unity/WebPlayerPrefs

Windows: %APPDATA%\Unity\WebPlayerPrefs

웹 플레이어에서 PlayerPrefs는 Mac OS X에서 ~/Library/Preferences/Unity/WebPlayerPrefs아래에

There is one preference file per Web player URL and the file size is limited to 1 megabyte. If this limit is exceeded, SetInt, SetFloat and SetString will not store the value and throw a PlayerPrefsException.

Static Functions

DeleteAll preference에서 모든 key와 값들을 제거합니다. 사용 시 경고가 뜹니다.
DeleteKey 키와 대응하는 값을 삭제합니다.
GetFloat Preference 파일에 존재하는 /key/에 대응하는 값을 반환합니다.
GetInt Preference 파일에 존재하는 /key/에 대응하는 값을 반환합니다.
GetString Preference 파일에 존재하는 /key/에 대응하는 값을 반환합니다.
HasKey 키가 존재하는지 확인합니다.
Save 수정된 모든 preferences를 디스크에 씁니다.
SetFloat /key/로 식별된 Preference의 값을 설정합니다.
SetInt /key/로 식별된 Preference의 값을 설정합니다.
SetString /key/로 식별된 Preference의 값을 설정합니다.

 

ref : https://docs.unity3d.com/kr/530/ScriptReference/PlayerPrefs.html

 

반응형
반응형

 

 

이번 시간에는 StatelessWidget(이하 SLW)와 StatefulWidget(이하 SFW)의 대해서 알아보고 그 차이점은 무엇인지도 생각해본다.

 

 

SLW와 SFW 모두 UI를 가지는 화면을 구성할 때 사용하는 위젯 클래스다. 두 위젯 모두 Scaffold를 이용해 동일한 방식으로 화면을 구성하게 된다.

 

1. StatelessWidget

우선 화면을 하나 살펴보자.

 

위의 화면은 숫자 0을 출력하는 Text위젯과 두 개의 FloatingActionButton 버튼으로 구성되어 있다. 예상하겠지만 버튼을 누를 때마다 숫자가 1씩 증가하거나 감소하는 기능을 가지는 화면을 구성하고자 한 것이다.

이를 SLW 클래스로 구현하면 그 소스는 다음과 같다.

class SLWdemo extends StatelessWidget {

  int _count = 0;

  @override
  Widget build(BuildContext context) {
    print("** build - StatelessWidget Demo");
    return Scaffold(
      appBar: AppBar(title: Text("Stateless Widget")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              "$_count",
              style: TextStyle(fontSize: 30),
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                FloatingActionButton(
                  heroTag: null,
                  child: Icon(Icons.add),
                  onPressed: () {
                    _count++;
                    print("value of _count = $_count");
                  },
                ),
                FloatingActionButton(
                  heroTag: null,
                  child: Icon(Icons.remove),
                  onPressed: () {
                    _count--;
                    print("value of _count = $_count");
                  },
                )
              ],
            )
          ],
        ),
      ),
    );
  }
}

코드를 살펴보면,

화면에 출력할 숫자 값을 저장할 _count 변수가 필드로 선언되어 있으며, Text 위젯에서 그 값을 출력하도록 하고 있다. 그리고 숫자를 가감할 각 FloatingActionButton의 onPressed 속성에서 _count의 값을 1씩 증가 혹은 감소시키고 있는 것을 확인할 수 있다. 추가로 값을 가감될 때마다 _count의 값을 print 함수로 출력하게 구현했다.

그럼 실제 버튼을 클릭할 때마다 화면상의 값이 변경될까?

그렇지 않다. 버튼을 클릭하여도 화면상의 숫자의 변화는 발생하지 않는다.

그러나 실행창을 확인하면 다음과 같이 실제 _count의 값은 정상적으로 변경되고 있는 것을 확인할 수 있다.

Performing hot reload...
Syncing files to device Android SDK built for x86...
I/flutter (23923): ** build - StatelessWidget Demo
Reloaded 0 of 468 libraries in 105ms.
I/flutter (23923): value of _count = 1
I/flutter (23923): value of _count = 2
I/flutter (23923): value of _count = 3
I/flutter (23923): value of _count = 2
I/flutter (23923): value of _count = 1

 

다시 말하면, 각 버튼을 클릭하여 해당 버튼의 onPressed 속성의 함수에 의해 _count의 값을 정상적으로 증가하거나 감소되고 있으나 변경된 _count 값이 Text 위젯에서 반영되고 있지 않다는 것을 확인할 수 있다.

이 점이 SLW의 특징이다. 이 특징을 이해하기 위해서는 build 메서드에 대해서 먼저 알아야 한다. build 메서드는 SLW과 SFW(더 정확히는 SFW의 State 클래스)에서 구현되며 화면을 구성할 UI들을 구현하는 메서드다.

 

 

 

즉 화면이 출력될 때 build 메서드가 호출되면서 build 메서드 내부에 구현한 UI 위젯들이 화면에 출력된다는 것이다.

 

 

 

이제 다시 SLW 코드를 살펴보자 build 메소드 내부 첫 번째 줄에 print 함수가 삽입되어 있는 것을 확인할 수 있으며 build 메서드가 호출될 때마다 실행창에 메시지를 출력할 것이다. 그리고 위 실행 창의 세 번째 줄에 메시지가 출력되는 것을 확인할 수 있다. 그런데 5번째 줄부터 내용을 다시 확인해 보면 버튼을 클릭할 때마다 _count 값을 출력하는 print 함수는 호출되고 있지만 build 메서드 첫 번째 줄의 print 함수는 호출되지 않고 있다.

이 점이 화면상에 숫자의 변화가 없는 이유이며 SLW의 특징이기도 하다.

StatelessWidget은 이름 그대로 상태(State)를 가지지 않는 위젯 클래스다. 그래서 SLW 내부의 모든 UI 위젯들은 상태를 가질 수 없으며 상태가 없으니 상태의 변화를 인지할 필요도 없고 할 수도 없는 것이다. 그래서 화면이 생성될 때 한 번만 build 메서드를 호출해서 화면을 구성한 후에는 build 함수가 다시 호출되지 않는다. 버튼을 클릭하여 _count의 값을 변경시키더라도 build 메서드는 호출되지 않으므로 화면 내 Text 위젯의 값도 변경되지 않는 것이다.

SLW을 정리하면,

SLW은 변화가 필요없는 화면을 구성할 때 사용하는 위젯 클래스이며, 그렇기 때문에 build 메서드는 한 번만 호출된다.

 

2. StatefullWidget

SFW은 한번 생성한 화면의 구성이 어떠한 이유로 인해 변경될 수 있는 경우에 사용하는 위젯 클래스다.

SFW의 경우도 다음과 같이 동일한 UI 구성의 화면으로 구현한다.

 

우선 SLW 데모에서 사용한 소스코드를 그대로 이용해서 SFW 데모 코드를 다음과 같이 작성한다.

class SFWdemo extends StatefulWidget {
  @override
  SFWdemoState createState() => SFWdemoState();
}

class SFWdemoState extends State<SFWdemo> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    print("** build - StatefulWidget Demo");
    return Scaffold(
      appBar: AppBar(title: Text("Statefull Widget")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              "$_count",
              style: TextStyle(fontSize: 30),
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                FloatingActionButton(
                  heroTag: null,
                  child: Icon(Icons.add),
                  onPressed: () {
                    _count++;
                    print("value of _count = $_count");
                  },
                ),
                FloatingActionButton(
                  heroTag: null,
                  child: Icon(Icons.remove),
                  onPressed: () {
                    _count--;
                    print("value of _count = $_count");
                  },
                )
              ],
            )
          ],
        ),
      ),
    );
  }
}

SFW으로 화면을 구성할 때에는 SLW의 경우와는 다르게 StatefulWidget을 상속하는 위젯 클래스와 State를 상속하는 상태 클래스 두 개로 구성된다. 그리고 화면을 구성하는 build 메서드를 SFW 클래스가 아닌 SFW 클래스 타입의 State를 상속하는 상태 클래스에서 구성한다.

왜 이렇게 언어를 개발했는지는 모르겠지만, 매번 하나의 화면을 두개의 클래스로 나눠서 개발하는 점이 번거롭다. 심지어 대부분의 경우 SFW 위젯 클래스는 상태 클래스를 생성시키는 기능만 하는 것이 다인 경우가 많다.

 

 

 

 

어쨌든 소스 코드로 돌아와서 상태 클래스 내부에 build 메서드의 내용은 위에서 살펴본 SLW 위젯의 소스코드와 일치한다.(AppBar의 텍스트 내용만 다르다.)

그런데 버튼을 클릭해보면 아직까진 화면상의 숫자의 변화는 발생하지 않고 실행 창의 프린트되는 값만 변경되는 것을 확인할 수 있다.

Performing hot reload...
Syncing files to device Android SDK built for x86...
I/flutter (23923): ** build - StatefulWidget Demo
Reloaded 0 of 468 libraries in 110ms.
I/flutter (23923): value of _count = 1
I/flutter (23923): value of _count = 2
I/flutter (23923): value of _count = 3
I/flutter (23923): value of _count = 2
I/flutter (23923): value of _count = 1

 

여기서 알 수 있는 것은, 단순히 StatefulWidget으로 구현한다고만 해서 화면내부 UI가 참조하는 값(속성)의 변화가 화면에 바로 반영되진 않는다는 것이다. 버튼을 클릭하여 _count의 값을 변경하여도 UI는 자신이 참조하는 _count의 값이 변경되었음을 인지하지 못하는다는 것이다. 실제로 실행창에 출력된 값을 보더라도 SLW의 경우와 같이 _count의 값은 변하지만 build 메서드는 호출되고 있지 않고 있는 것을 알 수 있다.

이렇게 변경된 값이 UI 위젯에 반영하기 위해 이용하는 메소드가 setState다.

소스코드를 아래와 같이 변경한다.

class SFWdemo extends StatefulWidget {
  @override
  SFWdemoState createState() => SFWdemoState();
}

class SFWdemoState extends State<SFWdemo> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    print("** build - StatefulWidget Demo");
    return Scaffold(
      appBar: AppBar(title: Text("Statefull Widget")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              "$_count",
              style: TextStyle(fontSize: 30),
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                FloatingActionButton(
                  heroTag: null,
                  child: Icon(Icons.add),
                  onPressed: () {
                    setState(() {
                      _count++;
                    });
                    print("value of _count = $_count");
                  },
                ),
                FloatingActionButton(
                  heroTag: null,
                  child: Icon(Icons.remove),
                  onPressed: () {
                    setState(() {
                      _count--;
                    });
                    print("value of _count = $_count");
                  },
                )
              ],
            )
          ],
        ),
      ),
    );
  }
}

 

위의 소스코드를 보면 기존의 코드의 각 버튼의 onPressed 속성에서 구현하는 함수의 내용이 조금 바뀌었다. _count 변수의 값을 setState 메서드로 감싼 후 변경하는 것을 알 수 있다.

메서드의 이름에서 알 수 있듯이 setState 메서드는 SFW 내부의 상태를 변경할 때 사용하는 메서드이며 setState 메서드에서 변경된 상태 값을 플랫폼에 전달하여 build 메서드가 호출되도록 한다.

좀 더 자세히 설명하자면,

위 코드에서는 필드인 _count 변수가 이 화면구성의 상태(State)가 된다. 그리고 이 필드는 Text 위젯에서 참조하고 있다. 각 버튼이 클릭될 때 setState 메서드 내부에서 상태 값인 _count의 값을 변경하면, 변경과 동시에 변경 사실이 플랫폼으로 전달되어 build 메서드가 다시 호출되게 되고 Text 위젯은 참조하고 있는 _count의 최신 값을 화면에 출력하게 되는 것이다.

화면을 보면 다음과 같이 클릭하는 버튼에 따라 값이 변경되는 것을 확인할 수 있다.

 

실행창도 확인해 보자.

Performing hot reload...
Syncing files to device Android SDK built for x86...
I/flutter (23923): ** build - StatefulWidget Demo
Reloaded 0 of 468 libraries in 109ms.
I/flutter (23923): value of _count = 1
I/flutter (23923): ** build - StatefulWidget Demo
I/flutter (23923): value of _count = 2
I/flutter (23923): ** build - StatefulWidget Demo
I/flutter (23923): value of _count = 3
I/flutter (23923): ** build - StatefulWidget Demo
I/flutter (23923): value of _count = 2
I/flutter (23923): ** build - StatefulWidget Demo
I/flutter (23923): value of _count = 1
I/flutter (23923): ** build - StatefulWidget Demo

버튼이 클릭될 때마다 _count의 값이 변경되고 있으며 동시에 build 메서드가 호출되는 것을 확인할 수 있다.

 

SFW은 다음과 같이 정리할 수 있다.

  • SFW은 화면의 구성이 상태 변화에 따라 재구성되어야 할 때 사용된다.
  • SFW의 상태 변경은 setState 메서드를 이용해서 변경해야 한다.
  • 플랫폼은 setState 메서드가 호출될 때마다 build 메서드를 재호출하여 화면을 다시 그린다.

 

3. 전체 소스 코드

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: MainPage(),
  ));
}

class MainPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("MainPage")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            RaisedButton(
              child: Text("Launch StatelessWidget"),
              onPressed: () {
                Navigator.push(context,
                    MaterialPageRoute(builder: (context) => SLWdemo()));
              },
            ),
            RaisedButton(
              child: Text("Launch StatefullWidget"),
              onPressed: () {
                Navigator.push(context,
                    MaterialPageRoute(builder: (context) => SFWdemo()));
              },
            )
          ]
              .map((children) => Container(
                    width: 200,
                    child: children,
                  ))
              .toList(),
        ),
      ),
    );
  }
}

class SLWdemo extends StatelessWidget {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    print("** build - StatelessWidget Demo");
    return Scaffold(
      appBar: AppBar(title: Text("Stateless Widget")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              "$_count",
              style: TextStyle(fontSize: 30),
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                FloatingActionButton(
                  heroTag: null,
                  child: Icon(Icons.add),
                  onPressed: () {
                    _count++;
                    print("value of _count = $_count");
                  },
                ),
                FloatingActionButton(
                  heroTag: null,
                  child: Icon(Icons.remove),
                  onPressed: () {
                    _count--;
                    print("value of _count = $_count");
                  },
                )
              ],
            )
          ],
        ),
      ),
    );
  }
}

class SFWdemo extends StatefulWidget {
  @override
  SFWdemoState createState() => SFWdemoState();
}

class SFWdemoState extends State<SFWdemo> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    print("** build - StatefulWidget Demo");
    return Scaffold(
      appBar: AppBar(title: Text("Statefull Widget")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              "$_count",
              style: TextStyle(fontSize: 30),
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                FloatingActionButton(
                  heroTag: null,
                  child: Icon(Icons.add),
                  onPressed: () {
                    setState(() {
                      _count++;
                    });
                    print("value of _count = $_count");
                  },
                ),
                FloatingActionButton(
                  heroTag: null,
                  child: Icon(Icons.remove),
                  onPressed: () {
                    setState(() {
                      _count--;
                    });
                    print("value of _count = $_count");
                  },
                )
              ],
            )
          ],
        ),
      ),
    );
  }
}
 
 
반응형
반응형

thisisdivakars said: 
how to set manually
Go to Player Settings -> Select Android -> Other Settings -> For Target API select API 29 (As of now API 30 is recommended at the time of this response)

 

 

ref : https://forum.unity.com/threads/android-sdk-does-not-include-your-target-sdk-of-28.1002894/

반응형

+ Recent posts