虚拟世界/NPC

来自人工智能助力教育知识百科
跳转至: 导航搜索

百科首页 - 3D虚拟世界 - 音乐与人工智能 - 人工智能机器人 - 知识百科 - 关于我们 - 网站首页

脚本首页 | Vehicles | NPC | HTTP | 脚本间通信 | 翻译参考

开源社区提供了一组 OSSL NPC 函数,用于创建和操作机器人(NPC、BOT)。

这种方法的一个优点是,机器人不需要外部客户端,并且机器人的外观可以保存在岛屿备份中。一个缺点是可用的功能集非常有限,因为需要编写脚本方法来控制 NPC 的不同方面。此外,这样的机器人不能移动到它们被创建的岛屿之外。

服务器配置

  • Opensim.ini
[NPC]
    ;# {Enabled} {} {Enable Non Player Character (NPC) facilities} {true false} false
    Enabled = true
	
	;; several options to control NPCs creation
	
    ;# {AllowNotOwned} {} {allow NPCs to be created not Owned} {true false} true
	;; allow NPCs to be created not Owned {true false} default: true
    AllowNotOwned = true
	
    ;# {AllowSenseAsAvatar} {} {allow NPCs to set to be sensed as Avatars} {true false} true
	;; allow NPCs to set to be sensed as Avatars {true false} default: true
    AllowSenseAsAvatar = true
	
    ;# {AllowCloneOtherAvatars} {} {allow NPCs to created cloning any avatar in region} {true false} true
	;; allow NPCs to created cloning any avatar in region {true false} default: true
    AllowCloneOtherAvatars = true
	
    ;# {NoNPCGroup} {} {if true NPCs will have no group title, if false display "- NPC -"} {true false} true
	;; if true NPCs will have no group title, if false display "- NPC -" for easy identification {true false} default: true
    NoNPCGroup = true 

……

[OSSL]
    ;# {Include-osslEnable} {} {Include file for enabling and permissions for OSSL functions} {}
    ;; Optionally include file to enable OSSL functions and set permissions on who can use which.
    ;; If this INI file is not included, the OSSL functions are disabled.
    Include-osslEnable = "config-include/osslEnable.ini"

  • config-include/osslEnable.ini

在该文件中包含对各种os*函数的授权

相关API

osIsNpc

函数:integer osIsNpc(key npc)。
功能:判断一个指定的key是不是一个NPC。
返回值:NPC的状态

  • 如果提供的key是一个NPC,则返回值为TRUE (1),否则为FALSE (0);
  • 如果提供的key是一个NPC,但不存在于场景中,返回FALSE (0);

osNpcCreate

函数:

  • key osNpcCreate(string firstname, string lastname, vector position, string cloneFrom)
  • key osNpcCreate(string firstname, string lastname, vector position, string cloneFrom, integer options)

功能:创建一个名为firstname lastname, 位置为position, 外观为cloneFrom的NPC。
示例:


// touch to create a NPC clone of the toucher in front of this emitter
// NPC will move to the toucher, then will greet them.
// Touch again to remove the NPC
 
key npc;
vector toucherPos;
 
default
{
    touch_start(integer number)
    {
        vector npcPos = llGetPos() + <1,0,0>;
        osAgentSaveAppearance(llDetectedKey(0), "appearance");
        // coud use avatar UUID directly in osNpcCreate, but then NPC appearance is not persisted
        npc = osNpcCreate("ImYour", "Clone", npcPos, "appearance");
        toucherPos = llDetectedPos(0);
        state hasNPC;
    }
}
 
state hasNPC
{
    state_entry()
    {
        osNpcMoveTo(npc, toucherPos + <1,1,0>); 
        osNpcSay(npc, "Hi there! My name is " + llKey2Name(npc));
    }
 
    touch_start(integer number)
    {
        osNpcSay(npc, "Goodbye!");
        osNpcRemove(npc);
        npc = NULL_KEY;
        state default;
    }
}

