Skip to content

Commit

Permalink
feat: init pre-defined character controller FCharacter3d
Browse files Browse the repository at this point in the history
  • Loading branch information
Gugustinette committed Aug 22, 2024
1 parent ecf9688 commit 21d1353
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 126 deletions.
65 changes: 20 additions & 45 deletions apps/playground-3d/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { F3dShapes, FCapsule, FCube, FGameCamera, FScene3d, FSphere } from '@fibbojs/3d'
import { F3dShapes, FCapsule, FCharacter3d, FCube, FGameCamera, FScene3d, FSphere } from '@fibbojs/3d'
import { fDebug } from '@fibbojs/devtools'
import { FKeyboard } from '@fibbojs/event'
import Duck from './classes/Duck'
import GltfCube from './classes/GltfCube'
import './style.css'
Expand Down Expand Up @@ -43,15 +42,12 @@ import MyCustomCube from './classes/MyCustomCube'
ground2.setColor(0x1F1F1F)
scene.addComponent(ground2)

// Create a capsule that will be controlled by the player
const capsule = new FCapsule(scene, {
position: { x: -5, y: 5, z: 5 },
})
capsule.setColor(0xA0FFA0)
capsule.initRigidBody({
lockRotations: true,
})
scene.addComponent(capsule)
// Create a character
const character = new FCharacter3d(scene)
scene.addComponent(character)

// Attach a camera to the character
scene.camera = new FGameCamera(character, scene)

/**
* Create other objects
Expand All @@ -62,7 +58,16 @@ import MyCustomCube from './classes/MyCustomCube'
sphere.initRigidBody()
scene.addComponent(sphere)

const capsule = new FCapsule(scene, {
position: { x: -5, y: 5, z: 5 },
rotationDegree: { x: 0, y: 45, z: 0 },
})
capsule.initRigidBody()
scene.addComponent(capsule)

const duck = new Duck(scene)
duck.setPosition(-5, 1, -5)
duck.initRigidBody()
scene.addComponent(duck)

const gltfCube = new GltfCube(scene)
Expand Down Expand Up @@ -99,42 +104,12 @@ import MyCustomCube from './classes/MyCustomCube'
/**
* Add collision events
*/
capsule.onCollisionWith(FCube, () => {
console.log('Cube collided with a FCube !')
})
capsule.onCollisionWith(sphere, () => {
console.log('Cube collided with the sphere!')
})

