How do you make a robust AI/scripting system for a game?
1) For all NPC's/environment/entities do you give them a SEPARATE single behavior tree (etc. patrolBehavior, allyBehavior, vendorBehavior, doorBehavior)? If there are 500 units on the screen, should I do a full pass on the tree (going from root -> node/action) or should I do 1-node progress for all the units?
2) I am doing the AI logic in the update() function... but I heard some games have their separate AI thread, any ideas?
3) I'm wondering how to divide my game into sections/chapters... do I use a simple variable (EVENT="Mission 3") to denote how for the player's been, and make it all linear? And then utilize variable in the trees above?
I'll try to answer your question.
I do all branching logic and behavior trees in a static class for example:
public static class Behavior
{
//Branch
public static Action Selector(Func<bool> cond, Action ifTrue, Action ifFalse) {
return () => { if (cond()) { ifTrue(); } else { ifFalse(); } };
}
public static Action Sequencer(Action a, Action b) {
return () => { a(); b(); }
}
//Example trees
public static Func<bool> ifPlayerIsInSight = () => { return true; /*...true iff WorldState shows guard can see player...*/};
public static Action shootAtPlayer = () => { /*...aim guard's weapon at player and fire...*/ };
public static Func<bool> ifUnderFire = () => { return true; /*...true iff WorldState shows guard hears player gunfire...*/};
public static Action takeCover = () => { /*...guard runs for nearest shelter... */};
public static Action walkBackAndForthGuardingDoorway = () => { /*...default guard patrol behaviour...*/ };
public static Action patrollingGuardBehaviour =
Selector(Behavior.ifPlayerIsInSight, Behavior.shootAtPlayer,
Selector(Behavior.ifUnderFire, Behavior.takeCover,
Behavior.walkBackAndForthGuardingDoorway));
}
Do it in LateUpdate() or last so it does not lag the main loop.
It is upto you. You could implement a "State" in each behavior tree or split it out and manage which get used at what time.
each NPC/Agent has its own behavior tree. The tree is updated and it 'knows' where to continue, so the efficiency is usually quite good.
AIs can be updated in the main thread, it can also be updated in a separated thead. it depends and up to you.
it depends and up to you.
behaviac is a really excellent one.
behaviac supports the behavior tree, finite state machine and hierarchical task network. Behaviors can be designed and debugged in the designer, exported and executed by the game.
The C++ version is suitable for the client and server side.
and, it is open sourced!
Related
I'm trying to aggregate (per key) a streaming data source in Apache Beam (via Scio) using a stateful DoFn (using #ProcessElement with #StateId ValueState elements). I thought this would be most appropriate for the problem I'm trying to solve. The requirements are:
for a given key, records are aggregated (essentially summed) across all time - I don't care about previously computed aggregates, just the most recent
keys may be evicted from the state (state.clear()) based on certain conditions that I control
Every 5 minutes, regardless if any new keys were seen, all keys that haven't been evicted from the state should be outputted
Given that this is a streaming pipeline and will be running indefinitely, using a combinePerKey over a global window with accumulating fired panes seems like it will continue to increase its memory footprint and the amount of data it needs to run over time, so I'd like to avoid it. Additionally, when testing this out, (maybe as expected) it simply appends the newly computed aggregates to the output along with the historical input, rather than using the latest value for each key.
My thought was that using a StatefulDoFn would simply allow me to output all of the global state up until now(), but it seems this isn't a trivial solution. I've seen hintings at using timers to artificially execute callbacks for this, as well as potentially using a slowly growing side input map (How to solve Duplicate values exception when I create PCollectionView<Map<String,String>>) and somehow flushing this, but this would essentially require iterating over all values in the map rather than joining on it.
I feel like I might be overlooking something simple to get this working. I'm relatively new to many concepts of windowing and timers in Beam, looking for any advice on how to solve this. Thanks!
You are right that Stateful DoFn should help you here. This is a basic sketch of what you can do. Note that this only outputs the sum without the key. It may not be exactly what you want, but it should help you move forward.
class CombiningEmittingFn extends DoFn<KV<Integer, Integer>, Integer> {
#TimerId("emitter")
private final TimerSpec emitterSpec = TimerSpecs.timer(TimeDomain.PROCESSING_TIME);
#StateId("done")
private final StateSpec<ValueState<Boolean>> doneState = StateSpecs.value();
#StateId("agg")
private final StateSpec<CombiningState<Integer, int[], Integer>>
aggSpec = StateSpecs.combining(
Sum.ofIntegers().getAccumulatorCoder(null, VarIntCoder.of()), Sum.ofIntegers());
#ProcessElement
public void processElement(ProcessContext c,
#StateId("agg") CombiningState<Integer, int[], Integer> aggState,
#StateId("done") ValueState<Boolean> doneState,
#TimerId("emitter") Timer emitterTimer) throws Exception {
if (SOME CONDITION) {
countValueState.clear();
doneState.write(true);
} else {
countValueState.addAccum(c.element().getValue());
emitterTimer.align(Duration.standardMinutes(5)).setRelative();
}
}
}
#OnTimer("emitter")
public void onEmit(
OnTimerContext context,
#StateId("agg") CombiningState<Integer, int[], Integer> aggState,
#StateId("done") ValueState<Boolean> doneState,
#TimerId("emitter") Timer emitterTimer) {
Boolean isDone = doneState.read();
if (isDone != null && isDone) {
return;
} else {
context.output(aggState.getAccum());
// Set the timer to emit again
emitterTimer.align(Duration.standardMinutes(5)).setRelative();
}
}
}
}
Happy to iterate with you on something that'll work.
#Pablo was indeed correct that a StatefulDoFn and timers are useful in this scenario. Here is the with code I was able to get working.
Stateful Do Fn
// DomainState is a custom case class I'm using
type DoFnT = DoFn[KV[String, DomainState], KV[String, DomainState]]
class StatefulDoFn extends DoFnT {
#StateId("key")
private val keySpec = StateSpecs.value[String]()
#StateId("domainState")
private val domainStateSpec = StateSpecs.value[DomainState]()
#TimerId("loopingTimer")
private val loopingTimer: TimerSpec = TimerSpecs.timer(TimeDomain.EVENT_TIME)
#ProcessElement
def process(
context: DoFnT#ProcessContext,
#StateId("key") stateKey: ValueState[String],
#StateId("domainState") stateValue: ValueState[DomainState],
#TimerId("loopingTimer") loopingTimer: Timer): Unit = {
... logic to create key/value from potentially null values
if (keepState(value)) {
loopingTimer.align(Duration.standardMinutes(5)).setRelative()
stateKey.write(key)
stateValue.write(value)
if (flushState(value)) {
context.output(KV.of(key, value))
}
} else {
stateValue.clear()
}
}
#OnTimer("loopingTimer")
def onLoopingTimer(
context: DoFnT#OnTimerContext,
#StateId("key") stateKey: ValueState[String],
#StateId("domainState") stateValue: ValueState[DomainState],
#TimerId("loopingTimer") loopingTimer: Timer): Unit = {
... logic to create key/value checking for nulls
if (keepState(value)) {
loopingTimer.align(Duration.standardMinutes(5)).setRelative()
if (flushState(value)) {
context.output(KV.of(key, value))
}
}
}
}
With pipeline
sc
.pubsubSubscription(...)
.keyBy(...)
.withGlobalWindow()
.applyPerKeyDoFn(new StatefulDoFn())
.withFixedWindows(
duration = Duration.standardMinutes(5),
options = WindowOptions(
accumulationMode = DISCARDING_FIRED_PANES,
trigger = AfterWatermark.pastEndOfWindow(),
allowedLateness = Duration.ZERO,
// Only take the latest per key during a window
timestampCombiner = TimestampCombiner.END_OF_WINDOW
))
.reduceByKey(mostRecentEvent())
.saveAsCustomOutput(TextIO.write()...)
I am new to Dart programming. I am trying to figure out what is the proper way (what everyone will do) to handle/guard those functions which are login required. The following is my first trial:
$ vim login_sample.dart:
var isLoggedIn;
class LoginRequiredException implements Exception {
String cause;
LoginRequiredException(this.cause);
}
Function loginRequired(Function f) {
if (!isLoggedIn) {
throw new LoginRequiredException("Login is reuiqred.");
}
return f;
}
void secretPrint() {
print("This is a secret");
}
void main(List<String> args) {
if (args.length != 1) return null;
isLoggedIn = (args[0] == '1') ? true : false;
try {
loginRequired(secretPrint)();
} on LoginRequiredException {
print("Login is required!");
}
}
then, run it with $ dart login_sample.dart 1 and $ dart login_sample.dart 2.
I am wondering if this is the recommended way to guard login required functions or not.
Thank you very much for your help.
Edited:
My question is more about general programming skills in Dart than how to use a plugin. In python, I just need to add #login_required decorator in the front of the function to protect it. I am wondering if this decorator function way is recommended in dart or not.
PS: All firebase/google/twitter/facebook etc... are blocked in my country.
I like the functional approach. I'd only avoid using globals, you can wrap it in a Context so you can mock then for tests and use Futures as Monads: https://dartpad.dartlang.org/ac24a5659b893e8614f3c29a8006a6cc
Passing the function is not buying much value. In a typical larger Dart project using a framework there will be some way to guard at a higher level than a function - such as an entire page or component/widget.
If you do want to guard at a per-function level you first need to decide with it should be the function or the call site that decides what needs to be guarded. In your example it is the call site making the decision. After that decision you can implement a throwIfNotAuthenticated and add a call at either the definition or call site.
void throwIfNotAuthenticated() {
if (!userIsAuthenticated) {
throw new LoginRequiredException();
}
}
// Function decides authentication is required:
void secretPrint() {
throwIfNotAuthenticated();
print('This is a secret');
}
// Call site decides authentication is required:
void main() {
// do stuff...
throwIfNotAuthenticated();
anotherSecreteMethod();
}
This is specific to LightInject's interception. Is it possible to apply interception logic based on PerWebRequest lifetime so that interception logic can be conditionally turned on/off based on user input? E.g. something like this.
public static void Configure()
{
var serviceContainer = new ServiceContainer();
serviceContainer.EnablePerWebRequestScope();
serviceContainer.Register<ITraceSwitcher, TraceSwitcher>(new PerScopeLifetime());
serviceContainer.Register<IMyService, MyService>(new PerScopeLifetime());
serviceContainer.Intercept(x => x.ServiceType == typeof(IMyService), (y, z) => DefineProxyType(z, IsTracingEnabled));
ServiceLocator.SetLocatorProvider(() => new LightInjectServiceLocator(serviceContainer));
}
private static void DefineProxyType(ProxyDefinition proxyDefinition, Func<bool> isTracingEnabled)
{
if (isTracingEnabled())
proxyDefinition.Implement(() => new MyServiceInterceptor(), m => m.Name == "SomeMethod");
}
private static bool IsTracingEnabled()
{
var traceSwitcher = ServiceLocator.Current.GetInstance<ITraceSwitcher>();
return traceSwitcher.IsTracingEnabled();
}
Now because IMyService lifetime is defined as PerWebRequest so it is created for every web request and I was under impression that it would also call Intercept method everytime it creates MyService instance so that it can dynamically decide to apply interception logic depending on whether tracing is enabled or disabled by user. However it looks like that it calls Intercept method only once for the first time when IMyService instance is requested and for all subsequent requests it reuses the same Interception mechanism.
I also know I can use ITraceSwitcher logic within MyServiceInterceptor and then decide to use or bypass interception logic there but I want to avoid creation of proxies to begin with if tracing is disabled to avoid overhead of proxy calls through reflection but this is only possible if Intercept method is called for every web request. Please let me know if it's doable or there is a better way?
Thanks,
Syed Danish.
You can put the IsTracingEnabled method call directly into the predicate that decides if the services should be intercepted. The proxy type will only be created if it matches the predicate.
using LightInject;
using LightInject.Interception;
class Program
{
static void Main(string[] args)
{
var container = new ServiceContainer();
container.Register<IFoo, Foo>();
container.Intercept(sr => sr.ServiceType == typeof(IFoo) && IsTracingEnabled(), (factory, definition) => DefineProxyType(definition));
var foo = container.GetInstance<IFoo>();
}
private static void DefineProxyType(ProxyDefinition proxyDefinition)
{
proxyDefinition.Implement(() => new SampleInterceptor(), m => m.Name == "SomeMethod");
}
private static bool IsTracingEnabled()
{
return true;
}
}
public class SampleInterceptor : IInterceptor
{
public object Invoke(IInvocationInfo invocationInfo)
{
return invocationInfo.Proceed();
}
}
public interface IFoo { }
public class Foo : IFoo { }
Best regards
Bernhard Richter
In some legacy code that works perfectly fine the following method is used:
public class A
{
public static A First;
public static A Last;
public A Next;
public A Previous;
public A()
{
if (First == null) { First = this; }
else { Previous = Last; Previous.Next = this; }
Last = this;
}
}
This way all instances of type A are linked in order of creation, without using explicitly a linked list or whatever.
As a matter of fact I consider it an elegant solution, but I also suppose many objections can be made. Which objections should I consider?
My main objection: it is not thread safe. If this algorithm is used in multiple threads the result is unpredictable
In Dart, is it possible for a function to have a prototype associated with it?
Example Javascript code:
doStuff.prototype.isDefined = true; //is there anything like Javascript's function prototypes in Dart?
function doStuff(){
console.log("The function doStuff was called!");
}
Is it possible to do the equivalent of this in Dart (i.e., create a list of properties for each function?)
Two things to address here:
First, Dart doesn't have prototypes or prototypal inheritance, and instead uses classical inheritance. Rather than a prototype, objects have a class, and instead of a prototype chain, objects have superclasses.
Second, for your specific case, I think we'd have to see more of what you need to do to figure out the idiomatic way to do it in Dart. It should soon be possible to emulate functions with objects so that you can invoke an object and still have state and other methods associated with it.
See this article for more: http://www.dartlang.org/articles/emulating-functions/
When that capability lands you'll be able to do this:
class DoStuff {
bool isDefined = true;
call() => print("The function doStuff was called!");
}
var doStuff = new DoStuff();
main() => doStuff();
Which works if you have a fixed set of metadata about your function that you need to keep track of. It's slightly different from JavaScript because each instance of the function in Dart will have its own state for isDefined. I'm not sure if it's possible or easy to get multiple instances of the function in JavasScript, but you might need to make isDefined static so that the value is shared across all instances.
Dart does not allow you to add or remove member variables from an instance of a class at runtime. Rewriting your example in Dart it might look something like this:
class doStuff {
bool isDefined;
doStuff() {
isDefined = true;
}
void stuff() {
print('The function stuff was called!');
}
}
main() {
new doStuff().stuff();
}
If you wanted to add a property bag to a class in Dart you would write:
class PropertyObject {
Map<String, Dynamic> properties;
PropertyObject() {
properties = new Map<String, Dynamic>();
}
Dynamic operator[](String K) => properties[K];
void operator[]=(String K, Dynamic V) => properties[K] = V;
}
main() {
PropertyObject bag = new PropertyObject();
bag['foo'] = 'world';
print('Hello ${bag['foo']}');
}
Note that you can't access map properties using the '.' operator.