diff --git a/components/Experience.js b/components/Experience.js
new file mode 100644
index 0000000..2f1aaaa
--- /dev/null
+++ b/components/Experience.js
@@ -0,0 +1,30 @@
+import { OrbitControls, useRef } from "@react-three/drei";
+import Soldier from "./Soldier";
+import ZWorld from "./ZWorld";
+
+const Experience = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default Experience;
\ No newline at end of file
diff --git a/components/Soldier.jsx b/components/Soldier.jsx
new file mode 100644
index 0000000..7809195
--- /dev/null
+++ b/components/Soldier.jsx
@@ -0,0 +1,156 @@
+import React, { useEffect, useRef } from "react";
+import { OrbitControls, useGLTF, useAnimations } from "@react-three/drei";
+import useInputs from "@/hooks/useInputs";
+import { useFrame, useThree } from "@react-three/fiber";
+import * as THREE from "three";
+
+
+let walkDirection = new THREE.Vector3();
+let rotateAngle = new THREE.Vector3(0, 1, 0);
+let rotateQuarternion = new THREE.Quaternion();
+let cameraTarget = new THREE.Vector3();
+
+
+const directionOffset = ({ forward, backward, left, right }) => {
+ var directionOffset = 0;
+
+ if(forward) {
+ if(left) {
+ directionOffset = Math.PI / 4;
+ } else if (right) {
+ directionOffset = -Math.PI / 4;
+ }
+ } else if (backward) {
+ if (left) {
+ directionOffset = Math.PI / 4 + Math.PI / 2;
+ } else if (right) {
+ directionOffset = -Math.PI / 4 - Math.PI / 2;
+ } else {
+ directionOffset = Math.PI;
+ }
+ } else if (left) {
+ directionOffset = Math.PI;
+ } else if (right) {
+ directionOffset = -Math.PI / 2;
+ }
+
+ return directionOffset;
+}
+
+
+const Soldier = (props) => {
+ const group = useRef();
+ const { nodes, materials, animations } = useGLTF("./Model/Soldier.glb");
+ const { actions } = useAnimations(animations, group);
+ const { forward, backward, left, right, shift } = useInputs();
+
+ const currentAction = useRef("");
+ const controlsRef = useRef();
+ const camera = useThree((state) => state.camera);
+
+ const updateCameraTarget = (moveX, moveZ) => {
+ //move Camera
+ camera.position.x += moveX;
+ camera.position.z += moveZ;
+
+ //update camera target
+ cameraTarget.x = group.current.position.x;
+ cameraTarget.y = group.current.position.y + 1;
+ cameraTarget.z = group.current.position.z;
+ if(controlsRef.current) controlsRef.current.target = cameraTarget;
+ };
+
+ useEffect(() => {
+ let action = "";
+
+ if(forward || backward || left || right) {
+ action = "Walk";
+
+ if(shift) {
+ action = "Run";
+ }
+ } else {
+ action = "Idle";
+ }
+
+ if(currentAction.current != action) {
+ const nextActionToPlay = actions[action];
+ const current = actions[currentAction.current];
+ current?.fadeOut(0.2);
+ nextActionToPlay?.reset().fadeIn(0.2).play();
+ currentAction.current = action;
+ }
+
+ }, [forward, backward, left, right, shift]);
+
+ useFrame((state, delta) => {
+ //camera direction
+ if(currentAction.current == "Run" || currentAction.current == "Walk") {
+ let angleYCameraDirection = Math.atan2(
+ camera.position.x - group.current.position.x,
+ camera.position.z - group.current.position.z
+ );
+
+ //diagonal movement angle offset
+ let newDirectionOffset = directionOffset({
+ forward,
+ backward,
+ left,
+ right,
+ });
+
+ // rotate model
+ rotateQuarternion.setFromAxisAngle(
+ rotateAngle,
+ angleYCameraDirection + newDirectionOffset
+ );
+ group.current.quaternion.rotateTowards(rotateQuarternion, 0.2)
+
+
+ // Calculate Direction
+ camera.getWorldDirection(walkDirection);
+ walkDirection.y = 0;
+ walkDirection.normalize();
+ walkDirection.applyAxisAngle(rotateAngle, newDirectionOffset);
+
+ // run/walk velocity
+ const velocity = currentAction.current == "Run" ? 60 : 30;
+
+ // Move model & camera
+ const moveX = walkDirection.x * velocity * delta;
+ const moveZ = walkDirection.z * velocity * delta;
+ group.current.position.x += moveX;
+ group.current.position.z += moveZ;
+ updateCameraTarget(moveX, moveZ);
+ }
+ })
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
+
+export default Soldier;
+
+useGLTF.preload("./Model/Soldier.glb");
diff --git a/components/ZWorld.jsx b/components/ZWorld.jsx
new file mode 100644
index 0000000..10aecb8
--- /dev/null
+++ b/components/ZWorld.jsx
@@ -0,0 +1,337 @@
+import React, { useRef } from "react";
+import { useGLTF, useAnimations } from "@react-three/drei";
+
+const ZWorld = (props) => {
+ const group = useRef();
+ const { nodes, materials, animations } = useGLTF("./Model/zworld.glb");
+ const { actions } = useAnimations(animations, group);
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default ZWorld;
+
+useGLTF.preload("./Model/zworld.glb");
diff --git a/hooks/useInputs.js b/hooks/useInputs.js
new file mode 100644
index 0000000..4537717
--- /dev/null
+++ b/hooks/useInputs.js
@@ -0,0 +1,39 @@
+import { useEffect, useState } from "react";
+
+const useInputs = () => {
+ const keys = {
+ KeyW: 'forward',
+ KeyS: 'backward',
+ KeyA: 'left',
+ KeyD: 'right',
+ ShiftLeft: 'shift',
+ }
+
+ const moveFieldByKey = (key) => keys[key]
+
+ const [movement, setMovement] = useState({
+ forward: false,
+ backward: false,
+ left: false,
+ right: false,
+ shift: false,
+ })
+
+ useEffect(() => {
+ const handleKeyDown = (e) => {
+ setMovement((m) => ({ ...m, [moveFieldByKey(e.code)]: true }))
+ }
+ const handleKeyUp = (e) => {
+ setMovement((m) => ({ ...m, [moveFieldByKey(e.code)]: false }))
+ }
+ document.addEventListener('keydown', handleKeyDown)
+ document.addEventListener('keyup', handleKeyUp)
+ return () => {
+ document.removeEventListener('keydown', handleKeyDown)
+ document.removeEventListener('keyup', handleKeyUp)
+ }
+ }, [])
+ return movement
+ }
+
+ export default useInputs;
\ No newline at end of file
diff --git a/pages/world.js b/pages/world.js
new file mode 100644
index 0000000..3f88119
--- /dev/null
+++ b/pages/world.js
@@ -0,0 +1,14 @@
+import { Canvas } from "@react-three/fiber";
+import Experience from "../components/Experience";
+
+const world = () => {
+ return (
+ <>
+
+ >
+ )
+}
+
+export default world
\ No newline at end of file
diff --git a/public/Model/Soldier.glb b/public/Model/Soldier.glb
new file mode 100644
index 0000000..1788f12
Binary files /dev/null and b/public/Model/Soldier.glb differ
diff --git a/public/Model/zworld.glb b/public/Model/zworld.glb
new file mode 100644
index 0000000..64c1ed8
Binary files /dev/null and b/public/Model/zworld.glb differ
diff --git a/styles/globals.css b/styles/globals.css
index 23d6a76..63ba0a0 100644
--- a/styles/globals.css
+++ b/styles/globals.css
@@ -150,3 +150,7 @@ a {
filter: blur(10px);
}
+canvas{
+ height: 100vh;
+ width: 100vw;
+}