Skip to content

Commit

Permalink
ThreeGraphics - Raycast pointer position and connect to game events
Browse files Browse the repository at this point in the history
Update demos for better 3d support
  • Loading branch information
Marak committed Mar 14, 2024
1 parent f35f378 commit 585ba18
Show file tree
Hide file tree
Showing 11 changed files with 198 additions and 119 deletions.
5 changes: 3 additions & 2 deletions mantra-game/Game.js
Original file line number Diff line number Diff line change
Expand Up @@ -348,8 +348,9 @@ class Game {
// not implemented directly, Graphics plugin will hoist this
}

setZoom() { // TODO: remove setZoom, use delegation to camera.zoom() instead of hoisting
setZoom(zoomScale) { // TODO: remove setZoom, use delegation to camera.zoom() instead of hoisting
// not implemented directly, Graphics plugin will hoist this
this.data.camera.currentZoom = zoomScale; // save the current zoom scale regardless
}

setCameraMode(mode) {
Expand Down Expand Up @@ -574,7 +575,7 @@ class Game {
game.viewportCenterOffsetY = 0;

// defaults camera back to 1x zoom
game.zoom(1);
// game.zoom(1);

}

Expand Down
2 changes: 0 additions & 2 deletions mantra-game/lib/Game/defaults/defaultTopdownMovement.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,10 @@ export default function topdownMovement(game) {
game.shakeCamera(1000);
});
rules.on('ZOOM_IN', function (entity) {
alert('default zoom in')
let currentZoom = game.data.camera.currentZoom || 1;
game.setZoom(currentZoom + 0.05);
});
rules.on('ZOOM_OUT', function (entity) {
alert('default zoom out')
let currentZoom = game.data.camera.currentZoom || 1;
game.setZoom(currentZoom - 0.05);
});
Expand Down
2 changes: 1 addition & 1 deletion mantra-game/plugins/block/Block.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ class Block {
if (typeof entityData.position === 'undefined') {
entityData.position = { x: 0, y: 0 };
}

return {
type: 'BLOCK',
mass: 50000,
texture: entityData.texture || 'tile-block',
size: entityData.size || { width: this.width, height: this.height },
position: entityData.position,
Expand Down
9 changes: 0 additions & 9 deletions mantra-game/plugins/flame/Flame.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export default class Flame {

init(game) {
this.game = game;
this.bindEvents();
this.game.systemsManager.addSystem('flame', this);
}

Expand Down Expand Up @@ -65,13 +64,5 @@ export default class Flame {

}

bindEvents() {
// TODO: move pointerDown event into Sutra
this.game.on('pointerDown', (entity, ev) => {
if (entity.type === 'FLAME') {
game.playNote('G4');
}
});
}

}
149 changes: 144 additions & 5 deletions mantra-game/plugins/graphics-three/ThreeGraphics.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// ThreeGraphics.js - Marak Squires 2023
import { MOUSE, Scene, WebGLRenderer, PerspectiveCamera, HemisphereLight, Vector3 } from 'three';
import { MOUSE, Scene, WebGLRenderer, PerspectiveCamera, HemisphereLight, Vector3, Vector2, Raycaster, Plane } from 'three';
import { OrbitControls } from '../../vendor/three/examples/jsm/controls/OrbitControls.js';
import { FontLoader } from '../../vendor/three/examples/jsm/loaders/FontLoader.js';

Expand Down Expand Up @@ -30,6 +30,12 @@ class ThreeGraphics extends GraphicsInterface {
};
this.config = config;

this.mouseButtons = {
LEFT: null,
RIGHT: null,
MIDDLE: null
};

// Set default camera configuration or use the provided one
this.cameraConfig = cameraConfig || {
fov: 75,
Expand All @@ -44,7 +50,7 @@ class ThreeGraphics extends GraphicsInterface {
}

init(game) {

this.render = render.bind(this);
this.inflateGraphic = inflateGraphic.bind(this);
this.createGraphic = createGraphic.bind(this);
Expand All @@ -59,7 +65,7 @@ class ThreeGraphics extends GraphicsInterface {
this.game.threeReady = false; // TODO: remove this, check case in SystemManager for double plugin init
}

this.loadFont(()=>{
this.loadFont(() => {
this.threeReady(game);
});

Expand All @@ -71,7 +77,6 @@ class ThreeGraphics extends GraphicsInterface {
return;
}
this.game.threeReady = true;

// get the three-render-canvas, if exists, clear it
let canvas = document.getElementById('three-render-canvas');
if (canvas) {
Expand Down Expand Up @@ -141,11 +146,145 @@ class ThreeGraphics extends GraphicsInterface {
// game.emit('plugin::ready::graphics-three', this);
game.graphics.push(this);

this.raycaster = new Raycaster();


//this.renderer.domElement.addEventListener('pointerdown', this.onPointerDown.bind(this), false);


// remove the default mouse pointerdown handler

this.renderer.domElement.addEventListener('pointermove', (event) => {
event.preventDefault();
// emit the pointer move event
const gameCoordinates = this.getGameCoordinates(event, this.camera);
// console.log('gameCoordinates', gameCoordinates)
game.emit('pointerMove', {
position: {
x: -gameCoordinates.x,
y: -gameCoordinates.z // mapped to 2d screen space
},
buttons: {},
}, event)
});

this.renderer.domElement.addEventListener('pointerup', (event) => {
event.preventDefault();

// TODO: better buttons control, use Mouse.js code ( shared )
/*
if (event.buttons === 1) {
this.mouseButtons.LEFT = false;
} else if (event.buttons === 2) {
this.mouseButtons.RIGHT = false;
} else if (event.buttons === 4) {
this.mouseButtons.MIDDLE = false;
}
*/
this.mouseButtons.LEFT = false;
this.mouseButtons.RIGHT = false;
this.mouseButtons.MIDDLE = false;

const gameCoordinates = this.getGameCoordinates(event, this.camera);
game.emit('pointerUp', {
position: {
x: -gameCoordinates.x,
y: -gameCoordinates.z // mapped to 2d screen space
},
buttons: this.mouseButtons,
}, event)
});

this.renderer.domElement.addEventListener('pointerdown', (event) => {
event.preventDefault();

const gameCoordinates = this.getGameCoordinates(event, this.camera);

this.mouseButtons.LEFT = false;
this.mouseButtons.RIGHT = false;
this.mouseButtons.MIDDLE = false;

// check event for buttons and map the this.mouseButtons
// TODO: remove this and move to Mouse.js with refactor
// console.log("event.buttons", event.buttons)
if (event.buttons === 1) {
this.mouseButtons.LEFT = true;
} else if (event.buttons === 2) {
this.mouseButtons.RIGHT = true;
} else if (event.buttons === 4) {
this.mouseButtons.MIDDLE = true;
}

//console.log('Game Coordinates:', gameCoordinates);
//console.log("three pointerdown", this.mouseButtons)
// TODO: should create mouse context using same code from Mouse.js / share the function
game.emit('pointerDown', {
position: {
x: -gameCoordinates.x,
y: -gameCoordinates.z // mapped to 2d screen space
},
buttons: this.mouseButtons,
}, event)
});

window.addEventListener('resize', this.onWindowResize.bind(this), false);

document.body.style.cursor = 'default';
}

// Function to get the game coordinates of a click/tap
getGameCoordinates(event, camera, plane = new Plane(new Vector3(0, 1, 0), 0)) {
const mouse = new Vector2();
const raycaster = new Raycaster();

// Convert the mouse position to normalized device coordinates (-1 to +1)
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

// Update the picking ray with the camera and mouse position
raycaster.setFromCamera(mouse, camera);

// Calculate the intersection of the picking ray with the ground plane
const intersectPoint = new Vector3();
raycaster.ray.intersectPlane(plane, intersectPoint);

// Return the intersection point as the game coordinates
return intersectPoint;
}

onPointerDown(event) {

// console.log("onPointerDown", event.clientX, event.clientY);
// Calculate mouse position in normalized device coordinates (-1 to +1) for both components
const mouse = new Vector3();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

// Update the picking ray with the camera and mouse position
this.raycaster.setFromCamera(mouse, this.camera);

// Calculate objects intersecting the picking ray
const intersects = this.raycaster.intersectObjects(this.scene.children, true);
// console.log('intersects', intersects);

if (intersects.length > 0) {
// Handle the first intersected object (closest to the camera)
const intersectedObject = intersects[0].object;
const intersectPoint = intersects[0].point; // World coordinate position of the intersection

// console.log("Intersected at", intersectPoint);

// Provide intersected object context to your ECS
const entityId = intersectedObject.userData.entityId;

if (entityId) {
// console.log("Entity clicked:", entityId);
// this.handleEntityClick(entityId, intersectPoint);
// see Mouse.js, use shared code
}
}
}

setCameraDefaults() {
// this.camera.position.set(0, 0, 100); // Set a default position
// this.setZoom(1); // Set a default zoom level
Expand Down Expand Up @@ -315,7 +454,7 @@ class ThreeGraphics extends GraphicsInterface {
setupZoomControls() {
const zoomSensitivity = -0.05; // Adjust this value based on desired zoom speed
const minFov = 20; // Minimum FOV
const maxFov = 150; // Maximum FOV, you can adjust this value
const maxFov = 120; // Maximum FOV, you can adjust this value

this.renderer.domElement.addEventListener('wheel', (event) => {
// Adjust the camera's FOV
Expand Down
34 changes: 21 additions & 13 deletions mantra-worlds/GravityGardens/GravityGardens.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ class GravityGardens {

game.setGravity(0, 0, 0);
game.setSize(800, 600);
game.setBackground('black');

game.createBorder({
thickness: 20,
collisionStart: function (a, b, pair, context) {
Expand Down Expand Up @@ -152,7 +154,7 @@ class GravityGardens {
let game = this.game;
// mouse drops particles logic
if (this.dropping && game.tick % 3 === 0) {
// console.log('dropping', mousePosition.x, mousePosition.y);
// console.log('dropping', this.mousePosition.x, this.mousePosition.y);
let randomRadialPosition = game.randomPositionRadial(this.mousePosition.x, this.mousePosition.y, 15);
let randomColor = game.randomColor();
// create burst of particles at this position
Expand Down Expand Up @@ -196,27 +198,33 @@ class GravityGardens {
that.mousePosition = position;

// adjust position for game camera offset
that.mousePosition.x = that.mousePosition.x - game.data.camera.offsetX;
that.mousePosition.y = that.mousePosition.y - game.data.camera.offsetY;
// Removed: 3/13/2024, not using zoom anymore, can change this to be part of Mouse context
// was causing minor conflict with Three.js pointer events
//that.mousePosition.x = that.mousePosition.x - game.data.camera.offsetX;
//that.mousePosition.y = that.mousePosition.y - game.data.camera.offsetY;
//that.mousePosition.clientX = event.clientX;
//that.mousePosition.clientY = event.clientY;

that.mousePosition.clientX = event.clientX;
that.mousePosition.clientY = event.clientY;
// if right click
if (event.button === 2) {}

// if left click
if (event.button === 0) {
that.dropping = true;
game.pingPosition(event.clientX, event.clientY, 1, { color: 'white', duration: 1500, size: 25, finalSize: 100, borderWidth: 3 });
//game.pingPosition(event.clientX, event.clientY, 1, { color: 'white', duration: 1500, size: 25, finalSize: 100, borderWidth: 3 });
that.slurping = true;
game.pingPosition(event.clientX, event.clientY, 1, { reverse: true, color: 'red', duration: 1500, size: 25, finalSize: 100, borderWidth: 3 });
//game.pingPosition(event.clientX, event.clientY, 1, { reverse: true, color: 'red', duration: 1500, size: 25, finalSize: 100, borderWidth: 3 });
}
});

game.on('pointerMove', function (position, event) {
that.mousePosition = position;
game.on('pointerMove', function (context, event) {
that.mousePosition = context.position;

// css graphics only
/*
that.mousePosition.x = that.mousePosition.x - game.data.camera.offsetX;
that.mousePosition.y = that.mousePosition.y - game.data.camera.offsetY;
*/
});

}
Expand All @@ -237,7 +245,7 @@ class GravityGardens {
type: 'PARTICLE',
color: 0xf03025,
isSensor: true,
position: { x: 200, y: 0 },
position: { x: 200, y: 0, z: 1 },
}
})
.color(0xf03025)
Expand All @@ -256,7 +264,7 @@ class GravityGardens {
type: 'PARTICLE',
color: 0x14b161,
isSensor: true,
position: { x: -200, y: 0 },
position: { x: -200, y: 0, z: 1 },
}
})
.color(0x14b161)
Expand All @@ -275,7 +283,7 @@ class GravityGardens {
type: 'PARTICLE',
color: 0x3c62f8,
isSensor: true,
position: { x: 0, y: -200 },
position: { x: 0, y: -200, z: 1 },
}
})
.color(0x3c62f8)
Expand All @@ -295,7 +303,7 @@ class GravityGardens {
type: 'PARTICLE',
color: 0xe9dd34,
isSensor: true,
position: { x: 0, y: 200 },
position: { x: 0, y: 200, z: 1 },
}
})
.color(0xe9dd34)
Expand Down
4 changes: 2 additions & 2 deletions mantra-worlds/Home/Home.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,8 @@ class Home {

createTwinFlames() {
// See Flame plugin for .build() entity config
this.game.make().Flame().position(-80, -60, 16).createEntity();
this.game.make().Flame().position(80, -60, 16).createEntity();
this.game.make().Flame().position(-80, -60, 1).createEntity();
this.game.make().Flame().position(80, -60, 1).createEntity();
}

unload() {
Expand Down
Loading

0 comments on commit 585ba18

Please sign in to comment.