[备注]

  1. 您可以从保存的外观记录卡名称或UUID或从登录到相同区域中克隆外观。
  2. 您可以使用以下函数创建和加载外观记录卡:osOwnerSaveAppearance,osAgentSaveAppearance,osNpcLoadAppearance,osNpcSaveAppearance。
  3. 在当前的OpenSimulator开发代码中(从2012年1月12日的提交c4972e77),已经添加了osNpcCreate()的重载版本。
  4. 关于重载函数key osNpcCreate(string firstname, string lastname, vector position, string cloneFrom, integer options)
    • options字段可以是OS_NPC_CREATOR_OWNED或OS_NPC_NOT_OWNED。
    • OS_NPC_CREATOR_OWNED将创建一个“被拥有”的NPC,只会响应与创建NPC相同的所有者脚本发出的osNpc *命令。
    • OS_NPC_NOT_OWNED将创建一个“无主”的NPC,它将响应任何具有OSSL权限的脚本来调用osNpc *命令。
    • 例如:key npc = osNpcCreate("ImYour", "Clone", npcPos, "appearance", OS_NPC_CREATOR_OWNED);
  5. 现有的没有options字段的osNpcCreate()函数将继续存在。

osGetNpcList

函数:list osGetNPCList( ).
功能:显示该区域中每个NPC的UUID、位置和名称的列表。
示例:

default
{
    touch_start(integer total_number)
    {
        list npcs = osGetNPCList();
        if (npcs == [])
            llSay(0, "You must be the owner. There is nobody else here who could have touched me.");
        else
            llSay(0, "NPC's in this region (without avatars):" + llList2CSV(npcs));
    }
}

osNpcGetOwner

函数:key osNpcGetOwner(key npc).
功能:获取NPC的所有者的UUID。

osNpcGetPos

函数:vector osNpcGetPos(key npc).
功能:返回NPC的当前位置。

osNpcGetRot

函数:rotation osNpcGetRot(key npc).
功能:获取化身的旋转。欧拉旋转(水平旋转)中只有Z平面的旋转才有意义。

osNpcLoadAppearance

函数:osNpcLoadAppearance(key npc, string notecard).
功能:从记事卡加载外观。此记事卡必须包含使用其中一个保存外观功能创建的外观数据。

osNpcMoveTo

函数:void osNpcMoveTo(key npc, vector position).
功能:移动NPC到指定位置。

osNpcMoveToTarget

函数:osNpcMoveToTarget(key npc, vector target, integer options).
功能:随着时间的推移,到达指定的地点。如何到达目的地取决于options的值。

  • OS_NPC_FLY :将飞到指定的位置。NPC将不会着陆,除非OS_NPC_LAND_AT_TARGET选项也被给出。
  • OS_NPC_NO_FLY : 不要飞到目标。NPC将尝试走向该地点。如果它在空中,那么NPC将一直处于无望的状态,直到另一个移动目标被提供或移动被停止。
  • OS_NPC_LAND_AT_TARGET : 如果给出,NPC正在飞行,那么它会在到达目标时着陆。如果给出OS_NPC_NO_FLY,那么这个选项不起作用。
    • OS_NPC_FLY和OS_NPC_NO_FLY是不能合并的选项 - NPC最终会做一个或另一个。如果你想让NPC飞行并落在目标上,那么OS_NPC_LAND_AT_TARGET必须与OS_NPC_FLY结合。
  • OS_NPC_RUNNING : 将NPC运行到给定的位置。

osNpcPlayAnimation

函数:osNpcPlayAnimation(key npc, string animation).
功能:在NPC的身份标识上播放动画。

osNpcRemove

函数:void osNpcRemove(key npc).
功能:移除指定key的NPC。
示例:

// sim-wide NPC killer
// kill all of NPCs in this SIM
// Attempts to kill agents too, but it will silently fail
// http://opensimulator.org/wiki/OsNpcRemove
 
default
{
    touch_start(integer number)
    {
        list avatars = llList2ListStrided(osGetAvatarList(), 0, -1, 3);
        integer i;
        llSay(0,"NPC Removal: No avatars will be harmed or removed in this process!");
        for (i=0; i<llGetListLength(avatars); i++)
        {
            string target = llList2String(avatars, i);
            osNpcRemove(target);
            llSay(0,"NPC Removal: Target "+target);
        }
    }
}

