Shortnote

The following is a Wandering NPC created in OpenSim. It uses the same technique as in the Second Life wanderer, but applies the calculations, and a little bit extra, to OpenSim NPCs. Please refer to the super section to find out how to configure your OpenSim server in order to activate the necessary options that will allow you to create NPCs.

Video

Script Configuration

The script can be configured in the configuration section. It has to be placed along with two animations, for walking, respectively standing in the same primitive as this script. By default, using the configuration options below, OpenSim rezzes a clone of the owner that starts to wander around in a 5 meter radius circle.

The STANDING_ANIMATION_CYCLE_TIME represents how much time it takes for the standing animation to repeat. Using the default settings, the NPC will travel to a certain point and will trigger the standing animation for a random amount of STANDING_ANIMATION_CYCLES, each of them taking STANDING_ANIMATION_CYCLE_TIME seconds. This gives the NPC a fluid motion as well as takes care of the transitions from walking to standing and vice-versa.

Design Limitations and Considerations

  • The wandering NPC is designed to move around a flat surface with no height deltas (not ground-zero). This is because the current script does not take into account the ground contour. In the video, the Alter Ego is moving on a deck that is perfectly uniform.
  • The wandering NPC is managed by the semi-transparent primitive in the middle of the deck. This controlling primitive can be set to be fully transparent but it was kept there during the videos to illustrate the actual controller. The script could be altered to use offsets and could be placed at any location on the simulator.

Code

This script was tested and works on OpenSim version 0.7.4!

wandering_npc.lsl
///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2012 - License: GNU GPLv3      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////
 
//////////////////////////////////////////////////////////
//                  CONFIGURATION                       //
//////////////////////////////////////////////////////////
 
// Specifies the radius of the circle in which the NPC
// will travel in.
float MOVEMENT_RANGE = 5.0;
// How often to check that the NPC has reached a 
// waypoint.
float POLL_TIME = 0.1;
// Set this to the name of the animation for the walk
// equence. This animation has to be placed in the
// same primitive as this script.
string ANIMATION_WALK="Walk";
// Set this to the name of the animation for the stand
// sequence. This animation has to be placed in the 
// same primitive as this script.
string ANIMATION_STAND="Stand";
// How much time, in seconds, does a standing animation 
// cycle take? 
float STANDING_ANIMATION_CYCLE_TIME = 20;
// How many cycles to wait, randomly?
integer STANDING_ANIMATION_CYCLES = 3;
 
///////////////////////////////////////////////////////////////////////////
//                              INTERNALS                                //
///////////////////////////////////////////////////////////////////////////
 
vector wasCirclePoint(float radius) {
    float x = llPow(-1, 1 + (integer) llFrand(2)) * llFrand(radius*2);
    float y = llPow(-1, 1 + (integer) llFrand(2)) * llFrand(radius*2);
    if(llPow(x,2) + llPow(y,2) <= llPow(radius,2))
        return <x, y, 0>;
    return wasCirclePoint(radius);
}
 
// Vector that will be filled by the script with
// the initial starting position in region coordinates.
vector iPos = ZERO_VECTOR;
// Storage for destination position.
vector dPos = ZERO_VECTOR;
// Key of the NPC
key npcKey = NULL_KEY; 
 
default {
    state_entry() {
        llSetStatus(STATUS_PHANTOM, TRUE);
        osNpcRemove(llGetObjectDesc());
        iPos = llGetPos();
        osAgentSaveAppearance(llGetOwner(), "appearance");
        npcKey = osNpcCreate("Alter", "Ego", iPos, "appearance");
        llSetObjectDesc(npcKey);
        // It seems that 1 second is a magic wait-time that must 
        // separate the osNpcCreate from other commands or else 
        // the NPC rezzes as a cloud.
        llSetTimerEvent(1);
    }
    timer() {
        llSetTimerEvent(0);
        osNpcLoadAppearance(npcKey, "appearance");
        state wander;
    }
}
 
state wander
{
    state_entry() {
        dPos = iPos + wasCirclePoint(MOVEMENT_RANGE);
        osNpcPlayAnimation(npcKey, ANIMATION_WALK);
        osNpcMoveToTarget(npcKey, dPos, OS_NPC_NO_FLY);
        llSetTimerEvent(POLL_TIME);
    }
 
    timer() {
        // Another magic value (2), it seems that even on flat ground
	// the NPC never really reaches the destination position.
	// Instead, it reaches some point that is always (experimentally)
	// smaller than 2 meters. Hence the comparison.
        if (llVecDist(osNpcGetPos(npcKey), dPos) > 2) return; 
        osNpcStopAnimation(npcKey, ANIMATION_WALK);
        osNpcPlayAnimation(npcKey, ANIMATION_STAND);
        llSetTimerEvent(0);
        state wait;
    }
}
 
state wait {
    state_entry() {
        llSetTimerEvent(STANDING_ANIMATION_CYCLE_TIME * 1+llFrand(STANDING_ANIMATION_CYCLES-1));
    }
    timer() {
        llSetTimerEvent(0);
        osNpcStopAnimation(npcKey, ANIMATION_STAND);
        state wander;
    }
}

opensim/npc/wandering.txt · Last modified: 2022/04/19 08:28 by 127.0.0.1

Access website using Tor Access website using i2p Wizardry and Steamworks PGP Key


For the contact, copyright, license, warranty and privacy terms for the usage of this website please see the contact, license, privacy, copyright.