Proper way to "spawn" game objects. (I want to spawn projectiles) - c#

Hey all I made the following class to act as "bullets" in my game and I don't know how to construct these bullet objects with the params that I want (determined at runtime) and spawn them in the game.
My first attempt looked like this:
if (canShoot())
{
shotCoolDown = FRAMES_BETWEEN_SHOTS;
Bullet bullet = new Bullet().setLoft(LOFT).setWobble(WOBBLE).setInitialVel(INITIAL_VEL).setDirectionOffset(internalRecoil.getRecoil());
}
but i get the following warnign from the unity editor:
You are trying to create a MonoBehaviour using the 'new' keyword. This is not allowed. MonoBehaviours can only be added using AddComponent(). Alternatively, your script can inherit from ScriptableObject or no base class at all
What is the proper way to spawn these bullets and to set the fields as I want them?
For more info here is the bullet class
public class Bullet : MonoBehaviour
{
private Rigidbody rigidbody;
private float wobble;
private float loft;
private float initialVel;
private Vector2 initialDirectionOffset;
private bool wobbleDirection = false;
private void Awake()
{
this.rigidbody = this.GetComponent<Rigidbody>();
}
private void Start()
{
transform.Rotate(initialDirectionOffset);
rigidbody.AddForce(initialVel*transform.forward);
rigidbody.AddForce(loft*transform.up);
}
private void FixedUpdate()
{
if (wobbleDirection)
{
rigidbody.AddForce(transform.right * wobble);
wobbleDirection = false;
}
else
{
rigidbody.AddForce(-transform.right * wobble);
wobbleDirection = true;
}
}
public Bullet setWobble(float wobble)
{
this.wobble = wobble;
return this;
}
public Bullet setLoft(float loft)
{
this.loft = loft;
return this;
}
public Bullet setInitialVel(float initialVel)
{
this.initialVel = initialVel;
return this;
}
public Bullet setDirectionOffset(Vector2 offset)
{
this.initialDirectionOffset = offset;
return this;
}
}
For more info here is the full "gun" class that spawns the bullets.
public class Gun : MonoBehaviour
{
private int FRAMES_BETWEEN_SHOTS = 10;
private int shotCoolDown = 0;
private const float LOFT = 100F;
private const float WOBBLE = 100F;
private const float INITIAL_VEL = 100F;
private Vector3 directionOffset;
private Recoil internalRecoil;
private void Awake()
{
internalRecoil = this.GetComponent<Recoil>();
if (internalRecoil == null)
{
throw new Exception("Could not find recoil component");
}
}
private void FixedUpdate()
{
if (shotCoolDown > 0)
{
shotCoolDown--;
}
}
private bool canShoot()
{
return shotCoolDown == 0;
}
public void performShoot()
{
if (canShoot())
{
//need to have a recoil component that reacts to bullet fire. then adjusts back to 0,0,0
shotCoolDown = FRAMES_BETWEEN_SHOTS;
Bullet bullet = new Bullet().setLoft(LOFT).setWobble(WOBBLE).setInitialVel(INITIAL_VEL).setDirectionOffset(internalRecoil.getRecoil());
}
}
}
My second attempt looks like:
GameObject.Instantiate(new Bullet().setLoft(LOFT).setWobble(WOBBLE).setInitialVel(INITIAL_VEL)
.setDirectionOffset(internalRecoil.getRecoil()));
is this the right way to do it???

As 3Dave mentioned, the best way to do this would be to instantiate a prefab. Assuming you will be dealing with multiple projectiles, I like to separate this into a utility function for instantiating prefabs at a given position.
public static class UnityUtil {
public static GameObject instantiatePrefab(
Object prefab,
Vector3 position,
Transform? parent = null
){
// Create an instance of the prefab
GameObject instance = Object.Instantiate(prefab, position, Quaternion.identity) as GameObject;
// Set the parent
if(parent != null){
instance.transform.parent = parent;
}
return instance;
}
}
Then you could have code like
Transform projectileHolder;
GameObject myBulletPrefab;
Vector3 bulletPosition;
...setup variables...
var BulletGameObj = UnityUtil.instantiatePrefab(myBulletPrefab, bulletPosition, projectileHolder);
Bullet bullet = BulletGameObj.GetComponent<Bullet>();
I keep some other functions in my util for dealing with generic gameobject cases, you can see my full UnityUtil class here

