Skip to content

Commit c625798

Browse files
committed
feat: add bound screen limit when enemy is created
1 parent fec7014 commit c625798

4 files changed

Lines changed: 68 additions & 13 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"build": "tsc && npx vite build",
88
"start": "npx vite --port 3000",
99
"test": "echo \"Error: no test specified\" && exit 1",
10-
"prettier:format": "prettier --write \"src/**/*.{ts,js,css,html}\" index.html"
10+
"prettier:format": "prettier --write \"src/**/*.{ts,js,css,html}\" \"!src/**/bootstrap.min.css\" index.html"
1111
},
1212
"keywords": [],
1313
"author": "Murillo Grübler",

src/Game.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,7 @@ export class Game {
2828
private initialNumberEnemies: number = 1;
2929
private speedIncreaseRate: number = 1.25;
3030
private sceneLimit: THREE.Vector3 = new THREE.Vector3(0, 0, 10);
31-
private limitsPositionEnemy = [
32-
new THREE.Vector3(-15, 0, -100),
33-
new THREE.Vector3(15, 0, -50),
34-
];
31+
private limitsPositionEnemy: THREE.Vector3[] = [];
3532
private inputStrategies: InputStrategy[] = [];
3633

3734
constructor(container: HTMLElement) {
@@ -55,6 +52,7 @@ export class Game {
5552
? CameraMode.Top
5653
: CameraMode.Default;
5754
await this.cameraComponent.load(this.scene, initialCameraMode);
55+
this.limitsPositionEnemy = Screen.recalculateEnemyLimits(this.cameraComponent.instance);
5856
this.inputStrategies = [
5957
new KeyboardInputStrategy(),
6058
new PointerInputStrategy(this.renderer.domElement, () =>

src/library/pages/css/styles.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ ul li a:hover {
267267
flex: 1;
268268
}
269269

270-
#game canvas{
270+
#game canvas {
271271
-webkit-user-select: none;
272272
-ms-user-select: none;
273273
user-select: none;

src/util/Screen.ts

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@ import { Bounds } from '../types/Bounds';
33

44
export class Screen {
55
/**
6-
* Checks if a pointer event happened on the left half of a given element.
7-
* @param {PointerEvent} event
8-
* @param {HTMLElement} element
9-
* @param {number} targetCenterX center position (in px, relative to the element) of the target to exclude
10-
* @param {number} targetWidth width (in px) of the target to exclude
11-
* @returns {boolean | null} true if left side, false if right side, null if inside the exclusion zone
6+
* Determines whether a pointer event occurred on the left half of an element,
7+
* with an optional exclusion zone around a central target.
8+
*
9+
* @param {PointerEvent} event - The pointer event to evaluate.
10+
* @param {HTMLElement} element - The element used as reference for relative coordinates.
11+
* @param {number} targetCenterX - X coordinate (in px, relative to the element) of the exclusion area's center.
12+
* @param {number} targetWidth - Width (in px) of the exclusion area.
13+
* @returns {boolean | null} Returns `true` if the event is on the left half, `false` if on the right half,
14+
* or `null` if the event falls within the exclusion zone.
1215
*/
1316
public static isLeftHalf(
1417
event: PointerEvent,
@@ -31,7 +34,14 @@ export class Screen {
3134
}
3235

3336
/**
34-
* Projects a 3D object's bounding box to screen space and returns its horizontal bounds.
37+
* Projects a 3D object's bounding box to screen space and returns its horizontal bounds in pixels.
38+
*
39+
* Note: This projection only considers the object's X-Z extents (Y set to 0) for horizontal bounds.
40+
*
41+
* @param {THREE.Object3D} object - The 3D object whose bounding box will be projected.
42+
* @param {THREE.Camera} camera - The camera used for projection.
43+
* @param {HTMLElement} element - The DOM element representing the render area (used for width in pixels).
44+
* @returns {Bounds} An object containing `centerX` (center in pixels) and `width` (width in pixels).
3545
*/
3646
public static getProjectedBoundsX(
3747
object: THREE.Object3D,
@@ -58,4 +68,51 @@ export class Screen {
5868
width: width,
5969
};
6070
}
71+
72+
/**
73+
* Recalculates horizontal spawn limits for enemies based on the camera and predefined Z depths.
74+
*
75+
* @param {THREE.PerspectiveCamera} camera - The perspective camera used to compute screen bounds at depth.
76+
* @returns {THREE.Vector3[]} An array with two `THREE.Vector3` entries: [leftSpawnPosition, rightSpawnPosition].
77+
*/
78+
public static recalculateEnemyLimits(camera: THREE.PerspectiveCamera): THREE.Vector3[] {
79+
const spawnZMax = -35;
80+
const spawnZMin = -45;
81+
const bounds = this.getScreenBoundsAtDepth(camera, spawnZMax);
82+
return [
83+
new THREE.Vector3(bounds.centerX - bounds.width / 2, 0, spawnZMin),
84+
new THREE.Vector3(bounds.centerX + bounds.width / 2, 0, spawnZMax),
85+
];
86+
}
87+
88+
/**
89+
* Calculate the world-space horizontal bounds (X) of the camera's view at a specific Z depth.
90+
*
91+
* This method unprojects normalized device coordinates (NDC) corners to world space,
92+
* intersects the camera ray with the plane at the given Z, and returns the center X and width.
93+
*
94+
* @private
95+
* @param {THREE.PerspectiveCamera} camera - The camera used to unproject screen corners.
96+
* @param {number} z - The world Z coordinate where bounds should be computed.
97+
* @returns {Bounds} An object with `centerX` (center X in world units) and `width` (width in world units).
98+
*/
99+
private static getScreenBoundsAtDepth(camera: THREE.PerspectiveCamera, z: number): Bounds {
100+
const ndcCorners = [new THREE.Vector2(-1, -1), new THREE.Vector2(1, 1)];
101+
const xs: number[] = [];
102+
103+
ndcCorners.forEach((corner) => {
104+
const clipPosition = new THREE.Vector3(corner.x, corner.y, 1).unproject(camera);
105+
const direction = clipPosition.sub(camera.position);
106+
const t = (z - camera.position.z) / direction.z;
107+
const intersection = camera.position.clone().add(direction.multiplyScalar(t));
108+
xs.push(intersection.x);
109+
});
110+
111+
const left = Math.min(...xs);
112+
const right = Math.max(...xs);
113+
return {
114+
centerX: (left + right) / 2,
115+
width: Math.abs(right - left),
116+
};
117+
}
61118
}

0 commit comments

Comments
 (0)