three.jsで3Dキャラを作成:腕を作る(1)

今回から本格的にキャラクターの形を作っていく。

まず前回までで作ったチューブ状の形を腕っぽく曲げてアニメーションするための仕組みを作る。

腕を上下させる角度を決定する

以下のような腕の可動域を考えて、ボーンの終端座標を角度から計算する。

const v = 1;
const bend = mapping( v, -1.0, 2.0, PI/4, -PI/2 );
function mapping( inVal, inMin, inMax, outMin, outMax ){
    const ratio = ( inVal - inMin ) / ( inMax - inMin );
    const outVal = ratio * ( outMax - outMin ) + outMin;
    return outVal;
};

アニメーションを付けるときに指定しやすいよう(角度をいちいち指定するのは面倒なので)、まず上記のようなmapping関数を定義してv = -1~2の値から角度を取れるようにした。

次に、腕の長さmapping関数で取得した角度から、ベジエ曲線の終端とコントロール点の座標を計算する。

const { ep, cp } = getBezierPt( armLength, bend );
function getBezierPt( len, bend ){
    const ep_x = len * cos( bend );
    const ep_y = len * sin( bend );
    const cp_x = abs( ep_y / 2 );
    const cp_y = Math.max( ep_y / 2, 0 );
    const ep = new THREE.Vector2( ep_x, ep_y );
    const cp = new THREE.Vector2( cp_x, cp_y );
    return { ep, cp }
}

cpのx座標は、(T字ポーズをしている時が腕の曲げが最小であるとして)腕の曲げが上下どちらの方向にも大きい時に大きくする。

cpのy座標は、腕を上に曲げた時だけ上方向に大きくする。

この時戻り値と受け取る側の変数名(ep, cp)が一致している必要がある(連想配列を渡しているのでキー名は任意にできない)ので注意。

upperArmのメッシュを生成

上腕パーツの初期状態(横にまっすぐ伸びている状態)のメッシュを生成してシーンに追加するまでの工程をupperArmInit関数として定義。

前回記事と同様に、手足オブジェクト(Limbs)のインスタンスにパラメータを設定する。

ジオメトリはアニメーション時に更新するためグローバル変数として定義しておく。(初回makeGeometry作成時に再代入されるのでletで定義)

//ジオメトリはmakeGeometry呼び出しで再代入される
let upperArmGeo;

//Limbsクラスのインスタンス化
const upperArmObj = new Limbs();

//upperArmを初期化して生成
upperArmInit();
function upperArmInit(){
    //太さをセット
    const upperArmThicks = new Array( limbSeg );
    for( let i=0; i<( limbSeg+1 ); i++ ){
        upperArmThicks[i] = upperArmThick;
    }
    //各パラメータをセット
    upperArmObj.ep = new THREE.Vector2( upperArmLength,0 );
    upperArmObj.thick = upperArmThicks;
    upperArmObj.width = upperArmThicks;
    //メッシュを作成しシーンに追加
    const pt = makePipePt( upperArmObj );
    upperArmGeo = makeGeometry( upperArmObj, pt );
    const mesh = new THREE.Mesh( upperArmGeo, upperArmMat );
    scene.add( mesh );
 }

アニメーションする

アニメーションシステムを使って、値が4秒かけて-1→2→-1とリニアに変化するアニメーションを作成。

const upperArmMove = new THREE.Object3D();
const dur = [ 0, 2, 4 ];
const val = [ -1, 2, -1 ];
const uppperArmKF = new THREE.NumberKeyframeTrack( '.userData', dur, val );
const clip = new THREE.AnimationClip( 'Action', 4, [ uppperArmKF ] );
const mixer = new THREE.AnimationMixer( upperArmMove );
const clipAction = mixer.clipAction( clip );
clipAction.play();

引数angleに指定した値からep,cpを求め、ジオメトリを更新する関数を定義。

function armUpdate( angle ){
    const bend = mapping( angle, -1.0, 2.0, PI/4, -PI/2 );
    const { ep, cp } = getBezierPt( upperArmLength, bend );
    upperArmObj.ep = ep;
    upperArmObj.cp = cp;
    const pt = makePipePt( upperArmObj );
    updateGeometry( upperArmObj, pt, upperArmGeo );
}

レンダリングサイクル内でarmUpdateを呼び出してジオメトリを更新する。

const clock = new THREE.Clock();
let armAngle;
render();
function render(){
    //アニメーションのアップデート
    mixer.update(clock.getDelta());
    armAngle = upperArmMove.userData;
    limbUpdate( armAngle );
    //次のサイクル呼び出し
    requestAnimationFrame(render);
    renderer.render(scene, camera);
}

腕を上げ下げしているイメージの、このようなアニメーションが完成。

「three.jsで3Dキャラを作成:腕を作る(1)」への1件のフィードバック

コメントは停止中です。