[备注] 如果化身是任何其他类型的代理(即用户的常规化身,而不是NPC)的UUID,则该功能将默认失败,不会擦除已存在的化身。

osNpcSaveAppearance

函数:key osNpcSaveAppearance(key npc, string notecard).
功能:将NPC的当前外表保存在资产库中的记事卡中。这包括身体部位数据,衣物和附件。如果同名的记事卡已经存在,则被替换。当这个函数被调用时,化身必须出现在该区域。化身的烘焙纹理(重现外观所必需的)被永久保存。

osNpcSay

函数:

  • void osNpcSay(key npc, int channel, string message).
  • void osNpcSay(key npc, string message).

功能:NPC在已给的通道表示信息(第二个通道为0).

osNpcSetProfileAbout

函数:osNpcSetProfileAbout(LSL_Key npc, string about).
功能:在创建的NPC的配置文件中设置。
示例:

//
// osNpcSetProfileImageScript Example
//

key npc = NULL_KEY;
string about = "I'm your Clone";
 
default
{
    touch_start(integer number)
    {
        if (npc == NULL_KEY)
        {
            osOwnerSaveAppearance("MyClone");
            llSetTimerEvent(2.0);
        }
 
        else
        {
            osNpcRemove(npc);
            npc = NULL_KEY;
            llRemoveInventory("MyClone");
        }
    }
 
    timer()
    {
        llSetTimerEvent(0.0);
        npc = osNpcCreate("John", "Smith", llGetPos() + <0.0, 0.0, 2.0>, "MyClone");
        osNpcSetProfileAbout(npc, about);
    }
}

osNpcSetProfileImage

函数:osNpcSetProfileImage(LSL_Key npc, string image).
功能:在创建的NPC的个人资料中设置图像。可以通过名称或UUID使用包含在资产库中的纹理。
示例:

//
// osNpcSetProfileImage Example
//
 
key npc = NULL_KEY;
string image = "My Photo";
 
default
{
    touch_start(integer number)
    {
        if (npc == NULL_KEY)
        {
            osOwnerSaveAppearance("MyClone");
            llSetTimerEvent(2.0);
        }
 
        else
        {
            osNpcRemove(npc);
            npc = NULL_KEY;
            llRemoveInventory("MyClone");
        }
    }
 
    timer()
    {
        llSetTimerEvent(0.0);
        npc = osNpcCreate("John", "Smith", llGetPos() + <0.0, 0.0, 2.0>, "MyClone");
        osNpcSetProfileImage(npc, image);
    }
}

osNpcSetRot

函数:osNpcSetRot(key npc, rotation rot).
功能:设置化身的旋转。只有在欧拉旋转中设置Z平面中的旋转将会产生任何有意义的效果(将化身指向一个方向或另一个方向)。设置X或Y欧拉值将导致化身以不确定的方式旋转。

osNpcShout

函数:void osNpcShout(key npc, int channel, string message).
功能:NPC在给定的通道上呼喊信息。

osNpcSit

函数:osNpcSit(key npc,key target,integer options).
功能:让一个NPC坐在一个物体上。
options : OS_NPC_SIT_NOW。

  • 让npc在可能的情况下,立即坐在prim上。这是唯一可用的选项,而且当前总是在选项字段中指定的任何内容上。
  • 如果prim有一个sit目标,那么无论在NPC和prim之间的距离,都要坐着。
  • 如果prim没有坐目标的话,如果prim在NPC的10米以内,那么坐就会成功。

osNpcStand

函数:osNpcStand(key npc).
功能:让一个坐着的NPC站起来。

osNpcStopAnimation

函数:osNpcStopAnimation(key npc, string animation).
功能:停止正在用指定NPC的密钥标识播放的动画。

osNpcStopMoveToTarget

函数:osNpcStopMoveToTarget(key npc).
功能:停止当前移动到目标的行为。

osNpcTouch

osNpcWhisper

函数:void osNpcWhisper(key npc, int channel, string message).
功能:NPC在给定的通道中低语指定消息。

