bw logo

Chapter 5. How to Move Client-Controlled Entities

All client-side movement is done using the physics object that acts like a controller, sending position updates to the server.

The navigation mesh is not present on the client, so the functions seek() and chase() must be used. They provide simple direct movement, following the terrain and colliding with obstacles.

5.1. seek()

5.1.1. Mouse click movement

In this example we will use seek to implement a simple mouse click-based movement.

To access this functionality in the FantasyDemo, press Z to bring up the cursor, and right-click on the terrain to move.

def moveKey( self, isDown ):
 if isDown:
  mp = GUI.mcursor().position
  type, target = collide.collide( mp.x, mp.y )
  if type == collide.COLLIDE_TERRAIN:
   self._movePlayer( target )
  elif type == collide.COLLIDE_ENTITY:
   self._movePlayer( target.position )

def _movePlayer( self, position ):
 player   = BigWorld.player()
 velocity = player.runFwdSpeed
 timeout  = 1.5 * (position - player.position).length / velocity
 curr_yaw = (position - player.position).yaw
 destination = (position[0], position[1], position[2], curr_yaw)
 player.physics.velocity = (0, 0, velocity)
 player.physics.seek( destination, timeout, 10, self._seekCallback )
 self.isMoving = True

client/MouseControl.py

Note

The destination needed by seek() is a four-member tuple containing the position and yaw.

5.1.2. Coordinated actions

The seek method is often used in conjunction with coordinated actions. These are actions involving two models, such as a handshake.

The position and yaw needed for the actions to line up can be extracted from the action, as in the following excerpt:

self.physics.seek( partner.model.Shake_B_Accept.seekInv, 5.0, 0.10, onSeek )
self.physics.velocity = ( 0, 0, self.walkFwdSpeed )

client/Avatar.py

5.2. chase()

To demonstrate the chase function we will implement a /follow chat console command in FantasyDemo. The command will cause the player to follow the targeted entity, until they press a movement key breaking the pursuit.

5.2.1. Handling the command

The Fantasy Demo chat console will automatically resolve the typed ‘/follow’ command to a function call. All we need to do is add the following function to the ConsoleCommands.py module.

def follow( player, string ):
    # Follow the current target
    if BigWorld.target() != None:
        player.physics.chase( BigWorld.target(), 2.0, 0.5 )
        player.physics.velocity = ( 0, 0, 6.0 )

client/Helpers/ConsoleCommands.py

5.2.2. Stopping the pursuit

To cancel the chase action, we need to add the code below to PlayerAvatar's moveForward, moveBackward, moveLeft and moveRight functions as in the excerpt below.

def moveForward(self, isDown):
    if isDown:
        if self.mouseControl.isMoving:
            self.mouseControl.cancel()
        
        if self.physics.chasing:
            self.physics.stop()

        self.forwardMagnitude = min(self.forwardMagnitude+1.0,1.0)
        if self.mode == Mode.COMBAT_CLOSE:
            if self.stance == Avatar.STANCE_BACKWARD:
                nst = Avatar.STANCE_NEUTRAL
            else:
                nst = Avatar.STANCE_FORWARD
            self.takeStance( nst )
    else:
        self.forwardMagnitude = max(self.forwardMagnitude-1.0,-1.0)

client/Avatar.py