Related

Assigning multiple prefabs to a script which only allows one to be added

I have a script where it puts an object (premade) onto a premade path using LeanTween which works fine.
The way this works is you can assign one object to the "path adder" (MoveController) that has the Moveable script attached to it.
However, I need to be able to add new prefabs made during runtime to the MoveController so they follow the path.
So how would I go about making instantiated objects attach to the MoveController during runtime.
Thank you.
Moveable script, also instantiates the prefabs
public class Moveable : MonoBehaviour
{
[SerializeField] private float _speedMetersPerSecond = 25f;
private Vector3? _destination;
private Vector3 _startPosition;
private float _totalLerpDuration;
private float _elapsedLerpDuration;
private Action _onCompleteCallback;
public GameObject Electron;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var NextOnPath = Instantiate(Electron, transform.position, Quaternion.identity);
NextOnPath.AddComponent<Moveable>();
}
if (_destination.HasValue == false)
return;
if (_elapsedLerpDuration >= _totalLerpDuration && _totalLerpDuration > 0)
return;
_elapsedLerpDuration += Time.deltaTime;
float percent = (_elapsedLerpDuration / _totalLerpDuration);
Debug.Log($"{percent} = {_elapsedLerpDuration} / {_totalLerpDuration}");
transform.position = Vector3.Lerp(_startPosition, _destination.Value, percent);
}
public void MoveTo(Vector3 destination, Action onComplete = null)
{
var distanceToNextWaypoint = Vector3.Distance(transform.position, destination);
_totalLerpDuration = distanceToNextWaypoint / _speedMetersPerSecond;
_startPosition = transform.position;
_destination = destination;
_elapsedLerpDuration = 0f;
_onCompleteCallback = onComplete;
}
}
MoveController script
using System.Linq;
public class MoverController : MonoBehaviour
{
[SerializeField] private Moveable target;
private List<Transform> _waypoints;
private int _nextWaypointIndex;
private void OnEnable()
{
_waypoints = GetComponentsInChildren<Transform>().ToList();
_waypoints.RemoveAt(0);
MoveToNextWaypoint();
}
private void MoveToNextWaypoint()
{
var targetWaypointTransform = _waypoints[_nextWaypointIndex];
target.MoveTo(targetWaypointTransform.position, MoveToNextWaypoint);
target.transform.LookAt(_waypoints[_nextWaypointIndex].position);
_nextWaypointIndex++;
if (_nextWaypointIndex >= _waypoints.Count)
_nextWaypointIndex = 0;
}
}
Let me know if I need to clarify anything.
I understand this is quite a loaded question but I would greatly appreciate any help!
Solution
Moveable:
public class Moveable : MonoBehaviour
{
[SerializeField] private float _speedMetersPerSecond = 25f;
public List<Moveable> moveables = new List<Moveable>();
private Vector3? _destination;
private Vector3 _startPosition;
private float _totalLerpDuration;
private float _elapsedLerpDuration;
private Action _onCompleteCallback;
public GameObject Electron;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Moveable NextOnPath = Instantiate(Electron, transform.position, Quaternion.identity).GetComponent<Moveable>();
moveables.Add(NextOnPath.GetComponent<Moveable>());
MoverController.instance.targets.Add(NextOnPath.GetComponent<Moveable>());
}
}
}
MoveController:
public List<Moveable> targets;
[SerializeField] private Moveable target;
private List<Transform> _waypoints;
private int _nextWaypointIndex;
public static MoverController instance;
private void MoveToNextWaypoint()
{
for (int i = 0; i < targets.Count; i++)
{
var targetWaypointTransform = _waypoints[_nextWaypointIndex];
targets[i].MoveTo(targetWaypointTransform.position, MoveToNextWaypoint);
targets[i].transform.LookAt(_waypoints[_nextWaypointIndex].position);
_nextWaypointIndex++;
if (_nextWaypointIndex >= _waypoints.Count)
_nextWaypointIndex = 0;
}
}
Note: Only the changed parts are displayed in the solution
Yes, if you use an Array then you can add as many prefabs as you want. You will still need to set the code in the script to handle multiple prefabs.
Although, it sounds like you might not understand the basics for C# and Unity and you might like to take a class, read a tutorial or watch a Youtube video.
Update
Because your question is so broad, I cannot just give you the code. That's something you should pay a coder for. But I will tell you the general ideas of what will need to be done.
In the Moveable class, you need to track your var NextOnPath after instantiating them. A great way to do this (As long as you don't plan to convert anything to JSON) would be with a List<>.
For example:
List<Moveable> moveables = new List<Moveable>();
void Update()
{
//Some code before - with instantiation
//It is best to track items by the script that you will use since you can just use moveables[i].gameObject to access the gameObject and moveables[i].transform to access transform
moveables.Add(NextOnPath.GetComponent<Moveable>());
//Some code after
}
You might save computing power by adding the Moveable script to your Electron prefab so you use:
Moveable nextOnPath = Instantiate(electron, transform.position, Quaternion.identity).GetComponent<Moveable>();
Look up Singleton, you can use this to assign values to your MoverController class easily:
public static MoverController instance;
void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
Destroy(gameObject);
return;
}
DontDestroyOnLoad(gameObject);
}
Then in the Moveable class simply add it to MoverController
//Requires changing the above code
void Update()
{
//Some code before - with instantiation
MoverController.instance.targets.Add(NextOnPath.GetComponent<Moveable>());
//Some code after
}

