Commit 6e17550e authored by Master Q's avatar Master Q

go

parent 63e01312
...@@ -4,3 +4,5 @@ dev ...@@ -4,3 +4,5 @@ dev
yarn add webpack webpack-cli webpack-dev-server typescript three @types/three ts-loa yarn add webpack webpack-cli webpack-dev-server typescript three @types/three ts-loa
der der
``` ```
6077
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<title>Three</title>
<style>
* {
padding: 0;
margin: 0;
-webkit-tap-highlight-color: rgba(0,0,0,0);
-webkit-touch-callout:none;
-webkit-user-select:none;
-khtml-user-select:none;
-moz-user-select:none;
-ms-user-select:none;
user-select:none;
touch-action: none;
}
div {
user-select: none;
}
</style>
<script src="//yun.duiba.com.cn/aurora/assets/4723050c150b41f362ecf483e9cf98eb31c4a15b.js"></script>
</head>
<body>
<!-- 这里的 bundle 就是 webpack 临时打包出来 -->
<script src="//yun.duiba.com.cn/aurora/assets/5feda82574ac6db79f09540a8d24d09ff29ec9c9.js"></script>
<script>
window.addEventListener("load", function () {
console.log('load')
})
</script>
</body>
</html>
\ No newline at end of file
...@@ -3,17 +3,36 @@ ...@@ -3,17 +3,36 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<title>Three</title> <title>Three</title>
<style> <style>
* { * {
padding: 0; padding: 0;
margin: 0; margin: 0;
-webkit-tap-highlight-color: rgba(0,0,0,0);
-webkit-touch-callout:none;
-webkit-user-select:none;
-khtml-user-select:none;
-moz-user-select:none;
-ms-user-select:none;
user-select:none;
touch-action: none;
}
div {
user-select: none;
} }
</style> </style>
<script src="./ammo.js"></script>
</head> </head>
<body> <body>
<!-- 这里的 bundle 就是 webpack 临时打包出来 --> <!-- 这里的 bundle 就是 webpack 临时打包出来 -->
<script src="bundle.js"></script> <script src="bundle.js"></script>
<script>
window.addEventListener("load", function () {
console.log('load')
})
</script>
</body> </body>
</html> </html>
\ No newline at end of file
import { EVENTS_ENUM, PerspectiveScene } from "../module/PerspectiveScene"; import { EVENTS_ENUM, PerspectiveScene } from "../module/PerspectiveScene";
import * as THREE from 'three' import * as THREE from 'three'
import { OimoPhysicWorld } from "../OimoPhysicWorld/OimoPhysicWorld"; import { OimoPhysicWorld } from "../OimoPhysicWorld/OimoPhysicWorld";
import { AmmoPhysicWorld } from "../module/AmmoPhysicWorld";
import { Vehicle } from "./vehicle";
import { FirstPersonCameraControl } from "../module/CameraControl/FirstControlCameraControl";
function gradTexture(color: [number[], string[]]) { function gradTexture(color: [number[], string[]]) {
var c = document.createElement("canvas"); var c = document.createElement("canvas");
...@@ -48,17 +51,66 @@ function basicTexture(n: number){ ...@@ -48,17 +51,66 @@ function basicTexture(n: number){
return tx; return tx;
} }
function generateHeight( width: number, depth: number, minHeight: number, maxHeight: number) {
// Generates the height data (a sinus wave)
var size = width * depth;
var data = new Float32Array( size );
var hRange = maxHeight - minHeight;
var w2 = width / 2;
var d2 = depth / 2;
var phaseMult = 10;
var p = 0;
for ( var j = 0; j < depth; j ++ ) {
for ( var i = 0; i < width; i ++ ) {
var radius = Math.sqrt(
Math.pow( ( i - w2 ) / w2, 2.0 ) +
Math.pow( ( j - d2 ) / d2, 2.0 ) );
var height = ( Math.sin( radius * phaseMult ) + 1 ) * 0.5 * hRange + minHeight;
data[ p ] = height;
p++;
}
}
return data;
}
const GeometryMap = { const GeometryMap = {
'box': new THREE.BoxGeometry(1,1,1) 'box': new THREE.BoxGeometry(1,1,1)
} }
var ZERO_QUATERNION = new THREE.Quaternion(0, 0, 0, 1);
export class CarScene extends PerspectiveScene { export class CarScene extends PerspectiveScene {
vehicleIns: Vehicle;
cameraControls: FirstPersonCameraControl
constructor() { constructor() {
super() super()
this.camera.position.set(20, 20, 20)
// this.OrbitControlsIns.enabled = false
// this.cameraControls = new FirstPersonCameraControl(this.camera)
// document.body.addEventListener('click', () => {
// this.cameraControls.lock()
// })
this.initEvents() this.initEvents()
this.initUi() this.initUi()
this.initPhysicWorld()
// this.initOimoPhysicWorld()
this.initAmmoPhySicWorld()
} }
initUi() { initUi() {
...@@ -71,6 +123,20 @@ export class CarScene extends PerspectiveScene { ...@@ -71,6 +123,20 @@ export class CarScene extends PerspectiveScene {
this.scene.add(dirLight) this.scene.add(dirLight)
this.scene.add(new THREE.DirectionalLightHelper(dirLight)) this.scene.add(new THREE.DirectionalLightHelper(dirLight))
dirLight.castShadow = true
console.log(dirLight.shadow)
dirLight.shadow.mapSize.width = 1024 // 2的 n次幂 数值越高 细节越高
dirLight.shadow.mapSize.height = 1024
dirLight.shadow.camera.near = 0.3
dirLight.shadow.camera.far = 60
dirLight.shadow.camera.top = 40
dirLight.shadow.camera.left = 44
dirLight.shadow.camera.bottom = -28
dirLight.shadow.camera.right = -44
dirLight.shadow.radius = 4
this.scene.add(new THREE.CameraHelper(dirLight.shadow.camera))
let renderer = this.renderer let renderer = this.renderer
// 开启阴影 // 开启阴影
renderer.shadowMap.enabled = true renderer.shadowMap.enabled = true
...@@ -83,7 +149,7 @@ export class CarScene extends PerspectiveScene { ...@@ -83,7 +149,7 @@ export class CarScene extends PerspectiveScene {
this.scene.add(new THREE.AmbientLight(0x3D4143)) this.scene.add(new THREE.AmbientLight(0x3D4143))
this.scene.add(new THREE.AxesHelper(100)) this.scene.add(new THREE.AxesHelper(100))
this.scene.add(new THREE.GridHelper(100)) this.scene.add(new THREE.GridHelper(100, 20))
// let imgTexture = new THREE.TextureLoader().load('http://qnpic.top/yoona2.jpg') // let imgTexture = new THREE.TextureLoader().load('http://qnpic.top/yoona2.jpg')
// imgTexture.wrapS = imgTexture.wrapT = THREE.RepeatWrapping; // imgTexture.wrapS = imgTexture.wrapT = THREE.RepeatWrapping;
...@@ -107,11 +173,151 @@ export class CarScene extends PerspectiveScene { ...@@ -107,11 +173,151 @@ export class CarScene extends PerspectiveScene {
} }
ammoPhysicWorld: AmmoPhysicWorld
initAmmoPhySicWorld() {
this.ammoPhysicWorld = new AmmoPhysicWorld()
console.log(this.ammoPhysicWorld)
const boxGeometry = new THREE.BoxGeometry(1,1,1)
const material = new THREE.MeshLambertMaterial({
// wireframe: true,
map: new THREE.TextureLoader().load('http://qnpic.top/yoona2.jpg')
})
material.color = new THREE.Color(0xffffff * Math.random())
const boxMesh = new THREE.Mesh(boxGeometry, material)
boxMesh.position.set(0, 20, 0)
boxMesh.castShadow = true
this.scene.add(boxMesh)
this.ammoPhysicWorld.addMesh(boxMesh, {
mass: 1,
// friction: 10,
restitution: 2
})
// const planeGeometry = new THREE.BoxGeometry(75, 1, 75)
// const planeMesh = new THREE.Mesh(planeGeometry, new THREE.MeshPhongMaterial({
// color: 0xffffff,
// side: THREE.DoubleSide
// }))
// planeMesh.receiveShadow = true
// planeMesh.position.y = -1
// this.scene.add(planeMesh)
// this.ammoPhysicWorld.addMesh(planeMesh, {
// // friction: 2,
// restitution: 0.1
// })
this.createTerrain()
const vehicleIns = this.vehicleIns = new Vehicle(new THREE.Vector3(0, 4, -20), ZERO_QUATERNION)
this.scene.add(vehicleIns)
this.ammoPhysicWorld.addDynamicBody(vehicleIns)
}
createTerrain() {
const terrainWidth = 128, terrainDepth = 128
const terrainWidthExtents = 100, terrainDepthExtents = 100
const terrainHelfWidth = terrainWidth / 2, terrainHelfDepath = terrainDepth / 2
const terrainMaxHeight = 8, terrainMinHeight = 0
const heightData = generateHeight(terrainWidth, terrainDepth, terrainMinHeight, terrainMaxHeight)
const Planegeometry = new THREE.PlaneBufferGeometry( terrainWidthExtents, terrainDepthExtents, terrainWidth - 1, terrainDepth - 1 );
Planegeometry.rotateX( - Math.PI / 2 );
const vertices = Planegeometry.attributes.position.array;
// console.log(vertices)
// i < l
for ( var i = 0, j = 0, l = vertices.length; j < l; i ++, j += 3 ) {
// j + 1 because it is the y component that we modify
// @ts-expect-error
vertices[j + 1] = heightData[ i ];
}
// 计算法线向量
Planegeometry.computeVertexNormals();
const groundMaterial = new THREE.MeshPhongMaterial( { color: 0xC7C7C7 } );
const terrainMesh = new THREE.Mesh( Planegeometry, groundMaterial );
terrainMesh.receiveShadow = true
this.scene.add( terrainMesh );
// This parameter is not really used, since we are using PHY_FLOAT height data type and hence it is ignored
var heightScale = 1;
// Up axis = 0 for X, 1 for Y, 2 for Z. Normally 1 = Y is used.
var upAxis = 1;
// hdt, height data type. "PHY_FLOAT" is used. Possible values are "PHY_FLOAT", "PHY_UCHAR", "PHY_SHORT"
var hdt = "PHY_FLOAT"
// "PHY_FLOAT";
// Set this to your needs (inverts the triangles)
var flipQuadEdges = false;
// Creates height data buffer in Ammo heap
const ammoHeightData = Ammo._malloc( 4 * terrainWidth * terrainDepth );
// Copy the javascript height data array to the Ammo one.
var p = 0;
var p2 = 0;
for ( var j = 0; j < terrainDepth; j ++ ) {
for ( var i = 0; i < terrainWidth; i ++ ) {
// write 32-bit float data to memory
Ammo.HEAPF32[ammoHeightData + p2 >> 2] = heightData[ p ];
p ++;
// 4 bytes/float
p2 += 4;
}
}
// Creates the heightfield physics shape
var heightFieldShape = new Ammo.btHeightfieldTerrainShape(
terrainWidth,
terrainDepth,
ammoHeightData,
heightScale,
terrainMinHeight,
terrainMaxHeight,
upAxis,
// @ts-expect-error
hdt,
flipQuadEdges
);
// Set horizontal scale
var scaleX = terrainWidthExtents / ( terrainWidth - 1 );
var scaleZ = terrainDepthExtents / ( terrainDepth - 1 );
heightFieldShape.setLocalScaling( new Ammo.btVector3( scaleX, 1, scaleZ ) );
heightFieldShape.setMargin( 0.05 );
var groundTransform = new Ammo.btTransform();
groundTransform.setIdentity();
// Shifts the terrain, since bullet re-centers it on its bounding box.
groundTransform.setOrigin( new Ammo.btVector3( 0, ( terrainMaxHeight + terrainMinHeight ) / 2, 0 ) );
var groundMass = 0;
var groundLocalInertia = new Ammo.btVector3( 0, 0, 0 );
var groundMotionState = new Ammo.btDefaultMotionState( groundTransform );
var groundBody = new Ammo.btRigidBody( new Ammo.btRigidBodyConstructionInfo( groundMass, groundMotionState, heightFieldShape, groundLocalInertia ) );
this.ammoPhysicWorld.physicWorld.addRigidBody( groundBody );
}
physicWorld: OimoPhysicWorld physicWorld: OimoPhysicWorld
/** /**
* 创建物理世界 * 创建物理世界
*/ */
initPhysicWorld() { initOimoPhysicWorld() {
this.physicWorld = new OimoPhysicWorld() this.physicWorld = new OimoPhysicWorld()
console.log(this.physicWorld) console.log(this.physicWorld)
...@@ -136,8 +342,7 @@ export class CarScene extends PerspectiveScene { ...@@ -136,8 +342,7 @@ export class CarScene extends PerspectiveScene {
side: THREE.DoubleSide side: THREE.DoubleSide
})) }))
planeMesh.receiveShadow = true planeMesh.receiveShadow = true
// planeMesh.position.set(5, 0, 5) // planeMesh.position.y = -2
// planeMesh.rotateX(Math.PI / 2)
this.scene.add(planeMesh) this.scene.add(planeMesh)
const ridigBody = this.physicWorld.addMesh(planeMesh, { const ridigBody = this.physicWorld.addMesh(planeMesh, {
...@@ -160,10 +365,26 @@ export class CarScene extends PerspectiveScene { ...@@ -160,10 +365,26 @@ export class CarScene extends PerspectiveScene {
} }
onEnterFrame() { onEnterFrame(e: any) {
if (this.physicWorld) { if (this.physicWorld) {
this.physicWorld.step() this.physicWorld.step()
} }
if (this.ammoPhysicWorld) {
this.ammoPhysicWorld.step()
}
// this.updateCamera(this.camera)
}
updateCamera(camera: THREE.Camera) {
if (this.vehicleIns) {
const carPosition = this.vehicleIns.chassisMesh.position
// 跟随 Car
const relativeCameraOffset = new THREE.Vector3(0, 10, -15)
relativeCameraOffset.applyMatrix4(this.vehicleIns.chassisMesh.matrixWorld)
this.camera.position.copy(relativeCameraOffset)
this.camera.lookAt(carPosition)
}
} }
} }
\ No newline at end of file
import * as THREE from 'three';
class TerrainShap extends THREE.Object3D {
constructor(terrainWith: number, terrainHeight: number, heightData: number[]) {
super()
}
}
\ No newline at end of file
This diff is collapsed.
import { CarScene } from "./CarScene" import { CarScene } from "./CarScene"
console.log('work') export class StageScene {
constructor() {
this.initStage()
}
new CarScene() initStage() {
\ No newline at end of file // @ts-expect-error
Ammo().then(() => {
new CarScene()
})
}
}
new StageScene()
\ No newline at end of file
import * as THREE from 'three'
interface BodyConfig {
mass: number,
friction: number // 摩擦力
restitution: number // 弹性
}
interface DynamicBody {
initPhysics: (w: Ammo.btDiscreteDynamicsWorld) => void
update: (delta: number) => void
}
export class AmmoPhysicWorld {
physicWorld: Ammo.btDiscreteDynamicsWorld
meshes: THREE.Mesh[] = []
meshesMap: WeakMap<THREE.Mesh, Ammo.btRigidBody> = new WeakMap()
clock: THREE.Clock
syncList: Function[] = []
DISABLE_DEACTIVATION: number = 4
TRANSFORM_AUX: Ammo.btTransform = new Ammo.btTransform();
ZERO_QUATERNION: THREE.Quaternion = new THREE.Quaternion(0, 0, 0, 1);
dynamicBodyList: DynamicBody[] = []
constructor() {
this.clock = new THREE.Clock();
this.initPhysicWorld()
}
initPhysicWorld() {
// btDefaultCollisionConfiguration 初始化一个默认碰撞配置
const collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
const dispatcher = new Ammo.btCollisionDispatcher( collisionConfiguration );
// 宽相位
const broadphase = new Ammo.btDbvtBroadphase();
// 解算器
const solver = new Ammo.btSequentialImpulseConstraintSolver();
const physicsWorld = this.physicWorld = new Ammo.btDiscreteDynamicsWorld( dispatcher, broadphase, solver, collisionConfiguration );
physicsWorld.setGravity( new Ammo.btVector3( 0, -9.82, 0 ) );
}
createMeshPhysicBody(mesh: THREE.Mesh, cfg: Partial<BodyConfig> = {}) {
console.log(mesh.type, mesh, mesh.geometry)
// @ts-expect-error
const parameters = mesh.geometry.parameters;
const geometry = mesh.geometry;
const finalCfg:BodyConfig = Object.assign({
mass: 0,
friction: 1,
restitution: 0
}, cfg)
var transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(mesh.position.x, mesh.position.y, mesh.position.z));
transform.setRotation(new Ammo.btQuaternion(mesh.quaternion.x, mesh.quaternion.y, mesh.quaternion.z, mesh.quaternion.w));
// 运动状态
var motionState = new Ammo.btDefaultMotionState(transform);
// 局部惯性?
var localInertia = new Ammo.btVector3(0, 0, 0);
if (geometry.type === 'BoxGeometry') {
const sx = parameters.width !== undefined ? parameters.width / 2 : 0.5;
const sy = parameters.height !== undefined ? parameters.height / 2 : 0.5;
const sz = parameters.depth !== undefined ? parameters.depth / 2 : 0.5;
const btGeometryShape = new Ammo.btBoxShape(new Ammo.btVector3(sx, sy, sz));
btGeometryShape.calculateLocalInertia(
finalCfg.mass
, localInertia);
// 设置 ridigbody 属性,并new 一个 刚体
const rbInfo = new Ammo.btRigidBodyConstructionInfo(finalCfg.mass, motionState, btGeometryShape, localInertia);
const body = new Ammo.btRigidBody(rbInfo);
body.setFriction(finalCfg.friction)
body.setRestitution(finalCfg.restitution)
this.physicWorld.addRigidBody(body)
if (finalCfg.mass) {
body.setActivationState(this.DISABLE_DEACTIVATION)
}
return body
}
// else if ( geometry.type === 'SphereGeometry' || geometry.type === 'IcosahedronGeometry' ) {
// const radius = parameters.radius !== undefined ? parameters.radius : 1;
// return new OIMO.OSphereGeometry( radius );
// }
return null;
}
addMesh(mesh: THREE.Mesh, config: Partial<BodyConfig> = {}) {
const ridigBody = this.createMeshPhysicBody(mesh, config)
if (ridigBody) {
this.meshes.push(mesh)
this.meshesMap.set(mesh, ridigBody)
}
return ridigBody
}
addDynamicBody(body: DynamicBody) {
body.initPhysics(this.physicWorld)
this.dynamicBodyList.push(body)
}
step() {
const dt = this.clock.getDelta()
const syncList = this.syncList
for (var i = 0; i < syncList.length; i++) {
syncList[i](dt)
}
const meshes = this.meshes
for ( let i = 0, l = meshes.length; i < l; i ++ ) {
const mesh = meshes[i];
// if ( mesh.isInstancedMesh ) {
// const array = mesh.instanceMatrix.array;
// const bodies = meshMap.get( mesh );
// for ( let j = 0; j < bodies.length; j ++ ) {
// const body = bodies[ j ];
// compose( body.getPosition(), body.getOrientation(), array, j * 16 );
// }
// mesh.instanceMatrix.needsUpdate = true;
// } else
if ( mesh.isMesh ) {
const body = this.meshesMap.get(mesh);
// 获取刚体的状态
var ms = body.getMotionState();
if (ms) {
ms.getWorldTransform(this.TRANSFORM_AUX);
var p = this.TRANSFORM_AUX.getOrigin();
var q = this.TRANSFORM_AUX.getRotation();
mesh.position.set(p.x(), p.y(), p.z());
mesh.quaternion.set(q.x(), q.y(), q.z(), q.w());
}
}
}
for (let i = 0; i < this.dynamicBodyList.length; i++) {
const currBody = this.dynamicBodyList[i]
currBody.update(dt)
}
// simulation 模拟
this.physicWorld.stepSimulation( dt, 10 );
}
}
\ No newline at end of file
import {
Vector3,
Euler,
EventDispatcher,
Camera
} from 'three'
const _euler = new Euler(0, 0, 0, 'YXZ');
const _vector = new Vector3();
const _cameraDefaultVector = new Vector3(0, 0, -1);
const _changeEvent = { type: 'change' };
const _lockEvent = { type: 'lock' };
const _unlockEvent = { type: 'unlock' };
const _PI_2 = Math.PI / 2;
export class FirstPersonCameraControl extends EventDispatcher {
domElement: HTMLElement
isLocked: boolean // 是否锁定
minPolarAngle: number // 最小相机俯仰角度
maxPolarAngle: number // 最大相机俯仰角度
camera: Camera // 相机
lookSpeedCoefficient: number // 旋转速度系数
constructor(camera: Camera, domElement?: HTMLElement) {
super()
if (!camera) {
throw new Error('FirstPersonCameraControl: The first parameter "camera" is required')
}
if ( domElement === undefined ) {
console.warn( 'FirstPersonCameraControl: The second parameter "domElement" is now mandatory.' );
domElement = document.body;
}
this.domElement = domElement;
this.camera = camera
this.isLocked = false;
// 设置相机俯仰角度 Range is 0 to Math.PI
this.minPolarAngle = 0; // radians
this.maxPolarAngle = Math.PI; // radians
this.lookSpeedCoefficient = 0.001
this.initEvents()
}
private initEvents() {
const ownerDocument = this.domElement.ownerDocument
ownerDocument.addEventListener('mousemove', this.onMouseMove, false)
ownerDocument.addEventListener('touchmove', this.onMouseMove, false)
ownerDocument.addEventListener('pointerlockchange', this.onPointerLockChange, false)
ownerDocument.addEventListener('pointerlockerror', this.onPointerLockError, false)
}
private removeEvents() {
const ownerDocument = this.domElement.ownerDocument
ownerDocument.removeEventListener('mousemove', this.onMouseMove, false)
ownerDocument.removeEventListener('touchmove', this.onMouseMove, false)
ownerDocument.removeEventListener('pointerlockchange', this.onPointerLockChange, false)
ownerDocument.removeEventListener('pointerlockerror', this.onPointerLockError, false)
}
private onPointerLockError() {
console.error('FirstPersonCameraControl: Pointer lock error')
}
private onMouseMove = (event: Event) => {
if (this.isLocked) {
const camera = this.camera
// @ts-ignore
const movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0
// @ts-ignore
const movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0
// 获取 相机的 四元 quaternion
_euler.setFromQuaternion( camera.quaternion );
_euler.y -= movementX * this.lookSpeedCoefficient;
_euler.x -= movementY * this.lookSpeedCoefficient;
_euler.x = Math.max( _PI_2 - this.maxPolarAngle, Math.min( _PI_2 - this.minPolarAngle, _euler.x ) );
camera.quaternion.setFromEuler( _euler );
this.dispatchEvent( _changeEvent );
}
}
/**
* pointerlock change 事件
*/
private onPointerLockChange = () => {
if ( this.domElement.ownerDocument.pointerLockElement === this.domElement ) {
this.dispatchEvent( _lockEvent );
this.isLocked = true;
} else {
this.dispatchEvent( _unlockEvent );
this.isLocked = false;
}
}
dispose() {
this.removeEvents()
}
getCamera() {
return this.camera
}
/**
* 获取相机的四元朝向
*/
getCameraDirection = (v: Vector3) => {
// 简单粗暴点就下面这样 getWorldDirection
return this.camera.getWorldDirection(v)
return v.copy(_cameraDefaultVector).applyQuaternion(this.camera.quaternion)
}
/**
* 向前移动
* @param distance
*/
moveForward(distance: number) {
const camera = this.camera
// camera.getWorldDirection(v) 这样也行
// 第一列为右向的向量
// 第二 列为上向的向量
// 第三 列为向前的向量
_vector.setFromMatrixColumn( camera.matrix, 0 );
// 向前的向量
_vector.crossVectors( camera.up, _vector );
camera.position.addScaledVector( _vector, distance );
}
getControlsRight() {
const _camera = this.camera
_vector.setFromMatrixColumn( _camera.matrix, 0 );
return _vector
}
/**
* 向右移动
* @param distance
*/
moveRight(distance: number) {
const _camera = this.camera
_vector.setFromMatrixColumn( _camera.matrix, 0 );
_camera.position.addScaledVector( _vector, distance );
}
moveToPostion(position: Vector3) {
this.camera.position.copy(position)
}
add(v: Vector3) {
this.camera.position.add(v)
}
/**
* 锁定
*/
lock() {
this.domElement.requestPointerLock()
}
/**
* 接触锁定
*/
unlock() {
this.domElement.ownerDocument.exitPointerLock()
}
}
\ No newline at end of file
...@@ -4,7 +4,7 @@ type EventCall<T extends any> = (e?: { ...@@ -4,7 +4,7 @@ type EventCall<T extends any> = (e?: {
}, ...arg1: any[]) => any }, ...arg1: any[]) => any
export class EventDispatcher<EventsKeyName extends string> { export class EventDispatcher<EventsKeyName extends string = string> {
private listeners: { private listeners: {
[key in EventsKeyName]?: { [key in EventsKeyName]?: {
listener: EventCall<EventsKeyName>, listener: EventCall<EventsKeyName>,
......
import { EventDispatcher } from "../EventDispatcher"
type KeyDownCallBack = (e?: KeyboardEvent) => void
export class KeyBoardState extends EventDispatcher {
constructor() {
super()
this.initEvents()
}
private keyState: any = {}
private initEvents() {
document.addEventListener('keydown', (e: KeyboardEvent) => {
const key = e.key
const code = e.code
for (let k of [key+'-down', code+'-down']) {
this.dispatchEvent(k, e)
}
this.keyState[key] = true
this.keyState[code] = true
})
document.addEventListener('keyup', (e) => {
const key = e.key
const code = e.code
for (let k of [key+'-up', code+'-up']) {
this.dispatchEvent(k, e)
}
this.keyState[key] = false
this.keyState[code] = false
})
}
/**
* 监听事件
* @param key
* @param callback
*/
onKeyDown(key: string, callback: KeyDownCallBack, context?: any) {
// @ts-expect-error
this.addEventListener(key+'-down', callback, context)
}
/**
* 删除事件 监听
* @param key
* @param callback
*/
removeKeyDown(key: string, callback: KeyDownCallBack) {
// @ts-expect-error
this.removeEventListener(key+'-down', callback)
}
isPressed(key: string) {
return this.keyState[key.toUpperCase()] || this.keyState[key.toLocaleLowerCase()]
}
}
export const KeyBoardStateStore = new KeyBoardState()
\ No newline at end of file
This diff is collapsed.
...@@ -13,13 +13,15 @@ ...@@ -13,13 +13,15 @@
"strict": true, /* Enable all strict type-checking options. */ "strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
"strictNullChecks": false, "strictNullChecks": false,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true, "esModuleInterop": true,
"skipLibCheck": true, /* Skip type checking of declaration files. */ "skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
"typeRoots": ["node_modules/ammojs-typed/ammo/ambient"]
}, },
"include": [ "include": [
"src/**/*.ts" "src/**/*.ts"
], , "libs/ammo.d.ts", "libs/ammo.d.ts" ],
"exclude": [ "exclude": [
"node_modules" "node_modules"
] ]
......
...@@ -22,7 +22,7 @@ module.exports = { ...@@ -22,7 +22,7 @@ module.exports = {
}, },
resolve: { resolve: {
alias: { alias: {
'@': './src', '@': './src'
}, },
extensions: ['.ts', '.js', 'tsx'] extensions: ['.ts', '.js', 'tsx']
}, },
...@@ -34,6 +34,6 @@ module.exports = { ...@@ -34,6 +34,6 @@ module.exports = {
port: '0.0.0.0', port: '0.0.0.0',
hot: true, hot: true,
open: true, open: true,
port: 9016 port: 9017
} }
} }
\ No newline at end of file
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment