Unity 5 Randomly generated moving objects that slowly approach - c#

this code is suppose to generate randomly placed clouds and planes in the distance that will fly towards you while the objective is to dodge them, however it just spawns random planes that don't move. It just gradually spawns them closer to your plane and keeps spawning them even if it goes past it, while the ones previously spawned stay in the same spot, so it's just a trail of non-moving planes, how would I code it so they approach me? Anything helps, thanks!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CloudGen : MonoBehaviour {
public GameObject cloud;
public GameObject enemy;
int firstRand;
int secondRand;
int distance = 12;
int time = 0;
float x;
float y;
float z;
Vector3 intPos;
void Update () {
if (Input.GetButtonDown ("left") || Input.GetButtonDown ("right"))
{
firstRand = Random.Range (1, 3);
if(firstRand == 1)
{
secondRand = Random.Range (1, 3);
GameObject cloudInt = Instantiate (cloud) as GameObject;
for (int i = 0; i < secondRand; i++)
{
intPos = new Vector3 (Random.RandomRange(1, 4), 0, distance);
cloudInt.transform.position = intPos;
cloudInt.transform.Translate (distance, 0, 0);
distance -= 1;
}
}
if (firstRand == 2)
{
secondRand = Random.Range (1, 8);
for (int i = 0; i < secondRand; i++)
{
intPos = new Vector3 (Random.RandomRange(1, 4), 0, distance); }
GameObject enemyInt = Instantiate (enemy) as GameObject;
enemyInt.transform.position = intPos;
enemyInt.transform.Translate (distance, 0, 0);
distance -= 1;
}
}
}
}

Related

Zooming out the camera so that it can see all objects