How to create correct script which instantiating objects using factory-pattern

I'm creating game. There are coins which player should collect. Now I'm trying to type factory-pattern and I have 2 problems.
In the factory I need to Instantiate prefab of coin and at the same time the factory must be available from other classes. It's unlikely to get factory by its instance, so it must be static. But in this case factory can't hold link to prefab. I thought about implementing singleton already, but in this way I should hold this script on the scene and it's look like some kind of "dirty way".
Coin.cs
public class Coin : MonoBehaviour
{
CellCoordinates coords;
public CellCoordinates Coords
{
get { return coords; }
set
{
coords = value;
transform.localPosition = coords.ToWorld();
}
}
static public event System.Action OnCollect;
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag == "Player")
{
Destroy(gameObject, 0.2f);
OnCollect?.Invoke();
}
}
}
CoinFactory.cs
public class CoinFactory : MonoBehaviour
{
[SerializeField]Coin coinPrefab;
public Coin Create(Maze.CellCoordinates coords)
{
Coin coin = Instantiate(coinPrefab) as Coin;
coin.Coords = coords;
return coin;
}
}
What I would do:
public class CoinFactory : MonoBehaviour
{
private static CoinFactory instance;
[SerializeField] private Coin coinPrefab;
private void Awake()
{
instance = this;
}
public static Coin Create(Maze.CellCoordinates coords)
{
var coin = Instantiate(instance.coinPrefab);
coin.Coords = coords;
return coin;
}
}

How to change player speed with public float from another script

