2014年9月11日木曜日

まばたきを実装せよ。

お久しぶりです><
ブログの更新がなくてもいろいろとやっておりました。ブログはある程度「ちゃんとした情報」を上げるようしていますが、日々のくだらない話などは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