paint-brush
Tạo game bắn súng 3D của riêng bạn bằng cách sử dụng ngăn xếp React và Three.js - Phần 1từ tác giả@varlab
2,947 lượt đọc
2,947 lượt đọc

Tạo game bắn súng 3D của riêng bạn bằng cách sử dụng ngăn xếp React và Three.js - Phần 1

từ tác giả Ivan Zhukov17m2023/10/21
Read on Terminal Reader

dài quá đọc không nổi

Trong thời đại phát triển tích cực của công nghệ web và các ứng dụng tương tác, đồ họa 3D ngày càng trở nên phù hợp và có nhu cầu. Nhưng làm thế nào để tạo một ứng dụng 3D mà không làm mất đi lợi thế của việc phát triển web? Trong bài viết này, chúng ta sẽ xem xét cách kết hợp sức mạnh của Three.js với tính linh hoạt của React để tạo trò chơi của riêng bạn ngay trong trình duyệt. Bài viết này sẽ giới thiệu cho bạn thư viện React Three Fiber và hướng dẫn bạn cách tạo trò chơi 3D tương tác.
featured image - Tạo game bắn súng 3D của riêng bạn bằng cách sử dụng ngăn xếp React và Three.js - Phần 1
Ivan Zhukov HackerNoon profile picture
0-item
1-item

Trong quá trình phát triển web hiện đại, ranh giới giữa ứng dụng web và ứng dụng cổ điển đang ngày càng mờ đi. Ngày nay, chúng ta không chỉ có thể tạo các trang web tương tác mà còn tạo ra các trò chơi hoàn chỉnh ngay trên trình duyệt. Một trong những công cụ giúp thực hiện được điều này là thư viện React Three Fiber - một công cụ mạnh mẽ để tạo đồ họa 3D dựa trên Three.js bằng công nghệ React .

Giới thiệu về ngăn xếp React Three Fiber

React Three Fiber là một trình bao bọc trên Three.js sử dụng cấu trúc và nguyên tắc của React để tạo đồ họa 3D trên web. Ngăn xếp này cho phép các nhà phát triển kết hợp sức mạnh của Three.js với sự tiện lợi và linh hoạt của React , giúp quá trình tạo ứng dụng trở nên trực quan và có tổ chức hơn.


Trọng tâm của React Three Fiber là ý tưởng rằng mọi thứ bạn tạo trong một cảnh đều là thành phần React . Điều này cho phép các nhà phát triển áp dụng các mô hình và phương pháp quen thuộc.

Một trong những ưu điểm chính của React Three Fiber là dễ tích hợp với hệ sinh thái React . Bất kỳ công cụ React nào khác vẫn có thể được tích hợp dễ dàng khi sử dụng thư viện này.

Sự liên quan của Web-GameDev

Web-GameDev đã trải qua những thay đổi lớn trong những năm gần đây, phát triển từ các trò chơi Flash 2D đơn giản đến các dự án 3D phức tạp có thể so sánh với các ứng dụng trên máy tính để bàn. Sự phát triển về mức độ phổ biến và khả năng này khiến Web-GameDev trở thành một lĩnh vực không thể bỏ qua.


Một trong những lợi thế chính của chơi game trên web là khả năng truy cập của nó. Người chơi không cần tải xuống và cài đặt bất kỳ phần mềm bổ sung nào - chỉ cần nhấp vào liên kết trong trình duyệt của họ. Điều này giúp đơn giản hóa việc phân phối và quảng bá trò chơi, giúp chúng có sẵn cho nhiều đối tượng trên toàn thế giới.


Cuối cùng, phát triển trò chơi trên web có thể là một cách tuyệt vời để các nhà phát triển thử sức mình với gamedev bằng các công nghệ quen thuộc. Nhờ các công cụ và thư viện có sẵn, ngay cả khi không có kinh nghiệm về đồ họa 3D, bạn vẫn có thể tạo ra các dự án thú vị và chất lượng cao!

