I can only +1 on @Deltakosh answer ![]()
I think the main issue is that implmenting AI is too specific to the individual project. I went through all major approaches starting with Behaviour Trees, followed by GOAP and UtilityAi. There is open source repos for each approach on GitHub. This stuff is in good supply
So it is more like getting the behaviour out of AI agents the way you want with whatever AI approach.
I do not think there is a way around trial and error.
In my case, it took me 5 or so rewrites of my AI code and I eventually settled with a GOAP/UtilityAi mix.
Version 0.0.154 is now live.
It’s been quite a while since my last update, but the project has been in active development ever since the last post.
Other than programming, I have also been working on game design document for a project which I intend to build using the engine.
I will be posting more regarding that project on my website www.sealdragon.com
But lets get back to the code!
State Machines
XState https://xstate.js.org/ is now part of the project, at least server-side.
It runs the machines for the current modes (editor, roam, social and team-deathmatch), and it also runs the bot machine.
I have been very happy with the integration of it, and I am glad I didn’t start building my own.
Team Deathmatch www.vrampage.com now runs team-deathmatch, it has 3 states (PRE, ACTIVE, POST).
I started with creating a simple diagram using UMLet extension for Visual Studio Code ( UMLet - Visual Studio Marketplace )
(And yes, I have been too lazy to update the diagram after adding more logic to the state machine)
The state machine running on the server-side also communicates to the client, which for example controls the various UI that will be shown for the PRE, ACTIVE and POST states:
Event-driven Entity Component System
So the initial implementation of the entity component system, had no performance optimization in mind, it was made quick and dirty, just how stuff usually tends to be when you start a new project.
Now it is event-driven, and other systems can now subscribe to entity component system, and listen for various events, such as:
onEntityAcquire(dto: EntityDTO, isClientOnly: boolean): void;
onEntityRelease(dto: EntityDTO, isClientOnly: boolean): void;
onEntityLocalUpdate(dto: EntityDTO, isClientOnly: boolean): void;
onEntityRemoteUpdate(dto: EntityDTO, isClientOnly: boolean): void;
onEntityComponentRelease(
entityId: string, dto: EntityComponentDTO, isClientOnly: boolean,): void;
onEntityComponentAcquire(
entityId: string, dto: EntityComponentDTO, isClientOnly: boolean,): void;
onEntityComponentLocalUpdate(
entityId: string, dto: EntityComponentDTO, isClientOnly: boolean,): void;
onEntityComponentRemoteUpdate(
entityId: string, dto: EntityComponentDTO, isClientOnly: boolean,): void;
For example the transform-system subscribes to the transform component and updates the transform node accordingly.
Editor
The editor functionality is still limited, and I still do a fair share of scene editing in JSON.
But now it is possible to use various editor tools, which are available if you enter the construct scene from the main menu (Press T).
The tools are:
Physics Tool (Select/Drag with left mouse, Change gizmo (position, rotation, scale) with right mouse)

Clone Tool (Left mouse to add clone, Right mouse to select entity to clone, R to randomize)

Remove Tool (Left mouse to remove entity, Right mouse to undo)

Play Tab
The first tab in the main menu (T) now shows the available experiences which you can choose to try out
Scoreboard
Scoreboard has been added to [TAB] and is currently primarily used for the team deathmatch.
Kill feed shows the latest kills in team deathmatch

Loading
When systems are in loading state or assets are being loaded, there is no longer a full loading screen, instead there is a spinner in the center of the screen.

I have been working on a new scene called Compact.
It is small and made for fast-paced team deathmatch.
I made it possible to define the amount of pellets a weapon should fire, for example the shotgun fires 12 pellets.

