- Home /
C# script as variable
Hi, Can't seem to find the answer, and I am surprised I am not able to find any answer about this. The idea is simple : I would like to assign a c# script as a variable, and use it during runtime to do a AddComponent, to be able to do something like this :
[System.Serializable]
public class PowerUp
{
public string name;
public string description;
public Sprite thumbnail;
public Component script;
}
In this very specific scenario, a script manages a power up behaviour : adding it or removing it enables or disable the PowerUp. I suppose this is how The Binding Of Isaac manages the items ?
public void AddPowerUp (PowerUp p)
{
transform.AddComponent<p>();
}
Right now I am using a prefab system wich works fine but is less intuitive to work with, and creates uncessearry gameObjects (while one gameObject could hoold all the created components). My understanding is that Component can only be assigned for scripts in gameObject. I also undestand that a limitation can be public variables in the script I want to assign (which I dont have).
Maybe I am missing a complete logic here. Does anyone have any clue ? Cheers,
you can only add components that derive from monobehaviour, there are many ways of doing what you are trying to do, but I would suggest you using ScriptableObjects as suggested by @Xan_9
Answer by Xan_9 · Jan 26 at 10:26 AM
I'm not 100% sure that I understand your problem completely, but I'll try to give you some ideas.
If you are trying to implement a system to be able to swap out powerups with similar logic, but different values, you can use Scriptable Objects.
You can implement an interface (e.g. IPowerup), and use a state machine to swap out the different powerups.
You can inherit from a base class, if you also want default values or behaviours.
Finally, you can use Actions, which are similar to anonymous methods, and can be saved as variables. This way, just exchange the Action with another one to change the behaviour.
If you give me more information about your use case, I can give a more specific answer to your problem.
Hey, thx for the answer, I appreciate the time you took.
Indeed, the #4 Actions solution is actually similar to what I'm doing with the prefab (i'm using UnityEvents instead, but Actions may be more appropriate).
Number 3. is already in place, but I do not think it's the issue I am facing.
I do not think #1, #2. are flexible enough, I may be wrong I will check it !
The problem is more structural, the goal is to have a plug-and-play component that will act differently based on the object it is attached to. Let's say for example :
MoveSpeed powerup (PowerUpMoveSpeed.cs) :
--> Attached to a bullet, the bullet travels faster
--> Attached to the players, the player moves faster
--> Attached to an enemy, the enemy moves faster
The main idea is to have a 'chaotic' powerup system based on combination, where one powerup combine with others (mixing MoveSpeed with TimeDilation around the object for example).
Being able to to something as
[System.Serializable]
public class PowerUp
{
public string name;
public string description;
public Sprite thumbnail;
public Component script;
}
// PowerUpManager class
// List of all available powerups assignable in inspector.
public List<PowerUp> powerUps = new List<PowerUp>();
// Add a script to an object
public void AddPowerUp(GameObject targetObject, PowerUp pu)
{
targetObject.AddComponent<pu.script>();
}
seemed to be a straightforward way to do it. but i think i'm way too focused on this solution to think about it differently. Not sure sure if clearer, it might be hard to visualize without having played a game that has this kind of mechanic (such as Isaac). Cheers,
You are describing one of the main uses of ScriptableObjects, anyway if you want to stick with your code you would need to do it like this
public class PowerUp : MonoBehaviour
{
public string name;
public string description;
public Sprite thumbnail;
public Component script;
}
// List of all available powerups assignable in inspector.
public List<PowerUp> powerUps = new List<PowerUp>();
// Add a script to an object
public void AddPowerUp(GameObject targetObject, PowerUp pu)
{
targetObject.AddComponen<PowerUp>() as PowerUp;
}
I know that using this method you cant serialize the classes thats what you want, there is where scriptable objects come into place, check this tutoriañ
Thanks for the help ! Alright i must misunderstanding how scriptable objects work then, I'll definitely have a better look at them =) ! I'll try with the monobahaviours, i did not even think about it thanks !
If you don't want to use scriptable objects, I made an example of using interfaces to add powerups to different objects. Hopefully you get some inspiration to create your own implementation.
public interface IPowerUp //All powerups implement this interface
{
public void Remove();
}
public interface IMovable //All objects that use the MovementSpeed property implement this interface
{
public float MovementSpeed { get; set; }
}
Player class: (Enemy or Bullet class can be implemented similarily)
public class Player : MonoBehaviour, IMovable
{
public List<IPowerUp> powerups = new List<IPowerUp>();
public float MovementSpeed{ get; set; }
public void AddPowerup(IPowerUp powerup)
{
powerups.Add(powerup); //Add it to the list
}
//I remove it by type, but it can even easier be remove as an instance
public void RemoveSinglePowerup(Type powerupType)
{
//remove item by type
var item = powerups.Where(item => powerupType == item.GetType()).FirstOrDefault();
item?.Remove();
powerups.Remove(item); //Remove it from the list as well
}
void Update()
{
//some logic causes a powerup to be removed from the player
RemoveSinglePowerup(typeof(MoveSpeedPowerup));
}
}
Powerup class:
public class MoveSpeedPowerup : IPowerUp
{
private IMovable attachedToObj;
private float modifier = 10;
public MoveSpeedPowerup(IMovable obj)
{
attachedToObj = obj;
AddBehaviour(); //As soon as the powerup is created, the effect is applied
}
public void AddBehaviour()
{
//All behaviour goes here. Can change anything based on the interface
//increase movement speed by 10
attachedToObj.MovementSpeed += modifier;
}
public void Remove()
{
//reduce the movement speed again
attachedToObj.MovementSpeed -= modifier;
}
}
//This is how you add a modifier: (you only need the player object (or any IMovable)
//make player pick up powerup:
player.AddPowerup(new MoveSpeedPowerup(player));
This can be implemented in a number of ways. If you don't want to call Remove() from the object, you can periodically check if the powerup exist and apply the effect using Actions.
Of course it gets more complicated with several powerups modifying the same properties, and will have to be implemented a bit differently, but hopefully this is a good start for you to get some ideas.
Ho, thanks a lot for that ! That's wayyy clearer than what I implemented. I will try both this kind of implementation and @xxmariofer's (plus having another look at SO).