Hiệu suất trò chơi trong các trình duyệt hiện đại

Các trình duyệt hiện đại đã đi được một chặng đường dài, phát triển từ các công cụ duyệt web khá đơn giản đến các nền tảng mạnh mẽ để chạy các ứng dụng và trò chơi phức tạp. Các trình duyệt chính như Chrome , Firefox , Edgecác trình duyệt khác liên tục được tối ưu hóa và phát triển để đảm bảo hiệu suất cao, khiến chúng trở thành nền tảng lý tưởng để phát triển các ứng dụng phức tạp.


Một trong những công cụ quan trọng đã thúc đẩy sự phát triển của trò chơi dựa trên trình duyệt là WebGL . Tiêu chuẩn này cho phép các nhà phát triển sử dụng khả năng tăng tốc đồ họa phần cứng, giúp cải thiện đáng kể hiệu suất của trò chơi 3D. Cùng với các webAPI khác, WebGL mở ra những khả năng mới để tạo các ứng dụng web ấn tượng trực tiếp trong trình duyệt.


Tuy nhiên, khi phát triển trò chơi cho trình duyệt, điều quan trọng là phải xem xét các khía cạnh hiệu suất khác nhau: tối ưu hóa tài nguyên, quản lý bộ nhớ và khả năng thích ứng cho các thiết bị khác nhau đều là những điểm chính có thể ảnh hưởng đến sự thành công của dự án.

Trên nhãn hiệu của bạn!

Tuy nhiên, lời nói và lý thuyết là một chuyện, còn trải nghiệm thực tế lại là một chuyện khác. Để thực sự hiểu và đánh giá cao tiềm năng phát triển web game, cách tốt nhất là hòa mình vào quá trình phát triển. Vì vậy, để làm ví dụ về việc phát triển trò chơi trên web thành công, chúng tôi sẽ tạo ra trò chơi của riêng mình. Quá trình này sẽ cho phép chúng tôi tìm hiểu các khía cạnh chính của quá trình phát triển, đối mặt với các vấn đề thực tế và tìm giải pháp cho chúng, đồng thời xem nền tảng phát triển trò chơi web có thể mạnh mẽ và linh hoạt như thế nào.


Trong loạt bài viết này, chúng ta sẽ xem cách tạo game bắn súng góc nhìn thứ nhất bằng cách sử dụng các tính năng của thư viện này và đi sâu vào thế giới thú vị của web-gamedev!


Bản demo cuối cùng


Kho lưu trữ trên GitHub


Bây giờ, hãy bắt đâù!

Thiết lập dự án và cài đặt gói

Trước hết, chúng ta sẽ cần một mẫu dự án React . Vì vậy, hãy bắt đầu bằng cách cài đặt nó.


 npm create vite@latest


  • chọn thư viện React ;
  • chọn JavaScript .


Cài đặt các gói npm bổ sung.


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


Sau đó xóa mọi thứ không cần thiết khỏi dự án của chúng tôi.


Mã phần

Tùy chỉnh hiển thị Canvas

Trong tệp main.jsx , thêm phần tử div sẽ được hiển thị trên trang dưới dạng phạm vi. Chèn thành phần Canvas và đặt trường nhìn của máy ảnh. Bên trong thành phần Canvas đặt thành phần Ứng dụng .


main.jsx


Hãy thêm kiểu vào index.css để kéo dài các thành phần UI đến hết chiều cao của màn hình và hiển thị phạm vi dưới dạng vòng tròn ở giữa màn hình.


chỉ mục.css


Trong thành phần Ứng dụng , chúng tôi thêm thành phần Bầu trời , thành phần này sẽ được hiển thị làm nền trong cảnh trò chơi của chúng tôi dưới dạng bầu trời.


Ứng dụng.jsx


Hiển thị bầu trời trong cảnh


Mã phần

Bề mặt sàn

