ODRP开发日记-靠近NPC触发交互(二)
书接上文,我们已经搭建好了服务器和开发环境,也初始化了JavaScript项目,接下来我们将开始正式编码
引入全局声明
由于@citizenfx/client及@citizenfx/server并不是一个模块,我们需要使用一种新的方式来引入全局声明,即在代码文件顶部添加这样一行
/// <reference types="@citizenfx/client" />
添加后,我们在编写代码时即可使用代码补全,编写时会舒服很多。
声明全局常量
我们希望当NPC离角色只有2.5M的时候才可以触发交互,因此我们事先声明好一个常量用于存储此值
const INTERACT_DISTANCE = 2.5;
检测附近的NPC
方法一:遍历GamePool
GmaePool是FiveM提供的一个实体缓存池,可以通过调用GetGamePool来获取。
GamePool有以下几种类型:
CPed: Ped池包含已加载的行人、动物和玩家CObject:Object池包含已加载的对象,比如门、发射物等CNetObject:NetObject池是网络同步对象池CVehicle:Vehicle池包含场景中已加载的载具CPickup:Pickup池包含可拾取的物品
GamePool本质上是一个一维数组,元素类型为int,其元素代表的是对象句柄,在js中,我们可以用number[]类型的变量来承接。
// 此值没有修改的必要,使用const声明为常量以符合代码规范
const pedPool: number[] = GetGamePool('CPed');
const objectPool: number[] = GetGamePool('CObject');
const netObjectPool: number[] = GetGamePool('CNetObject');
const vehiclePool: number[] = GetGamePool('CVehicle');
const pickupPool: number[] = GetGamePool('CPickup');
在C#中,GetGamePool的返回类型为dynamic,实际类型为ushort数组,不可直接使用,必须在for loop中强制转换为int才可使用。
获取CPed池
本文要做的是靠近NPC触发交互提示,因此我们获取CPed池即可。
const pedPool: number[] = GetGamePool('CPed');
缓存距离计算结果
对于每个Ped,我们都需要计算其与玩家之间的距离,取距离最近且满足我们要求的Ped进行交互,因此我们需要创建一个变量用于缓存我们计算到的最近距离,以及其对应的ped
let result:number | undefined = undefined;
let minimalDist = INTERACT_DISTANCE;
获取玩家PedId及坐标
在客户端侧的代码中,可以直接使用PlayerPedId()来获取指向玩家Ped的句柄,获取到句柄后,使用GetEntityCoords来获取实体坐标
const playerPed = PlayerPedId();// 玩家句柄
const playerCoords = GetEntityCoords(playerPed);// 玩家坐标
遍历
- 玩家判断
使用IsPedAPlayer来判断此句柄是否指向一名玩家,如果是玩家,则不满足我们的要求,跳过循环。
- 坐标获取
与获取玩家坐标一样,使用GetEntityCoords获取实体坐标即可
- 距离计算
FiveM提供了Vdist方法来快速计算坐标之间的距离,我们可以非常方便地调用它
我们只需要分别传入两个位置的x、y、z坐标即可,其函数声明为
/**
* Calculates the distance between two points in 3D space. For performance reasons, consider using direct mathematical calculations for distance, as they can be more efficient than calling this native function.
* ```
* NativeDB Introduced: v323
* ```
* @param x1 X coordinate of the first point.
* @param y1 Y coordinate of the first point.
* @param z1 Z coordinate of the first point. Represents the height or elevation at the first point.
* @param x2 X coordinate of the second point.
* @param y2 Y coordinate of the second point.
* @param z2 Z coordinate of the second point. Represents the height or elevation at the second point.
* @return Returns the distance as a float between the two points (`x1`, `y1`, `z1`) and (`x2`, `y2`, `z2`) in the game world.
*/
declare function Vdist(x1: number, y1: number, z1: number, x2: number, y2: number, z2: number): number;
for (const ped of pedPool) {
if (IsPedAPlayer(ped)) continue;
const pedCoords = GetEntityCoords(ped);
const dist = Vdist(playerCoords[0]!, playerCoords[1]!, playerCoords[2]!, pedCoords[0]!, pedCoords[1]!, pedCoords[2]!);
if (dist < minimalDist) {
minimalDist = dist;
result = ped;
}
}
返回结果
遍历完成后,result变量即我们所需的结果,如果存在符合要求的NPC,则result是离玩家最近的NPC,否则result为undefined
方法二:射线检测
方法二我没跑通,等会的吧
绘制提示GUI
这一部分没什么细节,总共就三行
BeginTextCommandDisplayHelp('STRING');
AddTextComponentString('按 ~INPUT_CONTEXT~ 与 AIC 交谈');
EndTextCommandDisplayHelp(0, false, false, -1);
第一行:创建了一个Help Display绘制事件
第二行:向绘制事件添加要绘制的文本,其中INPUT_CONTEXT是占位符,可以显示按键图标
第三行:结束绘制事件,并渲染绘制结果
这三个函数的文档在这里可以查看

浙公网安备 33010602011771号