2014년 12월 7일 일요일

미라 NPC


요기를 클릭.

마우스 왼버튼으로 화면을 돌리고, 휠버튼으로 줌인 줌아웃을 할수 있다.


곰해머와 여우의 맵상에 나오는 NPC캐릭터이다.
혓바닥이 나와 있는데 차라리 입을 벌리는 형태였으면 강공격의 물어 뜯기가 느낌이
좋았을것 같다.
PC캐릭터에 비해 이모션이 적다.



Mummy from Nlbo75 on Vimeo.

여우 캐릭터 애니메이션

요기를 클릭.

메뉴를 클릭하면 간단한 설명이 나온다.
유니티뷰어를 설치하라거나 화면 하단에 노란 박스가 뜨면서 승인하라면 승인 해주자.



곰헤머와 같이 작업하였던 여우 캐릭터 이다.
이런 저런 다른일이 생겨서 작업기간이 늘어졌었다.
곰캐릭터 보다는 빠른 도적같은 느낌으로 작업했다.
머플러가 2side처리가 되지 않아 깨져 보인다.
머플러는 스프링으로 제작 할까 했지만, 그냥 수작업으로 진행했다.



xyFox from Nlbo75 on Vimeo.

2014년 9월 17일 수요일

곰 해머캐릭터 애니메이션

요기를 클릭.(짬짬이내용물을 계속 추가할 예정이다.)

마우스 왼버튼으로 화면을 돌리고, 휠버튼으로 줌인 줌아웃을 할수 있다.



유니티뷰어를 설치하라거나 화면 하단에 노란 박스가 뜨면서 승인하라면 승인 해주자.


너무 무겁지 않은 느낌의 곰 캐릭터 모션을 진행했다.
팔은 긴편인데 다리가 짧아 난감한 부분도 있었다.
해머의 끝단 천에 본이 조금더 있었으면 좋겠는데,
모델링 및 리깅을 작업하신 분이 다른분이라 손대지 못해서
애니메이터로서는 아쉬운 부분이었다.


GomTank from Nlbo75 on Vimeo.

2014년 9월 15일 월요일

검 5연타 (예전 백업데이타를 뒤져보니.....3)


sword from Nlbo75 on Vimeo.

아마 예전에 포폴준비로 작업한것으로 생각한다.
그당시 고스트 트레일을 사용하면 멋진 검광이 만들어진다는 얘기를 듣고 테스트를 했으나,
짧은 프레임의 빠른 동작에서는 거칠게 나오는 문제가 발생하는것을 알고 상당히 실망했었다.

여자 쌍단검 10연타 (예전 백업데이타를 뒤져보니.....2)


female At01 from Nlbo75 on Vimeo.

예전에 아시는 분이 급하다며 하루안에 10콤보 모션을 2개 잡아달라고 해서 작업한 모션중에 하나.
그당시에는 일본식의 딱딱한 모션에 빠져있을때라 모션을 딱딱하게 잡았었다.
이동영상은 너무 딱딱한 부분을 살짝 손본 결과물이다.

타이페로 여자 아바타(예전 백업데이타를 뒤져보니.....1)


idle01 from Nlbo75 on Vimeo.



idle02 from Nlbo75 on Vimeo.



idle03 from Nlbo75 on Vimeo.



idle04 from Nlbo75 on Vimeo.



idle05 from Nlbo75 on Vimeo.



run from Nlbo75 on Vimeo.



sit from Nlbo75 on Vimeo.

아래에 있는 Boy와 같이 타이페로의 아바타중하나.

이 게임작업을 할때 일반게임보다 월등히 많은 감정표현 모션때문에,
기획을 담당하던 여자 기획자(당시 20대 초반의 꽃띠)가
참고 하라고 눈앞에서 춤도추고 동영상 촬영도 허가해준
신기한 경험을 하게 해주었다.
기획자 분께 감사의 묵념.(지금은 시집갔다는 풍문이 있다.)

이작업물의 모든 권리는 엔도어즈에 있습니다.

2014년 8월 26일 화요일

이전회사 작업물

요기를 클릭.(짬짬이내용물을 계속 추가할 예정이다.)

마우스 왼버튼으로 화면을 돌리고, 휠버튼으로 줌인 줌아웃을 할수 있다.


유니티뷰어를 설치하라거나 화면 하단에 노란 박스가 뜨면서 승인하라면 승인 해주자.

이전회사 작업물을 유니티로 구성해봤다.
(물론 이전회사는 유니티 엔진을 사용하지 않았다. 만드느라 골치좀 아팠다.)

액션 게임 이다 보니 동작들이 상당히 프레임에 쪼들리는 현상이 보인다.
프레임을 쪼개 쓰는것에대해 많은것을 배웠다.
(회사를 그만둔 후에는 유니티도 많이 배우게 해주었다 ㅋㅋ)

2014년 8월 17일 일요일

유니티 c# 공부

이동
transform.position = new Vector3(float, float, float);
- 절대좌표를 기준으로 위치시킴(순간이동)

transform.translate(new Vector3(float, float, float));
- 상대좌표를 기준으로 위치시킴(매 프레임마다 값만큼 이동)


회전
transform.rotation = Quaternion.Euler(float, float,  float);
- 절대좌표를 기준으로 회전시킴(순간 회전)

transform.Rotate(float, float, float);
- 상대좌표를 기준으로 회전시킴(매 프레임마다 값만 큼 회전)


프레임 고정
1) 'Edit -> Project Settings -> Time'을 선택 후 'InsPector'에서 'Fixed Timestep'값을 '0.01666667'로 하면 60프레임으로 고정.(30프레임은 '0.03333333'로 하면됨.)
단 'void Update ()'대신 'void FixedUpdate ()'를 사용해야 함.

2) 'Void Start ()'또는 'void Awake ()' 같이 시작시 한번 작동하는 함수에서 'Time.captureFramerate = 60;'를 적으면 디스플레이되는 프레임이 강제로 60프레임으로 변경.(30을 적으면 30프레임이 됨.)


동적배열
'string [][] frameForPosition;'로 2차원배열을 선언한 후.
'frameForPosition = new string[posInfo.Length][];'
'frameForPosition[i] = new string[posInfoTextSplit.Length];'과 같이 'new string'를 사용하여 배열의 길이를 선언.

00
000
0
0000
00
같은 형식으로 배열이 동적으로 할당됨.

예)
frameForPosition = new string[posInfo.Length][];

for(int i=0; i<posInfo.Length; i++)
{
posInfoTextSplit = posInfo[i].text.Split('\n');
frameForPosition[i] = new string[posInfoTextSplit.Length];

for(int j=0; j<posInfoTextSplit.Length; j++)
{
frameForPosition[i][j] = posInfoTextSplit[j];
}
}


텍스트형태의 파일 받기
'public TextAsset[] posInfo;'로 선언 후
'posInfo[0] = (TextAsset)Resources.Load("Txt/wp045_samba/wp045_samba_101_MOVE");'
로 스크립트에서 불러온다.(단 텍스트에셋은 'Resources'폴더 아래 있어야 한다.)


애니메이션 끝까지 출력하기
1)
 IEnumerator wait( float sec )
{
yield return new WaitForSeconds(sec);
        ableKeyInput = true;(키입력을 받을 수 있는지를 확인하는 Flag)
} 함수를 만든 후

if (Input.GetButtonDown("AKey"))
{
ableKeyInput = false;
animation.CrossFade("2D_TEST_GUARD", 0);
StartCoroutine(wait(animation["2D_TEST_GUARD"].clip.length));
}
을 하면 간단하게 ableKeyInput 가 true로 변경 될때 까지 다른 키 입력을 받지 못한다.
단 이경우 애니메이션 분기가 있는경우 사용되지 못한다.

2)
if((animation.IsPlaying("wp045_samba_301_STAN"))||(animation.IsPlaying("wp045_samba_302_GRD"))||(!animation.isPlaying))
{
.
.
.
} 과 같이 특정 모션이 '참'이거나 '거짓'일경우 함수 안으로 들어가지 못하게 하는 방식이 있다.
애니메이션에 분기를 넣고 싶으면 애니메이션에서 이벤트를 넣어 void 함수를 불러 오도록 한다.


플레이되는 애니메이션의 현재시간 구하기
(animation[aniname].time)
(System.Convert.ToInt32((animation[aniname].time)*60.0f)) 이렇게 덧붙이면, 시간을 프레임으로 변경할 수 있다.
단 FixedUpdate를 사용하고 'Fixed Timestep'값을 '0.01666667'로 할것.


