Upgrade to Pro — share decks privately, control downloads, hide ads and more …

three.jsとRapierでレースゲームが3日でできた話

yomotsu
October 21, 2023

 three.jsとRapierでレースゲームが3日でできた話

yomotsu

October 21, 2023
Tweet

More Decks by yomotsu

Other Decks in Programming

Transcript

  1. UISFFKTͱ3BQJFSͰ
    ϨʔεήʔϜ͕
    ೔ͰͰ͖ͨ࿩
    UISFFKTͱ3BQJFSͰ
    ϨʔεήʔϜ͕
    ೔ͰͰ͖ͨ࿩
    0DUTU4IJ[VPLBKT
    খࢁాߊߒ

    View full-size slide

  2. ձࣾͷ߹॓
    ձٞࣨ෇͖ͷԹઘ॓Ͱ
    ޷͖ͳ΋ͷΛ̏೔ؒͰ࡞Δ

    View full-size slide

  3. ̏೔ؒͰϨʔεήʔϜΛ
    ࡞Γ·ͨ͠

    View full-size slide

  4. https://63172814.rapier-tryout.pages.dev/
    %FNP

    View full-size slide

  5. גࣜձࣾϐΫηϧάϦου
    খࢁాߊߒ
    !ZPNPUTV

    View full-size slide

  6. ٕज़બఆ
    ྨࣅϥΠϒϥϦʔ ྨࣅϥΠϒϥϦʔ
    BNNPKTDBOOPOKT
    ෺ཧγϛϡϨʔγϣϯ
    %දࣔ

    View full-size slide

  7. w ୯ʹ࢖͍׳Ε͍ͯΔ
    w ͔ͳΓ׆ൃʹΞοϓσʔτ͞Ε͍ͯΔ
    w ਺ֶػೳ΋ɺͱͯ΋๛෋
    ʢϕΫλʔɺΫΥʔλχΦϯɺߦྻͳͲʣ
    ࢓ࣄͰ਺ֶػೳ͕ඞཁͳ৔߹͸ɺࢀߟʹ͍ͯ͠Δ
    ඳըUISFFKT

    View full-size slide

  8. w BNNPKTͱDBOOPOKT͸ݹ͘
    ౰͔࣌Βػೳ௥Ճ͕ͳ͍ͷͰআ֎
    w )BWPL͸ʢ࢖ͬͯΈ͍͚ͨͲʣ
    #BCZMPOҎ֎͸
    ૬ੑѱͦ͏ͳͷͰݟૹΓ
    w 3BQJFS͸ൺֱత৽͠ΊͳͷͰɺ࠾༻ͯ͠Έͨ
    ෺ཧԋࢉ3BQJFS

    View full-size slide

  9. https://rapier.rs/

    View full-size slide

  10. w 3VTUͰॻ͔Εͨɺ෺ཧԋࢉϥΠϒϥϦʔ
    w 8BTNͷ+4൛΋͋Γɺܕ΋͍ͭͯ͘Δ
    w %෺ཧɺ%෺ཧͷ྆ํʹରԠ
    w جຊతͳ෺ཧԋࢉػೳ͸ଗ͍ͬͯΔ
    w ࣗಈंγϛϡϨʔγϣϯػೳ΋͋Γͦ͏ʜ
    ෺ཧԋࢉ3BQJFS

    View full-size slide

  11. w ϓϦϛςΟϒਤܗʢ௚ํମ΍ٿମͳͲʣ
    Ҏ֎ʹ΋ɺ
    ޷͖ͳܗͷ߶ମΛ࡞ΕΔ
    w UISFFKTͷδΦϝτϦʔ͔Β௚઀
    3BQJFSͷ߶ମΛ࡞ΕΔ
    ෺ཧԋࢉ3BQJFS

    View full-size slide

  12. ೚ҙϝογϡ͔Β߶ମΛ࡞Δ

    View full-size slide

  13. terrainMesh.updateWorldMatrix( true, true );
    terrainMesh.traverse( ( object ) => {
    if ( ! object.isMesh ) return;
    const geometry = object.geometry.clone();
    geometry.applyMatrix4( object.matrix );
    geometry.computeVertexNormals();
    const vertices = new Float32Array( geometry.attributes.position.array );
    const indices = new Uint32Array( geometry.index.array );
    const groundColliderDesc = RAPIER.ColliderDesc.trimesh(
    new Float32Array(vertices),
    new Uint32Array(indices)
    )
    .setActiveEvents( RAPIER.ActiveEvents.COLLISION_EVENTS );
    const groundCollider = world.createCollider( groundColliderDesc );
    } );
    QPTJUJPO΍SPUBUJPO͕
    ద༻͞Ε͍ͯΔ৔߹͸
    ࣄલʹߦྻʹ൓ө͢Δ
    ʢ௨ৗ͸SFOEFS࣌ʹద༻͞ΕΔʣ

    View full-size slide

  14. terrainMesh.updateWorldMatrix( true, true );
    terrainMesh.traverse( ( object ) => {
    if ( ! object.isMesh ) return;
    const geometry = object.geometry.clone();
    geometry.applyMatrix4( object.matrix );
    geometry.computeVertexNormals();
    const vertices = new Float32Array( geometry.attributes.position.array );
    const indices = new Uint32Array( geometry.index.array );
    const groundColliderDesc = RAPIER.ColliderDesc.trimesh(
    new Float32Array(vertices),
    new Uint32Array(indices)
    )
    .setActiveEvents( RAPIER.ActiveEvents.COLLISION_EVENTS );
    const groundCollider = world.createCollider( groundColliderDesc );
    } );
    .FTI಺ͷ
    શδΦϝτϦʔʹରͯ͠ʜ

    View full-size slide

  15. terrainMesh.updateWorldMatrix( true, true );
    terrainMesh.traverse( ( object ) => {
    if ( ! object.isMesh ) return;
    const geometry = object.geometry.clone();
    geometry.applyMatrix4( object.matrix );
    geometry.computeVertexNormals();
    const vertices = new Float32Array( geometry.attributes.position.array );
    const indices = new Uint32Array( geometry.index.array );
    const groundColliderDesc = RAPIER.ColliderDesc.trimesh(
    new Float32Array(vertices),
    new Uint32Array(indices)
    )
    .setActiveEvents( RAPIER.ActiveEvents.COLLISION_EVENTS );
    const groundCollider = world.createCollider( groundColliderDesc );
    } );
    ίϐʔͨ͠δΦϝτϦΛ
    ഁյૢ࡞͍ͯ͘͠

    View full-size slide

  16. terrainMesh.updateWorldMatrix( true, true );
    terrainMesh.traverse( ( object ) => {
    if ( ! object.isMesh ) return;
    const geometry = object.geometry.clone();
    geometry.applyMatrix4( object.matrix );
    geometry.computeVertexNormals();
    const vertices = new Float32Array( geometry.attributes.position.array );
    const indices = new Uint32Array( geometry.index.array );
    const groundColliderDesc = RAPIER.ColliderDesc.trimesh(
    new Float32Array(vertices),
    new Uint32Array(indices)
    )
    .setActiveEvents( RAPIER.ActiveEvents.COLLISION_EVENTS );
    const groundCollider = world.createCollider( groundColliderDesc );
    } );
    ม׵ߦྻΛ௖఺ʹম͖෇͚Δ

    View full-size slide

  17. terrainMesh.updateWorldMatrix( true, true );
    terrainMesh.traverse( ( object ) => {
    if ( ! object.isMesh ) return;
    const geometry = object.geometry.clone();
    geometry.applyMatrix4( object.matrix );
    geometry.computeVertexNormals();
    const vertices = new Float32Array( geometry.attributes.position.array );
    const indices = new Uint32Array( geometry.index.array );
    const groundColliderDesc = RAPIER.ColliderDesc.trimesh(
    new Float32Array(vertices),
    new Uint32Array(indices)
    )
    .setActiveEvents( RAPIER.ActiveEvents.COLLISION_EVENTS );
    const groundCollider = world.createCollider( groundColliderDesc );
    } );
    UISFFKTͷHFPNFUSZ͔Β
    ಺༰ΛऔΓग़ͯ͠

    View full-size slide

  18. terrainMesh.updateWorldMatrix( true, true );
    terrainMesh.traverse( ( object ) => {
    if ( ! object.isMesh ) return;
    const geometry = object.geometry.clone();
    geometry.applyMatrix4( object.matrix );
    geometry.computeVertexNormals();
    const vertices = new Float32Array( geometry.attributes.position.array );
    const indices = new Uint32Array( geometry.index.array );
    const groundColliderDesc = RAPIER.ColliderDesc.trimesh(
    new Float32Array(vertices),
    new Uint32Array(indices)
    )
    .setActiveEvents( RAPIER.ActiveEvents.COLLISION_EVENTS );
    const groundCollider = world.createCollider( groundColliderDesc );
    } );
    ௖఺഑ྻ͔Β
    3BQJFSͷ߶ମΛ࡞ΕΔ

    View full-size slide

  19. +4൛ʹ͸ɺࣗಈं෺ཧ͕ͳ͍ʂ

    View full-size slide

  20. ͍͢͝ਓ͕ɺಠࣗʹ࡞͍ͬͯͨʜ

    View full-size slide

  21. w 5XJUUFSͰҎલ͔Β
    গ͠ݟ͔͚͍ͯͨ
    w φϏήʔγϣϯϝογϡͱ
    ΤʔδΣϯτػೳΛ+4΋
    ެ։ͯ͠Δ
    *TBBD͞Μɺ͍͢͝

    View full-size slide

  22. φϏήʔγϣϯϝογϡ

    View full-size slide

  23. ΤʔδΣϯτ
    IUUQTUZQJDBMTZOPOZNPVTTQJSJUHMJUDINF

    View full-size slide

  24. w UISFFKT
    w %ඳըػೳ
    w ೚ҙϞσϧͷಡΈࠐΈʢHM5'ʣ
    w 3BQJFS
    w ೚ҙϝογϡ͔Β߶ମΛ࡞Δ
    w ࣗಈं෺ཧγϛϡϨʔγϣϯʢ*TBBD͞Μ੡ʣ
    ཁ݅ୡ੒ʹඞཁͳػೳ

    View full-size slide

  25. ίʔυΛॻ͍͍ͯ͘ʜʂ

    View full-size slide

  26. ෺ཧԋࢉͱඳը
    ෺ཧԋࢉ
    ʢ3BQJFSͷXPSMEʣ
    දࣔ
    ʢUISFFKTͷTDFOFʣ

    View full-size slide

  27. w Ұൠతʹɺ෺ཧԋࢉͷੈք͸XPSMEͱݺ͹Ε
    දࣔͷੈք͸TDFOFͱݺ͹ΕΔ
    w ෺ཧԋࢉʢ3BQJFSʣͰ
    ࣌ؒ͝ͱͷγϛϡϨʔγϣϯΛ͢Δ
    w γϛϡϨʔγϣϯ݁ՌʢXPSMEʣΛ
    UISFFKTͷTDFOFʹ൓ө͢Δ
    ෺ཧԋࢉͱඳը

    View full-size slide

  28. // 物理
    const gravity = { x: 0, y: -9.81, z: 0 };
    const world = new RAPIER.World( gravity );
    world.timestep = 0.016;
    // 表示
    const width = window.innerWidth;
    const height = window.innerHeight;
    const clock = new THREE.Clock();
    const scene = new THREE.Scene();
    const camera =
    new THREE.PerspectiveCamera( 60, width / height, 0.01, 100 );
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize( width, height );
    document.body.appendChild( renderer.domElement );
    https://github.com/yomotsu/rapier-tryout
    ෺ཧγϛϡϨʔγϣϯೖΕ෺XPSME
    දࣔͷೖΕ෺TDFOF

    View full-size slide

  29. const radius = 1;
    // 表示
    const ballMesh = new Mesh(
    new SphereGeometry( radius, 16, 16 ),
    new MeshNormalMaterial( { wireframe: true } )
    );
    scene.add( ballMesh );
    // 物理
    const rigidBodyDesc = RAPIER.RigidBodyDesc.dynamic()
    .setLinearDamping( 0.1 )
    .setTranslation( 0, 3, 0 );
    const rigidBody = world.createRigidBody( rigidBodyDesc );
    const colliderDesc = RAPIER.ColliderDesc.ball( radius )
    .setFriction( 0.1 )
    .setFrictionCombineRule( RAPIER.CoefficientCombineRule.Max )
    .setRestitution( 0.6 )
    .setRestitutionCombineRule( RAPIER.CoefficientCombineRule.Max );
    world.createCollider( colliderDesc, rigidBody );
    https://github.com/yomotsu/rapier-tryout
    ൒ܘ̍ͷٿମͷϝογϡ
    ൒ܘ̍ͷٿମͷ߶ମ
    TDFOF಺ʹ഑ஔ
    XPSME಺ʹ഑ஔ

    View full-size slide

  30. ( function gameLoop() {
    world.step();
    ballMesh.position.copy( rigidBody.translation() );
    ballMesh.quaternion.copy( rigidBody.rotation() );
    setTimeout( gameLoop, world.timestep );
    } )();
    https://github.com/yomotsu/rapier-tryout
    UJNFTUFQ͸ඵͰݻఆࡁΈ
    ෺ཧ͸S"'Λ࢖Θͣɺݻఆඵϧʔϓͤ͞Δ
    ෺ཧγϛϡϨʔγϣϯ݁ՌΛ
    දࣔҐஔʹίϐʔ

    View full-size slide

  31. ( function renderLoop () {
    requestAnimationFrame( renderLoop );
    renderer.render( scene, camera );
    } )();
    https://github.com/yomotsu/rapier-tryout
    දࣔ͸S"'Ͱɺ
    σόΠεෛՙʹԠͨ͡ϧʔϓΛͤ͞Δ

    View full-size slide

  32. https://booth.pm/ja/items/1098032

    View full-size slide

  33. https://on-jin.com/sound/listshow.php?pagename=nori&title=ंɾΫϥΫγϣϯ02

    View full-size slide

  34. Web audio
    https://github.com/yomotsu/rapier-tryout/blob/main/6_hone/index.html

    View full-size slide

  35. const curvePath: THREE.CurvePath = new THREE.CurvePath();
    curvePath.add( new THREE.CatmullRomCurve3( [
    new THREE.Vector3( 0, 0, 0 ),
    new THREE.Vector3( 0, 0, 0.1 ),
    new THREE.Vector3( 0, 0, 10 ),
    new THREE.Vector3( 0, 18, 50 ),
    new THREE.Vector3( 20, 18, 60 ),
    new THREE.Vector3( 20, 18, 0 ),
    new THREE.Vector3( 0, 18, 0 ),
    new THREE.Vector3( - 10, 18, 0 ),
    new THREE.Vector3( - 50, 0, - 50 ),
    new THREE.Vector3( 0, 0, - 50 ),
    new THREE.Vector3( 0, 0, - 10 ),
    ] ) );

    View full-size slide

  36. three.jsʹ͸ɺ
    ఺͔ΒεϓϥΠϯΛ࡞Δػೳ͕͋Δ

    View full-size slide

  37. ͋Δ఺ʹ͓͍ͯɺ
    Ұॠલͷ఺
    Ұॠޙͷ఺
    ͷࠩ෼ϕΫτϧ͔Βɺ
    ਐߦํ޲ʢλϯδΣϯτʣ͕Θ͔Δ

    View full-size slide

  38. ਐߦํ޲ʢλϯδΣϯτʣ͕Θ͔Ε͹ɺ
    ্ํ޲ʢϊʔϚϧʣ΋ܭࢉͰ͖Δ

    View full-size slide

  39. ԣํ޲ʢόΠϊʔϚϧʣ΋ܭࢉͰ͖Δ

    View full-size slide

  40. ϑϨωɾηϨ

    View full-size slide

  41. ࿈ଓ͢Δ఺ͷ
    λϯδΣϯτɾϊʔϚϧɾόΠϊʔϥϧ
    ͔ΒɺϦϘϯঢ়ͷδΦϝτϦʔ͕࡞ΕΔ

    View full-size slide

  42. w /PEFKTίϚϯυϥΠϯπʔϧ
    w HMCϑΝΠϧΛѹॖ͢Δ
    w %SBDPΦϓγϣϯ
    w (MC಺ͷେαΠζະѹॖ1/(ςΫενϟΛ
    αΠζͷXFCQʹม׵
    w ͳͲͳͲ
    HMUGUSBOTGPSN

    View full-size slide

  43. https://gltf-transform.dev/

    View full-size slide

  44. w ϥΠϒϥϦʔɺϒϥ΢βͷਐԽͷ݁Ռ͸
    ໨֮·͍͠
    w ϞόΠϧ୺຤ɾճઢঢ়گ΋ਐԽͨ͠
    w +BWB4DSJQUͬͯԿͰ΋Ͱ͖ͦ͏ʜʂ
    ·ͱΊ

    View full-size slide

  45. ͓ΘΓ
    খࢁాߊߒ
    !ZPNPUTV

    View full-size slide