Getting a script from a gameobject using a string Unity - c#

I am working on a simple weapons controller for my FPS game and have come across an issue while trying to make it dynamic. What I want to happen when the player picks up a weapon is for the weapons stats and effects to be set as default. To do this each weapon has a script which is
weapon.name + "Stats"
But I'm having issues referencing said script. Right now, this is what my code looks like:
string weaponScriptName = weapon.name + "Stats";
weapon.GetComponent<weaponScriptName>().UseWeapon();
where weapon represents the currently equipped weapon's gameobject. Obviously, this doesn't work and various implementations that I've found on the Unity help pages only cause more errors. What can I do to solve this issue?
Thank you.

GetComponent has multiple overloads.
There is the generic version you refer to - the most commonly used one
T GetComponent<T>()
which you can only use with a compile time constant type parameter such as
var renderer = GetComponent<Renderer>();
There is one using a dynamic Type
Component GetComponent (Type type)
like
// Just an example, there are many ways of getting a type
var type = typeof(Renderer);
var renderer = (Renderer) GetComponent(type);
And finally there is one taking a string
Component GetComponent (string typeName);
like
// Again just an example, there are many ways of getting type names
// Especially when dealing with multiple assemblies you might even have to use the AssemblyQualifiedName
var renderer = (Renderer) GetComponent("Renderer");
Note that for any of the dynamic versions you either have to type cast or if it is enough to have only a generic Component reference you can use it of course.
However, as said if anyhow possible don't use string versions at all!
It is always slow and error prone. Rather use e.g. some common base class or interface, or use an enum or Dictionary to decide what to do for which state.
So I would rather have e.g.
public interface IWeapon
{
void UseWeapon();
}
and then every of your different weapons can implement this interface
public class WeaponA : MonoBehaviour, IWeapon
{
public void UseWeapon ()
{
Debug.Log("Used Weapon A");
}
}
public class WeaponB : MonoBehaviour, IWeapon
{
public void UseWeapon ()
{
Debug.Log("Used Weapon B");
}
}
and your code would simply be
var weapon = someObject.GetComponent<IWeapon>(). UseWeapon();
Or if your weapons all share some common implementation such as pickup etc rather have a common base class
public abstract class BaseWeapon : MonoBehaviour
{
// Everything that all weapons share as behavior and properties
// every subclass HAS TO implement and override this method
public abstract void UseWeapon ();
// Alternatively if there even is some common behavior
// subclasses CAN but don't have to override this
//public virtual void UseWeapon ()
//{
// // Implementation that is the default behavior
//}
}
And then
public class WeaponA : BaseWeapon
{
public override void UseWeapon ()
{
Debug.Log("Used Weapon A");
// If using virtual before then this class IGNORES and fully overwrites the default implementation
}
}
public class WeaponB : BaseWeapon
{
public override void UseWeapon ()
{
// If using virtual and you WANT the default behavior then add
//base.UseWeapon();
Debug.Log("Used Weapon B");
}
}
and your code would simply be
var weapon = someObject.GetComponent<BaseWeapon>(). UseWeapon();

If i got it correctly, you want to do something like GetComponent<"Transform">() ?
If so, you should do GetComponent("Transform"); instead

Related

how to change a get component class base on string?

