Spawning zombies just outside of camera bounds in Unity - c#

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 ;)

Related

I am making a space shooter but my bullets keep on going up instead of out and not deleting from the Unity Hierarchy

I am trying to practice making a space shooter in order to build on concepts for my main project, and I am having issues because I have gotten where the bullets fire and follow with the ship but they fire up and never delete from the hierarchy in Unity.
Here is the code for the Controller:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
private Transform myTransform;
public int playerSpeed = 5;
// Start is called before the first frame update
// Reusable Prefab
public GameObject BulletPrefab;
void Start()
{
myTransform = transform;
// Spawning Point
// Position x = -3, y = -3, z = -1
myTransform.position = new Vector3(-3, -3, -1);
}
// Update is called once per frame
void Update()
{
// Movement (left / right) and speed
myTransform.Translate(Vector3.right * playerSpeed * Input.GetAxis("Horizontal") * Time.deltaTime);
// Make the player wrap. If the player is at x = -10
// then it should appear at x = 10 etc.
// If it is positioned at 10, then it wraps to -10
if (myTransform.position.x > 10)
{
myTransform.position = new Vector3(-10, myTransform.position.y, myTransform.position.z);
}
else if (myTransform.position.x < -10)
{
myTransform.position = new Vector3(10, myTransform.position.y, myTransform.position.z);
}
// Space to fire!
// If the player presses space, the ship shoots.
if (Input.GetKeyDown(KeyCode.Space))
{
// Set position of the prefab
Vector3 position = new Vector3(myTransform.position.x, myTransform.position.y - 2, myTransform.position.z);
// Fire!
Instantiate(BulletPrefab, position, Quaternion.identity);
}
}
}
and here is the one for the Bullet prefab:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : MonoBehaviour
{
public int mySpeed = 50;
private Transform myTransform;
// Start is called before the first frame update
void Start()
{
myTransform = transform;
}
// Update is called once per frame
void Update()
{
// Shooting animation
myTransform.Translate(Vector3.up * mySpeed * Time.deltaTime);
if (myTransform.position.z > 20)
{
DestroyObject(gameObject);
}
}
}
Not sure what I am doing. The bullet has gravity unchecked & is kinematic checked.
Player is positioned at -3, -1, -3.
Why your object isn't being deleted is probably because you are checking its position along the z-axis when you are only moving it along the y-axis. The destroy never runs since your bullet's z position is static and always under 20:
// Shooting animation
myTransform.Translate(Vector3.up * mySpeed * Time.deltaTime);
if (myTransform.position.z > 20)
{
DestroyObject(gameObject);
}
Either you can change the if statement to check along the y-axis or move the bullet along the z-axis, which one you want I cant really say since I don't really understand which direction you want the bullet to travel in.
Also, I don't know if it would cause any problems but DestroyObject has been replaced by Destroy().

How do I detect if a sprite is overlapping any background sprite?

I want to detect if an empty gameobject is between a sprite which I've drawn and the camera.
Background objects are tagged "Background" and are in the layer "background".
Currently, the Debug.log statement always says "Does not detect background". I've tried switching the direction of the raycast from back to forward several times and that didn't fix it. The gameobject shooting the ray has a z position of -1, the sprite has a z position of 0. The sprite has a 2D box collider on it.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RoomMaker : MonoBehaviour
{
public float speed = 5;
public float distance = 5;
public Transform horse;
public Transform carriage;
void Update()
{
transform.Translate(Vector2.right * speed * Time.deltaTime);
RaycastHit2D backgroundInfo = Physics2D.Raycast(horse.position, Vector3.forward, distance);
if (backgroundInfo)
{
Debug.Log("DETECTSBACKGROUND");
}
else
{
Debug.Log("Does not detect Background");
//it always displays this one, it never displays the other debug.log
}
}
}
Consider using Physics2D.OverlapPointAll instead of a raycast.
This will require that background sprites have Collider2D components on their gameobjects, their gameobjects be tagged Background and be on the background layer.
I advise having the background layer ignore collisions with everything. See the physics 2d options documentation for more information on how to do that.
Here's what the code could look like:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RoomMaker : MonoBehaviour
{
public float speed = 5;
public float distance = 5;
public Transform horse;
public Transform carriage;
void FixedUpdate() // otherwise, visual lag can make for inconsistent collision checking.
{
transform.Translate(Vector2.right * speed * Time.deltaTime);
Collider2D[] cols = Physics2D.OverlapPointAll(
Vector3.Scale(new Vector3(1,1,0), horse.position), // background at z=0
LayerMask.GetMask("background")); // ignore non background layer objects
Collider2D backgroundCol = null;
for (int i = 0 ; i < cols.Length ; i++) // order of increasing z value
{
if (cols[i].tag == "Background") // may be redundant with layer mask
{
backgroundCol = cols[i];
break;
}
}
if (backgroundCol != null)
{
Debug.Log("DETECTSBACKGROUND: " + backgroundCol.name);
}
else
{
Debug.Log("Does not detect Background");
}
}
}
I tested with this setup:

