我正在尝试在我的游戏中实现 GOAP。该游戏是一款模拟类游戏,不仅可以从 GOAP 中受益,而且几乎本质上是基于代理可以做什么范围的要求。
我有以下代码。这里没有规划器,只有动作、状态等的定义
#include <vector>
#include <string>
#include <unordered_map>
class Condition
{
public:
bool operator()(const int value) const
{
return m_min <= value && value <= m_max;
}
enum class OP : std::uint8_t
{
EE, LT, GT, LE, GE
};
Condition(const int min, const int max, const OP op)
:
m_op(op),
m_min(min),
m_max(max)
{}
static Condition CreateEE(const int goal)
{
return Condition(goal, goal, OP::EE);
}
static Condition CreateGT(const int goal)
{
return Condition(goal+1, std::numeric_limits<int>::max(), OP::GT);
}
static Condition CreateLT(const int goal)
{
return Condition(std::numeric_limits<int>::min(), goal-1, OP::LT);
}
static Condition CreateGE(const int goal)
{
return Condition(goal, std::numeric_limits<int>::max(), OP::GT);
}
static Condition CreateLE(const int goal)
{
return Condition(std::numeric_limits<int>::min(), goal, OP::LT);
}
private:
OP m_op;
int m_min;
int m_max;
};
class Effect
{
public:
enum class OP : std::uint8_t
{
ADD, SUB, MUL, DIV, EQ
};
Effect(OP op, int value)
:
m_op(op),
m_value(value)
{
}
protected:
OP m_op;
int m_value;
};
class Action
{
public:
void AddPrereq(const std::string& name, Condition value)
{
m_prereqs.emplace_back(name,value);
}
void AddEffect(const std::string& name, Effect effect)
{
m_effects.emplace_back(name,effect);
}
void SetCost(const float cost)
{
m_cost = cost;
}
private:
float m_cost;
std::vector<std::pair<std::string,Condition>> m_prereqs;
std::vector<std::pair<std::string,Effect>> m_effects;
};
class WorldState
{
public:
void AddFact(const std::string& name, const int value)
{
m_facts[name] = value;
}
private:
std::unordered_map<std::string,int> m_facts;
};
class GoalState
{
public:
void AddCondition(const std::string& name, const Condition condition)
{
m_conditions.emplace_back(name, condition);
}
private:
std::vector<std::pair<std::string,Condition>> m_conditions;
};
int main()
{
Action goTo;
goTo.AddEffect("isAtPosition", Effect(Effect::OP::EQ, 1));
Action pickUp;
pickUp.AddPrereq("isAtPosition", Condition::CreateEE(1));
pickUp.AddEffect("itemInPossession", Effect(Effect::OP::EQ, 1));
Action drop;
drop.AddPrereq("itemInPossession", Condition::CreateEE(1));
drop.AddEffect("itemAtPosition", Effect(Effect::OP::EQ, 1));
WorldState ws;
ws.AddFact("itemAtPosition", 0);
GoalState gs;
gs.AddCondition("itemAtPosition", Condition::CreateGE(1));
return 0;
}
现在我只使用布尔值,因为对于我的测试用例,它是关于代理移动到一个位置,拿起一个项目,然后将它移动到另一个位置。稍后,目标可能是移动n 个项目或其他东西。
行动将是:
- Goto(将代理置于适当位置)
- 拾取(将物品归入所有)
- Goto(将代理置于适当位置)
- 放下(将物品放在某个位置) 目标达成!
但是我如何使用Goto
这里的动作两次呢?效果是一样的,设置“isAtPosition”变量。我需要为“isAtItemPosition”和“isAtDestinationPosition”创建一个新的状态变量吗?
因为那似乎我会为每个可能的目标有效地制定特定的行动,其效果是我确定了所有可能的行动序列。
如何对状态信息进行编码,以便池中的相同操作可以应用于不同阶段,产生不同的效果?(转到项目位置与转到目标位置)。