대포 물리학 - 3D web3 시리즈

독자 여러분,

3D-web3 시리즈의 3번째 글입니다.

1 -
2 -
3 - 3D 웹 - 대포 물리학
4 -

"Cannon" 간단한 충돌 감지, 다양한 바디 모양, 접촉, 마찰 및 제약 조건을 포함하는 강체 물리 엔진입니다.
npm i @react-three/cannon
작동시키는 간단한 단계:
1_ 물리 세계 가져오기 및 만들기

import { Physics, useBox, ... } from '@react-three/cannon'

<Physics>{/* Physics related objects in here please */}</Physics>


2_ 물체 접촉면에 맞는 모양을 선택하세요. 상자, 평면, 구 등이 될 수 있습니다. 질량도 지정하세요.

const [ref, api] = useBox(() => ({ mass: 1 }))


3_ 객체를 선택하세요. 메쉬, 라인, gltf 등 무엇이든 될 수 있으며 방금 받은 참조에 연결합니다. 이제 물리 세계 내부의 중력 및 기타 물체의 영향을 받습니다.

<mesh ref={ref} geometry={...} material={...} />


4_ 위치, 회전, 속도, 힘 및 충격을 적용할 수 있는 API를 사용하여 상호 작용할 수 있습니다.

useFrame(({ clock }) => api.position.set(Math.sin(clock.getElapsedTime()) * 5, 0, 0))


5_ 바디 API를 사용하여 속성을 구독하여 각 프레임에서 업데이트를 받을 수 있습니다.

const velocity = useRef([0, 0, 0])
useEffect(() => {
  const unsubscribe = api.velocity.subscribe((v) => (velocity.current = v))
  return unsubscribe
}, [])


"Box.jsx"구성 요소의 모든 단계는 다음과 같습니다.

import { Physics, useBox } from '@react-three/cannon'
import { useFrame } from '@react-three/fiber';

const Box = () => {

    const [ref, api] = useBox(() => ({ mass: 1 }))

    useFrame(({ clock }) => api.position.set(Math.sin(clock.getElapsedTime()) * 5, 0, 0))

    const velocity = useRef([0, 0, 0])
    useEffect(() => {
        const unsubscribe = api.velocity.subscribe((v) => (velocity.current = v))
        return unsubscribe
    }, [])

    return (
        <Physics>
            <mesh ref={ref}>
                <boxGeometry attach='geometry' args={[1, 1, 1]} />
                <meshStandardMaterial attach="material" color={'#000'} />
            </mesh>

        </Physics>
    )
}

export default Box


이 패키지를 저장소에 적용해 봅시다.

앱 로직__

First check previous post if you haven't, to understand why we are not using in code-sand-box our gltf model
Instead, we are using a "yellow box" with an onClick method to change between two "camera modes"



필요한 다른 3개 구성 요소의 부모가 될 "ActivateSpawner"구성 요소를 포함합니다.

카메라 RIG 모드에서 활성화하는 onClick 메서드가 있는 "블랙 박스"를 볼 수 있습니다.

a) "생성기"구성 요소: "y"속도로 "x"개의 거품을 생성합니다. "Spawner"에는 "Bubble"구성 요소가 자식으로 있습니다.

b) "PlayerBox"구성 요소: 당신의 움직임을 모방하고 거품이 생기는 것을 피해야 합니다.

두 구성 요소 모두 콜라이더 속성이 있습니다. 따라서 "PlayerBox"가 "Bubble"구성 요소와 충돌하면 게임이 중지됩니다.

