[HELP]Swap Element in Special Abilities

I’m trying to expand on the code from the old RPG course. I would like to swap out an element from the Special Abilities Array for a new one. For instance, Self Heal lvl 1 (SH_L1) for Self Heal lvl 2 (SH_L2). However, I have no idea on how to get this one started. Any suggestions?

Here’s the array code:

		void AttachInitialAbilities()
		{
			for (int abilityIndex = 0; abilityIndex < abilities.Length; abilityIndex++)
			{
				abilities[abilityIndex].AttachAbilityTo(gameObject);
			}
		}

I know how to setup the trigger to start the swap, I just don’t know enough about Array’s to make the swap. Any help would be appreciated. I’ve done some research on the topic but I haven’t found a good example to work from yet.

Thanks!

Hi,

I can’t be too specific without knowing a bit more about how your code is set up, but my main suggestion (and what I’ve done for my project) is to use a List for your special abilities instead of an array - they are pretty similar in the way they work, but lists tend to be more flexible and easier to work with when you want to change/add/remove any elements at runtime. Here’s a tutorial on how they can be used.
You can declare this list with [SerializeField] List<AbilityConfig> abilities; (assuming this is how you’ve populated your initial abilities), and then the following function can be used to swap new elements in/out:

private void SwapAbility(AbilityConfig oldAbility, AbilityConfig newAbility)
{
    if (abilities.Contains(oldAbility))
    {    
        abilities.Remove(oldAbility);
    }
    abilities.Add(newAbility);
}

Hope this helps!

~ Jonny

Hi Jonnyf3,

Thanks very much for the response. I’ve been looking at your suggestion of changing to a list. It does appear to be easier to swap out an element from a list rather than an array. Problem is, there are multiple scripts accessing the array. It doesn’t look like a list will work with the configuration scripts.

I have found one suggestion using a “swap” command:
Swapping 2 Elements in an Array

It appears to be using it in another application. Not sure that this will work in Unity. The Unity manual doesn’t show swap for array.

Actually…
Can’t I just recreate the array:

abilities[abilityIndex].AttachAbilityTo(gameObject);

And then change the value of 1 of them:

values[1] = SH_L2;

Would that work?

It might the only way to learn these things is to try and fail.

“Why do we fall Bruce? So we can learn to pick ourselves up again”

The “swap” method in this example you’ve linked is for swapping the positions of two elements already in the array (and is a custom method anyway, not a built-in one so it wouldn’t show up in any manual or docs), so probably isn’t needed for this particular case where you want to replace something in the array with something that isn’t.

Directly changing the value in the array should work fine, although if you’re sticking with arrays you’ll need to make sure you’re replacing the right element by finding its index first:

var index = Array.IndexOf(abilities, oldAbility);
values[index] = newAbility;

But don’t forget you’ll have to do something to remove the AbilityBehaviour component relating to SH_L1, that won’t go away automatically when you remove the SH_L1 config from your array!

Like @Marc_Carlyon said, sometimes the best way is just to give it a go and see what works and what doesn’t :slight_smile:

~ Jonny

1 Like

So that didn’t work. What I’m trying to do is pass the name of a scriptable object from another script. That does work:

public void AddNewLevel(ScriptableObject newLevel)

So I know that the 2 scripts are talking to each other (using the print to console). Using your example above, I came up with this:

			var index = Array.IndexOf(abilities, "SH_01");

That doesn’t appear to create any errors, but I’m not sure if it’s actually seeing the array value. Next I’m trying to change SH_01 into SH_02:

values[index] = newLevel;

That’s the issue there, values and index aren’t understood. It appears that Unity is reading the newLevel value though. And, I’m not sure how to remove the old value. Array’s appear to be really restrictive; now I understand why you suggested a list. However, Ben used an Array for a reason, and he even went as far as including other scriptable objects that would replace one in the array. Unfortunately, he doesn’t mention how to change them. :unamused:

I believe what I need to do is remove the old array, and make a new one that includes the new value. That seems a little daunting for a beginner. :nerd_face:

