2019年7月10日 星期三

[啥Web]Live2D網頁化(WebGL化)

 

Live2D WebGL資源


  這次要筆記的是Live2D網頁化的步驟。基本上這個在台灣好像沒啥人討論,中國那邊倒是有不少資源。我個人剛開始研究就是從中國的大大開放的程式碼開始研究的,連live2D官網的都稍微有下載下來開始研究。但很遺憾的是,不是我看不懂就是研究出來不是自己要的。因為中國那些大大太好心了,他們提供的方法都會導致我的live2D亂動...因為我只是希望單純執行我站立動畫,我想他動啥動作再去利用js執行。主要原因是他們多寫了和滑鼠游標有關的系統,加上我又無法把他們寫的看懂。其實後者才是原因,主要是我功力不足,我基本上連官網提供的我也看不懂....所以我曾經一度想放棄....直到我不知道為啥利用live2d-helper這關鍵字找到了kooritea這位大大寫的live2d-helper。因為他提供的範例非常完整所以才終於修改他的達到我要的,而在開始講解我如何使用之前我先列一下我當時找的所有資源:

直接看範例但要先解決跨領域讀取問題


  從剛剛提到的https://github.com/kooritea/live2d-helper 打包下載(右邊綠色的clone&download),你會看到裡面有個demo資料夾,裡面的index.html就是他的範例網頁,而因為他寫的js有寫讀取功能,因此有些瀏覽器為了安全性,會禁止在js單機模式下進行跨領域的讀取。所以有些人直接打開該index.html是看不到任何東西的。我個人是用firefox 56版(所以您可以下載攜帶版),這個版本可以看到。但現在最新的瀏覽器似乎應該都禁止了,所以要嘛就是想辦法灌類似XAMPP建立簡單的local server或找網路的免費空間(如000webhostbyehost...等)將剛剛那一大包上傳,就是有些地方要動一點手腳了。

  我想現在大多數的人應該都有Chrome,所以這邊只講Chrome。對著你的Chrome捷徑右鍵選內容,在目標後面加入以下指令" --disable-web-security --disable-gpu --user-data-dir=~/chromeTemp"(第一格記得要空格),如圖:



  然後打開dist資料夾的index.js,找到以下程式碼:
    request.onload = function(){
        switch(request.status){
        case 200:
            callback(request.response);
            break;
        default:
            console.error("Failed to load (" + request.status + ") : " + path);
            break;
        }
    }
    request.send(null);
修改成:
    request.onload = function(){
        switch(request.status){
            //modified by Nil 為了讓Chrome可以跑   
        //還要在chrome目標後面加上 --disable-web-security --disable-gpu --user-data-dir=~/chromeTemp
         case 0:
             callback(request.response);
             break;   

        case 200:
            callback(request.response);
            break;
        default:
            console.error("Failed to load (" + request.status + ") : " + path);
            break;
        }
    }
    request.send(null);

  就可以在單機的Chrome看了,不過由於這方法算是強制打開,所以記得之後要把程式碼改回來。

  他的範例可以對著人物拖動讓角色眼和滑鼠有互動,並且還有生動的音效@@",可以到這裡看DEMO


輸出模型檔

   
  接下來要做的事情是把自己在live2D裡做好的角色放入他的範例
  
  這邊請先準備好您live2D的模型與動態檔。然後由於我安裝的live 2D Editor是官方最新的3.x版,kooritea大的live2d-helper是要用到2.1的版本檔案,所以我要做的第一件事就是將參數版本降為2.1版


Modeling->Convert Model ID

視窗出現後選擇2.1並按下OK

  這時整個檔案的規格就會變成2.1版的參數。

  然後接著輸出模型檔:

File->Export For Runtime->Export as moc file (For2.1)


Expoer a model setting file(modle,json)一定要打勾


   這時會輸出材質圖檔moc檔與之後很重要會拿來編輯的json檔

 輸出動態檔


  接下來就是打開你的動態檔:
