proposal: Artificial intelligence systems #176
Labels
No labels
CI
all
basisu
blog
bug
build
contributor-friendly
core
correctness
deferred
dev
direct3d-headers
docs
driver-os-issue
duplicate
dxcompiler
editor
examples
experiment
feature-idea
feedback
flac
freetype
gamemode
gkurve
glfw
gpu
gpu-dawn
harfbuzz
help welcome
in-progress
infrastructure
invalid
libmach
linux-audio-headers
long-term
mach
mach.gfx
mach.math
mach.physics
mach.testing
model3d
needs-triage
object
opengl-headers
opus
os/linux
os/macos
os/wasm
os/windows
package-manager
priority
proposal
proposal-accepted
question
roadmap
slipped
stability
sysaudio
sysgpu
sysjs
validating-fix
vulkan-zig-generated
wayland-headers
website
wontfix
wrench
www
x11-headers
xcode-frameworks
zig-update
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
hexops/mach#176
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
After looking into game AI systems I stumbled upon GOAP which a planning system used in games like F.E.A.R, Tomb Raider (2013), and more which is generic enough to be part of a game engine framework in that the core planner can be implemented without prior knowledge of the game. I think this would be a nice addition to mach given that zig comptime or the vtable pattern can provide the implementation/specialization of the system where needed.
The GOAP tl;dr as I understand it is:
Since I've just found it I have no code to show other than that I've found in presentations and papers.
I'd like to have something here, I think, but I'm not sure what exactly yet.
You should check out some of Dave Mark's work, and this thread in which he notes some scalability issues with GOAP.
Also this talk: https://www.gdcvault.com/play/1021848/Building-a-Better-Centaur-AI
When/If this is worked on, the ability of this module to be integrated into a GUI should be carefully considered, same as the ECS - and on that note, would be quite important to weigh how much integration with the ECS module there would/should be. Should it act like an extension to the ECS, or be its own separate set of logic modules that just happen to operate well in tandem with the ECS?
Whichever approach is chosen, it shouldn't be too hard, I don't think, to execute it, since zig lazily evaluates declarations, and thus if one doesn't want to use the AI module, they'd simply have to not use to any functions in the ECS module that refer to it. Although one could also argue that this could potentially lead to overly-tight coupling, so maybe you want to make it a build option.
Lots to consider.
A few more resources
There's a new room in the Matrix chat about this where Levy and myself are discussing quite a bit: https://app.element.io/#/room/#mach-ai:matrix.org
For both the GUI portion of IAUS at least it would make sense to have a graph of the strength of the activations of the actions (same for components if zoomed in on an action) over time allowing the designer to tweak behaviour and see how it affects the choices the character would have made given a short sequence. This along with a recording of a single session where the designer can observe behaviour and decide how the character should behave by setting a constraints on which actions must be chosen where as a guide when tweaking utility constraints. The same can be done for exploring outcomes by replaying the session with the new behaviour. One could also explore multiple behaviour variants in parallel by running simulations side-by-side to see how they diverge (either different cameras or a 2D merged view with different colours for each variant).
Think of it as a video editor where it's only possible to tell characters what they should think about before each improv play.
After experimenting a bit with different systems a plain Dual Utility system seems like the most effective for both performance and quality. It's capable of replacing state machines pretty much anywhere and there's no need for behaviour trees at all and planners as they really don't scale well performance wise.
The system I have at the moment consists of a list of categories used to group actions that make sense together (e.g combat actions go in the combat category without dance/emote as you wouldn't want your character to start dancing at random when at 0.1hp) where each contains a list of actions. Each category is chosen by the same utility evaluation used for actions thus they share the same type of sensors. Thus, picking an action involves:
To improve performance at runtime and ease editing I've defined both a text-based format for quickly editing character behaviour:
Where actions and sensors are defined zig side:
And the in-memory format which can be mapped to memory and uses as-is:
Image.Headeru32u8u8u16u32u16u16u16u16u16u16u16u16[32]u8(ed25519, identifying the author)[32]u8(Blake3, file integrity)Image.Data[number_of_categories]Categoryu16u16(consideration)u16u16[number_of_actions]Actionenum(u16)(one tag per action)u16(consideration)u16[number_of_considerations]ConsiderationSensorenum(u8)(linear, bezier, logit, logistic, normal, poly, etc...)enum(u4)(sensor group, e.g: my, target, game, imap, ...)u4 = 0enum(u16)(name of the sensor within the given sensor group)f32(minimum bound when clamping)f32(maximum bound when clamping)[number_of_parameters]f32[action_symbol_table_size]u8(optional)[sensor_symbol_table_size]u8(optional)[name_pool_size]u8[comment_pool_size]u8Evaluation follows traversal order and each guarantees cache is filled with useful data while processing. Each
function"knows" how many parameters to consume from the parameter array and processing is always sequential for the evaluation of a single action's / category's considerations thus there's no need to store further indices.Debugging character behaviour relies on rendering graphs for each of the response curves (
sensor) initially. Plotting the estimated utility over time for categories, actions, and considerations, allow for further insight into how a character thinks such as why they chose to take a particular path over the expected. Given a replay of events it's then possible for a non-programmer to tweak response curves and see the change in behaviour live on the graph of estimated utility for each category/action/consideration as long as they have some concept of what utility is.The main advantage I've found is the ability for characters to multi-task when given a set of atomic actions. Perhaps picking up an apple from the table to eat is of lower importance until the lost cat that entered the cafe has been dealt with. Such task switching is often painful to configure (more so when it involves more than just the one distraction) but with dual utility it's handled by default. If eating the apple isn't valuable after dealing with all distractions then the character will just pick something else to do, otherwise they resume eating unless distracted again.
I've also found that the same system is suitable for selecting which track to play from ambient sound to combat based on the current situation (even selecting between variations / tone) rather than setting up areas/npcs with triggers.
TL;DR - Dual Utility is all you need
Note: I cannot use discord (they don't like VPNs) so can only discuss this here or on matrix.
Note: The format presented above is a "snapshot" of it at the time of writing and might not reflect it's current state.
The order in which categories, actions, and sensors appear has no effect on behaviour thus one can collect information on the frequency of activations at runtime to later sort the image to place cateories/actions/sensors that will likely score high in the list at the top to reduce the number of sensor queries (increase the chance for a short-circuit while taking into account the execution cost, effectively PGO).
Another topic of research:
To construct a vector database for objects such that characters can calculate similarity of objects using tsss/cosine similarity which may be useful for utility calculation and "discovering" actions which can be applied based on objects that are similar.
Edit:
In addition to GOAP, ukanren may be of interest and it's extension. It's suitable for long-term high-level goals where search may span multiple update frames.