I am currently creating a game in Unity and I want to create a spell system. For this purpose, I have created an interface named ISpell which currently has only one method (castSpell). I have also created a spell named Fireball which implements the ISpell interface and allows the player to launch a fireball.
My problem is as follows: I have a class named Abilities attached to my player, whose only purpose is to define the key to launch a spell and choose which spells the player has. To do this, I have created an attribute named spawnPoint (which is not important for my problem) and two attributes for each spell. The first attribute corresponds to the key to launch the spell, and the second is an attribute of type ISpell. However, I cannot drag and drop my Fireball spell because the attribute does not appear in the Unity inspector, even though it is declared public.
I don't know if it's possible with my solution, but I want to create a flexible and customizable spell system for my game.
Thank you in advance! Here is my classes :
public interface ISpell {
void castSpell(Transform spawnPoint);
float getSpellSpeed();
float getSpellLifetime();
float getSpellCooldown();
}
public class FireballSpell : MonoBehaviour, ISpell {
[Header("Spell Base Settings")]
public float SpellSpeed = 2.0f;
public float projectileLifeTime = 10.0f;
public float cooldown = 1.0f;
public GameObject spellPrefab;
[Header("Explosion Settings")]
public float explosionRadius = 10.0f;
public GameObject explosionEffect;
private Vector3 startScale;
void Start()
{
Destroy(gameObject, projectileLifeTime);
startScale = explosionEffect.transform.localScale;
}
public void castSpell(Transform spawnPoint)
{
GameObject spellClone = Instantiate(spellPrefab, spawnPoint.position, spawnPoint.rotation);
Rigidbody rb = spellClone.GetComponent<Rigidbody>();
rb.AddForce(spawnPoint.forward * spellClone.GetComponent<ISpell>().getSpellSpeed(), ForceMode.Impulse);
}
void Destroyed(){
Destroy(gameObject);
}
void OnDestroy()
{
Explode();
}
private void OnTriggerEnter(Collider other)
{
if(other.tag == "Player"){
return;
} else {
Destroy(gameObject);
}
}
public void Explode(){
Vector3 v = new Vector3(explosionRadius, explosionRadius, explosionRadius);
Instantiate(explosionEffect, transform.position, transform.rotation);
explosionEffect.transform.localScale = v;
Collider[] colliders = Physics.OverlapSphere(transform.position, explosionRadius);
foreach (Collider nearbyObject in colliders)
{
if(nearbyObject.tag == "Player" || nearbyObject.tag == "Enemy"){
Rigidbody rb = nearbyObject.GetComponent<Rigidbody>();
/**Health health = nearbyObject.GetComponent<Health>();
if (health != null)
{
health.TakeDamage(damage);
}**/
if(nearbyObject.tag == "Enemy"){
}
}
}
}
private void OnDrawGizmosSelected(){
Gizmos.DrawWireSphere(this.transform.position, explosionRadius);
}
public float getSpellSpeed(){
return SpellSpeed;
}
public float getSpellLifetime(){
return projectileLifeTime;
}
public float getSpellCooldown(){
return cooldown;
}
}
public class Abilities: MonoBehaviour {
public Transform spawnPoint;
[Header("Spell 1 Settings")]
public ISpell spell1;
public KeyCode key1;
void Update()
{
if(Input.GetKeyDown(key1))
{
spell1.castSpell(spawnPoint);
}
}
}
Unity cannot serialize interfaces by default. It can be done using Odin, but actually I do not recommend it.
If you want to assign links in the inspector window, you can use a little hack. Just replace your ISpell
interface with the base abstract MonoBehaviour
class. Then you will be able to assign this link in the inspector.
public abstract BaseSpell : MonoBehaviour
{
abstract public void castSpell(Transform spawnPoint);
abstract public float getSpellSpeed();
abstract public float getSpellLifetime();
abstract public float getSpellCooldown();
}
public FireballSpell : BaseSpell
{
...
}
public class Abilities : MonoBehaviour
{
...
public BaseSpell spell1;
...
}