실시간 게임을 제작하는 과정에서, 애니메이션 제어를 스크립트로 직접 구성하는 것보다

유니티에 구현된 메카님 시스템을 잘 사용하는게 시간이 절약된다고 판단하여 메카님 시스템을 통해 구현

그동안 구현하면서 느낀점들

 

설계 방향

 - 시스템에서는 상태값(Parameters)을 애니메이터에게 지속적으로 넘긴다.

 - animator.Play 등을 통해 강제로 특정한 노드를 플레이 시키지 않는다.

 - animator.speed는 히트프리즈와 같은 기능을 구현할때를 제외하고는 수정하지 않는다.

 - 상태전이는 animator의 Transition을 통해서만 구현한다.

 

장점

- 상태 관리를 애니메이터로만 하게 되므로, 코드의 수정이 적어진다.

- 애니메이션 블렌드 등 상태 전이할때 옵션을 세세하게 조정할 수 있다.

- Inspector를 이용해, 전이때 어떻게 애니메이션이 변경되는지 미리 관찰할 수 있다.

 

단점

- animator.speed가 0일때, 상태 전이는 이루어지지 않는다.

- animator override controller를 구현해도 세부적으로 Actor들마다 구현이 미묘하게 달라, 결국 각각의 애니메이터를 만들어야 하는 상황도 발생

- SubState 노드를 만든다면, animator.Play로 진입시, Default 노드의 기준을 알수가 없다.

 

추후 개선 할점

- 히트프리즈 (animator.speed = 0)를 구현한뒤, 특정 액션이 끝날때 애니메이션이 끝나는것을 맞춰야 한다면, 어떻게 설계할 것인가 ?

- node의 normalizeTime을, 코드에서 넘겨준 값으로 설정할때 blend와 상태전이는 어떻게 이루어 질것인가 ?

 

초기 게임을 만들때는 히트프리즈 또는 세부적인 애니메이션 재생을 고려하지 않고 만들어 졌지만 (초기에는 animator.Play(노드 이름) 으로 처리) 추후에 확장되면서 여러가지 변경이 이루어 졌다.

여러번 설계가 변경되면서 얻은 정보들을 적는다.

 

1. 애니메이션의 재생 속도는 animator.speed를 변경하는 것보다. Node의 Multiply를 변경하는것이 좋다.

애니메이터 창을 열어본뒤 노드를 선택하면 인스펙터에 다음과 같이 나타난다.

필드를 보면 Multiplier 라는 곳이 비활성화 되어있다.

animator 의 parameters에 float 파라미터를 한개 추가한뒤, 애니메이터 노드 (위 사진)의 Multiplier -> Parameter를 체크한다.

위의 사진은

1. animator의 파라미터에 WholeActive라는 파라미터를 float으로 만들었다.

2. Multiplier에 Parameter를 체크하고, WholeActive를 드롭다운 메뉴에서 선택했다.

 

실제로 사용할때는 코드에서 

animator.SetFloat(파라미터 이름, 값) 으로 설정한다.

* string으로 값을 넘기는것은 성능이 좋지 않기때문에

int hash = Animator.StringToHash("파라미터 이름");

aniamtor.SetFloat(hash, 값)로 처리한다.

 

위 사진의 Death의 Clip 길이가 2초이고, WholeActive의 값을 2로 설정하면

2배의 속도로 재생된다. (1초동안 애니메이션이 재생됨)

 

처음 써보기 전에는 애니메이터가 정확하게 예상한대로 시간을 맞춰줄까 ? 의심했지만.

사용해보니 잘 맞춰춘다.

 

2. 애니메이션이 중간에 멈추거나 다른 동작으로 자주 바뀐다면, Trigger보다 Bool 값을 통해서 전환하는게 유리하다.

이동중에 공격을 하거나, 공격 도중 움직이면 애니메이션이 Stand로 돌아가야 하는 등, 실시간으로 상태가 자주 바뀐다면, Transition을 할때 Bool을 통해서 하는게 유리하다.

 

다음과 같이 Transition (화살표 선)을 클릭하면, 조건을 설정할 수 있는데

다음과 같이 Bool 값을 통해서 전이하는게 유리하다.