So im trying to use compToGet string that have been passed through the parameter into slot.GetComponent().level++;
upgradeFoundation() will be called on button click.
and there is actually quite a lot of buttons with similar functionality (like: upgradeTurret(), upgradeTurret2(), etc)
thats why im trying to change the value of compToget string base on which button you click and use that new string to get component under the name of that new string but it seems it doesn't work that way and I dont know how it would work any other way, any help would be much appreciate.
public void upgradeFoundation()
{
float upgFoundationCost = slotGroup.transform.Find(slotName).gameObject.GetComponent<Slot>().upgFoundationCost;
Upgrade(upgFoundationCost, "Foundation");
}
public void Upgrade(float upgCost, string compToGet)
{
GameObject slot = slotGroup.transform.Find(slotName).gameObject;
if (inGameUIManagerScript.money >= upgCost)
{
Type compToGetType = Type.GetType(compToGet); //im not sure how to convert a string into a type
slot.GetComponent<compToGetType>().level++; //this is the error line saying im treating a var like a type
}
}
Thank you in advance.
Exactly the same issue as in your previous question => You can not use the generic! Instead use GetComponent(compToGetType);
However I removed the duplicate since you still would need to cast to your actual type which is anything but trivial!
=> Again I can only recommend: Don't use strings!
Rather have a common Base class or interface like e.g.
public abstract class BaseComponent : MonoBehaviour
{
private int level;
// public read-only access
public int Level => level;
public virtual void Upgrade()
{
level++;
}
// Other properties and methods all your components have in common
// Also get into "virtual" and "abstract" members!
}
and inherit your stuff from it like
public class Foundation : BaseComponent
{
// Additional stuff specific to the foundation
// overrides for the virtual and abstract members
}
public class Turret : BaseComponent
{
// Additional stuff specific to the turret
// overrides for the virtual and abstract members
}
//Maybe this would even inherit from Turret instead?
public class Turret2 : BaseComponent
{
// Additional stuff specific to the turret2
// overrides for the virtual and abstract members
}
and finally use that common base instead:
public void UpgradeComponent()
{
slot.GetComponent<BaseComponent>().Upgrade();
}

Don't use Interfaces in Unity?