우리는 다음을 사용할 것입니다(이전 튜토리얼 "개체/후크"는 포함되지 않음):
  • "Fiber"에서: useThree, useFrame
  • "Cannon"에서: useBox, useSphere
  • "Three"에서: Vector3

  • Step_1 "ActivateSpawner"컴포넌트 생성

    상자에 0의 "질량"을 제공하고 있습니다.

    import React from 'react'
    import { useBox } from '@react-three/cannon';
    import { useState } from 'react';
    
    import Spawner from './Spawner';
    import PlayerBox from './PlayerBox';
    
    const ActivateSpawner = () => {
    
        const [play, setPlay] = useState(false);
    
        // This box is used to start the game
        const [ref] = useBox(() => ({
            mass: 0,
            position: [-5, 2, -10],
            type: 'Dynamic',
            args: [1, 1, 1],
        }));
    
        return (
            <group>
                <mesh
                    ref={ref}
                    onClick={() => {
                        console.log(!play)
                        setPlay(!play)
                    }}
                >
                    <boxGeometry attach='geometry' args={[1, 1, 1]} />
                    <meshStandardMaterial attach="material" color={'#000'} />
                </mesh>
                {play && (<>
                    <Spawner />
                    <PlayerBox setPlay={setPlay} />
                </>
                )}
            </group>
        )
    }
    
    export default ActivateSpawner
    


    Step_2 "Spawner"컴포넌트 생성

    for 루프와 "randomIntBetween(a,b)"& randomIntBetweenAlsoNegatives(a,b) 함수를 사용하여 각 "버블"에 대한 임의 데이터(위치, 지연, 색상) 가져오기

    import { Vector3 } from 'three';
    import Bubble from './Bubble';
    
    
    const Spawner = () => {
    
    
        function randomIntBetween(min, max) { // min and max included 
            return Math.floor(Math.random() * (max - min + 1) + min)
        }
    
        function randomIntBetweenAlsoNegatives(min, max) { // min and max included 
            const math = Math.floor(Math.random() * (max - min + 1) + min)
            const random = Math.random()
            const zeroOrOne = Math.round(random)
            if (zeroOrOne) return -(math)
            return math
        }
    
        const attackersArray = [];
    
        for (let i = 0; i < 20; i++) {
    
            let position = new Vector3(
                randomIntBetweenAlsoNegatives(0, 2),
                randomIntBetweenAlsoNegatives(0, 2),
                0)
    
            let wait = randomIntBetween(1, 12) * 10
    
            let color = `#${Math.random().toString(16).substring(2, 8)}`
    
            const att = [position, wait, color]
            attackersArray.push(att)
        }
    
        return (
            <group>
                {attackersArray.map((attackers, key) => {
                    return <Bubble
                        key={key}
                        pos={attackers[0]}
                        wait={attackers[1]}
                        color={attackers[2]}
                    />
                })}
            </group>
        );
    };
    
    export default Spawner;
    
    


    Step_3 "PlayerBox"컴포넌트 생성

    캔버스 "카메라"개체에 대한 참조를 생성하려면 '@react-three/fiber'의 "useThree"후크를 사용하세요. 이제 "useFrame"후크를 사용하여 "PlayerBox"에 동일한 값을 제공할 수 있습니다.

    This hook calls you back every frame, which is good for running effects, updating controls, etc.



    "Box"에 "collisionFilterGroup"및 "collisionFilterMask"속성을 추가합니다.
    첫 번째는 그것이 속한 그룹을 정의하고 두 번째는 충돌할 수 있는 그룹을 정의합니다.

    import { useBox, } from '@react-three/cannon';
    import { useFrame } from '@react-three/fiber';
    import { useThree } from '@react-three/fiber'
    
    const PlayerBox = (props) => {
    
        const { camera } = useThree()
    
        const [ref, api] = useBox(() => ({
            mass: 0,
            type: 'Dynamic',
            position: [0, 0, -5],
            args: [0.3, 0.3, 0.1], // collision box size
            collisionFilterGroup: 1,
            // 1 PlayerBox 2 Objetive 3 BulletBox 4 Attackers
            collisionFilterMask: 4,
            onCollide: (e) => {
                props.setPlay(false);
                console.log('game over')
            },
        }));
    
        // Tambien simula el movimiento de la camara (y por lo tnato el del objetivo), para poder tener un collider para el game over
        useFrame(() => {
            api.position.set(camera.position.x, camera.position.y, -2);
        });
    
        return (
            <>
                <mesh ref={ref}>
                    <boxBufferGeometry attach='geometry' args={[0.1, 0.1, 0.1]} /> {/* box size */}
                    <meshStandardMaterial attach="material" color={'#000'} />
    
                </mesh>
            </>
        );
    };
    
    export default PlayerBox;
    
    


    Step_4 "버블"컴포넌트 생성

    동일한 "bubble"개체를 사용하여 동일한 경주를 "x"번 실행하려면 "setTimeout"함수를 추가하여 for 루프 내부의 거품 위치를 재설정합니다.

    import { useSphere } from '@react-three/cannon';
    import { useFrame } from '@react-three/fiber';
    
    const Bubble = (props) => {
    
        let zMovement = -20;
    
        const [ref, api] = useSphere(() => ({
            mass: 0,
            position: [props.pos.x, props.pos.y, props.pos.z - 200],
            type: 'Dynamic',
            // args: [1, 1, 1],
            // 1 PlayerBox 2 Objetive 3 BulletBox 4 Bubble
            collisionFilterGroup: 4,
            // No te va a colisionar, sino que vas a colisionar contra el
            collisionFilterMask: 1,
        }));
    
        useFrame(() => {
            api.position.set(
                props.pos.x,
                props.pos.y,
                (zMovement += 0.1) - props.wait
            );
        });
    
        for (let i = 1; i < 3; i++) {
            window.setTimeout(() => {
                zMovement = -50;
                api.position.set(0, 0, -zMovement);
                // 6 segs * i * wait= posicion de cada cubo para hacer que algunos salgan antes que otros
            }, 6 * 1000 + props.wait * 100);
        }
    
        return (
            <mesh ref={ref}>
                <sphereGeometry attach='geometry' args={[1, 32, 32]} />
                <meshStandardMaterial attach="material" color={props.color} />
            </mesh>
        );
    };
    
    export default Bubble;
    
    


    Step_5 "@react-three/cannon"에서 가져온 "physics"노드를 사용하여 App.jsx에 "ActivateSpawner"를 추가합니다.

    우리가 정의한 모든 구성 요소는 다음과 같은 경우 DOM에서 렌더링됩니다.
    cameraMode가 false => 카메라 RIG 모드가 설정됨

    import { Canvas } from '@react-three/fiber';
    import ActivateSpawner from './geometry/ActivateSpawner';
    ...
    return (
    ...
    {!cameraMode &&
                            < Physics >
                                <ActivateSpawner />
                            </Physics>
                        }
    ...
    )
    


    구성 요소 재개: ActivateSpawner , Spawner, PlayerBox, Bubble



    Web3는 다음 게시물에 추가됩니다.

    도움이 되었기를 바랍니다.

    좋은 웹페이지 즐겨찾기