File->Export For Runtime->Export motion file

 參數勾選Export all scenes以及 底下勾選Export as 2.1-format file

   建議在剛剛輸出模型檔的地方建一個motions資料夾(大小寫注意別寫錯,因為我有遇過我的免費網路空間判斷出錯),他會將所有動態輸出mtn檔。

建立自己的模型網頁


  這時候回到在一開始下載的網頁部分。在demo資料夾下的mode資料夾裡新增一個資料夾並將剛剛輸出的檔案都丟進去

  接下來打開demo資料夾下的index.html,找到baseUrl(角色資料夾路徑)與modelUrl(model.json檔路徑),並將資料夾路徑和檔名打對。還有確認model.json檔裡的路徑與對應的.moc檔與材質texture路徑是否正確,不過你model.json檔的輸出沒做任何檔名與位置更改,基本上不必擔心這一塊就是。

  這最基本的live2D網頁化就算成功了。如果你的角色是用live2D預設模型去做的,你會看到你可以對著人物做滑鼠拖動眼睛。所以應該是他本身js有抓到一些參數所以才能讀取眼睛的部分(你可在index.js搜尋PARAM_EYE就會看到相關程式碼)。不然如果完全是自己定義的參數,人物應該不會動就是。

  若出現破圖現象,我的作法是回到editor把圖集裡有透空但被算在範圍的圖拉開,而會明明透空還被算在Texture內的原因我個人推斷是你使用最一開始的模型被你後來多次重複使用的原因。像我這模型最一開始其實不是球兒的模型,我是拿別人畫好的圖去做live2D,只是這次為了DEMO所以我用我自己畫的圖,但因為想節省時間所以就拿之前那個檔案來做,雖然其實要微調的地方也算蠻多的就是,只是動作那裡我就完全沒去調整。

理解建構格式


  在index.html裡可以知道,他透過loadLive2D(JSON格式)來去呼叫建立canvas。所以基本的呼叫格式就是先在html寫一個canvas:
       <canvas id="id_live2D_canvas">
       </canvas>

  然後js再使用以下格式呼叫:
            var live2D;   
    var live2DWidth=640;
            var
live2DHeight=1000;
            var live2DJSON=
            {
              canvasId:'id_live2D_canvas', // canvas的id
              baseUrl: './live2D/Ball', // 资源原始路径
              modelUrl: './live2D/Ball/Ball.model.json', // 自定义model.json路径 方便用于一键换装

              crossOrigin: false, // 是否允许跨域获取数据(前提是http header中已有允许的跨域字段) def:false
              interval: 1, // 自动mation的开始时间点到下一个mation的开始点之间的间隔
              idle: 'idle', // 自动触发的mation
              width: live2DWidth, // html上的width属性优先级更高
              height: live2DHeight,// html上的height属性优先级更高

              globalollowPointer: false, // 全局跟随鼠标 def:false
              scaling: false, // 是否允许使用滚轮放大缩小 def:false
              debug:
              {
                DEBUG_LOG: false,
                DEBUG_MOUSE_LOG : false,
              },
              layout: { // 布局设置 这个优先级最高,然后到model.json(字段正确的话),再到默认
                width: '',
                height: '',
                x: '',
                y: '',
                center_x: '',
                center_y: '',
                top: '',
                bottom: '',
                left: '',
                right: ''
              },
              view: {
                VIEW_MAX_SCALE: 2, // 最大缩放比例
                VIEW_MIN_SCALE: 0.8, // 最小缩放比例
                VIEW_LOGICAL_LEFT: -1,
                VIEW_LOGICAL_RIGHT: 1,
                VIEW_LOGICAL_MAX_LEFT: -2,
                VIEW_LOGICAL_MAX_RIGHT: 2,
                VIEW_LOGICAL_MAX_BOTTOM: -2,
                VIEW_LOGICAL_MAX_TOP: 2
              },
              autoLoadAudio:function()
              {
                //console.log('audio loaded');
              }, // 自动下载音频 def:true
              initModelCallback(waifu)
              {
                //console.log(waifu);
                //console.log('加载完毕');
              }
            };

            live2D=loadLive2d(live2DJSON);

  比較要注意的是畫紅字的地方,除了名字與路徑要打對,初始化的寬度與高度那裡建議用兩個變數去記憶,因為之後會用到。剩下的參數就大家自行設定看看我比較沒有研究就是。