OsOwnerSaveAppearance

函数:key osOwnerSaveAppearance(string notecard).
功能:将所有者的当前外观保存到资产库中的记事卡中。这包括身体部位数据,衣物和附件。如果同名的记事卡已经存在,则被替换。当这个函数被调用时,所有者必须在区域中。所有者的烘焙纹理(需要在NPC上重现外观)被永久保存。 示例:

// 
// osOwnerSaveAppearance exxample.
// This example creates the notecard with the user's appearance in the inventory of a prim.
// You will find the notecard in the contents of the prim after the script has run.
//
 
default
{
    state_entry()
    {
        string ownername = llKey2Name(llGetOwner());    // Retrieve the owner's key, and find out his/her name.
        string date = (string)llGetDate();              // Store the date in a string.
        string notecard_name = ownername+" "+date;      // Combine the name and the date for use as a notecard name.
        osOwnerSaveAppearance(notecard_name);           // Make the notecard.
    }
}

综合实例

综合实例1

在此实例中,囊括了大多数关于NPC的API,通过对话框的形式成功实现了一个NPC的建立、移动、说话、站立、坐下、显示位置等功能。

功能 菜单等级 功能描述
触碰 用户化身触碰物体开始相关操作
create 一级菜单 创建一个npc,可以输入npc的姓名,也可系统命名
remove 一级菜单 移除该区域所有npc,并删除相应生成的外观记事卡
other 一级菜单 点击可输入指定npc的key值,随后可对该npc进行相关操作
exit 一级菜单 点击以退出对话框
return 二级菜单 点击返回一级菜单
exit 二级菜单 点击以退出对话框
get... 二级菜单 返回给定npc的位置、旋转角度、拥有者
move 二级菜单 输入坐标位置,将npc移动到给定位置
stopMove 二级菜单 若npc在移动工程中,可停止移动
remove 二级菜单 移除指定npc,并返回一级菜单
sit 二级菜单 输入坐下物体的key值,npc将坐在该物体上
stand 二级菜单 若npc坐在某物体上,将会站立
say 二级菜单 npc将和你打招呼

功能示意图.png

//
//一个关于NPC的综合案例,通过对话框的形式实现相关功能
//

integer gchannel;	//记录指定监听通道
integer glistener;	//记录相应监听ID
key toucherID;		//记录触碰者ID
key npc;		//记录用户输入的NPC的key值
string msg;
list options;		//记录相关操作的列表
list choices;		//记录对指定NPC进行相关操作的列表
integer num = 1;	//记录已生成的NPC的数量

//删除除了脚本以外所有生成的文件
delete_all_other_contents()
{
    string thisScript = llGetScriptName();
    string inventoryItemName;
 
    integer index = llGetInventoryNumber(INVENTORY_ALL);
    while (index)
    {
        --index;        // (faster than index--;)
 
        inventoryItemName = llGetInventoryName(INVENTORY_ALL, index);
 
        if (inventoryItemName != thisScript)   
            llRemoveInventory(inventoryItemName);     
    }
}

//移除函数:移除岛屿上所有NPC
remove()
{
	list avatars = llList2ListStrided(osGetAvatarList(),0,-1,3);
	//llList2ListStrided:从列表的第一个条到最后,返回第三项
	integer i;
	llSay(0,"NPC Removal: No avatars will be harmed or removed in this process!");
    for (i=0; i<llGetListLength(avatars); i++)
	{
		string target = llList2String(avatars, i);
		osNpcRemove(target);
		llSay(0,"NPC Removal: Target "+target);
	}
	delete_all_other_contents();
	num = 1;	//重置NPC计数
}

default
{
	state_entry()
	{
		llSay(0, "Click here to start!");
	}
	touch_start(integer total_number)
	{
		toucherID = llDetectedKey(0);	//获取触碰者ID
		gchannel = (integer)(llFrand(5000)+5000);	//随机生成对话通道,避免对话通道冲突
		state init;
	}
}

