Examples

Examples

Collection of examples showing different ways to use the React 3rd Person Character library.

Basic Example

A minimal setup with default settings:

import {
  CharacterConfigProvider,
  ExperienceWrapper,
  Player,
  Scenario,
  StyledMobileControls,
  FIBER,
  THREE,
  CharacterBuilder,
} from "react-3rd-person-character";
 
// Simple character configuration
const characterConfig = new CharacterBuilder({ fps: 60 })
  .withName('Player')
  .withCameraSettings({ radius: 5 })
  .withMovement({ movementSpeed: 5 })
  .with3DModels('/models/player', PlayerModel, Scenario3D)
  .build();
 
// Simple player model
function PlayerModel() {
  return (
    <mesh>
      <capsuleGeometry args={[0.5, 1, 4, 8]} />
      <meshStandardMaterial color="blue" />
    </mesh>
  );
}
 
// Simple scenario
function Scenario3D() {
  return (
    <group>
      <mesh position={[0, -0.5, 0]}>
        <boxGeometry args={[20, 1, 20]} />
        <meshStandardMaterial color="green" />
      </mesh>
    </group>
  );
}
 
export default function BasicExample() {
  return (
    <CharacterConfigProvider character={characterConfig}>
      <FIBER.Canvas shadows>
        <ExperienceWrapper>
          <Scenario />
          <Player />
        </ExperienceWrapper>
      </FIBER.Canvas>
      <StyledMobileControls />
    </CharacterConfigProvider>
  );
}

Advanced Character Configuration

A more detailed character setup with custom physics and movement:

const advancedCharacterConfig = new CharacterBuilder({ fps: 60 })
  .withName('Adventure Hero')
  .withCameraSettings({
    radius: 6,
    cameraOffsetY: 3,
    cameraOffsetZ: 1
  })
  .withVelocity({
    defaultVelocitySimulatorMass: 1.2,
    defaultVelocitySimulatorDamping: 0.7,
    defaultRotationSimulatorMass: 1.2,
    defaultRotationSimulatorDamping: 0.7,
    mass: 1.5
  })
  .withMovement({
    movementSpeed: 6,
    boostSpeed: 10,
    jumpForce: 12
  })
  .with3DModels(
    '/models/hero',
    HeroModel,
    ForestScenario,
    LoadingSpinner,
    [1.2, 1.2, 1.2]
  )
  .build();
 
// Custom loading component
function LoadingSpinner() {
  return (
    <div className="loading">
      <div className="spinner" />
      <p>Loading character...</p>
    </div>
  );
}

Custom 3D Models

Using GLTF models with animations:

import { useGLTF, useAnimations } from '@react-three/drei';
 
function HeroModel({ nodes, materials }) {
  const { scene, animations } = useGLTF('/models/hero.glb');
  const { actions } = useAnimations(animations);
  
  // Handle animations based on character state
  const { characterState } = useActorContext();
  
  useEffect(() => {
    if (characterState.matches('walking')) {
      actions.walk?.play();
    } else if (characterState.matches('running')) {
      actions.run?.play();
    } else {
      actions.idle?.play();
    }
  }, [characterState, actions]);
  
  return <primitive object={scene} />;
}
 
function ForestScenario() {
  const { scene } = useGLTF('/models/forest.glb');
  
  return (
    <group>
      <primitive object={scene} />
      {/* Add physics colliders */}
      <RAPIER.RigidBody type="fixed">
        <RAPIER.CuboidCollider args={[10, 0.5, 10]} />
      </RAPIER.RigidBody>
    </group>
  );
}

Custom Controls

Creating custom input handling:

import { useControls, useTouchControls } from 'react-3rd-person-character';
 
