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.
Related
Hey Guys I've been having a problem lately that I cant seem to solve.
A sprite is supposed to roam around (as it does) while nothing is inside its radius, however if the player moves close to it the sprite should theoretically move towards it and stop roaming.
The sprite doesn't follow the player and cant even see its tag since I cant even see the contents of the "Collider2D[] hits".
using System.Collections.Generic;
using UnityEngine;
public class FireCultist : MonoBehaviour
{
public float moveTimeSeconds; //Time it will take object to move, in seconds.
private float xMax = 10.0f; // The boundaries of the spawn area
private float yMax = 10.0f;
private float xMin = -10.0f; // The boundaries of the spawn area
private float yMin = -10.0f;
public int xDistanceRange; // The max distance you can move at one time
public int yDistanceRange;
private BoxCollider2D boxCollider; //The BoxCollider2D component attached to this object.
private Rigidbody2D rb2D; //The Rigidbody2D component attached to this object.
private float inverseMoveTime; //Used to make movement more efficient.
public Vector2 start;
public Vector2 end;
public bool roam = true;
public Collider2D[] hits;
void Start()
{
boxCollider = GetComponent<BoxCollider2D>();
rb2D = GetComponent<Rigidbody2D>();
inverseMoveTime = 1f / moveTimeSeconds;
InvokeRepeating("RandomMove", 0.0f, 5.0f);
}
void Update()
{
Collider2D[] hits = Physics2D.OverlapCircleAll(transform.position, 10); // returns all colliders within radius of enemy
int i = 0;
while(hits.Length > i)
{
Debug.Log("Sees");
Debug.Log(hits[i]);
i++;
}
followPlayer();
if (roam)
{
Debug.Log("Roam");
transform.position = Vector2.MoveTowards(transform.position, end, inverseMoveTime * Time.deltaTime); //Random move to new position
}
}
public void followPlayer()
{
foreach (Collider2D hit in hits)
{
if (hit.tag == "Player") // if the player is within a radius
{
Debug.Log("Chasing Player");
transform.position = Vector2.MoveTowards(transform.position, GameObject.Find("Prefabs/Player").GetComponent<Transform>().position, inverseMoveTime * Time.deltaTime); // chase player
roam = false;
}
else
{
Debug.Log("Continues");
roam = true; // Continue RandomMove()
}
}
}
public void RandomMove() // gets random coordinates near enemy and moves there
{
float xDistance = Random.Range(-xDistanceRange, xDistanceRange); // check
float yDistance = Random.Range(-yDistanceRange, yDistanceRange);// check
if (xDistance < xMax && xDistance > xMin && yDistance < yMax && yDistance > yMin && roam == true) // If within boundaries of walking space
{
start = transform.position;
end = start + new Vector2(xDistance, yDistance);
Debug.Log("Roaming From : " + start + " To : " + end);
}
}
}
The roaming algorithm works however not too sure about the tag detection.
The script belongs to this enemy object
Player Object Properties
It looks like you are declaring a new hits variable during every update instead of using your class-level variable. This means the variable inside of followPlayer() will never be instantiated, and the information will not be passed between the two methods.
Try this:
void Update()
{
hits = Physics2D.OverlapCircleAll(transform.position, 10);
//...
}
You might also consider attaching a trigger collider to the enemy which sends a notification when the player enters/exits the trigger instead of relying on OverlapCircleAll()
void OnTriggerEnter2D(Collider2D col)
{
if(col.gameObject.tag == "Player"){
roam = false;
}
}
void OnTriggerExit2D(Collider2D col)
{
if(col.gameObject.tag == "Player"){
roam = true;
}
}
The code helps in moving the game object in a continousy loop. I want the randomized generated cubes to follow the same pattern too. I didn't add the condition for stopping the generation of game objects when it completes one round. Currently, the generated game objects don't move.
The ultimate idea is to generate splash scene. I would like to know if the following way is gpu efficient too!
using UnityEngine;
using System.Collections.Generic;
public class IntegratedScrpt : MonoBehaviour
{
public List<GameObject> splashImagesGOList;
public float InvokeRate = 10f;
public GameObject cube01;
private int selection;
// Loop mode variables
private Vector3 startPosition;
private Vector3 endPosition;
private float distance = 54f;
private float distanceCovered;
private float currentDistance;
//for Vector Lerp
private float currentLerpTime = 0f;
private float lerpTime = 9f;
private float t;
void Start()
{
startPosition = splashImagesGOList[1].transform.position;
Debug.LogError("selection VALUE AT" + selection);
endPosition = Vector3.back * distance;
}
void Update()
{
InvokeRepeating("pickpoints", 1.0f, InvokeRate);
//loop mode
distanceCovered = Vector3.Distance(startPosition, endPosition);
currentDistance = distanceCovered * t;
currentLerpTime += Time.deltaTime;
if (currentLerpTime == lerpTime)
{
currentLerpTime = lerpTime;
}
if (currentLerpTime > lerpTime)
{
currentLerpTime = 0f;
}
t = currentLerpTime / lerpTime;
Move();
if (currentDistance == 64)
{
splashImagesGOList[selection].transform.position = startPosition;
Move();
}
Debug.LogError("SELECTION" + selection);
}
// method for making the gameobjects move
public void Move()
{
splashImagesGOList[selection].transform.position = Vector3.Lerp(startPosition, endPosition, t);
}
// code for instantiating the gameobjects
void pickpoints()
{
foreach (GameObject cube01 in splashImagesGOList)
{
int selection = UnityEngine.Random.Range(0, splashImagesGOList.Count);
// Instantiate(splashImagesGOList[selection], cube01.transform.position, cube01.transform.rotation);
Instantiate(splashImagesGOList[selection], startPosition, cube01.transform.rotation);
}
}
}
The reason the instantiated gameobjects aren't moving is because you're not assigning to any of their positions! splashImagesGOList[selection].transform.position is the position of one of the prefabs. In your current code, you instantiate the objects and then never interact with the instantiated objects.
You should have each object handle its own movement by separating out your movement logic into a different script and attaching the script to each of the prefabs in your list. You can use Mathf.Repeat to do the sort of looping your current code seems to mean to do.
Now, it's not clear what kind of pattern you are trying to achieve with the repeated simultaneous random instantiation but regardless of that, you probably don't mean to put InvokeRepeating in Update. Additionally, you should have some kind of end condition to cease the repeated PickPoints calls with CancelInvoke("PickPoints");. Creating an ever increasing number of objects is not gpu efficient ;)
Altogether, these changes might look like this:
public class SpashImageMover : MonoBehaviour
{
public Vector3 startPosition;
public Vector3 endPosition;
public float3 lerpTime;
private float t = 0; // in case code in Start is removed
void Start()
{
// remove these two lines if you don't want the objects synchronized
t = Mathf.Repeat(Time.time/lerpTime, 1f);
transform.position = Vector3.Lerp(startPosition, endPosition, t);
}
void Update()
{
t = Mathf.Repeat(t + Time.deltaTime / lerpTime, 1f);
transform.position = Vector3.Lerp(startPosition, endPosition, t);
}
}
public class IntegratedScrpt : MonoBehaviour
{
public List<GameObject> splashImagesGOList;
public float InvokeRate = 10f;
private int selection;
// Loop mode variables
private Vector3 startPosition;
private Vector3 endPosition;
//for Vector Lerp
private float lerpTime = 9f;
// end condition for PickPoints
private bool invokingPickPoints;
private float pickPointsTimeRemaining = 27f;
void Start()
{
startPosition = splashImagesGOList[1].transform.position;
Debug.LogError("selection VALUE AT" + selection);
endPosition = Vector3.back * distance;
InvokeRepeating("PickPoints", 1.0f, InvokeRate);
invokingPickPoints = true;
}
void Update()
{
if (invokingPickPoints)
{
pickPointsTimeRemaining -= Time.deltaTime;
if (pickPointsTimeRemaining <= 0 )
{
CancelInvoke("PickPoints");
invokingPickPoints = false;
}
}
}
// code for instantiating the gameobjects
void PickPoints()
{
foreach (GameObject cube01 in splashImagesGOList)
{
int selection = UnityEngine.Random.Range(0, splashImagesGOList.Count);
// Instantiate(splashImagesGOList[selection], cube01.transform.position, cube01.transform.rotation);
GameObject newGO = Instantiate(splashImagesGOList[selection], startPosition, cube01.transform.rotation);
SpashImageMover mover = newGO.GetComponent<SpashImageMover>();
mover.startPosition = startPosition;
mover.endPosition = endPosition;
mover.lerpTime = lerpTime;
}
}
}
As a sidenote, if you now find you don't like how you're instantiating the objects, that would be more appropriate for a different question with a very descriptive explanation of what you are trying to achieve. It's too broad of a question to try and address that here.
I have a script that spawns my ground prefabs infinitely. The only problem is that occasionally there is a "bump" for the player to hit at the boundary of a new ground prefab.
It seems like one prefab is slightly higher than the previous one and when the player hits that small bump they go flying. How do I fix this?
I might just make the player a game object instead of a rigidbody and animate it instead of using actual physics.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GroundManager : MonoBehaviour
{
//oH is object height
public GameObject[] ground;
public GameObject[] obstacles;
private Transform playerTransform;
private float spawnZ = 0.0f;
private float spawnO = 0.0f;
public float oH;
private float tileLength = 40.0f;
private float oLength = 36.0f;
private int tilesOnScreen = 8;
void Start()
{
playerTransform = GameObject.FindGameObjectWithTag("Player").transform;
for (int i = 0; i < tilesOnScreen; i++)
{
SpawnTile();
SpawnObstacle();
}
}
// Player must be tagged "Player"
void Update()
{
if (playerTransform.position.z > (spawnZ - tilesOnScreen * tileLength))
{
SpawnTile();
SpawnObstacle();
}
}
private void SpawnTile(int prefabIndex = -1)
{
GameObject go;
go = Instantiate(ground[0]) as GameObject;
go.transform.SetParent(transform);
go.transform.position = Vector3.forward * spawnZ;
spawnZ += tileLength;
}
private void SpawnObstacle()
{
GameObject go;
go = Instantiate(obstacles[Random.Range(0, obstacles.Length)]) as GameObject;
go.transform.SetParent(transform);
go.transform.position = new Vector3(0, oH, 1 * spawnO);
spawnO += oLength;
}
}
This code works to infinitely spawn the ground objects but has the bumps that I described. Just one bump is enough to screw up the whole game.
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);
}
}
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]);
}
}