이렇게 처리하면

 

1] 공격이 시작될때 animator에 true를 전달

2] 공격이 끝나면 animator에 false를 전달.

3] 공격 도중, 중단한다면 animator에 false를 전달.

 

false일때 재생중인 노드를 나오도록 설정하면, 편리하다

 

3. 상태전이가 동시에 여러번 일어날때, Interruption Source 옵션을 활용한다.

다음과 같은 상황을 예시로 들어보자.

상황 : 모든 Transition (상태 전이)은 Blend가 0.2초로 설정되어 있다.

1] 현재 Move 애니메이션을 재생하고 있다.

2] 멈춘다음 바로 공격을 한다. (Move -> Stand -> Atk_02)

 

별도의 설정을 하지 않았다면, 실제로 코드는 공격을 실행하고 있지만 (파라미터의 값도 변경되어 있지만) 애니메이터는 Move -> Stand로 전이를 하는 중이다 (0.2초가 지나지 않아서)

하지만 일반적으로 다른 파트에서는 공격을 시작했으니 Atk_02가 바로 재생되기를 원한다.

 

이럴때는 Move -> Stand 로 향하는 Transition (화살표) 를 클릭 한뒤 다음과 같이 설정하자.

Settings 에서 Interruption Source를 Next State로 설정한다.

Animator Interruption Source로 검색하면 더 많은 내용이 나오지만

간단하게 설명하자면

 

현재 Transition중일때, 다음 Transition 조건에 해당된다면 현재를 건너뛰고 바로 다음으로 가는것이다.

ex) Move -> Stand로 Transition 중일때, Stand -> Atk_02로 Transition이 가능한 상태라면 Atk_02로 바로 전이를 시작한다.

 

이때 Blend는 현재를 기준으로 진행되기 때문에 부자연스럽지 않다.

=> Move의 애니메이션 마지막 상태에서 Atk_02로 블렌드

'프로그래밍 > Unity (유니티)' 카테고리의 다른 글

WWW Request (post)  (0) 2018.09.05
Unity 다른 이름 앱 빌드  (0) 2018.07.20

S3에 있는 파일을 EFS로 옮겨야 되는데 AWS CLI를 사용해야 되는 상황이 있다.


근데 내컴퓨터가 윈도우라서 AWS CLI를 사용해야 되는데 PowerShell이 반응이 없는 상황이 자주 나와서 ㅡㅡ...


이번에는 최근 마이크로소프트에서 리눅스를 잘 지원해주면서 생긴 꿀기능으로 AWS CLI를 실행해보자.


우리는 Windows Subsystem for Linux 를 이용할것이다.


먼저 참고한 문서들은 다음과 같다.


https://docs.microsoft.com/ko-kr/windows/wsl/install-win10

https://docs.aws.amazon.com/ko_kr/powershell/latest/userguide/pstools-getting-set-up-windows.html

https://docs.aws.amazon.com/ko_kr/cli/latest/userguide/install-linux-python.html

https://superuser.com/questions/1319047/cant-install-virtual-interpreter-in-pycharm-in-linux

https://docs.aws.amazon.com/ko_kr/cli/latest/userguide/install-linux.html


1. WSL 설치


시작에서 PowerShell을 오른쪽 클릭, 관리자 권한으로 실행한다.

그다음 명령어를 입력한뒤 엔터

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux


이러면 재부팅이 된다. 정상적인 것이다.

다음 재부팅이 끝났으면 마이크로소프트 스토어에서 Ubuntu 를 설치한다.


우린 이제 이 우분투로 AWSCLI를 사용할 것이다.


2. Python, PIP 설치하기


AWS CLI를 설치하기 위해서는 Python과 PIP가 필요한데 이것들을 먼저 설치해보자.

우분투는 방금 깔아서 텅 비어있기 때문에 설치해줘야 한다.


- Python 설치

우리는 Ubuntu 이기 때문에 이 명령어로 설치한다.

sudo apt-get install python3


다음과 같은 명령어로 잘 설치되었는지 확인한다.

python3 --version


주의 : 파이썬3를 설치했기 때문에 대부분 명령어에서 python을 치라고 하면 python3로 바꿔서 써줘야 된다.