Hãy tạo một thành phần Ground và đặt nó vào thành phần Ứng dụng .


Ứng dụng.jsx


Trong Ground , tạo một phần tử có bề mặt phẳng. Trên trục Y, di chuyển nó xuống dưới để mặt phẳng này nằm trong tầm nhìn của camera. Và cũng lật mặt phẳng trên trục X để nó nằm ngang.


Ground.jsx


Mặc dù chúng tôi đã chỉ định màu xám làm màu vật liệu nhưng mặt phẳng có vẻ hoàn toàn màu đen.


Căn hộ tại hiện trường


Mã phần

Chiếu sáng cơ bản

Theo mặc định, không có ánh sáng trong cảnh, vì vậy hãy thêm nguồn sáng ambientLight , nguồn sáng này chiếu sáng đối tượng từ mọi phía và không có chùm tia định hướng. Là một tham số, đặt cường độ phát sáng.


Ứng dụng.jsx


Mặt phẳng được chiếu sáng


Mã phần

Kết cấu cho bề mặt sàn

Để làm cho bề mặt sàn trông không đồng nhất, chúng ta sẽ thêm họa tiết. Tạo mô hình bề mặt sàn dưới dạng các ô lặp lại dọc theo bề mặt.

Trong thư mục nội dung , thêm hình ảnh PNG có họa tiết.


Đã thêm kết cấu


Để tải một kết cấu vào hiện trường, hãy sử dụng hook useTexture từ gói @react-two/drei . Và như một tham số cho hook, chúng ta sẽ chuyển hình ảnh họa tiết được nhập vào tệp. Đặt độ lặp lại của hình ảnh theo trục ngang.


Ground.jsx


Kết cấu trên mặt phẳng


Mã phần

Chuyển động của máy ảnh

Sử dụng thành phần PointerLockControls từ gói @react-two/drei , cố định con trỏ trên màn hình để nó không di chuyển khi bạn di chuyển chuột mà thay đổi vị trí của camera trên cảnh.


Ứng dụng.jsx


Trình diễn chuyển động của máy ảnh


Hãy thực hiện một chỉnh sửa nhỏ cho thành phần Ground .


Ground.jsx


Mã phần

Thêm vật lý

Để rõ ràng, hãy thêm một khối lập phương đơn giản vào hiện trường.


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


Khối lập phương tại hiện trường


Hiện giờ anh ấy đang lơ lửng trong không gian.


Sử dụng thành phần Vật lý từ gói @react-ba/rapier để thêm "vật lý" vào cảnh. Là một tham số, hãy định cấu hình trường trọng lực, nơi chúng ta đặt lực hấp dẫn dọc theo các trục.


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


Tuy nhiên, khối lập phương của chúng ta nằm bên trong thành phần vật lý nhưng không có gì xảy ra với nó. Để làm cho khối lập phương hoạt động giống như một vật thể thực sự, chúng ta cần bọc nó trong thành phần RigidBody từ gói @react-two/rapier .


Ứng dụng.jsx


Sau đó, chúng ta sẽ thấy ngay mỗi lần tải lại trang, khối lập phương sẽ rơi xuống dưới tác dụng của trọng lực.


khối lập phương rơi


Nhưng bây giờ có một nhiệm vụ khác - cần phải biến sàn thành một vật thể mà khối lập phương có thể tương tác và vượt quá vật đó nó sẽ không rơi.


Mã phần

Sàn nhà như một vật thể

Hãy quay lại thành phần Ground và thêm thành phần RigidBody làm lớp bao bọc trên bề mặt sàn.


Ground.jsx


Bây giờ khi rơi xuống, khối lập phương vẫn nằm trên sàn giống như một vật thể thực sự.


Khối lập phương rơi trên máy bay


Mã phần

Đưa một nhân vật tuân theo các định luật vật lý

Hãy tạo một thành phần Người chơi sẽ điều khiển nhân vật trong cảnh.