state init
{
	state_entry()
	{
		msg = "create: create a npc. \nremove: remove all npc in the region. \n" +
			"other: operation to given npc.";
		glistener = llListen(gchannel,"","","");
		options = ["create","remove","other","exit"];
		llDialog(toucherID,msg,options,gchannel);	
	}
	//监听函数,获取用户的操作或提交的相关信息,下同
	
	listen(integer channel, string name, key id, string message)
	{
		if(message == "create"){
			llListenRemove(glistener);
			state create;
		}else if(message == "remove"){
			remove();
			llDialog(toucherID,msg,options,gchannel);
		}else if(message == "other"){
			llListenRemove(glistener);
			state other;
		}else{
			llListenRemove(glistener);
			state default;
		}
	}
}

//创建事件:创建一个NPC
state create
{
	state_entry()
	{
		glistener = llListen(gchannel,"","","");
		llTextBox(toucherID,"Enter the name of the npc:",gchannel);
	}
	listen(integer channel, string name, key id, string message)
	{
		if(channel == gchannel){
			llListenRemove(glistener);
			vector npcPos = llGetPos() + <1,0,0>;
			string npcName = (string)message;
			string npcIdname = (string)("clone"+num);
			osAgentSaveAppearance(toucherID,npcIdname);	//保存触碰者当前外观为记事卡中
			key NPC = osNpcCreate(npcIdname,npcName,npcPos,npcIdname);
			osNpcSay(NPC, "Hi there! My name is " + llKey2Name(NPC));
			num++;
			state init;
		}
	}
}

state other
{
	state_entry()
	{
		glistener = llListen(gchannel,"","","");
		llTextBox(toucherID,"The following operation are for a given NPC.\n"+
			"Now,Enter a key for an npc:",gchannel);
	}
	listen(integer channel, string name, key id, string message)
	{
		llListenRemove(glistener);
		npc = (string)message;
		
		//确定输入key值是否为在岛上存在的NPC
		if(!osIsNpc(npc))
		{
			msg = "The npc which key is " + npc + " is not existing in the region.";
			llSay(0,msg);
			state init;
		}
		state option;
	}
}

state option
{
	state_entry()
	{		
		msg = "Please select the operation you want to perform for the NPC.";
		choices = ["sit","stand","say","move","stopMove","remove","getPos","getRot","getOwner","return","exit"];
		glistener = llListen(gchannel,"","","");
		llDialog(toucherID,msg,choices,gchannel);
	}
	listen(integer channel, string name, key id, string choice)
	{
		if(choice == "sit"){
			llListenRemove(glistener);
			state SIT;
		}else if(choice == "stand"){
			osNpcStand(npc);
			osNpcSay(npc,"I'm standing.");
			llDialog(toucherID,msg,choices,gchannel);
		}else if(choice == "say"){
			osNpcSay(npc,"Hello!");
			llDialog(toucherID,msg,choices,gchannel);
		}else if(choice == "move"){
			llListenRemove(glistener);
			state move;
		}else if(choice == "stopMove"){
			osNpcStopMoveToTarget(npc);
			llSay(gchannel,"The npc has stop moving!");
			llDialog(toucherID,msg,choices,gchannel);
		}else if(choice == "remove"){
			llListenRemove(glistener);
			osNpcSay(npc, "Goodbye!");
			osNpcRemove(npc);
			state init;
		}else if(choice == "getPos"){
			vector pos = osNpcGetPos(npc);
			osNpcSay(npc,"The position of the npc is " + (string)pos);
			llDialog(toucherID,msg,choices,gchannel);
		}else if(choice == "getRot"){
			rotation rot = osNpcGetRot(npc);
			osNpcSay(npc,"The rotation of the npc is " + (string)rot);
			llDialog(toucherID,msg,choices,gchannel);
		}else if(choice == "getOwner"){
			key owner = osNpcGetOwner(npc);
			osNpcSay(npc,"The npc belongs to the user whose ID is " + (string)owner);
			llDialog(toucherID,msg,choices,gchannel);
		}else if(choice == "return"){
			llListenRemove(glistener);
			state init;
		}else{
			llListenRemove(glistener);
			state default;
		}
	}
}