in Unity I make use of interfaces. I set a logic for components which are totally different to each other.
Examples:
A car, a dog and a aircraft would implement IMovable. I can call Move() from each component but these components execute different code.
Same for ISavable, each component, that has to save data to the database could save the stuff when looping through all savables.
The problem:
Some people in forums say that interfaces are bad for Unity.
When destroying a gameobject and call its interface method this still gets executed.
No error would come up because Destroy() does not destroy objects. Unity as a C++ driven Engine would setup a C# wrapper for the objects. These objects just get a flag destroyed which is a bool.
Destroyed gameobjects will not get destroyed immediately, they will be destroyed later on at the end of the frame.
Until this end of the frame is not reached the method can still get called from the destroyed object.
The best way would be using abstract classes only and never use interfaces because of the bad behaviour coming up when destroying objects.
I tested this with a small example, I created the following scripts:
public interface IIntfacable
{
void DoSomething();
void DestroyComponent();
}
public class bar : MonoBehaviour
{
private IIntfacable i;
private void Start()
{
i = FindObjectOfType<foo>().GetComponent<IIntfacable>();
}
private void Update()
{
i.DoSomething();
i.DestroyComponent();
i.DoSomething();
}
}
public class foo : MonoBehaviour, IIntfacable
{
public void DoSomething()
{
Debug.Log("=> DoSomething");
}
public void DestroyComponent()
{
Debug.Log("=> DestroyComponent");
Destroy(gameObject);
}
}
When executing this code I get the following result
Workaround:
I could create an abstract base class and choose between
public abstract void Foo();
and
public virtual void Bar()
{
return;
}
but this might lead to overengineering. Because all Scripts would need this base class whether they need this method or not.
Conclusion:
Should I prevent using interfaces?
I am confident in saying there is no harm in using interfaces.
The underlying fear is about keeping track of unmanaged references, a problem which will still be there weather you are using interfaces, abstract classes or whatever. You simply have to make sure that your game code will not try to access any objects which have been Destroy()ed.
Basically, I just construct a collection of objects that I know are not destroyed in my scene, and remove them after destruction.
With risk of answering an xy-problem, If you are scared to miss out on your reference count anyway or there is something in particular which wont allow creating such a list, there is not really any magic wand here, but there are a few precedent patterns in the .net framework with the IDisposable interface/pattern that may lead the way.
Many implementations of these patterns checks a flag in a few public-facing methods of the object. IDisposable.Dispose() would set the flag to true and throw an ObjectDisposedException on some public method if this is set to true, analog to MissingReferenceException in this case. Some patterns will then expose the flag IsDisposed, so that other objects that use the implementation can check instead of doing a try-catch on any access to the object. Your analog could be IsDestroyed, and you should set it in the override of OnDestroy.
You could change your method update like this (well it's not really a use case, why would you try to use it after destroying it, but to show my point):
private void Update()
{
i.DoSomething();
i.DestroyComponent();
if (!i.IsDestroyed) {
// This will not be called
i.DoSomething();
}
}
and implementation could be
public interface IIntfacable : IDestroyable
{
void DoSomething();
}
public interface IDestroyable
{
void DestroyComponent();
bool IsDestroyed { get; }
}
public class foo : MonoBehaviour, IIntfacable
{
bool IsDestroyed { get; private set; }
public void DoSomething()
{
Debug.Log("=> DoSomething");
}
public void DestroyComponent()
{
Debug.Log("=> DestroyComponent");
Destroy(gameObject);
}
public override OnDestroy() {
base.OnDestroy();
IsDestroyed = true;
}
}

C# - Create class instance inside another class with same meaning

I want to create a class named Enemy, which should be used in a programmed rpg-themed-battlesystem. The problem is that I would want to create multiple monster types in the Enemy class, but then I would have to create a possibility for the battlesystem with every enemy class for example Enemy.Goblin or Enemy.Golem.
Question:
How could I achieve this by using only one parameter in the battlesystem function? I wanted to use
public static void InitiateBattle ( Player player, Enemy enemy )
but now I cannot use the Enemy.Goblin instance, because it cant implicitly convert Enemy.Goblin to Enemy. How could I most easily and with minimal code fix this?
You need to use inheritance.
public class Enemy
{
// put all properties and methods common to all here
}
public class Goblin: Enemy
{
// goblin specific stuff here
}
you will then be able to pass in a goblin as an enemy.
It sounds like you want to use inheritance?
public class Enemy {}
public class Goblin : Enemy {}
public class Golem : Enemy {}
You can then pass in an instance of Goblin or Golem to your method and the statement will be valid because the compiler will 'box' your object into an instance of the parent type.
Then, if you want to use a member from the Goblin or Golem subclasses, you would need to 'cast' the enemy parameter variable back into the appropriate type using as:
public static void InitiateBattle (Player player, Enemy enemy)
{
var golem = enemy as Golem;
var goblin = enemy as Goblin;
}
Make sure you check for null after the cast!
Bear in mind that C# does not allow multiple-inheritance; each class can inherit from only one parent.
I think it would be best to use interface.
public interface IEnemy
{
//e.g.
public void Attack();
}
public class Goblin : IEnemy
{
public void Attack()
{
throw new System.NotImplementedException();
}
}
public class Battle
{
public static void InitiateBattle(Player player, IEnemy enemy);
}

Is it possible to override a method with a derived parameter instead of a base one?

I'm stuck in this situation where:
I have an abstract class called Ammo, with AmmoBox and Clip as children.
I have an abstract class called Weapon, with Firearm and Melee as children.
Firearm is abstract, with ClipWeapon and ShellWeapon as children.
Inside Firearm, there's a void Reload(Ammo ammo);
The problem is that, a ClipWeapon could use both a Clip and an AmmoBox to reload:
public override void Reload(Ammo ammo)
{
if (ammo is Clip)
{
SwapClips(ammo as Clip);
}
else if (ammo is AmmoBox)
{
var ammoBox = ammo as AmmoBox;
// AddBullets returns how many bullets has left from its parameter
ammoBox.Set(clip.AddBullets(ammoBox.nBullets));
}
}
But a ShellWeapon, could only use an AmmoBox to reload. I could do this:
public override void Reload(Ammo ammo)
{
if (ammo is AmmoBox)
{
// reload...
}
}
But this is bad because, even though I'm checking to make sure it's of type AmmoBox, from the outside, it appears like a ShellWeapon could take a Clip as well, since a Clip is Ammo as well.
Or, I could remove Reload from Firearm, and put it both ClipWeapon and ShellWeapon with the specific params I need, but doing so I will lose the benefits of Polymorphism, which is not what I want to.
Wouldn't it be optimal, if I could override Reload inside ShellWeapon like this:
public override void Reload(AmmoBox ammoBox)
{
// reload ...
}
Of course I tried it, and it didn't work, I got an error saying the signature must match or something, but shouldn't this be valid 'logically'? since AmmoBox is a Ammo?
How should I get around this? And in general, is my design correct?
(Note I was using interfaces IClipWeapon and IShellWeapon but I ran into trouble, so I moved to using classes instead)
Thanks in advance.
but shouldn't this be valid 'logically'?
No. Your interface says that the caller can pass in any Ammo - where you're restricting it to require an AmmoBox, which is more specific.
What would you expect to happen if someone were to write:
Firearm firearm = new ShellWeapon();
firearm.Reload(new Ammo());
? That should be entirely valid code - so do you want it to blow up at execution time? Half the point of static typing is to avoid that sort of problem.
You could make Firearm generic in the type of ammo is uses:
public abstract class Firearm<TAmmo> : Weapon where TAmmo : Ammo
{
public abstract void Reload(TAmmo ammo);
}
Then:
public class ShellWeapon : Firearm<AmmoBox>
That may or may not be a useful way of doing things, but it's at least worth considering.
You can use composition with interface extensions instead of multiple-inheritance:
class Ammo {}
class Clip : Ammo {}
class AmmoBox : Ammo {}
class Firearm {}
interface IClipReloadable {}
interface IAmmoBoxReloadable {}
class ClipWeapon : Firearm, IClipReloadable, IAmmoBoxReloadable {}
class AmmoBoxWeapon : Firearm, IAmmoBoxReloadable {}
static class IClipReloadExtension {
public static void Reload(this IClipReloadable firearm, Clip ammo) {}
}
static class IAmmoBoxReloadExtension {
public static void Reload(this IAmmoBoxReloadable firearm, AmmoBox ammo) {}
}
So that you will have 2 definitions of Reload() method with Clip and AmmoBox as arguments in ClipWeapon and only 1 Reload() method in AmmoBoxWeapon class with AmmoBox argument.
var ammoBox = new AmmoBox();
var clip = new Clip();
var clipWeapon = new ClipWeapon();
clipWeapon.Reload(ammoBox);
clipWeapon.Reload(clip);
var ammoBoxWeapon = new AmmoBoxWeapon();
ammoBoxWeapon.Reload(ammoBox);
And if you try pass Clip to AmmoBoxWeapon.Reload you will get an error:
ammoBoxWeapon.Reload(clip); // <- ERROR at compile time
The problem with which you are wrestling comes from the need to call a different implementation based on the run-time types of both the ammo and the weapon. Essentially, the action of reloading needs to be "virtual" with respect to two, not one, object. This problem is called double dispatch.
One way to address it would be creating a visitor-like construct:
abstract class Ammo {
public virtual void AddToShellWeapon(ShellWeapon weapon) {
throw new ApplicationException("Ammo cannot be added to shell weapon.");
}
public virtual void AddToClipWeapon(ClipWeapon weapon) {
throw new ApplicationException("Ammo cannot be added to clip weapon.");
}
}
class AmmoBox : Ammo {
public override void AddToShellWeapon(ShellWeapon weapon) {
...
}
public override void AddToClipWeapon(ClipWeapon weapon) {
...
}
}
class Clip : Ammo {
public override void AddToClipWeapon(ClipWeapon weapon) {
...
}
}
abstract class Weapon {
public abstract void Reload(Ammo ammo);
}
class ShellWeapon : Weapon {
public void Reload(Ammo ammo) {
ammo.AddToShellWeapon(this);
}
}
class ClipWeapon : Weapon {
public void Reload(Ammo ammo) {
ammo.AddToClipWeapon(this);
}
}
"The magic" happens in the implementations of Reload of the weapon subclasses: rather than deciding what kind of ammo they get, they let the ammo itself do "the second leg" of double dispatch, and call whatever method is appropriate, because their AddTo...Weapon methods know both their own type, and the type of the weapon into which they are being reloaded.
I think, that it's perfectly fine to check, whether passed Ammo is of valid type. The similar situation is, when function accepts a Stream, but internally checks, whether it is seekable or writeable - depending on its requirements.

Empty methods on base class vs explicit type checking

Let's say you have two types of object, one that derives from the other but adds a single piece of extra functionality. The two ways I can think to deal with this extra functionality are adding an empty method on the base class that is always called (the derived class can then override this method) or explicit type checking to see if you have an instance of the derived class and then calling the extra method.
Both of these seem like hacks, is there a better way? If not is one preferred over the other? Both ways would work but neither seems particularly clean, one way you are polluting the base class with useless method stubs, the other way you are using explicit type checking which is usually considered a bad idea.
Here's an example to make it clear what I mean:
public class Weapon
{
// Should there be an empty StartCharging() here?
public virtual void Fire()
{
// Do something
}
}
public class ChargedWeapon : Weapon
{
public void StartCharging()
{
// Do something
}
public override void Fire()
{
// Do something
base.Fire();
}
}
public class Game
{
private Weapon weapon;
public void HandleUserInput()
{
if (MouseButton.WasPressed())
{
// Or should there be an if (weapon is ChargedWeapon) here
weapon.StartCharging();
}
else if (MouseButton.WasReleased())
{
weapon.Fire();
}
}
}
It's better to add the method to base class instead of doing a type check. What will happen if you do a typecheck and then decide to implement a new type of weapon which also needs charging? Will you add another test condition?
Edit: In your code, I see a start for an implementation of Strategy Pattern. I guess that your use case will benefit greatly from it and from State Pattern. If you need more details on these, leave a comment (as they are a little offtopic from the initial question's point of view)
Definitely don't do Type Checking here.
The big question is why you are dealing with a type Weapon and then calling StartCharging on it in your Game class? The implication in this code is that all Weapons implement StartCharging - if they do not, then you have already diverged from good OO practices.
Instead of this I would create an abstract method such as Initialise on Weapon. - In your Concrete Weapon classes implement this in different ways - e.g. for ChargedWeapon you would use:
public override void Initialise()
{
StartCharging();
}
for different weapons, the implementation would differ, e.g. For a HolsteredWeapon it might be:
public override void Initialise()
{
DrawWeapon();
}
In these example, only ChargedWeapon classes need to contain a StartCharging() method, and only HolsteredWeapon classes need to contain a DrawWeapon() method. However, every weapon needs an Initialise method.
Now the base type only contains methods which apply to ALL concrete implementations, so we are once again following good OO principles.
IMHO it is better to let the weapon(class) handle its own logic without exposing to much of its internal designs.
So simply add two methods like with the pattern startAction()/stopAction() in this case startFiring()/stopFiring() and the weapons decides if it needs to charge first/fire a single shot/fire burst/continuous fire...
Better way is to do:
public interface IChargable
{
void StartCharging();
}
public interface IWeapon
{
void Fire();
}
public class Weapon : IWeapon
{
public void Fire()
{ }
}
public class ChargedWeapon : Weapon, IChargable
{
public void StartCharging ()
{ }
}
private Weapon weapon;
public void HandleUserInput()
{
if (MouseButton.WasPressed() && weapon is IChargable)
{
((IChargable)weapon).StartCharging();
}
else if (MouseButton.WasReleased())
{
weapon.Fire();
}
}
Edit: Suppose you need to add a new weapons that is not chargeable too like "ExtraWeapon, SupperWeapon" , then you can see that using that empty method "StartCharging" for all the weapons that is not support it is useless and a bad design, furthermore you may have other methods or properties to set in that new types when MouseButton... so checking the type and only use its prepare methods/properties is a better choice.

Categories

Resources