- PIP 설치

파이썬 스크립트를 하나 받는다..

curl -O https://bootstrap.pypa.io/get-pip.py

※ 0이아니라 대문자 O임


파이썬 스크립트 실행할때 필요한 유틸을 받는다

sudo apt-get install python3-distutils


스크립트를 실행해서 PIP를 다운로드, 설치한다.

python3 get-pip.py --user

여기까지 PIP 설치 완료


3. PATH 설정하기

이제 PATH를 연결해 보자.

export PATH=~/.local/bin:$PATH

해당 커맨드를 한번 실행한뒤

source ~/.profile

커맨드를 실행한다.


pip --version

했을때 에러없이 pip 숫자 ~~~ 버전이 이렇게 나와야 한다.


4. AWSCLI 설치

pip를 통해서 설치한다.

pip install awscli --upgrade --user


aws --version으로 정상 설치됬는지 확인한다.


5. 명령줄 경로에 AWS CLI 실행파일 추가


이과정까지 하면 설치 끝


PowerShell은 그냥 못쓴다고 생각하고 리눅스에 적응하는게 나은것 같다.

이제 도메인 주소를 입력하면 S3의 버킷으로 연결되어야 한다.

Route 53을 이용해서 연결시켜 보자.

AWS Console 에서 Route 53 메뉴로 이동한다.

호스트 존이 아무것도 없다면 Create Hosted Zone을 클릭하고 

있다면 해당 호스트 존을 클릭하면 된다.


호스트 존 이름은 버킷 이름과 같게 하면 된다. (www가 없는 버킷 이름)


다음과 같이 4개의 Record Set이 필요하다.

Record Set을 만들었다면 먼저 

NS와 SOA는 생성 되어있을 것이다.


A를 생성해보자 (Type)

CreateRecordSet 버튼을 누르면 다음과 같은 창이 오른쪽에 뜬다.

Name 에 아무것도 입력하지 않고

Type은 A - IPv4 Address로 설정한다.

Alias를 Yes로 변경한다.

그러면 다음과 같이 S3의 버킷이 보인다.

해당 버킷을 클릭해서 연결시켜준다.

Routing Policy는 simple

Evaluate Target Health는 NO로 설정한다.


이제 spiritlink.online 주소를 통해 S3 버킷 접속이 가능해 졌다.

www.spiritlink.online 주소를 통해 S3 버킷에 연결시켜보자.


CreateRecordSet 버튼을 클릭한 다음 Name에 www 를 입력한다.

그다음 똑같이 Alias를 Yes로 선택하고 S3버킷을 선택한다. 

* 주의 : www.spiritlink.online 버킷과 spiritlink.online 버킷은 별도이다.

www.spiritlink.online 주소를 입력하면 S3에서 spiritlink.online 버킷으로 연결되도록 처리가 필요하다.



Routing Policy 를 Simple로 선택하고

Evaluate Target health 를 NO로 선택한뒤 생성을 완료한다.


이제 다른 사이트에서 구매한 도메인에 연결시켜보자.

나는 가비아 에서 도메인을 구매했는데 다른 사이트도 이와 비슷하다.

도메인을 구매한 사이트에 도메인 정보로 들어가게 되면 도메인 네임 서버 주소를 입력하는 창이 있다.

여기에 NS 에 적혀있는 4개의 주소를 입력하면 된다.


1차, 2차, 3차, 4차 도메인 네임서버를 입력해 준뒤

몇분이 지나면 정상적으로 호스팅 되는것을 확인할 수 있다.

AWS 에서 웹사이트를 호스팅 하기 위해서는 다양한 서비스의 조합이 필요하다.

그중에 웹사이트를 호스팅 하기 위해서 제일 먼저 필요한것은

js, html 등 작성한 코드, 리소스 들이다.

먼저 해당 리소스들을 업로드 해보자.


aws console에서 s3로 이동한다.


그뒤 버킷을 생성한다.

버킷 이름은 도메인 이름과 같게 설정하는게 좋다.

리전은 서울로 설정한다.

다음을 전부 넘기고 생성한다.


