I've got some Unity code that creates a grid (2^dimensions in total) of one prefab using a c# script. Upon changing the 'dimensions' value (via the editor for now), I'd like to see all the prefabs get deleted, before Unity updates using OnValidate. Unity does not seem to want to delete the previous set of objects representing the space, as these objects are still accessible in the Unity Hierarchy Pane:
Having trouble destroying objects in unity. the error says:
"Can't destroy Transform component of 'XXX'. If you want to
destroy the game object, please call 'Destroy' on the game object
instead. Destroying the transform component is not allowed."
(refer to function DeletePoints/GeneratePoints. Call Graph: OnValidate --> GeneratePoints(->DeletePoints, ->GeneratePointsHelper)
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Collections;
public class BinarySpacePointGenerator : MonoBehaviour {
private const int UNITY_DRAW_SPACE_DIMENSIONALITY = 3;
/**
* These values denote spacings for the three dimensional space between binary points.
*/
public float xoff, yoff, zoff;
public float scale;
public Transform pointPrefab;
/**
* The current dimensionality of binary space to be displayed.
*/
public int dimensions;
/*
* The container of points that represent our B space.
*/
private List<Transform> points;
// Use this for initialization
void Start () {
xoff = 1.0f;
yoff = 1.0f;
zoff = 1.0f;
scale = 0.25f;
dimensions = 2;
points = new List<Transform> ();
GeneratePoints ();
}
void OnValidate() {
/* ensure dimensionality */
/* TODO: set up a B^0 space. */
if (dimensions < 1) {
dimensions = 1;
}
if (dimensions >= 13) {
dimensions = 12;
}
/* ensure that our spacing sizes are valid */
if (xoff <= 0.0f) {
xoff = 1.0f;
}
if (yoff <= 0.0f) {
yoff = 1.0f;
}
if (zoff <= 0.0f) {
zoff = 1.0f;
}
if (scale <= 0.0f) {
scale = 0.25f;
}
/* now that we've ensured the dimensionality, we can change the space */
GeneratePoints ();
}
private void DeletePoints() {
for (int i = 0; i < transform.childCount; i++) {
Destroy (transform.GetChild (0));
}
points.RemoveRange(0, points.Count); /* pop off */
}
/**
* Instantiates the points field based on the value of dimensions at call-time.
*/
private void GeneratePoints() {
DeletePoints ();
int[] vectorProto = new int[dimensions];
for (int i = 0; i < dimensions; i++) {
vectorProto [i] = 0;
}
GeneratePointsHelper(vectorProto, dimensions);
}
/**
*
* GeneratePointsHelper
*
* Description: Recursively builds the binary space B^n.
*
* Parameters:
* int[] vector: the proto-type of all higher dimensions for the current trail.
* int n: the number of dimensions left to traverse from this recursion step.
*
* Recursion Termination/Description:
* When n == 0, which means that we have created a unique vector.
*
*/
private void GeneratePointsHelper(int[] vector, int n) {
if (n == 0) {
// use vector to set Sphere object
var point = Instantiate(pointPrefab);
Vector3 pointPosition = new Vector3 ();
pointPosition.x = 0;
pointPosition.y = 0;
pointPosition.z = 0;
for (int i = 0; i < dimensions; i++) {
int d = (i / UNITY_DRAW_SPACE_DIMENSIONALITY);
if ( i % UNITY_DRAW_SPACE_DIMENSIONALITY == 0) {
pointPosition.x += (xoff * vector[i] * Mathf.Pow(2, d));
} else if (i % UNITY_DRAW_SPACE_DIMENSIONALITY == 1) {
pointPosition.y += (yoff * vector[i] * Mathf.Pow(2, d));
} else if (i % UNITY_DRAW_SPACE_DIMENSIONALITY == 2) {
pointPosition.z += (zoff * vector[i] * Mathf.Pow(2, d));
}
}
point.localPosition = pointPosition;
point.localScale = new Vector3 (scale, scale, scale);
point.parent = transform;
points.Add (point);
} else {
vector[dimensions-n] = 0;
GeneratePointsHelper (vector, n - 1);
vector[dimensions-n] = 1;
GeneratePointsHelper (vector, n - 1);
}
}
}
You are currently destroying the GameObjects with Destroy (transform.GetChild (0));.
The problem is that transform.GetChild returns a Transform and you cannot destroy a Transform. With the lastest version Unity, you will get this error:
Can't destroy Transform component of 'GameObject'. If you want to
destroy the game object, please call 'Destroy' on the game object
instead. Destroying the transform component is not allowed.
You need to access the GameObject from the Transform then destroy it. You also need to use i in the GetChild instead of 0 since Destroy is being called in the for loop and that's likely what you are trying to do.
for (int i = 0; i < transform.childCount; i++)
{
Destroy(transform.GetChild(i).gameObject);
}
I'd like to see all the prefabs get deleted, before Unity updates
using OnValidate
Then call DeletePoints() in the first line of the void OnValidate(){} function.
Destroy(Player_RayCasting.LastTower);
changed to
Destroy(Player_RayCasting.LastTower.gameObject);
in my case i was accessing a transform so i had to change it to a game object to delete it
Related
I have a method that is supposed to generate a certain number of Vector3 at a distance not less than specified.
// Generate random point based on plane area
public List<Vector3> GeneratePositions(int numberOfPositions, float minDistanceBetweenPositions)
{
float entireArea = 0f;
List<AreasWeight> areasWeights = new List<AreasWeight>();
List<Vector3> positions = new List<Vector3>();
foreach (GeneratorPlane plane in GeneratorPlanes.GetCollectionAsList())
{
entireArea += plane.GetArea();
}
foreach (GeneratorPlane plane in GeneratorPlanes.GetCollectionAsList())
{
float weight = plane.GetArea() / entireArea;
int numOfPositionsInArea = Mathf.RoundToInt(numberOfPositions * weight);
areasWeights.Add(new(plane, weight, numOfPositionsInArea));
}
foreach (AreasWeight areaWeight in areasWeights)
{
for (int i = 0; i < areaWeight.NumOfPointsInArea; i++)
{
Vector3 generatedPoint = areaWeight.Plane.GetRandomPointOnPlane();
foreach (Vector3 position in positions)
{
int attempts = 1;
while ((position - generatedPoint).magnitude < minDistanceBetweenPositions)
{
generatedPoint = areaWeight.Plane.GetRandomPointOnPlane();
attempts++;
if (attempts > 2000)
{
Debug.Log("Can't generate all positions.");
break;
}
}
}
positions.Add(generatedPoint);
}
}
return positions;
}
Get random point method:
public Vector3 GetRandomPointOnPlane()
{
float xPosition = Random.Range(Mathf.Min(DownPoint.x, DownPointHelper.x), Mathf.Max(DownPoint.x, DownPointHelper.x));
float zPosition = Random.Range(Mathf.Min(DownPoint.z, UpPointHelper.z), Mathf.Max(DownPoint.z, UpPointHelper.z));
return new(xPosition, DownPoint.y + 0.002f, zPosition);
}
But when i Instantiate objects based on these Vector3. Objects still have a distance less than the specified. What am i doing wrong?
I found a solution. The problem was a bad loop structure. When the algorithm confirmed that the distance was too small and generated a new one, it did not check whether the generated position had a gap from the previous positions on the list. It only confirmed that the gap was preserved and the program continued to execute.
I moved the code that makes sure that the distances are saved to the public List<Vector3> GeneratePositions(int numberOfPositions, float minDistanceBetweenPositions) method in the GeneratorPlane class. I also added a private Vector3 PickRandomPos() method to it, just to return the generated position.
Methods in the public class GeneratorPlane:
public Vector3 GetRandomPointOnPlane(List<Vector3> alreadyGeneratedPoints, float minDistnaceBetweenPositions)
{
if (alreadyGeneratedPoints.Count != 0)
{
int attemps = 1;
bool pointFound = false;
Vector3 posToReturn = new();
while (!pointFound)
{
pointFound = true;
posToReturn = PickRandomPos();
foreach (Vector3 position in alreadyGeneratedPoints)
{
if (Vector3.Distance(position, posToReturn) < minDistnaceBetweenPositions)
{
pointFound = false;
attemps++;
if (attemps > 2000)
{
Debug.LogError("Points cannot be generated. Too little available space");
return Vector3.zero;
}
break;
}
}
}
return posToReturn;
}
else
{
Debug.Log("First point generated");
return PickRandomPos();
}
}
private Vector3 PickRandomPos()
{
float xPosition = Random.Range(Mathf.Min(DownPoint.x, DownPointHelper.x), Mathf.Max(DownPoint.x, DownPointHelper.x));
float zPosition = Random.Range(Mathf.Min(DownPoint.z, UpPointHelper.z), Mathf.Max(DownPoint.z, UpPointHelper.z));
return new(xPosition, DownPoint.y + 0.002f, zPosition);
}
Method to generate and return a certain number of items:
public List<Vector3> GeneratePositions(int numberOfPositions, float minDistanceBetweenPositions)
{
float entireArea = 0f;
List<AreasWeight> areasWeights = new();
List<Vector3> positions = new();
foreach (GeneratorPlane plane in PlanesGenerator.GetCollectionAsList())
{
entireArea += plane.GetArea();
}
foreach (GeneratorPlane plane in PlanesGenerator.GetCollectionAsList())
{
float weight = plane.GetArea() / entireArea;
int numOfPositionsInArea = Mathf.RoundToInt(numberOfPositions * weight);
areasWeights.Add(new(plane, weight, numOfPositionsInArea));
}
foreach (AreasWeight areaWeight in areasWeights)
{
for (int i = 0; i < areaWeight.NumOfPointsInArea; i++)
{
Vector3 generatedPoint = areaWeight.Plane.GetRandomPointOnPlane(positions, minDistanceBetweenPositions);
positions.Add(generatedPoint);
}
}
return positions;
}
On the original code if you generate a point 2000 times you actually keep the last generatedPoint, and as you mentioned you don't actually cross check the whole list of positions, only the remaining positions.
Although you have solved your problem and posted a solution, I took the liberty of doing a simple script with the same end, I will share it here in hopes its useful for you or others.
This solution will not fill any area, its only making sure no two objects are at shorter distance than specified.
In my tests, with 50 nPoints only 10/20 points are instantiated before a point takes over 2000 attempts and consequently conclude the search for points. Although this will depend on the ratio between spawnLimits and nPoints.
[SerializeField]
GameObject trunkPrefab;
List<Vector3> positions;
//input variables
int nPoints = 50;
float minDistance = 2.5f;
int spawnLimits = 20;
void Start()
{
positions = new();
for (int i = 0; i < nPoints; i++)
{
Vector3 position = Vector3.zero;
bool newPosition = true;
int attempts = 0;
do
{
//first generation will be automatically added to the list
position = new(Random.Range(-spawnLimits, spawnLimits), .5f, Random.Range(-spawnLimits, spawnLimits));
if (positions.Count < 1)
{
break;
}
//every position will be compared here,
//if any position is too close from then new position
//"newPosition" is set to false and we try again from the start.
for (int p = 0; p < positions.Count; p++)
{
if (Vector3.Distance(position, positions[p]) < minDistance)
{
newPosition = false;
attempts++;
if (attempts > 2000)
{
Debug.Log("Max attempts reached.");
return;
}
break;
}
}
} while (!newPosition);
//adding a random rotation
Vector3 rotation = new(Random.Range(80, 100), Random.Range(0, 179), 0);
Instantiate(trunkPrefab, position, Quaternion.Euler(rotation));
positions.Add(position);
}
}
I'm trying to create a voxel-style cone shape in C#. I've got the cone building using cubes, but can't work out how to only build the outer layer (make it hollow) rather than build a solid cone as shown in the pic.
The code so far (Edited from someone else's script)
// Create a cone made of cubes. Can be called at runtime
public void MakeVoxelCone()
{
for (int currentLength = 0; currentLength < maxLength; currentLength++)
MakeNewLayer(currentLength);
}
// Make a new layer of cubes on the cone
private void MakeNewLayer(int currentLength)
{
center = new Vector3(0, currentLength, 0);
for (int x = -currentLength; x < currentLength; x++)
{
for (int z = -currentLength; z < currentLength; z++)
{
// Set position to spawn cube
Vector3 pos = new Vector3(x, currentLength, z);
// The distance to the center of the cone at currentLength point
float distanceToMiddle = Vector3.Distance(pos, center);
// Create another layer of the hollow cone
if (distanceToMiddle < currentLength)
{
// Add all cubes to a List array
Cubes.Add(MakeCube(pos));
}
}
}
}
// Create the cube and set its properties
private GameObject MakeCube(Vector3 position)
{
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.GetComponent<Renderer>().material.color = Color.blue;
if (!AddCollider)
Destroy(cube.GetComponent<BoxCollider>());
cube.transform.position = position;
cube.transform.parent = transform;
cube.name = "cube [" + position.y + "-" + position.x + "," + position.z + "]";
return cube;
}
I think its probably simple, but cant figure it out.
Maybe something about the if (distanceToMiddle < currentLength) part, but swapping < for == breaks the whole shape.
#Jax297 >= (currentLength-1) Closer, but not correct just yet. Now its a pyramid with a cone cut out.
assuming your currentlength is the outer diameter you have to introduce a thickness vareable and compare to currentleght - thickness, so there is a inner diameter to be kept free
(currentLength - thickness) < distanceToMiddle && distanceToMiddle < currentLength
If you want to create a "empty" cone. Try to describe that shape in English first.
Create a circle at height going from 0...h with increasing radius for each step from 0..r
So now you need to find equation of a circle (should take you back to HS trig): sin(theta)^2 + cos(theta)^2 = radius:
Now you want to create this circle while increasing the height.
This will create only the empty cone you wish.
Here's an quick implementation (adjust as needed):
public List<GameObject> instantiatedObjects = new List<GameObject>();
public GameObject rootParent;
public int numOfRows = 10;
public int incrementPerRow = 4;
public float radiusStep = 0.5f;
public float height = 5;
[Button]
public void CreateVoxelCone()
{
// first one. root object.
rootParent = GameObject.CreatePrimitive(PrimitiveType.Cube);
rootParent.name = "Pivot";
rootParent.transform.position = Vector3.zero;
var itemsForThisRow = incrementPerRow;
var heightStep = height / numOfRows;
// for each row...
for (int i = 0; i < numOfRows; i++)
{
// create items in a circle
var radianStep = Mathf.PI * 2 / itemsForThisRow;
for (float j = 0; j < Mathf.PI*2; j=j+radianStep)
{
var newPosition = new Vector3(Mathf.Cos(j) * radiusStep * i, heightStep * i, Mathf.Sin(j) * radiusStep*i);
var point = GameObject.CreatePrimitive(PrimitiveType.Cube);
point.name = ($"Row: {i}, {j}");
point.transform.SetParent(rootParent.transform);
point.transform.localPosition = newPosition;
instantiatedObjects.Add(point);
}
itemsForThisRow += incrementPerRow;
}
}
[Button]
public void CleanUp()
{
DestroyImmediate(rootParent);
}
I say that I am a beginner.
I have a question during the project.
I'm currently implementing a card-matching game.
When I start the game, The image is loaded from the external path (D: / ..).
My question is in the code below.
public void createCardList()
{
int count_x = 0;
int count_y = 0;
GameObject parent_area = GameObject.Find("GameMain");
List<int> num = createRandomIndex(createArrayIndex());
for (int i = 0; i < card_list.Count; ++i)
{
if ((i % CARD_ROWS) == 0 && i > 0)
{
count_y += 1;
count_x = 0;
}
GameObject card_prefab_load = Resources.Load("Prefabs/card") as GameObject;
GameObject card_prefab_instantiate = Instantiate(card_prefab_load, card_prefab_load.transform.position, Quaternion.identity);
float card_position_x = (CARD_WIDTH + CARD_SPACE) * (i % CARD_ROWS) - 290f;
//Debug.Log("card_position_x" + card_position_x);
float card_position_y = count_y * (CARD_HEIGHT + CARD_SPACE) - 280f;
//Debug.Log("card_position_y" + card_position_y);
card_prefab_instantiate.transform.SetParent(parent_area.transform);
card_prefab_instantiate.transform.name = "card_" + num[i];
card_prefab_instantiate.transform.localScale = new Vector3(1f, 1f, 1f);
card_prefab_instantiate.transform.localPosition = new Vector3(card_position_x, card_position_y, 1f);
StartCoroutine(firstRotateOriginalImage());
}
}
// Rotate image
private IEnumerator firstRotateOriginalImage()
{
yield return new WaitForSeconds(2f);
GameObject findCardList = GameObject.Find("GameMain");
for (int i = 0; i < findCardList.transform.childCount; ++i)
{
// I don't know what code to put in this part.
}
}
What I want to implement is below.
When the card rotation value reaches 90 degrees,How to change an externally imported image to a Sprite Image of a child GameObject?
How to rotate the child objects 360 degrees after the first task is completed?
For example, the picture below.
Arrows indicate the order in which cards are flipped.
also, Of the 12 GameObjects, Six GameObjects try to implement a common image.
I don't know much. I need your help.
There are many ways to do that...
transform.Rotate(...)
transform.RotateAround(...)
transform.localEulerAngles = transform.localEulerAngles.X|Y|Z +
amount
transform.localRotation = transform.localRotation*relativeRotation
/*= a Quaternion*/
Something else entirely...
I'm a doubtful regarding how well I understand your question...
But it seems like you want to simply flip the cards over don't you?
The approach I'd take is to have each card as the combination of the face (rotated 180 degrees in Y) and the back, both being children of an empty GameObject.
That way you could simply rotate the Card object using transform.Rotate(0, 180, 0)
To use it in a coroutine you could do
//Speed of animation (here 0.55 seconds)
float degreesPerSecond = 200f;
//The amount you want to rotate
float maxDegrees = 180f;
//Animate
for (float f = maxDegrees; f < 0;)
{
//How much to rotate
float rot = degreesPerSecond * Time.deltaTime;
//Rotate children
foreach(var child in transform)
child.Rotate(0, rot, 0);
//Record rotation
f -= rot;
//Wait next frame
yield return null;
}
//Round to desired rotation
foreach(var child in transform)
child.position = new Vector3(child.position.x, maxDegrees, child.position.z);
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
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);