Nhân vật là đối tượng vật lý giống như khối được thêm vào, vì vậy nó phải tương tác với bề mặt sàn cũng như khối trên hiện trường. Đó là lý do tại sao chúng tôi thêm thành phần RigidBody . Và hãy tạo nhân vật ở dạng viên nang.


Player.jsx


Đặt thành phần Player bên trong thành phần Vật lý.


Ứng dụng.jsx


Bây giờ nhân vật của chúng ta đã xuất hiện trên hiện trường.


Một nhân vật ở dạng viên nang


Mã phần

Di chuyển nhân vật - tạo móc câu

Nhân vật sẽ được điều khiển bằng các phím WASD và nhảy bằng phím cách .

Với React-hook của riêng mình, chúng tôi triển khai logic di chuyển nhân vật.


Hãy tạo một tệp hooks.js và thêm hàm usePersonControls mới vào đó.


Hãy xác định một đối tượng ở định dạng {"keycode": "hành động cần thực hiện"}. Tiếp theo, thêm trình xử lý sự kiện để nhấn và nhả phím trên bàn phím. Khi trình xử lý được kích hoạt, chúng tôi sẽ xác định các hành động hiện tại đang được thực hiện và cập nhật trạng thái hoạt động của chúng. Kết quả cuối cùng, hook sẽ trả về một đối tượng ở định dạng {"action in Progress": "status"}.


hook.js


Mã phần

Di chuyển nhân vật - thực hiện hook

Sau khi triển khai hook usePersonControls , nó sẽ được sử dụng khi điều khiển nhân vật. Trong thành phần Người chơi , chúng tôi sẽ thêm tính năng theo dõi trạng thái chuyển động và cập nhật vectơ hướng chuyển động của nhân vật.


Chúng ta cũng sẽ xác định các biến sẽ lưu trữ trạng thái của các hướng chuyển động.


Player.jsx


Để cập nhật vị trí của nhân vật, hãy sử dụngFrame được cung cấp bởi gói @react-ba/fiber . Hook này hoạt động tương tự như requestAnimationFrame và thực thi phần thân của hàm khoảng 60 lần mỗi giây.


Player.jsx


Giải thích mã:

1. const playerRef = useRef(); Tạo một liên kết cho đối tượng người chơi. Liên kết này sẽ cho phép tương tác trực tiếp với đối tượng người chơi trên hiện trường.

2. const { tiến, lùi, trái, phải, nhảy } = usePersonControls(); Khi sử dụng hook, một đối tượng có giá trị boolean cho biết nút điều khiển nào hiện được người chơi nhấn sẽ được trả về.

3. useFrame((state) => { ... }); Cái móc được gọi trên mỗi khung hình của hoạt ảnh. Bên trong cái móc này, vị trí và vận tốc tuyến tính của người chơi được cập nhật.

4. if (!playerRef.current) trả về; Kiểm tra sự hiện diện của đối tượng người chơi. Nếu không có đối tượng người chơi, hàm sẽ dừng thực thi để tránh lỗi.

5. vận tốc const = playerRef.current.linvel(); Lấy vận tốc tuyến tính hiện tại của người chơi.

6. frontVector.set(0, 0, lùi - tiến); Đặt vectơ chuyển động tiến/lùi dựa trên các nút được nhấn.

7. sideVector.set(trái - phải, 0, 0); Đặt vectơ chuyển động trái/phải.

8. Direction.subVectors(frontVector, sideVector).normalize().multiplyScalar(MOVE_SPEED); Tính vectơ chuyển động cuối cùng của người chơi bằng cách trừ các vectơ chuyển động, chuẩn hóa kết quả (sao cho độ dài vectơ là 1) và nhân với hằng số tốc độ di chuyển.

9. playerRef.current.wakeUp(); "Đánh thức" đối tượng người chơi để đảm bảo nó phản ứng với những thay đổi. Nếu bạn không sử dụng phương pháp này, sau một thời gian đối tượng sẽ "ngủ" và không phản ứng với những thay đổi về vị trí.