이렇게 생성한뒤 버킷 이름을 클릭하면 다음과 같은 창이 보인다.

업로드 버튼을 누른뒤 js 파일, html 파일 등을 업로드 한다.

파일 추가 버튼을 누르는것보다

마우스로 드래그 한뒤 드랍하는게 훨씬 편하다. (폴더 내의 파일들까지 알아서 업로드 할 수 있다.)


다음과 같이 파일이 업로드 되었다. 이제 웹사이트 호스팅을 시작해보자 (S3에서)

속성 탭으로 이동한뒤 정적 웹 사이트 호스팅을 클릭한다.

이 버킷을 사용하여 웹 사이트를 호스팅한다를 클릭한다.

그 뒤 위의 엔드포인트 옆 주소를 클릭한다.

그러면 403 Forbidden 이 출력된다.

읽기 권한이 할당되지 않아서 이러한 현상이 발생한다.

인덱스에 자신의 프로젝트 index.html을 입력해주면 된다.

버킷에서 권한 -> 버킷 정책 탭으로 이동한다.


다음과 같이 입력하면 된다.

모자이크 된 부분은 버킷의 이름이다.

적용이 정상적으로 완료되면 다음과 같이 보인다.


이렇게 처리가 완료되면 엔드포인트 주소를 클릭했을때

웹사이트가 호스팅 되기 시작하는것을 확인할 수 있다.


다음 글에서는 도메인과 연결을 시작해보자.

자바스크립트 코드를 작성하다 보면 객체 내에서 일부 데이터만 필요하고

나머지 데이터는 필요없는 상황이 많다.

이때 간단하게 오브젝트 내에서 필요한 데이터만 추출해 보자.


1. 배열 (Array)


let data = ["crong", "honux", "jk", "jinny"];
let [jisu,,jung] = data;
console.log(jisu, jung);

data의 배열 내에 4개의 이름이 들어가 있다.

jisu는 data의 0번째, jung은 data의 2번째 값을 받고있다.

1번째 값은 ,,으로 건너 뛰었다.

로그를 출력하면 crong, jk가 출력되는것을 확인할 수 있다.


2. 오브젝트 (Object)


let obj = {
name : "crong",
address : "Korea",
age : 10
};

오브젝트 내에서 원하는 데이터를 뽑고싶다면

let {name, age} = obj;

이렇게 추출하면 된다.

name은 crong

age는 10이 들어있다.


만약 변수의 이름을 다르게 하고 싶다면

let {name:myName, age:myAge } = obj;

myName에 name의 값이 들어가게 되고

myAge에 age의 값이 들어가게 된다.


myName, myAge 변수를 사용하면 된다.


3. JSON 파싱


let news = [
{
"title" : "sbs",
"imgurl" : "http://static.naver.net/1",
"newslist" : [
"[가보니] 가상 경주도 즐기고, 내 손으로 자동차도 만들고",
"'갤럭시S8' 출시? '갤노트7' 처리 계획부터 밝혀야"
]
},
{
"title" : "mbc",
"imgurl" : "http://static.naver.net/2",
"newslist" : [
"Lerem ipsum dolor sit amet",
"ipsum dolor sit amet"
]
}
];

JSON 내의 데이터중 필요한 데이터만 추출해보자.


let [,mbc] = news;

JSON 배열 내의 데이터중 1번째 데이터를 mbc에 할당한다.

let {title, imgurl} = mbc;

title에 mbc의 title값이 담기고 imgurl에 mbc의 imgurl값이 담긴다.

console.log(title, imgurl);

출력하면 mbc와 url주소가 출력된다.


Destructuring을 이용해서

let [, {title, imgurl} ] = news;

바로 뽑아낼 수도 있다.


4. Event 객체 파싱


<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<ul>
<div> "Lerem ipsum dolor sit amet",
"ipsum dolor sit amet"</div>
</ul>
</body>
</html>

HTML에 div 속성으로 다음과 같은 데이터를 삽입한다.


document.querySelector("div").addEventListener("click", function({type,target}){
console.log(type, target.tagName);
});

argument로 객체 전체가 넘어오게 되는데

상당수가 필요없는 데이터 일때가 많다.