how to get camera width in unity

Problem
I have a spawn manager written in c# which spawns my game object, i use screen.width, to set the maximum screen with and -screen.width to set the minimum screen width for the spawning, but my game object spawns way off the screen.
I am using a portrait camera 2:3 instead of free aspect as my camera view, as i want my game to be in portrait mode
how do i make my game object spawn within the camera widths(max and min)?
my code
public class SpawnManager : MonoBehaviour {
public int maxBalloons = 100;
public GameObject balloon;
public float horizontalMin = -Screen.width;
public float horizontalMax = Screen.width;
public float verticalMin = -5.0f;
public float verticalMax = 1.0f;
private Vector2 originPosition;
void Start () {
originPosition = transform.position;
Spawn ();
}
void Spawn()
{
for (int i = 0; i < maxBalloons; i++)
{
Vector2 randomPosition = originPosition + new Vector2 (Random.Range(horizontalMin, horizontalMax), Random.Range (verticalMin, verticalMax));
Instantiate(balloon, randomPosition, Quaternion.identity);
originPosition = randomPosition;
}
}
}
Edit
I changed
Vector2 randomPosition = originPosition + new Vector2 (Random.Range(horizontalMin, horizontalMax), Random.Range (verticalMin, verticalMax));
To
Vector2 randomPosition =Camera.ScreenToWorldPoint (originPosition + new Vector2 (Random.Range(horizontalMin, horizontalMax), Random.Range (verticalMin, verticalMax)));
Still did not work as it should
A position on your screen with a X smaller than 0 will be always out of your screen. The bottom left corner of your screen is the origin of your the coordinate (x: 0, y: 0)
But here I think you mingled 2 kinds of positions. The position on your screen, is not the same position in the world space of your scene. It depends where the camera is rendering.
The position on the bottom left corner of your screen will not be x:0 y:0 in the world space.
To achieve what you are looking for you need to transform the position on the left and the right of your camera into worldspace positions.
Use Camera.ScreenToWorldPoint to achieve that : https://docs.unity3d.com/ScriptReference/Camera.ScreenToWorldPoint.html