I have a list of objects, these are blue stickmen on the video, I need to make the camera move away by itself and all objects (blue stickmen) always fit into it, you need to take into account that there will be more and more objects each time, so the camera should be dynamic and adapt itself to all objects
https://youtube.com/shorts/x3uSO2L22Kc?feature=share
Practical solution for any camera angle and objects positions
The idea here is fairly simple.
At each step we check if each object is inside of camera view or is camera too far away, then we simply adjust the camera position towards a better one.
Step by step, our camera will follow target objects dynamically, and when stabilized, all target objects will be captured by camera.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraAutoFitSmooth : MonoBehaviour
{
public int maxNumberOfObjs = 100;
public GameObject objPrefab;
public float maxInitRange = 10f;
public float minCameraHeight = 1f;
public float maxCameraMoveSpeed = 9f;
public float marginalPos = 0.1f;
[HideInInspector]
public List<Transform> objs = new List<Transform>();
Camera cam;
void Start()
{
cam = Camera.main;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
RandomObjs();
}
// Randomly regenerate objects
void RandomObjs()
{
int nowNumberOfObjs = Random.Range(0, maxNumberOfObjs);
for (int i = objs.Count - 1; i > nowNumberOfObjs; i--)
{
Destroy(objs[i].gameObject);
objs.RemoveAt(i);
}
for (int i = objs.Count; i <= nowNumberOfObjs; i++)
objs.Add(Instantiate(objPrefab).transform);
foreach (var obj in objs)
obj.position = Random.insideUnitSphere * maxInitRange;
}
void LateUpdate()
{
SetCameraFitPosition(Time.deltaTime);
}
void SetCameraFitPosition(float deltaTime)
{
Vector3 targetCamPos = cam.transform.position;
if (objs.Count == 1)
{
targetCamPos = objs[0].position - minCameraHeight * cam.transform.forward;
}
else if (objs.Count > 1)
{
float minInsideDiff = 1f, maxOutsideDiff = 0f;
Vector3 center = Vector3.zero;
foreach (var obj in objs)
{
Vector3 screenPos = GetScreenPos(obj.position);
if (IsInsideView(screenPos))
minInsideDiff = Mathf.Min(minInsideDiff, CalculateInsideDiff(screenPos.x), CalculateInsideDiff(screenPos.y), CalculateInsideDiff(screenPos.z));
else
maxOutsideDiff = Mathf.Max(maxOutsideDiff, CalculateOutsideDiff(screenPos.x), CalculateOutsideDiff(screenPos.y), CalculateOutsideDiff(screenPos.z));
center += obj.position;
}
center /= objs.Count;
float nowHeight = Vector3.Project(cam.transform.position - center, cam.transform.forward).magnitude;
float maxDiff = maxOutsideDiff > 0f ? maxOutsideDiff : -minInsideDiff;
float finalHeight = Mathf.Max(nowHeight + maxDiff * maxCameraMoveSpeed, minCameraHeight);
targetCamPos = center - finalHeight * cam.transform.forward;
}
cam.transform.position = Vector3.MoveTowards(cam.transform.position, targetCamPos, maxCameraMoveSpeed * deltaTime);
}
Vector3 GetScreenPos(Vector3 pos)
{
return cam.WorldToViewportPoint(pos);
}
float CalculateOutsideDiff(float pos)
{
float diff = 0f;
if (pos > 1f + marginalPos)
diff = pos - 1f;
else if (pos < -marginalPos)
diff = -pos;
return diff;
}
float CalculateInsideDiff(float pos)
{
float diff = 0f;
if (pos < 1f - marginalPos && pos > marginalPos)
diff = Mathf.Min(1f - pos, pos);
return diff;
}
bool IsInsideView(Vector3 screenPoint)
{
return screenPoint.z > 0f && screenPoint.x > 0f && screenPoint.x < 1 && screenPoint.y > 0f && screenPoint.y < 1;
}
}
If you need more info feel free to contact me :) Cheers!
The following script will position a perspective camera in a top down view, so that all tracked GameObjects (objs) are visible.
It is assumed that the objects are on the zero xz plane and are points, so their actual dimensions are not taken into account. There must be at least one tracked object. The objects may not be spaced in such a way that would require the cameras height to exceed the maximum floating point value.
public GameObject[] objs;//objects that must be fitted
private Camera cam;
float recXmin, recXmax, recYmin, recYmax;
Vector3 center;
void Start()
{
cam = Camera.main;
cam.transform.rotation = Quaternion.Euler(90, 0, 0);
}
void LateUpdate()
{
recXmin = objs[0].transform.position.x;
recXmax = objs[0].transform.position.x;
recYmin = objs[0].transform.position.z;
recYmax = objs[0].transform.position.z;
center = Vector3.zero;
foreach (GameObject obj in objs)
{
if (obj.transform.position.x < recXmin)
{
recXmin = obj.transform.position.x;
}
if (obj.transform.position.x > recXmax)
{
recXmax = obj.transform.position.x;
}
if (obj.transform.position.z < recYmin)
{
recYmin = obj.transform.position.z;
}
if (obj.transform.position.z > recYmax)
{
recYmax = obj.transform.position.z;
}
}
float horizontalHeight = (recYmax - recYmin) / 2 / Mathf.Tan(Mathf.Deg2Rad * cam.fieldOfView / 2);
float verticalHeight = (recXmax - recXmin) / 2 / Mathf.Tan(Mathf.Deg2Rad * Camera.VerticalToHorizontalFieldOfView(cam.fieldOfView, cam.aspect) / 2);
float finalHeight = horizontalHeight > verticalHeight ? horizontalHeight : verticalHeight;
center = new Vector3(recXmin + (recXmax - recXmin) / 2, finalHeight, recYmin + (recYmax - recYmin) / 2);
cam.transform.position = center;
}
void OnDrawGizmos()
{
Gizmos.color = Color.red;
Gizmos.DrawLine(new Vector3(recXmin, 0, recYmin), new Vector3(recXmin, 0, recYmax));
Gizmos.DrawLine(new Vector3(recXmax, 0, recYmin), new Vector3(recXmax, 0, recYmax));
Gizmos.color = Color.green;
Gizmos.DrawLine(new Vector3(recXmin, 0, recYmin), new Vector3(recXmax, 0, recYmin));
Gizmos.DrawLine(new Vector3(recXmin, 0, recYmax), new Vector3(recXmax, 0, recYmax));
Gizmos.color = Color.blue;
Gizmos.DrawSphere(center, 0.5f);
}
the script determines the "bounding square" formed by all tracked objects
the bounding square is assumed to be the basis of a pyramid with the camera at its peak
using trigonometry the height of the camera can be calculated, by taking into account the known length of the pyramid's base side and the cameras field of view
the calculation is made twice for the cameras horizontal and vertical field of view
the greater of these two values is then selected
lastly the camera is position into the middle of the pyramids base and at the determined height, so that it ends up at the pyramids peak

Setting the spawn position for a 3D object