type과 target의 데이터만 필요할 경우 function의 파라미터에 { type, target }을 이용해서 해당 데이터만 추출한다.

console.log 를 통해 확인하면 해당 데이터만 전달 받는것을 확인할 수 있다.

배열 안의 모든 값을 확인할때

제일 기본적인 방법은


let data = [1, 2, 3 ];
for(let i = 0; i < data.length; i++){
console.log(i);
}

이렇게 순회할 수 있지만 다음과 같이 forEach에 callback을 이용해서도 가능하다.


let data = [1, 2, 3 ];
data.forEach( function(value){
console.log("valueis", value);
});

상황에 맞춰 편하게 쓰면 될것같다.


let data = [1, 2, 3 ];
for( let value of data ){
console.log( value);
}

for of를 써도 가능


let data = [1, 2, 3 ];
for( let idx in data ){
console.log( data[idx]);
}

for in 같은 경우 상위 객체의 함수가 출력될수 있기 때문에 for i in 은 조심해서 사용하도록 한다.

유니티에서 WWW를 이용해 Post 메시지를 전송했었는데


upload Progress는 1인데 


downloadProgress, progress는 계속 0에 머무르는 현상이 있었다.


while( !www.isDone )

{

Debug.log(www.downloadProgress);

Debug.log(www.uploadProgress);

yield return new WaitForSeconds(0.1f);

}

와 같은 코드로 확인


원인을 확인해보니 WWW를 생성할때 URL, Form만 삽입하는게 아니라


Header를 삽입하지 않을경우 해당문제가 발생했었다.


WWW www = new WWW(URL, form.data, header)와 같은식으로 생성한뒤 post메시지 전송시 정상 처리됨

기존에 ECMA6기반으로 코드를 작성, 프로젝트를 압축 (concat) 하고 난독화 (uglify) 등을 진행할때 gulp를 사용했었습니다.

클래스의 확장, 라이브러리간 의존성이 생겨 task를 나누어서 관리했었는데

다른 클래스를 참조할때 전역객체를 통해 클래스를 참조하다 보니 window.console에 노출되는등 좋지 않은점이 많이 발견되었습니다.

프로젝트가 커질수록 이렇게 관리하는건 시간이 오래 걸린다 판단되어 Webpack으로 전환했습니다.


하지만 인터넷등을 보니 WebPack 개념 자체에 대해서는 설명이 잘 되어있지만

import.. export를 실질적으로 어떻게 하는지 보여주는 부분은 없어서 난감했었는데 해당 부분에 대해 정리해 놨습니다.


webpack의 개념과 모듈, import, export에 관한 개념은 다른 글에 많이 나와있기 때문에

여기서는 실질적인 구성, 실행을 하는것이 목적입니다.


- 목표 - 

1. main 진입점에서 다른 js파일을 import한뒤 import한 클래스의 함수를 이용해서 로그를 찍어보자. 

2. js파일을 한개로 압축한뒤 minify 시킨다.


현재 사용하는 개발도구는 IntelliJ 입니다. Webstorm은 같은 Jetbrain에서 만든 도구라 크게 다른점은 없을거라 판단되며

기타 도구들도 Node.js 와 npm이 있다면 충분히 가능할것으로 판단됩니다.


[1] terminal 에서 webpack을 global로 설치한다.


정상적으로 설치가 완료되면 위 화면과 같이 나옵니다. (현재 사진은 재설치한 상태)

설치가 되지 않는다면 Node.js설치가 제대로 되었는지 확인해 본다.


[2] terminal 에서 webpack-cli를 global로 설치한다.


의존성과 관련된 문제는 스스로 설치해서 해결해야 된다고 나와있지만

현재 프로젝트 자체가 깨끗하게 비어있기 때문에 문제되는 부분은 없습니다.


[3]webpack.config.js 파일을 생성



-> webpack을 통해 빌드할때 환경설정을 하는 스크립트라고 생각하면 된다.


module.exports = {

mode: 'development',

context: __dirname + '/app', // 모듈 파일 폴더
entry: { // 엔트리 파일 목록
app: './main.js'
},
output: {
path: __dirname + '/dist', // 번들 파일 폴더
filename: '[name].bundle.js' // 번들 파일 이름 규칙
}
}

