I know the struggle to make things abstract and also useful at the same time.
The problem is if we assume too much about the “universe structure”, we will missed a lot of features that should be easier to adapt if we don’t assume that.
Java utils library itself does excellent work in not assuming anything, to be used as generic as possible. I think we should continue to make it that way, stick with Collection, don’t force an Event mechanism but let the user give their own.
I’m talking about blackboard architecture for example. An Agent will want to contribute its knowledge into knowledge base by giving a way a piece of data call “Signal”… This is depends in user implementation. Signal can be anything from primitive (numbers) to complex data.
To support this kind of flexibility, we should not:
- assume the Event as anything specific (maximum an empty interface with unique Id)
- assume Behavior as anything specific because it can be decomposed as Signal. In some implementation, it can be Component of an Entity; in other, it can be a Control like what you did.
I will give two examples how I do this differently in several projects:
/** Because this AILayer is built manually, instead of code generation or editor. That's why we need an interface for AIEvent.*/
public interface AIEvent{
UniqueID getId();
}
public interface Signal extends Component{ //,AIEvent { // Yes, you can make Signal and AIEvent if you want!
}
public interface Agent{
UniqueID getId();
void aware(Signal... datas);
void signal(Signal data);
}
/** It's fair enoug to make this abstract instead of interface because we will involve movement here! */
abstract class SteeringBehavior implements Signal{
void getForces();
}
public class CharacterEntity {
Entity aiEntity;
public ChacaterEntity{
aiEntity.add(new SteeringBehavior());
}
}
/** This is a USER class! This class will normally compose different Layers in order of they want. Let say SteeringBehavior, then CharacterMovent, then GoalDriven and finally a TeamTacticStragegy. Those States normally generated by an editor! */
public class GameWorld extends AppState{
/** EventDispatcher will be our unoppiniated implementation that suite both update beat mechanism and event base mechanism. EventDispatcher will broadcast Signal underhood and notify Subcriblers of Signal all over the game. */
static EventDispatcher eventDispacher= new RxJavaEventDispatcher();
ArrayList<AppState> layers;
//StateManager stateManager = new StateManager(null); // My implementation of StateManager that agnostic
public void initialize(Application app, AppStateManager stateManager)
{
layers.add(new SteeringBehaviorState());
layers.add(new CharacterMovementState());
stateManager.attachAll(layers);
}
public void update( float tpf){
entityData.resolve(eventDispacher);
//stateManager.updateInternal(layers);
}
}
EventDispatcher will be our unoppiniated implementation that suite both update beat mechanism and event base mechanism. EventDispatcher will broadcast Signal underhood and notify Subscriber of Signal all over the game.
Then later if you want to debug the game AI, you just make a Swing frame Subscriber that take signals and display in another thread (Obsever pattern).
This is incredible transparent, composable and extensible. So user can make up their own solution, make component and contribute back. I’ve learned from various of sources, and sell my Unity AI components for living.
I suggest you take a look at RxJava’s Obsever pattern to see how their wrap their mind around real-time pipeline and events. I’d like to think about AI is nothing then a stream of ideas, that’s why I looked at RxJava intensively and comeup with this.
It’s modern and cool as hell at the same time!
And maybe a video of how cool a Rx can be in game.