A whole separation of which entities to sync has now been implemented. That means static entites are not included in the state from 180.248 KB to 13.45 KB (92.5% reduction). Future goal will be excluding components which change infrequently.
Mod can now be defined as query parameter in the url, which makes it possible to start vrampage with editor mod, for example: https://vrampage.com?mod=editor
I am having an issue with Recast, where I can’t get it to work with the right parameters.
Currently these are my parameters:
{
cs: 1, ch: 1, walkableSlopeAngle: 90, walkableHeight: 1, walkableClimb: 1, walkableRadius: 1, maxEdgeLen: 12, maxSimplificationError: 1.3, minRegionArea: 8, mergeRegionArea: 20, maxVertsPerPoly: 6, detailSampleDist: 6, detailSampleMaxError: 1, borderSize: 1,};
and I get this result, where the navigation mesh hovers over the ground:
If I instead use the parameters from Babylonjs documentation:
{
cs: 0.2,
ch: 0.2,
walkableSlopeAngle: 35,
walkableHeight: 1,
walkableClimb: 1,
walkableRadius: 1,
maxEdgeLen: 12,
maxSimplificationError: 1.3,
minRegionArea: 8,
mergeRegionArea: 20,
maxVertsPerPoly: 6,
detailSampleDist: 6,
detailSampleMaxError: 1,
};
I get much better result, but now my agents can’t use the navigation mesh and ends up stuck at 0,0,0
You are killing it dude!
Epic!!
I believe it’s expected for the navmesh to hover above the ground. Though it’s strange how the agents are stuck at 0,0,0 for the second configuration.. I see that the walkableSlopeAngle is lower for the second configuration, though I wouldn’t expect that to have such a drastic effect
If you could share an image of the agents stuck at 0,0,0 with the navmesh debug drawing, that could give hints about the issue
Yep I also believe that to be the case, it’s just way too much with my current parameters, and the agents float too high above the ground.
Here you can see they get stuck:
Code:
const pos = this.navigationPlugin.getClosestPoint(context.newTargetPosition);
Logger.debug(
tag, \`Agent ${context.index} (${context.entity.entityId}) moving to new position ${pos} (requested: ${context.newTargetPosition})\`,);
this.crowd.agentGoto(
context.index, pos,);
Current working parameters, but with too much hovering:
[8:21:59:007] (navigation-mesh-builder) Navigation mesh built successfully with 47 meshes logger.ts:39:17
[8:22:0:306] (navigation-ground-system) Agent 4 (bot-bot-5-npc-0.es9krunw2e4) moving to new position {X: -13 Y: 1 Z: -22} (requested: {X: -13 Y: 3 Z: -22}) logger.ts:39:17
[8:22:0:533] (navigation-ground-system) Agent 2 (bot-bot-3-npc-0.qqb2eo2v4sq) moving to new position {X: -13 Y: 1 Z: -22} (requested: {X: -13 Y: 3 Z: -22}) logger.ts:39:17
[8:22:1:169] (navigation-ground-system) Agent 2 (bot-bot-3-npc-0.qqb2eo2v4sq) moving to new position {X: -13.711043357849121 Y: 1 Z: -23.56527328491211} (requested: {X: -13.71104314308213 Y: 3.014062552452087 Z: -23.56527355949351}) logger.ts:39:17
[8:22:1:337] (navigation-ground-system) Agent 2 (bot-bot-3-npc-0.qqb2eo2v4sq) moving to new position {X: -16.809349060058594 Y: 1 Z: -25.00363540649414} (requested: {X: -16.809348129075595 Y: 3.0149414423108096 Z: -25.003636274000442}) logger.ts:39:17
[8:22:1:532] (navigation-ground-system) Agent 1 (bot-bot-2-npc-0.afv2usxefh) moving to new position {X: -13 Y: 1 Z: 22} (requested: {X: -13 Y: 3 Z: 22}) logger.ts:39:17
[8:22:1:740] (navigation-ground-system) Agent 2 (bot-bot-3-npc-0.qqb2eo2v4sq) moving to new position {X: -22.697105407714844 Y: 1 Z: -20.881555557250977} (requested: {X: -22.798763821189006 Y: 3.0149999982129607 Z: -19.76331559668801})
Ideal parameters that does not work:
[8:21:0:283] (navigation-mesh-builder) Navigation mesh built successfully with 47 meshes
[8:21:1:143] (navigation-ground-system) Agent 4 (bot-bot-5-npc-0.qzkuhu7bxd) moving to new position {X: 0 Y: 0 Z: 0} (requested: {X: 4.684074932927061 Y: 3.0149999999999997 Z: -31.23396888193514}) logger.ts:39:17
[8:21:1:346] (navigation-ground-system) Agent 2 (bot-bot-3-npc-0.ngkxfntv81) moving to new position {X: 0 Y: 0 Z: 0} (requested: {X: 4.6749876665207335 Y: 3.0149999999999997 Z: -31.23410552797595}) logger.ts:39:17
[8:21:1:951] (navigation-ground-system) Agent 0 (bot-bot-1-npc-0.rb3hacg9eod) moving to new position {X: 0 Y: 0 Z: 0} (requested: {X: 4.6749520301818865 Y: 3.0149999999999997 Z: -31.234106063842773}) logger.ts:39:17
[8:21:2:572] (navigation-ground-system) Agent 3 (bot-bot-4-npc-0.7ixg9m4dp0t) moving to new position {X: -0.0002593994140625 Y: 0.20000028610229492 Z: 0.0003814697265625} (requested: {X: -0.0002593994140625 Y: 0.20004301070730435 Z: 0.0003814697265625}) logger.ts:39:17
[8:21:2:778] (navigation-ground-system) Agent 2 (bot-bot-3-npc-0.ngkxfntv81) moving to new position {X: 0 Y: 0 Z: 0} (requested: {X: 5.380539597396804 Y: 3.0149999999999997 Z: -31.21123640756205}) logger.ts:39:17
[8:21:2:983] (navigation-ground-system) Agent 4 (bot-bot-5-npc-0.qzkuhu7bxd) moving to new position {X: 0 Y: 0 Z: 0} (requested: {X: 8.427067311837057 Y: 3.0149999999999997 Z: -28.748845314018894}) logger.ts:39:17
[8:21:3:179] (navigation-ground-system) Agent 2 (bot-bot-3-npc-0.ngkxfntv81) moving to new position {X: 0 Y: 0 Z: 0} (requested: {X: 9.252386356929343 Y: 3.0149999999999997 Z: -28.596998313591367}) logger.ts:39:17
[8:21:3:401] (navigation-ground-system) Agent 0 (bot-bot-1-npc-0.rb3hacg9eod) moving to new position {X: 0 Y: 0 Z: 0} (requested: {X: 9.253120332755685 Y: 3.0149999999999997 Z: -28.59690285894364}) logger.ts:39:17
[8:21:3:591] (navigation-ground-system) Agent 4 (bot-bot-5-npc-0.qzkuhu7bxd) moving to new position {X: 0 Y: 0 Z: 0} (requested: {X: 9.256124608521889 Y: 3.0149999999999997 Z: -28.596902903576442}) logger.ts:39:17
[8:21:5:207] (navigation-ground-system) Agent 1 (bot-bot-2-npc-0.j0bjrp55bc) moving to new position {X: 0 Y: 0.20000028610229492 Z: 0} (requested: {X: -3.2359679444877937e-59 Y: 0.20000028610229492 Z: 5.476253444517805e-59})
I’m having a bit of trouble understanding what’s happening in the image
Are the white boxes physics colliders? I also don’t seem to see a navmesh debug drawing
Yep, the white boxes are physics colliders.
The navigation mesh is dark gray on the ground with black lines. It’s close to the ground, and the result looks good.
But the thing is when I use the parameters with cs and ch to 0.2 instead of 1.0, every time I use navigationPlugin.getClosestPoint it will return 0,0,0 as shown in the logging.
Just to make sure, reposting this: Digesting Duck: Recast Settings Uncovered Important to distinguish voxel units vs world units.
As for 0,0,0: Could this be a “query-failed” result?
Thanks for sharing @Joe_Kerr, that is very good information. I am also confident it is because of a failed query.
Right now I don’t have precisely matching sizes between my UniversalCamera, Agent Parameters and models + colliders. (For example the “eyes” of the camera does not match the “eyes” on the models.)
I think I will have to look into ensuring all those have proper setups, and then take a look on the navigation mesh generation afterwards.

All work and no play makes jack a dull boy, so of course instead of fixing my navigation issues, I worked on functionality for showing the equipped item of the player/npc.
Currently it’s only for the items that is being hold in the hands, but the same functionality will be extended to include other types of items that would be related to head, chest, legs, feet slots.
Can be tried out at vreboot.com
Cheers!