製作DEMO,修改js


  而接下來,要做的是接近我的demo的部分。我要做的內容是一個角色動態控制器,點選按鈕就表現什麼樣的動態。也因此我會把他的程式碼改很大,接下來的部分就需要一些基本的html與js語法概念了,如果文章看到這,您因為不會網頁技術,您其實可以跳到後面我有提到怎麼修改我寫的demo檔。由於我不需要他的拖曳功能,所以我就把他index.js(在我的打包檔有將此檔改名,是放在js/live2d的live2d-helper.js)裡把modelTurnHead這function下的obj.drag = true改為false,並把obj.dragMgr.setPoint(vx,vy)這段程式給註解掉。這樣他的拖曳功能就沒有了。

  然後,要注意的是,如果你更改了canvas的大小,建議modelTurnHead該函式下取的x與y坐標位置也要更改。因為他有提供點擊身體部位做出反應的方法(這個之後會提),因此若這邊沒修改,你更改了canvas的大小他的sx,sy,vx,vy變數會改變,在判斷實際位置會抓錯。而修改方式就是將你縮放的比例用一個變數記住,我是在js開頭就宣告一個叫changeScale=1的變數,他是我之後會提的製作RWD的縮放比例,在他相關的sx,sy與vx,vy變數去除以changeScale來還原我們原本模型正確的位置坐標,也因此modelTurnHead這funtcion下的程式碼我改成這樣:
    obj.drag = false;//20181122 modified by Nil 不要讓角色可拖曳

    var rect = event.target.getBoundingClientRect();

    var sx = transformScreenX(obj,(event.clientX - rect.left)/changeScale);//20181122 modified by Nil 因為rwd進行縮放時會更改他的判斷位置,要還原原本沒被改的大小位置
    var sy = transformScreenY(obj,(event.clientY - rect.top)/changeScale);//20181122 modified by Nil 因為rwd進行縮放時會更改他的判斷位置,要還原原本沒被改的大小位置

    var vx = transformViewX(obj,(event.clientX - rect.left)/changeScale);//20181122 modified by Nil 因為rwd進行縮放時會更改他的判斷位置,要還原原本沒被改的大小位置
    var vy = transformViewY(obj,(event.clientY - rect.top)/changeScale);//20181122 modified by Nil 因為rwd進行縮放時會更改他的判斷位置,要還原原本沒被改的大小位置
    if (obj.debug.DEBUG_MOUSE_LOG)
        console.log("onMouseDown device( x:" + event.clientX + " y:" + event.clientY + " ) view( x:" + vx + " y:" + vy + ")");

    obj.lastMouseX = sx;
    obj.lastMouseY = sy;
    //console.log(changeScale);
    //obj.dragMgr.setPoint(vx, vy);//20181122 modified by Nil 為了不要讓他拖曳亂動

  因為我的模型是預設模型,所以他會自動眨眼,我這邊將thisRef.eyeBlink = new L2DEyeBlink();這段段程式碼註解掉。

  所以如果還可能有其他衝突,可能就要搜尋"PARAM_"開頭的字樣去尋找哪些動作讓他啟動了。

  最後由於這個程式有鎖右鍵,這讓我每次想檢查元素很不方便,所以我也將obj.canvas.addEventListener("contextmenu", mouseEvent, false);那段程式碼註解掉了