I am making an endless runner style game in unity where the floor tiles spawn randomly and endlessly in front of the player as they run and delete themselves after a certain distance behind the player this is all working fine and as intended however the individual tiles spawn about half way inside each other and as much as I try to debug my code I can't seem to effect them. Ideally, I want the code to do exactly what it's doing, but the tiles spawn end to end rather than inside each other.
Any help would be greatly appreciated.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Tile_Manager : MonoBehaviour
{
public GameObject[] tilePrefabs;
private Transform playerTransform;
private float spawnZ = 5.0f;
private float tileLength = 5.0f;
private float safeZone = 7.0f;
private int amtTilesOnScreen = 10;
private int lastPrefabIndex = 0;
private List<GameObject> activeTiles;
// Use this for initialization
void Start () {
activeTiles = new List<GameObject>();
playerTransform = GameObject.FindGameObjectWithTag ("Player").transform;
for (int i = 0; i < amtTilesOnScreen; i++)
{
if (i < 2)
SpawnTile(0);
else
SpawnTile();
}
}
// Update is called once per frame
void Update () {
if (playerTransform.position.z - safeZone > (spawnZ - amtTilesOnScreen * tileLength))
{
SpawnTile();
DeleteTile();
}
}
private void SpawnTile(int prefabIndex = -1)
{
GameObject go;
if (prefabIndex == -1)
go = Instantiate(tilePrefabs[RandomPrefabIndex()]) as GameObject;
else
go = Instantiate(tilePrefabs[prefabIndex]) as GameObject;
go.transform.SetParent(transform);
go.transform.position = Vector3.forward * spawnZ;
spawnZ += tileLength;
activeTiles.Add (go);
}
private void DeleteTile()
{
Destroy(activeTiles [0]);
activeTiles.RemoveAt (0);
}
private int RandomPrefabIndex()
{
if (tilePrefabs.Length <= 1)
return 0;
int randomIndex = lastPrefabIndex;
while (randomIndex == lastPrefabIndex)
{
randomIndex = Random.Range(0, tilePrefabs.Length);
}
lastPrefabIndex = randomIndex;
return randomIndex;
}
}
stacked tiles
You need to take the length of a tile into account. Try changing this
go.transform.position = Vector3.forward * spawnZ;
to this
go.transform.position = Vector3.forward * (spawnZ + tileLength / 2);
to add half the tile length to the spawn position.
Wouldn't you want
go.transform.Translate(Vector3.forward * spawnZ);
not position?
As you're spawning things relative to the world coordinate system.
https://docs.unity3d.com/ScriptReference/Transform.Translate.html

Spawn gameObject horde, modify concentration of spawned objects

How would I create a non-uniform random generation of gameObjects (enemies) that mimic the formation of a "horde" like this image:
I want there to be more gameObjects in the front and less as it trails off towards the back. I thought of making an Empty gameObject and having the enemies target with code like this:
public Vector3 target : Transform;
if (target == null && GameObject.FindWithTag("Empty"))
{
target = GameObject.FindWithTag("Empty").transform;
}
However, doing that would not give me the "trail" effect where there are fewer int he back.
Here is my code for randomly generating enemies, if it helps:
void SpawnHorde()
{
for (int i = 0; i < hordeCount; i++)
{
Vector3 spawnPosition = new Vector3(Random.Range (0, 200), 50, Random.Range (0, 200));
Instantiate(Resources.Load ("Prefabs/Sphere"), spawnPosition, Quaternion.identity);
}
}
Does anyone have a suggestion as to how to achieve this?
My results after implementing #Jerry's code:
More concentrated in the front; less in the back :)
I would go for Maximilian Gerhardt suggestions. Here is some raw implementation, for you to tweak it as you want. The most important to tweak is positioning in one column, what you can achieve with some random numbers.
void SpawnHorde()
{
int hordeCount = 200;
float xPosition = 0;
const int maxInColumn = 20;
while (hordeCount > 0)
{
int numberInColumn = Random.Range(5, maxInColumn);
hordeCount -= numberInColumn;
if (hordeCount < 0)
numberInColumn += hordeCount;
for (int i = 0; i < numberInColumn; i++)
{
Vector3 spawnPosition = new Vector3(xPosition, 50, Random.Range(0, 100));
Instantiate(Resources.Load("Prefabs/Sphere"), spawnPosition, Quaternion.identity);
}
xPosition += (float)maxInColumn * 2f / (float)hordeCount;
}
}

How can I delete ground that is behind the player and out of view?