There’s a couple of misunderstandings here, and I can see at least one of them is my fault for a fairly key typo in my example, so I’ll go through it again a bit more clearly:

  1. When you are trying to get the index, you’ll have to pass a ScriptableObject into Array.IndexOf, not a string. Your array abilities contains ScriptableObjects, not strings, so it won’t be able to find “SH_01”. If you print out the value of index after this line, I suspect you’ll probably get -1 at the moment, which will cause problems when you try to use that in your array later.
    I can’t really comment on the best way to pass the SH_01 ScriptableObject into your code here without knowing how you’ve got it all set up, so that’s up to you.

  2. This one is my fault: I meant to type abilities[index] = newAbility (or newLevel, in your case) - I got mixed up between what I was writing and the example you gave in your earlier post, sorry! The idea is that, having found where the oldLevel ScriptableObject is in the abilities array previously, you then overwrite that position in the array with newLevel.

Hope that clarifies a couple of things!

Would it make more sense then to just change the index number? That is, the value position in the array like [2] for the third element?

Not quite sure what you mean by this - you can’t just change the index of something in an array.

If you had an array ["a", "b", "c", "d"], what would it even mean to say that the index of “b” was now 2? What would happen to “c”? What would be left in the second place in the array?


Using that example array, what you are looking to do with your Special Abilities is similar to saying you want to replace “b” with “e”, except you can’t be certain where “b” is in the array before you try. The idea is then that:

  • var index = Array.IndexOf(array, "b"); tells you where “b” is in the array
  • array[index] directs you to the place where “b” currently is
  • array[index] = "e" overwrites the value in that spot (previously “b”) with “e”.

Oh my gosh! That makes more sense! Thanks for sticking around; much appreciated. :slightly_smiling_face:
I was actually going to try and use the position number of the element in the array (0 being the first). It would be better to search the array for the name as you described. I’m going to give your suggestion a try using the example you did for me. Here’s hoping. :wink:

Ok, so working with your example, I tried to do it so it’s triggered on a space bar press - and it works!!! Horray! Here’s what I’ve got:

using System;
using UnityEngine;

public class ExampleScript : MonoBehaviour
{

    public string[] letters = new string[] { "a", "b", "c", "d" };

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            SwapElement();
        }
    }

    void SwapElement()
    {
        Debug.Log("Space key was pressed.");
        var index = Array.IndexOf(letters, "a");
        letters[index] = "e";
    }
}

Now to implement it into the game; that’s next.

thanks for all your help, much appreciated! :upside_down_face:

No problem, happy to have helped and glad that you’ve got it working with the example!

If you’ve got the hang of that, then it should be a pretty similar job to apply it to your game, except now you’ll just be working with ScriptableObjects rather than strings. Good luck getting it working :slight_smile:

Thanks jonnyf3!
It’s been a good learning experience. :wink:

I’m close. I’ve learned a few things about Debug.Log and that’s been really helpful in verifying what I’ve coded. Right now, I’m getting an explicit conversion error. I believe I need to let Unity know that my variable is a ScriptableObject. Here’s the code:

		public void AddNewLevel(ScriptableObject newLevel)
		{
			Debug.Log(newLevel);

            var index = Array.IndexOf(abilities, "SH_L1");

            abilities[index] = newLevel;

            for (int i = 0; i < abilities.Length; i++)
            {
                Debug.Log(abilities[i]);
            }	
		}

Using Debug.Log I know that I’m passing through the correct ScriptableObject SH_02 as the variable newLevel. The issue is with the line:

            abilities[index] = newLevel;

I believe I need to let Unity know that newLevel is a ScriptableObject. This is what I tried, but with no success:

abilities[index] = (ScriptableObject)newLevel;

Do you have any suggestions?

This is probably best discussed in real time if you have discord to jump into?
I am sure there will be plenty of people in the RPG channel that would be willing and eager to help as this is a bit more of wait for a reply :slight_smile:
Hope this helps

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms