Unity point clone to specific GameObject - c#

I want to have clones of a certian gameobject face the direction of the player at an angle, for example, if there were enemy clones with guns I would want the guns to face the players position. Any ideas how to do?
This is even harder to achieve because since it's a clone you cannot access other GameObjects.
Script:
public class GunProperties : MonoBehaviour
{
public Transform target;
void Update()
{
transform.LookAt(target);
}
}

You could initialize the clones with some values when you instantiate it.
Example:
In this case I have a GameManager that keeps track on the player, and passes the reference over when an enemy is spawned.
public class GameManager : MonoBehaviour
{
public Transform player;
public Enemy enemyPrefab;
void SpawnEnemy() {
var enemy = Instantiate<Enemy>(enemyPrefab);
enemy.Init(player)
}
}
public class Enemy : MonoBehaviour {
private GunProperties gun;
public void Init(Transform target) {
gun.target = target;
}
}
You could also reverse it by making the GameManager a singleton and having the enemies find the player on awake by using GameManager.Instance.player

Related

How to call a script component from a GameObject that is child to another GameObject, but from other GameObject?

In my unity project, I have a GameObject called "Spawn_manager", that has another Child GameObject called "Enemy_Container". The script of Spawn_Manager makes "Enemy" prefabs instantiate inside the "Enemy_Container". I am trying to acess the "Enemy" script(also named Enemy) from another script.
Anyone knows how I can get it?
I tried writing this:
private Enemy _enemy;
private void Start()
{
_enemy = GameObject.Find("Enemy").GetComponent<Enemy>();
}
But obviously it is not working, but I can't get it to work. Basically, I wanna the script, and the path would be Spawn_Manager>Enemy_Container>Enemy.
If you want to make the Enemy easily accessible you can add all spawned Enemy to static list. Like this:
public class Enemy : MonoBehaviour
{
public static List<Enemy> AllEnemy { get; private set; } = new List<Enemy>();
private void Awake()
{
AllEnemy.Add(this);
}
private void OnDestroy()
{
AllEnemy.Remove(this);
}
}
Using Find is demanding and not a reliable operation. If you will have any other GameObjects with name Enemy it will found them instead of desired one.
in this case i think that problem is that after instantiation prefab new GameObject have name in format $"{prefab.name}(Clone)".

Is it okay in OOP to handle object events on outside the object class?

Hello i make my 2d game with unity and i feel confuse about oop design.
There is a 4 class in my game.
StageView : The view(scene) where the game logic run.
ObjectPool : The object pool that can manage the gameobjects, and it is the member field of
stage view. (It is not a singleton)
Projectile : The projectile class that can attack the monster.
Monster : The monster class that could be attacked by projectile.
public class Monster : MonoBehaviour
{
private int hp;
public delegate void OnMobHitHandler(Projectile projectile, Monster monster, long damage);
public delegate void OnMobDieHandler(Monster monster);
public OnMobHitHandler OnMobHit;
public OnMobDieHandler OnMobDie;
public void OnTriggerEnter2D(Collider2D collision)
{
Projectile projectile = collision.gameObject.GetComponent<Projectile>();
hp -= projectile.Power;
OnMobHit?.Invoke(projectile, this, projectile.Power);
if(hp <= 0)
OnMobDie?.Invoke(this);
}
}
If the monster got hit, then the fragment object will be created.
And if the monster die, then it must be released.
But the fragment objects are managed by ObjectPool which is in StageView.
(Also monster are managed by ObjectPool samely)
So there are 2 choices in my head.
Make Init(Objectpool objectpool) Method and pass the objectpool to monster as a parameter.
Then put "create fragment logic" and "Release(die) monster logic" in to the Monster class.
Expose the monster's callback event to the StageView like above example code.
Their 2 logics must be in StageView's OnMonsterHit, OnMonsterDie.
I choiced second, because i want to loosen the dependencies of Monster class.
(You know, if i don't loosen the dependencies then it's gonna be more complicate later)
But sometimes, I think that it may seem more appropriate for the object to handle it internally rather than entrusting it to the outside.
I'm very confused which one is more correct in OOP aspect.
I think it's a good idea to make one more layer between ObjectPool and Monster classes that will manage what you want. It will complete Single responsibility principle of SOLID.
So both classes ObjectPool and Monster will not depend on each other and every class will be making their jobs.
I done it next way (similar to your second example): Spawner components containing and sometimes sharing the same Pool ScriptableObject. When a Spawner spawns an object it passes OnDespawn handler to Despawner component on the pooled object. From there the spawned object is on its own.
In a sense Spawners act as a middle (sub-pool) layer that (together with other components) are responsible to setup the freshly pooled object.
Btw Spawner adds Despawner component on the pooled object if it is missing and Despawner have destroy fall-back if OnDespawn handler is not assigned. This ensures all prefabs can work both as pool's prototype and stand-alone instantiable.
The Spawner component goes on a spawner game object (for example tied to some trigger-collision region that calls one of the spawn public methods):
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
namespace SpawnSystem
{
public class Spawner : MonoBehaviour
{
[Header("Pooler")]
// in this case this is pool randomizer but it could be directly a ScriptableObject pool
[SerializeField] private PoolAlternator pooler;
[Header("Debug")]
// this is just to see in the inspector what is spawned for debugging, it can be skipped in a build
[SerializeField] [ReadOnly] private List<GameObject> spawned;
[Header("Events")]
[SerializeField] private UnityEvent<GameObject> onSpawn;
[SerializeField] private UnityEvent<GameObject> onDespawn;
#region Spawn
public GameObject GetSpawn()
{
GameObject spawnedObject = pooler.Spawn();
SpawnRoutine(spawnedObject);
return spawnedObject;
}
public void Spawn()
{
GameObject spawnedObject = pooler.Spawn();
SpawnRoutine(spawnedObject);
}
public void Spawn(Vector3 position)
{
GameObject spawnedObject = pooler.Spawn(position);
SpawnRoutine(spawnedObject);
}
public void Spawn(Vector3 position, Quaternion rotation)
{
GameObject spawnedObject = pooler.Spawn(position, rotation);
SpawnRoutine(spawnedObject);
}
public void Despawn(GameObject go)
{
if (go == null) { return; }
onDespawn?.Invoke(go);
spawned.Remove(go);
pooler.Despawn(go);
}
public void DespawnAll()
{
for (int i = spawned.Count - 1; i >= 0; i--)
{
Despawn(spawned[i]);
}
}
#endregion Spawn
private void SpawnRoutine(GameObject spawnedObject)
{
if (spawnedObject == null) { return; }
spawned.Add(spawnedObject);
SetupDespawner(spawnedObject);
onSpawn?.Invoke(spawnedObject);
}
private void SetupDespawner(GameObject spawnedObject)
{
Despawner despawner =
spawnedObject.GetComponentInChildren<Despawner>()
?? spawnedObject.AddComponent<Despawner>();
despawner.SetReturnToPool(Despawn);
}
}
}
The Despawner component lives at the spawned/pooled object. When the entity is dead (say health handling/changing component detects HP is <= 0) it calls Despawner.Despawn():
using UnityEngine;
using UnityEngine.Events;
namespace SpawnSystem
{
public class Despawner : MonoBehaviour
{
[Tooltip("Fall-back strategy when the object is not spawned from a pool.")]
[SerializeField] private FallbackStrat fallbackTo = FallbackStrat.Destroy;
[Header("Events")]
[SerializeField] UnityEvent<GameObject> onDespawn;
private enum FallbackStrat { Destroy = 0, Disable = 1 }
private UnityAction<GameObject> returnToPoolRoutine;
public void SetReturnToPool(UnityAction<GameObject> returnToPool)
{
this.returnToPoolRoutine = returnToPool;
}
public void Despawn()
{
onDespawn?.Invoke(this.gameObject);
if (returnToPoolRoutine != null)
{
returnToPoolRoutine.Invoke(this.gameObject);
returnToPoolRoutine = null;
}
else { Fallback(this.gameObject); }
}
private void Fallback(GameObject go)
{
switch (fallbackTo)
{
default:
case FallbackStrat.Destroy:
Destroy(go);
break;
case FallbackStrat.Disable:
go.SetActive(false);
break;
}
}
}
}

Instantiating a Prefab returns the NullReferenceException error

