How can I randomize the public Transform tilePrefab in my code? - c#

So I want to make a map generator and have a prefab for a tile. But I want to make more tiles which it randomly chooses from.
How can I make it that tilePrefab is chosen randomly every time from an array of prefabs?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MapGenerator : MonoBehaviour
{
public Transform tilePrefab; //This is the part which I want to be random
public Vector2 mapSize;
private void Start()
{
GenerateMap();
}
public void GenerateMap()
{
for (int x = 0; x < mapSize.x; x++)
{
for (int y = 0; y < mapSize.y; y++)
{
Vector3 tilePosition = new Vector3(-mapSize.x / 2 + 0.5f + x, 0,-mapSize.y / 2 + 0.5f + y);
Transform newTile = Instantiate(tilePrefab, tilePosition, Quaternion.Euler(Vector3.right * 360)) as Transform;
}
}
}
}
I want that tilePrefab is chosen randomly every time a tile gets generated.

Have an array that holds all the tile prefabs. Randomly choose an index for your new tile.
Transform newTile = Instantiate(tilePrefabs[Random.Range(0, tilePrefabs.Length - 1), tilePosition, Quaternion.Euler(Vector3.right * 360)) as Transform;

Related

SceneManager.LoadScene() is not working in the way i want

In my unity project i have 3 scenes.
Title
Play
Over
For achieving the flow Title -> Play -> Over. I had to start the game from Over scene. Over -> Title -> Play -> Over .. I don't want this. I want it to work when i start the game from Title scene. On doing so i am unable to change from Play -> Over.
for Title scene
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LoadSceneOnInput : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (Input.GetAxis("Submit") == 1) {
SceneManager.LoadScene("Play");
}
}
}
for Over Scene
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameOverInput : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (Input.GetAxis("Submit") == 1) {
SceneManager.LoadScene("Title");
}
}
}
Play's script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LevelGenerator : MonoBehaviour {
public GameObject floorPrefab;
public GameObject wallPrefab;
public GameObject ceilingPrefab;
public GameObject characterController;
public GameObject floorParent;
public GameObject wallsParent;
// allows us to see the maze generation from the scene view
public bool generateRoof = true;
// number of times we want to "dig" in our maze
public int tilesToRemove = 50;
public int mazeSize;
// spawns at the end of the maze generation
public GameObject pickup;
// this will determine whether we've placed the character controller
private bool characterPlaced = false;
// 2D array representing the map
private bool[,] mapData;
// we use these to dig through our maze and to spawn the pickup at the end
private int mazeX = 4, mazeY = 1;
// Use this for initialization
void Start () {
// initialize map 2D array
mapData = GenerateMazeData();
// create actual maze blocks from maze boolean data
for (int z = 0; z < mazeSize; z++) {
for (int x = 0; x < mazeSize; x++) {
if (mapData[z, x]) {
CreateChildPrefab(wallPrefab, wallsParent, x, 1, z);
CreateChildPrefab(wallPrefab, wallsParent, x, 2, z);
CreateChildPrefab(wallPrefab, wallsParent, x, 3, z);
} else if (!characterPlaced) {
// place the character controller on the first empty wall we generate
characterController.transform.SetPositionAndRotation(
new Vector3(x, 1, z), Quaternion.identity
);
// flag as placed so we never consider placing again
characterPlaced = true;
CreateChildPrefab(floorPrefab, floorParent, x, 0, z);
}
//create floor and ceiling
if(mapData[z, x]){
CreateChildPrefab(floorPrefab, floorParent, x, 0, z);
}else{
if((z > 0 && z < mazeSize - 1) && (x > 0 && x < mazeSize - 1)){
if(!(Random.value > 0.8)){
CreateChildPrefab(floorPrefab, floorParent, x, 0, z);
}
}
}
if (generateRoof) {
CreateChildPrefab(ceilingPrefab, wallsParent, x, 4, z);
}
}
}
// spawn the pickup at the end
var myPickup = Instantiate(pickup, new Vector3(mazeX, 1, mazeY), Quaternion.identity);
myPickup.transform.localScale = new Vector3(0.25f, 0.25f, 0.25f);
}
void Update() {
if(characterController.transform.position.y < -5){
SceneManager.LoadScene("Over");
}
}
// generates the booleans determining the maze, which will be used to construct the cubes
// actually making up the maze
bool[,] GenerateMazeData() {
bool[,] data = new bool[mazeSize, mazeSize];
// initialize all walls to true
for (int y = 0; y < mazeSize; y++) {
for (int x = 0; x < mazeSize; x++) {
data[y, x] = true;
}
}
// counter to ensure we consume a minimum number of tiles
int tilesConsumed = 0;
// iterate our random crawler, clearing out walls and straying from edges
while (tilesConsumed < tilesToRemove) {
// directions we will be moving along each axis; one must always be 0
// to avoid diagonal lines
int xDirection = 0, yDirection = 0;
if (Random.value < 0.5) {
xDirection = Random.value < 0.5 ? 1 : -1;
} else {
yDirection = Random.value < 0.5 ? 1 : -1;
}
// random number of spaces to move in this line
int numSpacesMove = (int)(Random.Range(1, mazeSize - 1));
// move the number of spaces we just calculated, clearing tiles along the way
for (int i = 0; i < numSpacesMove; i++) {
mazeX = Mathf.Clamp(mazeX + xDirection, 1, mazeSize - 2);
mazeY = Mathf.Clamp(mazeY + yDirection, 1, mazeSize - 2);
if (data[mazeY, mazeX]) {
data[mazeY, mazeX] = false;
tilesConsumed++;
}
}
}
return data;
}
// allow us to instantiate something and immediately make it the child of this game object's
// transform, so we can containerize everything. also allows us to avoid writing Quaternion.
// identity all over the place, since we never spawn anything with rotation
void CreateChildPrefab(GameObject prefab, GameObject parent, int x, int y, int z) {
var myPrefab = Instantiate(prefab, new Vector3(x, y, z), Quaternion.identity);
myPrefab.transform.parent = parent.transform;
}
}