function CustomInputHandler() {
  const controls = useControls();
  const touchControls = useTouchControls();
  
  // Custom input logic
  useEffect(() => {
    if (controls.forward && controls.run) {
      // Handle sprint
      console.log('Sprinting forward');
    }
    
    if (touchControls.joystick.isPressed) {
      // Handle mobile joystick
      console.log('Joystick angle:', touchControls.joystick.angle);
    }
  }, [controls, touchControls]);
  
  return null;
}
 
// Use in your app
function App() {
  return (
    <CharacterConfigProvider character={characterConfig}>
      <FIBER.Canvas>
        <ExperienceWrapper>
          <CustomInputHandler />
          <Scenario />
          <Player />
        </ExperienceWrapper>
      </FIBER.Canvas>
    </CharacterConfigProvider>
  );
}

Custom UI Integration

Integrating with your own UI components:

import { useActorContext } from 'react-3rd-person-character';
 
function CharacterUI() {
  const { characterState, character } = useActorContext();
  
  return (
    <div className="character-ui">
      <div className="character-name">{character.name}</div>
      <div className="character-state">
        State: {characterState.value}
      </div>
      <div className="character-stats">
        <div>Speed: {character.movement.movementSpeed}</div>
        <div>Mass: {character.velocity.mass}</div>
      </div>
    </div>
  );
}
 
function GameUI() {
  return (
    <div className="game-ui">
      <CharacterUI />
      <div className="controls-hint">
        WASD to move, Space to jump, Shift to run
      </div>
    </div>
  );
}

Physics Integration

Custom physics setup and interactions:

function PhysicsExample() {
  return (
    <CharacterConfigProvider character={characterConfig}>
      <FIBER.Canvas>
        <RAPIER.Physics>
          <ExperienceWrapper>
            <Scenario />
            <Player />
            
            {/* Custom physics objects */}
            <RAPIER.RigidBody position={[5, 2, 0]}>
              <RAPIER.CuboidCollider args={[1, 1, 1]} />
              <mesh>
                <boxGeometry args={[2, 2, 2]} />
                <meshStandardMaterial color="red" />
              </mesh>
            </RAPIER.RigidBody>
            
            <RAPIER.RigidBody position={[-5, 1, 0]} type="fixed">
              <RAPIER.CuboidCollider args={[2, 0.5, 2]} />
              <mesh>
                <boxGeometry args={[4, 1, 4]} />
                <meshStandardMaterial color="yellow" />
              </mesh>
            </RAPIER.RigidBody>
          </ExperienceWrapper>
        </RAPIER.Physics>
      </FIBER.Canvas>
    </CharacterConfigProvider>
  );
}

Mobile-First Design

Optimized for mobile devices:

import { useDevice } from 'use-device-react';
 
function MobileOptimizedApp() {
  const { isDesktop, isMobile } = useDevice();
  
  const mobileCharacterConfig = new CharacterBuilder({ fps: 60 })
    .withName('Mobile Player')
    .withCameraSettings({
      radius: isMobile ? 4 : 6, // Closer camera on mobile
      cameraOffsetY: isMobile ? 1.5 : 2
    })
    .withMovement({
      movementSpeed: isMobile ? 4 : 6, // Slower on mobile
      boostSpeed: isMobile ? 6 : 8
    })
    .with3DModels('/models/player', PlayerModel, Scenario3D)
    .build();
  
  return (
    <CharacterConfigProvider character={mobileCharacterConfig}>
      <FIBER.Canvas>
        <ExperienceWrapper>
          <Scenario />
          <Player />
        </ExperienceWrapper>
      </FIBER.Canvas>
      
      {/* Conditional mobile controls */}
      {!isDesktop && (
        <div className="mobile-controls">
          <StyledMobileControls />
          <div className="mobile-hints">
            <p>Use joystick to move</p>
            <p>Tap buttons for actions</p>
          </div>
        </div>
      )}
    </CharacterConfigProvider>
  );
}

Performance Optimization

Optimized for high-performance applications:

