paint-brush
React와 Three.js 스택을 사용하여 나만의 3D 슈터 만들기 - 1부by@varlab
781
781

React와 Three.js 스택을 사용하여 나만의 3D 슈터 만들기 - 1부

Ivan Zhukov17m2023/10/21
Read on Terminal Reader

웹 기술과 대화형 애플리케이션이 활발히 개발되는 시대에 3D 그래픽은 점점 더 관련성이 높아지고 수요가 많아지고 있습니다. 하지만 웹 개발의 장점을 잃지 않고 3D 애플리케이션을 만드는 방법은 무엇일까요? 이 기사에서는 Three.js의 강력한 기능과 React의 유연성을 결합하여 브라우저에서 바로 자신만의 게임을 만드는 방법을 살펴보겠습니다. 이 기사에서는 React Three Fiber 라이브러리를 소개하고 대화형 3D 게임을 만드는 방법을 알려줍니다.
featured image - React와 Three.js 스택을 사용하여 나만의 3D 슈터 만들기 - 1부
Ivan Zhukov HackerNoon profile picture
0-item
1-item

현대 웹 개발에서는 클래식 애플리케이션과 웹 애플리케이션 사이의 경계가 매일 모호해지고 있습니다. 오늘날 우리는 대화형 웹사이트뿐만 아니라 브라우저에서 바로 본격적인 게임도 만들 수 있습니다. 이를 가능하게 하는 도구 중 하나는 React 기술을 사용하여 Three.js 기반의 3D 그래픽을 생성하기 위한 강력한 도구인 React Three Fiber 라이브러리입니다.

React Three Fiber 스택 정보

React Three Fiber는 웹에서 3D 그래픽을 생성하기 위해 React 의 구조와 원리를 사용하는 Three.js 에 대한 래퍼입니다. 이 스택을 통해 개발자는 Three.js 의 강력한 기능과 React 의 편리함 및 유연성을 결합하여 애플리케이션을 보다 직관적이고 체계적으로 만드는 프로세스를 만들 수 있습니다.


React Three Fiber 의 핵심은 장면에서 생성하는 모든 것이 React 구성 요소라는 아이디어입니다. 이를 통해 개발자는 익숙한 패턴과 방법론을 적용할 수 있습니다.

React Three Fiber 의 주요 장점 중 하나는 React 생태계와의 통합이 쉽다는 것입니다. 이 라이브러리를 사용하면 다른 React 도구도 쉽게 통합할 수 있습니다.

Web-GameDev의 관련성

Web-GameDev는 최근 몇 년 동안 간단한 2D 플래시 게임에서 데스크탑 애플리케이션에 필적하는 복잡한 3D 프로젝트로 발전하는 등 큰 변화를 겪었습니다. 이러한 인기와 기능의 성장으로 인해 Web-GameDev는 무시할 수 없는 영역이 되었습니다.


웹게임의 가장 큰 장점 중 하나는 접근성입니다. 플레이어는 추가 소프트웨어를 다운로드하거나 설치할 필요가 없습니다. 브라우저에서 링크를 클릭하기만 하면 됩니다. 이는 게임의 배포 및 홍보를 단순화하여 전 세계의 광범위한 청중이 게임을 사용할 수 있도록 합니다.


마지막으로, 웹 게임 개발은 개발자가 익숙한 기술을 사용하여 게임 개발을 시도해 볼 수 있는 좋은 방법이 될 수 있습니다. 사용 가능한 도구와 라이브러리 덕분에 3D 그래픽 경험이 없어도 흥미롭고 고품질의 프로젝트를 만들 수 있습니다!

최신 브라우저의 게임 성능

최신 브라우저는 상당히 단순한 웹 검색 도구에서 복잡한 애플리케이션과 게임을 실행하기 위한 강력한 플랫폼으로 발전하면서 많은 발전을 이루었습니다. Chrome , Firefox , Edge 등과 같은 주요 브라우저는 고성능을 보장하기 위해 지속적으로 최적화 및 개발되고 있으므로 복잡한 애플리케이션 개발에 이상적인 플랫폼이 됩니다.


