Hi everybody. I stocked a few questions while developing and have now reached a point where they are actually limiting my progress. I hope this isn't too much!

1) Battle Auto-Join
I am using Active time Battles with these settings: player is in control; battle menu is called on button press; combatants auto-join battles when in a certain range. Functionally speaking this is working ok, however combatants are teleported to the battle center when they join in. Obviously I'd want them to join without teleporting.

2) Action range
AIs can choose an action and move into range to use it. Is there any option to separate the range for CHOOSING an action and actually USING it for the user controlled characters as well?? I'd like my player to be able to select attack (which has range 2) from anywhere in battle, then act only when he gets in range

3) Combatant HUDs
I'm using individual combatant HUDs to show HP and timebar over the combatants' heads and encountered two issues. First: the HUDs don't scale with distance. I'm guessing this is because they aren't in world space. Is there any workaround or option to have the HUDs scale or at least be displayed only in a certain range from the player/camera?

4) Battle menu
I currently have a few issues with the way I call the battle menu in my game. Everything in the game works through an action menu, both in field and in battle, so I have an action menu screen for the field which displays a menu identical to the one used in battles. Both freeze the game. Ideally, I'd like to CALL this menu and CONFIRM the selection with the same button and close it with a CANCEL button. I couldn't find a way to do this, so I currently have CALL button, a CONFIRM button and a CANCEL button. This is ok (even though I'd like it if it could work the way I originally wanted), but I can only close the battle menu with "2nd press closes" (so with the same button that opens it) and not with the CANCEL button. What could I do to go in the direction I originally imagined?

5) ActorEvent behaviours (mover, fader, rotator etc) and generally battle or event-related temporary objects
Is there a way to remove these components after the battle is finished (or after the action is)? I ask this both because I often find my player still blinking after the battle ended (I guess there are relatively easy solutions) for this, but also because I noticed stuff generally sticks around after its intended purpose is finished. Battle objects with combatant spots, event behaviours and so on. Is there a way to clean this stuff up?

6) Game Events
I'm finishing a quest which works fine (love the quest system!) but is still missing a crucial part. I have 6 combatant spawners in a scene. These need to be off until a certain task in finisced and on after that (forever). I created an event which finds the 6 objects by name but the "Start combatant spawner" node only activates one of them. How is this done? Also, is there a better way to reference the objects? I'd like to directly feed the event with the six objects instead of string-referencing them in a node

