# Character movement programming

<figure><img src="https://2195662805-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUL3kt9YBybm9hHgVsG1h%2Fuploads%2Fgit-blob-4828f611b780f189892ce0b76bd70c7b04c5e985%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

**Level:** Beginner

In this tutorial we will explain how to setup a playable human-like character with keyboard controls and animations that are synced to the movement. This is similar to the [starter-project-third-person-shooter](https://docs.hology.app/getting-started/starter-project-third-person-shooter "mention") and we will go into details of how to set these up so that you can understand what is happening and how to extend it.

The complete code for this project can be cloned from GitHub.

<https://github.com/hologyengine/movement-tutorial>

{% hint style="info" %}
**Prerequisites**

Before continuing, you need to create a new project and familiarise yourself with the basics of the editor. Go through the article [the-first-steps](https://docs.hology.app/getting-started/the-first-steps "mention") to learn how to set up a project. Then learn how to navigate in the editor using the articles in [editor-basics](https://docs.hology.app/getting-started/editor-basics "mention").

While some coding skills are useful, all the code needed will be provided in this tutorial.
{% endhint %}

## Set up the scene

After creating the project, you have an empty scene that you can use for this tutorial. We will just create a couple of objects in this scene to illustrate how to make a character. In order for our character to have something to walk on, we will add a landscape shape. We will also add a spawn point actor so that we can spawn the playable character in the scene from game code.

In the Asset browser, select Shapes and scroll down to find the Landscape item.

<figure><img src="https://2195662805-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUL3kt9YBybm9hHgVsG1h%2Fuploads%2Fgit-blob-d78a4f802951e0def54e56eab0abbba554627ddd%2FScreenshot%202024-05-26%20160253.png?alt=media" alt=""><figcaption></figcaption></figure>

Right click the Landscape item click "Add to scene". You will be prompted with some options for creating the landscape. For the purpose of this tutorial, you can leave the defaults as they are and click "Create".

Next, we need to create a spawn point. In the Asset browser, select Actors and then right click Spawn Point and add it to the scene. Click anywhere on the landscape you created earlier to place the spawn point.

<figure><img src="https://2195662805-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUL3kt9YBybm9hHgVsG1h%2Fuploads%2Fgit-blob-9ffc64afdb3651e011be9b037817b72feed05f33%2Fimage%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1).png?alt=media" alt=""><figcaption></figcaption></figure>

## Import the character asset

We need a 3D models for our character that has animations that we can play. For this tutorial, we will use a character from an asset pack made by Kenney.

{% embed url="<https://kenney.nl/assets/mini-dungeon>" %}

Once you have downloaded the asset pack, unzip the file, find one of the character models such as **Models/GLB format/character-human.glb** and drag it into the editor to import it. We will later refer to this asset from game code.

<figure><img src="https://2195662805-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUL3kt9YBybm9hHgVsG1h%2Fuploads%2Fgit-blob-02f4c4b30c9ff24d3a3d3f0e33862b23e8cb304b%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

## Programming a character actor

We will now create an actor class to represent the playable character. Create a file in src/actors/character.ts. Add the code below to the file.

```typescript

import { Actor, AnimationState, AnimationStateMachine, AssetLoader, BaseActor, attach, inject } from "@hology/core/gameplay";
import { CharacterAnimationComponent, CharacterMovementComponent, CharacterMovementMode, ThirdPersonCameraComponent } from "@hology/core/gameplay/actors";

@Actor()
class Character extends BaseActor {
  private animation = attach(CharacterAnimationComponent)
  public readonly movement = attach(CharacterMovementComponent, {
    maxSpeed: 1.5,
    maxSpeedSprint: 4,
    maxSpeedBackwards: 1,
    snapToGround: 0.1,
    autoStepMinWidth: 0,
    autoStepMaxHeight: 0.1,
    fallingReorientation: true,
    fallingMovementControl: 0.2,
    colliderHeight: .4,
    colliderRadius: 0.2,
    jumpVelocity: 3.5
  })
  public readonly thirdPersonCamera = attach(ThirdPersonCameraComponent, {
    height: .7,
    offsetX: -0.3,
    offsetZ: 0.2,
    minDistance: 3,
    maxDistance: 3,
    distance: 3,
    autoActivate: false
  })

  private assetLoader = inject(AssetLoader)
  protected modelName = 'character-human'

  async onInit(): Promise<void> {
    const { scene, animations } = await this.assetLoader.getModelByAssetName(this.modelName)

    this.object.add(scene)

    const clips = Object.fromEntries(animations.map(clip => [clip.name, clip]))
  
    const idle = new AnimationState(clips.idle)
    const walk = new AnimationState(clips.walk)
    const sit = new AnimationState(clips.sit)
    const jump = new AnimationState(clips.jump)
    const sprint = new AnimationState(clips.sprint)

    idle.transitionsBetween(walk, () => this.movement.horizontalSpeed > 0)
    walk.transitionsBetween(sprint, () => this.movement.isSprinting)
    idle.transitionsTo(sit, elapsedTime => elapsedTime > 1)
    sit.transitionsTo(walk, () => this.movement.horizontalSpeed > 0)
  
    for (const state of [idle, walk, sit, sprint]) {
      state.transitionsBetween(jump, () => this.movement.mode === CharacterMovementMode.falling)
    }

    const sm = new AnimationStateMachine(idle)

    this.animation.setup(scene)
    this.animation.playStateMachine(sm)
  }

}

export default Character

```

First we attach a few components to quickly add a lot of functionality to our actor.

* **CharacterAnimationComponent** allows us to play animations using a state machine. Read more about [character-animation](https://docs.hology.app/gameplay/animation/character-animation "mention")
* **CharacterMovementComponent** can take player input and move the character with common modes such as standing idle, walking, running and jumping. Read more about [character-movement](https://docs.hology.app/gameplay/character-movement "mention")
* **ThirdPersonCameraComponent** moves the camera to behind the character. It provides options to offset the camera in various ways.

We load an asset using the injected AssetLoader using the name of the asset as can be found in the editor. This gives us a model's scene which is the character mesh and an array of animations.

In the rest of the code we set up the animation state machine. Read the guide on [animation-state-machine](https://docs.hology.app/gameplay/animation/animation-state-machine "mention") to get a better understanding of how these work.

## Set up player input

To be able to control the character's movement, we will create a player controller service that takes the player's input and forwards it to the character's movement component.

Create a file at src/services/player-controller.ts with the following code.

```typescript
import { inject, Service } from "@hology/core/gameplay"
import {
  InputService,
  Keybind,
  Mousebind,
  Wheelbind,
} from "@hology/core/gameplay/input"
import Character from "../actors/character"

enum InputAction {
  moveForward,
  moveBackward,
  moveLeft,
  moveRight,
  jump,
  sprint,
  rotate,
  rotateCamera,
  zoomCamera
}

@Service()
class PlayerController {
  private inputService = inject(InputService)
  private character?: Character

  constructor() {
    this.inputService.setKeybind(InputAction.jump, new Keybind(" "))
    this.inputService.setKeybind(InputAction.sprint, new Keybind("Shift"))
    this.inputService.setKeybind(InputAction.moveForward, new Keybind("w"))
    this.inputService.setKeybind(InputAction.moveBackward, new Keybind("s"))
    this.inputService.setKeybind(InputAction.moveLeft, new Keybind("a"))
    this.inputService.setKeybind(InputAction.moveRight, new Keybind("d"))
    this.inputService.setMousebind(
      InputAction.rotate,
      new Mousebind(0.01, true, "x")
    )
    this.inputService.setMousebind(
      InputAction.rotateCamera,
      new Mousebind(0.003, false, "y")
    )
    this.inputService.setWheelbind(
      InputAction.zoomCamera,
      new Wheelbind(0.0003, false)
    )
  }

  public setup(character: Character) {
    this.inputService.stop()
    this.character = character
    this.bindCharacterInput()
    this.inputService.start()
  }

  private bindCharacterInput() {
    if (this.character == null) return

    const playerMove = this.character.movement.directionInput
    const playerJump = this.character.movement.jumpInput
    const playerSprint = this.character.movement.sprintInput

    this.inputService.bindToggle(InputAction.jump, playerJump.toggle)
    this.inputService.bindToggle(InputAction.sprint, playerSprint.toggle)
    this.inputService.bindToggle(InputAction.moveForward, playerMove.togglePositiveY)
    this.inputService.bindToggle(InputAction.moveBackward, playerMove.toggleNegativeY)
    this.inputService.bindToggle(InputAction.moveLeft, playerMove.toggleNegativeX)
    this.inputService.bindToggle(InputAction.moveRight, playerMove.togglePositiveX)
    this.inputService.bindDelta(
      InputAction.rotate,
      this.character.movement.rotationInput.rotateY
    )
    this.inputService.bindDelta(
      InputAction.rotateCamera,
      this.character.thirdPersonCamera.rotationInput.rotateX
    )
    this.inputService.bindDelta(
      InputAction.zoomCamera,
      this.character.thirdPersonCamera.zoomInput.increment
    )

  }
}

export default PlayerController

```

This code uses the InputService. To get a better understanding of how this works, read the [player-input](https://docs.hology.app/gameplay/player-input "mention") guide.

## Spawning and setup

Now we just need to spawn the character and setup the player controller which we can do from the game class found in src/services/game.ts. Replace the contents of this file with the following.

```typescript

import { GameInstance, Service, World, inject } from '@hology/core/gameplay';
import { SpawnPoint } from '@hology/core/gameplay/actors';
import Character from '../actors/character';
import PlayerController from './player-controller';

@Service()
class Game extends GameInstance {
  private world = inject(World)
  private playerController = inject(PlayerController)

  async onStart() {
    const spawnPoint = this.world.findActorByType(SpawnPoint)
    const character = await spawnPoint.spawnActor(Character)
    character.thirdPersonCamera.activate()
    this.playerController.setup(character)
  }
}

export default Game

```

In this code we do a few things in the onStart function which gets called after the game has loaded and is ready to start.

1. We find a spawn point actor in the world.
2. Spawn the character using the spawn point and the Character actor class created earlier to get a new character that is placed in the scene with the position and rotation of the spawn point.
3. Call the setup function on the player controller with the character to bind the input events to this character's movement component.

This is everything necessary to have a playable character with animations.

## Testing the game

Run the following command in the terminal to run the game.

```
npm run dev
```

<figure><img src="https://2195662805-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUL3kt9YBybm9hHgVsG1h%2Fuploads%2Fgit-blob-4c83d561bc301eedea201561839a3a49e918325d%2Fimage.png?alt=media" alt="" width="326"><figcaption></figcaption></figure>

This should give you a link such as <http://localhost:5173> that you can open in your browser to see the game.

You should now see the game running like in the picture at the start of this tutorial. Try pressing W,A,S,D on your keybaord. Click somewhere in the scene with your mouse first to be able to rotate the character and camera by moving your mouse.

## Wrapping up

You now have a starting point of what could be a super fun game.

* Read more about the features covered in this tutorial by following the links.
* Design a more interesting world by importing more assets and place them in your scene
* Create a more interesting game. For example, add some game play mechanisms such as [trigger-volumes](https://docs.hology.app/gameplay/trigger-volumes "mention") that could trigger various things as you walk into them them or place other actors that does something when you are nearby and press a button.