브라우저 기반 게임 개발을 촉진한 주요 도구 중 하나는 WebGL 입니다. 이 표준을 통해 개발자는 하드웨어 그래픽 가속을 사용할 수 있어 3D 게임의 성능이 크게 향상되었습니다. 다른 webAPI와 함께 WebGL은 브라우저에서 직접 인상적인 웹 애플리케이션을 만들 수 있는 새로운 가능성을 열어줍니다.


그럼에도 불구하고 브라우저용 게임을 개발할 때는 다양한 성능 측면을 고려하는 것이 중요합니다. 리소스 최적화, 메모리 관리 및 다양한 장치에 대한 적응은 모두 프로젝트 성공에 영향을 미칠 수 있는 핵심 요소입니다.

당신의 표시에!

그러나 말과 이론은 하나이지만 실제 경험은 전혀 다릅니다. 웹 게임 개발의 모든 잠재력을 실제로 이해하고 평가하기 위한 가장 좋은 방법은 개발 프로세스에 직접 참여하는 것입니다. 따라서 성공적인 웹 게임 개발의 사례로 우리만의 게임을 만들어 보겠습니다. 이 프로세스를 통해 우리는 개발의 주요 측면을 배우고, 실제 문제에 직면하고, 이에 대한 해결책을 찾고, 웹 게임 개발 플랫폼이 얼마나 강력하고 유연할 수 있는지 확인할 수 있습니다.


일련의 기사에서 우리는 이 라이브러리의 기능을 사용하여 1인칭 슈팅 게임을 만드는 방법을 살펴보고 웹 게임 개발의 흥미로운 세계로 뛰어들 것입니다!


최종 데모


GitHub 의 저장소


이제 시작해보자!

프로젝트 설정 및 패키지 설치

우선 React 프로젝트 템플릿이 필요합니다. 그럼 설치부터 시작해 보겠습니다.


 npm create vite@latest


  • React 라이브러리를 선택하십시오.
  • 자바스크립트를 선택하세요.


추가 npm 패키지를 설치합니다.


 npm install three @react-three/fiber @react-three/drei @react three/rapier zustand @tweenjs/tween.js


그런 다음 프로젝트에서 불필요한 모든 것을 삭제하십시오 .


섹션 코드

캔버스 표시 사용자 정의

main.jsx 파일에서 페이지에 범위로 표시될 div 요소를 추가합니다. Canvas 구성 요소를 삽입하고 카메라의 시야를 설정합니다. Canvas 구성 요소 내부에 App 구성 요소를 배치합니다.


main.jsx


index.css 에 스타일을 추가하여 UI 요소를 화면의 전체 높이까지 늘리고 범위를 화면 중앙에 원으로 표시해 보겠습니다.


index.css


App 구성 요소에는 하늘 형태로 게임 장면의 배경으로 표시되는 Sky 구성 요소를 추가합니다.


App.jsx


장면에 하늘 표시


섹션 코드

바닥면

Ground 컴포넌트를 생성하여 App 컴포넌트에 배치해 보겠습니다.


App.jsx


Ground 에서 평평한 표면 요소를 생성합니다. Y축에서 아래쪽으로 이동하여 이 평면이 카메라의 시야에 들어오도록 합니다. 또한 X축의 평면을 뒤집어 수평으로 만듭니다.


Ground.jsx


재료 색상을 회색으로 지정했지만 평면은 완전히 검은색으로 나타납니다.


현장에서 평평하게


섹션 코드

기본 조명

기본적으로 장면에는 조명이 없으므로 모든 방향에서 개체를 비추고 지향성 광선이 없는 광원 주변 조명을 추가해 보겠습니다. 매개변수로 글로우의 강도를 설정합니다.


App.jsx


조명된 평면


섹션 코드

바닥 표면의 질감

바닥면이 균일해 보이지 않게 질감을 추가해 보겠습니다. 표면 전체를 따라 반복되는 셀 형태로 바닥 표면의 패턴을 만듭니다.

자산 폴더에 텍스처가 포함된 PNG 이미지를 추가합니다.


질감이 추가되었습니다.


장면에 텍스처를 로드하려면 @react-3/drei 패키지의 useTexture 후크를 사용하겠습니다. 그리고 후크에 대한 매개변수로 파일로 가져온 텍스처 이미지를 전달합니다. 수평축에서 이미지의 반복을 설정합니다.


Ground.jsx


비행기의 질감


섹션 코드

카메라 움직임