//移动事件:将指定NPC移动到指定地点
state move
{
	state_entry()
	{
		msg = "Please input the new vector,in sample format,<x,y,z>";
		glistener = llListen(gchannel,"","","");
		llTextBox(toucherID,msg,gchannel);
	}
	listen(integer channel, string name, key id, string message)
	{
		if(channel == gchannel){
			llListenRemove(glistener);
			vector position = (vector)message;
			osNpcMoveTo(npc, position);
			osNpcSay(npc,"The npc has been moved to a given position.");
			state option;
		}
	}
}

//坐下事件:命令指定NPC坐在某物体上,最好在该物体上有相应的设置坐下的脚本
state SIT
{
	state_entry()
	{
		msg = "Please input the key what the npc sit on.";
		glistener = llListen(gchannel,"","","");
		llTextBox(toucherID,msg,gchannel);
	}
	listen(integer channel, string name, key id, string message)
	{
		if(channel == gchannel){
			llListenRemove(glistener);
			key sitNpc = (string)message;
			osNpcSit(npc, sitNpc, OS_NPC_SIT_NOW);
			osNpcSay(npc,"I'm sitting.");
			state option;
		}
	}
}

综合实例2

在此实例中,囊括了大多数关于NPC的API,通过在指定通道上通讯的形式成功实现了一个乃至多个NPC的建立、移动、说话、站立、坐下等功能。
操作说明:

  • 输入“/10 help”获取命令菜单(10为给定通讯通道,可以在脚本中进行修改)
  • 输入“/10 show”显示具体每个命令的通讯格式
  • 在执行针对于指定NPC的相关操作之前,利用npckey命令输入指定NPC的key值,即为其UUID
命令 通讯格式 功能描述
create create <notecard-name> Create NPC from a stored notecard
create create <notecard-name> <firstName> <lastName> Create NPC from a stored notecard
createm createm <num> Create a specified number of NPCS
remove remove Remove the current NPC
delete delete Delete all generated files except the script
npckey npckey <npc-uuid> Enter a key value for an NPC
say say NPCS greet you
clone clone <notecard-name> Clone own appearance to a notecard
load load <notecard-name> Load appearance on notecard to current npc
save save <notecard-name> Save appearance of current NPC to notecard
rot rot NPCS rotate in one direction
rotabs rotabs <angles> NPC rotation specifies angles
move move <x> <y> <z> move to absolute position
movetarget movetarget Move NPCS to users nearby
stop stop If the NPC is moving, it will stop moving
sit sit <target-uuid> Make a NPC sit on the specified object
stand stand If the NPC is sitting, it will stand
key npc;
integer listenChannel = 10;

