1

So, I have a ScriptableObject which stores the KeyCodes for the player input:

InputController.cs:

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 [CreateAssetMenu(fileName = "InputController", menuName = "ScriptableObjects/InputController", order = 1)]
 
 public class InputController : ScriptableObject
 {
     public KeyCode jump;
     public KeyCode left;
     public KeyCode right;
     public KeyCode shoot;
     public KeyCode hover;
     public KeyCode dash;
 }

The initial key values are assigned from the inspector, but I want to be able to change them from the options menu during runtime, so I created coroutine in another file, and that coroutine is called inside a function, which itself is assigned to a button.

The coroutine waits for me to press a key, and is supposed to assign that key to the corresponding action, in this case, jumping:

UI.cs:

public List<InputController> warshControls = new List<InputController>(); //This list stores the two ScritableObjects containing the control keys for the two players, assigned in the inspector.

     IEnumerator changeKey(KeyCode key)
     {   
     bool waitforInput=true;
     pressKeyText.text="Press a Key!";
     while(waitforInput)
     {
     foreach(KeyCode vKey in System.Enum.GetValues(typeof(KeyCode))){
             if(Input.GetKey(vKey)){
                 key=vKey;
                 waitforInput=false;
                 pressKeyText.text=" ";
                 Debug.Log(key);
                 Debug.Log(warshControls[0].jump);
             }
         }  
         yield return null;  
     }
 }
 
 public void setPlayer1Jump()
 {
     StartCoroutine("changeKey", warshControls[0].jump);
 }
 //This is the function assigned to the button, it calls the coroutine to change the jumping key for Player1.

According to the Debug Logs, the value of the parameter "key" does change to whatever key I pressed, but for some reason the value of the argument I passed stays the same, meaning if the value of "warshControls[0].jump" was the key Z by default, and I called the coroutine and I pressed the key M, "key" would be M, while "warshControls[0].jump" would stay Z for some reason.

However, if I replace the line "key=vKey;" with "warshControls[0].jump=vKey;", the KeyCode value does actually change (And even persists after Play Mode is turned off which is a different problem entirely), but I want to be able to choose what KeyCode to modify, so I want this problem solved.

The Riser
  • 309
  • 2
  • 4
  • 12

1 Answers1

0

C# objects are reference type, but they're passed by value. Passing these objects is like having people do stuff at a house by handing out an address. As long as you are modifying the CONTENTS of the object that was passed, those changes will seen outside the function call.

What you do on this line, though

key=vKey;

Is to REPLACE the object you were passed with a new object. The key now is NOT the same thing you were given, so the code outside the function doesn't see any changes because it's going to look at the object it gave you, not what you replaced it with. The stuff external to the function has no way to know you swapped the objects.

Fortunately you can get around this with the ref keyword, which means you're passing the argument by reference. This sounds a lot like the same thing, because objects are reference types, but again they're passed by value by default.

Try the following:

IEnumerator changeKey(ref KeyCode key)

and see if that does it for you. You could also go "up a level" and pass the object holding the jump key, so you're modifying the contents and not the object, but then you'd need a ton of different functions like changeJumpKey, etc. Go the ref route.

Chuck
  • 1,977
  • 1
  • 17
  • 23
  • That was one of the first things I tried, turns out coroutines don't accept references as parameters. I also tried creating a function that does accept a reference and calling that inside the coroutine, same result. – The Riser Aug 06 '22 at 15:00