@react-3/drei 패키지의 PointerLockControls 컴포넌트를 사용하여 마우스를 움직일 때 커서가 움직이지 않고 장면에서 카메라의 위치가 변경되도록 화면의 커서를 고정합니다.


App.jsx


카메라 모션 시연


Ground 구성요소를 약간 수정해 보겠습니다.


Ground.jsx


섹션 코드

물리학 추가하기

명확성을 위해 장면에 간단한 큐브를 추가해 보겠습니다.


 <mesh position={[0, 3, -5]}> <boxGeometry /> </mesh> 


현장의 큐브


지금 그는 단지 우주에 매달려 있을 뿐입니다.


@react-3/rapier 패키지의 물리 구성 요소를 사용하여 장면에 "물리"를 추가하세요. 매개변수로 중력장을 구성하여 축을 따라 중력을 설정합니다.


 <Physics gravity={[0, -20, 0]}> <Ground /> <mesh position={[0, 3, -5]}> <boxGeometry /> </mesh> </Physics>


그러나 우리 큐브는 물리 구성 요소 안에 있지만 아무 일도 일어나지 않습니다. 큐브가 실제 물리적 개체처럼 동작하도록 하려면 @react-3/rapier 패키지의 RigidBody 구성 요소에 큐브를 래핑해야 합니다.


App.jsx


그 후에는 페이지가 다시 로드될 때마다 큐브가 중력의 영향으로 떨어지는 것을 즉시 확인할 수 있습니다.


큐브 가을


그러나 이제 또 다른 작업이 있습니다. 바닥을 큐브가 상호 작용할 수 있고 넘어지지 않는 개체로 만드는 것이 필요합니다.


섹션 코드

물리적 대상으로서의 바닥

Ground 구성 요소로 돌아가서 바닥 표면 위에 래퍼로 RigidBody 구성 요소를 추가해 보겠습니다.


Ground.jsx


이제 떨어질 때 큐브는 실제 물리적 물체처럼 바닥에 유지됩니다.


비행기에서 떨어지는 큐브


섹션 코드

캐릭터에 물리 법칙 적용

장면의 캐릭터를 제어할 Player 구성 요소를 만들어 보겠습니다.


캐릭터는 추가된 큐브와 동일한 물리적 개체이므로 장면의 큐브뿐만 아니라 바닥 표면과도 상호 작용해야 합니다. 이것이 바로 RigidBody 구성 요소를 추가하는 이유입니다. 그리고 캡슐 형태로 캐릭터를 만들어 보겠습니다.


Player.jsx


Player 구성 요소를 Physics 구성 요소 안에 배치합니다.


App.jsx


이제 우리 캐릭터가 현장에 등장했습니다.


캡슐 형태의 캐릭터


섹션 코드

캐릭터 이동 - 후크 만들기

캐릭터는 WASD 키를 사용하여 제어되고 스페이스바를 사용하여 점프합니다.

자체적인 반응 후크를 사용하여 캐릭터 이동 논리를 구현합니다.


Hooks.js 파일을 생성하고 거기에 새로운 usePersonControls 함수를 추가해 보겠습니다.


{"keycode": "action to be done"} 형식으로 개체를 정의해 보겠습니다. 다음으로, 키보드 키를 누르거나 떼는 이벤트 핸들러를 추가합니다. 핸들러가 트리거되면 현재 수행 중인 작업을 확인하고 활성 상태를 업데이트합니다. 최종 결과로 후크는 {"action in Progress": "status"} 형식의 개체를 반환합니다.


Hooks.js


섹션 코드

캐릭터 이동 - 후크 구현

usePersonControls 후크를 구현한 후 캐릭터를 제어할 때 사용해야 합니다. Player 구성 요소에서는 모션 상태 추적을 추가하고 캐릭터 이동 방향의 벡터를 업데이트합니다.


또한 이동 방향의 상태를 저장할 변수를 정의하겠습니다.


Player.jsx


캐릭터의 위치를 업데이트하려면 @react-3/섬유 패키지에서 제공하는 Frame을 사용해 보겠습니다. 이 후크는 requestAnimationFrame 과 유사하게 작동하며 초당 약 60회 함수 본문을 실행합니다.


Player.jsx


코드 설명:

1. const playerRef = useRef(); 플레이어 개체에 대한 링크를 만듭니다. 이 링크를 사용하면 장면의 플레이어 개체와 직접 상호 작용할 수 있습니다.

2. const { 앞으로, 뒤로, 왼쪽, 오른쪽, 점프 } = usePersonControls(); 후크를 사용하면 플레이어가 현재 어떤 컨트롤 버튼을 누르고 있는지 나타내는 부울 값이 포함된 개체가 반환됩니다.

3. useFrame((state) => { ... }); 후크는 애니메이션의 각 프레임에서 호출됩니다. 이 후크 내에서 플레이어의 위치와 선형 속도가 업데이트됩니다.

4. if (!playerRef.current) return; 플레이어 개체가 있는지 확인합니다. 플레이어 개체가 없으면 오류를 방지하기 위해 함수 실행이 중지됩니다.

5. const 속도 = playerRef.current.linvel(); 플레이어의 현재 선형 속도를 가져옵니다.

6. frontVector.set(0, 0, 뒤로 - 앞으로); 누른 버튼을 기준으로 전진/후진 모션 벡터를 설정합니다.

7. sideVector.set(왼쪽 - 오른쪽, 0, 0); 왼쪽/오른쪽 이동 벡터를 설정합니다.

8. 방향.subVectors(frontVector, sideVector).normalize().multiplyScalar(MOVE_SPEED); 이동 벡터를 빼고 결과를 정규화하고(벡터 길이가 1이 되도록) 이동 속도 상수를 곱하여 플레이어 이동의 최종 벡터를 계산합니다.

9. playerRef.current.wakeUp(); 플레이어 개체를 "깨워서" 변경 사항에 반응하는지 확인합니다. 이 방법을 사용하지 않으면 일정 시간이 지나면 개체가 "잠자기" 상태가 되어 위치 변경에 반응하지 않게 됩니다.

10. playerRef.current.setLinvel({ x: 방향.x, y: 속도.y, z: 방향.z }); 계산된 이동 방향을 기반으로 플레이어의 새로운 선형 속도를 설정하고 현재 수직 속도를 유지합니다(점프나 추락에 영향을 주지 않도록).


그 결과 WASD 키를 누르면 캐릭터가 장면 주위를 움직이기 시작했습니다. 둘 다 물리적 개체이기 때문에 그는 큐브와 상호 작용할 수도 있습니다.


캐릭터 움직임


섹션 코드

캐릭터 이동 - 점프

점프를 구현하기 위해 @dimforge/rapier3d-compat@react- three/rapier 패키지의 기능을 사용해 보겠습니다. 이번 예시에서는 캐릭터가 지면에 누워 있고 점프키가 눌려져 있는지 확인해 보겠습니다. 이 경우에는 캐릭터의 방향과 가속력을 Y축으로 설정합니다.


플레이어 의 경우 모든 축에 질량과 블록 회전을 추가하여 장면의 다른 개체와 충돌할 때 다른 방향으로 넘어지지 않도록 합니다.


Player.jsx


코드 설명:

  1. const 세계 = rapier.world; Rapier 물리 엔진 장면에 액세스합니다. 여기에는 모든 물리적 개체가 포함되어 있으며 상호 작용을 관리합니다.
  1. const ray = world.castRay(new RAPIER.Ray(playerRef.current.translation(), { x: 0, y: -1, z: 0 })); 여기에서 "레이캐스팅"(레이캐스팅)이 발생합니다. 플레이어의 현재 위치에서 시작하여 y축 아래를 가리키는 광선이 생성됩니다. 이 광선은 장면에 "캐스트"되어 장면의 객체와 교차하는지 확인합니다.
  1. const 접지 = ray && ray.collider && Math.abs(ray.toi) <= 1.5; 플레이어가 지상에 있으면 조건이 확인됩니다.
  • ray - 광선이 생성되었는지 여부.
  • ray.collider - 광선이 장면의 객체와 충돌했는지 여부.
  • Math.abs(ray.toi) - 광선의 "노출 시간"입니다. 이 값이 주어진 값보다 작거나 같으면 플레이어가 "지상"으로 간주될 만큼 표면에 충분히 가깝다는 것을 나타낼 수 있습니다.