키입력
Input.GetKey
- 키가 눌려지는 동안

Input.GetKeyDown
- 키가 눌릴때

Input.GetKeyUp
- 키를 눌렀다 뗀 후

Input.GetButton
- 키(키이름이 정의된 키셋팅에 해당하는 키)가 눌려지는 동안

Input.GetButtonDown
- 키(키이름이 정의된 키셋팅에 해당하는 키)가 눌릴때

Input.GetButtonUp
- 키(키이름이 정의된 키셋팅에 해당하는 키)를 눌렀다 뗀 후

예)
if(Input.GetKey("left"))
if(Input.GetButton("dashKey"))

GetKey와 GetButton의 차이
- GetKey는 키보드의 키를 직접 나타내고
- GetButton는 Edit-> Project Settings->Input에서 정의된 키의 네임을 나타냄.


맥스에서 읽은 텍스트 파일을 인식하지 못할때.
MonoDevelop의 문제인지 유니티의 문제인지는 모르겠지만, 텍스트문서를 읽어 스트링으로 정장한 후 해당 스트링을 IF문으로 비교했을때 인식하지 못하는 경우가 있다.
이럴때는 텍스트 문서를 MonoDevelop에서 부른 후에 한글자를 지우고 다시 똑같이 적은 후 저장을 하면 Convert할거냐고 묻는데 한다고 하면 된다.
텍스트 파일이 많을 경우 모든 텍스트 파일을 MonoDevelop에서 연 후에 한개의파일만 수정하고 저장하면 모두 Convert할거냐고 묻는데 한다고 하면 모두 변경된다.

2014년 7월 19일 토요일

2DTest


2DTest from Nlbo75 on Vimeo.

처음으로 진행해본 2d 캐릭터 모션이다.
유니티에서 실시간으로 녹화를 뜬것인데,
겉보기에는 2d같지만 사실 플랜을 여러개 덧댄 3d다.
그래도 기본이 3d이다 보니 모션블랜딩도 먹고,
같은 노드형태를가지고 있다면, 애니메이션 공유도 되서 상당히 쓸만한 느낌이다.
아는 분이 '좀비빌리지'나 '배틀하트'같은 느낌을 내고 싶다고 해서 테스트 했다.
여러가지 덧 붙일것이 있지만, 어떤 형태로 작업을 해야 할지 방향을 제시해준 결과물이었다.

게임샐러드 공부 중단!!

이전에 올린 글에서 게임샐러드가 화면 밖으로 나간 액터(오브젝트)를 임의로 삭제한다고 적고 그 범위를 구해봤다.

신기하게 모바일이나 PC나 사이즈는 동일하다.
변경할수 있는 방법도 없는것 같다.

횡스크롤 액션게임을 만든다고 했을때 이는 큰 문제를 발생시킨다.
물론 일일이 저장하고 불러오는 방법을 강구 할 수는 있겠지만,
다른 엔진에 비해 너무 번거롭다.

화면이 움직이지 않는게임을 만든다 하여도, 부모자식 개념이 없어서, 스폰명령을 이용한 복제 액터(오브젝트)들의 컨트롤이 너무 어려워 진다.

물론 간단한 게임을 만든다면, 충분히 써먹을 수 있겠지만, 내가 하고픈거랑은 방향성이 다르다.

게임샐러드에 적당한 게임을 만든다면 모르겠지만 지금은 아니다.
그간 잠시 손 놓았던 유니티엔진을 다시 만져 봐야겠다.

그런데 게임샐러드의 '비헤이비어'개념은 상당히 좋았다.
다른개임엔지에서도 이를 유념한다면, 좋은구조를 짤 수 있을것 같다.

백수가 됐다.

아~~ 이나이 먹고 다시 백수가 될줄이야.. ㅋㅋ

약 4년동안 열심히 달렸으니 이젠 조금 쉬면서 공부도 하고,
그간 묶여있던것 정리도 하고,
해야겠다.

2014년 7월 2일 수요일

게임샐러드 지원 기기의 해상도와 액터의 삭제되는 범위

지원 기기의 해상도

iPhone Landscape (X : 568, Y : 320)
iPhone Portrait (X : 320, Y : 568)

iPad Landscape (X : 1024, Y : 768)
iPad Portrait (X : 768, Y : 1024)

Nook color Landscape (X : 1024, Y : 600)
Nook color Portrait (X : 600, Y : 1024)

Kindle Fire Landscape (X : 1024, Y : 580)
Kindle Fire Portrait (X : 600, Y : 1004)

GameSalad Arcade (X : 480, Y : 320)
720 HD Fullscreen (X : 1280, Y : 720)
MacBook Fullscreen (X : 1280, Y : 800)

Legacy iPhone Landscape (X : 480, Y : 320)
Legacy iPhone Portrait (X : 320, Y : 480)
Legacy Web Game (X : 720, Y : 480)

액터의 삭제 되는 범위

기기및 해상도와는 관계 없이
X축 -500 ~1780사이를 벗어나면 액터가 삭제 된다.
Y축 -500 ~ 1300사이를 벗어나면 액터가 삭제 된다.

상당히 신기한것이 스크린의 사이즈와는 관계 없이 동알한 범위를 벗어나면 액터가 삭제된다.
유동적으로 사용자가 변경할 수 있는 방법이 있으면 좋겠지만 그런명령은 없는것 같고, 사용자가 삭제되기 전에 저장했다가 다시 범위내로 들어오면 불러와야 한다.

2014년 6월 28일 토요일

여주인공


MDA girl from Nlbo75 on Vimeo.

한달쯤 전에 지인으로 부터 부탁받은 작업이다.
작업량도 많지는 않아서, 리깅에서 이것 저것 테스트를 해본 작업이다.
기존보다는 면수가 많아서 조금더 애니메이션 할맛이 났다.

2014년 6월 27일 금요일

자주 이용되는 맥스 스크립트 사용

in coordsys local rotate MountLEar (EulerAngles 180 0 90)
-로컬축으로 회전하기

$'Bip01_Breast'.rotation.controller.value =
-회전시킬 때 포지션 값이 변동되지 않게
setUserPropBuffer $ ("\r\nNiOptimizeKeep\n")
-유저디파인에 문자남기기

