Unity make instantiated object toggle appear or disappear - c#

So I have a simple project where I make a game board out of multiple squares, and it has a grid that can be toggled on or off.
Unfortunately, it does not work that way. Here is my board manager code:
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
namespace BoardHandle {
[RequireComponent(typeof(SpriteRenderer))]
public class BoardManager : MonoBehaviour {
//2d list that represents the board
List<List<GameObject>> board = new List<List<GameObject>> ();
//Gamebjects import
public GameObject GrassTile;
public GameObject Grid;
//UI buttons import
public Toggle GridToggle;
//Miscellanious variables that could be edited in inspector
public int rows;
public int cols;
public float tileWidth;
public float tileHeight;
void Start() {
GrassTile.transform.localScale = new Vector3 (tileWidth, tileHeight, 0f);
Grid.transform.localScale = new Vector3 (tileWidth, tileHeight, 0f);
//Adds all of the stuff to the game board
for (int x = 0; x < cols; x++) {
board.Add (new List<GameObject> ());
for (int y = 0; y < rows; y++) {
board [x].Add (GrassTile);
}
}
//Makes board bits all go to the screen
for (int x = 0; x < board.Count; x++) {
for (int y = 0; y < board[x].Count; y++) {
//Makes the grid. And it stays there currently until the game ends.
Instantiate (Grid,
new Vector3 (x * tileWidth, y * tileHeight, 0),
Quaternion.identity);
//This is the actual board, filled with grass tiles. I wnat to keep this.
Instantiate (board [x] [y],
new Vector3 (x * tileWidth, y * tileHeight, 0),
Quaternion.identity);
}
}
}
void Update() {
}
}
}
My game programming experience is from Pygame. In Pygame, the background has to constantly regenerate over itself, so if I want to make the grid disappear, all I need to do is stop blitting it once per frame, and a frame later it is gone. In unity, since objects can move without having to regenerate the background in the code, the grid stays after just one instantiate. I just want to know a way that when GridToggle.isOn is true, the grid is instantiated and will stay there until GridToggle.isOn is false, when the grid will no longer be there until it is toggled on again. Thank you!

In Unity GameObjects can be deactivated and components can be disabled.
If a GameObject is deactivated it is considered as "not there". If you just want to disable rendering, you could alternatively just disable the renderer component of the tiles. Sounds like the first option is what you want.
Deactivating a GameObject also deactivates its child GameObjects. To disable the board and its contents, I would suggest having the instantiated objects as children of the board. You can do this through the transform of the GameObject:
instantiatedGameObject.transform.parent = this.transform;
Since you are using Toggle, you could add an event listener to it:
GridToggle.onValueChanged.AddListener((value) => this.SetActive(value));

Related

Can you spawn gameobjects and move them to an x position that is added by the last one

I am very new to Unity game development and was just curious if there was any way to spawn a gameobject in a position then the next game object in that position added like for is 1 then the next one will be 2 then 3 and so on.
From inspector you will need to assign the prefab and set the objectCount and spacing values. This will spawn the objects along the positive x axis starting at zero. If you want to move the objects along a different axis, change the x in position.x to one of the other vector3 components (y, z). To move along the axis in the opposite direction, invert the spacing value (eg. from 1 to -1).
using UnityEngine;
public class ObjectSpawner : MonoBehaviour
{
public GameObject prefab;
public int objectCount;
public float spacing;
void Start()
{
var position = new Vector3();
for (int i = 0; i < objectCount; i++)
{
Instantiate(prefab, position, Quaternion.identity);
position.x += spacing;
}
}
}

Unity Tilemaps, Single Tile Collision detection for highlighting Tiles/Cells, strange behaviour

Ive been working for a few days on this problem, trying to create a square grid of tiles, using Unity Tilemaps seems to be the most efficient way, and it has a lot built in already.
I want to highlight the tiles around the player so that he will see what the legal moves are.
I use a second Tilemap for this, on top of the basemap(ground/grass).
The second Tilemap, the highlightsMap is made up of invisible Tiles, which will turn into highlighted Tiles when a Physics.SphereOverlap occurs, or a Physics2D.CircleAll
To detect every Tile, I have added a box collider on an empty object and I made a grid of these colliders on top of the Tilemap grid, so that:
Box Collider at Position (2,4,0) is exactly on top of Tile at Position(2,4,0)
This should be the most straightforward way to handle this, as you can change a Tile, using Tilemap.SetTile(Vector3Int Pos, Tile tile)
The problem is very strange however. The colliders have the correct positional values to be able to reference the tiles exactly underneath them, just through that position data. As explained above, and I have double checked this, the collider empties have the exact same position as the tiles underneath them, no conversion is needed.
The problem is that the tiles are not highlighting around the player as expected, instead a few next to the player are highlighted and others arent, the Physics.OverlapSphere I am using only works on 3D Colliders, which is why I added them as another grid on top of everything.
Using Physics2D.CircleAll, unfortunately does not detect any 2D colliders on the tiles themselves(Sprite, or Grid), not sure if that is intended to work like that anyway.
Collision Grid Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
public class scr_ColliderGrid : MonoBehaviour
{
public GameObject eCollider;
public Tile invisibleTile;
public Tile highlightTile;
public Tilemap highlightMap;
public int xSize, ySize;
private Vector3Int[] gridArray; // Tilemaps use Vector3Int BUT only use (X, Y, 0) !!!!!!!
private int xC = 0, yC = 0, i = 0;
private Tile[] tiles;
private Vector3Int previous;
private void Start()
{
gridArray = new Vector3Int[xSize * ySize];
tiles = new Tile[xSize * ySize];
GenerateCollisionGrid();
}
private void GenerateCollisionGrid()
{
for (xC = 0; xC < xSize; xC++)
{
for (yC = 0; yC < ySize; yC++)
{
Vector3Int newPos = new Vector3Int(xC, yC, 0); // 2, 4, 0
Vector3Int newColPos = new Vector3Int(xC, yC, 0); // 2, 4, 0 //This used to be different values, but now they are exactly the same.
if (invisibleTile != null)
{
Tile tile = Instantiate(invisibleTile, newPos, Quaternion.identity, transform);
tiles[i] = tile;
GameObject col = Instantiate(eCollider, newColPos, Quaternion.identity, transform);
}
gridArray[i] = newPos;
i++;
}
}
highlightMap.SetTiles(gridArray, tiles);
highlightMap.SetTile(new Vector3Int(2,4,0), highlightTile); // 2,4,0 //Test to see if positions are the same. (collider and tiles)
}
Player Highlight Legal Moves Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
public class scr_TankMoves : MonoBehaviour
{
public Tile highlightTile;
public TileBase[] highlightTiles;
public Tilemap highlightMap;
public int maxMoveTiles;
public bool highlight;
private Vector3Int previous, previousLeft, previousRight, previousForward, previousAft, previousVect;
private Vector3Int[] previousVectors;
void Start()
{
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.W))
{
transform.localPosition = new Vector3(transform.localPosition.x,
transform.localPosition.y + 1,
transform.localPosition.z );
}
if (Input.GetKeyDown(KeyCode.S))
{
transform.localPosition = new Vector3(transform.localPosition.x,
transform.localPosition.y - 1,
transform.localPosition.z );
}
if (Input.GetKeyDown(KeyCode.D))
{
transform.localPosition = new Vector3(transform.localPosition.x + 1,
transform.localPosition.y,
transform.localPosition.z);
}
if (Input.GetKeyDown(KeyCode.A))
{
transform.localPosition = new Vector3(transform.localPosition.x - 1,
transform.localPosition.y,
transform.localPosition.z);
}
}
void LateUpdate()
{
if (highlight)
HighlightMoves();
else EraseHighlights();
}
private void HighlightMoves()
{
previousVectors = new Vector3Int[1];
highlightMap.SetTiles(previousVectors, null);
int tMax = 0;
Collider[] legalTiles = Physics.OverlapSphere(transform.position, maxMoveTiles/2);
previousVectors = new Vector3Int[legalTiles.Length];
foreach (Collider col in legalTiles)
{
//Vector3 conversionVector = new Vector3(col.transform.localPosition.x, col.transform.localPosition.y, col.transform.localPosition.z);
Vector3Int tileVector = Vector3Int.FloorToInt(col.transform.position);
previousVectors[tMax] = tileVector;
tMax++;
}
highlightMap.SetTiles(previousVectors, highlightTiles);
}
private void EraseHighlights()
{
//highlightMap.SetTile(previousForward, null);
//highlightMap.SetTile(previousAft, null);
//highlightMap.SetTile(previous, null);
//highlightMap.SetTile(previousRight, null);
//highlightMap.SetTile(previousLeft, null);
highlightMap.SetTiles(previousVectors, null);
}
private void OnDrawGizmos()
{
Gizmos.DrawWireSphere(transform.position, maxMoveTiles/2);
}
}
}
If you open a new 3D Project in Unity and setup a grid with a Tilemap, called highlightMap in my code example, you should be able to recreate this exactly, using the 2 scripts.
Everything is oriented in 2D, this means x+ is Right y+ is forward and z+ is up/dephth (unused by tilemaps).
The empty Collider prefab I have is an empty GO with pos 0,0,0. it has another empty GO Child, which has the Box Collider Component, and the transform value of this child is 0.5,0.5,0.5, so that the Collider is centered on top of each tile.
This is after 0 moves, so just pressed Play:
This is after 1 move forward, (y+1):
Instead of going by colliders at all afaik you could also just use GridLayout.WorldToCell like e.g.
Tilemap yourTileMap;
// see https://docs.unity3d.com/ScriptReference/Tilemaps.Tilemap-layoutGrid.html
var grid = yourTileMap.layoutGrid;
// The "Grid" implements "GridLayout"
// see https://docs.unity3d.com/ScriptReference/Grid.html
var currentCellPos = grid.WorldToCell(transform.position);
var currentCell = yourTileMap.GetTile(currentCellPos);
Then if you need to get multiple tiles I would probably go the "lazy/stupid" way and just do something like e.g.
var currentCellPos = grid.WorldToCell(transform.position);
var currentCell = yourTileMap.GetTile(currentCellPos);
var leftCellPos = grid.WorldToCell(transform.position - transform.right * someRange);
var leftCell = yourTileMap.GetTile(leftCellPos);
var rightCellPos = grid.WorldToCell(transform.position + transform.right * someRange);
var rightCell = yourTileMap.GetTile(rightCellPos);
var frontCellPos = grid.WorldToCell(transform.position + transform.forward * someRange);
var frontCell = yourTileMap.GetTile(frontCellPos);
var backCellPos = grid.WorldToCell(transform.position - transform.forward * someRange);
var backCell = yourTileMap.GetTile(backCellPos);
highlightTiles[tMax] = Instantiate(highlightTile, col.transform.parent.position, Quaternion.identity, transform);
I dont know why, but apparently I didnt instantiate the tiles themselves into an Array of tiles, so everytime I was ''placing'' them on the grid, it was actually placing an empty array.
Now its fixed! Just need to find a nice way to erase all after a move and im set!