또한 장면의 다른 객체와 상호 작용할 물리적 객체를 추가하여 "착륙" 상태를 결정하기 위한 광선 추적 알고리즘이 올바르게 작동하도록 지면 구성 요소를 수정해야 합니다.


Ground.jsx


장면을 더 잘 볼 수 있도록 카메라를 조금 더 높이겠습니다.


main.jsx


캐릭터 점프


섹션 코드

캐릭터 뒤로 카메라 이동

카메라를 이동하기 위해 플레이어의 현재 위치를 가져와 프레임이 새로 고쳐질 때마다 카메라 위치를 변경합니다. 그리고 캐릭터가 카메라가 향하는 궤적을 따라 정확히 이동하려면 applyEuler 를 추가해야 합니다.


Player.jsx


코드 설명:

applyEuler 메소드는 지정된 오일러 각도를 기반으로 벡터에 회전을 적용합니다. 이 경우 카메라 회전은 방향 벡터에 적용됩니다. 이는 카메라 방향을 기준으로 모션을 일치시키는 데 사용되므로 플레이어는 카메라가 회전하는 방향으로 움직입니다.


Player 의 크기를 약간 조정하고 큐브에 비해 더 크게 만들어 CapsuleCollider 의 크기를 늘리고 "점프" 논리를 수정하겠습니다.


Player.jsx


카메라 이동


섹션 코드

큐브 생성

장면이 완전히 비어있는 느낌이 들지 않도록 큐브 생성을 추가해 보겠습니다. json 파일에서 각 큐브의 좌표를 나열한 다음 장면에 표시합니다. 이렇게 하려면 좌표 배열을 나열하는 Cubes.json 파일을 만듭니다.


 [ [0, 0, -7], [2, 0, -7], [4, 0, -7], [6, 0, -7], [8, 0, -7], [10, 0, -7] ]


Cube.jsx 파일에서 루프에서 큐브를 생성하는 Cubes 구성 요소를 만듭니다. 그리고 Cube 구성 요소는 직접 생성된 개체가 됩니다.


 import {RigidBody} from "@react-three/rapier"; import cubes from "./cubes.json"; export const Cubes = () => { return cubes.map((coords, index) => <Cube key={index} position={coords} />); } const Cube = (props) => { return ( <RigidBody {...props}> <mesh castShadow receiveShadow> <meshStandardMaterial color="white" /> <boxGeometry /> </mesh> </RigidBody> ); }


기존 단일 큐브를 삭제하여 생성된 Cubes 컴포넌트를 App 컴포넌트에 추가해 보겠습니다.


App.jsx


큐브 생성


섹션 코드

모델을 프로젝트로 가져오기

이제 장면에 3D 모델을 추가해 보겠습니다. 캐릭터의 무기 모델을 추가해 보겠습니다. 3D 모델을 찾는 것부터 시작해 보겠습니다. 예를 들어, 이것을 살펴보겠습니다.


GLTF 형식의 모델을 다운로드하고 프로젝트 루트에 아카이브의 압축을 풉니다.

모델을 장면으로 가져오는 데 필요한 형식을 얻으려면 gltf-pipeline 추가 기능 패키지를 설치해야 합니다.


npm i -D gltf-pipeline


gltf-pipeline 패키지를 사용하여 모델을 GLTF 형식 에서 GLB 형식 으로 다시 변환합니다. 이 형식에서는 모든 모델 데이터가 하나의 파일에 저장되기 때문입니다. 생성된 파일의 출력 디렉터리로 공용 폴더를 지정합니다.


gltf-pipeline -i weapon/scene.gltf -o public/weapon.glb


그런 다음 이 모델의 마크업을 포함하는 반응 구성 요소를 생성하여 장면에 추가해야 합니다. @react-3/섬유 개발자의 공식 리소스를 사용해 보겠습니다.


변환기로 이동하려면 변환된 Weapon.glb 파일을 로드해야 합니다.


드래그 앤 드롭 또는 탐색기 검색을 사용하여 이 파일을 찾아서 다운로드하세요.


변환된 모델


변환기에서 우리는 생성된 반응 구성 요소를 볼 수 있습니다. 이 코드는 새 파일 WeaponModel.jsx 에서 프로젝트로 전송되어 구성 요소의 이름을 파일과 동일한 이름으로 변경합니다.


섹션 코드