10. playerRef.current.setLinvel({ x: Direction.x, y: Velocity.y, z: Direction.z }); Đặt vận tốc tuyến tính mới của người chơi dựa trên hướng di chuyển được tính toán và giữ nguyên vận tốc thẳng đứng hiện tại (để không ảnh hưởng đến việc nhảy hoặc rơi).


Kết quả là khi nhấn các phím WASD , nhân vật bắt đầu di chuyển xung quanh khung cảnh. Anh ta cũng có thể tương tác với khối lập phương vì cả hai đều là vật thể.


Chuyển động của nhân vật


Mã phần

Di chuyển nhân vật - nhảy

Để thực hiện bước nhảy, hãy sử dụng chức năng từ các gói @dimforge/rapier3d-compat@react-two/rapier . Trong ví dụ này, hãy kiểm tra xem nhân vật có ở trên mặt đất và phím nhảy đã được nhấn hay chưa. Trong trường hợp này, chúng tôi đặt hướng và lực gia tốc của nhân vật trên trục Y.


Đối với Người chơi, chúng tôi sẽ thêm khối lượng và khối xoay trên tất cả các trục để anh ta không bị ngã theo các hướng khác nhau khi va chạm với các vật thể khác trong hiện trường.


Player.jsx


Giải thích mã:

  1. const world = rapier.world; Đạt được quyền truy cập vào cảnh động cơ vật lý Rapier . Nó chứa tất cả các đối tượng vật lý và quản lý sự tương tác của chúng.
  1. const ray = world.castRay(new RAPIER.Ray(playerRef.current.translation(), { x: 0, y: -1, z: 0 })); Đây là nơi diễn ra quá trình "raycasting" (raycasting). Một tia được tạo bắt đầu từ vị trí hiện tại của người chơi và hướng xuống trục y. Tia này được "đưa" vào cảnh để xác định xem nó có giao nhau với bất kỳ vật thể nào trong cảnh hay không.
  1. const căn cứ = ray && ray.collider && Math.abs(ray.toi) <= 1.5; Điều kiện được kiểm tra nếu người chơi ở trên mặt đất:
  • tia - liệu tia có được tạo ra hay không;
  • ray.collider - liệu tia có va chạm với bất kỳ vật thể nào trong hiện trường hay không;
  • Math.abs(ray.toi) - "thời gian phơi sáng" của tia. Nếu giá trị này nhỏ hơn hoặc bằng giá trị nhất định, nó có thể cho thấy rằng người chơi ở đủ gần bề mặt để được coi là "trên mặt đất".


Bạn cũng cần sửa đổi thành phần Ground để thuật toán raytraced xác định trạng thái "hạ cánh" hoạt động chính xác, bằng cách thêm một đối tượng vật lý sẽ tương tác với các đối tượng khác trong cảnh.


Ground.jsx


Hãy nâng máy ảnh lên cao hơn một chút để nhìn rõ khung cảnh hơn.


main.jsx


Nhân vật nhảy


Mã phần

Di chuyển camera phía sau nhân vật

Để di chuyển camera, chúng ta sẽ lấy vị trí hiện tại của người chơi và thay đổi vị trí của camera mỗi khi khung hình được làm mới. Và để nhân vật di chuyển chính xác theo quỹ đạo, nơi camera hướng tới, chúng ta cần thêm applyEuler .


Player.jsx


Giải thích mã:

Phương thức applyEuler áp dụng phép quay cho một vectơ dựa trên các góc Euler đã chỉ định. Trong trường hợp này, phép quay camera được áp dụng cho vectơ chỉ hướng . Điều này được sử dụng để khớp chuyển động tương ứng với hướng của camera, để người chơi di chuyển theo hướng xoay camera.


Hãy điều chỉnh một chút kích thước của Player và làm cho nó cao hơn so với khối lập phương, tăng kích thước của CapsuleCollider và sửa lỗi logic "nhảy".