Issue in procedural tree generator. C# Unity

I am currently working on a procedural terrain generator and I have come to an issue when procedurally spawning trees in my scene. Unity gives me one error but I don't really know how to go about fixing this issue.
Here is the code for my world generator and tree Generator.
World Generator
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TerrainGenerator : MonoBehaviour
{
[SerializeField]
private int terrainWidthInChunks, terrainLengthInChunks;
[SerializeField]
private GameObject ChunkPrefab;
[SerializeField]
private float centreVertex, maxDistance;
[SerializeField]
private TreeGenerator treeGenerator;
[SerializeField]
private RiverGenerator riverGenerator;
// Start is called before the first frame update
void Start()
{
GenerateWorld ();
}
void GenerateWorld()
{
//gets the chunk dimensions from the chunk prefab
Vector3 chunkSize = ChunkPrefab.GetComponent<MeshRenderer>().bounds.size;
int terrainHeight = (int)chunkSize.z;
int terrainWidth = (int)chunkSize.x;
//Calculates the number of vertices of the chunk in each axis of the mesh
Vector3[] terrainMeshVertices = ChunkPrefab.GetComponent<MeshFilter>().sharedMesh.vertices;
int terrainHeightInVertices = (int)Mathf.Sqrt(terrainMeshVertices.Length);
int terrainWidthInVertices = terrainHeightInVertices;
float distanceBetweenVertices = (float)terrainHeight / (float)terrainHeightInVertices;
//builds an empty object to be filled with the terrain chunks that are generated on run-time
WorldData worldData = new WorldData(terrainHeightInVertices, terrainWidthInVertices, this.terrainLengthInChunks, this.terrainWidthInChunks);
//for each chunk we will want to instantiate a chunk in the correct position
for (int x = 0; x < terrainWidthInChunks; x++)
{
for (int z = 0; z < terrainLengthInChunks; z++)
{
// calculates the chunk position based on x and z indices
Vector3 chunkPosition = new Vector3(this.gameObject.transform.position.x + x * terrainWidth, this.gameObject.transform.position.y, this.gameObject.transform.position.z + z * terrainHeight);
//instantiates new chunk
GameObject Chunk = Instantiate(ChunkPrefab, chunkPosition, Quaternion.identity) as GameObject;
//generates the chunk texture and stores it in ChunkData
ChunkData chunkData = Chunk.GetComponent<TerrainchunkGenerator>().GenerateChunk(centreVertex, maxDistance);
worldData.AddChunkData(chunkData, z, x);
}
}
//generates trees for the world
treeGenerator.GenerateTrees(this.terrainLengthInChunks * terrainHeightInVertices, this.terrainWidthInChunks * terrainWidthInVertices, distanceBetweenVertices, worldData);
//generates rivers for the world
riverGenerator.GenerateRivers(this.terrainLengthInChunks * terrainHeightInVertices, this.terrainWidthInChunks * terrainWidthInVertices, worldData);
}
}
//class to store merged chunk data
public class WorldData
{
private int terrainHeightInVertices, terrainWidthInVertices;
public ChunkData[,] chunksData;
public WorldData(int terrainHeightInVertices, int terrainWidthInVertices, int terrainLengthInChunks, int terrainWidthInChunks)
{
//builds the chunkdata matrix based on terrain depth and width in verts and chunks
chunksData = new ChunkData[terrainHeightInVertices * terrainLengthInChunks, terrainWidthInVertices * terrainWidthInChunks];
this.terrainHeightInVertices = terrainHeightInVertices;
this.terrainWidthInVertices = terrainWidthInVertices;
}
public void AddChunkData(ChunkData chunkData, int chunkZIndex, int chunkXIndex)
{
//saves the chunk data in the correct coordinates
chunksData[chunkZIndex, chunkXIndex] = chunkData;
}
public ChunkCoordinate ConvertToChunkCoordinate(int z, int x)
{
//the chunk index is calculates by dividing the index by yhr number of chunks in that axis
int chunkZIndex = (int)Mathf.Floor((float)z / (float)this.terrainHeightInVertices);
int chunkXIndex = (int)Mathf.Floor((float)x / (float)this.terrainWidthInVertices);
//the coord index is calculated by aquiring the remainder of the division operation above
//this translates te origin to the bottom left corner
int coordinateZIndex = this.terrainHeightInVertices - (z % this.terrainHeightInVertices) - 1;
int coordinateXIndex = this.terrainWidthInVertices - (x % this.terrainWidthInVertices) - 1;
ChunkCoordinate chunkCoordinate = new ChunkCoordinate(chunkZIndex, chunkXIndex, coordinateZIndex, coordinateXIndex);
return chunkCoordinate;
}
}
//class that represents a coordinate in the chunk coordinate System
public class ChunkCoordinate
{
public int chunkZIndex;
public int chunkXIndex;
public int coordinateZIndex;
public int coordinateXIndex;
public ChunkCoordinate(int chunkZIndex, int chunkXIndex, int coordinateZIndex, int coordinateXIndex)
{
this.chunkZIndex = chunkZIndex;
this.chunkXIndex = chunkXIndex;
this.coordinateZIndex = coordinateZIndex;
this.coordinateXIndex = coordinateXIndex;
}
}
*tree generator*
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TreeGenerator : MonoBehaviour
{
[SerializeField]
private NoisemapGenerator noisemapGenerator;
[SerializeField]
private Octave[] octaves;
[SerializeField]
private float terrainScale;
[SerializeField]
private float[] neighborRadius;
[SerializeField]
private GameObject[] treePrefab;
public void GenerateTrees(int height, int width, float distanceBetweenVertices, WorldData worldData)
{
//Generates a tree noise map using the perlin noise function from noise map generator
float[,] treeNoiseMap = this.noisemapGenerator.GeneratePerlinNoiseMap(height, width, this.terrainScale, 0, 0, this.octaves);
float chunkSizeX = width * distanceBetweenVertices;
float chunkSizeZ = height * distanceBetweenVertices;
for (int z = 0; z < height; z++)
{
for (int x = 0; x < width; x++)
{
//converts from the world coord system to the chunk coord system and retreieves the chunkdata
ChunkCoordinate chunkCoordinate = worldData.ConvertToChunkCoordinate(z, x);
ChunkData chunkData = worldData.chunksData[chunkCoordinate.chunkZIndex, chunkCoordinate.chunkXIndex];
int chunkWidth = chunkData.heightMap.GetLength(1);
//calculates the mesh vertex index
Vector3[] meshVertices = chunkData.mesh.vertices;
int vertexIndex = chunkCoordinate.coordinateZIndex * chunkWidth + chunkCoordinate.coordinateXIndex;
//gets the terrain region of this coord
TerrainRegion terrainRegion = chunkData.chosenHeightTerrainRegion[chunkCoordinate.coordinateZIndex, chunkCoordinate.coordinateXIndex];
//gets the current biome of the coord
Biome biome = chunkData.chosenBiomes[chunkCoordinate.coordinateZIndex, chunkCoordinate.coordinateXIndex];
//check if the coord is "water" terrain. We obviouslt don't want trees to be placed within the water
if (terrainRegion.name != "water")
{
float treeValue = treeNoiseMap[z, x];
int terrainRegionIndex = terrainRegion.index;
//compares the current noise value to the neighbouring values
int neighborZBegin = (int)Mathf.Max(0, z - this.neighborRadius[biome.index]);
int neighborZEnd = (int)Mathf.Min(height-1, z + this.neighborRadius[biome.index]);
int neighborXBegin = (int)Mathf.Max(0, x - this.neighborRadius[biome.index]);
int neighborXEnd = (int)Mathf.Min(width - 1, x + this.neighborRadius[biome.index]);
float maxValue = 0f;
for (int neighborZ = neighborZBegin; neighborZ <= neighborZEnd; neighborZ++)
{
for (int neighborX = neighborXBegin; neighborX <= neighborXEnd; neighborX++)
{
float neighborValue = treeNoiseMap[neighborZ, neighborX];
//saves the max tree noise values in the radius
if (neighborValue >= maxValue)
{
maxValue = neighborValue;
}
}
}
//if the current trees nise value is the max of one then place a tree in that location
if (treeValue == maxValue)
{
Vector3 treePosition = new Vector3(x * distanceBetweenVertices, meshVertices[vertexIndex].y, z * distanceBetweenVertices);
GameObject tree = Instantiate(this.treePrefab[biome.index], treePosition, Quaternion.identity) as GameObject;
tree.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
}
}
}
}
}
}
In GenerateWorld() method, i see you are using treeGenerator.GenerateTrees method but i didn't see any initialization of the treeGenerator object. Is this where you are getting the error. then you need to initialize the class object before using it.
I think you also need to initialize the riverGenerator object as well.