환경설정과 관련되서 현재 프로젝트의 디렉토리에 관한 설명



현재 프로젝트는 app폴더, dist폴더로 나뉘어져 있다.


app폴더는 기존처럼 우리가 작성한 스크립트들이 들어있는 폴더이고

dist폴더는 webpack을 통해서 하나로 합쳐진 파일이 저장되는 폴더이다.


스크립트를 저장하는 폴더명 (모듈 파일 폴더 이름)을 바꾸고 싶다면 webpack.config.js에서

'/app' 부분을 변경하면 된다 (작성한 코드를 저장하는 폴더)


하나로 합쳐져서 나오는 폴더명 (번들 파일 폴더 이름)을 바꾸고 싶다면

'/dist' 부분을 변경하면 된다 (합쳐진 코드를 저장하는 폴더)



app 폴더 안에 진입점이 될 main.js 와 모듈이 될 Utils.js를 생성합니다.


main.js

"use strict";

import Utils from './Utils'
Utils.log(' Hello Webpack ');

console.log(" Yes !");

Utils.js

"use strict";

export default class Utils{
static log(msg){ console.log('[LOG]' + msg ); }
}


진입점 에서는 Utils 클래스의 static함수를 이용해서 로그를 찍을 예정입니다.

이제 터미널에 webpack 커맨드를 사용해서 빌드합니다.



빌드가 정상적으로 완료되면 이렇게 뜹니다.

dist폴더에 빌드된 파일 (app.bundle.js) 을 확인해보면


/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "./main.js");
/******/ })
/************************************************************************/
/******/ ({

/***/ "./Utils.js":
/*!******************!*\
!*** ./Utils.js ***!
\******************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return Utils; });\n\r\n\r\nclass Utils{\r\n static log(msg){ console.log('[LOG]' + msg ); }\r\n}\n\n//# sourceURL=webpack:///./Utils.js?");

/***/ }),

/***/ "./main.js":
/*!*****************!*\
!*** ./main.js ***!
\*****************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _Utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Utils */ \"./Utils.js\");\n\r\n\r\n\r\n_Utils__WEBPACK_IMPORTED_MODULE_0__[\"default\"].log(' Hello Webpack ');\r\n\r\nconsole.log(\" Yes !\");\n\n//# sourceURL=webpack:///./main.js?");

/***/ })

/******/ });

다음과 같이 합쳐진 코드를 확인할 수 있습니다.

추후에 config에서 development 로 되어있는 옵션을 Production 으로 변경한뒤

concat, babel, minify 옵션을 적용하면 코드를 한줄로 압축, 난독화해서 처리할 수 있습니다.


이제 index.html 파일을 다음과 같이 수정합니다.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="dist/app.bundle.js"></script>
</body>
</html>

빌드된 파일 1개만 로드하게 하면 됩니다.

그후 index.html 파일을 우클릭 한뒤 Run, 또는 Debug로 실행하면


다음과 같이 Utils.js의 함수를 이용해서 정상적으로 Log가 생성되는걸 확인할 수 있습니다.


1. webpack설정에서 진입점만 정의한뒤

2. 다른 파일에 있는 클래스를 호출할때는 import를 통해서 호출하면

3. Webpack이 빌드할때 의존성 관련된 부분을 알아서 처리한뒤

4. 한개의 파일로 합쳐준다.

5. index.html 에서는 합쳐준 파일 1개만 로드하면 된다.


이렇게 요약할수 있습니다.

Three js 에서 애니메이션 재생시 애니메이션 한개마다 FBX 파일을 로드, 처리하는건 불필요하다고 판단되어


3D Max에서 JD 파일로 모델을 애니메이션 정보와 함께 출력했다.


JD 파일을 이용해서 씬에 출력 및 애니메이션을 재생해보자.


JDLoader.min.js


JDLoader 파일을 먼저 index에 추가, 로드 시킨다. (검색해도 찾을수 있음)


jd파일을 json로드한뒤 parse 하면 다양한 정보들이 담겨서 나오는데 해당 데이터를 담아보자.