OverlapSphere centering incorrectly

I am attempting to use an overlapSphere to determine whether a tile within a tile map contains a game object. Then, if the tile is not already occupied, a new game object will be spawned in that tile (if certain other conditions are met). I'm trying to prevent game objects from overlapping with one another. However, I am having trouble centering the spheres about the right location.
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
Vector3 tilePos = tileMap.GetCellCenterWorld(new Vector3Int(x, y, 0));
if (terrainMap[x, y] == blocked)
{
bool spawn = detectOverlap(tilePos, obstCheckRad);
if (spawn)
{
GameObject obstacle = Instantiate(obstaclePrefab) as GameObject;
obstacle.transform.position = tilePos;
obstacle.transform.eulerAngles = new Vector3(0, 0, (Random.Range(0, 359)));
}
}
}
}
}
public bool detectOverlap (Vector3 center, float radius)
{
Collider[] collider = Physics.OverlapSphere(center, radius);
if (collider.Length == 0)
{
return true;
}
else
{
return false;
}
}
I am using "GetCellCenterWorld" to as the center of my overlapSpheres, however they always seem to originate in the bottom left corner of the map, causing this region of the map to be completely empty, and objects elswhere in the map to still overlap with one another. (see image)
My confusion is based around the fact that I am using the same vector3 (tilePos) to place down the game objects. The objects are all moved to the correct position, however, the overlapSpheres all stay at the bottom left.