Hey I am trying to change a float when my player collides with a object. I tried many ways of reference but only got null when trying to debug I came up with this so far. I want to get the gameobject that contains the player script meaning the player and after I want to get the component script tankmovement to change the variable in it.
Getting the null reference error in the powerups script line 79 reset function Tank=GameObject.FindWithTag("Player")
using System.Collections.Generic;
using UnityEngine;
public class PowerUp : MonoBehaviour {
public bool boosting = false;
public GameObject effect;
public AudioSource clip;
public GameObject Tank;
private void Start()
{
Tank = GameObject.Find("Tank(Clone)");
TankMovement script = GetComponent<TankMovement>();
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Player"))
{
if (!boosting)
{
clip.Play();
GameObject explosion = Instantiate(effect, transform.position, transform.rotation);
Destroy(explosion, 2);
GetComponent<MeshRenderer>().enabled = false;
GetComponent<Collider>().enabled = false;
Tank.GetComponent<TankMovement>().m_Speed = 20f;
//TankMovement.m_Speed = 20f;
boosting = true;
Debug.Log(boosting);
StartCoroutine(coolDown());
}
}
private IEnumerator coolDown()
{
if (boosting == true)
{
yield return new WaitForSeconds(4);
{
boosting = false;
GetComponent<MeshRenderer>().enabled = true;
GetComponent<Collider>().enabled = true;
Debug.Log(boosting);
// Destroy(gameObject);
}
}
}
void reset()
{
//TankMovement.m_Speed = 12f;
TankMovement collidedMovement = Tank.gameObject.GetComponent<TankMovement>();
collidedMovement.m_Speed = 12f;
//TankMovement1.m_Speed1 = 12f;
}
}
}
Trying to call on my m_Speed float in the player script to boost the speed of my player when he collides with it. How would you get a proper reference since my player is a prefab.
Tank script
using UnityEngine;
public class TankMovement : MonoBehaviour
{
public int m_PlayerNumber = 1;
public float m_Speed = 12f;
public float m_TurnSpeed = 180f;
public AudioSource m_MovementAudio;
public AudioClip m_EngineIdling;
public AudioClip m_EngineDriving;
public float m_PitchRange = 0.2f;
private string m_MovementAxisName;
private string m_TurnAxisName;
private Rigidbody m_Rigidbody;
private float m_MovementInputValue;
private float m_TurnInputValue;
private float m_OriginalPitch;
private void Awake()
{
m_Rigidbody = GetComponent<Rigidbody>();
}
private void OnEnable ()
{
m_Rigidbody.isKinematic = false;
m_MovementInputValue = 0f;
m_TurnInputValue = 0f;
}
private void OnDisable ()
{
m_Rigidbody.isKinematic = true;
}
private void Start()
{
m_MovementAxisName = "Vertical" + m_PlayerNumber;
m_TurnAxisName = "Horizontal" + m_PlayerNumber;
m_OriginalPitch = m_MovementAudio.pitch;
}
private void Update()
{
// Store the player's input and make sure the audio for the engine is playing.
m_MovementInputValue = Input.GetAxis(m_MovementAxisName);
m_TurnInputValue = Input.GetAxis(m_TurnAxisName);
EngineAudio();
}
private void EngineAudio()
{
// Play the correct audio clip based on whether or not the tank is moving and what audio is currently playing.
if (Mathf.Abs(m_MovementInputValue) < 0.1f && Mathf.Abs(m_TurnInputValue) < 0.1f)
{
if (m_MovementAudio.clip == m_EngineDriving)
{
m_MovementAudio.clip = m_EngineIdling;
m_MovementAudio.pitch = Random.Range(m_OriginalPitch - m_PitchRange, m_OriginalPitch + m_PitchRange);
m_MovementAudio.Play();
}
}
else
{
if (m_MovementAudio.clip == m_EngineIdling)
{
m_MovementAudio.clip = m_EngineDriving;
m_MovementAudio.pitch = Random.Range(m_OriginalPitch - m_PitchRange, m_OriginalPitch + m_PitchRange);
m_MovementAudio.Play();
}
}
}
private void FixedUpdate()
{
// Move and turn the tank.
Move();
Turn();
}
private void Move()
{
// Adjust the position of the tank based on the player's input.
Vector3 movement = transform.forward * m_MovementInputValue * m_Speed * Time.deltaTime;
m_Rigidbody.MovePosition(m_Rigidbody.position + movement);
}
private void Turn()
{
// Adjust the rotation of the tank based on the player's input.
float turn = m_TurnInputValue * m_TurnSpeed * Time.deltaTime;
Quaternion turnRotation = Quaternion.Euler(0f, turn, 0);
m_Rigidbody.MoveRotation(m_Rigidbody.rotation * turnRotation);
}
}
Since the TankMovement component you need to access is attached to the GameObject that is colliding with the power, you can get the TankMovement component you need to change by using other.gameObject.GetComponent<TankMovement>():
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Player"))
{
if (!boosting)
{
// stuff
TankMovement collidedMovement = other.gameObject.GetComponent<TankMovement>();
collidedMovement.m_Speed = 20f;
// more stuff
}
}
}