How can I move an object randomly in small area from his current position?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TargetMove : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
transform.transform.position = RandomVector(0.1f, 0.5f);
}
private Vector3 RandomVector(float min, float max)
{
var x = Random.Range(min, max);
var y = Random.Range(min, max);
var z = Random.Range(min, max);
return new Vector3(transform.position.x + x, transform.position.y + y, transform.position.z);
}
}
I want it to move in small area randomly between 0.1 and 0.5 but since I did + x and + y it keep changing position and move far far nonstop.
return new Vector3(transform.position.x + x, transform.position.y + y, transform.position.z);
What should I do instead transform.position.x + x and transform.position.x + y ?
This is what I wanted :
The script is attached to a cube.
Another script is attached to the sci-fi drone I have that all is do is making the drone LookAt the cube.
That's it this create a lost control effect of the sci-fi drone shooting out of control.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TargetMove : MonoBehaviour
{
[SerializeField] private List<Vector3> waypoints = new List<Vector3>(); // Hom many items you want, will show in Inspector
public float speed;
private int current = 0;
private float WPradius = 1;
private void Start()
{
GetRandom();
}
private void Update()
{
MoveBetweenWaypoints();
}
private void GetRandom()
{
for (int i = 0; i < 30; i++)
{
waypoints.Add(new Vector3(Random.Range(transform.position.x, transform.position.x + 3f),
Random.Range(transform.position.y, transform.position.y + 3f),
transform.position.z));
}
}
private void MoveBetweenWaypoints()
{
if(Vector3.Distance(waypoints[current], transform.position) < WPradius)
{
current = Random.Range(0, waypoints.Count);
if(current >= waypoints.Count)
{
current = 0;
}
}
transform.position = Vector3.MoveTowards(transform.position, waypoints[current], Time.deltaTime * speed);
}
}

