Skip to content

Commit a55bfeb

Browse files
committed
feat: enhance World component with floor interaction and visual indicators
- Added a transparent floor to the World component, allowing for player interaction. - Implemented hover and click event handlers to update player position and show/hide an indicator. - Introduced a custom shader for a gradient effect on the hover indicator. - Removed redundant hover indicator logic from GameCanvas component to streamline functionality.
1 parent 00bc6d1 commit a55bfeb

File tree

3 files changed

+95
-68
lines changed

3 files changed

+95
-68
lines changed

components/environments/World.vue

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
<script setup lang="ts">
22
import type { Group } from 'three'
3-
3+
import { MeshBasicMaterial } from 'three'
44
const { getResource } = useResourcePreloader()
5+
const gameStore = useGameStore()
56
67
const { scene: model_dungeon } = getResource('models', 'dungeon')
78
const { scene: model_lights } = getResource('models', 'lights')
89
const { scene: model_chests } = getResource('models', 'chests')
910
const { scene: model_doors } = getResource('models', 'doors')
1011
const { scene: model_spikes } = getResource('models', 'spikes')
11-
const { scene: model_visual_blocker } = getResource('models', 'visual_blocker')
12+
const { scene: model_visual_blocker, nodes: model_visual_blocker_nodes } = getResource('models', 'visual_blocker')
13+
14+
const floor = model_visual_blocker_nodes['room1001'].clone()
1215
16+
floor.material = new MeshBasicMaterial({
17+
transparent: true,
18+
opacity: 0,
19+
})
1320
const model_dungeon_ref = ref()
1421
const model_lights_ref = ref()
1522
const model_chests_ref = ref()
@@ -28,9 +35,58 @@ const { stop } = watch(model_visual_blocker_ref, (newValue: Group | undefined) =
2835
})
2936
stop()
3037
})
38+
39+
// Custom shader for gradient cylinder
40+
const cylinderShader = {
41+
uniforms: {
42+
color: { value: { r: 1.0, g: 1.0, b: 1.0 } },
43+
},
44+
vertexShader: `
45+
varying float vY;
46+
void main() {
47+
vY = position.y;
48+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
49+
}
50+
`,
51+
fragmentShader: `
52+
uniform vec3 color;
53+
varying float vY;
54+
void main() {
55+
float alpha = 1.0 - (vY + 0.5); // Gradient from bottom to top
56+
gl_FragColor = vec4(color, alpha * 0.5); // Semi-transparent
57+
}
58+
`,
59+
}
60+
61+
const showIndicator = ref(false)
62+
const hoverIndicatorRef = shallowRef()
63+
64+
65+
const handleFloorClick = (e: ThreeEvent<PointerEvent>) => {
66+
const newPosition = { x: e.point.x, y: 0, z: e.point.z }
67+
gameStore.setPlayerPosition(gameStore.players[0], newPosition)
68+
69+
}
70+
71+
const handleFloorHover = (e: ThreeEvent<PointerEvent>) => {
72+
showIndicator.value = true
73+
console.log('hoverIndicatorRef', hoverIndicatorRef.value)
74+
if (hoverIndicatorRef.value) {
75+
hoverIndicatorRef.value.position.set(e.point.x, 0.2, e.point.z)
76+
}
77+
}
78+
79+
const handleFloorLeave = () => {
80+
showIndicator.value = false
81+
}
82+
83+
watch(hoverIndicatorRef, (newValue) => {
84+
console.log('hoverIndicatorRef',newValue)
85+
})
3186
</script>
3287