製作等比例縮放


  這邊因為為了要讓角色等比例縮放,所以我個人採用了Adobe Animate他HTML5 RWD輸出的方式去做修改,在自己的index.html寫了以下程式:
            var live2DCanvas = document.getElementById("id_live2D_canvas");
            var canvasSizeModified=1.15;
            function makeResponsive()
            {   
 
                window.addEventListener('resize', resizeCanvas);       
                resizeCanvas();    
                function resizeCanvas()
                {          
                    var w = live2DWidth, h = live2DHeight;           
                 
                    var ih=$(window).height();//這邊是依照高度做等比例縮放
                    var pRatio = window.devicePixelRatio || 1, yRatio=ih/h,sRatio=1;         

                    if(h>ih)
                    {              
                        sRatio = yRatio;            
                    }
                             
                    live2DCanvas.width = w*pRatio*sRatio*canvasSizeModified;        
                    live2DCanvas.height = h*pRatio*sRatio*canvasSizeModified;
                    live2DCanvas.style.width =  w*sRatio*canvasSizeModified+'px';              
                    live2DCanvas.style.height = h*sRatio*canvasSizeModified+'px';
                    changeScale=sRatio*canvasSizeModified;//為了live2D點擊位置判斷用
                }
            }
            makeResponsive(); 

  而我是依照整個網頁的高度做等比例縮放,這邊若其他人並非像我是這種一頁式的,可能需要去針對ih這變數去做修改,比如讓ih等於某個物件的高度做等比例縮放。

  要注意裡面有個changeScale,這邊這個變數就是前面我提的,在我的live2d-helper.js的變數,他是用來判斷還原正確的身體部位位置用的。

修改model.json檔


  如果你對前面輸出靜態model時有印象,會有個檔名.model.json的檔案,這個檔案如果你有去觀察他的範例檔,會發現裡面寫了很多東西,他其實包含了點擊部位以及可讓js控制執行的動作檔、該動作檔的fadein與fadeout時間和可發出的音效

  其實這個檔案能做的東西不只我上面提的就是,而我個人除了音效外只研究到剛剛提的部分,所以我提供的打包檔其實沒有用到音效,但我會稍微提一下。

  我基本上就是新增點擊部位名稱與id,這些我一律全都是live2D裡的部位id,如胸部我就叫body_chest,如圖示:

  再來就是新增動態與檔案關聯性,fadeIn與fadeOut時間(單位毫秒)。

  最後這邊提的loop,要給定true或false,為了要讓該角色動作是否要保持循環(例如說話就需要)。這是我個人自己寫的,所以之後會提到還得修改live2d-helper.js檔。

  所以完整的基本json檔如以下格式:
{
    "type": "Live2D Model Setting",
    "name": "Ball",
    "model": "Ball.moc",
    "textures": [
        "Ball.2048/texture_00.png"
    ],

        "hit_areas":
        [
            {"name":"body_chest", "id":"body_chest"},
            {"name":"leg_right", "id":"leg_right"},
            {"name":"leg_left", "id":"leg_left"},
            {"name":"face_base", "id":"face_base"},
            {"name":"body", "id":"body"},
            {"name":"arm_left_up", "id":"arm_left_up"},
            {"name":"arm_left_down", "id":"arm_left_down"}
           
        ],
        "motions":
        {
            "idle":
            [
                {
                    "file":"motions/Ball_1_front_happy_stand_0.mtn",
                    "fade_in":1000,
                    "fade_out":1000,
                    "loop":true
                }
            ],
            "Ball_1_front_unhappy_stand":
            [
                {
                    "file":"motions/Ball_1_front_unhappy_stand_0.mtn",
                    "fade_in":100,
                    "fade_out":100,
                    "sound":"sound/D_Sak_Live2D001.wav",
                    "sound_delay":-1,

                    "loop":true
                },
                {
                    "file":"motions/Ball_1_front_unhappy_stand_1.mtn",
                    "fade_in":100,
                    "fade_out":100,
                    "loop":true
                }
            ]
        }
}

  而要注意的是,他這程式裡把idle預設為站立動態檔,所以這是基本一定要寫入的。另外上面的sound在我的球兒demo檔不會有,但卻可以從kooritea大大的範例檔找到是如何播放音效的。