default
{
	state_entry()
	{
		llListen(listenChannel,"",NULL_KEY,"");        //监听listenChannel对话通道
		llSetText("Listening on " + listenChannel, <0, 255, 0>, 1);    //设置提示语显示在物体上方
		llOwnerSay("Say '/" + (string)listenChannel + " help' for commands");
	}

	listen(integer channel, string name, key id, string msg)
	{
		if (msg != "")
		{
			list commands = llParseString2List(msg, [ " " ], []);    //接收用户在通道listenChannel输入的对话,并以空格分隔成多个字段
			string msg0 = llList2String(commands, 0);    //msg0记录用户输入的第一个字段
			string msg1 = llList2String(commands, 1);
			string msg2 = llList2String(commands, 2);
			string msg3 = llList2String(commands, 3);

			if (msg0 == "create" && msg1 != "")//创建一个npc
			{
				osOwnerSaveAppearance("appearance");    //保存所有者的外观为记事卡appearance

				string notecardName = "appearance";
				string firstName = "clone";
				string lastName = "tom";
				//判断用户输入的外观卡是否有效,若无效,则用默认记事卡
				string thisScript = llGetScriptName();
				string inventoryItemName;
				integer index = llGetInventoryNumber(INVENTORY_ALL);
				while (index)
				{
					--index;        // (faster than index--;)
					inventoryItemName = llGetInventoryName(INVENTORY_ALL, index);
					if (inventoryItemName == msg1)
						notecardName = msg1;
				}
				//在用户通信格式正确且输入了npc的姓名的情况下,设置npc的姓名
				if(msg2 != "" && msg3 != "")
				{
					firstName = msg2;
					lastName = msg3;
				}

				npc = osNpcCreate(firstName, lastName, llGetPos() + <5, 5, 0>, notecardName);    //生成给定条件的npc
				llOwnerSay("Good morning! ");
			}
			else if (msg0 =="createm" && msg1 != "")    //创建多个npc
			{
				osOwnerSaveAppearance("appearance");
				vector pos = llGetPos();
				integer i;
				for (i = 0; i < (integer)msg1; i++)
				{
					osNpcCreate("clone", (string)i, pos + <8, 0, 0>, "appearance");
					llSleep(1);
				}
			}
			else if (msg0 == "remove" && osIsNpc(npc))    //删除指定npc
			{
				osNpcSay(npc, "You will pay for this with your liiiiiivvveeessss!!!.....");
				osNpcRemove(npc);
			}
			else if(msg0 == "delete")	//删除除了脚本以外所有生成的外观记事卡
			{
				string thisScript = llGetScriptName();
				string inventoryItemName;

				integer index = llGetInventoryNumber(INVENTORY_ALL);
				while (index)
				{
					--index;        // (faster than index--;)

					inventoryItemName = llGetInventoryName(INVENTORY_ALL, index);

					if (inventoryItemName != thisScript)
						llRemoveInventory(inventoryItemName);
				}
				llOwnerSay("All generated files except the script have been deleted.");
			}
			else if (msg0 == "say" && osIsNpc(npc))    //npc说话,打招呼
			{
				osNpcSay(npc, " I am your worst Nightmare, " + (string)npc);
			}
			else if (msg0 == "move" && msg1 != "" && msg2 != "" && osIsNpc(npc))    //移动一个npc,输入的为npc移动的目标地址
			{
				vector pos = <(integer)msg1, (integer)msg2, 0>;
				//z值可输入,也可不输入,最终高度取决于地形
				if (msg3 != "")
				{
					pos.z = (integer)msg3;
				}

				osNpcMoveTo(npc, pos);
			}
			else if (msg0 == "movetarget" && osIsNpc(npc))    //移动npc到人物(用户)附近
			{
				osNpcMoveToTarget(npc, llGetPos() + <9,9,5>, OS_NPC_FLY|OS_NPC_LAND_AT_TARGET);
			}
			else if (msg0 == "rot" && osIsNpc(npc))    //使npc旋转一个方向
			{
				vector xyz_angles = <0,0,90>; // 定义一个度数改变值
				vector angles_in_radians = xyz_angles * DEG_TO_RAD; //改变弧度
				rotation rot_xyzq = llEuler2Rot(angles_in_radians); //改变旋转度数
				rotation rot = osNpcGetRot(npc);
				osNpcSetRot(npc, rot * rot_xyzq);
			}
			else if (msg0 == "rotabs" && msg1 != "")    //使npc旋转给定角度
			{
				vector xyz_angles = <0, 0, (integer)msg1>;
				vector angles_in_radians = xyz_angles * DEG_TO_RAD;
				rotation rot_xyzq = llEuler2Rot(angles_in_radians);
				osNpcSetRot(npc, rot_xyzq);
			}
			else if (msg0 == "save" && msg1 != "" && osIsNpc(npc))    //保存npc的外观到给定名称的记事卡中
			{
				osNpcSaveAppearance(npc, msg1);
				llOwnerSay("Saved appearance " + msg1 + " to " + npc);
			}
			else if (msg0 == "load" && msg1 != "" && osIsNpc(npc))    //npc加载指定外观记事卡
			{
				osNpcLoadAppearance(npc, msg1);
				llOwnerSay("Loaded appearance " + msg1 + " to " + npc);
			}
			else if (msg0 == "clone"  && msg1 != "")    //保存拥有者外观到给定名称的记事卡中
			{
				osOwnerSaveAppearance(msg1);
				llOwnerSay("Cloned your appearance to " + msg1);
			}
			else if (msg0 == "stop" && osIsNpc(npc))    //使npc停止移动,如果npc在运动中的话
			{
				osNpcStopMoveToTarget(npc);
			}
			else if (msg0 == "sit" && msg1 != "" && osIsNpc(npc))    //使npc坐到指定物体上
			{
				osNpcSit(npc, msg1, OS_NPC_SIT_NOW);
			}
			else if (msg0 == "stand" && osIsNpc(npc))        //使npc站立起来
			{
				osNpcStand(npc);
			}
			else if(msg0 == "npckey" && msg1 != "")    //输入npc的key值,为其他操作提供npc的key值
			{
				npc = msg1;
				if(osIsNpc(npc))
					llOwnerSay("The value of key is valid.");
				else
					llOwnerSay("The value of key is not valid.Please re-enter!");
			}
			else if (msg0 == "help")    //显示所有命令
			{
				llOwnerSay("Commands are:");
				llOwnerSay("create \t createm \t remove \t delete \t npckey \n" +
					"\t say \t clone \t load \t save \t rot \t rotabs \n" +
					"\t move \t movetarget \t stop \t sit \t stand \n" );
				llOwnerSay("You can enter '/" + listenChannel +" instruction' to learn more about the communication rules");
			}
			else if(msg0 == "instruction")    //显示所有命令的具体说明
			{
				llOwnerSay("In addition to creating and removing operations, you need to enter or check whether the NPC's key values are valid before the rest of the operations are performed.");
				llOwnerSay("create <notecard-name> - Create NPC from a stored notecard.");
				llOwnerSay("create <notecard-name> <firstName> <lastName> - Create NPC from a stored notecard.");
				llOwnerSay("createm <num> - Create a specified number of NPCS.");
				llOwnerSay("remove - Remove the current NPC.");
				llOwnerSay("delete - Delete all generated files except the script.");
				llOwnerSay("npckey <npc-uuid> - Enter a key value for an NPC.");
				llOwnerSay("say - NPCS greet you.");
				llOwnerSay("clone <notecard-name> - Clone own appearance to a notecard.");
				llOwnerSay("load <notecard-name>  - Load appearance on notecard to current npc.");
				llOwnerSay("save <notecard-name>  - Save appearance of current NPC to notecard.");
				llOwnerSay("rot - NPCS rotate in one direction.");
				llOwnerSay("rotabs <angles> - NPC rotation specifies angles.");
				llOwnerSay("move <x> <y> <z> - move to absolute position.");
				llOwnerSay("movetarget - Move NPCS to users nearby.");
				llOwnerSay("stop - If the NPC is moving, it will stop moving.");
				llOwnerSay("sit <target-uuid> - Make a NPC sit on the specified object");
				llOwnerSay("stand - If the NPC is sitting, it will stand.");
			}
			else
			{
				llOwnerSay("I don't understand [" + msg + "]");
				llOwnerSay("You can enter '/" + listenChannel +" help' to learn more about the communication rules");
			}
		}
	}
}

专题探索与应用

NPC生命周期的控制

osNpcRemove可以移除化身,但有时候发现模型无缘无故消失了。如何在岛屿启动和关闭时生成和移除化身

  • 移除化身工具
integer gchannel; // dialog channel
integer glistener; // holds the listener handle;

default
{
    state_entry()
    {
        llSay(0, "单击我开始");
    }
    touch_start(integer x)
    {
        if (llDetectedKey(0) == llGetOwner())  {
            gchannel = llCeil(llFrand(5000) + 5000);
            glistener = llListen(gchannel,"","","");

            llTextBox(llDetectedKey(0), "输入一个NPC的KEY", gchannel);
        }
    }
     listen(integer channel, string name, key id, string message)
    {
        if(channel==gchannel){
            llListenRemove(glistener);
            key npc=(key)message;
            osNpcSay(npc, "Goodbye!");
            osNpcRemove(npc);
        }
    }
}

NPC形象如何个性化

目前来看,可以通过制作一个化身,然后讲将化身保存下来,然后供其他使用。

可以研究下danger上的化身

如何控制NPC

包括移动、控制动作等

如何与NPC对话

如何让NPC接收到化身或物体与他的通信