I'm using a namespace to instantiate a prefab in my game however unity thinks that the prefab is not a GameObject and returns the NullReferenceException error
I've linked the Prefab, properly in the GameObject that holds the script. This is the code that I currently have in Visual Studio but I also tried various forms of the code, they are what follows the first lines of code
public GameObject Prefab;
public void OnAppear(){
GameObject spawn = Instantiate(Prefab, Spawnpoint.position, Spawnpoint.rotation);
spawn.transform.parent = Spawnpoint.transform;}
V1
var spawn = Instantiate(Prefab, Spawnpoint.position, Spawnpoint.rotation);
V2
var spawn = Instantiate(Prefab, Spawnpoint.position, Spawnpoint.rotation) as GameObject;
Entire script:
namespace AugReal
{
public class StartAll : MonoBehaviour
{
public Transform Spawnpoint;
public GameObject Prefab;
public void OnAppear()
{
GameObject spawn = Instantiate(Prefab, Spawnpoint.position, Spawnpoint.rotation);
spawn.transform.parent = Spawnpoint.transform;
}
public void OnDisappear()
{
Debug.Log("You lose");
}
}
}
Inspector:
Try the following code instead:
You do not need to create a public reference to the transform that this script is attached to. Since the script is a monobehaviour, you can directly access this via this.transform
Rather than setting the parent explicitly after instantiating, consider usign the Instantiate method with the parent override.
(I have also change the casing on your property "Prefab". It doesn't affect the code, but standard is to keep property names camelCase to distinguish them from the PascalCased class types.)
namespace AugReal
{
public class StartAll : MonoBehaviour
{
public GameObject prefab;
public void OnAppear()
{
GameObject spawn = Instantiate(prefab, this.transform);
}
public void OnDisappear()
{
Debug.Log("You lose");
}
}
}

The local function 'OnTriggerEnter2D' is declared but never used

I don't know how to fix it. It's a 2D game. When you collide with the box, it should load/teleport you to the next scene, but it doesn't.
I tried everything on the internet and it didn't work.
using UnityEngine;
using UnityEngine.SceneManagement;
public class VictoryZone : MonoBehaviour
{
public void LoadNextLevel()
{
void OnTriggerEnter2D(Collider2D collider)
{
SceneManager.LoadScene(1);
}
}
}
I expect it to teleport me to my next level.
It looks like you nested the OnTriggerEnter2D() function inside of LoadNextLevel()? That's what the Local Function warning is referring to.
Those should be two separate functions, not one within the other. OnTriggerEnter2D() is a function of MonoBehavior; the MonoBehavior (VictoryZone) is what gets notified of the collision.
using UnityEngine.SceneManagement;
public class VictoryZone : MonoBehaviour {
public void LoadNextLevel() {
SceneManager.LoadScene(1);
}
void OnTriggerEnter2D(Collider2D collider) {
LoadNextLevel();
}
}
Note: you might also need to check the GameObject associated with collider to make sure that it's a player, and not an enemy or something (if, hypothetically, you had enemies or projectiles or other objects with colliders moving into the victory zone).

Instantiate and destroy GameObject/ParticleSystem

I currently have a script that should Instantiate a particle system and destroy it after a certain amount of time, but after the object is Instantiated, this error code shows up:
MissingReferenceException: The object of type 'ParticleSystem' has been
destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the
object.
the script is currently goes as such:
public class MuzzleFlash : MonoBehaviour {
public Transform gun;
public ParticleSystem smoke;
public ParticleSystem flare;
public ParticleSystem bullet;
private float smokeTime = 0.2f;
private void Update () {
if (Input.GetButtonDown("Fire1"))
{
smokeFun();
flare.Play();
bullet.Play();
}
}
void smokeFun()
{
Instantiate(smoke, gun);
Destroy(smoke, smokeTime);
}
}
How can i Instantiate this particle system and destroy it properly?
You are attempting to destroy the prefab ParticleSystem which the smoke variable not the ParticleSystem you instantiated.
The Instantiate function returns any GameObject it instantiates. To destroy the ParticleSystem you just instantiated, you have to instantiate it, store it in a temporary variable then destroy it with that temporary variable later on.
void smokeFun()
{
//Instantiate and store in a temporary variable
ParticleSystem smokeInstance = Instantiate(smoke, gun);
//Destroy the Instantiated ParticleSystem
Destroy(smokeInstance, smokeTime);
}

Categories

Resources