Snark Posted April 19, 2016 Share Posted April 19, 2016 Let's say I'm in the vehicle editor, and I want to trap an event when one of the following things happen: The amount of resource in the container was changed (e.g. if the user clicks on the "amount" slider in the right-click menu) The resource was enabled/disabled (e.g. if the user clicks on the little checkbox to the right of the slider) How do I do that? I would have expected to get a GameEvents.onEditorPartEvent for the affected part, with a ConstructionEventType of "PartTweaked", for both cases. However, here's what I observe: I never get any GameEvents.onEditorPartEvent notifications with PartTweaked, at all, for anything, ever. If I change the resource slider amount, I do get a GameEvents.onEditorShipModified (so I know the ship was touched... but I have no idea which part was modified) If I check/uncheck the "enabled" checkbox... as far as I can tell, I get no notifications at all. Specifically, I do not get a GameEvents.onEditorShipModified, the way I get one with the resource slider changing. Does anyone have any suggestions? How do I track this? Link to comment Share on other sites More sharing options...
xEvilReeperx Posted April 19, 2016 Share Posted April 19, 2016 I'd look to editing the resource item prefab and adding a component that fires GameEvents.onEditorPartEvent, or your own event if you want more information. Here's how it might look in 1.1, 1.0.5 would be similar but with EzGUI instead of uGUI [KSPAddon(KSPAddon.Startup.EditorAny, false)] class TestResourceTweakEvents : MonoBehaviour { public void Start() { GameEvents.onEditorPartEvent.Add(EditorPartEvent); } private void OnDestroy() { GameEvents.onEditorPartEvent.Remove(EditorPartEvent); } private void EditorPartEvent(ConstructionEventType constructionEvent, Part part) { if (constructionEvent != ConstructionEventType.PartTweaked) return; print("Part tweaked: " + part + ", " + EditorLogic.fetch.ship.parts.IndexOf(part)); } } [KSPAddon(KSPAddon.Startup.EditorAny, false)] class InstallResourceTweakListener : MonoBehaviour { class ResourceTweakListener : MonoBehaviour { // the UIPartActionResourceEditor contains everything we might want to know, right down // to which exact PartModule and resource has been edited private UIPartActionResourceEditor _host; private Slider _resourceSlider; private UIButtonToggle _resourceToggle; private void Start() { _host = GetComponentInParent<UIPartActionResourceEditor>(); // use the version of GetComponentsInChildren that returns inactive objects as well, since resources with // no flow mode will have the toggle button disabled _resourceToggle = GetComponentsInChildren<UIButtonToggle>(true).FirstOrDefault(); _resourceSlider = GetComponentInChildren<Slider>(); if (!_host || !_resourceSlider || !_resourceToggle) { Debug.LogError("Couldn't find something ResourceTweakListener needed"); _resourceSlider = null; _resourceToggle = null; Destroy(this); } else { _resourceSlider.onValueChanged.AddListener(ResourceValueChanged); _resourceToggle.onToggle.AddListener(ResourceToggled); } } private void OnDestroy() { if (_resourceSlider != null) _resourceSlider.onValueChanged.RemoveListener(ResourceValueChanged); if (_resourceToggle != null) _resourceToggle.onToggle.RemoveListener(ResourceToggled); } // note: slider value is in range of [0..1] private void ResourceValueChanged(float newValue) { print("Resource " + _host.Resource.resourceName + " set to " + _host.Resource.amount); GameEvents.onEditorPartEvent.Fire(ConstructionEventType.PartTweaked, _host.Part); } private void ResourceToggled() { print("Resource " + _host.Resource.resourceName + " toggled: " + (_resourceToggle.state ? "ON" : "OFF")); GameEvents.onEditorPartEvent.Fire(ConstructionEventType.PartTweaked, _host.Part); } } private IEnumerator Start() { while (UIPartActionController.Instance == null) yield return 0; UIPartActionController.Instance.resourceItemEditorPrefab.gameObject .AddOrGetComponent<ResourceTweakListener>(); Destroy(gameObject); } } Link to comment Share on other sites More sharing options...
Snark Posted April 19, 2016 Author Share Posted April 19, 2016 3 hours ago, xEvilReeperx said: I'd look to editing the resource item prefab and adding a component that fires GameEvents.onEditorPartEvent, or your own event if you want more information. Here's how it might look in 1.1, 1.0.5 would be similar but with EzGUI instead of uGUI Thanks! A few questions about the code: Maybe a stupid question, but what's the deal with OnDestroy? You've included an OnDestroy method in both of your classes there. I assume that you're talking about this Unity thing? What's confusing me is that in both cases, you've declared the method as private (which means nobody but this class can call it), but then the class itself doesn't call it. The example that's given in the abovelinked Unity documentation has an OnDestroy method that's not marked private. Therefore I'm confused, how / from where does this get called? Object lifecycle question. Is it always the case that every MonoBehaviour will have Start() called exactly once, and OnDestroy() called exactly once, and therefore I should use those two methods in "bookend" fashion to register/unregister events? Unity documents the Start() method as being void, and shows an example of it with default access; but you have the Start method of InstallResourceTweakListener as private and returning IEnumerator, which confuses me. What's the story there? Broadly speaking, I'm really having trouble getting a toehold on what's the deal with InstallResourceTweakListener, no doubt because I'm fairly ignorant of Unity stuff (e.g. don't know what a prefab is or why it's important or how this code works). I won't ask you to explain everything because it would probably take many pages to do so and I don't expect you to teach me Unity. However, I would like to at least verify that my basic impression of what that code does is correct. Here's how I interpret this: in an ideal world, PartTweaked events would just work as I expect them to however, KSP being idiosyncratic, it doesn't, so there's a "bug" where it won't fire the notification I want you're going in and doing some sort of Unity brain surgery to correct the way that KSP fires events in general, in effect a bugfix in the editor ...is that last item basically the correct impression? Link to comment Share on other sites More sharing options...
xEvilReeperx Posted April 20, 2016 Share Posted April 20, 2016 Sure, happy to answer 1 hour ago, Snark said: Maybe a stupid question, but what's the deal with OnDestroy? You've included an OnDestroy method in both of your classes there. I assume that you're talking about this Unity thing? What's confusing me is that in both cases, you've declared the method as private (which means nobody but this class can call it), but then the class itself doesn't call it. The example that's given in the abovelinked Unity documentation has an OnDestroy method that's not marked private. Therefore I'm confused, how / from where does this get called? Unity will call those magic name methods if they exist on MonoBehaviour-derived objects, regardless of their access level. The example actually shows a private OnDestroy (the default access modifier for a struct/class member is private, if not specified) 1 hour ago, Snark said: Object lifecycle question. Is it always the case that every MonoBehaviour will have Start() called exactly once, and OnDestroy() called exactly once, and therefore I should use those two methods in "bookend" fashion to register/unregister events? Yes. There are some specifics to be aware of (see Unity docs). 1 hour ago, Snark said: Unity documents the Start() method as being void, and shows an example of it with default access; but you have the Start method of InstallResourceTweakListener as private and returning IEnumerator, which confuses me. What's the story there? Some of the magic methods can be coroutines (Start is the only one I've ever used personally) which Unity will automatically start for you if they have the correct return type. I could have sworn that was in the unity documentation at one point but I can't find it now. Moving the code from Start into a new IEnumerator MyMethod() and having Start call StartCoroutine(MyMethod()) would be equivalent. It might not even be necessary in this case, if UIPartActionController.Instance always exists by the time KSPAddons are created. I didn't check 3 hours ago, Snark said: in an ideal world, PartTweaked events would just work as I expect them to however, KSP being idiosyncratic, it doesn't, so there's a "bug" where it won't fire the notification I want you're going in and doing some sort of Unity brain surgery to correct the way that KSP fires events in general, in effect a bugfix in the editor I suppose you could call it a bugfix. I wouldn't say it's brain surgery though. In a nutshell, the code takes advantage of the way Instantiate works by inserting a script into the resource slider prefab. Whenever that prefab gets cloned, the custom script gets cloned too. Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now