How would I go about extending UAISense_Sight?

Hey, I’m pretty new to Unreal and I’m trying to figure out how I might extend the UAISense_Sight class. Creating a derived class that overrides some functions is simple enough, and then I can change the sense implementation to my derived class, and that all works fine.

The bit I’m having trouble with is what if I also need to extend UAISenseConfig_Sight to add some new parameters that are necessary for this extended implementation? I can create a derived class of the sight config, but in the actual sense class that config is turned into an FDigestedSightProperties struct, which takes a UAISenseConfig_Sight specifically. So I could derive from FDigestedSightProperties and add these new parameters to that as well, and a constructor that takes my new config class.

This is where I seem to get stuck. UAISense_Sight stores those digested properties in a TMap<FPerceptionListenerID, FDigestedSightProperties> called DigestedProperties, which is used in various places to look up the properties for each listener. That I can’t override without making an alternate map in my derived class, and overriding all functions that use it so that it points to my new map, which would be silly, at that point I might as well just duplicate the entire class. If the map stored pointers of FDigestedSightProperties this would probably all ‘just work’, I could cast to the derived properties struct that contains the info the derived sense requires and it’d be fine, but they’re not pointers.

It kinda feels like I’m missing something. Maybe the intention is that you only create entirely new senses rather than trying to add to the existing ones? I could basically just copy UAISense_Sight and the config and make additions to those rather than deriving from it, but obviously that’s not ideal. Plus the ‘Implementation’ in the config being a TSubclassOf seems to imply that extending these classes is the intended way to go about this…

What is your goal with your custom sight ?

You can implement interface to do different traces, etc on things which are a stimuli, etc. This is a lot simpler if this is what you are wanting to do?

A couple things I’d like to do as examples are things like splitting the view angle into separate horizontal and vertical angles (In real life horizontal view angle is much larger, but the sight sense effectively creates a 3D cone by default), and having multiple different regions of sight that give different ‘strength’ values (So that peripheral vision is far weaker for example). Ideally I’d want all those things to be configurable in the sight sense’s config.

Now in theory I could override IAISightTargetInterface::CanBeSeenFrom and either assume that IgnoreActor is the viewer to cast it and try to get these various bits of sight information out, or maybe try to pass this in the UserData somehow (Though I can’t really figure out how that’s supposed to be used 'cause it’s just an int32). That doesn’t seem like a very good idea to me though since it places the viewer’s behaviour inside the viewee, and if I want to have multiple derived sight classes with different behaviour that creates an even bigger mess.

I believe you’ve hit upon exactly how to do it (you could add your own bits to the IAISightTargetInterface to tell you who’s actually calling it, if that’s necessary… yeah modifying the engine is a bit of a pain, but you do have the ability to do it :smiley: )

I’m unfortunately not able to give any implementation specifics on what my project has, but CanBeSeenFrom override in our Character does have a comment

“// assume IgnoreActor is the observing actor”

If you want to be a little more accurate in that, without making a modification to the interface, you could do a quick distance check between IgnoreActor->GetActorLocation and ObserverLocation

Maybe I’m missing something, but it seems to me that the only point where the DigestedProperties are used and where it would be affected by your change, would be the Update function. And that function you would need to copy paste anyway, no? So you could additionally add your own “DigestedProperties” where you then use your config and then use that inside your Update function.

Digested Properties is used in 6/17 functions in UAISense_Sight. If I create my own DigestedProperties map I need to replace all of those functions which, as I mentioned above, seems silly. My assumption is that these classes would be built in a way that doesn’t require me to do silly things like copy and paste half the code, or make assumptions about IgnoreActor, which is why I feel like I may be missing something.

It’s possible that assumption is wrong, in which case I’d be more likely to try and create my own sight sense class that works in a way which is more easily extendable.

Digested Properties is used in 6/17 functions in UAISense_Sight.

Yeah, but e. g. where it just checks the team of the listener you don’t need to overwrite that =)

Ofc in the functions where it adds/removes listener you would need to implement those in your child class (but only as far as adding/removing stuff into your own “DigestedProperties”, and then call the super).

One point where as far as I see it you will need to replace the function would be the Update function. But then if you would want to split the view angle into separate horizontal and vertical angles, you need to test for that inside the update function. And that means replacing the dot product there with your own code, and that means copy/pasting the whole function.

Otherwise as stated above you could use the CanBeSeenFrom function, then you won’t need to copy/paste anything. But yeah, nothing of that seems to be a very good solution =)

I think I probably misunderstood what you meant, I thought you meant replace the DisgestedProperties map with my own that stores the info from the existing digested properties plus anything extra I want to add, which would require changing all uses of DigestedProperties. But I suspect you meant adding a map which has just the extra bits my derived class needs, which yeah would mean I’d only need to change the Add/Remove and then anywhere that uses the extra properties.

Unfortunately that doesn’t seem to be doable either because functions like OnNewListenerImpl aren’t virtual, so there’s no way to add my own when listeners are added.

It’s strange, it seems like there was some effort made to make these classes extendable in some places, but clearly it’s incomplete. I think I will probably just commit to making my own sight sense class that can be derived from in a more sensible way.