Skip to content

Commit d89b07c

Browse files
fix: aim point updates
1 parent e4d3d61 commit d89b07c

3 files changed

Lines changed: 55 additions & 87 deletions

File tree

examples/courses/courses.js

Lines changed: 38 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ const gameContext = {
3131
aimPoint: new THREE.Vector3(0, 0, 0)
3232
};
3333

34-
let canvas;
35-
3634
const lights = {};
3735

3836
const defaultSkyColor = 'rgb(192, 215, 241)';
@@ -41,7 +39,7 @@ const lightColor = new THREE.Color('rgb(255, 247, 224)');
4139
let trackTimeout;
4240

4341
function launchShot(shot) {
44-
console.log('[DEBUG} Received new shot data:', shot);
42+
console.log('[DEBUG] Received new shot data:', shot);
4543
if (shot.ballSpeed && !gameContext.golfBall.isShotActive) {
4644
gameContext.shotData.updateShotData(shot);
4745
gameContext.golfBall.launchShot(shot);
@@ -53,58 +51,38 @@ function launchShot(shot) {
5351
}
5452

5553
function setupNextShot(playerStatus) {
56-
console.log('setupNextShot', playerStatus);
54+
console.log('Setup next shot with player', playerStatus);
5755

5856
gameContext.camera.setTracking(false);
59-
60-
// const startPoint = gameContext.game.startPoint();
6157
gameContext.startPoint.copy(gameContext.game.startPoint());
6258
gameContext.aimPoint.copy(gameContext.game.aimPoint());
6359

64-
// const aimPoint = gameContext.game.aimPoint();
65-
6660
gameContext.camera.setPositions(gameContext.startPoint, gameContext.aimPoint);
6761

6862
// recreate ball after each shot to ensure physics are fully reset
6963
gameContext.golfBall.reset(gameContext.aimPoint, gameContext.startPoint);
70-
// aimMarker.visible = true;
7164

72-
aimPointUpdated();
65+
aimPointUpdated(true);
7366

7467
gameContext.courseMap.updatePosition(gameContext.startPoint, gameContext.game.pinPoint());
7568
gameContext.courseMap.updateHole(gameContext.game.activeHole);
7669
gameContext.playerMenu.update(gameContext.game.currentPlayer());
7770
}
7871

79-
async function onProgress(progress) {
80-
document.getElementById('progress-bar-fill').style.width = `${progress.percent.toFixed(1)}%`;
81-
document.getElementById('progress-text').textContent = `${progress.percent.toFixed(0)}%`;
82-
}
83-
84-
function onHoleEnded() {
85-
console.log('onHoleEnded[main]');
86-
gameContext.playerMenu.update(gameContext.game.currentPlayer());
87-
}
88-
89-
function onShotEnded() {
90-
console.log('onShotEnded[main]');
91-
gameContext.playerMenu.update(gameContext.game.currentPlayer());
92-
}
93-
94-
function setupWorld() {
95-
canvas = document.getElementById('canvas');
96-
gameContext.renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
72+
function setupRenderer() {
73+
gameContext.renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('canvas'), antialias: true });
9774
gameContext.renderer.setSize(window.innerWidth, window.innerHeight);
9875
gameContext.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1));
9976
gameContext.renderer.shadowMap.enabled = true;
10077
gameContext.renderer.shadowMap.type = THREE.PCFShadowMap;
10178
}
10279