How can i spawn random objects that are not overlap between specific given area?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WallsTest : MonoBehaviour
{
// using a GameObject rather than a transform
public GameObject prefab;
public Vector3 wallsStartPosition;
public float width = 0;
public float height = 1;
public float length = 2;
public Camera wallsCamera;
public float wallsArea;
void Start()
{
wallsCamera.transform.position = new Vector3(wallsStartPosition.x, wallsStartPosition.y + 100, wallsStartPosition.z - 235);
BuildWalls();
}
private void Update()
{
}
void BuildWalls()
{
for (int i = -2; i < 2; i++)
{
GameObject go = Instantiate(prefab);
go.transform.parent = transform;
Vector3 scale = Vector3.one;
Vector3 adjustedPosition = wallsStartPosition;
float sign = Mathf.Sign(i);
if ((i * sign) % 2 == 0)
{
adjustedPosition.x += (length * sign) / 2;
scale.x = width;
scale.y = height;
scale.z *= length + width;
}
else
{
adjustedPosition.z += (length * sign) / 2;
scale.x *= length + width;
scale.y = height;
scale.z = width;
}
adjustedPosition.y += height / 2;
go.transform.localScale = scale;
go.transform.localPosition = adjustedPosition;
}
}
}
For example the length is 100 so the area will be 100x100 i think.
And i have the wallsStartPosition for example 250,0,250
Now i want inside the walls area to instantiate at random position number of objects. For example 50 cubes. But they should not be overlap each other and for example the minimum gap between each other should be 5. and maximum gap as fas it can be.
But i don't understand yet how to calculate the area and position of the walls and just instantiate random objects inside.
And this is the script for spawning gameobjects at random positions in given area. In this case the area is the terrain. But i want the area to be inside the walls i create in the first script. This script is attached to the same empty gameobject like the WallsTest script.
Another problem with the SpawnObjects script is that there is no any set for the gap between the objects and if the objects too good like scale 20,20,20 some of the objects that spawn on the edge half out of the terrain.
spawn
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnObjects : MonoBehaviour
{
public Terrain terrain;
public int numberOfObjects; // number of objects to place
private int currentObjects; // number of placed objects
public GameObject objectToPlace; // GameObject to place
private int terrainWidth; // terrain size (x)
private int terrainLength; // terrain size (z)
private int terrainPosX; // terrain position x
private int terrainPosZ; // terrain position z
void Start()
{
// terrain size x
terrainWidth = (int)terrain.terrainData.size.x;
// terrain size z
terrainLength = (int)terrain.terrainData.size.z;
// terrain x position
terrainPosX = (int)terrain.transform.position.x;
// terrain z position
terrainPosZ = (int)terrain.transform.position.z;
}
// Update is called once per frame
void Update()
{
// generate objects
if (currentObjects <= numberOfObjects)
{
// generate random x position
int posx = Random.Range(terrainPosX, terrainPosX + terrainWidth);
// generate random z position
int posz = Random.Range(terrainPosZ, terrainPosZ + terrainLength);
// get the terrain height at the random position
float posy = Terrain.activeTerrain.SampleHeight(new Vector3(posx, 0, posz));
// create new gameObject on random position
GameObject newObject = (GameObject)Instantiate(objectToPlace, new Vector3(posx, posy, posz), Quaternion.identity);
newObject.transform.localScale = new Vector3(20, 20, 20);
currentObjects += 1;
}
if (currentObjects == numberOfObjects)
{
Debug.Log("Generate objects complete!");
}
}
}
My technical knowledge is limited, but you'll have to either get the script to spawn these objects every [X] amount of position ("posX += 1.0f;", or something to that effect) so that they appear in a uniform manner, or, have the script record each new object's position and use that information to calculate space away from said objects to spawn another one.
In any case, depending on the end result you're aiming for, you'll have to write how these objects operate in a given space. For example, if you have a messy room as a scene, you can have one desk that would find space in the corner of two walls, one bed alongside one wall and rubbish spawning randomly.
You can use Physics.OverlapSphere to test if two objects are overlapping. This assumes that the objects both have colliders.
Once you test this condition and it evaluates to true, I would suggest simply moving the object that overlaps and testing it again against the other objects in the scene. You can get clever with this by only testing objects you think might be close to overlapping, or overlapping.
Alternatively you can just compare every object to every other object in the scene. This would not be ideal if you have a lot of objects, because doing so would be O(n^2) complexity.
In any case you can use this function to test overlapping. Good luck.
hint: if you make the collider bigger than the object, you can place items a minimum space apart. For example if you have a cube of localSize 1,1,1 - the colider can be 5,5,5 in size and Physics.OverlapSphere will return true if the colliders overlap, demanding that the cubes be a certain space apart from each other

Unity make instantiated object toggle appear or disappear

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));

Categories