// Create a keyboard instance
const fKeyboard = new FKeyboard(scene)
// Detect inputs to move the cube
fKeyboard.on('ArrowUp', () => {
const cameraDirection = scene.camera.getCameraDirection()
cameraDirection.y = 0
capsule.rigidBody?.applyImpulse(cameraDirection.multiplyScalar(0.4), true)
character.onCollisionWith(FCube, () => {
console.log('Character collided with a FCube !')
})
fKeyboard.on('ArrowDown', () => {
const cameraDirection = scene.camera.getCameraDirection()
cameraDirection.y = 0
capsule.rigidBody?.applyImpulse(cameraDirection.multiplyScalar(-0.4), true)
character.onCollisionWith(sphere, () => {
console.log('Character collided with the sphere!')
})
fKeyboard.on('ArrowLeft', () => {
const cameraDirection = scene.camera.getCameraDirection()
cameraDirection.y = 0
capsule.rigidBody?.applyImpulse(cameraDirection.multiplyScalar(0.4).applyAxisAngle(new scene.THREE.Vector3(0, 1, 0), Math.PI / 2), true)
})
fKeyboard.on('ArrowRight', () => {
const cameraDirection = scene.camera.getCameraDirection()
cameraDirection.y = 0
capsule.rigidBody?.applyImpulse(cameraDirection.multiplyScalar(-0.4).applyAxisAngle(new scene.THREE.Vector3(0, 1, 0), Math.PI / 2), true)
})
fKeyboard.on(' ', () => {
capsule.rigidBody?.applyImpulse({ x: 0, y: 1, z: 0 }, true)
})

// Attach a camera to the cube
scene.camera = new FGameCamera(capsule, scene)

// After 3 seconds, add a third gltfCube
setTimeout(() => {
Expand Down
3 changes: 2 additions & 1 deletion packages/3d/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@fibbojs/core": "^0.0.1"
"@fibbojs/core": "^0.0.1",
"@fibbojs/event": "^0.0.1"
}
}
134 changes: 134 additions & 0 deletions packages/3d/src/FCharacter3d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import * as THREE from 'three'
import { FKeyboard } from '@fibbojs/event'
import RAPIER from '@dimforge/rapier3d'
import type { FScene3d } from './FScene3d'
import { F3dShapes } from './types/F3dShapes'
import type { FComponent3dOptions, FComponent3dOptions__initCollider, FComponent3dOptions__initRigidBody } from './FComponent3d'
import { FComponent3d } from './FComponent3d'

/**
* @description A pre-defined character controller.
* @category Model
* @example
* ```ts
* import { FScene3d, FCharacter } from '@fibbojs/3d'
*
* const scene = new FScene3d()
*
* const capsule = new FCharacter(scene)
* scene.addComponent(capsule)
* ```
*/
export class FCharacter3d extends FComponent3d {
/**
* The character controlelr that will be used to move the character.
*/
characterController: RAPIER.KinematicCharacterController

constructor(scene: FScene3d, options?: FComponent3dOptions) {
super(scene, options)
// Create a capsule
const geometry = new THREE.CapsuleGeometry(0.5, 1, 32)
const material = new THREE.MeshBasicMaterial({ color: 0xA0FFA0 })
this.mesh = new THREE.Mesh(geometry, material)

// Create a keyboard instance
const fKeyboard = new FKeyboard(scene)

// The gap the controller will leave between the character and its environment
const offset = 0.01
// Create the character controller
this.characterController = scene.world.createCharacterController(offset)

// Map of the movements (will be updated by the keyboard)
const inputs = {
forward: false,
backward: false,
left: false,
right: false,
}

// Handle movements on each frame (gravity + character movement)
scene.onFrame((delta) => {
let worldDirection = new THREE.Vector3(0, 0, 0)
// Compute the movement direction
worldDirection.x = inputs.left ? 1 : inputs.right ? -1 : 0
worldDirection.z = inputs.forward ? 1 : inputs.backward ? -1 : 0
// Normalize the movement direction
worldDirection = worldDirection.normalize()
// Apply the camera direction to the movement direction
const cameraDirection = scene.camera.getCameraDirection()
worldDirection.applyAxisAngle(new THREE.Vector3(0, 1, 0), Math.atan2(cameraDirection.x, cameraDirection.z))

// Create movement vector
const desiredMovement = {
x: worldDirection.x * delta * 8,
y: this.scene.world.gravity.y * delta,
z: worldDirection.z * delta * 8,
}
// Compute the desired movement
this.characterController.computeColliderMovement(
this.collider as RAPIER.Collider,
desiredMovement,
)
// Get the corrected movement
const correctedMovement = this.characterController.computedMovement()
// Apply the movement to the rigid body
this.rigidBody?.setLinvel({
x: correctedMovement.x / delta,
y: correctedMovement.y / delta,
z: correctedMovement.z / delta,
}, true)
})

// Key down
fKeyboard.onKeyDown('ArrowUp', () => {
inputs.forward = true
})
fKeyboard.onKeyDown('ArrowDown', () => {
inputs.backward = true
})
fKeyboard.onKeyDown('ArrowLeft', () => {
inputs.left = true
})
fKeyboard.onKeyDown('ArrowRight', () => {
inputs.right = true
})

// Key up
fKeyboard.onKeyUp('ArrowUp', () => {
inputs.forward = false
})
fKeyboard.onKeyUp('ArrowDown', () => {
inputs.backward = false
})
fKeyboard.onKeyUp('ArrowLeft', () => {
inputs.left = false
})
fKeyboard.onKeyUp('ArrowRight', () => {
inputs.right = false
})

// Initialize the rigid body
this.initRigidBody()
}

onFrame(_delta: number): void {
super.onFrame(_delta)
}

initRigidBody(options?: FComponent3dOptions__initRigidBody): void {
super.initRigidBody({
...options,
shape: F3dShapes.CAPSULE,
rigidBodyType: RAPIER.RigidBodyType.KinematicVelocityBased,
})
}

initCollider(options?: FComponent3dOptions__initCollider): void {
super.initCollider({
...options,
shape: F3dShapes.CAPSULE,
})
}
}
54 changes: 29 additions & 25 deletions packages/3d/src/FComponent3d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,33 @@ export interface FComponent3dOptions {
rotationDegree?: { x: number, y: number, z: number }
}

export interface FComponent3dOptions__initRigidBody {
position?: THREE.Vector3
scale?: THREE.Vector3
rotation?: THREE.Vector3
shape?: F3dShapes
rigidBodyType?: RAPIER.RigidBodyType
lockTranslations?: boolean
lockRotations?: boolean
enabledTranslations?: {
enableX: boolean
enableY: boolean
enableZ: boolean
}
enabledRotations?: {
enableX: boolean
enableY: boolean
enableZ: boolean
}
}

export interface FComponent3dOptions__initCollider {
position?: THREE.Vector3
scale?: THREE.Vector3
rotation?: THREE.Vector3
shape: F3dShapes
}

/**
* @description The base class for all 3D objects in FibboJS.
* @category Core
Expand Down Expand Up @@ -263,25 +290,7 @@ export abstract class FComponent3d extends FComponent {
* })
* ```
*/
initRigidBody(options?: {
position?: THREE.Vector3
scale?: THREE.Vector3
rotation?: THREE.Vector3
shape?: F3dShapes
rigidBodyType?: RAPIER.RigidBodyType
lockTranslations?: boolean
lockRotations?: boolean
enabledTranslations?: {
enableX: boolean
enableY: boolean
enableZ: boolean
}
enabledRotations?: {
enableX: boolean
enableY: boolean
enableZ: boolean
}
}): void {
initRigidBody(options?: FComponent3dOptions__initRigidBody): void {
// Apply default options
const DEFAULT_OPTIONS = {
position: new THREE.Vector3(this.position.x, this.position.y, this.position.z),
Expand Down Expand Up @@ -382,12 +391,7 @@ export abstract class FComponent3d extends FComponent {
* })
* ```
*/
initCollider(options?: {
position?: THREE.Vector3
scale?: THREE.Vector3
rotation?: THREE.Vector3
shape: F3dShapes
}): void {
initCollider(options?: FComponent3dOptions__initCollider): void {
// Apply default options
const DEFAULT_OPTIONS = {
position: new THREE.Vector3(this.position.x, this.position.y, this.position.z),
Expand Down
3 changes: 2 additions & 1 deletion packages/3d/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// Core 3d Classes
export { FScene3d } from './FScene3d'
export { FComponent3d } from './FComponent3d'
export { FCharacter3d } from './FCharacter3d'

// Models
export { FCapsule } from './model/FCapsule'
Expand All @@ -21,5 +22,5 @@ export { FOrbitCamera } from './cameras/FOrbitCamera'

// Types
export { F3dShapes } from './types/F3dShapes'
export type { FComponent3dOptions } from './FComponent3d'
export type { FComponent3dOptions, FComponent3dOptions__initRigidBody, FComponent3dOptions__initCollider } from './FComponent3d'
export type { FScene3dOptions } from './FScene3d'
30 changes: 3 additions & 27 deletions packages/3d/src/model/FCapsule.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import * as THREE from 'three'
import type * as RAPIER from '@dimforge/rapier3d'
import type { FScene3d } from '../FScene3d'
import { F3dShapes } from '../types/F3dShapes'
import type { FComponent3dOptions } from '../FComponent3d'
import type { FComponent3dOptions, FComponent3dOptions__initCollider, FComponent3dOptions__initRigidBody } from '../FComponent3d'
import { FPolyhedron } from './FPolyhedron'

/**
Expand Down Expand Up @@ -31,37 +30,14 @@ export class FCapsule extends FPolyhedron {
super.onFrame(_delta)
}

initRigidBody(options?: {
position?: THREE.Vector3
scale?: THREE.Vector3
rotation?: THREE.Vector3
shape?: F3dShapes
rigidBodyType?: RAPIER.RigidBodyType
lockTranslations?: boolean
lockRotations?: boolean
enabledTranslations?: {
enableX: boolean
enableY: boolean
enableZ: boolean
}
enabledRotations?: {
enableX: boolean
enableY: boolean
enableZ: boolean
}
}): void {
initRigidBody(options?: FComponent3dOptions__initRigidBody): void {
super.initRigidBody({
...options,
shape: F3dShapes.CAPSULE,
})
}

initCollider(options?: {
position?: THREE.Vector3
scale?: THREE.Vector3
rotation?: THREE.Vector3
shape?: F3dShapes
}): void {
initCollider(options?: FComponent3dOptions__initCollider): void {
super.initCollider({
...options,
shape: F3dShapes.CAPSULE,
Expand Down
Loading

0 comments on commit 21d1353

Please sign in to comment.