function OptimizedApp() {
  const characterConfig = new CharacterBuilder({ fps: 60 })
    .withName('Optimized Player')
    .withCameraSettings({ radius: 5 })
    .withMovement({ movementSpeed: 5 })
    .with3DModels('/models/player', OptimizedPlayerModel, OptimizedScenario)
    .build();
  
  return (
    <CharacterConfigProvider character={characterConfig}>
      <FIBER.Canvas
        gl={{
          powerPreference: "high-performance",
          antialias: false, // Disable for performance
          stencil: false,
          depth: false
        }}
        shadows={false} // Disable shadows for performance
      >
        <ExperienceWrapper>
          <Scenario />
          <Player />
        </ExperienceWrapper>
      </FIBER.Canvas>
    </CharacterConfigProvider>
  );
}
 
// Optimized models with LOD
function OptimizedPlayerModel() {
  return (
    <LOD distances={[0, 10, 50]}>
      <mesh>
        <capsuleGeometry args={[0.5, 1, 8, 16]} />
        <meshStandardMaterial color="blue" />
      </mesh>
      <mesh>
        <capsuleGeometry args={[0.5, 1, 4, 8]} />
        <meshStandardMaterial color="blue" />
      </mesh>
      <mesh>
        <capsuleGeometry args={[0.5, 1, 2, 4]} />
        <meshStandardMaterial color="blue" />
      </mesh>
    </LOD>
  );
}

Multi-Character Setup

Managing multiple characters:

function MultiCharacterExample() {
  const player1Config = new CharacterBuilder({ fps: 60 })
    .withName('Player 1')
    .withCameraSettings({ radius: 5 })
    .withMovement({ movementSpeed: 5 })
    .with3DModels('/models/player1', Player1Model, Scenario3D)
    .build();
    
  const player2Config = new CharacterBuilder({ fps: 60 })
    .withName('Player 2')
    .withCameraSettings({ radius: 5 })
    .withMovement({ movementSpeed: 5 })
    .with3DModels('/models/player2', Player2Model, Scenario3D)
    .build();
  
  return (
    <div className="multi-player">
      <CharacterConfigProvider character={player1Config}>
        <FIBER.Canvas>
          <ExperienceWrapper>
            <Scenario />
            <Player />
          </ExperienceWrapper>
        </FIBER.Canvas>
      </CharacterConfigProvider>
      
      <CharacterConfigProvider character={player2Config}>
        <FIBER.Canvas>
          <ExperienceWrapper>
            <Scenario />
            <Player />
          </ExperienceWrapper>
        </FIBER.Canvas>
      </CharacterConfigProvider>
    </div>
  );
}

Complete Game Example

A more complete game setup:

function CompleteGame() {
  const [gameState, setGameState] = useState('menu');
  const [score, setScore] = useState(0);
  
  const gameCharacterConfig = new CharacterBuilder({ fps: 60 })
    .withName('Game Hero')
    .withCameraSettings({ radius: 6 })
    .withMovement({ movementSpeed: 6, boostSpeed: 10 })
    .with3DModels('/models/hero', GameHeroModel, GameWorld)
    .build();
  
  if (gameState === 'menu') {
    return (
      <div className="menu">
        <h1>Adventure Game</h1>
        <button onClick={() => setGameState('playing')}>
          Start Game
        </button>
      </div>
    );
  }
  
  return (
    <div className="game">
      <CharacterConfigProvider character={gameCharacterConfig}>
        <FIBER.Canvas shadows>
          <ExperienceWrapper>
            <Scenario />
            <Player />
            <GameObjects />
          </ExperienceWrapper>
        </FIBER.Canvas>
        
        <div className="game-ui">
          <div className="score">Score: {score}</div>
          <button onClick={() => setGameState('menu')}>
            Back to Menu
          </button>
        </div>
      </CharacterConfigProvider>
    </div>
  );
}

These examples demonstrate various use cases and integration patterns. You can combine and modify them to suit your specific needs.