Controlling a first person shooter game using three.js

I'm a beginner in three.js and 3D design, aiming to create a simple first-person shooter game. While I've come across several examples, they seem too complex for me to grasp. I prefer to understand the code thoroughly before implementing it. My main challenge lies in the rotation of the camera. Although I've set the z-axis to 0, it still rotates on that axis. Below is the complete 125-line code I've been working on:

var width    = window.innerWidth, height = window.innerHeight,
    scene    = new THREE.Scene(),
    camera   = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000),
    renderer = new THREE.WebGLRenderer(),
    mouse = {
      x: 0,
      y: 0,
      movedThisFrame: false
    };
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);

var geometry = new THREE.BoxGeometry(1, 1, 1);
var material = new THREE.MeshBasicMaterial({
  color: 0x00ff00
});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
var floorgeometry = new THREE.BoxGeometry(100,0.1,100);
var floormaterial = new THREE.MeshBasicMaterial({
  color: 0xff0000
});
var floor = new THREE.Mesh(floorgeometry, floormaterial);
floor.position.y = -1;
scene.add(floor);

var keys = {
  w: false,
  a: false,
  s: false,
  d: false
};

camera.position.z = 5;

function radDeg(radians) {
  return radians * 180 / Math.PI;
}

function degRad(degrees) {
  return degrees * Math.PI / 180;
}

function rotateCam() {
  if (!mouse.movedThisFrame) {
    mouse.x = 0;
    mouse.y = 0;
  }
  /*

  The issue seems to be here.

  */
  camera.rotation.x -= mouse.y * 0.001;
  camera.rotation.y -= mouse.x * 0.001;
  camera.rotation.z = 0;

  mouse.movedThisFrame = false;
}

function moveCam() {
  var rotation = camera.rotation.y % (Math.PI * 2), motion = [0,0];
  if (keys.w) {
    motion[0] += 0.1 * Math.cos(rotation);
    motion[1] += 0.1 * Math.sin(rotation);
  }
  if (keys.a) {
    motion[0] += 0.1 * Math.cos(rotation + degRad(90));
    motion[1] += 0.1 * Math.sin(rotation + degRad(90));
  }
  if (keys.s) {
    motion[0] += 0.1 * Math.cos(rotation - degRad(180));
    motion[1] += 0.1 * Math.sin(rotation - degRad(180));
  }
  if (keys.d) {
    motion[0] += 0.1 * Math.cos(rotation - degRad(90));
    motion[1] += 0.1 * Math.sin(rotation - degRad(90));
  }

  camera.position.z -= motion[0];
  camera.position.x -= motion[1];
}

window.onload = function() {
  renderer.domElement.onclick = function() {
    console.log('requested pointer lock');
    renderer.domElement.requestPointerLock();
  };

  renderer.domElement.onmousemove = function(e) {
    if (!mouse.movedThisFrame) {
      mouse.x = e.movementX;
      mouse.y = e.movementY;
      mouse.movedThisFrame = true;
    }
  };

  document.onkeydown = function(e) {
    var char = String.fromCharCode(e.keyCode);
    if (char == 'W')
      keys.w = true;
    else if (char == 'A')
      keys.a = true;
    else if (char == 'S')
      keys.s = true;
    else if (char == 'D')
      keys.d = true;
  };

  document.onkeyup = function(e) {
    var char = String.fromCharCode(e.keyCode);
    if (char == 'W')
      keys.w = false;
    else if (char == 'A')
      keys.a = false;
    else if (char == 'S')
      keys.s = false;
    else if (char == 'D')
      keys.d = false;
  };

  function animate() {
    requestAnimationFrame(animate);
    rotateCam();
    moveCam();
    renderer.render(scene, camera);
  }
  animate();
};

The issue can be found in the rotateCam function, and I'm uncertain about the reason behind it.

Furthermore, I attempted to use the code provided in this post, but it didn't deliver the expected outcome.

Answer №1