I have 3 scripts one duplicate a script and the third should get created objects but I can't get this objects. How can I get the objects?

I have 3 scripts one duplicate a script and the third should get created objects but I can't get this objects. How can I get the objects?
The first script is Generate Stairs Units. This script is attached to a empty GameObject.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GenerateStairsUnits : MonoBehaviour
{
[Header("Stairs Units Prefab")]
public GameObject stairsUnitsPrefab;
[Space(5)]
[Header("Settings")]
[Range(1, 20)]
public int numberOfUnits = 1;
public static GameObject Unit;
private int oldNumberOfUnits = 0;
private List<GameObject> units = new List<GameObject>();
// Use this for initialization
void Start ()
{
oldNumberOfUnits = numberOfUnits;
var unitsParent = GameObject.Find("Stairs Units");
for (int i = 0; i < numberOfUnits; i++)
{
Unit = Instantiate(stairsUnitsPrefab, unitsParent.transform);
Unit.name = "Stairs " + i.ToString();
units.Add(Unit);
Unit.AddComponent<MoveObjects>();
}
}
// Update is called once per frame
void Update ()
{
}
}
The GenerateStairsUnits script duplicate a prefab of the second script Generate Stairs:
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
public class GenerateStairs : MonoBehaviour
{
[Header("Stairs Prefb")]
public GameObject stairsPrefab;
[Space(5)]
[Header("Platforms")]
public bool addPlatforms = false;
public GameObject platformsPrefab;
[Space(5)]
[Header("Settings")]
public float delay = 3;
public int stairsNumber = 5;
public Vector3 stairsStartPosition;
public Vector3 stairSize;
public Vector3 stairsSize;
public float stepWidthFactor = 1f;
public GameObject moveobjects;
private Vector3 stairsPosition;
private GameObject stairsParent;
// Use this for initialization
void Start()
{
moveobjects = GameObject.Find("Move Objects");
stairsParent = new GameObject();
stairsParent.name = "Stairs";
stairsParent.transform.parent = GenerateStairsUnits.Unit.transform;
StartCoroutine(BuildStairs());
}
// Update is called once per frame
void Update()
{
}
private IEnumerator BuildStairs()
{
for (int i = 1; i <= stairsNumber; i++)
{
stairsPosition = new Vector3(
stairsStartPosition.x,
stairsStartPosition.y + (i * stairsSize.y),
stairsStartPosition.z + (i * stairsSize.y) * stepWidthFactor);
GameObject stair = Instantiate(
stairsPrefab,
stairsPosition,
Quaternion.identity);
stair.tag = "Stair";
stair.transform.parent = transform;
stair.transform.localScale = stairSize;
yield return new WaitForSeconds(delay);
}
}
}
The GenerateStairsUnits also add as component the third script Move Objects:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class MoveObjects : MonoBehaviour
{
public List<GameObject> objectsToMove = new List<GameObject>();
public AnimationCurve curve;
public float stepsPerSecond = 1f;
public bool changeDirection = false;
private Vector3 trackStart;
private Vector3 trackEnd;
private Vector3 horizontalTravel;
private float verticalTravel;
private float divisor;
private float phase = 0f;
// Use this for initialization
public void Init()
{
if (curve == null)
{
curve = new AnimationCurve(new Keyframe(0, 0), new Keyframe(1, 1));
}
curve.preWrapMode = WrapMode.Clamp;
curve.postWrapMode = WrapMode.Clamp;
trackStart = objectsToMove[0].transform.position;
int count = objectsToMove.Count;
var span = objectsToMove[count - 1].transform.position - trackStart;
divisor = 1f / count;
horizontalTravel = (count + 1) * span * divisor;
horizontalTravel.y = 0f;
verticalTravel = span.y;
trackEnd = trackStart + (count + 1) * span / count;
}
// Update is called once per frame
void Update()
{
if (objectsToMove != null && objectsToMove.Count > 0 && curve != null)
{
AnimationCurve();
}
}
private void AnimationCurve()
{
phase = Mathf.Repeat(phase + stepsPerSecond * divisor * Time.deltaTime, 1f);
for (int i = 0; i < objectsToMove.Count; i++)
{
float t = Mathf.Repeat(phase + i * divisor, 1f);
// Get the height of the curve at this step.
float curveHeight = curve.Evaluate(t) * verticalTravel;
if (changeDirection)
{
objectsToMove[i].transform.position = trackStart // First step
- horizontalTravel * t // evenly spaced horizontal
+ curveHeight * Vector3.up; // curving vertical
}
else
{
objectsToMove[i].transform.position = trackStart // First step
+ horizontalTravel * t // evenly spaced horizontal
+ curveHeight * Vector3.up; // curving vertical
}
}
}
private void StraightLineTrack()
{
float divisor = 1f / objectsToMove.Count;
// Compute the current phase of the escalator,
// from 0 (1st step at track start) to 1 (1st step at track end)
phase = Mathf.Repeat(phase + stepsPerSecond * divisor * Time.deltaTime, 1f);
// Place each step a proportional distance along the track.
for (int i = 0; i < objectsToMove.Count; i++)
{
float t = Mathf.Repeat(phase + i * divisor, 1f);
objectsToMove[i].transform.position = Vector3.Lerp(trackStart, trackEnd, t);
}
}
}
Now the problem:
After duplicating the GenerateStairs and creating stairs for each Stairs Unit and adding the Move Objects component the result is:
In the Hierarchy I have empty GameObject name Stairs Units. Under Stairs Units there are the Units Stairs 0, Stairs 1, Stairs 2, Stairs 3, Stairs 4. And under each Stairs there are the Stairs.
Each Unit for example Stair 0 have also attached the Move Objects script.
Now my problem is how to get the stairs of each unit to the Move Objects objectsToMove List.
In the Move Objects I have a List name objectsToMove. For example under Stairs 0 there are 10 stairs I need to get this 10 stairs to the objectsToMove of Stairs 0. Then the next 10 stairs of the Stairs 1 and so on. But I can't figure out how to add the stairs of each unit to the objectsToMove.
In the end the objectsToMove that what should move the stairs of each Stairs Unit.
Since your Stairs N gameobject attaches two scripts GenerateStairs and MoveObjects on it, you can get the MoveObjects's reference by calling GetComponent before generating the Stairs and pass it to the BuildStairs function.
void Start()
{
...
MoveObjects moveObjects = gameObject.GetComponent<MoveObjects>();
StartCoroutine(BuildStairs(moveObjects));
}
By getting the MoveObjects's reference, you can then pass this reference into your BuildStairs function and add those generating stairs into list objectsToMove inside MoveObjects.
Modify the function and pass MoveObjects like below:
private IEnumerator BuildStairs(MoveObjects moveObjects)
{
...
moveObjects.objectsToMove.add (stair);
...
}
About GetComponent.

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