I have a cube that bounces forward and each time that it goes forwards more ground is added. What I want is to make it so that each time the player bounces further forward the ground that the player has past and can no longer see is destroyed. However I am not sure how to do this. Here is my code so far:
using UnityEngine;
using System.Collections;
public class Generation : MonoBehaviour {
public GameObject Water;
public GameObject Road;
public GameObject Grass;
int firstRand;
int secondRand;
int disPlayer = 1;
Vector3 intPos = new Vector3(0, 0, 0);
// Update is called once per frame
void Update () {
if (Input.GetButtonDown ("up")) {
firstRand = Random.Range (1, 4);
if (firstRand == 1) {
secondRand = Random.Range (1, 5);
for (int i = 0; i < secondRand; i++) {
intPos = new Vector3 (0, 0, disPlayer + 3);
disPlayer+=3;
GameObject GrassIns = Instantiate (Grass) as GameObject;
GrassIns.transform.position = intPos;
}
}
if (firstRand == 2) {
secondRand = Random.Range (1, 5);
for (int i = 0; i < secondRand; i++) {
intPos = new Vector3 (0, 0, disPlayer + 3);
disPlayer+=3;
GameObject RoadIns = Instantiate (Road) as GameObject;
RoadIns.transform.position = intPos;
}
}
if (firstRand == 3) {
secondRand = Random.Range (1, 5);
for (int i = 0; i < secondRand; i++) {
intPos = new Vector3 (0, 0, disPlayer + 3);
disPlayer+=3;
GameObject WaterIns = Instantiate (Water) as GameObject;
WaterIns.transform.position = intPos;
}
}
}
}
}
The most simple solution would be to add a game object with a collider as a child of your player. Place it behind enough out of viez so that when the platform enters the collider, you destroy it and it does not show.

objects using their own unique waypoints array