修改loop


  接下來為了讓前面提的loop能實現,要打開live2d-helper.js修改。

  首先你找到function LAppModel(obj)這個函式,會發現他底下有寫一個timer,那個就是他寫的為將動作做完切回idel這動作用的。我改寫的就是那個地方。

  先在js最上頭新增兩個變數名稱,一個是nowMotionName="idle"一個是nowMotionIndex=0。這是為了要記憶每次動作如果是loop,他在做完該動作後去持續執行用的。所以你會看到我將timer改寫成以下內容:
function LAppModel(obj)
{
    //L2DBaseModel.apply(this, arguments);
    L2DBaseModel.prototype.constructor.call(this);

    this.modelHomeDir = "";
    this.modelSetting = null;
    this.tmpMatrix = [];
    this.obj = obj;
    // this.timmer = setInterval(() => {
    //   if (this.mainMotionManager.isFinished())
    //   {
    //    this.startRandomMotion(this.obj.idle, 1);
      
    //   }
    // },this.obj.interval)
    //modified by Nil 20190706 為了不讓他會自動切回站立動作

    //modified by Nil 20190706 為了加入判斷loop動作
    this.timmer = setInterval(() =>
    {
      if (this.mainMotionManager.isFinished())
      {

           if(this.modelSetting.json.motions!=undefined)
           {
              
               if(this.modelSetting.json["motions"][nowMotionName][nowMotionIndex]["loop"])//如果該動作是循環則繼續
               {
                  
                   this.startMotion(nowMotionName,nowMotionIndex, 3);
               }
               else
               {
                   this.startMotion("idle",0, 3);//沒有loop就切回idle
               }

           }
      }

    },this.obj.interval)

}

  這邊大概提一下流程就是他透過this.mainMotionManager.isFinished()判斷目前動作有無結束,如果動作結束,我再透過this.modelSetting.json["motions"][nowMotionName][nowMotionIndex]["loop"]讀取前面提到的model.json判斷這動作是否循環,如果是循環則透過startMotion繼續執行,不是就回去idle站立動畫。這邊提一下他這程式有兩個方法可以啟動動作,一個是startMotion另一個是startRandomMotion,如果你有注意,在動作種類底下的動作檔是可以放一個以上的,所以我才稱第一個要放的參數名字是叫動作種類,第二個參數就是該動作種類第幾個要播放,第三個參數是優先權,分為1、2、3,我基本上都放3強制執行。因此startRandomMotion的意思就是從該動作種類隨機挑選一個執行,所以他第二個參數不必放要播第幾個,他要放的是優先權。

  最後要去LAppModel.prototype.startMotion = function(name, no, priority, hitArea, callback)
{記住每次執行動作的動作種類名稱(nowMotionName)和動作位置(nowMotionIndex)
。程式碼如下:

LAppModel.prototype.startMotion = function(name, no, priority, hitArea, callback)
{
    // console.log("startMotion : " + name + " " + no + " " + priority);
    var motionName = this.modelSetting.getMotionFile(name, no);
    nowMotionName=name;//modified by Nil 20190706 取得目前動作名稱用
    nowMotionIndex=no;//modified by Nil 20190706 取得目前動作名稱index用


//(略)
}

 

新增點擊部位反應


  前面有提到過,他的modle.json裡有對應live2D裡的身體部位id,也就是hit_areas部份。而你要針對比如點擊身體他會有什麼樣的反應你要在一開始index.html的建置初始呼叫的json格式要修改,需要新增binding,並在底下放入對應的name與函式,總之格式如下:
            var live2DJSON=
            {
              canvasId:'id_live2D_canvas', // canvas的id
              baseUrl: './live2D/Ball', // 资源原始路径
              modelUrl: './live2D/Ball/Ball.model.json', // 自定义model.json路径 方便用于一键换装
              crossOrigin: false, // 是否允许跨域获取数据(前提是http header中已有允许的跨域字段) def:false
              interval: 1, // 自动mation的开始时间点到下一个mation的开始点之间的间隔
              idle: 'idle', // 自动触发的mation
              width: live2DWidth, // html上的width属性优先级更高
              height: live2DHeight,// html上的height属性优先级更高
              globalollowPointer: false, // 全局跟随鼠标 def:false
              scaling: false, // 是否允许使用滚轮放大缩小 def:false
              debug:
              {
                DEBUG_LOG: false,
                DEBUG_MOUSE_LOG : false,
              },
              layout: { // 布局设置 这个优先级最高,然后到model.json(字段正确的话),再到默认
                width: '',
                height: '',
                x: '',
                y: '',
                center_x: '',
                center_y: '',
                top: '',
                bottom: '',
                left: '',
                right: ''
              },
              view: {
                VIEW_MAX_SCALE: 2, // 最大缩放比例
                VIEW_MIN_SCALE: 0.8, // 最小缩放比例
                VIEW_LOGICAL_LEFT: -1,
                VIEW_LOGICAL_RIGHT: 1,
                VIEW_LOGICAL_MAX_LEFT: -2,
                VIEW_LOGICAL_MAX_RIGHT: 2,
                VIEW_LOGICAL_MAX_BOTTOM: -2,
                VIEW_LOGICAL_MAX_TOP: 2
              },
              binding:
              { // 需要自行根据不同模型的model.json将mation绑定到对应的hit_areas 支持hit_areas_custom

                body: function()
                {//點身體

                  live2D.startMotion("Ball_1_front_unhappy_shakeHead",0, 3);
                },
                body_chest: function()
                {//點胸部
                  //console.log(this);
                 live2D.startMotion("Ball_1_front_unhappy_roar",0, 3);
                 
                }
              },

              autoLoadAudio:function()
              {
                //console.log('audio loaded');
              }, // 自动下载音频 def:true
              initModelCallback(waifu)
              {
                //console.log(waifu);
                //console.log('加载完毕');

              }
            };

  而若有看我打包的檔案,我是點擊什麼部位就做出什麼樣的反應,所以我執行的是live2D.startMotion("Ball_1_front_unhappy_shakeHead",0, 3);

  如果還記得前面提的startMotion與startRandomMotion。這邊由於是外部的呼叫,所以要利用之前記憶呼叫建置的變數live2D.startMotion與live2D.startRandomMotion。我demo的按鈕都是在執行該功能而已。
 

讀取中動畫


  其實live2D網頁化放在網路上的讀取的速度有時候會很慢,所以以在使用感觀上我覺得要加讀取中的動畫,只要在前面提到的初始json格式新加一個initModelCallback函式在該函式裡把讀取中的小動畫隱藏就可以,如以下格式:
            var live2DJSON=
            {
              canvasId:'id_live2D_canvas', // canvas的id
              baseUrl: './live2D/Ball', // 资源原始路径
              modelUrl: './live2D/Ball/Ball.model.json', // 自定义model.json路径 方便用于一键换装
              crossOrigin: false, // 是否允许跨域获取数据(前提是http header中已有允许的跨域字段) def:false
              interval: 1, // 自动mation的开始时间点到下一个mation的开始点之间的间隔
              idle: 'idle', // 自动触发的mation
              width: live2DWidth, // html上的width属性优先级更高
              height: live2DHeight,// html上的height属性优先级更高
              globalollowPointer: false, // 全局跟随鼠标 def:false
              scaling: false, // 是否允许使用滚轮放大缩小 def:false
              debug:
              {
                DEBUG_LOG: false,
                DEBUG_MOUSE_LOG : false,
              },
              layout: { // 布局设置 这个优先级最高,然后到model.json(字段正确的话),再到默认
                width: '',
                height: '',
                x: '',
                y: '',
                center_x: '',
                center_y: '',
                top: '',
                bottom: '',
                left: '',
                right: ''
              },
              view: {
                VIEW_MAX_SCALE: 2, // 最大缩放比例
                VIEW_MIN_SCALE: 0.8, // 最小缩放比例
                VIEW_LOGICAL_LEFT: -1,
                VIEW_LOGICAL_RIGHT: 1,
                VIEW_LOGICAL_MAX_LEFT: -2,
                VIEW_LOGICAL_MAX_RIGHT: 2,
                VIEW_LOGICAL_MAX_BOTTOM: -2,
                VIEW_LOGICAL_MAX_TOP: 2
              },
              binding:
              { // 需要自行根据不同模型的model.json将mation绑定到对应的hit_areas 支持hit_areas_custom

                body: function()
                {

                  live2D.startMotion("Ball_1_front_unhappy_shakeHead",0, 3);
                },
                body_chest: function()
                {
                  //console.log(this);
                 live2D.startMotion("Ball_1_front_unhappy_roar",0, 3);
                 
                }
              },
              autoLoadAudio:function()
              {
                //console.log('audio loaded');
              }, // 自动下载音频 def:true
              initModelCallback(waifu)
              {
                //console.log(waifu);
                //console.log('加载完毕');
                isModleFinish=true;
                live2DProgress=1;//確定讀完
                $("#id_loading_img").hide();//將讀取動畫隱藏
              }

            };

  那個 initModelCallback函式裡的程式就代表他建置完成會做的動作。

  這邊如果有注意我還寫了個live2DProgress,其實是我之前勉強算是有研究出來,他的讀取進度。但目前只研究到初始時的進度,換裝時的進度我無法讀取。詳細作法可以去live2d-helper.js搜尋"modified"找進度讀取相關的註解。其實原理就是我得知他在function draw(obj)裡會去嘗試讀取五個地方,而我就是在那五個地方確定有讀取到時就判斷會獲得一個進度點,也因此獲得五個進度點就代表完成。這也意味著每完成一個是完成20%的意思。所以你可以透過自己寫一個timer去判斷目前進度多少%。但這個要搭配在index.html裡的 initModelCallback函式就是,所以其實蠻容易混亂的。


換模型


  基本上換模型就是把前面用來記憶初始json的變數,修改json檔後再重新利用loadLive2D重新呼叫就是。只是我的demo檔我加了一些fadeIn與fadeOut效果。本來是有加入jquery ui有很炫的切換。但因為我canvas的置中方式是絕對路徑加上transform。好像用到transform時jquery UI都會在位置上出問題,所以我就放棄了。

  總之當你要換裝時,可以寫以下程式碼:
           var cheatToogle=false;
            function change()
            {
                      $("#id_loading_img").fadeIn();
                      $( "#id_live2D_canvas" ).fadeOut("slow",function()
                      {
                         if(cheatToogle)
                         {
                           cheatToogle=false;
                           live2DJSON.baseUrl="./live2D/Ball";       
                           live2DJSON.modelUrl="./live2D/Ball/Ball.model.json";

                          
                         }
                         else
                         {
                           cheatToogle=true;
                           live2DJSON.baseUrl="./live2D/Ball_comic";       
                           live2DJSON.modelUrl="./live2D/Ball_comic/Ball.model.json";

                          }
                         var tmpFunc=function()
                         {
                          $("#id_live2D_canvas").fadeIn();
                          $("#id_loading_img").fadeOut();
                         }
                        live2DJSON.initModelCallback=tmpFunc;
                         live2D=loadLive2d(live2DJSON);

                       });
            }

  這邊要提一個我發生的問題,就是換模型後,有些電腦裡我點胸部的功能就消失了,但有些電腦又ok,且這功能我在之前寫的其他網頁時完全沒這問題,所以我就不清楚到底是哪個環結出錯了就是。但也只有點胸部功能失常,其他都好好的就是。

若您不會寫網頁..


  其實我寫這個筆記有個初衷是希望不會寫網頁的live2D人也能分享他的作品,但現在想想,感覺還是有點複雜。最基本的是你將你的檔案覆蓋我輸出的所有live2D檔(但要記得modle.json檔下面打的那一大串要記得保留)。當然你若是想用自己的動作檔名,你可以更改我的檔案,在index.html裡找到:
<img class="img-fluid center-block myBtn btnLive2D"  src="images/style0/btn4-1.png?v=1.0.6" title="眨眼1" id="id_btn_Ball_1_front_happy_blink_0">

  你會發現我有很明顯的規則變化,id_btn_後接著就是動態檔mtn的名字,你直接用我的demo頁改了之後並在modle.json的motion新增你的動作類別和第1個以上的動作檔位置指定,應該就可以變成你要的動作(但要記得第一個動作一定要在"_"後寫0)。至於換圖我想你去images找到相對應圖檔去換應該就可以了。不過我要說我因為是demo用,所以我這網頁寫得還不夠好,沒考慮到平板看的樣子,他的介面會拉長以及按鈕位置會跑掉。


最後補充


  •  
    關於DEMO頁面的角色是我的創作《甘姆》中未來希望會登場的角色-球兒,該demo頁右下角問號按鈕可連至其FB相片說明頁,左下角的C按鈕可以變成黑白漫畫版本。然後如果有注意到黑白漫畫版本的胸部比較詭異,這是因為這個live2D的模板原本不是為了球兒設計的,在製作的過程中調整正面時他變形得很嚴重,我花了很大的力氣才調成現在這樣= ="...至於彩色版為何沒變形,是因為我後來花力氣把一些參數通通都調掉,不讓他轉正面是45度角側面。省了再調整的力氣....至於為啥不把黑白的拿來套再輸出...我其實不太確定還有沒有機會再有地方變形...就懶得做了Orz...
     
  • 其實我覺得這很少人研究也可能跟效能有點差有關,但我實在不知道該怎麼優化,我頂多是做到把輸出的圖片拿去壓縮,剩下就完全沒有頭緒了。不然就是要研究一下3版的phina.js,但這部分我已經很累了,就懶了。只能說希望官方能開發出更有效率也更好使用的工具了。
     
  • 有些地方還可以再優化,比如如果要做兩個模型同時在場上我前面有些寫法會出問題,例如等比例縮放那邊。
     
  • 希望能看到更多的live2D分享,老實說我覺得目前很多遊戲做的動態都太愛亂動了,我個人比較理想的站立動畫應該單純動上半身做呼吸動作就好。當然不做呼吸,只是做單純臉部動作以及偶而一個大動作可能更好,我沒採取的原因是因為原本球兒的動作不是為了我demo做的,所以在製作上還是要符合一下交代……總之雖然自己做的也很普通,但就是希望能看到更多的動態其實是自然的並且有2D動畫質感的。簡單來說,以碧藍幻想這部卡通材質的3D遊戲為例(因為實在找不到我覺得OK的Live2D動畫...很多遊戲都用他來讓站立動畫是亂動的…):

    大約39秒處開始

    他在裡面的站立動畫只有做呼吸,除非是要做比較特別的動作才會有誇張的擺動,但那些擺動都很自然,不是為動而動。  
    而特別擺動畫live2D動畫中我是有注意到一款遊戲做的蠻有2D動畫質感的,是台灣做的天使帝國幻獸之月,如這個:


    雖然一開始有些地方動得還是有點電腦感,但他後面拿劍那邊的動畫真的做得很不錯,很有2D動畫質感。其實我還看過更厲害的,是穿著泳裝拿水槍的希蜜,但youtube找不到,我勉強只找到下面這個影片:

    請直接拉到11:34

    我看到的是有發射水槍的,但很遺憾她沒呈現到希蜜發射水槍的動態就關掉直播了,其實從他搭配一個角色的活潑質感,多少能感受得到他站立動態算做得不錯。雖然我前面說理想上的站立動畫應該只要做呼吸就好,可是如果是為了符合情境,有些角色確實動態上會活潑一點。只是我還是覺得有稍微一丁點不自然感,但水槍發射那動態是真的做得很好,我當時看到的檔案其實算是不能公開的檔案,所以就沒辦法讓大家看。我個人是還蠻希望能用live2D做動態時能達到和我們在看的2D動畫一樣的質感就是。主要是live2D可以和遊戲做很好的結合,他在輸出動畫會比在遊戲引擎裡重新再編排動作來得有效率。所以如果可以在這裡做到和動畫一樣質感的動作,在遊戲中就也能都看得到,我還蠻希望自己能做到或看到有人做到就是。

1 則留言:

匿名 提到...

謝謝 :>