IX: AI ⌘
Overview
Default Cube AI system contains 2 elements: AI game objects (wrapped in AiWrapper
) and AI nodes.
AI Nodes
AI nodes are static meshes with assigned custom property:
aiNode: [aiNodeId]
AI Maps
AI maps are an extension of AI nodes.
aiMap: [aiMapId]
Each vertex of a static mesh with an assigned AI map custom property will become an independent AI node. This makes creating complex routing and behaviour a bit easier.
AiWrapper & AI Behaviours
Each AI wrapper should provide an advice callback - using the game object position, state, and AI nodes.
const ai = new AiWrapper(object);
ai.registerBehaviour(() => {
if (!ai.hasTargetNode()) {
ai.setTargetNode(AiService.getAiNodeById('start'));
}
// NOTE Advice object can, for example, tell which direction should the character move next
let advice = {
w: false,
a: false,
s: false,
d: false
};
const distanceToTarget = ai.getDistanceToTargetNode();
const angleToTarget = ai.getGroundAngleToTargetNode();
if (distance > 0.1) {
advice.w = true;
} else {
ai.setTargetNode(AiService.getAiNodeById(ai.targetNodeId + 1));
}
if (angle < -0.1) {
advice.d = true;
} else if (angle > 0.1) {
advice.a = true;
}
return advice;
});
In the game object definition you can then retrieve AI advice on each frame:
SceneService.parseScene({
target: sceneModel,
gameObjects: {
heroObject: (object) => {
const ai = new AiWrapper(object);
// NOTE Behaviour code
TimeService.registerFrameListener(() => {
const { w, a, s, d } = ai.getAiBehaviour();
// NOTE React to AI advice
});
}
}
onCreate: () => {
// NOTE Rest of the code
}
});
Pathfinding
Using Default Cube navmaps and AiWrapper
you can find a path between two points on the scene. This allows you to plan a path an object should move along:
SceneService.parseScene({
target: sceneModel,
gameObjects: {
heroObject: (object) => {
const physics = new PhysicsWrapper(object);
physics.enableNavmaps();
// NOTE Pathfinding can sometimes move the object to the edge of a navmesh
// which PhysicsService will consider leaving a navmap. To prevent annoying
// stutter - consider disabling navmap clipping when using pathfinding.
physics.enableNoClip();
const ai = new AiWrapper(object);
ai.registerBehaviour(() => {
if (ai.hasTargetNode() && ai.getDistanceToTargetNode() <= 0.5) {
ai.setTargetNode(null);
}
if (!ai.hasTargetNode() || ai.path.length === 0) {
ai.setTargetNode(AiService.getAiNodeById('targetNode'));
ai.findPathToTargetNode();
}
return { targetNode: ai.getTargetNode() };
});
TimeService.registerFrameListener(() => {
const { targetNode } = ai.getAiBehaviour();
if (!targetNode) {
return;
}
const targetNodePosition = MathService.getVec3();
targetNode.getWorldPosition(targetNodePosition);
object.lookAt(targetNodePosition);
const heroDirection = MathService.getVec3();
object.getWorldDirection(heroDirection);
object.setSimpleVelocity(heroDirection);
MathService.releaseVec3(targetNodePosition);
MathService.releaseVec3(heroDirection);
});
}
},
onCreate: () => {
// NOTE Rest of the code
}
});