Mastering first person controls requires more skill than meets the eye. Despite understanding the angle math, unsecured pointers can hit window edges and abruptly halt turning.

My recommendation is to delve into the pointer lock example () for a hands-on experience of first person controls in 3js.

Answer №2

Check out my innovative demo solution for a first-person shooter game using Three.js. This project showcases the usage of the PointerLockControls class.

<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Exciting Three.js Examples - First Person Shooter Game Starter</title>
    <style>
        @font-face {
            font-family: 'Robus-BWqOd';
            src: url('https://www.shanebrumback.com/fonts/Robus-BWqOd.otf') format('opentype');
        }

        body {
            margin: 0;
        }

        canvas {
            display: block;
        }

        #blocker {
            position: fixed;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.5);
        }

        #instructions {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: white;
        }

        #crosshair {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 100px;
            height: 100px;
            display: none; /* Crosshair hidden by default */
        }

        #playButton {
            font-family: 'Robus-BWqOd';
            font-size: 5vw;
            color: white;
            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.75);
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            white-space: nowrap;
        }

        p {
            font-family: Arial;
            font-size: medium;
            text-align: center;
        }

        @media (max-width: 900px) {
            /* Styles for mobile with a max width of 767px */
            #playButton {
                font-family: 'Robus-BWqOd';
                font-size: 15vw; 
            }
            p {
                font-size: 4vw;
            }
        }

    </style>
</head>
<body>
    <div id="blocker">
        <div id="instructions">
            <div id="playButton">
                Play Now
                <p>
                    ESC - Menu
                    <br />
                    WASF ARROWS - Move
                    <br />
                    LEFT MOUSE  - Fire
                    <br />
                    SPACEBAR  - Fire
                    <br />
                    M - Play / Pause Music
                </p>
            </div>
        </div>
    </div>
    <img id="crosshair" src="https://www.shanebrumback.com/images/reticle.png" alt="Crosshair">

    <script src="https://cdn.jsdelivr.net/npm/three@latest/build/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@latest/examples/js/controls/OrbitControls.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@latest/examples/js/controls/PointerLockControls.js"></script>
    
    <script type="module">

        /* Remaining JavaScript code and functionalities go here */

    </script>

</body>
</html>

Answer №3

To achieve this, you must utilize the following code:

euler.setFromQuaternion( camera.quaternion );
euler.y -= e.movementX/500;
euler.x -= e.movementY/500;
camera.quaternion.setFromEuler( euler );

(feel free to adjust the value of 500)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is preventing me from accessing the sub-elements of an AngularJS controller?

I know this question has probably been asked countless times before, but I'm struggling to find the right words to explain my specific issue. Essentially, I'm having trouble accessing something like $ctrl.thing.subelement. However, the following ...

Transmit intricate data structure to a web API endpoint using AJAX requests

When attempting to send a complex object named vm containing an object 'Budget' and an array 'BudgetDetails' populated with rows from an HTML table to my API controller using AJAX, I encounter the error message "Uncaught RangeError: Max ...

Is there a way to delay the closing of the browser until the server call is finished?

I am attempting to invoke a service that updates data upon browser close or tab close. The issue I am facing is that the service call uses $http and is asynchronous, so I believe that the browser unload event is cancelling the call and closing the browser. ...

transfer information between different express middleware functions within a universal react application

