今回から本格的にキャラクターの形を作っていく。
まず前回までで作ったチューブ状の形を腕っぽく曲げてアニメーションするための仕組みを作る。
腕を上下させる角度を決定する
以下のような腕の可動域を考えて、ボーンの終端座標を角度から計算する。
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件のフィードバック
コメントは停止中です。