Player.jsx


Di chuyển máy ảnh


Mã phần

Tạo hình khối

Để làm cho khung cảnh không có cảm giác hoàn toàn trống rỗng, hãy thêm khối lập phương. Trong tệp json, liệt kê tọa độ của từng khối và sau đó hiển thị chúng trên hiện trường. Để thực hiện việc này, hãy tạo một tệp Cubes.json , trong đó chúng tôi sẽ liệt kê một mảng tọa độ.


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


Trong tệp Cube.jsx , hãy tạo thành phần Cubes để tạo các hình khối trong một vòng lặp. Và thành phần Cube sẽ là đối tượng được tạo trực tiếp.


 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> ); }


Hãy thêm thành phần Khối đã tạo vào thành phần Ứng dụng bằng cách xóa khối đơn trước đó.


Ứng dụng.jsx


Tạo hình khối


Mã phần

Nhập mô hình vào dự án

Bây giờ hãy thêm mô hình 3D vào cảnh. Hãy thêm mô hình vũ khí cho nhân vật. Hãy bắt đầu bằng cách tìm kiếm một mô hình 3D. Ví dụ: hãy lấy cái này .


Tải xuống mô hình ở định dạng GLTF và giải nén kho lưu trữ trong thư mục gốc của dự án.

Để có được định dạng, chúng ta cần nhập mô hình vào cảnh, chúng ta sẽ cần cài đặt gói bổ trợ gltf-pipeline .


npm i -D gltf-pipeline


Sử dụng gói gltf-pipeline , chuyển đổi lại mô hình từ định dạng GLTF sang định dạng GLB , vì ở định dạng này, tất cả dữ liệu mô hình được đặt trong một tệp. Là thư mục đầu ra cho tệp được tạo, chúng tôi chỉ định thư mục chung .


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


Sau đó, chúng ta cần tạo một thành phần phản ứng sẽ chứa đánh dấu của mô hình này để thêm nó vào cảnh. Hãy sử dụng tài nguyên chính thức từ nhà phát triển @react-ba/fiber .


Đi tới trình chuyển đổi sẽ yêu cầu bạn tải tệp Weapon.glb đã chuyển đổi.


Sử dụng kéo và thả hoặc tìm kiếm Explorer, tìm tệp này và tải xuống.


Mô hình chuyển đổi


Trong trình chuyển đổi, chúng ta sẽ thấy thành phần phản ứng được tạo, mã mà chúng ta sẽ chuyển sang dự án của mình trong một tệp mới WeaponModel.jsx , thay đổi tên của thành phần thành cùng tên với tệp.


Mã phần

Trưng bày mô hình vũ khí tại hiện trường

Bây giờ hãy nhập mô hình đã tạo vào cảnh. Trong tệp App.jsx thêm thành phần WeaponModel .


Ứng dụng.jsx


Trình diễn mẫu xe nhập khẩu


Mã phần

Thêm bóng

Tại thời điểm này trong khung cảnh của chúng ta, không có vật thể nào đổ bóng.

Để bật bóng trên cảnh, bạn cần thêm thuộc tính bóng vào thành phần Canvas .


main.jsx


Tiếp theo, chúng ta cần thêm một nguồn sáng mới. Mặc dù thực tế là chúng ta đã có ambientLight trên hiện trường, nhưng nó không thể tạo bóng cho các vật thể vì nó không có chùm sáng định hướng. Vì vậy, hãy thêm một nguồn sáng mới có tên là directionalLight và định cấu hình nó. Thuộc tính để kích hoạt chế độ đổ bóng " cast " là castShadow . Việc bổ sung tham số này cho biết rằng đối tượng này có thể tạo bóng lên các đối tượng khác.


Ứng dụng.jsx


Sau đó, hãy thêm một thuộc tính khác getShadow vào thành phần Ground , nghĩa là thành phần trong cảnh có thể nhận và hiển thị bóng trên chính nó.


