Difference between revisions of "Modular Pathfinding Kit"

From Second Life Wiki
Jump to navigation Jump to search
m (fixed glaring scripting error before anyone noted ;-))
Line 34: Line 34:
}
}
on_rez() //good to include so it scuttles off when rezzed from inventory or a rezzer
on_rez(integer start_param) //good to include so it scuttles off when rezzed from inventory or a rezzer
{
{
startup();
startup();
Line 40: Line 40:
}
}
</lsl>
</lsl>
 
==Behavioural Tropes==
==Behavioural Tropes==



Revision as of 16:25, 26 July 2012

There is a lot of options for how to create pathfinding creatures. It’s best to start by having an idea of what you want to create, either a real world analogue like a horse or a dog, or something mythical such as a ghost or fairy.

things that don’t work (at present):
   1. 3D spatial movement - for example underwater free movement or aerial free movement
   2. mixing physics and pathfinding (for instance using llApplyImpulse with a pathfinding character)

Quick Guide

> object + pathfinding character skeleton + behavioural tropes = autonomous pathfinding creature

This guide gives quick guidance of how to create specific behavioural tropes (commonly found autonomous characteristics) and doesn't go into detail about individual functions - see Pathfinding LSL Functions for more details.

Components

pathfinding character skeleton

<lsl> doSomething() { //insert pathfinding functionality here }

startup() //called as necessary from events such as script reset or rezzing { llCreateCharacter([CHARACTER_MAX_SPEED, 25, CHARACTER_DESIRED_SPEED, 15.0]); //create the character doSomething(); }

default { state_entry() //standard event, called when the script resets { startup(); }

on_rez(integer start_param) //good to include so it scuttles off when rezzed from inventory or a rezzer { startup(); } } </lsl>

Behavioural Tropes

flying/hovering

3D spatial free flight is at present unavailable. We can however fake it but one of the following methods:

  1. create a small object with a tall root prim - not recommended, causes excessive collisions
  2. create a small object and create a large character envelope - recommended

The example below creates a character with a 1.5m tall and 0.25m wide envelope. If the object itself if 0.25 it will appear to be "flying", or, more accurately, hovering.

<lsl> llCreateCharacter([CHARACTER_MAX_SPEED, 25, CHARACTER_DESIRED_SPEED, 15.0, CHARACTER_RADIUS, 0.25, CHARACTER_LENGTH, 1.5]); </lsl>

In order to further the illusion of flying we can do a number of things. If the creature is something that works at near ground height, such as a hummingbird or dragonfly just use one of the movement tropes, such as wandering.

If you want the creature to fly higher than the 10.0m maximum character height (which will give a centre of mass height of 5.0m), we can use a platform. If you want a parrot to fly between trees place a prim or series of prims that stretch between them and use something like llPatrolPoints or llWanderWithin. Even if you use a series of narrow pathways the pathfinding will find a way to valid targets.

We can make a really nice effect by using a mesh surface with an undulating form - the creature will follow the dips and rises giving the appearance of varying flight.

For ground hugging creatures we can something similar that looks natural for hovering creatures by dynamically changing the envelope size of the creature by calling llUpdateCharacter, as below. Call this function repeatedly with different values and the character will change the level it hovers at.

<lsl> llUpdateCharacter(CHARACTER_LENGTH, 6.5); </lsl>

fleeing

There are two distinct behaviours we can exploit. If we wanted a character to run from a specific point such as an explosion we can use llFleeFrom. If we want to run from a specific object or agent (avatar) use llEvade

hiding

Part of llEvade is hiding (blocked line of sight) from the agent/object the character is fleeing from. See llEvade for more details.

move-wait-repeat

By responding to path events we can create varying behaviour. All pathfinding functions are moving to specific targe coordinates, whether determined randomly by something like llWanderWithin, specific as in llPatrolPoints or even llPursue.

Have a look at Path update for guidance, and see the example script in the multiple states discussion at the end of this article.

chase

Use llPursue and specify a target object or avatar.

hunt

Use a sensor of some description, such as llSensorRepeat or some sort of collision detection such llVolumeDetect

wait

To make a character pause you can use a state change with a new event trigger making the character active again, or simply use something like:

<lsl> llExecCharacterCmd([CHARACTER_CMD_STOP]); llSleep(15.0); llWanderWithin(llGetPos(), <10.0, 10.0, 5.0>, []); //resume with any desired pathfinding function </lsl>

It's a personal preference but generally speaking state changes are easier to manage and debug.

wandering

Use llWanderWithin to specify an area to wander within.

hopping/jumping

Use llExecCharacterCmd(CHARACTER_CMD_JUMP, [height, 1.0]) to make the character hop 1m into the air. If done with a timer event while using another pathfinding command such as llWanderWithin the hop will have an arc rather than just vertical movement.

Optional Extras

To add to immersion it's worth adding some extras. If desired add...

animation

see Animation Streamlined for tips

sounds

see Creature Sounds and Event Driven Sounds

more than one state

States are an excellent way of managing pathfinding creatures, but note however that pathfinding behaviour survives state changes - it's simple a simpler way of managing a status than using a variable. See the example below, which simulates a rabbit grazing.

<lsl> animateGrassEating() { //insert a cunning grass eating animation here }

animateTailWiggle() { //insert very cute tail wiggle animation here }

startup() //called as necessary from events such as script reset or rezzing { llCreateCharacter([CHARACTER_MAX_SPEED, 2, CHARACTER_DESIRED_SPEED, 1.0]); //create the character state wander; }

default { state_entry() //standard event, called when the script resets { startup(); }

on_rez() //good to include so it scuttles off when rezzed from inventory or a rezzer { startup(); } }

state wander { state_entry() { llWanderWithin(llGetPos(), <10.0, 10.0, 5.0>, []); }

path_update(integer type, list reserved) { if (type == PU_SLOWDOWN_DISTANCE_REACHED) { //we're near the goal, we need to switch behaviour llExecCharacterCmd([CHARACTER_CMD_SMOOTH_STOP]); state graze; } } }

state graze { state_entry() { animateGrassEating(); llSetTimerEvent(15.0); //replace this with random timer, see next section }

timer() { llSetTimerEvent(0); animateTailWiggle(); state wander; } } </lsl>

Random Timers

In the example above the rabbit eat grass for 15 seconds then wanders again, for a random period defined by how long it takes to reach it's next random wander target. This 15 second wait will make it an easy target for hunting creatures or agents (hunting game? what if this was a zombie bunny with a taste for brains? hmm, brains).

To make things less predictable use:

<lsl> llSetTimerEvent(llFrand(15.0)); </lsl>

This generates a random graze/brain eating of between 0s and 15s, each time it's called.


Return to Good_Building_Practices