I want to spawn prefab objects but now the object spawn multiply in one position. I have set up 4 positions around the player and for spawning the objects I'm using this code:
public class EnemySpawner : MonoBehaviour {
public static EnemySpawner instance;
[SerializeField]
private int scoreMileStone = 100; //this will define number of cars in the scene
private int milestoneIncreaser = 100; //this sets new milestone once old is reached
[SerializeField]
private Transform[] spawnPos; //store all the available spawn position
[SerializeField]
private int policeCarRequired; //this will tell how much cars are needed in the scene at a time
private int currentPoliceCar; //this variables keep track of total number of cars in the scene
private GameObject target; //store player reference in this variable
private int lastPosition, r;
private int lastSpawnPos;
public int CurrentPoliceCar { get { return currentPoliceCar; } set { currentPoliceCar = value; } } //getter and setter
// Use this for initialization
void Awake ()
{
if (instance == null) instance = this;
}
// Update is called once per frame
void Update ()
{
if (GuiManager.instance.GameStarted == false || GuiManager.instance.GameOver == true)
return;
if (target == null) //if target is null
{
target = GameObject.FindGameObjectWithTag("Player"); //try detecting target again
return; //return from the method
}
MilestoneIncreaser(); //increase milestone
if (currentPoliceCar < policeCarRequired) //if currentPoliceCar is less than policeCarRequired
{
SpawnPoliceCar(); //spawn police car
}
}
void SpawnPoliceCar() //spawn police car
{
GameObject wolf = ObjectPooling.instance.GetPooledObject("Wolf"); //get police car reference from objectpooling
RandomPos();
int r = Random.Range (0, spawnPos.Length);
while(lastSpawnPos == r)
r = Random.Range (0, spawnPos.Length);
wolf.transform.position = new Vector3(spawnPos[r].position.x, 0, spawnPos[r].position.z); //set the transform
wolf.SetActive(true); //set it active in scene
wolf.GetComponent<Damage>().DefaultSetting(); //call DefaultSettings method
lastSpawnPos = r;
currentPoliceCar++; //increase currentPoliceCar by 1
}
void MilestoneIncreaser() //increase the milestone
{
if (GuiManager.instance.Score >= scoreMileStone) //if currentScore is greater or equal to scoreMilestone
{
scoreMileStone += milestoneIncreaser; //increase the milestone
if (policeCarRequired < 8) //if max policeCarRequired is less than 8
policeCarRequired++; //increase policeCarRequired by 1
}
}
void RandomPos()
{
int r = Random.Range(0, spawnPos.Length); //get random number between zero and total spawnpos
while(lastPosition == r)
r = Random.Range(0, spawnPos.Length);
}
}
That gives me the objects but without any space between them, the objects spawn for example 4 in one position and 3 in the other but they are squeezed. Can I fix that in some way?
Related
Background:
I'm trying to set up a system to spawn in a set amount of GameObjects, defined in the Menu Scene loaded before the Main Scene, into my game. I do this by running 3 for loops for the length of however many objects the user inputs in the Menu, with each loop run instantiating a copy of the Prefab.
The Problem:
Whenever the Main Scene (Game Scene) is loaded from the menu, it always spawns at least 1 of each Prefab even if the number is defined as 0 or null. This is an issue, as it always adds a copy of each Prefab on top of the user defined amount.
Partial Menu Image:
Menu segment for inputting values
Images of Issue:
Spawned assets in scene
Assets in navigator
So far I've tried setting up multiple Debug.Log(); -s throughout all the scripts that handle the numbers that are eventually parsed and passed to the spawning script itself (Data Managers, scriptable objs...) , as well as inside the spawning script itself. All the logs return the values as 0 and as such nothing should be spawning.
Code of the Spawner Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PawnSpawner : MonoBehaviour
{
[SerializeField] VarManager VarManager;
[SerializeField] GameObject Pawn;
[SerializeField] GameObject InfPawn;
[SerializeField] GameObject VaxPawn;
private int? totPawns;
private int? defPawns;
private int? infPawns;
private int? vaxPawns;
private void Awake()
{
VarManager = GameObject.Find("GameManager").GetComponent<VarManager>();
totPawns = VarManager.totalPawns;
defPawns = VarManager.uninfectedPawnAmount;
infPawns = VarManager.infectedPawnAmount;
vaxPawns = VarManager.vaccinatedPawnAmount;
}
// Start is called before the first frame update
void Start()
{
if(defPawns != 0 || defPawns != null)
{
for (int i = 0; i <= defPawns; i++)
{
int x = UnityEngine.Random.Range(-300, 301);
int y = UnityEngine.Random.Range(-300, 301);
GameObject D_Pawn = Instantiate(Pawn, new Vector3(x, y, 0), Quaternion.identity);
}
}
if(infPawns != 0 || infPawns != null)
{
for (int i = 0; i <= infPawns; i++)
{
int x = UnityEngine.Random.Range(-300, 301);
int y = UnityEngine.Random.Range(-300, 301);
GameObject I_Pawn = Instantiate(InfPawn, new Vector3(x, y, 0), Quaternion.identity);
}
}
if (vaxPawns != 0 || vaxPawns != null)
{
for (int i = 0; i <= vaxPawns; i++)
{
int x = UnityEngine.Random.Range(-300, 301);
int y = UnityEngine.Random.Range(-300, 301);
GameObject V_Pawn = Instantiate(VaxPawn, new Vector3(x, y, 0), Quaternion.identity);
}
}
}
// Update is called once per frame
void Update()
{
Destroy(this);
}
private void OnDestroy()
{
Debug.Log("---PAWN SPAWNER HAS FULLY EXECUTED SPAWNING SEQUENCE---");
Debug.Log("TOTAL PAWNS SPAWNED:" + totPawns);
Debug.Log("Default Pawns:" + defPawns);
Debug.Log("Infected Pawns:" + infPawns);
Debug.Log("Vaccinated Pawns:" + vaxPawns);
Debug.Log("---REMOVING SELF---");
}
}
I am making a tower defense game and everytime I summon an enemy, i add 1 to the enemyCount variable. and everytime the enemy reaches the endpoint, I subtract 1 to the same variable. Default value is zero.
For some reason the variable gives different values in different parts of the script. I have 2 other scripts that has access to enemyCount. One is the PlayerUI script which just displays it, and the other is the enemyMovement script which is suppose to subtract 1 to it everytime the enemy object is destroyed.
The value of enemyCount in PlayerUI is 0, and it updates when the subtraction happens so it becomes -1.
Now when i run the game again, it retains the value of -1, and when subtracted to -2, its -2 that is displayed on the next run.
The value of enemyCount when I log it in the update function inside the script is that it updates to +1 when the enemy is summoned but doesnt update when the enemy is destroyed.
Can anyone point out what i did wrong?
WaveSpawner script:
public Transform enemyPrefab;
private Transform[] spawners;
private bool summonWave = false;
private float countDown = 2;
private int enemyPerWave = 1;
private int waveCtr = 0;
private int enemyCount;
void Awake() {
spawners = new Transform[transform.childCount];
for (var i = 0; i < spawners.Length; i++) {
spawners[i] = transform.GetChild(i);
}
}
void Start() {
enemyCount = 0;
}
void Update() {
if(countDown <= 0) {
summonWave = true;
countDown = 999;
}
if (summonWave) {
StartCoroutine(waveSpawner());
}else {
countDown -= Time.deltaTime;
}
Debug.Log(enemyCount); //GIVES THE RIGHT VALUE BUT DOESNT SUBTRACT 1 WHEN ENEMY IS DESTROYED
}
public int getEnemyCount() { //Accessed by the PlayerUI script. just to display it onscreen.
Debug.Log(enemyCount); // GIVES A VALUE OF N-1
return enemyCount;
}
public void reduceEnemyCount() { //Accessed by the EnemyMovement script.
enemyCount--; //IF THE ENEMY REACHES THE END, THIS FUNCTION IS CALLED AND THEN THE ENEMY OBJECT IS DESTROYED
}
IEnumerator waveSpawner() {
waveCtr++;
summonWave = false;
for (int i = 0; i < enemyPerWave; i++) {
summonEnemies();
yield return new WaitForSeconds(0.3f);
}
}
void summonEnemies() {
enemyCount += 1; //ADDS 1 TO THE VARIABLE EVERY TIME I SPAWN AN ENEMY UNIT. IN THIS CASE JUST 1
Instantiate(enemyPrefab, spawners[0].position, spawners[0].rotation);
//Instantiate(enemyPrefab, spawners[1].position, spawners[1].rotation);
//Instantiate(enemyPrefab, spawners[2].position, spawners[2].rotation);
//Instantiate(enemyPrefab, spawners[3].position, spawners[3].rotation);
}
}
EnemyMovement script:
public WaveSpawner spawner;
private float speed = 35f;
private int numberOfCycles = 2;
private Transform target;
private Transform startingPoint;
private int cycleCtr = 0;
private bool isDestroyed;
void Start() {
target = getClosestWaypoint(Waypoints.points);
startingPoint = target;
isDestroyed = false;
}
void Update() {
Vector3 dir = target.position - transform.position;
transform.Translate(dir.normalized * speed * Time.deltaTime, Space.World);
if (Vector3.Distance(transform.position, target.position) <= 0.2f && !isDestroyed) {
if (cycleCtr == numberOfCycles) {
spawner.reduceEnemyCount(); //subtracts the enemyCount in WaveSpawner
isDestroyed = true;
Destroy(gameObject); //destroying the enemy unit
} else {
if (target.name == startingPoint.name) {
cycleCtr++;
}
target = getToNextWaypoint(target);
}
}
}
PlayerUI script:
public Text enemyCount_UI;
[SerializeField]
private WaveSpawner spawner;
private int enemyCount_var;
void Start() {
enemyCount_UI.text = "Enemy count: 0";
enemyCount_var = 0;
}
void Update() {
enemyCount_var = spawner.getEnemyCount();
enemyCount_UI.text = "Enemy count: " + enemyCount_var.ToString();
}
I believe it is because you have assigned WaveSpawner prefab to EnemyMovement prefab and PlayerUI object. And you also probably have instance of WaveSpawner added somewhere in the scene.
Let's call WaveSpawner (from prefab) a PrefabSpawner and WaveSpawner (from scene) a SceneSpawner. When game is started, SceneSpawner is created. It has its own value of enemy count. When you spawn a prefab of EnemyMovement it is referencing PrefabSpawner, not SceneSpawner. What happens, is that SceneSpawner always incremets its own enemyCount, while EnemyMovement decreases count on PrefabSpawner. Because PlayerUI is also accessing PrefabSpawner it sees its value instead of SceneSpawner.
Try changing the spawning to this:
void summonEnemies() {
enemyCount += 1; //ADDS 1 TO THE VARIABLE EVERY TIME I SPAWN AN ENEMY UNIT. IN THIS CASE JUST 1
var enemyInstnace = Instantiate(enemyPrefab, spawners[0].position, spawners[0].rotation);
enemyInstance.spawner = this;
}
Rather than having WaveSpawner script on both PlayerUI and EnemyMovement, you'll want to make sure they are accessing the same instance of the script.
If you haven't already, create an empty game object to be your "wave spawner" in the scene and drop the WaveSpawner script on that.
Then update PlayerUI and EnemyMovement to get a reference to that game object, and then its WaveSpawner script
public Text enemyCount_UI;
[SerializeField]
private GameObject waveSpawnerGameObject;
private WaveSpawner spawner;
private int enemyCount_var;
void Start() {
enemyCount_UI.text = "Enemy count: 0";
enemyCount_var = 0;
}
void Awake()
{
// Get the reference here
spawner = waveSpawnerGameObject.GetComponent<WaveSpawner>();
}
void Update() {
enemyCount_var = spawner.getEnemyCount();
enemyCount_UI.text = "Enemy count: " + enemyCount_var.ToString();
}
And do the same for your enemy movement and enemy prefabs
I dont know if I can call this algorithm. But I am working on a game in which player will move in a circular path.
As you can see in the picture player is suppose to orbit the circle. And obstacle shall be instantiated in the circle.I am trying to first create the obstacle in first half(left to the long cube) and then in the second half. But things are getting created in the next half too when code is not supposed to do that. Also, it is showing argument exception error. Please have a look at my code and tell me whether my method is wrong or my formulas are wrong or anything else.
public class ObjectInstantiater : MonoBehaviour {
DataHolder dataholder;
GameObject Obstacle;
LevelData leveldata;
private int currentlevel=0; // default level starts from 0
private List<GameObject> Inactivegameobject = new List<GameObject>(); // this object can be used
private List<GameObject> Activegameobject = new List<GameObject>();
private int totalgameobjects;
private int firsthalfgameobjects, secondhalfgameobjects;
public float outerradius;
public float innerradius;
private bool shallspawnouterradiues = true;
// Use this for initialization
void Awake () {
dataholder = (Object)GameObject.FindObjectOfType<DataHolder>() as DataHolder;
Obstacle = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
leveldata = dataholder.Leveldata[0];
}
void Start()
{
Updateleveldata();
FirstHalf();
}
public int Currentlevel
{
get { return currentlevel; }
set { currentlevel = value;
leveldata = dataholder.Leveldata[currentlevel];//sets the level data
}
}
private void Updateleveldata() // this function gets called after a round
{
totalgameobjects = Random.Range(leveldata.MinimumObstacle, leveldata.MaximumObstacle);
firsthalfgameobjects = Mathf.RoundToInt(totalgameobjects / 2);
secondhalfgameobjects = totalgameobjects - firsthalfgameobjects;
}
private void FirstHalf()
{
Debug.Log(firsthalfgameobjects);
Vector3 pos;
if (Inactivegameobject.Count < firsthalfgameobjects)
{
for (int x = 0; x <= (firsthalfgameobjects - Inactivegameobject.Count); x++)
{
GameObject obs = Instantiate(Obstacle) as GameObject;
obs.SetActive(false);
Inactivegameobject.Add(obs);
}
}
float spawnangledivision = 180 / firsthalfgameobjects;
float spawnangle = 180f;
for(int x = 0; x < firsthalfgameobjects; x++)
{
float proceduralRandomangle = spawnangle;
proceduralRandomangle = Random.Range(proceduralRandomangle , proceduralRandomangle + 2f);
if (shallspawnouterradiues)
{
pos = new Vector3(outerradius * Mathf.Cos(spawnangle), outerradius * Mathf.Sin(spawnangle), 0f);
shallspawnouterradiues = false;
}else
{
pos = new Vector3(innerradius * Mathf.Cos(spawnangle), innerradius * Mathf.Sin(spawnangle), 0f);
shallspawnouterradiues = true;
}
spawnangle += spawnangledivision;
Inactivegameobject[0].SetActive(true); // set it to 0
Inactivegameobject[0].transform.position = pos;
Activegameobject.Add(Inactivegameobject[0]);
Inactivegameobject.RemoveAt(0);
}
}
private void SecondHalf()// No need to check this
{
if (Inactivegameobject.Count < firsthalfgameobjects)
{
GameObject obs = Instantiate(Obstacle) as GameObject;
obs.SetActive(false);
Inactivegameobject.Add(obs);
}
}
}
I put together a simple script that creates a sprite-based pointer I can use in UI. It's not complete yet (still needs support for when the player pushes a button), but already running into problems.
I get a NullReferenceException: Object reference not set to an instance of an object
At line 52...
_ptrSpriteRenderer.sprite = newSprite;
...and 72
_ptr.transform.position = new Vector2(((float)_ptrPosX * _ptrPositionXDistance) + _ptrPositionTopLeft.x, ((float)_ptrPosY * _ptrPositionYDistance) + _ptrPositionTopLeft.x);
I am creating _ptr and _ptrSpriteRenderer in the Start, but for some reason at all my other functions these two GameObjects are null, and yet they aren't null during Start.
I'm sure it's something simple that I goofed up on, but I've spent hours comparing this class to other classes where I've created sprites and I can't see the issue.
using UnityEngine;
using System.Collections;
public class Pointer : MonoBehaviour {
private GameObject _ptr;
private Sprite _ptrSprite;
private SpriteRenderer _ptrSpriteRenderer;
private bool _ptrEnabled; // Is the pointer receiving input?
private int _ptrMovePerSecond; // Number of positions to move per second if input held down
private int _ptrXInput;
private int _ptrYInput;
private float _ptrTimeSinceLastInput = 0f;
private float _ptrTimePerInput = 0.25f;
private bool _ptrInputDelay = false;
private Vector2 _ptrPositionTopLeft; //The top left position the pointer can reach in the grid
private Vector2 _ptrPositionBottomRight; //The bottom right position the pointer can reach in the grid
private int _ptrPositionsX; //The number of grid positions the pointer can traverse in X
private int _ptrPositionsY; //The number of grid positions the pointer can traverse in Y
private float _ptrPositionXDistance; //The distance of each X position in the grid
private float _ptrPositionYDistance; //The distance of each Y position in the grid
private int _ptrPosX; //Current X position of pointer in the grid
private int _ptrPosY; //Current Y position of pointer in the grid
// Use this for initialization
void Start () {
_ptr = new GameObject();
_ptrSpriteRenderer = new SpriteRenderer();
_ptr.AddComponent<SpriteRenderer>();
_ptrSpriteRenderer = _ptr.GetComponent<SpriteRenderer>();
_ptrEnabled = true;
}
public void setSprite ( Sprite newSprite )
{
if (newSprite == null)
{
Debug.LogError("No sprite passed to setSprite in Pointer");
}
else
{
_ptrSpriteRenderer.sprite = newSprite;
}
}
public void setPositions (Vector2 positionTopLeft, Vector2 positionBottomRight, int numPositionsX, int numPositionsY)
{
_ptrPositionsX = numPositionsX;
_ptrPositionsY = numPositionsY;
_ptrPositionTopLeft = positionTopLeft;
_ptrPositionBottomRight = positionBottomRight;
_ptrPositionXDistance = Mathf.Abs((positionBottomRight.x - positionTopLeft.x) / numPositionsX);
_ptrPositionYDistance = Mathf.Abs((positionBottomRight.y - positionTopLeft.y) / numPositionsY);
}
public void setPosition (int x, int y)
{
_ptrPosX = x;
_ptrPosY = y;
_ptr.transform.position = new Vector2(((float)_ptrPosX * _ptrPositionXDistance) + _ptrPositionTopLeft.x, ((float)_ptrPosY * _ptrPositionYDistance) + _ptrPositionTopLeft.x);
}
// Update is called once per frame
void Update () {
//Is the pointer enabled?
if (_ptrEnabled)
{
if (_ptrInputDelay)
{
_ptrTimeSinceLastInput += Time.deltaTime;
if (_ptrTimeSinceLastInput >= _ptrTimePerInput)
{
_ptrInputDelay = false;
}
}
if (_ptrInputDelay == false)
{
_ptrXInput = (int)Input.GetAxis("Horizontal");
_ptrYInput = (int)Input.GetAxis("Vertical");
if (_ptrXInput != 0 || _ptrYInput != 0)
{
_ptrPosX += _ptrXInput;
_ptrPosY += _ptrYInput;
Debug.Log("WHEE");
if (_ptrPosX < 0) _ptrPosX = 0;
if (_ptrPosX > _ptrPositionsX) _ptrPosX = _ptrPositionsX;
if (_ptrPosY < 0) _ptrPosY = 0;
if (_ptrPosY > _ptrPositionsY) _ptrPosY = _ptrPositionsY;
_ptr.transform.position = new Vector2(((float)_ptrPosX * _ptrPositionXDistance) + _ptrPositionTopLeft.x, ((float)_ptrPosY * _ptrPositionYDistance) + _ptrPositionTopLeft.x );
_ptrInputDelay = true;
_ptrTimeSinceLastInput = 0f;
}
}
}
}
}
And the place where my Pointer class is being called is done like this:
GameObject newPointer = new GameObject();
newPointer.AddComponent<Pointer>();
Pointer newPointerScript = newPointer.GetComponent<Pointer>();
newPointerScript.setPositions(new Vector2(-1f, -1f), new Vector2(1f, 1f), 3, 3);
newPointerScript.setSprite(newWeapon);
newPointerScript.setPosition(1, 1);
These lines look wrong:
_ptrSpriteRenderer = new SpriteRenderer();
_ptr.AddComponent<SpriteRenderer>();
_ptrSpriteRenderer = _ptr.GetComponent<SpriteRenderer>();
You are creating a SpriteRenderer and then two lines later overwriting the value with what's in _ptr which is, in all probability, null.
Do you really need this line?
Also if you're adding a component shouldn't you be actually passing the component into the Add method?
So turns out everything works fine if I switch Start() with Awake(). That's all that was needed.
In my game there are various levels
I have made those level in separate scenes
Every Scene has a player at the starting point and when it crosses the exit point the next level (next scene) is loaded but the health and life are reset as starting point in the game and not remain what the player currently has when he exited the level
Exit level Script:-
using UnityEngine;
using System.Collections;
public class ExitLevel : MonoBehaviour {
public string scene;
private Player player;
// Use this for initialization
void Start () {
player = GameObject.Find ("Player").GetComponent<Player> ();
}
// Update is called once per frame
void Update () {
}
void OnTriggerEnter2D(Collider2D target){
if (target.gameObject.tag == "Player") {
if (player.ArtifactCount == 1) {
player.ArtifactCount = 0;
Destroy (target.gameObject);
Application.LoadLevel (scene);
}
}
}
}
LifeMeter Script:-
using UnityEngine;
using System.Collections;
public class LifeMeter : MonoBehaviour {
public float Life = 100;
public float maxLife = 100;
public float LifeBurnRate = 1f;
public Texture2D bgTexture;
public Texture2D LifeBarTexture;
public Texture2D HeartTexture;
public int iconWidth = 32;
public Vector2 LifeOffset = new Vector2(255, 10);
public int HeartCount = 3;
public Vector2 HeartOffset = new Vector2(455, 6.5f);
public int HeartDistance = 0;
public int HeartDistanceAddup = 40;
public int HeartDisplayWidth = 40;
public int HeartDisplayHeight = 40;
private Player player;
private ExitLevel exitLevel;
// Use this for initialization
void Start () {
player = GameObject.FindObjectOfType<Player> ();
}
public void OnGUI(){
var percent = Mathf.Clamp01 (Life / maxLife);
if (!player)
percent = 0;
if(Life == 0 && HeartCount !=0 ){
HeartCount = HeartCount - 1;
Life = 100;
}
HeartDistance = 0;
DrawMeter (LifeOffset.x, LifeOffset.y, LifeBarTexture, bgTexture, percent);
for (int i = 0; HeartCount > i; i++) {
HeartDistance = HeartDistance + HeartDistanceAddup;
GUI.DrawTexture (new Rect (HeartOffset.x + HeartDistance, HeartOffset.y, HeartDisplayWidth, HeartDisplayHeight), HeartTexture);
}
}
void DrawMeter(float x, float y, Texture2D texture, Texture2D background, float percent){
var bgW = background.width;
var bgH = background.height;
GUI.DrawTexture (new Rect (x, y, bgW, bgH), background);
var nW = ((bgW - iconWidth) * percent) + iconWidth;
GUI.BeginGroup (new Rect (x, y, nW, bgH));
GUI.DrawTexture (new Rect (0, 0, bgW, bgH), texture);
GUI.EndGroup ();
}
// Update is called once per frame
void Update () {
}
public void life(){
Life = Life - LifeBurnRate;
if (HeartCount == 0) {
{ Explode script = player.GetComponent<Explode> ();
script.OnExplode ();
}
}
}
}
you can use PlayerPrefs (Stores and accesses player preferences between game sessions)for save current status of Player.
Reffer this link http://docs.unity3d.com/ScriptReference/PlayerPrefs.html
There are two ways to proceed in the above case:
You can add DontDestroyOnload(this); to your player object. This
will not destroy that game object when the new level loads. And On
Every start of level you can spawn him your level start point. This
way your player's stats will remain consistent throughout the game.
You can use a static class to get the players health and other
required stats. Save the players health in the static class while
exiting the level and apply the values to the next levels player
object when the next level loads.