Ground.jsx


Người mẫu đổ bóng


Các thuộc tính tương tự nên được thêm vào các đối tượng khác trong hiện trường: hình khối và trình phát. Đối với các hình khối, chúng tôi sẽ thêm castShadowgetShadow , vì chúng có thể tạo và nhận bóng, còn đối với người chơi, chúng tôi sẽ chỉ thêm castShadow .


Hãy thêm castShadow cho Player .


Player.jsx


Thêm castShadowgetShadow cho Cube .


Cube.jsx


Tất cả các vật thể trong cảnh đều đổ bóng


Mã phần

Thêm bóng - sửa lỗi cắt bóng

Nếu bây giờ bạn nhìn kỹ hơn, bạn sẽ thấy rằng diện tích bề mặt mà bóng đổ lên khá nhỏ. Và khi đi ra ngoài khu vực này, cái bóng chỉ đơn giản là bị cắt bỏ.


Cắt bóng


Lý do cho điều này là theo mặc định, máy ảnh chỉ chụp một vùng nhỏ bóng được hiển thị từ directionalLight . Chúng ta có thể làm điều đó đối với thành phần directionalLight bằng cách thêm các thuộc tính bổ sung bóng-máy ảnh-(trên, dưới, trái, phải) để mở rộng vùng hiển thị này. Sau khi thêm các thuộc tính này, bóng sẽ hơi mờ. Để cải thiện chất lượng, chúng tôi sẽ thêm thuộc tính Shadow-mapSize .


Ứng dụng.jsx


Mã phần

Ràng buộc vũ khí vào một nhân vật

Bây giờ hãy thêm màn hình vũ khí ở góc nhìn thứ nhất. Tạo thành phần Vũ khí mới, thành phần này sẽ chứa logic hành vi của vũ khí và chính mô hình 3D.


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


Hãy đặt thành phần này ngang hàng với RigidBody của nhân vật và trong hook useFrame , chúng ta sẽ đặt vị trí và góc xoay dựa trên vị trí của các giá trị từ camera.


Player.jsx


Màn trình diễn mô hình vũ khí góc nhìn thứ nhất


Mã phần

Hoạt hình vung vũ khí khi đi bộ

Để làm cho dáng đi của nhân vật tự nhiên hơn, chúng ta sẽ thêm động tác lắc nhẹ vũ khí khi di chuyển. Để tạo hoạt ảnh, chúng tôi sẽ sử dụng thư viện tween.js đã cài đặt.


Thành phần Vũ khí sẽ được gói trong một thẻ nhóm để bạn có thể thêm tham chiếu đến nó thông qua hook useRef .


Player.jsx


Hãy thêm một số useState để lưu hoạt ảnh.


Player.jsx


Hãy tạo một hàm để khởi tạo hoạt ảnh.


Player.jsx


Giải thích mã:

  1. const twSwayingAnimation = new TWEEN.Tween(currentPosition) ... Tạo hoạt ảnh của một đối tượng "đu đưa" từ vị trí hiện tại sang vị trí mới.
  1. const twSwayingBackAnimation = new TWEEN.Tween(currentPosition) ... Tạo hoạt ảnh của đối tượng quay trở lại vị trí bắt đầu sau khi hoạt ảnh đầu tiên hoàn thành.
  1. twSwayingAnimation.chain(twSwayingBackAnimation); Kết nối hai ảnh động để khi ảnh động đầu tiên kết thúc thì ảnh động thứ hai sẽ tự động bắt đầu.


Trong useEffect chúng ta gọi hàm khởi tạo hoạt ảnh.


Player.jsx


Bây giờ cần xác định thời điểm chuyển động xảy ra. Điều này có thể được thực hiện bằng cách xác định vectơ hiện tại về hướng của nhân vật.


Nếu chuyển động của nhân vật xảy ra, chúng tôi sẽ làm mới hoạt ảnh và chạy lại khi hoàn tất.


