mylingo3d
Version:
Lingo3D is a React/Vue 3d game development framework that ships with a complete visual editor
142 lines (125 loc) • 5.07 kB
text/typescript
import CameraBase from "."
import { container } from "../../../engine/renderLoop/renderSetup"
import { PerspectiveCamera } from "three"
import {
getCameraPointerLock,
setCameraPointerLock
} from "../../../states/useCameraPointerLock"
import { mouseEvents } from "../../../api/mouse"
import { getCameraRendered } from "../../../states/useCameraRendered"
import isMobile from "../../../api/utils/isMobile"
import { getEditing } from "../../../states/useEditing"
export default function (this: CameraBase<PerspectiveCamera>) {
if (this.done) return
this.createEffect(() => {
if (
getCameraRendered() !== this.camera ||
!this.mouseControlState.get()
)
return
if (getCameraPointerLock() === this.camera) {
const handleMove = (e: MouseEvent) =>
this.gyrate(e.movementX, e.movementY)
document.addEventListener("mousemove", handleMove)
return () => {
document.removeEventListener("mousemove", handleMove)
}
}
let started = false
let identifier: number | undefined
let xOld: number | undefined
let yOld: number | undefined
const handleDown = () => {
started = true
;[xOld, yOld] = [undefined, undefined]
}
const handleUp = () => (started = false)
const handleMove = (e: MouseEvent | Touch) => {
xOld === undefined && (xOld = e.clientX)
yOld === undefined && (yOld = e.clientY)
const [movementX, movementY] = [e.clientX - xOld, e.clientY - yOld]
;[xOld, yOld] = [e.clientX, e.clientY]
started &&
this.gyrate(
(movementX / window.innerWidth) * 3000,
(movementY / window.innerHeight) * 3000
)
}
if (isMobile) {
const handleTouchStart = (e: TouchEvent) => {
if (identifier !== undefined) return
identifier =
e.changedTouches[e.changedTouches.length - 1].identifier
handleDown()
}
container.addEventListener("touchstart", handleTouchStart)
const handleTouchEnd = (e: TouchEvent) => {
if (identifier === undefined) return
if (
e.changedTouches[e.changedTouches.length - 1].identifier ===
identifier
) {
identifier = undefined
handleUp()
}
}
container.addEventListener("touchend", handleTouchEnd)
const handleTouchMove = (e: TouchEvent) => {
if (identifier === undefined) return
let touch: Touch | undefined
for (let i = 0; i < e.changedTouches.length; ++i) {
const t = e.changedTouches[i]
if (t.identifier === identifier) {
touch = t
break
}
}
touch && handleMove(touch)
}
container.addEventListener("touchmove", handleTouchMove)
return () => {
container.removeEventListener("touchstart", handleTouchStart)
container.removeEventListener("touchend", handleTouchEnd)
container.removeEventListener("touchmove", handleTouchMove)
identifier = undefined
started = false
}
}
const handle0 = mouseEvents.on("down", handleDown)
const handle1 = mouseEvents.on("up", handleUp)
container.addEventListener("mousemove", handleMove)
return () => {
handle0.cancel()
handle1.cancel()
container.removeEventListener("mousemove", handleMove)
identifier = undefined
started = false
}
}, [this.mouseControlState.get, getCameraRendered, getCameraPointerLock])
this.createEffect(() => {
const camera = getCameraRendered()
if (
this.mouseControlState.get() !== true ||
camera !== this.camera ||
getEditing()
)
return
const onClick = () => container.requestPointerLock?.()
const onPointerLockChange = () => {
if (document.pointerLockElement === container)
setCameraPointerLock(camera)
else setCameraPointerLock(undefined)
}
container.addEventListener("click", onClick)
document.addEventListener("pointerlockchange", onPointerLockChange)
return () => {
container.removeEventListener("click", onClick)
document.removeEventListener(
"pointerlockchange",
onPointerLockChange
)
document.exitPointerLock()
setCameraPointerLock(undefined)
}
}, [this.mouseControlState.get, getCameraRendered, getEditing])
}