When starting a project that's larger than a single example, how do you guys structure your code?

How do you guys structure your code? I’m trying to make it so it’s easier to manage as the project grows bigger. Basically I made it so everything else is a class with methods, like the Background and Button class. (Main.js should probably be a class too but I haven’t done it.) I currently have it setup like this for my entry point and I was wondering how other people are setting things up?

Also when removing stuff, is the best way to just group everything and remove the group?

import './style.css';
import * as THREE from 'three';
import Background from './background.js';
import Button from './button.js';

// three.js
let scene, camera, renderer;
let background, button;

// browser
let windowHalfX = window.innerWidth / 2;
let windowHalfY = window.innerHeight / 2;

let mouse = { x:0, y:0 };

main();

function main() {
    init();
    initScene();
    animate();

    initBrowser();
}

// three.js stuff
function init() {
    scene = new THREE.Scene();

    camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1000 );
    camera.position.z = 5;

    renderer = new THREE.WebGLRenderer({
        canvas: document.querySelector('#bg'),
    });
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
}

function initScene() {
    background = new Background( scene, camera );
    button = new Button( scene, camera, document );
}

function animate() {
    requestAnimationFrame(animate);

    background.render( mouse );
    button.render();


    renderer.render( scene, camera );
}

// browser events
function initBrowser() {
    document.body.style.touchAction = 'none';
    document.body.addEventListener( 'pointermove', onPointerMove );

    window.addEventListener( 'resize', onWindowResize );
}

function onPointerMove(e) {
    if (e.isPrimary === false) return;

    mouse.x = e.clientX - windowHalfX; // center of screen is zero
    mouse.y = e.clientY - windowHalfY; // center of screen is zero

    // console.log( 'mouseX: ', mouse.x, 'mouseY: ', mouse.y );
}

function onWindowResize() {
    windowHalfX = window.innerWidth / 2;
    windowHalfY = window.innerHeight / 2;

    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize(window.innerWidth, window.innerHeight);
}

I use classes with state.
In these different projects, I created a game class and then call its update method in the animation loop.

  1. Ball-VR/client.ts at main · Sean-Bradley/Ball-VR (github.com)
  2. First-Car-Shooter/client.ts at main · Sean-Bradley/First-Car-Shooter (github.com)
  3. Ballgame/client.ts · Sean-Bradley/Three.js-TypeScript-Boilerplate (github.com)
1 Like

I created a game class and then call its update method in the animation loop.

I’ve been doing something similar as well. :slight_smile:

I use classes with state.

Could you expand on what you mean by, “use classes with state”?

look at the first example.
Ball-VR/client.ts at dbc4c2747842d3ed20942c2013393b276b563754 · Sean-Bradley/Ball-VR (github.com)
See what game.update(delta) does.
It’s a rabbit hole.
But it’s a rabbit hole that fits the purpose of this 1 project only.
The state of any class, can contain a variable, an object, a function or even be a pointer to a variable, object or method of a different class. I do all those things in the first project. The second project is different from the others, and the third is as well.
There are many classes under the game class, all managing their own state, whatever that is, and whatever it points to.

1 Like

Ah, thank you for the clarification. I’ve actually been doing the same thing but didn’t realize you call that classes with state. Good to know!