static jdModel( data )
{
let meshes = [];
let mixers = [];
let actions = [];

for( let i = 0; i < data.objects.length; ++i )
{
if( data.objects[i].type == "Mesh" || data.objects[i].type == "SkinnedMesh" )
{
let mesh = null;
let matArray = this.createMaterials( data );
if ( data.objects[i].type == "SkinnedMesh")
{
mesh = new THREE.SkinnedMesh( data.objects[i].geometry, matArray );
}
else
{
mesh = new THREE.Mesh( data.objects[i].geometry, matArray );
}
meshes.push( mesh );

if( mesh && mesh.geometry.animations )
{
for(let j = 0, length = mesh.geometry.animations.length; j < length; j++)
{
let mixer = new THREE.AnimationMixer( mesh );
mixers.push( mixer );
let action = mixer.clipAction( mesh.geometry.animations[j] );
action.play();
actions.push( action );
}
}
}
else if ( data.objects[i].type == "Line" )
{
let jd_color = data.objects[i].jd_object.color;
let color1 = new THREE.Color( jd_color[0] / 255, jd_color[1] / 255, jd_color[2] / 255 );
let material = new THREE.LineBasicMaterial({ color: color1 }); //{ color: new THREE.Color( 0xff0000 ) }
let line = new THREE.Line( data.objects[i].geometry, material );
// scene.add( line );

if( line.geometry.animations )
{
for( let j = 0, length = mesh.geometry.animations.length; j < length; j++ )
{
let mixer = new THREE.AnimationMixer( line );
mixers.push( mixer );
let action = mixer.clipAction( line.geometry.animations[j] );
action.play();
actions.push( actions );
}
}
}
}

let object = {};
object.meshes = meshes;
object.mixers = mixers;
object.actions = actions;
return object;
}

static createMaterials( data )
{
let matArray = [];
for( let i = 0; i < data.materials.length; ++i )
{
let mat = new THREE.MeshPhongMaterial({});
mat.copy( data.materials[i] );
matArray.push( mat );
}

return matArray;
}

Static 함수로 처리했다.


기존의 JDModel 로드 방법에 일부 스크립트가 추가되었는데

반환되는 object내부에

meshes , mixers, actions가 담겨서 나오게 된다.


이 함수를 통해 오브젝트를 받은뒤

meshes에 들어있는 모든 오브젝트를 Scene에 등록해준다.


this.object = new THREE.Group();

let model = StaticMethodCall.jdModel( data );

for(let i = 0, length = model.meshes.length; i < length; i++)
this.object.add( model.meshes[i] );

Group을 하나 만든뒤 씬에 등록하고 그 자식으로 넣어주는 방법이 편하다.


mixer는 애니메이션을 담당하므로 update함수에서 지속적으로 호출한다.

this.mixers[0].update( delta );

애니메이션을 재생, 정지 하고 싶을때는 Actions를 이용한다.

this.actions[1].stop();
this.actions[0].play();


유니티 앱 이름 변경은 프로젝트 셋팅에서 간단히 진행하면 되지만

Firebase, Facebook, Google Auth (인증)이 추가되고 나서는 APK이름 바꾸는게 쉽지가 않다.

페이스북, 구글 로그인을 적용하고 나서 다른 앱 이름으로 빌드하는 방법이다.


1. Project 폴더에서 google-services.json 파일의 package 이름을 새 APK 이름으로 변경한다.


2. Project Setting에서 AppId 및 App 이름 변경


3. Build Setting에서 Minify -> Proguard를 None으로 변경한다.


4. Facebook메뉴 -> EditSettings -> Selected App Id 를 다른 App Id로 변경한다.


5. Assets 메뉴 -> PlayService Resolver -> Android Resolver -> Resolve를 눌러 처리한다.


6. PlayerSettings 에서 Product Name, Package Name을 변경한뒤 빌드한다.


* 이때 Product Name과 google-service.json의 package 이름이 같아야 한다.

'프로그래밍 > Unity (유니티)' 카테고리의 다른 글

[Unity] 애니메이션 메카님 시스템 (Mecanim)  (2) 2020.02.12
WWW Request (post)  (0) 2018.09.05

+ Recent posts