I'm fairly new to programming and right now I'm working on a 2D-Game for Android with Unity. The basic concept right now is that a object moves to the middle of the screen and it needs to "touch" an other object so you can press on a button and the moving object is getting destroyed. After it got destroyed I want another to spawn in and keep that cycle going. There is my problem. I have the (moving) object as a prefab and added it as the public GameObject "RedTriangle". The object is getting spawned in and moves to the center of the screen but it doesn't change the layer of the new objects when I press the button (I guess because its not the same object as the "RedTriangle" object). I really don't know how to change the Layer of new spawned in objects and hope I can get help here. Thanks already for all responses.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class Red : MonoBehaviour, IPointerClickHandler
{
public GameObject RedTriangle;
public GameObject FalseTriangle;
public Transform spawnPos;
public float spawnT;
private void Start()
{
GameObject newTriangle = Instantiate(RedTriangle, spawnPos.position, Quaternion.identity);
}
IEnumerator touchCD()
{
while (true)
{
yield return new WaitForSeconds(0.1f);
if (RedTriangle == null)
{
}
else if (RedTriangle != null)
{
Debug.Log("!= null");
RedTriangle.layer = 0;
}
}
}
public void Update()
{
if (RedTriangle == null)
{
spawnT = Random.Range(1, 4);
if (spawnT == 3)
{
GameObject newTriangle = Instantiate(FalseTriangle, spawnPos.position, Quaternion.identity);
}
else
{
GameObject newFalseTriangle = Instantiate(RedTriangle, spawnPos.position, Quaternion.identity);
}
}
}
public void OnPointerClick(PointerEventData eventData)
{
RedTriangle.layer = 7;
Debug.Log("button");
StartCoroutine(touchCD());
}
Assuming you want to change the layer of the object immediately after spawning, just do exactly that. For example:
GameObject newTriangle = Instantiate(FalseTriangle, spawnPos.position, Quaternion.identity);
newTriangle.layer = 0;
If you want to change the current (MonoBehaviour) object's own layer from its own attached component script e.g. when a certain event occurs, you don't need to keep the reference in your own variable as you can just use for example:
gameObject.layer = 0;
Related
I am creating a sort of game where you have to sell some food. I am programming the prototype of the game, and my idea is that after you place the food (in this case an hamburger) on a certain place, it's sold. I would like that this gameObject disappear and respawn in another point, but my script doesnt work at all. Can you help me? Thanks a lot.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class sellingSystem : MonoBehaviour
{
public GameObject food;
public bool foodExist;
void Start()
{
food = Resources.Load("food") as GameObject;
}
void Update()
{
food = GameObject.Find("food");
if(GameObject.Find("food") != null)
{
foodExist = true;
}
if (foodExist == false)
{
Instantiate(food, new Vector3(2.996f, 1.249f, -7.474f), Quaternion.identity);
foodExist = true;
}
}
public void OnCollisionEnter(Collision collision)
{
if(collision.gameObject.name == "food")
{
Destroy(food);
foodExist = false;
}
}
}
Why are you instantiating the food obj with Resources.Load and then in the update using GameObject.Find?
Instantiate(food, new Vector3(2.996f, 1.249f, -7.474f), Quaternion.identity);
Also in the above line you are trying to instantiate an object that is null, if there isn't a food object you cannot make a "clone" of it.
May I suggest a different approach? Instead of destroying the object use the SetActive() method and then re-locate the object to the desired position.
Just wondering if anyone could help me out with a small issue in my code. On line 60, there is an error essentially telling me what I am referencing to does not exist. To my knowledge it does, but I am very new to Unity. I am trying to create a random dungeon generator for a University project. My classes are below:
using System.Collections.Generic;
using UnityEngine;
public class RoomSpawner : MonoBehaviour
{
public int openingDirection;
//1 = need bottom door
//2 = need top door
//3 = need left door
//4 = need right door
//So for a room with a door on the right, you will type 3, as a left door is needed in the next room to connect the two rooms.
private RoomTemplates templates;
private int rand;
private bool spawned = false;
private Destroyer destroyer;
void Start()
{
templates = GameObject.FindGameObjectWithTag("Rooms").GetComponent<RoomTemplates>();
Invoke("Spawn", 0.1f);
}
void Spawn()
{
if (spawned == false)
{
rand = Random.Range(0, templates.bottomRooms.Length);
if (openingDirection == 1)
{
//Need to spawn room with BOTTOM door
Instantiate(templates.bottomRooms[rand], transform.position, templates.bottomRooms[rand].transform.rotation);
}
else if (openingDirection == 2)
{
//Need to spawn room with TOP door
Instantiate(templates.topRooms[rand], transform.position, templates.topRooms[rand].transform.rotation);
}
else if (openingDirection == 3)
{
//Need to spawn room with LEFT door
Instantiate(templates.leftRooms[rand], transform.position, templates.leftRooms[rand].transform.rotation);
}
else if (openingDirection == 4)
{
//Need to spawn room with RIGHT door
Instantiate(templates.rightRooms[rand], transform.position, templates.rightRooms[rand].transform.rotation);
}
spawned = true;
}
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Spawn Point"))
{
if (other.GetComponent<RoomSpawner>().spawned == false && spawned == false)
{
Instantiate(templates.closedRoom, transform.position, Quaternion.identity);
}
spawned = true;
}
}
}
using System.Collections.Generic;
using UnityEngine;
public class Destroyer : MonoBehaviour
{
void OnTriggerEnter2D(Collider2D other)
{
Destroy(other.gameObject);
}
}
This issue is causing the entry room to be blocked by closed rooms, which are meant to block exits out into the scene view. I have followed BlackThornProd's tutorial on this, but cannot figure out what I have done wrong. Here is a link to the tutorial.
Many thanks!
EDIT
Here I have included the RoomTemplates Class if it helps. Many Thanks!
using System.Collections.Generic;
using UnityEngine;
public class RoomTemplates : MonoBehaviour
{
public GameObject[] bottomRooms;
public GameObject[] topRooms;
public GameObject[] leftRooms;
public GameObject[] rightRooms;
public GameObject closedRoom;
}
Are templates a gameobject? If so, I think you can try something like this:
private GameObject templates;
void Start()
{
templates = GameObject.FindGameObjectWithTag("Rooms");
RoomTemplates Templates = templates.GetComponent<RoomTemplates>();
Invoke("Spawn", 0.1f);
}
My problem is that when all the enemies are killed the scene that should be loaded is not loading. I did add the scene to the Build setting (it has an index of 3) but it is still not loading. The script I created is attached to an empty object and not directly to the sprite (is that okay?). Can someone tell me why the scene isn't loading? Thank you.
This image is for to show you the EnemySpawner empty object inspector
EnemySpawner Script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class EnemySpawner : MonoBehaviour {
[SerializeField] GameObject EnemyPreFab;
[SerializeField] int MaxEnemies = 30;
[SerializeField] float EnemySpawnTime = 1.00001f;
[SerializeField] GameObject FirstWaypoint;
int CurrentNumOfEnemies = 0;
public LevelManager myLevelManager;
public int maximumnumberofhits = 0;
int timesEnemyHit;
IEnumerator SpawningEnemies()
{
while(CurrentNumOfEnemies <= MaxEnemies)
{
GameObject Enemy = Instantiate(EnemyPreFab, this.transform.position, Quaternion.identity);
CurrentNumOfEnemies++;
yield return new WaitForSeconds(EnemySpawnTime);
}
}
void Start()
{
StartCoroutine(SpawningEnemies());
timesEnemyHit = 0;
if (this.gameObject.tag == "EnemyHit")
{
CurrentNumOfEnemies++;
}
}
void OnCollisionEnter2D()
{
timesEnemyHit++;
if (timesEnemyHit == maximumnumberofhits)
{
CurrentNumOfEnemies--;
Destroy(this.gameObject);
}
if (CurrentNumOfEnemies == 0)
{
myLevelManager.LoadLevel("NextLevelMenu");
Debug.Log("LevelLoaded");
}
}
}
LevelManger script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LevelManager : MonoBehaviour {
public void LoadLevel(string name)
{
print("Level loading requested for" + name);
SceneManager.LoadScene(name);
}
}
EnemyShooting Script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyShooting : MonoBehaviour {
[SerializeField] float EnemyLaserSpeed = 10f;
[SerializeField] float EnemyLaserFireTime;
[SerializeField] GameObject LaserBulletEnemyPreFab;
[SerializeField] int MaxNumberOfHits = 1;
int CurrentNumberOfHits = 0;
Coroutine FireCoroutine;
void OnTriggerEnter2D(Collider2D collider)
{
if(collider.gameObject.tag == "PlayerLaser")
{
if(CurrentNumberOfHits < MaxNumberOfHits)
{
CurrentNumberOfHits++;
Destroy(collider.gameObject);
Score.ScoreValue += 2;//The user will be rewarded 1 point
}
}
}
void DestroyEnemy()
{
if(CurrentNumberOfHits >= MaxNumberOfHits)
{
Destroy(gameObject);
}
}
private void Fire()
{
FireCoroutine = StartCoroutine(ShootContinuously());
}
void BecomeVisible()
{
Fire();
}
IEnumerator ShootContinuously()
{
while (true)
{
GameObject LaserBulletEnemy = Instantiate(LaserBulletEnemyPreFab, this.transform.position, Quaternion.identity) as GameObject;
LaserBulletEnemy.GetComponent<Rigidbody2D>().velocity = new Vector2(0, EnemyLaserSpeed);
EnemyLaserFireTime = Random.Range(0.5f, 0.9f);
yield return new WaitForSeconds(EnemyLaserFireTime);
}
}
// Use this for initialization
void Start () {
BecomeVisible();
}
// Update is called once per frame
void Update () {
DestroyEnemy();
}
}
EnemyPathing script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyPathing : MonoBehaviour {
[SerializeField] List<Transform> WayPoints;
[SerializeField] float EnemyMovingSpeed = 5f;
int WayPointIndex = 0;
void EnemyMoving()
{
if (WayPointIndex <= WayPoints.Count - 1)
{
var TargetedPosition = WayPoints[WayPointIndex].transform.position; //The position of where the enemy needs to go
TargetedPosition.z = 0f;
var MoveThisFrame = EnemyMovingSpeed * Time.deltaTime;
transform.position = Vector2.MoveTowards(this.transform.position, TargetedPosition, MoveThisFrame);
if(transform.position == TargetedPosition)
{
WayPointIndex++;
}
}
else
{
Destroy(gameObject);
}
}
// Use this for initialization
void Start () {
transform.position = WayPoints[WayPointIndex].transform.position;
}
// Update is called once per frame
void Update () {
EnemyMoving();
}
}
Problem
You're checking for collisions on the SPAWNER; when someone hits the Spawner it counts down enemies. But the Spawner doesn't have a collision box in the screenshot so it can never be hit. The Scene changing code can never be called.
So the game, based on the code, looks like this:
Spawn X enemies,
Hit the Spawner X times,
(Removed: Destroy the Spawner,)
Change scene.
I'm guessing this is conceptually incorrect and you actually want to check collisions on the spawned enemies, which will then count up the amount of destroyed enemies, and change the scene when they are all dead.
Solution
Conceptually, what you want is:
Spawn X enemies
Count up variable for every enemy
On Enemy death, count it down
When 0, change scene
So how do we code this?
Well, every enemy needs a reference to the object that holds the count. You can do this in several ways, when I personally do it I usually have just one spawner that is responsible for everyone so I make that one a Singleton, that can be references from anywhere:
EnemySpawner
public class EnemySpawner : MonoBehaviour
{
public static Spawner Instance = null;
int CurrentNumOfEnemies = 0;
// ... etc
void Start()
{
if (Instance == null)
Instance = this;
// Spawn enemies like you do already, CurrentNumOfEnemies++ for every spawned
}
public OnEnemyDeath() {
CurrentNumOfEnemies--;
if (CurrentNumOfEnemies < 1)
{
// You killed everyone, change scene:
LevelManager.LoadLevel("Your Level");
}
}
}
Enemy script (I don't know how your current code looks, but here's a minimal solution based on how I THINK your code looks):
void OnDestroy()
{
// This will run automatically when you run Destroy() on this gameObject
EnemySpawner.Instance.OnEnemyDeath(); // Tell the EnemySpawner that someone died
}
This will only work if you have exactly only ONE spawner. If you have multiple ones you will have to send a reference to the instance of its spawner to every spawned enemy. I can show you how to do ths too, if you wish.
Bonus content
LevelManager doesn't need to be on a GameObject, it can be static instead:
Remove the LevelManager script from any GameObject
Change your LevelManager code to this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public static class LevelManager
{
public static void LoadLevel(string name)
{
Debug.Log("Level loading requested for" + name);
SceneManager.LoadScene(name);
}
}
Now you can use it from ANYWHERE, without needing to initialize a reference to any script or GameObject:
LevelManager.LoadLevel("My Level");
myLevelManager.LoadLevel("NextLevelMenu"); is never executed, because you destroy the object in the if-test above.
I have multiple empty game objects that serve as the spawn points that spawn the game objects and I want the spawned game objects to be destroyed and Instantiate a new one on same spawn point if the condition to be tested is true.
I have 2 separate scripts, the one attached on the spawn point objects and another for the game manager that has the condition in it.
The Condition on the Game manager script:
public void checkword()
{
wordBuilded = displayer.text.ToString();
LetterTiles[] tiles = FindObjectsOfType<LetterTiles>();
foreach (LetterTiles item in tiles)
{
if (txtContents.Contains(wordBuilded))
{
if (item.gameObject.CompareTag("clicked"))
{
Destroy(item.gameObject);
FindObjectOfType<letterSpawner>().refresh();
}
}
else
{
if (item.gameObject.CompareTag("clicked"))
item.GetComponent<Button>().interactable = true;
}
}
}
The script attached to the spawn point objects that Instantiates the objects
using UnityEngine;
public class letterSpawner : MonoBehaviour {
public GameObject[] letterTiles;
GameObject tiles;
Vector3 scale = new Vector3(0.8f, 0.8f, 0);
void Start () {
refresh();
}
public void refresh()
{
int rand = Random.Range(0, letterTiles.Length);
tiles = Instantiate(letterTiles[rand], transform.position, Quaternion.identity);
tiles.transform.SetParent(gameObject.transform);
tiles.transform.localScale = scale;
}
}
You can do that by making a small change to whatever you have, first change your refresh function into this one
public void refresh(Vector3 position)
{
int rand = Random.Range(0, letterTiles.Length);
tiles = Instantiate(letterTiles[rand], position, Quaternion.identity);
tiles.transform.SetParent(gameObject.transform);
tiles.transform.localScale = scale;
}
also in the same file add another default one that calls this with the default value that you had
public void refresh()
{
refresh(transform.position);
}
and then in your checkword function
if (item.gameObject.CompareTag("clicked"))
{
Vector3 pos = item.transform.position;
Destroy(item.gameObject);
FindObjectOfType<letterSpawner>().refresh(pos);
}
that should do it for you
I have a Parent GameObject ZombieArmy with an attached script Zombie; its Transform changes each time a new zombie is instantiated as a child. How do I prevent the zombieArmy transform from changing and keep its transformed fixed at Vector3(0,0,0) while having the zombie have its own unique transform from each reSpawn()?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Zombie : MonoBehaviour {
public GameObject zombiePrefab;
public Transform zombieArmy;
public Transform zombieSpawnPoint;
private Transform[] spawnPositions;
public bool reSpawn = false;
private bool lastToggle = false;
private GameObject spawn;
// Use this for initialization
void Start () {
spawnPositions = zombieSpawnPoint.GetComponentsInChildren<Transform>();
}
private void NewSpawn() //spawn location of newZombie
{
if (reSpawn)
{
int i = Random.Range(1, spawnPositions.Length);
transform.position = spawnPositions[i].transform.position;
spawn = Instantiate(zombiePrefab, this.transform.position, this.transform.rotation, zombieArmy);
// zombieArmy.transform.position = new Vector3(0, 0, 0);
}
}
void Update () { //T-toggle
if (reSpawn != lastToggle)
{
NewSpawn();
reSpawn = false;
}
else
lastToggle = reSpawn;
}
}
If I'm understanding you correctly, that Zombie script is attached to the parent gameobject, right?
Then your NewSpawn() method is a bit incorrect.. This line
transform.position = spawnPositions[i].transform.position;
is actually changing the transform of the parent gameobject, as you say, because that's exactly what you're telling it to do.
If what you want is place each newly spawned object in the location of the randomly selected spawn point, try this instead:
int i = Random.Range(0, spawnPositions.Length); // Any reason the 0th index shouldn't be used?
spawn = Instantiate(zombiePrefab, this.transform);
spawn.transform.position = spawnPositions[i].transform.position;
spawn.transform.rotation = spawnPositions[i].transform.rotation;