How to make enemies not spawn on/too close to player. C# - c#

I'm making a "player fish eats smaller fish and grows" game as practice. But I'm struggling with making the npc fish not spawn on or too close to the player.
This is the code for the spawner, atm fish can still spawn on the player.
public class Spawner : MonoBehaviour
{
public GameObject[] itemsToPickFrom;
public int numberToSpawn;
float placeX;
float placeY;
float minDistance;
void Start()
{
Spawn();
minDistance = 5f;
}
void Spawn()
{
for (int i = 0; i < numberToSpawn; i++)
{
int randomIndex = Random.Range(0, itemsToPickFrom.Length);
placeX = Random.Range(-10f, 9f);
placeY = Random.Range(-4f, 4f);
while (Vector2.Distance(GameObject.FindGameObjectWithTag("Player").transform.position, new Vector3(placeX, placeY, 0)) < minDistance)
{
placeX = Random.Range(-10f, 9f);
placeY = Random.Range(-4f, 4f);
}
Instantiate(itemsToPickFrom[randomIndex], new Vector3(placeX, placeY, 0), quaternion.identity);
}
}

I guess it's Unity, if so numberToSpawn is probably set on the editor interface, so it shouldn't be a problem.
The issue I can see is that you set minDistance after Spawn().
void Start()
{
Spawn();
minDistance = 5f;
}
If you set minDistance before Spawn(), it could be correct. The other logics seems correct at least.
float minDistance = 5f;
void Start()
{
Spawn();
}

Related

Enemies don't target the spawned Player

I'm building a team game (dungeon crawler) and I was faced with a problem.
If we put the player prefab directly in the sceane the enemies treat the player correctly (follow, attack, etc). The problem is: my peer created a player spawn pad that instantiate the exacly same prefab but the enemies ignore completly the player.
Can someone help me?
Here's the enemy script:
public bool isDead = false;
public float speed = 4f;
public int hp = 0;
public int dmg = 0;
public int def = 0;
public int amtSouls = 0;
//Item drop
public GameObject drop;
//follow player
public GameObject Player;
private NavMeshAgent Enemy;
public float enemyDistanceRun = 5.0f; //radius to spot the player
//--
Rigidbody rb;
Scene currentScene;
PlayerHealthScript phs;
public Type type; //defines the type (common, elite, boss)
public enum Type
{
Common,
Elite,
Boss
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == "Player")
{
//not working
hp -= phs.dmg;
}
}
void death()
{
isDead = true;
Instantiate(drop, transform.position, drop.transform.rotation);
//---- get Destroy script reference
GameObject ds = GameObject.Find("Soul");
Destroy des = ds.GetComponent<Destroy>();
//----
Destroy(gameObject);
//send the amount of souls to the Destroy script
des.sls += amtSouls;
}
void Start()
{
rb = this.GetComponent<Rigidbody>();
Enemy = GetComponent<NavMeshAgent>();
phs = GameObject.Find("Player Character").GetComponent<PlayerHealthScript>();
currentScene = SceneManager.GetActiveScene();
// gate.SetActive(false);
if (CompareTag(Type.Common.ToString()))
{
hp = 60;
dmg = 5;
def = 5;
amtSouls = 10;
// gate.SetActive(true);
}
else
if(CompareTag(Type.Elite.ToString()))
{
hp = 100;
dmg = 15;
def = 10;
amtSouls = 25;
}
else
if(CompareTag(Type.Boss.ToString()))
{
hp = 200;
dmg = 25;
def = 20;
amtSouls = 50;
}
// Retrieve the index of the scene in the project's build settings.
int buildIndex = currentScene.buildIndex;
// Check the scene and multiply the values
switch (buildIndex)
{
case 0:
//Do nothing because values already setted
break;
case 1:
hp = hp * 2;
dmg = dmg * 2;
def = def * 2;
amtSouls = amtSouls + 2;
break;
}
}
void Update()
{
float distance = Vector3.Distance(transform.position, Player.transform.position);
//chase player
if(distance < enemyDistanceRun)
{
Vector3 dirToPlayer = transform.position - Player.transform.position;
Vector3 newPos = transform.position - dirToPlayer;
Enemy.SetDestination(newPos);
}
if (hp <= 0)
{
death();
}
Player Spawn Pad Script:
public GameObject PlayerPrefab;
public GameObject MainCam;
public GameObject FreelookCam;
public GameObject PlayerHUD;
// Start is called before the first frame update
void Start()
{
GameObject Player = Instantiate(PlayerPrefab, transform.localPosition, transform.localRotation);
Player.name = PlayerPrefab.name;
GameObject MainCam1 = Instantiate(MainCam, transform.localPosition, transform.localRotation);
MainCam1.name = MainCam.name;
GameObject FreeCam = Instantiate(FreelookCam, transform.localPosition, transform.localRotation);
FreeCam.name = FreelookCam.name;
GameObject HUD = Instantiate(PlayerHUD, transform.localPosition, transform.localRotation);
HUD.name = PlayerHUD.name;
GameObject.Destroy(gameObject);
}
// Update is called once per frame
void Update()
{
}
As it was said in a comment, your enemy never checks if a new Player is spawned.
"The best solution" depends on how often you are going to spawn and destroy that player GameObject.
For example, if you're only going to spawn the player at the beginning of the scene, then you could alter the scripts' execution order so that the player is spawned before the enemy tries to look for it.
[DefaultExecutionOrder(-50)]
class PlayerSpawnPad : MonoBehaviour { /* ... */ }
And in the enemy class you get the reference in one of these three ways:
player = GameObject.Find("Player Character");
// Not really recommended. If you decide to change the game object's name
// later in the project, you'd have to change this too.
player = FindObjectWithTag("Player");
// Better, but although tags are less likely to be changed, it's still
// something more to think about
player = FindObjectOfType<Player>();
// If you ever decide to change the player script's name, the compiler will
// tell you immediately
If instead you plan on spawning the player after the enemy, you could use an event
using System;
class Player : MonoBehaviour {
public static event Action<Player> onPlayerSpawned;
private void Start() {
onPlayerSpawned(this);
}
/* ... */
}
class Enemy : MonoBehaviour {
private GameObject player;
private void Start() {
Player.onPlayerSpawned += RegisterPlayer;
}
private void RegisterPlayer(Player player) {
this.player = player.gameObject;
}
/* ... */
}
I found out the solution.
{
Vector3 dirToPlayer = transform.position - Player.transform.position;///<-
Vector3 newPos = transform.position - dirToPlayer;
Enemy.SetDestination(newPos);
}
if (distance < enemyDistanceRun)
{
Vector3 dirToPlayer = transform.position - phs.transform.position;//<-
Vector3 newPos = transform.position - dirToPlayer;
Enemy.SetDestination(newPos);
Debug.Log("Chacing.");
}
The enemy was following the Player gameobject that, for some reason didn't recognize so I put it to follow the player health script holder and works perfectly.

Spawning and destroying random platforms in unity

I am trying to make a doodle jump game in unity for fun and I have a question.
How could I change my script to spawn other platforms on top and destroy any platform that is under main camera.I'll let down my code for spawning random platforms
public class GradinariuZAHAR : MonoBehaviour
{
public Transform Player;
public GameObject platformPrefab;
private float minimY = 0.2f;
private float maximY = 1.6f;
public int numberOfPlatforms = 200;
public float levelWidth = 3f;
// Use this for initialization
void Start()
{
GenerateChunk(.2f, 1.5f);
}
void GenerateChunk(float minY, float maxY)
{
Vector3 spawnPosition = new Vector3(0f, 0f, 0f);
for (int i = 0; i < numberOfPlatforms; i++)
{
spawnPosition.y += Random.Range(minY, maxY);
spawnPosition.x = Random.Range(-levelWidth, levelWidth);
Instantiate(platformPrefab, spawnPosition, Quaternion.identity);
}
}
void Update()
{
if (Player.position.y > 160) {
Debug.Log("acum");
minimY = maximY;
maximY = maximY * 2;
GenerateChunk(minimY, maximY);
}
}
}
Just attach the following script to your platform. It'll destroy the platform once its entire sprite is completely below the camera, exactly how you want it.
using UnityEngine;
public class Platform : MonoBehaviour
{
SpriteRenderer sprite;
float bottomOfScreen;
private void Start()
{
sprite = GetComponent<SpriteRenderer>();
var cam = Camera.main;
var screen = new Vector2(Screen.width, Screen.height);
var camWorldPos = cam.ScreenToWorldPoint(screen);
bottomOfScreen = camWorldPos.y - cam.orthographicSize * 2;
}
private void Update()
{
var height = sprite.bounds.size.y;
var topOfPlatform = transform.position.y + height / 2;
if (topOfPlatform < bottomOfScreen)
Destroy(gameObject);
}
}

Spawning game object unity (C#)

I writing simple runner game.
I have game object (quad).
I want it to generate.
I wrote spawnscript:
using UnityEngine;
using System.Collections;
public class SpawnScript : MonoBehaviour {
public GameObject[] obj;
//public GameObject obj;
public float spawnMin = 1f;
public float spawnMax = 2f;
// Use this for initialization
void Start () {
Spawn();
}
void Spawn()
{
//for (int i = 0; i < 10; i++)
// Instantiate(obj, new Vector3(i * 2.0f, 0, 0), Quaternion.identity);
Instantiate(obj[Random.Range(0, obj.GetLength(0))], transform.position, Quaternion.identity);
Invoke("Spawn", Random.Range(spawnMin, spawnMax));
}
}
But my quad spawning one time.I need it to spawn multiple times.
Where is my problem?
Use InvokeRepeating instead of Invoke in start event:
// Invokes the method methodName in time seconds, then repeatedly every repeatRate seconds.
InvokeRepeating("Spawn", 3, 3);
If you do InvokeRepeating("Function", 1.0f, 1.0f), it will call Function one second after the InvokeRepeating call and then every one second thereafter.
Hence, you can control the spawn timings.
Update
As asked in comments:
You can also cancel InvokeRepeating any time by calling below code. More information here.
CancelInvoke("Spawn");
There is no problem with code. All GameObjects instantiated same position so you can use code below:
public float spawnDistanceFactor = 2f; //Value is your choice. You can change it.
void Spawn()
{
float startPosX = 0f; // I assume your camera look at 0,0,0 point.
for (int i = 0; i < 10; i++){
Vector3 spawnPos = new Vector3(startPosX,0f,0f);
Instantiate(obj, spawnPos, Quaternion.identity);
startPosX+=spawnDistanceFactor;
}
}
It is only moves positions on X axis you can move it x,y,z.
Also
You can use Random function for moving spawn position
public float randomMin = 2f;
public float randomMax = 4f;
void Spawn()
{
float startPosX = 0f; // I assume your camera look at 0,0,0 point.
for (int i = 0; i < 10; i++){
Vector3 spawnPos = new Vector3(startPosX,0f,0f);
Instantiate(obj, spawnPos, Quaternion.identity);
float randomX = Random.Range(randomMin,randomMax);
startPosX+=randomX;
}
}
You can do a lot of things.
For destroying prefabs you can add Destroy script to object (You should create prefab with script) Like:
void DestroyObject(){
Destroy(this.gameObject);
}
or use list to hold pointers to the GameObjects. Like:
using System.Collections.Generic; // For list
private List<GameObject> objectList;
public float randomMin = 2f;
public float randomMax = 4f;
void Start(){
objectList = new List<GameObject>();
Spawn();
}
void Spawn()
{
objectList.Clear(); ///For multiple spawn purpose dont dublicate items.
float startPosX = 0f; // I assume your camera look at 0,0,0 point.
for (int i = 0; i < 10; i++){
Vector3 spawnPos = new Vector3(startPosX,0f,0f);
GameObject newObject = Instantiate(obj, spawnPos, Quaternion.identity) as GameObject;
objectList.Add(newObject);
float randomX = Random.Range(randomMin,randomMax);
startPosX+=randomX;
}
}
void DestroyObject(){
for(int i=0;i<objectList.Count;i++){
Destroy(objectList[i]);
}
}

How to spawn enemy at random intervals?

I am using EnemyScript to move the enemy towards the player and killing the player, but I'm unable to spawn it randomly in code. I am currently spawning it directly through screen by placing the prefab on the scene.
Here is my EnemyScript
using UnityEngine;
using System.Collections;
public class EnemyScript : MonoBehaviour {
public Transform target;
public float speed = 2f;
void Update ()
{
transform.position = Vector2.MoveTowards(transform.position, target.position, speed * Time.deltaTime);
}
void OnTriggerEnter2D(Collider2D otherCollider)
{
PlayerControl shot = otherCollider.gameObject.GetComponent<PlayerControl>();
if (shot != null)
{
Destroy(shot.gameObject);
}
}
}
you could use something similar to this:
public GameObject myObj;
void Start ()
{
enemy = GameObject.Find("enemy");
InvokeRepeating("SpawnEnemy", 1.6F, 1F);
}
public void SpawnEnemy()
{
Vector3 position = new Vector3(Random.Range(35.0F, 40.0F), Random.Range(-4F, 2F), 0);
Instantiate(myObj, position, Quaternion.identity);
}
in the InvokeRepeating call you could possibly add the random range there also instead of a timed instantiate. This example is just a snippet of some prototype code i did a while ago, it may not suit your needs directly but hopefully will give you a general idea on what to do.
EDIT: to make sense, put this into a blank object somewhere in your scene, dont attach this to the actual enemy.
i programmed it like this.
public GameObject enemyPrefab;
public float numEnemies;
public float xMin = 20F;
public float xMax = 85F;
public float yMin = 3.5F;
public float yMax = -4.5F;
void Start () {
for (int i=0; i< numEnemies; i++) {
Vector3 newPos = new Vector3(Random.Range(xMin,xMax),Random.Range(yMin,yMax),0);
GameObject enemy = Instantiate(enemyPrefab,newPos,Quaternion.identity) as GameObject;
}
}
This will spawn a random number of enemies, at random locations, after random periods of time, all adjustable in the Inspector.
public float minTime = 1;
public float maxTime = 3;
public int minSpawn = 1;
public int maxSpawn = 4;
public Bounds spawnArea; //set these bounds in the inspector
public GameObject enemyPrefab;
private spawnTimer = 0;
Vector3 randomWithinBounds(Bounds r) {
return new Vector3(
Random.Range(r.min.x, r.max.x),
Random.Range(r.min.y, r.max.y),
Random.Range(r.min.z, r.max.z));
}
void Update() {
spawnTimer -= Time.deltaTime;
if(spawnTimer <= 0) {
spawnTimer += Random.Range(minTime, maxTime);
int randomSpawnCount = Random.Range(minSpawn, maxSpawn);
for(int i = 0; i < randomSpawnCount; i++) {
Instantiate(transform.transformPoint(enemyPrefab), randomWithinBounds(spawnArea), Quaternion.identity);
}
}
}
//bonus: this will show you the spawn area in the editor
void OnDrawGizmos() {
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.color = Color.yellow;
Gizmos.drawWireCube(spawnArea.center, spawnArea.size);
}
People on here seem to like using coroutines for time delays, but I personally prefer to track my own timers in Update() to maximize control and predictability.

Enemy Random Spawn

I am making my first Unity game and I decided to make a 5 level shooter for mobile. I have made a random spawn script for my enemies, but the spawn is too frequent and generates too many at once.
Here is the script:
using UnityEngine;
using System.Collections;
public class CreateRandomOctodeamons : MonoBehaviour {
public GameObject enemyPrefab;
public GameObject enemyPrefab1;
public float numEnemies;
public float xMin = 19F;
public float xMax = 85F;
public float yMin = 3.5F;
public float yMax = -4.5F;
void Start () {
GameObject newParent = GameObject.Find("1-background elements");
for (int i = 0; i < numEnemies; i++)
{
Vector3 newPos = new Vector3(Random.Range(xMin, xMax), Random.Range(yMin, yMax), 0);
GameObject octo = Instantiate(enemyPrefab, newPos, Quaternion.identity) as GameObject;
octo.transform.parent = newParent.transform;
Vector3 newPos1 = new Vector3(Random.Range(xMin, xMax), Random.Range(yMin, yMax), 0);
GameObject octo1 = Instantiate(enemyPrefab1, newPos, Quaternion.identity) as GameObject;
octo1.transform.parent = newParent.transform;
}
}
Please look over it and let me know if I should change anything or add anything. I am also looking for a generic health script where I could just add my play prefab to allow his health to show above his head.
This sounds like 3 or more questions in one. I'll try to answer the question about "too many."
Try something like this:
void Start () {
if (numEnemies < YourThreshholdForNumberOfEnemies)
{
GameObject newParent = GameObject.Find("1-background elements");
for (int i = 0; i < numEnemies; i++)
{
Vector3 newPos = new Vector3(Random.Range(xMin, xMax), Random.Range(yMin, yMax), 0);
GameObject octo = Instantiate(enemyPrefab, newPos, Quaternion.identity) as GameObject;
octo.transform.parent = newParent.transform;
Vector3 newPos1 = new Vector3(Random.Range(xMin, xMax), Random.Range(yMin, yMax), 0);
GameObject octo1 = Instantiate(enemyPrefab1, newPos, Quaternion.identity) as GameObject;
octo1.transform.parent = newParent.transform;
}
}
}
well if you want you can use this
public GameObject[] obj; // for your enemies, using array so you can put many kind of enemy prefabs
public float spawnMin = 1f; //your minimum time enemies spawn
public float spawnMax = 2f; //your maximum time enemies spawn
void Start ()
{
spawn();
}
void spawn()
{
Instantiate(obj[Random.Range(0, obj.GetLength(0))], //instantiate random prefabs
new Vector3(transform.position.x, transform.position.y, z),
Quaternion.identity)
//this is the one that control how many times spawn called
Invoke("Spawn", Random.Range(spawnMin, spawnMax));
}

Categories

Resources