update...
First Class
using UnityEngine;
using System.Collections;
[System.Serializable]
public class Wave
{
public GameObject enemyPrefab;
public float spawnInterval = 2;
public int maxEnemies = 20;
}
public class SpawnEnemy : MonoBehaviour
{
public GameObject[] waypoints;
public GameObject testEnemyPrefab;
public Wave[] waves;
public int timeBetweenWaves = 5;
private GameManagerBehavior gameManager;
private float lastSpawnTime;
private int enemiesSpawned = 0;
// Use this for initialization
void Start()
{
lastSpawnTime = Time.time;
gameManager =
GameObject.Find("GameManager").GetComponent<GameManagerBehavior>();
}
// Update is called once per frame
void Update()
{
// 1 Get the index of the current wave, and check if it’s the last one.
int currentWave = gameManager.Wave;
if (currentWave < waves.Length)
{
// 2 If so, calculate how much time passed since the last enemy spawn and whether it’s time to spawn an enemy. Here you consider two cases.
// If it’s the first enemy in the wave, you check whether timeInterval is bigger than timeBetweenWaves.
// Otherwise, you check whether timeInterval is bigger than this wave’s spawnInterval. In either case, you make sure you haven’t spawned all the enemies for this wave.
float timeInterval = Time.time - lastSpawnTime;
float spawnInterval = waves[currentWave].spawnInterval;
if (((enemiesSpawned == 0 && timeInterval > timeBetweenWaves) ||
timeInterval > spawnInterval) &&
enemiesSpawned < waves[currentWave].maxEnemies)
{
// 3 If necessary, spawn an enemy by instantiating a copy of enemyPrefab. You also increase the enemiesSpawned count.
lastSpawnTime = Time.time;
GameObject newEnemy = (GameObject)
Instantiate(waves[currentWave].enemyPrefab);
newEnemy.GetComponent<MoveEnemy>().waypoints = waypoints;
newEnemy.GetComponent<MoveEnemy>().JiggleWaypoints();
enemiesSpawned++;
}
// 4 You check the number of enemies on screen. If there are none and it was the last enemy in the wave you spawn the next wave.
// You also give the player 10 percent of all gold left at the end of the wave.
if (enemiesSpawned == waves[currentWave].maxEnemies &&
GameObject.FindGameObjectWithTag("Enemy") == null)
{
gameManager.Wave++;
gameManager.Gold = Mathf.RoundToInt(gameManager.Gold * 1.1f);
enemiesSpawned = 0;
lastSpawnTime = Time.time;
}
// 5 Upon beating the last wave this runs the game won animation.
}
else {
gameManager.gameOver = true;
GameObject gameOverText = GameObject.FindGameObjectWithTag("GameWon");
gameOverText.GetComponent<Animator>().SetBool("gameOver", true);
}
}
}
Second Class
using UnityEngine;
using System.Collections;
public class MoveEnemy : MonoBehaviour
{
[System.NonSerialized]
public GameObject[] waypoints;
private int currentWaypoint = 0;
private float lastWaypointSwitchTime;
public float speed = 1.0f;
// Use this for initialization
void Start()
{
lastWaypointSwitchTime = Time.time;
}
// Update is called once per frame
void Update()
{
// 1
Vector3 startPosition = waypoints[currentWaypoint].transform.position;
Vector3 endPosition = waypoints[currentWaypoint + 1].transform.position;
// 2
float pathLength = Vector3.Distance(startPosition, endPosition);
float totalTimeForPath = pathLength / speed;
float currentTimeOnPath = Time.time - lastWaypointSwitchTime;
gameObject.transform.position = Vector3.Lerp(startPosition, endPosition, currentTimeOnPath / totalTimeForPath);
// 3
if (gameObject.transform.position.Equals(endPosition))
{
if (currentWaypoint < waypoints.Length - 2)
{
// 3.a
currentWaypoint++;
lastWaypointSwitchTime = Time.time;
RotateIntoMoveDirection();
}
else {
// 3.b
Destroy(gameObject);
AudioSource audioSource = gameObject.GetComponent<AudioSource>();
AudioSource.PlayClipAtPoint(audioSource.clip, transform.position);
//<< deduct health
GameManagerBehavior gameManager =
GameObject.Find("GameManager").GetComponent<GameManagerBehavior>();
gameManager.Health -= 1;
//>>
}
}
}
public void JiggleWaypoints()
{
for (int i = 1; i < waypoints.Length; i++)
{
waypoints[i].transform.position = new Vector3(waypoints[i].transform.position.x + Random.Range(-3, 3), waypoints[i].transform.position.y + Random.Range(-3, 3), 0);
}
}
private void RotateIntoMoveDirection()
{
//1 It calculates the bug’s current movement direction by subtracting the current waypoint’s position from that of the next waypoint.
Vector3 newStartPosition = waypoints[currentWaypoint].transform.position;
Vector3 newEndPosition = waypoints[currentWaypoint + 1].transform.position;
Vector3 newDirection = (newEndPosition - newStartPosition);
//2 It uses Mathf.Atan2 to determine the angle toward which newDirection points, in radians, assuming zero points to the right.
// Multiplying the result by 180 / Mathf.PI converts the angle to degrees.
float x = newDirection.x;
float y = newDirection.y;
float rotationAngle = Mathf.Atan2(y, x) * 180 / Mathf.PI;
//3 Finally, it retrieves the child named Sprite and rotates it rotationAngle degrees along the z-axis.
// Note that you rotate the child instead of the parent so the health bar — you’ll add it soon — remains horizontal.
GameObject sprite = (GameObject)
gameObject.transform.FindChild("Sprite").gameObject;
sprite.transform.rotation =
Quaternion.AngleAxis(rotationAngle, Vector3.forward);
}
public float distanceToGoal()
{
float distance = 0;
distance += Vector3.Distance(
gameObject.transform.position,
waypoints[currentWaypoint + 1].transform.position);
for (int i = currentWaypoint + 1; i < waypoints.Length - 1; i++)
{
Vector3 startPosition = waypoints[i].transform.position;
Vector3 endPosition = waypoints[i + 1].transform.position;
distance += Vector3.Distance(startPosition, endPosition);
}
return distance;
}
}
Code is working 100% without errors, BUT....
After each spawn all objects get the same waypoint array. This can be seen on the screen as all objects jump to new waypoint in line together each time new object is spawned. I want the object which is already spawn to live life with it's own array of only once created waypoints.
You need to create a new array of waypoints each time you create one from the prefabricated object. You don't show your Instantiate method but I'm guessing that it has a line like this:
this.waypoints = prefab.waypoints;
This will mean that all object you create will share the same list of waypoints (as you've discovered).
What you need is something like this - assuming that the waypoints have X, Y, and Z properties):
this.waypoints = new GameObject[5];
for (int i = 0; i++ ; i < 5)
{
this.waypoints[i].X = prefab.waypoints[i].X;
this.waypoints[i].Y = prefab.waypoints[i].Y;
this.waypoints[i].Z = prefab.waypoints[i].Z;
}
(If you want your points to be a variable length you might want to consider using a list).
This means that each object has a list of unique points even if they start with the same values you can change each independently.
Based on ChrisFs' and Joe Blows' answers, do something like this in your MoveEnemy script:
private Vector3[] myWay;
public void JiggleWaypoints(GameObject[] waypoints)
{
myWay = new Vector3[waypoints.Length];
for(int i = 1; i < waypoints.Length; i++)
{
myWay[i] = new Vector3(waypoints[i].transform.position.x + Random.Range(-3, 4), waypoints[i].transform.position.y + Random.Range(-3, 4), 0);
}
}
myWay replaces the GameObject[].
In your SpawnEnemy script you do this:
GameObject e = (GameObject)Instantiate(enemyPrefab);
e.GetComponent<MoveEnemy>().JiggleWaypoints(waypoints);

Categories