Thank you very much for your help!
  • 1) Combatants are placed based on their battle spot - they're set up in Battle System >
    Battle Spots
    . Combatants exceeding the defined spots are placed according to the Additional Spot settings.
    You can animate a combatant joining the battle using the Join Battle Animation settings of the combatant. They're found in the combatant's Battle Settings > Battle Animations.

    2) No, there's only the use range, which is used for selecting available targets and being able to use it on them.

    3) Individual combatant HUDs can be limited to only display within a certain range of the player by using the Use Range Limit setting. There currently are no scaling options.

    4) Will look into canceling in that case, might be that this isn't possible.
    Using the call key to confirm choices should be doable by overriding the Menu Controls in the GUI box used by the battle menu.

    5) You can remove the components using a Remove Component node. However, the components are still on the combatants after fading/blinking is done to have them available for the next time this is needed. Adding/removing components costs more performance than keeping them around.
    If a combatant doesn't stop blinking, it's because you started the blinking and didn't stop it in one of your events :)

    6) Hm, check if the combatant spawners all have the same Scene ID, if yes, either click reset all IDs (button) or disable using scene ID (since you want them to be around forever).
    You can directly use the objects by setting up actors (type Object without using event object of find object settings) for them and selecting them in the event interaction using the event.
    If you're enjoying my products, free updates and support, please consider supporting me on patreon.com!
  • edited February 8
    1) The thing is, I don't want the combatants to be placed at a fixed spot if they're joining the by getting inside the range (since everything is moving around, including said combatant, either by move AI or directly controlled by the player). I want them to stay where they are when they enter the range and keep moving as set in their move AI. This seems to work if I use an empty battle event for joining the battle on the combatants (I guess overriding a "place at spots" node in the default behaviour), but is there a way to set this as the default behaviour? or do I need to put this on every combatant?

    2) Ok. I can live with this, I guess, but just in case could there be a workaround? Involving scripting perhaps?

    3) I'll try that, thanks. Do you think creating a simple HUD script, to be placed on the combatant prefab, to display the bars in a world space canvas would be feasible? Should I look into it?

    4) Will try that and come back at you, thank you! this would be amazing

    5) I'll let them stick around then. The blinking still happens even though I stop it in the attack event (animate->wait->start blinking->calculate->wait->stop blinking->wait). I can't really put my finger on why, but I'll explore this further later.

    6) the spawners have different scene ids (I already reset them). I will try removing the scene ID, but the actor way solved the issue perfectly. I didn't realize (or remember, I check the tutorials every time, but some things I still miss!) I could actually feed objects to the event interactions the same way as I did with prefabs. Great!

    Thank you for your assistance, as always.
    Post edited by azazello on
  • For #3, I've already got a script to display a Combatant's HP bar in world space (using a UGUI slider). I'll post it here later, when I'm on my computer.
  • As promised, here are the scripts.

    I generally create an empty GameObject on the "head" transform of the combatant's skeleton and then add the world space Canvas underneath that. This component is placed on that GameObject. You can make a prefab out of this once you've got all of the UGUI elements set up the way you want.

    image

    image

    using UnityEngine;
    using System.Collections;
    using ORKFramework;
    using UnityEngine.UI;

    namespace ORKIntegrationLibrary.UI
    {
    public class SimpleCombatantHUD : MonoBehaviour
    {
    [Tooltip("Enables easing when changing values")]
    public bool useEasing = true;

    [Tooltip("The easing curve function to use")]
    public AnimationCurve easingCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f);

    [Tooltip("Speed at which the value is eased")]
    public float easingSpeed = 1f;

    [Tooltip("Destroys the HUD upon the character's death")]
    public bool removeOnDeath = false;

    [Tooltip("The ORK Status Value representing current health")]
    public int healthAttributeId = 1;

    [Tooltip("The ORK Status Value representing maximum health")]
    public int maxHealthAttributeId = 0;

    [Tooltip("The UGUI Slider control used for the heath bar")]
    [SerializeField]
    protected Slider _healthBar;

    [Tooltip("The UGUI Canvas object used to hold the world space HUD")]
    [SerializeField]
    protected Canvas _canvas;

    // The ORK Combatant that this HUD is attached to
    protected Combatant _combatant;



    protected virtual void Awake()
    {
    if (_healthBar == null) { _healthBar = GetComponentInChildren<Slider>(); }
    if (_canvas == null) { _canvas = GetComponentInChildren<Canvas>(); }
    }

    protected virtual void Start()
    {
    _combatant = ComponentHelper.GetCombatant(transform.root.gameObject);

    if (_combatant != null)
    {
    // register for StatusValueChanged event
    _combatant.Status[healthAttributeId].Changed += OnHealthChanged;

    // set initial values
    _healthBar.minValue = 0;
    _healthBar.maxValue = _combatant.Status[maxHealthAttributeId].GetValue();
    _healthBar.value = _combatant.Status[healthAttributeId].GetValue();
    }
    }

    protected virtual void OnHealthChanged(Combatant combatant, int id, int change)
    {
    if (id != healthAttributeId || _healthBar == null) { return; }

    //Debug.Log( "Health changed: " + change);

    float lValue = _healthBar.value + change;
    if (useEasing)
    {
    StartCoroutine(ChangeSliderValue(lValue, _healthBar));
    }
    else
    {
    _healthBar.value = lValue;
    }
    }

    protected virtual IEnumerator ChangeSliderValue(float newValue, Slider slider)
    {
    float lTimer = 0f;
    while (lTimer < 1f)
    {
    float lValue = Mathf.Lerp(slider.value, newValue, easingCurve.Evaluate(lTimer));
    slider.value = lValue;

    lTimer += Time.deltaTime * easingSpeed;
    yield return null;
    }

    slider.value = newValue;

    // Remove the HUD upon the combatant's death (if specified)
    if (newValue < 1 && removeOnDeath)
    {
    Destroy(gameObject);
    }
    }

    protected virtual void Update()
    {
    // Ensure that the HUD Canvas is always facing the camera
    if (Camera.main != null) transform.LookAt(Camera.main.transform.position, Vector3.up);
    }

    protected virtual void OnDestroy()
    {
    if (_combatant != null)
    {
    // Un-register the StatusValueChanged event handler
    _combatant.Status[healthAttributeId].Changed -= OnHealthChanged;
    }
    }
    }

    }


    And here is the custom inspector (place it an a folder named "Editor"):

    using ORKFramework;
    using ORKFramework.Editor;
    using UnityEditor;

    namespace ORKIntegrationLibrary.UI
    {
    [CanEditMultipleObjects]
    [CustomEditor(typeof(SimpleCombatantHUD))]
    public class SimpleCombatantHUDEditor : Editor
    {
    protected string[] _statusValues;

    protected void OnEnable()
    {
    // Ensure that ORK is instantiated so that we have access to the data lists
    if (!ORK.Instantiated)
    {
    ORK.Initialize(ORKAssetHelper.LoadORKProject());
    }

    _statusValues = ORK.StatusValues.GetNames(true);
    }

    public override void OnInspectorGUI()
    {
    serializedObject.UpdateIfRequiredOrScript();
    Undo.RecordObject(target, "Inspector");

    EditorGUILayout.Separator();

    DrawAttributeProperties();
    EditorGUILayout.Separator();

    EditorGUILayout.PropertyField(serializedObject.FindProperty("removeOnDeath"));
    EditorGUILayout.Separator();

    DrawEasingProperties();
    EditorGUILayout.Separator();

    DrawGUIProperties();

    serializedObject.ApplyModifiedProperties();
    }

    protected virtual void DrawAttributeProperties()
    {
    // Get the list of Status Values from ORK
    //string[] statusValues = ORK.StatusValues.GetNames(true);

    var healthProperty = serializedObject.FindProperty("healthAttributeId");
    var maxHealthProperty = serializedObject.FindProperty("maxHealthAttributeId");

    healthProperty.intValue = EditorGUILayout.Popup(
    "Health Status Value", healthProperty.intValue,_statusValues);

    maxHealthProperty.intValue = EditorGUILayout.Popup(
    "MaxHealth Status Value", maxHealthProperty.intValue, _statusValues);
    }

    protected virtual void DrawEasingProperties()
    {
    var useEasingProperty = serializedObject.FindProperty("useEasing");
    EditorGUILayout.PropertyField(useEasingProperty);
    if (useEasingProperty.boolValue)
    {
    EditorGUILayout.PropertyField(serializedObject.FindProperty("easingCurve"));
    EditorGUILayout.PropertyField(serializedObject.FindProperty("easingSpeed"));
    }
    }

    protected virtual void DrawGUIProperties()
    {
    EditorGUILayout.PropertyField(serializedObject.FindProperty("_healthBar"));
    EditorGUILayout.PropertyField(serializedObject.FindProperty("_canvas"));
    }

    }
    }


  • 1) Currently this needs to be set up for every combatant.

    2) Well, you'd have to change some stuff in ORK's gameplay code.

    4) Canceling from a non auto-call battle menu will be possible in the next update.
    If you're enjoying my products, free updates and support, please consider supporting me on patreon.com!
  • Thank you for your help everybody!
Sign In or Register to comment.