MountLUpperArm.rotation = (inverse (biped.getTransform $'Bip01 L Clavicle' #rotation))
-쇠골뼈같은경우 축맞추기(이렇게 하지 않으면 이상하게 틀어짐)

$.pos.controller=Position_XYZ()
-해당 오브젝트의 콘트롤러 변경
$.transform.controller.Biped_SubAnim.controller.BipScaleList.controller.Available.controller = bezier_Scale ()
- 해당 오브젝트의 콘트롤러 변경 (바이패드오브젝트는 요렇게 바꾸고 추가를 함)

if(matchPattern (((getNoteTrack $'bip01' 1).keys)[i].value) pattern:"start -*" == true) do
-문자열검색

firePointName = filterstring KeysValue[j] ":"
-문자열 분리 하여 저장

$bip01.controller.enableSubAnims = true
-망할 썹애니 플래그

a = selection as array
- 선택한 오브젝트를 변수에 넣기

aPos = nodeGetBoundingBox $ $.transform
bipLength = aPos[2].x
-해당축으로의 오브젝트길이 구하기(바이패드길이 구할 때 용이)


selObj = selection as array
- 선택한 모든오브젝트를 변수에 저장


selObj = $* as array
- 존재하는 모든 오브젝트를 변수에 저장


exptFileName = getFilenameFile maxFileName
- 'maxFileName' 명령의 경우 확장자명까지 받아 오는데  'getFilenameFile'로 한번더 걸러주면 정확히 파일명만을 얻는다.(replace maxFileName (maxFileName.count - 3) 4 ""와 같이 어렵게 하지 않아도 된다 ㅠ,.ㅠ)

biped.setTransform $ #pos [0,-6.3386,97.4567] off
- 바이패드의 위치값 수정 off는 애니메이션 키생성 옵션(on이면 키생성)

degToRad -74.145
- 와이어파라메타 같은 것을 사용할 때 회전값을 라디안 단위로 변환

2014년 6월 19일 목요일

게임샐러드팁2

액터간의 부모자식 만들기

게임샐러드는 링크(마운트,어테치,그룹 등등)의 개념이 없다.
그래서 작업자가 비슷한 것을 직접 만들어 주어야 한다.
방법은 다음과 같다.
-------------------------------------
Constrain Attribute를 이용해
'자신의 위치 X축' to '자식과의 거리 * cos(부모의 회전값+ 부모의 정면을 기준으로 위치각)

Constrain Attribute를 이용해
'자신의 위치 Y축' to '자식과의 거리 * sin(부모의 회전값+ 부모의 정면을 기준으로 위치각)

Constrain Attribute를 이용해
'자신의 회전각 to 부모의 회전각'
-------------------------------------

여기서 '부모의 정면을 기준으로 위치각'이란것은 자식이 항상 부모의 정면쪽에 붙어있는 경우는 없다.
비행기의 날개의 경우도 몸통의 좌우(90/270의 각도)에 부터있다.
이것의 각도를 뜻하는 것이다.


게임샐러드 팁1

멈추지 못하고 떨릴때.
액터의 Physics내의 Drag항목은 저항값을 의미한다.
문제는 Accelerate Behavior를 사용하거나 액터의 Linear Velocity항목에 값을 입력하여 움직였을때 Drag에 의해 Linear Velocity값이 0이 되야 하는데
4~-4값정도(확실하지는 않다) 왔다갔다하며 조금씩 움직인다.

이때 해결방법은
Linear Velocity의 X축 Y축의 값이 모두 5~-5가되면
Drag를 Change Attribute Hehavior를 사용해 0으로 변경하고
Linear Velocity의 X축 Y축의 값을 Constrain Attribute를 사용해 0으로 변경한다.




방향키를 이용해 이동할때.
방향 Key값을 이용하여 오브젝트를 움직일때 가끔 움직임이 멈출경우가 있다.
Boolean으로 키입력의 참거짓을 정의하고, 그값을 이용하여 움직이게 한다.
그리고 회전의 경우는 Rotate Behavior를 사용하면 빠른 키전환시 멈추는경우가 있는데, 이는 액터의 Rotation값을 Constrain Attribute를 사용해 직접 변경하면 된다.


2014년 5월 24일 토요일

게임샐러드 소개


게임샐러드는 쉬운엔진이다.

게임샐러드 홈페이지 'www.gamesalad.com' 에서 엔진을 다운 받을때 보면 '13세 이상...'이라는 부분에 체크를 해야 하는데 이말은 반대로 '13살부터는 이엔진을 이용하여 게임을 만들수 있다'는 얘기다.

제작 방식도 드래그앤 드롭과 셀렉트 방식으로 간단하고 실제로 타이핑과 문법이 필요한 코딩은 없다.

그럼에도 아직 한국에는 변변한 커뮤니티조차 없고 알고있는 사람도 드믄 엔진이다.
이유를 생각해 보자면
1. 우리나라에서는 한물 갔다고 생각되는 2D전용 엔진에다
2. 쉬운엔진이라는 인식때문에 어린 학생들이 공부를 하고 싶지만 언어의 장벽이 있다.
(언어의 장벽을 못느끼는 성인의 경우 더 복잡한 엔진을 선호한다)
3. 코딩이 없다는것 때문에 개발시 확장성부분을 의심받는다.
(이 부분이 전문개발자들에게 외면받는 이유가 크다.)
4.  홈페이지에 나온 선전용이미지가 구리다.
등이 있을것 같다.

아무래도 전문개발자가 봤을때는 코딩을 통한 세세한 셋팅이 불가능해 보여서, 초보자가 봤을때는 언리얼 같은 화려한 그래픽이 없어서 인것 같다.

하지만 '툴은 툴일 뿐' 아무리 좋은 도구를 주더라도 작업자가 능력이 나쁘다면 좋은 결과물을 얻을 수 없는것과 같이 아무리 허접해 보이는 툴이라도 좋은 아이디어와 실력이 있다면 좋은 결과물이 나온다.
최근 스팀의 2D레트로 게임들이 기발한 아이디어로 히트작들을 내고 있다.


게임샐러드는 맥용이 갑이다.

게임샐러드는 특이하게 윈도우용과 맥용이 다른 생김새를 가지고 있다.

맥용으로 있는 기능이 윈도우용에선 없다던가 하는 일은 없지만, 인터넷의 동영상 강의를 찾아보면 대부분(내가 아는한 모두) 맥용으로 진행을 한다.
생김새가 다르다는것은 사소한 버튼의 위치 부터 기능으로 들어가기위한 방법 까지 다른 부분을 가지고 있다.
두가지 버전을 다써본 경험으로 맥용이 더 작업이 편하게 되어있다.

게다가 기본(무료)버전의 경우
맥용은 맥PC, IOS, 웹 으로 퍼블리싱하는게 가능하지만,
윈도우는 웹 만 퍼블리싱이 가능하다.(물론 프로버전을 사용한다면 안드로이등으로 확장이 가능하다)
약간 귀찮지만, PC버전으로 만든것을 압축하여 맥용으로 컨버팅하는것이 가능하기 때문에 중고 맥PC나 맥북을(라이언 버전으로)구매하여 퍼블리싱하는것도 생각해 볼수 있다.
(사실 가벼운 2D엔진이기 때문에 라이언버전이 돌아가는 맥이면 직접 작업하는게 빠를 수도 있다.)
추후의 버전업 때 베이직의 확장성을 기대해본다.


한국에서는 정보를 얻기가 힘들다.



처음 이책이 있다는것을 알고 옳다구나 하고 구매를 했다.

하지만 책내용이 원서가 부실한건지 번역한사람이 개판을 친건지 5장을 넘어서먼서 무슨 이야기인지 알수 가없다.
어떤기능을 추가하여 이러한 효과를 낸다라고 적혀는 있는데 어떤 액터에게 추가하는것인지 주어가 없다.(이모 전대통님의 주어가 없는 얘기를 감명깊게 들은건지 모르겠다.)
원서가 개판이어도 번역한 사람이 의지가 있었다면 충분히 주어를 넣어서 알아 들을수 있게 했을텐데 그런 의지도 않보이고, 번역을 할때도 직접 테스트하며 한게 아닌것 같다는 생각이든다.(직접 해봤다면 이렇게 쓰진 않았을것이다.)

가장 큰 난관은 국내에는 이 책밖에 없다는 것이다. ㅠ,.ㅠ


그럼 커뮤니티는 없는것인가?

아쉽게도 네이버에 카페가 있기는한데 폐점직전이다.
그나마 추천하는곳은 'http://diyga.me/' 이다.
장점은 무려 한국어인 동영상강의가 있다.
단점은 가입을해야 하고, 고급스킬은 유료강의이고, 질문에대해 약간 동문서답이다.
그래도 여기에서 무료동강을 듣고 툴의 사용법을 익히고 해외 동강을 본다면, 큰도움이 될것이다.
약간 부족한점이 있지만 급한 사람에게는 감지덕지한 사이트다.


그래도 뭔가 아쉬운 기능

엔진이라면 왠지 당연히 있을 법한 링크(어테치, 마운트, 그룹)기능이 없다.
아니 없는건지 못찾은건지 몇일간 찾아봤지만 확인하지 못했다.
항간에는 업뎃을 기대하라는 얘기가 있었다.


그럼에도 한번 해보면 좋다.

자신이 만들고 싶은 게임에대한 느낌을 빠르게 확인해 보고 싶다면, 혹은 간단한 2D게임이라면(두들점프, 쿠키런류, 드래곤플라이트류) 훌륭하게 쓸수 있는 엔진이다.
물론 더 복잡한게임도 가능하리라 생각한다.


이런게임이 만들어졌다.


13살짜리도 게임을 만들수 있다는 게임샐럳러드.... 시작한다

6년 전쯤 유니티엔진이 레고를 끼워 맞추듯이 슥슥슥 붙이고 약간의 스크립트 코딩을 하면 캐릭터가 움직이는것을 보고 충격을 받았었다.
'이런 사기같은 엔진이 있다니!!'

하지만 약 1달전쯤 게임샐러드라는 엔진을 보고 또 비슷한 충격을 받았다.
'코드 한줄없이 게임재작'이 모토인 엔진이다.
정말 코드가 한줄도 없는가 한다면 '한줄도 없는것이 맞다.'이지만,
그래도 코드의 개념과 비슷한 '룰'과 '비헤이비어'라는것이 있다.

하지만 이것은 프로그래밍을 모르는 사람도 문법을 배울 필요없이 원리를 따져가면서 바로 캐릭터를 움직이게 할 수 있다는것이 큰 특징이다.

간단한 플로우차트 정도를 구사 할 수 있다면 OK인것이다.

아.. 그러고보니 유니티엔진은 3D, 2D(약함)의 범용 엔진이지만, 개임샐러드는 2D전용 엔진이다.
(요즘 스팀 게임을 즐기고 있는데 3D게임 보다 2D게임을 선호 하게 된다. 화려한 3D보다는 알차고 매력적인 게임 2D게임이 더 좋은것 같다.)


이제 부터 천천히 개임샐러드 엔진에 대해서 공부하겠다.

2014년 3월 24일 월요일

스프링개량 스크립트 Ver0.8

--------------- 스프링 루트 구간 시작---------------------
createRootPoint = point()
createRootPoint.name = "SpringRoot"
createRootPoint.size = 3.0
createRootPoint.box = true
createRootPoint.cross = false
createRootPoint.wirecolor = (color 200 10 10)
addModifier createRootPoint (EmptyModifier ())
--------------- 스프링 루트 구간 끝---------------------

--------------- 스프링 본 구간 시작---------------------
createBone = Bonesys.createBone[0,0,0][1,0,0][0,0,1]
createBone.name = "SpringBone"
createBone.width = 1
createBone.height = 2
createBone.length = 3
--createBone.boxmode = on
createBone.parent = createRootPoint

createEndBone = Bonesys.createBone[3,0,0][4,0,0][0,0,1]
createEndBone.name = "SpringEndBone"
createEndBone.width = 1
createEndBone.height = 1
createEndBone.length = 1
--createBone.boxmode = on
createEndBone.parent = createBone
--------------- 스프링 본 구간 끝---------------------

--------------- 스프링 포인트 구간 시작---------------------
createSpringPoint = point()
createSpringPoint.name = "SpringPoint"
createSpringPoint.size = 2.0
createSpringPoint.box = false
createSpringPoint.cross = true
createSpringPoint.wirecolor = (color 200 10 10)
createSpringPoint.position = [createBone.length, 0, 0]
createSpringPoint.parent = createRootPoint
createSpringPoint.position.controller =  SpringPositionController()
--------------- 스프링 포인트 구간 끝---------------------

--------------- ExposeTm 구간 시작---------------------
createExpTm = ExposeTm ()
createExpTm.name = "SpringExpTm"
createExpTm.size = 2.0
createExpTm.wirecolor = (color 170 170 170)
createExpTm.parent = createRootPoint
createExpTm.exposeNode = createSpringPoint
createExpTm.useParent  = false
createExpTm.localReferenceNode = createRootPoint
--------------- ExposeTm 구간 끝---------------------

--------------- attrivutes 구간 시작---------------------
springCA = attributes springData
(
parameters main rollout:params
(
yAxisMin type:#float ui:yAxisMin_spi default:-45.0
yAxisMax type:#float ui:yAxisMax_spi default:45.0
zAxisMin type:#float ui:zAxisMin_spi default:-45.0
zAxisMax type:#float ui:zAxisMax_spi default:45.0
forceVal type:#float ui:force_spi default:1.0
editBone type:#integer ui:editBone_cb default:0
)


rollout params "Spring Parameters"
(
spinner yAxisMin_spi "Y-AxisMin " type:#float Range:[-90.0,-1.0,-45.0]
spinner yAxisMax_spi "Y-AxisMax " type:#float Range:[0.0,90.0,45.0]
spinner zAxisMin_spi "Z-AxisMin " type:#float Range:[-90.0,-1.0,-45.0]
spinner zAxisMax_spi "Z-AxisMax " type:#float Range:[0.0,90.0,45.0]
spinner force_spi "Force " type:#float Range:[-100.0,100.0,1.0]
checkbutton editBone_cb "EditBone" checked:false

on editBone_cb changed state do
(
createSpringPoint.position = createEndBone.position
createSpringPoint.rotation = createEndBone.Rotation
editBone_cb.checked = false
)
)
)

ah = createRootPoint.modifiers[#'Attribute Holder']
CustAttributes.add  ah  springCA  #Unique BaseObject:false
--------------- attrivutes 구간 끝---------------------

---------------스프링본에 Rotation_Script_Controller추가 구간 시작------------------
createBone.rotation.controller = rotation_script()
createBone.rotation.controller.script = 
"
y = -(createExpTm.'local Position Z')
z = createExpTm.'local Position Y'
forc = createRootPoint.modifiers[#Attribute_Holder].springData.forceVal
yMinNum = createRootPoint.modifiers[#Attribute_Holder].springData.yAxisMin
yMaxNum = createRootPoint.modifiers[#Attribute_Holder].springData.yAxisMax
zMinNum = createRootPoint.modifiers[#Attribute_Holder].springData.zAxisMin
zMaxNum = createRootPoint.modifiers[#Attribute_Holder].springData.zAxisMax

y = y * forc
z = z * forc

if (y > yMaxNum) do y = yMaxNum

if (y < yMinNum) do y = yMinNum

if (z > zMaxNum) do z = zMaxNum

if (z < zMinNum) do z = zMinNum

rot = eulerAngles 0 y z

rot

"
---------------스프링본에 Rotation_Script_Controller추가 구간 끝------------------

=================================================================================

기존의 맥스 스프링의 경우 포스의 형태가 월드 축을 기준으로 적용되기 때문에 스프링 효과의 X, Y, Z 축을 셋팅하다고 해도 부모노드의 축이 변경된다면 셋팅값이 슬모가 없게 된다.

오토데스크사에서는 왜 이것을 그대로 두는지 모르겠다.
딱히 같은 기능을 하는 다른 명령도 없는데 말이다.

평소에도 가슴본 셋팅을 하면서 이것에 대해 뭔가 해결책이 있을것 이라고 생각했는데, 그간 제대로 고민해 본적은 없다. (물론 이것도 아직 완벽한 스크립트는 아니지만 ㅎㅎ)

이것에 대한 아이디어는 간단했다.
1 스프링 컨트롤러가 적용된 포인터의 Y, Z축의 이동값을 본의 회전값으로 전달한다.
2 회전되는 본의 Rotation Controller를 Script Controller로 특정 각도 이상 회전하지 못하게 한다.

글런데 알고 보니 이미 케릭터 페이셜 리깅에 이런 개념이 있었다. (난 페이셜 컨트롤러를 만들어 본적이 없었기 때문에 이런 간단한것도 몰랐다. ㅠ,.ㅠ)

구현을 해보니 큰 문제가 있었다.
1 이동거리가 회전값이다보니 노드의 크기가 차이가 나면 적용이 너무 작아지거나 너무 커진다.(2미터 짜리 캐릭터의 가슴 움직임 위치값과 20M짜리 캐릭터의 가슴 움직임 위치값은 당연히 차이가 있다)
2 한계 각도가 있기 때문에 한계각도를 넘으면 멈추는것이 당연하지만, 한계값을 넘어가면 스무스하게 멈추지를 못하고 딱 멈춰 버린다.

1번은 내가 수학을 잘한다면 자동으로 해결될 수 있게 할 수 있지만 나는 수학을 못한다. 그래서 공부를 할까 했는데 '낫 놓고 ㄱ자를 모른다.'는 속담이 뭔지 느낄수 있는 계기가 됐다.
그래서 받은값을 곱하기로 증폭하는 수치를 넣었다.(싸게 먹히니까!!!)
2번은 해결방법을 모르겠다.
적당한 함수를 찾아 보려했지만 구할 수가 없었다.
이 두개가 해결된다면 'Ver1.0'이 되겠지만 어쩔 수 없이 'Ver0.8'이다.
뭐 그래도 이정도면 훌륭하게 써먹을 수 있는 녀석이다.

생성되는 오브젝트는 다음과 같다.
SpringRoot - 최종부모이기 때문에 가슴에 이용한다면 바이패드의 Spine에 자식으로 링크를 걸면 된다. 셋팅옵션을 넣을 수 있는 'Attrribute Holder'가 포함된다.
SpringBone - 실제 흔들리는 결과 값을 표현하는 본
SpringEndBone - 본툴을 이용해 'SpringBone'본의 길이를 조정할때와 'SpringPoint'의 위치값을 위한 본
SpringPoint - 'SpringBone'의 흔들리는 가이드를 하는 스프링포인트
SpringExpTm - 'SpringPoint'의 위치값을 부모노드와 얼만큼 멀어지는지를 구하는 역활

생성된 모습에서 빨간 박스형 포인트를 선택하면모디파이어에서 다음과 같은 옵션을 볼 수 있다.

Y, Z - 축의 한계값 셋팅
Force - 가중치를 조정(0은 넣지 말자)
EditBone - 본툴로 본 사이즈를 조절한 후 누르면 스프링포인트위치가 정렬됨



사용법은
1 스크립트를 실행한다.
2 적당한 노드에 링크한다.(단독으로 테스트할때는 필요 없음)
3 메쉬나 오브젝트에 맞게 사이즈 조절한다.(스케일로는 하지는 말것)
4 본 길이가 변경되면 'EditBone'를 한번 누른다.
5 흔들릴 각도를 셋팅한다.
6 스프링포인트가 적당한 값을 갖도록 스프링 셋팅을 한다.
7 본이 스프링에 비슷하게 움직이도록 Force값을 적는다.(스프링포인트가 많이 흔들리는 프레임에서 적용하면 쉽다. 단 적용된값은 바로 표시되지 않으므로 이전프레임으로 갔다가 다시 원래 프레임으로 온다._
8 중간에 본길이를 조정한다면 스프링값이 적용되 않는 프레임(대부분 0프레임)에서 조정하자

언제 수정될지는 모르겠지만 큰 버그가 없는 이상 버전업은 없을것 같다.

2014년 3월 8일 토요일

Boy


Boy from Nlbo75 on Vimeo.

2008년에 작업한 타이페로라는 게임의 작업물이다. 캐쥬얼게임의 캐릭터로 이런저런 공부도 많이했고 애니메이션의 반응도 나쁘지 않았는데 결국 사장이 된게임. 회사를 나온 후에 들은 풍문으론 본전은 뽑았다는것 같다. 무료 툴인 버처덥으로 동영상들을 끼워 맞추려니 튀는 부분이 있다.

2014년 3월 3일 월요일

쫀득리깅


쫀득 from Nlbo75 on Vimeo.

예전에 잠시 진행했던 프로젝트의 캐릭터.
쫀득함을 살리고 파츠교환이 되는 캐릭터의 테스트 모션.
모델링은 다른 분이 작업하셨다.

이때부터 천둥벌거숭이가 리깅에대해 생각하게 된것 같다.
그래도 아직까지 미흡한 부분이 많은것 같다.

타란튤라 테스트


Tarantula from Nlbo75 on Vimeo.

모바일게임용 테스트 몹 타란튤라.
소녀와 다르게 상당히 리얼한 느낌이다.
바이패드 두개를 이어붙여서 실제로 필요없는 본이 꽤 들어있다.
작업한 바이패드 흉내내기 스크립트로 필요한 것만 뽑을 수 있도록 수정해야 할것 같다.
모델링은 다른분이 진행하셨다.

이 몹을 작업하면서 '바이패드 흉내내기 본' 작업을 생각했다.
모델링과 원화를 받고 리얼한느낌에 '보스몹이구나!' 하고 작업을 했는데
나중에 구현된것을 보니 쪼랩 몹이었다.ㅋㅋㅋㅋ
덕분에 애니메이션은 어느정도 덩치가 있는 몹으로 상정하고 작업을 했다.

소녀 테스트 모션


Girl from Nlbo75 on Vimeo.

모바일게임용 테스트 캐릭터.
개인적으론 손발이 작아서 수정을 해야 할것 같다.
(손발의 움직임이 잘 않보인다.)
또한 게임 캐릭터로서 개성이 약한 느낌이다.
모델링은 다른분이 작업하셨다.

테스트를 위해 작업된 모델링이기에 면의 수도 적고, 본의 수도 적기 때문에
표현의 한계가 있다.
색상또한 밝은 색이 대부분이고, 손이 작은 상태에서 옷과 색이 겹치며 동작이 확실히 보이지 않는 느낌이다.

개인적으론 '록맨'이나 '덴마'의 캐릭터같이 손발이 크고 선이 더 굵은 것이 모바일 화면에서 더 좋은 퀄리티를 낼수 있을것 같다.

나중에 여건이 된다면 유니티로 작업된 파일도 올리고 싶다.

2014년 2월 15일 토요일

오브젝트 회전값 구하기_애니키 기준

objName = ""
objFrame = ""
objRotEul = ""
objRotQut = ""
outputStr = ""
txtfilename = ""

selObjs = selection as array

hideByCategory.geometry = true
hideByCategory.helpers = true
hideByCategory.bones = true

if (selObjs.count !=0) then
(
txtfilename = (Filterstring maxFileName ".")[1]
pathFile = maxfilepath + "\\" + txtfilename + ".txt"
filestm = createFile pathFile

for i  = 1 to selObjs.count do
(
objName = selObjs[i].name
outputStr = outputStr + objName + "\n"

if (classof selObjs[i] == Biped_Object) then
(
for j =1 to (selObjs[i].controller.keys).count do
(
sliderTime = selObjs[i].controller.keys[j].time
objFrame = (selObjs[i].controller.keys[j].time) as string
objRotQut = (selObjs[i].transform.rotation) as string
objRotEul = ((selObjs[i].transform.rotation) as eulerAngles) as string

outputStr = outputStr + objFrame + "\t" + objRotEul + "\n"

if((selObjs[i].controller.keys).count == j)do outputStr = outputStr + "\n"
)
)
else
(
for j =1 to (selObjs[i].rotation.controller.keys).count do
(
sliderTime = selObjs[i].rotation.controller.keys[j].time
objFrame = (selObjs[i].rotation.controller.keys[j].time) as string
objRotQut = (selObjs[i].rotation.controller.value) as string
objRotEul = ((selObjs[i].rotation.controller.value) as eulerAngles) as string

outputStr = outputStr + objFrame + "\t" + objRotEul + "\n"

if((selObjs[i].rotation.controller.keys).count == j)do outputStr = outputStr + "\n"
)
)


)
format "%" outputStr to:filestm
close filestm
)
else
(
)

hideByCategory.geometry = false
hideByCategory.helpers = false
hideByCategory.bones = false

===============================================================================

바이패드 또는 그외의 오브젝트들의 애니메이션 키값을 순차 적으로 읽어 해당값의 로테이션 값을 저장하는 스크립트다.
조금더 다듬는다면 맥스파일의 애니메이션 회전값을 다른 맥스파일에 적용하는데 사용될 수있는 스크립트다.

만들게 된 계기가 있는데 다음과 같다.

몇달전에 모 카페에다가 맥스 스크립트를 외주하는것이 실효성이 있는가 하는 의견을 올린적이 있다.
본업은 애니메이션 이긴하지만 회사의 업무에 필요한 맥스 스크립트를 제작하면서, 모바일게임이나 인원수가 적은 업체에서는 스크립트를 작업 할 수 있는 사람을 구하기 힘들어 작업 시간을 줄일 수 있는 일을 줄이지 못해 어려움이 있을것 같다는 생각이 들었기 때문이다.

물론 본격적으로 맥스 스크립트를 공부하고 작업한것이 아니기 때문에 사람들의 답변이 궁금했었다.

그런데 뜬금없이 모 회사에서 스크립트 외주 요청이 들어왔었다.

결국에는 나의 잘못으로 외주 업무를 하지 못했지만, 내심 스크립트도 잘하면 외주를 할수 있는 하나의 업종이 될수 있겠구나 하는 생각이 들었다.

당시 모 회사에서 요청했던 기능을 간단하게 구현한 스크립트다.


결론은 스크립트 공부 열심히 하자!!!

바이패드 복사 본 만들기 ver1.1

bips = #()
bons = #()
boneName = ""
boundingBox = undefined
bipLength = undefined
bipwidth = undefined
bipheight = undefined
selObj = selection as array
objs = #()
rootBipName = ""
filterRootBipName = #()


cNameValue = "Bone"

fn changeName strs oldStr newStr =
(
findFlag = findstring strs oldStr
if (findFlag != undefined) do
(
changeStr = replace strs findFlag oldStr.count newStr
)
changeStr
)

for i = 1 to selObj.count do
(
if (classof selObj[i] == Biped_Object) do append objs selObj[i]
)


if objs.count == 0 then 
(
messagebox "바이패드 노드를 하나 이상 선택하세요."
)

else
(
filterRootBipName = filterstring objs[1].name " "



(execute("$'"+filterRootBipName[1] + "'")).transform.controller.figureMode = true
(execute("$'"+filterRootBipName[1] + "'")).transform.controller.trianglePelvis = false
(execute("$'"+filterRootBipName[1] + "'")).transform.controller.triangleNeck = true
rollout bipMirrbone "흉내 내기본 생성"
(
edittext newNameText "Bip 대신 넣을 이름" text:"Bone"
checkbox twistBon_chk "트위스트본사용" checked:false width:200
checkbox autoNeck_chk "자동본사용" checked:false width:200
Button start_btn  "실행"

on newNameText changed txt do
(
cNameValue = txt
)

on start_btn pressed do
(

for i = 1 to objs.count do
(
if((classof objs[i] == Biped_Object) and (objs[i].name != "Bip01 Footsteps")) do append bips objs[i]
)

clearSelection()

for i = 1 to bips.count do
(
boneName = bips[i].name

boneName = changeName boneName "Bip" cNameValue

boundingBox = nodeGetBoundingBox bips[i]bips[i].transform
bipLength = boundingBox[2].x
bipheight = boundingBox[2].y
bipwidth = boundingBox[2].z
madeBone = Bonesys.createBone[0,0,0][1,0,0][0,0,1]
madeBone.name = boneName
madeBone.width = bipWidth
madeBone.height = bipheight
madeBone.length = bipLength
madeBone.boxmode = on
madeBone.rotation = (inverse(biped.getTransform bips[i] #rotation))
madeBone.position = (bips[i].transform.pos)

if((autoNeck_chk.checked == true)and(matchPattern bips[i].name pattern:"*Neck" == true)) then
(
madeBone.rotation.controller= Orientation_Constraint()
madeBone.rotation.controller.appendTarget bips[i].parent 50.0
madeBone.rotation.controller.appendTarget bips[i].children[1] 50.0
)
else if((autoNeck_chk.checked == true)and(matchPattern bips[i].name pattern:"*Head" == true)) then
(
madeBone.rotation.controller= Orientation_Constraint()
madeBone.rotation.controller.appendTarget bips[i] 50.0

)
else
(
madeBone.position.controller = Position_Constraint() 
madeBone.position.controller.appendTarget bips[i] 50.0
madeBone.rotation.controller= Orientation_Constraint()
madeBone.rotation.controller.appendTarget bips[i] 50.0
)

append bons madeBone
)

for i = 1 to bips.count do
(
if(bips[i].parent != undefined) do
(
parentBip = bips[i].parent
boneName = parentBip.name

boneName = changeName boneName "Bip" cNameValue

bons[i].parent = execute("$'" + boneName + "'")
)
)

filterRootBoneName = filterstring boneName " "

if(twistBon_chk.checked == true) do
(
if((execute  ("$'" + filterRootBoneName[1] + " L Clavicle'") != undefined) and (execute  ("$'" + filterRootBoneName[1] + " L UpperArm'") != undefined) and (execute  ("$'" + filterRootBoneName[1] + " L Forearm'") != undefined)) do
(
-- 왼쪽 어께트위스트1 더미
Point_L_UpperArmTwist02 = point()
Point_L_UpperArmTwist02.name = "Point_L_UpperArmTwist02"
Point_L_UpperArmTwist02.size = 2.0
Point_L_UpperArmTwist02.box = false
Point_L_UpperArmTwist02.cross = true
Point_L_UpperArmTwist02.rotation = (inverse(biped.getTransform (execute("$'"+filterRootBipName[1] + " L UpperArm'")) #rotation))
Point_L_UpperArmTwist02.pos = ((execute("$'"+filterRootBipName[1] + " L UpperArm'")).transform.pos)
Point_L_UpperArmTwist02.parent =(execute  ("$'" +  filterRootBoneName[1] + " L Clavicle'"))
Point_L_UpperArmTwist02.pos.controller = Position_Expression()
Point_L_UpperArmTwist02.parent =  (execute  ("$'" +  filterRootBoneName[1] + " L UpperArm'"))
Point_L_UpperArmTwist02.wirecolor = (color 10 10 10)

-- 왼쪽 어께트위스트2 더미
Point_L_UpperArmTwist01 = point()
Point_L_UpperArmTwist01.name = "Point_L_UpperArmTwist01"
Point_L_UpperArmTwist01.size = 2.0
Point_L_UpperArmTwist01.box = true
Point_L_UpperArmTwist01.cross = false
Point_L_UpperArmTwist01.rotation = (inverse(biped.getTransform (execute("$'"+filterRootBipName[1] + " L Clavicle'")) #rotation))
Point_L_UpperArmTwist01.pos = ((execute("$'"+filterRootBipName[1] + " L Clavicle'")).transform.pos)
Point_L_UpperArmTwist01.parent = (execute  ("$'" +  filterRootBoneName[1] + " L Clavicle'"))
Point_L_UpperArmTwist01.rotation.controller=LookAt_Constraint()
Point_L_UpperArmTwist01.rotation.controller.appendTarget $'Point_L_UpperArmTwist02' 50.0
Point_L_UpperArmTwist01.rotation.controller.viewline_length_abs = false
Point_L_UpperArmTwist01.rotation.controller.upnode_world = false
Point_L_UpperArmTwist01.rotation.controller.pickUpNode = (execute  ("$'" +  filterRootBoneName[1] + " L Clavicle'"))
Point_L_UpperArmTwist01.wirecolor = (color 10 10 10)

-- 왼쪽 어께 트위스트1,2 본
LUpperArmTwist01 = Bonesys.createBone[0,0,0][1,0,0][0,0,1]
LUpperArmTwist01.name =  filterRootBoneName[1] +" L UpperArmTwist01"
LUpperArmTwist01.width = ((execute  ("$'" +  filterRootBoneName[1] + " L UpperArm'")).width) * 0.8
LUpperArmTwist01.height = ((execute  ("$'" +  filterRootBoneName[1] + " L UpperArm'")).height) * 0.8
LUpperArmTwist01.length = ((execute  ("$'" +  filterRootBoneName[1] + " L UpperArm'")).length) / 4
LUpperArmTwist02 = Bonesys.createBone[(((execute  ("$'" +  filterRootBoneName[1] + " L UpperArm'")).length) / 4),0,0][1,0,0][0,0,1]
LUpperArmTwist02.name =  filterRootBoneName[1] + " L UpperArmTwist02"
LUpperArmTwist02.width = ((execute  ("$'" +  filterRootBoneName[1] + " L UpperArm'")).width) * 0.8
LUpperArmTwist02.height = ((execute  ("$'" +  filterRootBoneName[1] + " L UpperArm'")).height) * 0.8
LUpperArmTwist02.length = ((execute  ("$'" +  filterRootBoneName[1] + " L UpperArm'")).length) / 3
LUpperArmTwist02.parent = LUpperArmTwist01
LUpperArmTwist01.rotation = (inverse(biped.getTransform (execute("$'"+filterRootBipName[1] + " L UpperArm'")) #rotation))
LUpperArmTwist01.pos = ((execute("$'"+filterRootBipName[1] + " L UpperArm'")).transform.pos)
LUpperArmTwist01.parent = (execute  ("$'" +  filterRootBoneName[1] + " L UpperArm'"))
LUpperArmTwist02.parent = (execute  ("$'" +  filterRootBoneName[1] + " L UpperArm'"))
LUpperArmTwist01.rotation.controller=LookAt_Constraint()
LUpperArmTwist01.rotation.controller.appendTarget (execute  ("$'" +  filterRootBoneName[1] + " L Forearm'"))  50.0
LUpperArmTwist01.rotation.controller.viewline_length_abs = false
LUpperArmTwist01.rotation.controller.upnode_world = false
LUpperArmTwist01.rotation.controller.pickUpNode = Point_L_UpperArmTwist01
LUpperArmTwist01.wirecolor = (color 10 10 10)
LUpperArmTwist01.boxmode = on

LUpperArmTwist02.rotation.controller=Orientation_Constraint()
LUpperArmTwist02.rotation.controller.appendTarget LUpperArmTwist01 60.0
LUpperArmTwist02.rotation.controller.appendTarget (execute  ("$'" +  filterRootBoneName[1] + " L UpperArm'")) 40.0
LUpperArmTwist02.wirecolor = (color 10 10 10)
LUpperArmTwist02.boxmode = on
)

if((execute  ("$'" +  filterRootBoneName[1] + " R Clavicle'") != undefined) and (execute  ("$'" +  filterRootBoneName[1] + " R UpperArm'") != undefined) and (execute  ("$'" +  filterRootBoneName[1] + " R Forearm'") != undefined)) do
(
-- 오른쪽 어께트위스트1 더미
Point_R_UpperArmTwist02 = point()
Point_R_UpperArmTwist02.name = "Point_R_UpperArmTwist02"
Point_R_UpperArmTwist02.size = 2.0
Point_R_UpperArmTwist02.box = false
Point_R_UpperArmTwist02.cross = true
Point_R_UpperArmTwist02.rotation = (inverse(biped.getTransform (execute("$'"+filterRootBipName[1] + " R UpperArm'")) #rotation))
Point_R_UpperArmTwist02.pos = ((execute("$'"+filterRootBipName[1] + " R UpperArm'")).transform.pos)
Point_R_UpperArmTwist02.parent =(execute  ("$'" +  filterRootBoneName[1] + " R Clavicle'"))
Point_R_UpperArmTwist02.pos.controller = Position_Expression()
Point_R_UpperArmTwist02.parent =  (execute  ("$'" +  filterRootBoneName[1] + " R UpperArm'"))
Point_R_UpperArmTwist02.wirecolor = (color 10 10 10)

-- 오른쪽 어께트위스트2 더미
Point_R_UpperArmTwist01 = point()
Point_R_UpperArmTwist01.name = "Point_R_UpperArmTwist01"
Point_R_UpperArmTwist01.size = 2.0
Point_R_UpperArmTwist01.box = true
Point_R_UpperArmTwist01.cross = false
Point_R_UpperArmTwist01.rotation = (inverse(biped.getTransform (execute("$'"+filterRootBipName[1] + " R Clavicle'")) #rotation))
Point_R_UpperArmTwist01.pos = ((execute("$'"+filterRootBipName[1] + " R Clavicle'")).transform.pos)
Point_R_UpperArmTwist01.parent = (execute  ("$'" +  filterRootBoneName[1] + " R Clavicle'"))
Point_R_UpperArmTwist01.rotation.controller=LookAt_Constraint()
Point_R_UpperArmTwist01.rotation.controller.appendTarget $'Point_R_UpperArmTwist02' 50.0
Point_R_UpperArmTwist01.rotation.controller.viewline_length_abs = false
Point_R_UpperArmTwist01.rotation.controller.upnode_world = false
Point_R_UpperArmTwist01.rotation.controller.pickUpNode = (execute  ("$'" +  filterRootBoneName[1] + " R Clavicle'"))
Point_R_UpperArmTwist01.wirecolor = (color 10 10 10)

-- 오른쪽 어께 트위스트1,2 본
RUpperArmTwist01 = Bonesys.createBone[0,0,0][1,0,0][0,0,1]
RUpperArmTwist01.name =  filterRootBoneName[1] +" R UpperArmTwist01"
RUpperArmTwist01.width = ((execute  ("$'" +  filterRootBoneName[1] + " R UpperArm'")).width) * 0.8
RUpperArmTwist01.height = ((execute  ("$'" +  filterRootBoneName[1] + " R UpperArm'")).height) * 0.8
RUpperArmTwist01.length = ((execute  ("$'" +  filterRootBoneName[1] + " R UpperArm'")).length) / 4
RUpperArmTwist02 = Bonesys.createBone[(((execute  ("$'" +  filterRootBoneName[1] + " L UpperArm'")).length) / 4),0,0][1,0,0][0,0,1]
RUpperArmTwist02.name =  filterRootBoneName[1] + " R UpperArmTwist02"
RUpperArmTwist02.width = ((execute  ("$'" +  filterRootBoneName[1] + " R UpperArm'")).width) * 0.8
RUpperArmTwist02.height = ((execute  ("$'" +  filterRootBoneName[1] + " R UpperArm'")).height) * 0.8
RUpperArmTwist02.length = ((execute  ("$'" +  filterRootBoneName[1] + " R UpperArm'")).length) / 4
RUpperArmTwist02.parent = RUpperArmTwist01
RUpperArmTwist01.rotation = (inverse(biped.getTransform (execute("$'"+filterRootBipName[1] + " R UpperArm'")) #rotation))
RUpperArmTwist01.pos = ((execute("$'"+filterRootBipName[1] + " R UpperArm'")).transform.pos)
RUpperArmTwist01.parent = (execute  ("$'" +  filterRootBoneName[1] + " R UpperArm'"))
RUpperArmTwist02.parent = (execute  ("$'" +  filterRootBoneName[1] + " R UpperArm'"))
RUpperArmTwist01.rotation.controller=LookAt_Constraint()
RUpperArmTwist01.rotation.controller.appendTarget (execute  ("$'" +  filterRootBoneName[1] + " R Forearm'"))  50.0
RUpperArmTwist01.rotation.controller.viewline_length_abs = false
RUpperArmTwist01.rotation.controller.upnode_world = false
RUpperArmTwist01.rotation.controller.pickUpNode = Point_R_UpperArmTwist01
RUpperArmTwist01.wirecolor = (color 10 10 10)
RUpperArmTwist01.boxmode = on

RUpperArmTwist02.rotation.controller=Orientation_Constraint()
RUpperArmTwist02.rotation.controller.appendTarget RUpperArmTwist01 60.0
RUpperArmTwist02.rotation.controller.appendTarget (execute  ("$'" + filterRootBoneName[1] + " R UpperArm'")) 40.0
RUpperArmTwist02.wirecolor = (color 10 10 10)
RUpperArmTwist02.boxmode = on
)

if((execute  ("$'" +  filterRootBoneName[1] + " L Forearm'") != undefined) and (execute  ("$'" +  filterRootBoneName[1] + " L Hand'") != undefined)) do
(
-- 왼쪽 손목 트위스트 본
L_ForearmTwist = Bonesys.createBone[0,0,0][1,0,0][0,0,1]
L_ForearmTwist.name =  filterRootBoneName[1] + " L_ForearmTwist"
L_ForearmTwist.width = ((execute  ("$'" +  filterRootBoneName[1] + " L Forearm'")).width) * 0.8
L_ForearmTwist.height = ((execute  ("$'" +  filterRootBoneName[1] + " L Forearm'")).height) * 0.8
L_ForearmTwist.length = ((execute  ("$'" +  filterRootBoneName[1] + " L Forearm'")).length) / 4
L_ForearmTwist.rotation = (inverse(biped.getTransform (execute("$'"+filterRootBipName[1] + " L Forearm'")) #rotation))
L_ForearmTwist.pos = ((execute("$'"+filterRootBipName[1] + " L Forearm'")).transform.pos)
coordsys local move L_ForearmTwist [((execute  ("$'" + filterRootBoneName[1] + " L UpperArm'")).length) - (((execute  ("$'" + filterRootBoneName[1] + " L UpperArm'")).length) / 4),0,0]
L_ForearmTwist.parent = (execute  ("$'" +  filterRootBoneName[1] + " L Forearm'"))
L_ForearmTwist.rotation.controller=LookAt_Constraint()
L_ForearmTwist.rotation.controller.appendTarget (execute  ("$'" +  filterRootBoneName[1] + " L Hand'")) 50.0
L_ForearmTwist.rotation.controller.viewline_length_abs = false
L_ForearmTwist.rotation.controller.upnode_world = false
L_ForearmTwist.rotation.controller.pickUpNode = (execute  ("$'" +  filterRootBoneName[1] + " L Hand'"))
L_ForearmTwist.wirecolor = (color 10 10 10)
L_ForearmTwist.boxmode = on
)

if((execute  ("$'" +  filterRootBoneName[1] + " R Forearm'") != undefined) and (execute  ("$'" +  filterRootBoneName[1] + " R Hand'") != undefined)) do
(
-- 오른쪽 손목 트위스트 본
R_ForearmTwist = Bonesys.createBone[0,0,0][1,0,0][0,0,1]
R_ForearmTwist.name =  filterRootBoneName[1] + " R_ForearmTwist"
R_ForearmTwist.width = ((execute  ("$'" +  filterRootBoneName[1] + " R Forearm'")).width) * 0.8
R_ForearmTwist.height = ((execute  ("$'" +  filterRootBoneName[1] + " R Forearm'")).height) * 0.8
R_ForearmTwist.length = ((execute  ("$'" +  filterRootBoneName[1] + " R Forearm'")).length) / 4
R_ForearmTwist.rotation = (inverse(biped.getTransform (execute("$'"+filterRootBipName[1] + " R Forearm'")) #rotation))
R_ForearmTwist.pos = ((execute("$'"+filterRootBipName[1] + " R Forearm'")).transform.pos)
coordsys local move R_ForearmTwist [((execute  ("$'" +  filterRootBoneName[1] + " R UpperArm'")).length) - (((execute  ("$'" +  filterRootBoneName[1] + " R UpperArm'")).length) / 4),0,0]
R_ForearmTwist.parent = (execute  ("$'" +  filterRootBoneName[1] + " R Forearm'"))
R_ForearmTwist.rotation.controller=LookAt_Constraint()
R_ForearmTwist.rotation.controller.appendTarget (execute  ("$'" +  filterRootBoneName[1] + " R Hand'")) 50.0
R_ForearmTwist.rotation.controller.viewline_length_abs = false
R_ForearmTwist.rotation.controller.upnode_world = false
R_ForearmTwist.rotation.controller.pickUpNode = (execute  ("$'" +  filterRootBoneName[1] + " R Hand'"))
R_ForearmTwist.wirecolor = (color 10 10 10)
R_ForearmTwist.boxmode = on
)

if(((execute  ("$'" +  filterRootBoneName[1] + " L Thigh'")) != undefined) and ((execute  ("$'" +  filterRootBoneName[1] + " L Calf'")) != undefined)) do
(
-- 왼쪽 허벅지 트위스트 ExposeTM
ExposeTransformL = ExposeTransform()
ExposeTransformL.name = "ExposeTransform L"
ExposeTransformL.size = 1.5
ExposeTransformL.box = true
ExposeTransformL.cross = false
ExposeTransformL.rotation = (inverse(biped.getTransform (execute("$'"+filterRootBipName[1] + " L Thigh'")) #rotation))
ExposeTransformL.pos = ((execute("$'"+filterRootBipName[1] + " L Thigh'")).transform.pos)
ExposeTransformL.parent = (execute  ("$'" +  filterRootBoneName[1] + " L Thigh'"))
ExposeTransformL.exposeNode = (execute  ("$'" +  filterRootBoneName[1] + " L Calf'"))
ExposeTransformL.useParent = false
ExposeTransformL.localReferenceNode  = (execute  ("$'" +  filterRootBoneName[1] + " Pelvis'"))
ExposeTransformL.wirecolor = (color 10 10 10)

-- 왼쪽 허벅지 트위스트 더미
PointLThigh = point()
PointLThigh.name =  filterRootBoneName[1] + " Point L Thigh"
PointLThigh.size = 1.0
PointLThigh.box = false
PointLThigh.cross = true
PointLThigh.pos = ((execute("$'"+filterRootBipName[1] + " Pelvis'")).transform.pos)
coordsys local move PointLThigh [10,0,0]
PointLThigh.parent = (execute  ("$'" +  filterRootBoneName[1] + " Pelvis'"))
reactContL = PointLThigh.pos.controller = Position_reactor()
PointLThigh.wirecolor = (color 10 10 10)

-- 왼쪽 허벅지 트위스트 본
L_ThighTwist = Bonesys.createBone[0,0,0][1,0,0][0,0,1]
L_ThighTwist.name =  filterRootBoneName[1] + " L_ThighTwist"
L_ThighTwist.width = ((execute  ("$'" +  filterRootBoneName[1] + " L Thigh'")).width) * 0.8
L_ThighTwist.height = ((execute  ("$'" +  filterRootBoneName[1] + " L Thigh'")).height) * 0.8
L_ThighTwist.length = ((execute  ("$'" +  filterRootBoneName[1] + " L Thigh'")).length) / 4
L_ThighTwist.rotation = (inverse(biped.getTransform (execute("$'"+filterRootBipName[1] + " L Thigh'")) #rotation))
L_ThighTwist.pos = ((execute("$'"+filterRootBipName[1] + " L Thigh'")).transform.pos)
L_ThighTwist.parent = (execute  ("$'" +  filterRootBoneName[1] + " L Thigh'"))
L_ThighTwist.rotation.controller=LookAt_Constraint()
L_ThighTwist.rotation.controller.appendTarget  (execute  ("$'" +  filterRootBoneName[1] + " L Calf'")) 50.0
L_ThighTwist.rotation.controller.viewline_length_abs = false
L_ThighTwist.rotation.controller.upnode_world = false
L_ThighTwist.rotation.controller.pickUpNode = (execute  ("$'" +  filterRootBoneName[1] + " Point L Thigh'"))
L_ThighTwist.rotation.controller.upnode_ctrl = 0
L_ThighTwist.rotation.controller.StoUP_axisFlip = true
L_ThighTwist.wirecolor = (color 10 10 10)
L_ThighTwist.boxmode = on

reactContL.reactTo $'ExposeTransform L'.localPosition.controller
)

if(((execute  ("$'" +  filterRootBoneName[1] + " R Thigh'")) != undefined) and ((execute  ("$'" +  filterRootBoneName[1] + " R Calf'")) != undefined)) do
(
-- 오른쪽 허벅지 트위스트 ExposeTM
ExposeTransformR = ExposeTransform()
ExposeTransformR.name = "ExposeTransform R"
ExposeTransformR.size = 1.5
ExposeTransformR.box = true
ExposeTransformR.cross = false
ExposeTransformR.rotation = (inverse(biped.getTransform (execute("$'"+filterRootBipName[1] + " R Thigh'")) #rotation))
ExposeTransformR.pos = ((execute("$'"+filterRootBipName[1] + " R Thigh'")).transform.pos)
ExposeTransformR.parent = (execute  ("$'" +  filterRootBoneName[1] + " R Thigh'"))
ExposeTransformR.exposeNode = (execute  ("$'" +  filterRootBoneName[1] + " R Calf'"))
ExposeTransformR.useParent = false
ExposeTransformR.localReferenceNode  = (execute  ("$'" +  filterRootBoneName[1] + " Pelvis'"))
ExposeTransformR.wirecolor = (color 10 10 10)

-- 오른쪽 허벅지 트위스트 더미
PointRThigh = point()
PointRThigh.name =  filterRootBoneName[1] + " Point R Thigh"
PointRThigh.size = 1.0
PointRThigh.box = false
PointRThigh.cross = true
PointRThigh.pos = ((execute("$'"+filterRootBipName[1] + " Pelvis'")).transform.pos)
coordsys local move PointRThigh [-10,0,0]
PointRThigh.parent = ((execute  ("$'" +  filterRootBoneName[1] + " Pelvis'")))
reactContR = PointRThigh.pos.controller = Position_reactor()
PointRThigh.wirecolor = (color 10 10 10)

-- 오른쪽 허벅지 트위스트 본
R_ThighTwist = Bonesys.createBone[0,0,0][1,0,0][0,0,1]
R_ThighTwist.name =  filterRootBoneName[1] + " R_ThighTwist"
R_ThighTwist.width = ((execute  ("$'" +  filterRootBoneName[1] + " R Thigh'")).width) * 0.8
R_ThighTwist.height = ((execute  ("$'" +  filterRootBoneName[1] + " R Thigh'")).height) * 0.8
R_ThighTwist.length = ((execute  ("$'" +  filterRootBoneName[1] + " R Thigh'")).length) / 4
R_ThighTwist.rotation = (inverse(biped.getTransform (execute("$'"+filterRootBipName[1] + " R Thigh'")) #rotation))
R_ThighTwist.pos = ((execute("$'"+filterRootBipName[1] + " R Thigh'")).transform.pos)
R_ThighTwist.parent = (execute  ("$'" +  filterRootBoneName[1] + " R Thigh'"))
R_ThighTwist.rotation.controller=LookAt_Constraint()
R_ThighTwist.rotation.controller.appendTarget  (execute  ("$'" +  filterRootBoneName[1] + " R Calf'")) 50.0
R_ThighTwist.rotation.controller.viewline_length_abs = false
R_ThighTwist.rotation.controller.upnode_world = false
R_ThighTwist.rotation.controller.pickUpNode = (execute  ("$'" +  filterRootBoneName[1] + " Point R Thigh'"))
R_ThighTwist.rotation.controller.upnode_ctrl = 0
R_ThighTwist.wirecolor = (color 10 10 10)
R_ThighTwist.boxmode = on

reactContR.reactTo $'ExposeTransform R'.localPosition.controller
)
)
select bons
destroydialog bipMirrbone
)
)
createdialog bipMirrbone
)
================================================================================

아는 동생의 집에서 이 스크립트를 사용했는데 문제가 발생했다.
확인해 본 결과 종전의 맥스버전에서는 당연히 바이패드가 'Bip01'로 지정이 되는데
2012, 2013버전에서는 'Bip001'로 변경된것이다.
허거덩~~~~
물론 바이패드 이름을 'Bip01'로 변경해주면 되지만 그건 그거 나름대로 스크립트 작업자로서 거부감이 들어서 수정작업을 했다.
수정을 하면서 보니 트위스트 본에도 문제가 있다는것을 알았다.
조금더 확인 한다음에 올릴걸~~ㅠ,.ㅠ

아무튼 수정하여 올린다.