현장에 무기 모델 표시

이제 생성된 모델을 장면으로 가져오겠습니다. App.jsx 파일에 WeaponModel 구성 요소를 추가합니다.


App.jsx


가져온 모델 시연


섹션 코드

그림자 추가

장면의 이 시점에서는 어떤 객체도 그림자를 드리우지 않습니다.

장면에 그림자를 활성화하려면 Canvas 구성 요소에 그림자 속성을 추가해야 합니다.


main.jsx


다음으로, 새로운 광원을 추가해야 합니다. 장면에 이미 AmbientLight가 있음에도 불구하고 방향성 광선이 없기 때문에 객체에 대한 그림자를 만들 수 없습니다. 이제 DirectionalLight 라는 새로운 광원을 추가하고 구성해 보겠습니다. " 캐스트 " 섀도우 모드를 활성화하는 속성은 CastShadow 입니다. 이 개체가 다른 개체에 그림자를 투사할 수 있음을 나타내는 것은 이 매개변수의 추가입니다.


App.jsx


그런 다음 또 다른 속성 receiveShadow를 Ground 구성 요소에 추가해 보겠습니다. 이는 장면의 구성 요소가 자체적으로 그림자를 수신하고 표시할 수 있음을 의미합니다.


Ground.jsx


모델이 그림자를 드리우고 있다


유사한 속성을 장면의 다른 개체(큐브 및 플레이어)에 추가해야 합니다. 큐브의 경우 그림자를 투사하고 받을 수 있으므로 CastShadowreceiveShadow를 추가하고 플레이어의 경우 CastShadow 만 추가합니다.


PlayerCastShadow를 추가해 보겠습니다.


Player.jsx


Cube 에 대해 CastShadowreceiveShadow를 추가합니다.


Cube.jsx


장면의 모든 객체가 그림자를 투사합니다.


섹션 코드

그림자 추가 - 그림자 클리핑 수정

이제 자세히 살펴보면 그림자가 드리워지는 표면적이 상당히 작다는 것을 알 수 있습니다. 그리고 이 영역을 넘어가면 그림자가 간단히 잘려나가게 됩니다.


그림자 자르기


그 이유는 기본적으로 카메라가 DirectionalLight 에서 표시된 그림자의 작은 영역만 캡처하기 때문입니다. 이 가시성 영역을 확장하기 위해 추가 속성 Shadow-camera-(상단, 하단, 왼쪽, 오른쪽)를 추가하여 DirectionalLight 구성 요소에 사용할 수 있습니다. 이러한 속성을 추가하면 그림자가 약간 흐려집니다. 품질을 향상시키기 위해 Shadow-mapSize 속성을 추가하겠습니다.


App.jsx


섹션 코드

무기를 캐릭터에 바인딩

이제 1인칭 무기 디스플레이를 추가해 보겠습니다. 무기 동작 논리와 3D 모델 자체를 포함하는 새로운 무기 구성 요소를 만듭니다.


 import {WeaponModel} from "./WeaponModel.jsx"; export const Weapon = (props) => { return ( <group {...props}> <WeaponModel /> </group> ); }


이 구성 요소를 캐릭터의 RigidBody 와 동일한 수준에 배치하고 useFrame 후크에서 카메라의 값 위치를 기반으로 위치와 회전 각도를 설정하겠습니다.


Player.jsx


1인칭 무기 모델 디스플레이


섹션 코드

걷는 동안 무기를 휘두르는 애니메이션

캐릭터의 걸음걸이를 좀 더 자연스럽게 만들기 위해 움직일 때 무기의 약간의 흔들림을 추가하겠습니다. 애니메이션을 만들기 위해 설치된 tween.js 라이브러리를 사용합니다.


Weapon 구성 요소는 useRef 후크를 통해 참조를 추가할 수 있도록 그룹 태그로 래핑됩니다.


Player.jsx


애니메이션을 저장하기 위해 useState를 추가해 보겠습니다.


Player.jsx


애니메이션을 초기화하는 함수를 만들어 보겠습니다.


Player.jsx


