This guide provides a quick introduction to setting up and running a basic agent simulation using the C++ core of AgentSimMiddleware. It walks through the essential steps to initialize the simulation, add agents, and run a simulation loop.
- A C++ development environment (e.g., Visual Studio on Windows, GCC/Clang on Linux/macOS).
- CMake (version 3.15 or newer).
- The AgentSimMiddleware C++ core built as a static or dynamic library. Follow the C++ Core Build Instructions to build the project.
- The
data/actions_goals.jsonfile, which defines the GOAP actions and goals for agents.
- Create a New C++ Project: Start a new C++ console application project in your preferred IDE.
- Link AgentSimMiddleware Library:
- Include the necessary header files from
src/cpp/agent_core/(e.g.,AgentContainer.h,WorldSnapshot.h,DataTypes.h). - Link against the compiled
AgentSimMiddlewarelibrary (e.g.,.lib,.a,.so, or.dylib). The exact linking steps will depend on your build system (e.g., CMake, Visual Studio project settings).
- Include the necessary header files from
- Copy GOAP Data: Ensure
data/actions_goals.jsonis accessible by your executable at runtime. You might need to copy it to your build output directory.
The following code snippet demonstrates a minimal C++ simulation setup. This example initializes an AgentContainer, adds a few agents, and runs a simple update loop.
#include <vector>
#include <chrono>
#include <thread>
#include <filesystem>
#include <numeric>
#include <algorithm>
#include <fstream>
#include <sstream>
// Include necessary AgentSimMiddleware headers
#include "AgentContainer.h"
#include "WorldSnapshot.h"
#include "DataTypes.h"
#include "AgentCommand.h"
#include "spdlog/spdlog.h" // For logging (optional, but recommended)
#include "spdlog/sinks/stdout_color_sinks.h"
// Define a simple logging callback for use with AgentSimApi's SetLogCallback
void LogCallback(LogLevel level, const char* message) {
switch (level) {
case LogLevel::Trace: spdlog::trace(message); break;
case LogLevel::Debug: spdlog::debug(message); break;
case LogLevel::Info: spdlog::info(message); break;
case LogLevel::Warn: spdlog::warn(message); break;
case LogLevel::Error: spdlog::error(message); break;
case LogLevel::Critical: spdlog::critical(message); break;
default: spdlog::info(message); break;
}
}
int main() {
// 1. Initialize Logging
auto console = spdlog::stdout_color_mt("console");
spdlog::set_default_logger(console);
spdlog::set_level(spdlog::level::info); // Set default logging level
spdlog::info("Starting C++ Quick Start Application...");
// Set the logging callback for the AgentSimMiddleware core
SetLogCallback(LogCallback);
// 2. Load GOAP Data
std::string goapJsonContent;
try {
// Assuming data/actions_goals.json is in the executable's directory or a known path
std::ifstream goapFile("data/actions_goals.json");
if (!goapFile.is_open()) {
throw std::runtime_error("Failed to open data/actions_goals.json");
}
std::stringstream buffer;
buffer << goapFile.rdbuf();
goapJsonContent = buffer.str();
} catch (const std::exception& e) {
spdlog::critical("Error loading GOAP JSON: {}", e.what());
return 1;
}
// 3. Create AgentContainer
const int maxAgents = 100; // Define maximum agents
// Use the public API call to create the agent container
AgentContainer* agentContainer = CreateAgentContainer(maxAgents, goapJsonContent.c_str());
if (!agentContainer) {
spdlog::critical("Failed to create AgentContainer!");
return 1;
}
spdlog::info("AgentContainer created with maxAgents: {}.", maxAgents);
// Optional: Set LOD thresholds
SetLODThresholds(agentContainer, 20.0f, 50.0f); // Example: Full sim up to 20m, Lite sim up to 50m
// 4. Add Agents
spdlog::info("Adding agents...");
int numAgentsToAdd = 10;
for (int i = 0; i < numAgentsToAdd; ++i) {
Vector3 initialAgentPos = {static_cast<float>(i * 2), 0.0f, static_cast<float>(i * 2)};
Quaternion initialAgentRot = {0.0f, 0.0f, 0.0f, 1.0f}; // Identity rotation
int initialGoal = 0; // Assuming Goal ID 0 exists (e.g., GATHER_WOOD)
int initialState = static_cast<int>(AgentState::IDLE); // Start agents as IDLE
int agentId = AddAgent(agentContainer, initialAgentPos, initialAgentRot, initialGoal, initialState);
if (agentId == -1) {
spdlog::error("Failed to add agent {}.", i);
} else {
spdlog::info("Agent {} added at ({}, {}, {}).", agentId, initialAgentPos.x, initialAgentPos.y, initialAgentPos.z);
}
}
spdlog::info("Total agents in simulation: {}.", GetAgentCount(agentContainer));
// 5. Prepare World Snapshot (Minimal Example)
// In a real application, this would come from the game engine
std::vector<WorldObject> worldObjectsData;
worldObjectsData.push_back({1001, WorldObjectType::TREE, {15.0f, 0.0f, 15.0f}, {0,0,0,1}});
worldObjectsData.push_back({1002, WorldObjectType::ROCK, {25.0f, 0.0f, 25.0f}, {0,0,0,1}});
worldObjectsData.push_back({1003, WorldObjectType::TREE, {5.0f, 0.0f, 5.0f}, {0,0,0,1}});
WorldSnapshot currentWorldSnapshot;
currentWorldSnapshot.timestamp = 0;
currentWorldSnapshot.worldObjects = worldObjectsData.data();
currentWorldSnapshot.numWorldObjects = static_cast<int>(worldObjectsData.size());
currentWorldSnapshot.worldEvents = nullptr; // No events for this minimal example
currentWorldSnapshot.numWorldEvents = 0;
currentWorldSnapshot.sunDirection = {0.0f, -1.0f, 0.0f};
currentWorldSnapshot.timeOfDay = 12.0f;
// 6. Simulation Loop
float deltaTime = 0.016f; // Approximately 60 FPS
const int numTicks = 500;
Vector3 cameraPosition = {0.0f, 10.0f, -20.0f}; // Example camera position
std::vector<AgentCommand> agentCommands(maxAgents);
int commandCount = 0;
int maxCommandCapacity = maxAgents;
spdlog::info("Starting simulation loop for {} ticks...", numTicks);
for (int tick = 0; tick < numTicks; ++tick) {
commandCount = 0; // Reset command count for each tick
// Update the simulation using the public API
Update(agentContainer, deltaTime, cameraPosition, ¤tWorldSnapshot,
agentCommands.data(), &commandCount, maxCommandCapacity);
// Process AgentCommands (in a real scenario, these would be sent to the game engine)
if (commandCount > 0) {
spdlog::debug("Tick {}: Received {} agent commands.", tick, commandCount);
// Example: Log commands for the first few agents
for (int i = 0; i < std::min(commandCount, 3); ++i) {
spdlog::debug(" Agent {}: Command Type {}", agentCommands[i].agentId, (int)agentCommands[i].commandType);
}
}
// Optional: Get and log some agent data
if (tick % 100 == 0) {
spdlog::info("Tick {}: Agent 0 position: ({:.1f}, {:.1f}, {:.1f})", tick,
agentContainer->m_agentManager.getPosition(0).x,
agentContainer->m_agentManager.getPosition(0).y,
agentContainer->m_agentManager.getPosition(0).z);
}
std::this_thread::sleep_for(std::chrono::milliseconds(static_cast<int>(deltaTime * 1000))); // Simulate real-time
}
// 7. Shutdown Simulation
spdlog::info("Shutting down simulation...");
DestroyAgentContainer(agentContainer);
spdlog::info("Simulation shutdown complete.");
return 0;
}- Build your C++ project.
- Ensure
data/actions_goals.jsonis in the same directory as your executable, or provide the correct path. - Run the executable. You should see console output detailing the simulation's progress and agent activities.
This example provides a foundational understanding of how to interact with the AgentSimMiddleware C++ core in a standalone application.