Unity random object generation always returning same object

In the vertical game, I am creating random blocks spawn within a 100f. I am randomly creating 3 different types of objects which are 'platforms' 'boostplatforms' and 'breakableplatforms'. My code for generating all of these give no errors and when I run the game the blocks generated when I check the activity however visually only the objects named 'platform' appear.
The coding I used for this was:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LevelGenerator : MonoBehaviour {
public GameObject platformPrefab;
public GameObject platformPrefab2;
public GameObject platformPrefab3;
public int numberOfPlatforms = 999;
public int numberOfBoostPlatforms = 999;
public int numberOfBreakablePlatforms = 999;
public float levelWidth = 100f;
public float minY = 55f;
public float maxY = 120f;
// Use this for initialization
void Start () {
Vector3 startPosition = new Vector3 ();
for (int i = 0; i < numberOfPlatforms; i++)
{
startPosition.y += Random.Range (minY, maxY);
startPosition.x = Random.Range (-levelWidth, levelWidth);
Instantiate (platformPrefab, startPosition, Quaternion.identity);
}
for (int i = 0; i < numberOfBoostPlatforms; i++)
{
startPosition.y += Random.Range (minY, maxY);
startPosition.x = Random.Range (-levelWidth, levelWidth);
Instantiate (platformPrefab2, startPosition, Quaternion.identity);
}
for (int i = 0; i < numberOfBreakablePlatforms; i++)
{
startPosition.y += Random.Range (minY, maxY);
startPosition.x = Random.Range (-levelWidth, levelWidth);
Instantiate (platformPrefab3, startPosition, Quaternion.identity);
}
}
// Update is called once per frame
void Update () {
}
}
Based on the information provided, I would recommend the following:
Verify that your prefab variables are set to the correct prefabs.
Notice that each platform's Y position is cumulative. Verify that the boost and break platforms aren't simply spawning after all of the standard platforms (which they will be based on your code).
Switch to object pooling. You are spawning 3,000 objects in a single frame even though the majority won't even be visible. The idea would be to spawn a small pool, 10 of each let's say, and when a new platform is needed (coming into view) you recycle one of the existing objects that is no longer visible or needed. Unity's Object Pooling tutorial

Spawning zombies just outside of camera bounds in Unity

Usually in any new engine I try to make a top down zombie shooter using simple graphics (usually squares/rectangles) and that's what I'm currently trying to do in Unity.
I've got to the point where I have:
A player that shoots (and is controlled via WASD/arrow keys and
mouse)
Zombies that spawn and go towards the player
Zombies that can be killed (and once all zombies are dead, another
wave spawns)
But, currently, it seems that the way I spawn them spawns them way too far away from the player. I use an orthographic camera.
Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ZombieSpawner : MonoBehaviour {
private int waveNumber = 0;
public int enemiesAmount = 0;
public GameObject zombie;
public Camera cam;
// Use this for initialization
void Start () {
cam = Camera.main;
enemiesAmount = 0;
}
// Update is called once per frame
void Update () {
float height = 2f * cam.orthographicSize;
float width = height * cam.aspect;
if (enemiesAmount==0) {
waveNumber++;
for (int i = 0; i < waveNumber; i++) {
Instantiate(zombie, new Vector3(cam.transform.position.x + Random.Range(-width, width),3,cam.transform.position.z+height+Random.Range(10,30)),Quaternion.identity);
enemiesAmount++;
}
}
}
}
If You want them to spawn zombies just outside camera view don't multiply orthographic size.
float height = cam.orthographicSize; // now zombies spawn on camera view border
float height = cam.orthographicSize + 1 // now they spawn just outside
It'a a small change, but You could also set width as:
float width = cam.orthographicSize * cam.aspect + 1;
Try to spawn another wave when there is one zombie left and see how game pacing has changed ;)

Categories