How do I access the custom class by knowing a variable from that class?

I created a custom class:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Obstacle {
public GameObject gameObj;
public Color color;
public GameObject starExplosion;
public GameObject regular_Trail;
[HideInInspector]
public bool firstCollistion = true;
public static Vector3 SpawnLocation()
{
int positionQuadran = Random.Range(1, 3);
switch (positionQuadran)
{
//spawn above the player
case 1:
return new Vector3(Random.Range(1.5f, -1.5f),
Random.Range(4f - SpawnStars.closerToPlayer, 4.5f),
Random.Range(1, -3.2f));
//spawn benith the player
case 2:
return new Vector3(Random.Range(1.5f, -1.5f),
Random.Range(-0.5f, SpawnStars.closerToPlayer),
Random.Range(1f, -3.2f));
}
return Vector3.zero;
}
}
Now, as you can see in this class there is a variable public GameObject gameObj; Now in another script I need to acces the instance of the Obstacle class that this gameObj instance is in. And I am trying to do that like this:
private void OnCollisionEnter(Collision collision)
{
collision.collider. //what do I do next?
}
For a few reasons I don't want to make the Obstacle class inherit from MonoBehaviour. Since I can't access this class from another GameObject that would be in the scene how do I access it by only knowing the gameObj variable?
Update I will add the script that I use to generate the Obstacle Class:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnStars : MonoBehaviour
{
[SerializeField]
private List<Obstacle> obstacles;
[HideInInspector]
public List<Obstacle> normalStarsPool = new List<Obstacle>();
[SerializeField]
private List<Obstacle> otherObstacles;
[HideInInspector]
public List<Obstacle> otherObstaclesPool;
[SerializeField]
private int spawnNumber = 2;
private Obstacle nextObstacleToSpawn;
[SerializeField]
public GameObject panel;
public static bool spawnNow = true;
public static bool first_find_star = true;
public static float starSpeed;
/* this variables will make the stars to spawn closer and closer to the player as
the score is progresing*/
public static float closerToPlayer;
private float spawnPositionZ;
private void Start()
{
first_find_star = true;
spawnNow = true;
GeneratePrefabs(spawnNumber, obstacles, normalStarsPool);
StartCoroutine(ShuffleList(normalStarsPool));
GeneratePrefabs(2, otherObstacles, otherObstaclesPool);
StartCoroutine(ShuffleList(otherObstaclesPool));
}
private void LateUpdate()
{
if (spawnNow)
{
spawnNow = false;
if (first_find_star)
{
nextObstacleToSpawn = FindStar(normalStarsPool);
first_find_star = false;
}
//spawn the current star
int randomNumber = Random.Range(0, 100);
if(randomNumber >= 20){
nextObstacleToSpawn = FindStar(normalStarsPool);
}
else{
Debug.Log("corrupt star");
nextObstacleToSpawn = FindStar(otherObstacles);
}
SpawnStar(nextObstacleToSpawn);
}
}
void GeneratePrefabs(int how_many, List<Obstacle> prefabList, List<Obstacle> poolList)
{
foreach (Obstacle prefab in prefabList)
{
for (int i = 0; i <= how_many; i++)
{
Obstacle go = new Obstacle
{
gameObj = Instantiate(prefab.gameObj)
};
go.regular_Trail = go.gameObj.transform.GetChild(1).gameObject;
go.starExplosion = go.gameObj.transform.GetChild(0).gameObject;
go.color = prefab.color;
go.gameObj.SetActive(false);
//setap all the colors for the obstacle
ParticleSystem ps = go.starExplosion.GetComponent<ParticleSystem>();
ParticleSystem.MainModule psmain = ps.main;
psmain.startColor = go.color;
//setup the collor of a partycle system
ps = go.starExplosion.GetComponent<ParticleSystem>();
psmain = ps.main;
psmain.startColor = go.color;
psmain.startColor = go.color;
go.gameObj.GetComponent<Renderer>().material.color = go.color;
poolList.Add(go);
}
}
}
Obstacle FindStar(List<Obstacle> poolList)
{
while (true)
{
int randomIndex = Random.Range(0, poolList.Count);
if (!poolList[randomIndex].gameObj.activeInHierarchy)
{
Color color = poolList[randomIndex].color;
color.a = 0.5f;
panel.GetComponent<Renderer>().material.color = color;
return poolList[randomIndex];
}
else randomIndex = Random.Range(0, poolList.Count);
}
}
void SpawnStar(Obstacle star)
{
star.firstCollistion = false;
star.starExplosion.SetActive(false);
star.regular_Trail.SetActive(true);
star.gameObj.GetComponent<MeshRenderer>().enabled = true;
ScaleDifficulty();
star.gameObj.transform.position = Obstacle.SpawnLocation();
star.gameObj.SetActive(true);
}
//Shuffle a list every 4 seconds, don't pus this in an update or something cuz it's a coroutine
IEnumerator ShuffleList(List<Obstacle> list_to_Shuffle)
{
while (true)
{
yield return new WaitForSeconds(4f);
for (int i = 0; i < list_to_Shuffle.Count; i++)
{
Obstacle temp = list_to_Shuffle[i];
int randomIndex = Random.Range(i, list_to_Shuffle.Count);
list_to_Shuffle[i] = list_to_Shuffle[randomIndex];
list_to_Shuffle[randomIndex] = temp;
}
}
}
//this will scale the difficulty as the score get's higher and higher
public void ScaleDifficulty()
{
if (Menu.score < 60)
{
//the speed of the star as the score goes up
starSpeed = ((float)Menu.score / 30) + 1f;
//how close relative to the player will the stars spawn in the x and y axis?
closerToPlayer += Menu.score / 60;
// Debug.Log(starSpeed);
}
}
}
As you can see i generated the Obstacle objects in this method:
void GeneratePrefabs(int how_many, List<Obstacle> prefabList, List<Obstacle> poolList)
{
foreach (Obstacle prefab in prefabList)
{
for (int i = 0; i <= how_many; i++)
{
Obstacle go = new Obstacle
{
gameObj = Instantiate(prefab.gameObj)
};
go.regular_Trail = go.gameObj.transform.GetChild(1).gameObject;
go.starExplosion = go.gameObj.transform.GetChild(0).gameObject;
go.color = prefab.color;
go.gameObj.SetActive(false);
//setap all the colors for the obstacle
ParticleSystem ps = go.starExplosion.GetComponent<ParticleSystem>();
ParticleSystem.MainModule psmain = ps.main;
psmain.startColor = go.color;
//setup the collor of a partycle system
ps = go.starExplosion.GetComponent<ParticleSystem>();
psmain = ps.main;
psmain.startColor = go.color;
psmain.startColor = go.color;
go.gameObj.GetComponent<Renderer>().material.color = go.color;
poolList.Add(go);
}
}
}
In short, you can't. Not the way you've got things set up. If you need to access that object globally, you'll need to keep track of it globally. Unity provides a way to do that (essentially the Service Locator pattern), but you've said you don't want to use it. So you're going to have to build your own.
Assuming you've got a collection of your Obstacle objects, e.g. IList<Obstacle> obstacles you could get it by obstacles.Where(o => o.gameObj == myLocalGameObjReference). A better solution, assuming you have this script attached 1:1 for each Obstacle would be to simple inject the Obstacle into the script. Look at a dependency injection (DI) framework like Zenject to assist with configuring and managing your dependencies. Another alternative is the singleton pattern. Personally, I'd opt for DI over singleton. The singleton seems easier at first, but you'll quickly run into trouble with it.
Now in another script I need to acces the instance of the Obstacle class that this gameObj instance is in.
Given an instance of an Obstacle class, how can you even know that it is referenced by an instance of GameObject at all? Or how can you know that it's referenced by only one GameObject?
Unless there's a two-way reference - the GameObject has a reference to the Obstacle and the Obstacle also has a reference back to the GameObject - there's no way to determine what references an instance of Obstacle.