Player.jsx


Giải thích mã:

  1. const isMoving = Direction.length() > 0; Ở đây trạng thái chuyển động của đối tượng được kiểm tra. Nếu vectơ chỉ phương có độ dài lớn hơn 0 thì vật có hướng chuyển động.
  1. if (isMoving && isSwayingAnimationFinished) { ... } Trạng thái này được thực thi nếu đối tượng đang chuyển động và hoạt ảnh "lắc lư" đã kết thúc.


Trong thành phần Ứng dụng , hãy thêm useFrame để cập nhật hoạt ảnh tween.


Ứng dụng.jsx


TWEEN.update() cập nhật tất cả hoạt ảnh đang hoạt động trong thư viện TWEEN.js . Phương pháp này được gọi trên mỗi khung hình động để đảm bảo rằng tất cả các hình động chạy trơn tru.


Mã phần:

Hoạt hình giật lại

Chúng ta cần xác định thời điểm bắn một phát súng - tức là khi nhấn nút chuột. Hãy thêm useState để lưu trữ trạng thái này, useRef để lưu tham chiếu đến đối tượng vũ khí và hai trình xử lý sự kiện để nhấn và thả nút chuột.


Vũ khí.jsx


Vũ khí.jsx


Vũ khí.jsx


Hãy thực hiện hoạt ảnh giật lại khi nhấp vào nút chuột. Chúng tôi sẽ sử dụng thư viện tween.js cho mục đích này.


Hãy để chúng tôi xác định các hằng số cho lực giật và thời lượng hoạt ảnh.


Vũ khí.jsx


Giống như hoạt ảnh lắc lư vũ khí, chúng tôi thêm hai trạng thái useState cho hoạt ảnh giật và quay về vị trí ban đầu và một trạng thái có trạng thái kết thúc hoạt ảnh.


Vũ khí.jsx


Hãy tạo các hàm để lấy một vectơ ngẫu nhiên của hoạt ảnh giật - generateRecoilOffsetgenerateNewPositionOfRecoil .


Vũ khí.jsx


Tạo một hàm để khởi tạo hoạt ảnh giật lại. Chúng tôi cũng sẽ thêm useEffect , trong đó chúng tôi sẽ chỉ định trạng thái "shot" làm phần phụ thuộc, để tại mỗi lần chụp, hoạt ảnh sẽ được khởi tạo lại và tọa độ cuối mới được tạo.


Vũ khí.jsx


Vũ khí.jsx


Và trong useFrame , hãy thêm kiểm tra "giữ" phím chuột để kích hoạt, để hoạt ảnh kích hoạt không dừng lại cho đến khi phím được nhả ra.


Vũ khí.jsx


Hoạt hình giật lại


Mã phần

Hoạt ảnh khi không hoạt động

Hiện thực hóa hoạt ảnh “không hoạt động” cho nhân vật, để không có cảm giác game bị “treo”.


Để làm điều này, hãy thêm một số trạng thái mới thông qua useState .


Player.jsx


Hãy sửa lỗi khởi tạo hoạt ảnh "lắc lư" để sử dụng các giá trị từ trạng thái. Ý tưởng là các trạng thái khác nhau: đi bộ hoặc dừng lại, sẽ sử dụng các giá trị khác nhau cho hoạt ảnh và mỗi lần hoạt ảnh sẽ được khởi tạo trước tiên.


Player.jsx


Hoạt ảnh nhàn rỗi


Phần kết luận

Trong phần này chúng tôi đã thực hiện việc tạo cảnh và chuyển động của nhân vật. Chúng tôi cũng đã thêm mô hình vũ khí, hoạt ảnh giật khi bắn và khi không hoạt động. Trong phần tiếp theo, chúng tôi sẽ tiếp tục cải tiến trò chơi của mình, bổ sung thêm chức năng mới.


Cũng được xuất bản ở đây .