I am working on an isomorphic react app and I am looking for a way to pass state between express middleware functions. One of my express routes handles form submission: export const createPaymentHandler = async (req: Request, res: Response, next: NextFun ...

What is the best way to indicate a particular element within a subdocument array has been altered in mongoose?

I have a specific structure in my Mongoose schema, shown as follows: let ChildSchema = new Schema({ name:String }); ChildSchema.pre('save', function(next){ if(this.isNew) /*this part works correctly upon creation*/; if(this.isModifi ...

Deleting the HTML element

Can someone assist me with my script issue? I am facing a problem: when I open my file on fullscreen (over 768px), and move my pointer around the logo div, nothing happens. However, if I resize my browser to below 768px, then back again above 768px, the ...

Display personalized content over images utilizing the jQuery galleria plugin

Hey there, I've been successfully using the jquery galleria plugin, but now I'm looking to add some custom content and links that will display in the center of the image. Is it possible to achieve this with galleria? Your insights would be greatl ...

How to simultaneously update two state array objects in React

Below are the elements in the state array: const [items, setItems] = useState([ { id: 1, completed: true }, { key: 2, complete: true }, { key: 3, complete: true } ]) I want to add a new object and change the ...

I am unable to log the response, but I can view it in Postman and the network tab

Currently, I am working on setting up a register feature with a specific endpoint. The challenge I am facing revolves around handling validation responses from the backend. For instance, if I attempt to register with a username that is less than six charac ...

Allow Microsoft OAuth authentication for web applications only, restricting access to other Microsoft services

I am currently integrated Firebase into my website, allowing users to sign in using their Microsoft accounts through OAuth 2.0: import {getAuth, signInWithRedirect, OAuthProvider} from "firebase/auth"; (...) const provider = new OAuthProvider(& ...

if the response from the AJAX request contains

I need to search for a specific word in the response text and then perform some actions based on that, but I am only getting a "true" response from PHP. How can I achieve this functionality? function checkCellNo() { var cellNumber = _("CellNo"). ...

What is the process for utilizing datePipe in an Angular component?

How can I implement DatePipe in my Angular component? This is the code snippet that I am currently using. for (let i = 0; i < this.days.length; i++) { this.storeStart(this.days[i], null, null, null); } I have stored weekdays (Monday to Frid ...

What are some techniques for ensuring a CSS div remains stable and intact when adjusting the browser size?

Is there a way to prevent the entire website from resizing when you minimize or maximize your browser? I want the website size to adjust in proportion to your resize without reorganizing everything. How can this be achieved while maintaining the site lengt ...

What is the best way to retrieve the src value upon clicking on an image in a JavaScript function with multiple images displayed on

I have 3 images on my website. Each time I click on an image, I want to retrieve the source value. I attempted the code below, but it doesn't seem to work with multiple images. <div class="test"> <a href="#" class="part-one" ...

Having trouble with the page redirection issue? Here's how you can troubleshoot and resolve

My goal is to restrict access to both the user home and admin dashboard, allowing access only when logged in. Otherwise, I want to redirect users to the login or admin login page. import { NextResponse } from 'next/server' import { NextRequest } ...

Redux does not have the capability to insert an object into an array

I'm currently learning about redux and I've encountered an issue trying to add multiple objects into the initialState array. I attempted using the push() method, but it isn't working as expected. The submitter value is being passed to my act ...

The discrepancy in the array leads to a result of either 1 or an undetermined

Array x = [3,5,7,9,1] Array y = [3,7,8] z = x - y this should result in z = [5,9,1] (7 is common but I want it excluded) Below is the code snippet: function arrayDifference(x, y) { var arr = []; var difference = []; for (var i = 0; i<x.length& ...

The mystery of the unassigned value in $(this).data(value) when using the jQuery click() method

In my Rails 5 application, I am working on creating a dynamic menu that will guide users to different parts of the site based on their location. The idea is that when they click on a specific menu item from the home page, a modal will appear allowing them ...

To view the contents of the dropdown list on an iPhone, simply tap the arrow key next to the mobile dropdown list and the contents will be

I am attempting to trigger the dropdown list contents to open/show by tapping on the arrow keys near AutoFill on the iPhone mobile keyboard. This action should mimic clicking on the dropdown itself. Currently, I am working on implementing this feature for ...

JavaScript Object Instantiation within the Object Prototype

If we consider a scenario where there are 3 chapters of a novel each with its own unique URL structure: Chapter 1 = /1.html Chapter 2 = /2.html Chapter 3 = /3.html Now, to implement object-oriented principles using jQuery, the idea is to create two JS ...