3388
<template>
89+
3490
<primitive
3591
v-if="model_dungeon"
3692
ref="model_dungeon_ref"
@@ -67,4 +123,27 @@ const { stop } = watch(model_visual_blocker_ref, (newValue: Group | undefined) =
67123
:name="model_visual_blocker"
68124
:object="model_visual_blocker"
69125
/>
126+
<primitive
127+
v-if="floor"
128+
ref="floor_ref"
129+
:name="floor"
130+
:object="floor"
131+
:position-y="0.1"
132+
@click="handleFloorClick"
133+
@pointer-move="handleFloorHover"
134+
@pointer-leave="handleFloorLeave"
135+
/>
136+
<!-- Hover Indicator -->
137+
<TresMesh
138+
v-if="showIndicator"
139+
ref="hoverIndicatorRef"
140+
>
141+
<TresCylinderGeometry :args="[0.5, 0.5, 1, 32]" />
142+
<TresShaderMaterial
143+
transparent
144+
:vertex-shader="cylinderShader.vertexShader"
145+
:fragment-shader="cylinderShader.fragmentShader"
146+
:uniforms="cylinderShader.uniforms"
147+
/>
148+
</TresMesh>
70149
</template>

components/game/GameCanvas.vue

Lines changed: 13 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ const characters = computed(() => {
2525
2626
const { sendMsg } = useMultiplayer(gameStore.isMultiplayer)
2727
const orbitControlsRef = ref()
28-
const showIndicator = ref(false)
29-
const hoverIndicatorRef = shallowRef()
28+
3029
3130
useControls('fpsgraph')
3231
@@ -48,52 +47,8 @@ const { edgeStrength, pulseSpeed, visibleEdgeColor, blur } = useControls({
4847
},
4948
})
5049
51-
// Custom shader for gradient cylinder
52-
const cylinderShader = {
53-
uniforms: {
54-
color: { value: { r: 1.0, g: 1.0, b: 1.0 } },
55-
},
56-
vertexShader: `
57-
varying float vY;
58-
void main() {
59-
vY = position.y;
60-
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
61-
}
62-
`,
63-
fragmentShader: `
64-
uniform vec3 color;
65-
varying float vY;
66-
void main() {
67-
float alpha = 1.0 - (vY + 0.5); // Gradient from bottom to top
68-
gl_FragColor = vec4(color, alpha * 0.5); // Semi-transparent
69-
}
70-
`,
71-
}
72-
73-
const handleFloorClick = (e: ThreeEvent<PointerEvent>) => {
74-
const newPosition = { x: e.point.x, y: 0, z: e.point.z }
75-
if (gameStore.isMultiplayer) {
76-
sendMsg({
77-
type: 'UPDATE_PLAYER_POSITION',
78-
lobbyId: lobbyStore.currentLobbyId,
79-
position: [newPosition.x, newPosition.y, newPosition.z],
80-
})
81-
}
82-
else {
83-
gameStore.setPlayerPosition(gameStore.players[0], newPosition)
84-
}
85-
}
8650
87-
const handleFloorHover = (e: ThreeEvent<PointerEvent>) => {
88-
showIndicator.value = true
89-
if (hoverIndicatorRef.value) {
90-
hoverIndicatorRef.value.position.set(e.point.x, 0, e.point.z)
91-
}
92-
}
9351
94-
const handleFloorLeave = () => {
95-
showIndicator.value = false
96-
}
9752
9853
const outlineRef = ref()
9954
@@ -153,34 +108,27 @@ const { cursor } = useGameCursor()
153108
/>
154109
</template>
155110
</Suspense>
156-
<!-- Hover Indicator -->
157-
<TresMesh
158-
v-if="showIndicator"
159-
ref="hoverIndicatorRef"
160-
>
161-
<TresCylinderGeometry :args="[0.5, 0.5, 1, 32]" />
162-
<TresShaderMaterial
163-
transparent
164-
:vertex-shader="cylinderShader.vertexShader"
165-
:fragment-shader="cylinderShader.fragmentShader"
166-
:uniforms="cylinderShader.uniforms"
167-
/>
168-
</TresMesh>
111+
169112

170113
<!-- Level Items -->
171114
<template v-if="gameStore.currentLevel">
172115
<template v-for="item in gameStore.currentLevel.items" :key="item.id">
173116
<Item :id="item.id" />
174117
</template>
175118
</template>
176-
119+
<!-- <TresMesh
120+
v-if="gameStore.currentLevel"
121+
:position-y="0.1"
122+
:rotation-x="-Math.PI / 2"
123+
:scale="100"
124+
125+
>
126+
<TresPlaneGeometry />
127+
<TresMeshBasicMaterial color="red" transparent :opacity="0.2" />
128+
</TresMesh> -->
177129
<!-- World -->
178130
<Suspense>
179-
<World
180-
@click="handleFloorClick"
181-
@pointer-move="handleFloorHover"
182-
@pointer-leave="handleFloorLeave"
183-
/>
131+
<World />
184132
</Suspense>
185133

186134
<!-- Postprocessing -->

fly.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# fly.toml app configuration file generated for nuxt-3d-dnd-game on 2025-03-12T14:31:17+01:00
1+
# fly.toml app configuration file generated for nuxt-3d-dnd-game on 2025-04-03T08:48:37+02:00
22
#
33
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
44
#

0 commit comments

Comments
 (0)