お久しぶりです><
ブログの更新がなくてもいろいろとやっておりました。ブログはある程度「ちゃんとした情報」を上げるようしていますが、日々のくだらない話などはG+のほうをご覧いただければ、と思っています。
ちなみについ最近「勝手にみっくみくダヨー夏祭り」をやってました><
ところで、何度も紹介していますが、以下の動画をご覧くださいませ。
口パクもそうですが、「瞬き」してます。はい、するんです。
ただ、YouTube の動画のコメントにもあるように、「どうやってるの?」と思われる方も多いようです。これ、別に「秘密の奥義」なるものは存在せず、要は「パラパラ漫画」をセカンドライフ内で実装しているだけなんです。
多くの 3DCG の世界だと「モーフ」とか「シェイプキー」でこのような動きを実装しているのですが、セカンドライフにはモーフも ShapeKey もありません。無いんだったら何とかするしかないわけですw
で、このビデオの Head のパーツは以下のようなメッシュから構成されています。
このメッシュパーツをすべて「材質名」を個別にして1つのオブジェクトにします。1オブジェクトの材質数の上限は「8」なので、必ず8以下にします。そうすると1つのオブジェクトに8つの材質名をもったメッシュをアップロードできるわけです。
材質はセカンドライフでは「面」となります。あとはスクリプトで面の透過・不透過を繰り返すわけです。
が、ここで LSL(スクリプト)の知識が必要になります。
このパラパラ漫画方式のスクリプトのポイントは「for 文を使わない」です。
for の代わりに llSetTimerEvent を使って繰り返しの処理を行うのが基本中の基本。なぜならば、for ループに入っちゃうと「制御ができない」からです。止めたいときに止められない、という感じですね。
瞬き部分などは具体的に以下のようなソースになります。
list blinkList0 = [6,5,4,3,4,4,3,4,5,6]; //瞬きのパターンその1
list blinkList1 = [6,5,4,3,3,3,3,4,5,6]; //瞬きのパターンその2
list eyeList = [];
float interval = 0.12;
integer cIndex = 0;
integer cFace = 1;
integer setEye;
list blink = [];
integer blinkStat = FALSE;
integer myLink = -4; //LINK_THIS
key owner;
integer myChan = 1212;
integer handle;
integer bFlg=FALSE;
blinking()
{
//llWhisper(0,"bon "+(string)cIndex);
if(cIndex<llGetListLength(blink))
{
integer nextFace = llList2Integer(blink,cIndex);
if(cFace!=nextFace)
{
llSetLinkAlpha(myLink,1.0,nextFace);
llSetLinkAlpha(myLink,0.0,cFace);
cFace = nextFace;
}
cIndex++;
llSetTimerEvent(interval);
}else{
cIndex=0;
blinkStat=FALSE;
llSetAlpha(1.0,setEye);
if(cFace!=setEye)
{
llSetAlpha(0.0,cFace);
}
cFace=setEye;
llSetTimerEvent(llFrand(5.0)+5.0);
}
}
default
{
attach(key id)
{
if(id!=NULL_KEY)
{
//llWhisper(0,(string)llGetLinkNumber());
myLink = llGetLinkNumber();
owner=id;
handle = llListen(myChan,"","","");
llListenControl(handle,TRUE);
}else{
llListenControl(handle,FALSE);
}
}
state_entry()
{
//llSay(0, "face "+(string)llGetNumberOfSides());
eyeList=[];
integer i=0;
for(;i<llGetNumberOfSides();i++)
{
eyeList+=(string)i;
}
llSetLinkAlpha(myLink,0.0,0); //JITOME
llSetLinkAlpha(myLink,0.0,1); //WINKU
llSetLinkAlpha(myLink,0.0,2); //SMILE
llSetLinkAlpha(myLink,0.0,3); //Close
llSetLinkAlpha(myLink,0.0,4); //30
llSetLinkAlpha(myLink,0.0,5); //60
llSetLinkAlpha(myLink,1.0,6); //OPEN
llSetLinkAlpha(myLink,0.0,7); //Hau
blinkStat=TRUE;
blink = blinkList0;
setEye = 6;
owner=llGetOwner();
handle = llListen(myChan,"","","");
llListenControl(handle,TRUE);
llSetTimerEvent(1.0);
}
timer()
{
llSetTimerEvent(0.0);
if(blinkStat){ //TRUE
blinking();
}else{ //FALSE
integer rand = ((integer)llFrand(11.0))%2;
//llWhisper(0,(string)rand);
if(rand){ //1 TRUE
blink = blinkList0;
}else{ //0 FALSE
blink = blinkList1;
}
blinkStat=TRUE;
llSetTimerEvent(0.5);
}
}
listen(integer chan,string name,key id,string msg)
{
//llOwnerSay(msg);
list cmdList = llParseString2List(msg,["|"],[""]);
string idx = llList2String(cmdList,0);
string cmd = llList2String(cmdList,1);
string opt = llList2String(cmdList,2);
if(idx==(string)owner){
if(llToUpper(cmd)=="FACE")
{
//llSetTimerEvent(0.0);
if(llToUpper(opt)=="LIST"){
llOwnerSay(llDumpList2String(eyeList,","));
llOwnerSay("Current face:"+(string)setEye);
}else if(llToUpper(opt)=="ON"){
llSetTimerEvent(0.5);
}else if(llToUpper(opt)=="OFF"){
llSetTimerEvent(0.0); //いみない
}else if(llToUpper(opt)=="HIDE"){
llSetTimerEvent(0.1);
if(bFlg==TRUE){
llOwnerSay("Blinking ON");
bFlg=FALSE;
}
}else if(opt!=""){
if(bFlg==FALSE){
llSetTimerEvent(0.0);
llOwnerSay("Blinking OFF");
bFlg=TRUE;
}
integer nextFace = (integer)opt;
llSetAlpha(1.0,nextFace);
if(nextFace!=cFace)
{
llSetAlpha(0.0,cFace);
}
cFace = setEye=nextFace;
}
}
}
}
}
このくらいのスクリプトで瞬きの実装ができます。
材質=面の番号はオブジェクトをアップしてみないとわかりませんので、0~7の面がどれに相当するかをチェックして、瞬きに必要なメッシュの面番号を「瞬きパターン」として登録しています。
以上、「瞬きを実装せよ」でしたw