junkerstock
 vrm-text-bvh4 

<!DOCTYPE html>
<html>
<head>
<title>VRM with BVH Animation Player (Final Fix)</title>
<meta charset="utf-8">
<script src="https://aframe.io/releases/1.5.0/aframe.min.js"></script>
<script src="https://unpkg.com/three@0.150.1/examples/js/loaders/BVHLoader.js"></script>
<script src="./js/aframe-vrm.js"></script> <script>
// ==================== コンポーネント定義エリア ====================

// BVH再生用コンポーネント(プロパティ名修正版)
AFRAME.registerComponent('bvh-player', {
schema: {
src: { type: 'string' },
loop: { type: 'boolean', default: true },
timeout: { type: 'number', default: 2000 }
},

init: function () {
console.log('✅ bvh-player: コンポーネント初期化');
this.mixer = null;
this.hasStarted = false; // 処理が二重に実行されるのを防ぐフラグ

// 指定した時間後に処理を強制的に開始するタイマー
setTimeout(() => {
if (this.hasStarted) return;
console.log(`⏰ bvh-player: ${this.data.timeout / 1000}秒経過。処理を試みます。`);

const vrmComponent = this.el.components.vrm;

// =================================================================
// ▼▼▼ ここが最重要修正点です! .model -> .vrm に修正しました ▼▼▼
// =================================================================
if (vrmComponent && vrmComponent.vrm) {
this.startBvh(vrmComponent.vrm);
} else {
console.error('❌ bvh-player: タイムアウト後もVRMデータ(.vrmプロパティ)が見つかりませんでした。');
}
}, this.data.timeout);

// イベントリスナーも同様に修正
this.el.addEventListener('vrm-loaded', (e) => {
if (this.hasStarted) return;
this.startBvh(e.detail.vrm);
}, { once: true });
},

// BVH処理を開始するメインの関数
startBvh: function(vrmData) {
if (this.hasStarted) return;
this.hasStarted = true;
console.log('✅ bvh-player: VRMデータを正常にキャッチ!BVH処理を開始します。', vrmData);

const loader = new THREE.BVHLoader();
loader.load(this.data.src, (bvh) => {
console.log('✅ bvh-player: BVH読み込み成功');
const boneMap = {
'J_Bip_C_Hips': 'hips', 'J_Bip_C_Spine': 'spine', 'J_Bip_C_Chest': 'chest',
'J_Bip_C_UpperChest': 'upperChest', 'J_Bip_C_Neck': 'neck', 'J_Bip_C_Head': 'head',
'J_Bip_L_Shoulder': 'leftShoulder', 'J_Bip_L_UpperArm': 'leftUpperArm',
'J_Bip_L_LowerArm': 'leftLowerArm', 'J_Bip_L_Hand': 'leftHand',
'J_Bip_R_Shoulder': 'rightShoulder', 'J_Bip_R_UpperArm': 'rightUpperArm',
'J_Bip_R_LowerArm': 'rightLowerArm', 'J_Bip_R_Hand': 'rightHand',
'J_Bip_L_UpperLeg': 'leftUpperLeg', 'J_Bip_L_LowerLeg': 'leftLowerLeg',
'J_Bip_L_Foot': 'leftFoot', 'J_Bip_L_ToeBase': 'leftToes',
'J_Bip_R_UpperLeg': 'rightUpperLeg', 'J_Bip_R_LowerLeg': 'rightLowerLeg',
'J_Bip_R_Foot': 'rightFoot', 'J_Bip_R_ToeBase': 'rightToes'
};

const newTracks = [];
bvh.clip.tracks.forEach(track => {
const bvhNodeName = track.name.split('.')[0];
const vrmBoneName = boneMap[bvhNodeName];
if (vrmBoneName) {
const vrmBoneNode = vrmData.humanoid.getBoneNode(vrmBoneName);
if (vrmBoneNode) {
const newTrack = track.clone();
newTrack.name = `${vrmBoneNode.name}.${track.name.split('.')[1]}`;
newTracks.push(newTrack);
}
}
});
console.log(`✅ bvh-player: ${newTracks.length}個のトラックをマッピングしました。`);
if (newTracks.length === 0) {
console.error('❌ エラー: マッピングされたボーンがありません。');
return;
}

const filteredTracks = newTracks.filter(track => !track.name.endsWith('.position'));
const newClip = new THREE.AnimationClip('bvh_animation', bvh.clip.duration, filteredTracks);

this.mixer = new THREE.AnimationMixer(this.el.object3D);
const action = this.mixer.clipAction(newClip);
if(this.data.loop) {
action.setLoop(THREE.LoopRepeat);
}
action.play();
console.log('🎉 bvh-player: アニメーション再生を開始しました!');

}, undefined, (error) => {
console.error('❌ bvh-player: BVHファイルの読み込みに失敗しました。', error);
});
},

tick: function (time, timeDelta) {
if (this.mixer) {
this.mixer.update(timeDelta / 1000);
}
}
});
</script>
</head>
<body>

<a-scene renderer="physicallyCorrectLights: true">

<a-entity
id="avatar"
vrm="src: ./vrm/tesA1_V0a.vrm"
bvh-player="src: https://p-bookmark.sakura.ne.jp/junkerstock/vrm/8.bvh"
position="0 0 0"
rotation="0 180 0">
</a-entity>

<a-entity camera position="0 1.6 2.5" look-controls wasd-controls></a-entity>

<a-sky color="#ECECEC"></a-sky>
<a-plane position="0 0 0" rotation="-90 0 0" width="100" height="100" color="#FFFFFF" shadow></a-plane>
<a-light type="directional" color="#FFF" intensity="0.6" position="-1 2 2"></a-light>
<a-light type="ambient" color="#FFF" intensity="0.6"></a-light>

</a-scene>

</body>
</html>


使用変数

-------( Function )
action
boneMap
bvhNodeName
charset
color
filteredTracks
hasStarted
height
id
intensity
length
loader
mixer
name
newClip
newTrack
newTracks
player
position
renderer
rotation
src
track
type
vrm
vrmBoneName
vrmBoneNode
vrmComponent
vrmData) { if -------( Function )
width
コンポーネント定義エリア