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?
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 isQHierarchy.
페이로드(payload)는전송되는 데이터를 의미합니다. 데이터를 전송할 때, 헤더와 메타데이터, 에러 체크 비트 등과 같은 다양한 요소들을 함께 보내어, 데이터 전송의 효율과 안정성을 높히게 됩니다. 이 때, 보내고자 하는데이터 자체를 의미하는 것이 바로 페이로드입니다. 우리가 택배 배송을 보내고 받을 때, 택배 물건이 페이로드이고, 송장이나 박스, 뾱뾱이와 같은 완충재 등등은 부가적인 것이기 때문에 페이로드가 아닙니다.
추가적으로 위키피디아에 아주 이해하기 좋은 예시가 아래와 같이 나와있어서 첨부합니다.
페이로드(payload)라는 단어는 운송업에서 비롯하였는데,지급(pay)해야 하는적화물(load)을 의미합니다. 예를 들어, 유조선 트럭이 20톤의 기름을 운반한다면 트럭의 총 무게는 차체, 운전자 등의 무게 때문에 그것보다 더 될 것이다. 이 모든 무게를 운송하는데 비용이 들지만, 고객은 오직 기름의 무게만을 지급(pay)하게 된다. 그래서 ‘pay-load’란 말이 나온 것이다
json으로 보는 실제 데이터에서의 payload는 아래의 json에서 “data”입니다. 그 이외의 데이터들은 전부 통신을 하는데 있어 용이하게 해주는 부차적인 정보들입니다.
Making games is more accessible than it ever has been. There's now a myriad ofgame 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 tobuilding 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 companyacquired 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.
방향에 따른 과목들은 상담을 통해 설정됩니다 추가 과목에 대한 추가 비용은 들지 않으며 방향에 따라 과목들이 정해집니다
일반 수학, 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 네트워크 데이' 에서 정보 교류활동의 기회
병합 요청(MR)은 한 브랜치를 다른 브랜치로 병합하기 위한 요청이다. 병합 요청을 사용하여 소스 코드에 대해 제안된 변경사항을 시각화하고 협업한다.
개요
병합 요청(일명 "MR")은 제안된 변경사항에 대한 많은 정보를 표시한다. MR의 본문에는 위젯(CI/CD 파이프라인이 있는 경우, 관련 정보 표시)과 함께 설명이 포함되며, 그 뒤에 해당 MR로 협업하는 사람들의 토론 스레드가 표시된다.
MR에는 스레드에서 발생하는 토론, 커밋 목록, 파이프라인 및 Job 목록, 코드 변경 및 인라인 코드 리뷰를 볼 수 있는 내비게이션 탭도 포함되어 있다.
사용 사례
A. 팀에서 일하는 소프트웨어 개발자일 경우
새 브랜치를 체크아웃하고 병합 요청을 통해 변경사항을 제출한다.
팀에서 피드백을 수집한다.
코드 품질 보고서로 코드를 최적화하는 구현 작업을 수행한다.
GitLab CI/CD의 단위 테스트 보고서로 변경사항을 검증한다.
라이선스 준수 보고서를 통해 라이선스가 프로젝트와 호환되지 않는 종속성을 사용하지 않도록 한다. (ULIMATE)
관리자에게 승인을 요청한다. (STARTER)
관리자 : a. 최종 리뷰와 함께 커밋을 푸시한다. b. 병합 요청을 승인한다. (STARTER) c. 파이프라인이 성공하면 병합되도록 설정한다.
변경사항은 GitLab CI/CD에 대한 수동 작업을 통해 프로덕션에 배포된다.
구현이 성공적으로 고객에게 전달된다.
B. 회사의 웹 사이트를 위한 웹페이지를 작성하는 웹 개발자일 경우
새 브랜치를 체크아웃하고 병합 요청을 통해 새 페이지를 제출한다.
리뷰어(Reviewers)로부터 피드백을 수집한다.
Review Apps로 변경사항을 미리 볼 수 있다.
웹 디자이너에게 구현을 요청한다.
관리자에게 승인을 요청한다. (STARTER)
승인되면, 병합 요청이 스쿼시(Squash) 및 병합되고, GitLab Pages를 사용하여 스테이징(Staging)에 배포된다.
프로덕션 팀이 프로덕션으로 병합 커밋을 체리 픽(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, 행동 유도 또는 클릭 유도)을 확인한다.
I have a base classMediaand several derived classes, namelyDVD,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 warningsclass 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:
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 ?
유니티는 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);
}
}
자동차 바퀴는 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키를 누릅니다. 그러면 애셋에서 모든 피직스 바디가 제거됩니다.
이제 루트 조인트부터 시작해서 비히클 조인트에 피직스 바디를 만들기 시작합니다. 염두에 둘 것은, 물리 시뮬레이션을 하거나 비히클의 바운드에 영향을 끼칠 필요가 있는 조인트에만 피직스 바디가 필요하다는 것입니다.
여기 우리 버기의 경우, 루트에 대한 박스나 휠에 대한 구체가 시작하기 딱 좋을 것이며, 원하는 동작을 내기 위해서는 여러가지 다른 피직스 바디가 필요할 것입니다.
바디 콜리전 생성
비히클 루트에 대한 박스를 생성하려면:
계층구조패널에서 루트 조인트에 우클릭합니다.
바디 추가를 선택합니다.
콜리전 지오메트리를박스로 설정합니다.
"OK" 를 클릭합니다.
그 후 새로운피직스 바디를 이동, 회전, 스케일 조절하여 비히클의 모양을 더욱 잘 추정해 낼 수 있습니다.
휠 콜리전 생성
휠에 쓸 구체를 생성하려면:
계층구조패널에서 휠 조인트에 우클릭합니다.
바디 추가를 선택합니다.
콜리전 지오메트리를구체로 설정합니다.
"OK" 를 클릭합니다.
디테일패널에서Physics Type프로퍼티를 찾아Kinematic으로 설정해 줍니다.비히클의 바운드에 영향을 끼치려는 휠에는 필수인데, 그림자와 컬링이 정상 작동하도록, 또 게임이 시작되면 비히클에서 휠이 떨어져 나오지 않도록 하기 위해서입니다.
휠피직스 바디는 절대 실제 콜리전에 사용되지 않습니다. 현재 휠은 레이 캐스트를 사용하여 월드와의 상호작용을 처리합니다.
더 나아가서
간단한 셋업은 이렇습니다. '비히클 게임' 내 '비히클' 의 '피직스 애셋' 을 살펴보면 이렇습니다:
휠 외에 모든 콜리전 바디는 비히클의 루트 조인트상에 존재합니다. 휠이 특정 게임 요소에 걸리지 않도록 하고, 휠의 메시가 벽이나 철책에 잘리지 않도록 하기도 합니다.
이 경고는 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.
};
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 로 만들어준다.
일반적으로 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전송이 다시금 불가능해진 것으로 판단하여, 릴레이 서버를 이용하도록 하면서 지금껏 전송 확인이 안된 패킷들을 릴레이를 통해 전달하면 됩니다. 이렇게 해야 패킷의 지연은 있을 수 있으나 패킷의 소실은 발생하지 않습니다.
콘텐츠 브라우저에서 새 애셋 이름을 지을 때, 다이내믹 머티리얼 인스턴스의 파라미터를 변경할 때, 스켈레탈 메시에서 본에 접근할 때, 모두 FName 을 사용합니다. FName 은 문자열 사용에 있어서 초경량 시스템을 제공하는데, 주어진 문자열이 재사용된다 해도 데이터 테이블에 한 번만 저장되는 것입니다. FName 은 대소문자를 구분하지 않습니다. 변경도 불가능하여, 조작할 수 없습니다. 이처럼 FName 의 정적인 속성과 저장 시스템 덕에 찾기나 키로 FName 에 접근하는 속도가 빠릅니다. FName 서브시스템의 또다른 특징은 스트링에서 FName 변환이 해시 테이블을 사용해서 빠르다는 점입니다.
In Unreal Engine 4 (UE4) the primary component fortext localizationis theFTextclass. All user-facing text should use this class, as it supports text localization by providing the following features:
FTextalso features theAsCultureInvariantfunction (or theINVTEXTmacro), 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 blankFTextusing eitherFText::GetEmpty(), or by using justFText().
FName 이나 FText 와는 달리, FString 은 조작이 가능한 유일한 스트링 클래스입니다. 대소문자 변환, 부분문자열 발췌, 역순 등 사용가능한 메서드는 많습니다. FString 은 검색, 변경에 다른 스트링과의 비교도 가능합니다. 그러나 바로 그것이 FString 이 다른 불변의 스트링 클래스보다 비싸지는 이유입니다.
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()를 사용하는 편이 보다 효율적입니다.
해당 클래스는 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 aPlayerPrefsException.
화면에 출력할 숫자 값을 저장할 _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 데모 코드를 다음과 같이 작성한다.
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 메서드는 호출되고 있지 않고 있는 것을 알 수 있다.
위의 소스코드를 보면 기존의 코드의 각 버튼의 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 메서드를 재호출하여 화면을 다시 그린다.
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)