Trying to put a copy/clone/duplicant of a class in a List

I'm having a problem with a class which derives from MonoBehavior and is attached to a GameObject. When this GameObject("the projectile") collides with something, the component "Effect" should be duped and stored in a List() on the hitted target. After the assignment the projectile should be destroyed.
When i run this, i see in the List only "Missing (Effect)". I guess i assign just a reference into the list, and when the projectile gets deleted the reference is getting lost.
Projectile GameObject has Effect and Projectile classes as components attached:
public class Effect : MonoBehaviour {
public float dps = 2f;
public float duration = 2f;
public operatingDelay = 0.1f;
public bool freezing = false;
void Start(){
StartCoroutine("DamageHealRoutine");
}
IEnumerator DamageHealRoutine() {
int goalCount = (int)(duration / operatingDelay);
float partialDamage = dps * operatingDelay;
for (int i = 0; i < goalCount; i++) {
if (dps > 0)
stats.TakeDamage(partialDamage);
else if (dps < 0)
stats.GetHeal(partialDamage);
yield return new WaitForSeconds(operatingDelay);
}
}
}
public class Projectile : MonoBehaviour {
public Effect effect;
private void Awake() {
effect = GetComponent<Effect>(); // not null
}
public void hittedSomething(GameObject go) {
go.GetComponent<Stats>().effects.Add(effect);
// Without destroying, the List entry is assinged accordingly.
Destroy(this.gameObject);
//Became Missing when destroying the Projectile
}
}
Target GameObject has the stats class as component attached:
public class Stats : MonoBehaviour {
public List<Effect> effects;
}
Effect has to a inheritor from MonoBehavior, because it should be able to start Coroutines and i want to alter it's values.
Is there a possibility to achieve this without adding the Effect as a component on the target?
Edit1:
Missing
Effect has to a inheritor from MonoBehavior, because it should be able
to start Coroutines and i want to alter it's values.
Is there a possibility to achieve this without adding the Effect as a
component on the target?
Yes. You simply need a reference of any MonoBehaviour from any script you are 100% sure that won't be destroyed. I said this because if they are destroyed, the coroutine may stop running.
In this example I will get the reference from Stats script but you can get it from any script you wish.
New Stats script script:
public class Stats : MonoBehaviour
{
public List<Effect> effects;
private MonoBehaviour mono;
public MonoBehaviour monoRef
{
get
{
return mono;
}
}
// Use this for initialization
void Awake()
{
mono = this;
}
}
New Effect script. No MonoBehaviour required:
public class Effect
{
public float dps = 2f;
public float duration = 2f;
public operatingDelay = 0.1f;
public bool freezing = false;
MonoBehaviour coroutineMono;
public Effect()
{
coroutineMono = GameObject.Find("StatsObj").GetComponent<Stats>().monoRef;
coroutineMono.StartCoroutine("DamageHealRoutine");
}
IEnumerator DamageHealRoutine()
{
int goalCount = (int)(duration / operatingDelay);
float partialDamage = dps * operatingDelay;
for (int i = 0; i < goalCount; i++)
{
if (dps > 0)
stats.TakeDamage(partialDamage);
else if (dps < 0)
stats.GetHeal(partialDamage);
yield return new WaitForSeconds(operatingDelay);
}
}
}
You should now use Effect effect = new Effect () and it will start the coroutine without MonoBehaviour.

Categories

Resources