코드 설명:

  1. const twSwayingAnimation = new TWEEN.Tween(currentPosition) ... 현재 위치에서 새 위치로 "스윙"하는 객체의 애니메이션을 생성합니다.
  1. const twSwayingBackAnimation = new TWEEN.Tween(currentPosition) ... 첫 번째 애니메이션이 완료된 후 시작 위치로 돌아가는 객체의 애니메이션을 만듭니다.
  1. twSwayingAnimation.chain(twSwayingBackAnimation); 첫 번째 애니메이션이 완료되면 두 번째 애니메이션이 자동으로 시작되도록 두 애니메이션을 연결합니다.


useEffect 에서는 애니메이션 초기화 함수를 호출합니다.


Player.jsx


이제 움직임이 발생하는 순간을 결정해야 합니다. 이는 캐릭터 방향의 현재 벡터를 결정하여 수행할 수 있습니다.


캐릭터 이동이 발생하면 애니메이션을 새로 고치고 완료되면 다시 실행합니다.


Player.jsx


코드 설명:

  1. const isMoving = 방향.길이() > 0; 여기서는 객체의 이동 상태를 확인합니다. 방향 벡터의 길이가 0보다 크다면 물체가 움직이는 방향이 있다는 뜻입니다.
  1. if (isMoving && isSwayingAnimationFinished) { ... } 이 상태는 객체가 움직이고 있고 "스윙" 애니메이션이 완료된 경우 실행됩니다.


구성 요소에서 트윈 애니메이션을 업데이트할 useFrame을 추가해 보겠습니다.


App.jsx


TWEEN.update()는 TWEEN.js 라이브러리의 모든 활성 애니메이션을 업데이트합니다. 이 메서드는 모든 애니메이션이 원활하게 실행되도록 하기 위해 각 애니메이션 프레임에서 호출됩니다.


섹션 코드:

반동 애니메이션

총알이 발사되는 순간, 즉 마우스 버튼을 누르는 순간을 정의해야 합니다. 이 상태를 저장하는 useState , 무기 객체에 대한 참조를 저장하는 useRef , 마우스 버튼을 누르고 놓기 위한 두 개의 이벤트 핸들러를 추가해 보겠습니다.


Weapon.jsx


Weapon.jsx


Weapon.jsx


마우스 버튼을 클릭할 때 반동 애니메이션을 구현해 보겠습니다. 이를 위해 tween.js 라이브러리를 사용합니다.


반동력과 애니메이션 지속 시간에 대한 상수를 정의해 보겠습니다.


Weapon.jsx


무기 흔들기 애니메이션과 마찬가지로 반동 및 홈 위치로 돌아가기 애니메이션에 대한 두 개의 useState 상태와 애니메이션 종료 상태가 있는 상태를 추가합니다.


Weapon.jsx


반동 애니메이션의 임의 벡터를 얻는 함수인 generateRecoilOffsetgenerateNewPositionOfRecoil을 만들어 보겠습니다.


Weapon.jsx


반동 애니메이션을 초기화하는 함수를 만듭니다. 또한 useEffect를 추가하여 "샷" 상태를 종속성으로 지정하여 각 샷에서 애니메이션이 다시 초기화되고 새로운 끝 좌표가 생성되도록 합니다.


Weapon.jsx


Weapon.jsx


그리고 useFrame 에서 발사를 위해 마우스 키를 "유지"하는 검사를 추가하여 키를 놓을 때까지 발사 애니메이션이 멈추지 않도록 합시다.


Weapon.jsx


반동 애니메이션


섹션 코드

비활성 중 애니메이션

캐릭터의 "비활동" 애니메이션을 실현하여 게임이 "멈춘다"는 느낌이 없습니다.


이를 위해 useState를 통해 몇 가지 새로운 상태를 추가해 보겠습니다.


Player.jsx


상태의 값을 사용하도록 "흔들기" 애니메이션의 초기화를 수정해 보겠습니다. 아이디어는 걷기 또는 정지 등 다양한 상태가 애니메이션에 대해 서로 다른 값을 사용하고 애니메이션이 먼저 초기화될 때마다라는 것입니다.


Player.jsx


유휴 애니메이션


결론

이 부분에서는 장면 생성과 캐릭터 이동을 구현했습니다. 또한 무기 모델, 발사 시 및 유휴 상태에서의 반동 애니메이션을 추가했습니다. 다음 부분에서는 계속해서 게임을 개선하고 새로운 기능을 추가할 것입니다.


여기에도 게시되었습니다.