103-
// helpers, move to core?
10480
function setupScene() {
10581
const skyType = gameContext.course?.sceneSettings?.sky?.type;
10682
const clouds = gameContext.course?.sceneSettings?.sky?.clouds;
107-
console.log('clouds', skyType, clouds);
83+
84+
// Base scene
85+
// TODO: move to course loader?
10886
gameContext.scene = new THREE.Scene();
10987
gameContext.scene.background = new THREE.Color(clouds?.skyColor ?? defaultSkyColor);
11088

@@ -114,18 +92,23 @@ function setupScene() {
11492
gameContext.lightGroup = new CourseLight(lightColor);
11593
gameContext.scene.add(gameContext.lightGroup);
11694

95+
// Main Camera
11796
gameContext.camera = new ShotPerspectiveCamera(gameContext.renderer, gameContext.course.getGroundMeshes(), {
11897
cameraOffsetX: -(gameContext.setupData.cameraOffset / 100),
11998
});
99+
100+
// Aim point
120101
gameContext.visualAimPoint = new AimPoint(gameContext.camera, {
121102
units: gameContext.setupData.units
122103
});
123104
gameContext.scene.add(gameContext.visualAimPoint.object);
124105

106+
// Course Map
125107
gameContext.courseMap = new UICourseMap({
126108
units: gameContext.setupData?.units
127109
});
128-
// gameContext.controls = new OrbitControls( gameContext.camera, gameContext.renderer.domElement );
110+
111+
// Controls
129112
gameContext.controls = new CourseKeyboardControls({ testShots: true });
130113
gameContext.controls.on('aim', aimKeys => {
131114
gameContext.camera.aimKeys = aimKeys;
@@ -134,6 +117,7 @@ function setupScene() {
134117
gameContext.controls.on('toggleStats', () => gameContext.stats.toggle());
135118

136119

120+
// TODO: move to course loader..
137121
// Sky/Clouds
138122
gameContext.clouds = new VolumetricClouds(gameContext.camera, {
139123
radius: 800,
@@ -143,61 +127,45 @@ function setupScene() {
143127
skyColor: new THREE.Color(clouds?.skyColor ?? defaultSkyColor),
144128
cloudColor: clouds?.cloudColor && new THREE.Color(clouds?.cloudColor),
145129
fogColor: clouds?.fogColor && new THREE.Color(clouds?.fogColor),
146-
// position: new THREE.Vector3(0, -50, 0)
147130
position: new THREE.Vector3(clouds?.position ?? [0, -50, 0])
148131
});
149132
gameContext.scene.add(gameContext.clouds.object);
150133

151-
// gameContext.playerMenu = new UIPlayerMenu('#top-left');
152134
gameContext.shotData = new UIShotData('#shot-data', { units: gameContext.setupData?.units });
153135
gameContext.rangeFinder = new UIRangeFinder('#top-center', { units: gameContext.setupData?.units });
154136
gameContext.playerMenu = new UIPlayerMenu('#top-left');
155137
}
156138

157-
function getObjectBounds(obj) {
158-
const box = new THREE.Box3().setFromObject(obj);
159-
const center = new THREE.Vector3();
160-
box.getCenter(center);
161-
const size = new THREE.Vector3();
162-
box.getSize(size);
163-
return { size, center };
164-
}
165-
166-
// async function setupCourse() {
167-
168-
// }
169-
170-
171139
/**
172140
* Manually place ball
173141
*/
174-
function adjustStartPoint(event) {
175-
const newPosition = event.detail;
142+
function adjustStartPoint(newPosition) {
176143
console.log('update start', newPosition);
177144
const ground = gameContext.course.getGroundY(newPosition.x, newPosition.z);
178145
if (ground) {
179146
newPosition.y = ground.y;
180147
}
181148
gameContext.game.updateStartPoint(newPosition);
182-
console.log('UPDATE START POINT');
183149
setupNextShot();
184150
}
185151

186-
function adjustAimPoint(event) {
187-
const newPosition = event.detail;
152+
/**
153+
* Adjust the aim point
154+
*/
155+
function adjustAimPoint(newPosition) {
188156
// console.log('update aim', newPosition);
189157
const ground = gameContext.course.getGroundY(newPosition.x, newPosition.z);
190158
if (ground) {
191159
newPosition.y = ground.y;
192160
}
193-
// console.log('find ground', newPosition);
194161
gameContext.aimPoint.copy(newPosition);
195-
gameContext.camera.setPositions(gameContext.game.startPoint(), gameContext.aimPoint);
196-
console.log('UPDATE AIM POINT');
197-
198-
aimPointUpdated();
162+
gameContext.camera.setPositions(gameContext.game.startPoint(), gameContext.aimPoint);
163+
aimPointUpdated(true);
199164
}
200165

166+
/**
167+
* Called after the aim point has changed
168+
*/
201169
function aimPointUpdated(forced = false) {
202170
gameContext.distanceToAim = gameContext.startPoint.distanceTo(gameContext.aimPoint);
203171
gameContext.heightToAim = gameContext.startPoint.y - gameContext.aimPoint.y;
@@ -206,9 +174,11 @@ function aimPointUpdated(forced = false) {
206174
if (forced) {
207175
gameContext.visualAimPoint.reset(gameContext.aimPoint);
208176
}
209-
// console.log('AIM POINT UPDATED');
210177
}
211178

179+
/**
180+
* Generates setup data for testing
181+
*/
212182
function testSetupData() {
213183
const clubs = [
214184
{ fullName: 'Driver', name: 'DR', id: 'DR', distance: 180 },
@@ -241,19 +211,11 @@ function testSetupData() {
241211
}
242212

243213

244-
async function initialize(payload) {
214+
async function handleSetup(payload) {
215+
console.log('Received setup event', payload);
245216
gameContext.setupData = payload?.setupData;
246217
gameContext.gameData = payload?.gameData;
247-
console.log('SETUP RECEIVED', payload);
248218
preLoad();
249-
250-
// test data
251-
// const courseUrl = './MountainVista.glb';
252-
// // pretend we're given a URL and setupData
253-
// const setupData = testSetupData();
254-
// await setupCourse(payload.gameData.courseUrl, payload.setupData);
255-
// console.log('POST SETUP');
256-
// postLoad();
257219
}
258220

259221
let isLoaded = false;
@@ -266,7 +228,7 @@ async function setupFullCourse() {
266228
if (!gameContext?.gameData?.courseUrl) {
267229
throw new Error('Missing a courseUrl to a GLB in the gameData object');
268230
}
269-
setupWorld();
231+
setupRenderer();
270232

271233
// load course details and meshes
272234
gameContext.course = new CourseLoader(app.world, app.rapier, gameContext.setupData, gameContext.loadingScreen.manager);
@@ -278,7 +240,6 @@ async function setupFullCourse() {
278240

279241
setupScene();
280242

281-
const bounds = getObjectBounds(gameContext.course.scene);
282243
gameContext.scene.add(gameContext.course.scene);
283244

284245
gameContext.golfBall = new GolfBall(gameContext.scene, app.world, app.rapier, {
@@ -291,8 +252,8 @@ async function setupFullCourse() {
291252
setupNextShot();
292253

293254
// gameContext.playerMenu.update(gameContext.game.currentPlayer());
294-
gameContext.courseMap.addEventListener('updateAim', adjustAimPoint);
295-
gameContext.courseMap.addEventListener('updateStart', adjustStartPoint);
255+
gameContext.courseMap.on('updateAim', adjustAimPoint);
256+
gameContext.courseMap.on('updateStart', adjustStartPoint);
296257

297258
}
298259

@@ -373,15 +334,16 @@ async function initializeDebug() {
373334
}
374335
}
375336

376-
/**
377-
* Required setup for OpenGolfSim FUSE
378-
*/
337+
// -- Required setup --
338+
//
379339
// listen for setup event from OpenGolfSim app
380-
app.on('setup', initialize);
340+
app.on('setup', handleSetup);
381341
// listen for shot event from OpenGolfSim app
382342
app.on('shot', launchShot);
343+
383344
// initialize must be called before engaging physics/world
384345
app.initialize(() => {
346+
// if we passed a test course URL as a query param, we start in debug mode
385347
if (window.location.search) {
386348
initializeDebug();
387349
}

src/objects/aimPoint.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,12 @@ export class AimPoint {
3838

3939
this.scaleFactor = 0.004;
4040
this.opacity = 0.98;
41-
this.fadeDistance = [60, 20];
41+
this.fadeDistance = [21, 20];
4242
this.color = new THREE.Color(colors.background);
4343
this.#pointHeight = 5;
4444
this.#pointOffsetY = 0.25;
45-
this.#panelWidth = 10;
46-
this.#panelHeight = 5;
45+
this.#panelWidth = 9;
46+
this.#panelHeight = 4;
4747
this.#panelAspect = this.#panelHeight / this.#panelWidth;
4848

4949
this.canvas = document.createElement('canvas');
@@ -115,16 +115,17 @@ export class AimPoint {
115115
#updateDistanceMaterial(distance: number, height: number, units: string) {
116116

117117
this.ctx.fillStyle = colors.background;
118-
this.ctx.roundRect(0, 0, this.canvas.width, this.canvas.height, 30); // 20px radius for all corners
119-
this.ctx.fill();
118+
// this.ctx.roundRect(0, 0, this.canvas.width, this.canvas.height, (30 * this.#pixelRatio)); // 20px radius for all corners
119+
// this.ctx.fill();
120+
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
120121
this.ctx.fillStyle = 'white';
121-
const fontSize = 100 * this.#pixelRatio;
122+
const fontSize = 80 * this.#pixelRatio;
122123
this.ctx.font = this.#fontStyle(fontSize, '800');
123124
this.ctx.textBaseline = 'middle';
124125
this.ctx.textAlign = 'center';
125126
this.ctx.fillText(`${distance.toFixed(0)} ${units}`, this.canvas.width/2, (this.canvas.height/2) - (30 * this.#pixelRatio));
126127

127-
const fontSizeHeight = 60 * this.#pixelRatio;
128+
const fontSizeHeight = 50 * this.#pixelRatio;
128129
this.ctx.font = this.#fontStyle(fontSizeHeight, '400');
129130
const prefix = height >= 0.1 ? '+' : '';
130131
this.ctx.fillStyle = '#aaa';

src/ui/UICourseMap.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as THREE from 'three';
2+
import EventEmitter from 'eventemitter3';
23
import styles from '@/css/ui.module.css';
34
import { Hole } from '@/courses/types';
45
import { UnitConversions } from '@/utils/units';
@@ -8,8 +9,12 @@ type UICourseMapOptions = {
89
mapWidthPercent?: number;
910
units?: OpenGolfSim.MeasurementUnits;
1011
}
12+
interface UICourseMapsEvents {
13+
updateAim: (position: THREE.Vector3) => void;
14+
updateStart: (position: THREE.Vector3) => void;
15+
}
1116

12-
export class UICourseMap extends EventTarget {
17+
export class UICourseMap extends EventEmitter {
1318
mapWidthPercent: number;
1419
camera: THREE.OrthographicCamera;
1520
renderer: THREE.WebGLRenderer;
@@ -195,7 +200,7 @@ export class UICourseMap extends EventTarget {
195200
};
196201
}
197202

198-
_minimapToWorld(event: PointerEvent) {
203+
_minimapToWorld(event: PointerEvent): THREE.Vector3 {
199204
const rect = this.overlayCanvas.getBoundingClientRect();
200205
// Convert click to normalized device coords (-1 to 1)
201206
const ndc = new THREE.Vector3(
@@ -214,9 +219,9 @@ export class UICourseMap extends EventTarget {
214219
console.log('POS', pos);
215220

216221
if (event.shiftKey) {
217-
this.dispatchEvent(new CustomEvent('updateStart', { detail: pos }));
222+
this.emit('updateStart', pos);
218223
} else {
219-
this.dispatchEvent(new CustomEvent('updateAim', { detail: pos }));
224+
this.emit('updateAim', pos);
220225
}
221226
}
222227

0 commit comments

Comments
 (0)