I am making infinity road game. I am using object pooling for roads and enemy objects. roads is working well but I have problem with enemy objects, I can add enemy from pool but I cant return back enemy objects to pool.
public void CreateEnemy( EnemyPool pool, int roadLenght, Vector3 startPos , int numberOfEnemies, int enemyType )
{
enemy = new Transform[numberOfEnemies];
enemyIndex = new int[numberOfEnemies];
currentRoadLenght = roadLenght;
//I add enemies along the length of the path. Path Lenght is randomly generated.
currentEnemyNumber = numberOfEnemies;
int arrayedEnemyObject = 0;
for( int i = 0; i < roadLenght; i++ )
{
Vector3 pos = startPos + new Vector3(0, 1, i * 15);
for( int j = 0; j < numberOfEnemies; j++ )
{
Transform obj = pool.PullEnemyFromPool(enemyType);
obj.position = pos;
obj.gameObject.SetActive(true);
enemy[j] = obj;
enemyIndex[j] = enemyType;
Debug.Log(j);
pos.z += 3;
arrayedEnemyObject++;
}
if( arrayedEnemyObject == numberOfEnemies )
{
arrayedEnemyObject = 0;
i += enemyObjectDistance;
}
}
}
public void DestroyEnemy( EnemyPool objectPooler )
{
if (enemy != null)
{
int destroyedObj = 0;
for( int i = 0; i < currentRoadLenght; i++ )
{
for( int j = 0; j < currentEnemyNumber; j++ )
{
Transform obj = enemy[j];
obj.gameObject.SetActive( false );
objectPooler.AddEnemyToPool( enemyIndex[j], obj );
destroyedObj++;
}
if( destroyedObj == currentEnemyNumber )
{
destroyedObj = 0;
i += enemyObjectDistance;
}
}
enemy = null;
}
}
My EnemyPoolScript
public class EnemyPool : MonoBehaviour
{
private GameObject[] enemyObjects;
private List<Transform>[] enemyObjectsPool;
public void FillPool( GameObject[] enemyObjects , int size )
{
this.enemyObjects = enemyObjects;
Vector3 pos = Vector3.zero;
Quaternion tilt = Quaternion.identity;
GameObject obj;
enemyObjectsPool = new List<Transform>[enemyObjects.Length];
for ( int i = 0; i < enemyObjects.Length; i++ )
{
enemyObjectsPool[i] = new List<Transform>();
for( int j = 0; j < size; j++ )
{
obj = Instantiate( enemyObjects[i], pos, tilt ) as GameObject;
obj.SetActive( false );
enemyObjectsPool[i].Add( obj.transform );
}
}
}
public void AddEnemyToPool( int index, Transform obj )
{
enemyObjectsPool[index].Add (obj);
}
public Transform PullEnemyFromPool( int index )
{
Transform obj;
if( enemyObjectsPool[index].Count <= 0 )
{
obj = ( Instantiate( enemyObjects[index], Vector3.zero, Quaternion.identity ) as GameObject ).transform;
}
else
{
obj = enemyObjectsPool[index][0];
enemyObjectsPool[index].RemoveAt (0);
}
return obj;
}
}
Why are you itterating over road length in your adds and deletes. that's probably where your problem lies. you're setting the same item in your array multiple times. you are pulling an item out of your pool for every road, and putting it in the same slot.
for (int i = 0; i < roadCount; i++)
enemy[j] = pullObject;
the outer loop is probably the culprit.
Also, in your pool, it might help to use a queue instead of a list. queues are specifically used for removing items from the front, which will be more efficient than RemoveAt(0)
Edit:
okay... it's a bit hard to explain, but your outerloop probably isn't doing what you think it's doing. all your outer loop is doing is making sure you repeat your inner loop roudcount number of times. lets pretend you have a road length of 3, and a number of enemies of 2.
This is what your program is doing:
pos = startPos + new Vector3(0,1,0);
obj.position = pos;
enemy[0] = obj;
pos.z += 3;
arrayedEnemeyObject++;
obj.position = pos;
enemy[1] = obj;
pos.z += 3;
arrayedEnemeyObject++;
arrayedEnemeyObject = 0;
i += enemyObjectDistance;
pos = startPos + new Vector3(0,1,15);
obj.position = pos;
enemy[0] = obj;
pos.z += 3;
arrayedEnemeyObject++;
obj.position = pos;
enemy[1] = obj;
pos.z += 3;
Notice how enemy[0] gets set twice. i still don't know what roaddistance is meant to do, because your outerloop isn't doing anything. i'm... guessing, this is what you want... but i really don't know
enemy[,] = new Transform[roadCount, numberOfEnemies];
enemyIndex[,] = new int[roadCount, numberOfEnemies];
then in your inner loop:
enemy[i,j] = obj;
Related
I have some code that is supposed to create a grid of voxels, and then rooms out of those voxels. However, a lot of the rooms still overlap, even when I wrote code specifically to prevent this from happening. Would anybody be able to tell me what's going?
I'm using an out variable for the first time, and I think that's part of the issues.
Basically, the code is supposed to create a list of voxels to turn into a room, turn them into a room, and repeat for the total number of rooms. If a voxel in the list is found to already be a room, it should clear the list and not create a room.
All the relevant code is below, you should be able to plug this into a new Unity project and see for yourself everything, complete with Gizmos.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum VoxelType{
Undefined = 0,
OpenRoom,
OpenHall,
Wall,
Buffer,
}
[System.Serializable]
public class Voxel {
public Vector3 position;
public VoxelType voxelType;
public int voxelX;
public int voxelY;
public Voxel(Vector3 newPosition, int x, int y, VoxelType newType = 0){
position = newPosition;
voxelType = newType;
voxelX = x;
voxelY = y;
}
}
public class VoxelManager : MonoBehaviour
{
public Vector2 mapSize;
public float voxelSize;
public int rooms;
public int maxRoomHeight;
public int minRoomHeight;
public int maxRoomWidth;
public int minRoomWidth;
List<Voxel> voxels = new List<Voxel>();
void OnDrawGizmosSelected(){
foreach(Voxel voxel in voxels){
if (voxel.voxelType == VoxelType.OpenRoom){
Gizmos.color = Color.red;
} else {
Gizmos.color = Color.blue;
}
switch (voxel.voxelType){
case VoxelType.Undefined:
Gizmos.color = Color.blue;
break;
case VoxelType.Wall:
Gizmos.color = Color.magenta;
break;
case VoxelType.OpenRoom:
Gizmos.color = Color.red;
break;
}
Gizmos.DrawCube(voxel.position, Vector3.one * voxelSize *.995f);
}
}
void Start(){
voxelSpawn = prefab;
for(int i = 0; i < mapSize.x; i++){
for (int j = 0; j < mapSize.y; j++){
float xLocation = voxelSize * i;
float zLocation = voxelSize * j;
float centerXLocation = (xLocation - (mapSize.x * voxelSize) / 2f) + voxelSize / 2f;
float centerZLocation = (zLocation - (mapSize.y * voxelSize) / 2f) + voxelSize / 2f;
string voxelName = i + "x" + j;
Vector3 center = new Vector3(centerXLocation, voxelSize / 2, centerZLocation);
voxels.Add(new Voxel(center, i, j));
}
}
for (int i = 0; i < rooms; i++){
List<Voxel> roomVoxels = new List<Voxel>();
int generatedHeight = Random.Range(minRoomHeight, maxRoomHeight);
int generatedWidth = Random.Range(minRoomWidth, maxRoomWidth);
int horizontalRoomIndex = Random.Range(0, ((int)mapSize.x));
int verticalRoomIndex = Random.Range(0, ((int)mapSize.y));
bool occupied = false;
foreach(Voxel voxel in voxels){
if(voxel.voxelX == horizontalRoomIndex && voxel.voxelY == verticalRoomIndex){
for (int j = 0; j < generatedHeight; j++){
for (int k = 0; k < generatedWidth; k++){
roomVoxels.Add(GetVoxelAtPosition(horizontalRoomIndex + j, verticalRoomIndex + k, out occupied));
}
}
}
}
if(occupied == true){
Debug.Log("Room occupied; unable to create! Clearing the current list to be turned into a room!");
roomVoxels.Clear();
i--;
} else {
Debug.Log("Making a room!");
foreach(Voxel roomVoxel in roomVoxels){
roomVoxel.voxelType = VoxelType.OpenRoom;
}
}
}
}
Voxel GetVoxelAtPosition(int x, int y, out bool occupied){
foreach(Voxel voxel in voxels){
if(voxel.voxelX == x && voxel.voxelY == y){
if (voxel.voxelType == VoxelType.Undefined){
occupied = false;
return voxel;
} else {
Debug.Log("Voxel occupied!");
occupied = true;
return voxels[0];
}
}
}
occupied = true;
return voxels[0];
}
}
I'm making a little boxing game in Unity. I designed a function (attackOverdrive) to accept a whole slew of arguments and then cause the enemy boxer to make attacks based on the data fed into the function. I have another class which sets a bunch of variables and then I read those values, assign them to local variables, and then run my function. Thing is that the code in my loop is executing on all members of the array instead of iterating over them one at a time, resulting in the enemy throwing many attacks at once that were intended to be thrown in sequence. The punchDelayTimer is supposed to count up to a limit, then send a message to instantiate an attack, then loop back and do it again.
I'm going nuts, tried all the different types of iterators and got nowhere. I'm new to C#, no idea what I've done wrong here. The function that sets the variables in the other script is at the bottom of the code block.
using UnityEngine;
using System.Text.RegularExpressions;
public class overdrivePiano : MonoBehaviour
{
private GameObject enemyBoxer;
private pianoRoll theRoll;
private string theCoordMod;
private string[] thePatterns;
private int howMany;
private float thePunchDelay, thePatternDelay, theCoordModAmount, punchDelayTimer, patternDelayTimer;
private Vector2[] theCoords;
void Start()
{
enemyBoxer= GameObject.Find("enemyBoxer");
theRoll = gameObject.GetComponent<pianoRoll>();
punchDelayTimer = 0;
}
private void readRoll()
{
thePatterns = theRoll.patterns;
thePunchDelay = theRoll.punchDelay;
thePatternDelay = theRoll.patternDelay;
theCoords = theRoll.coords;
theCoordMod = theRoll.coordMod;
theCoordModAmount = theRoll.modAmount;
}
public void onSwitch()
{
theRoll.SendMessage("firstVerse");
readRoll();
attackOverdrive(thePatterns, thePunchDelay, thePatternDelay, theCoords);
}
public void attackOverdrive(string[] patterns, float punchDelay, float patternDelay, Vector2[] coords, string coordMod = "none", float modAmount = 0)
{
for(int i = 0; i < patterns.Length; i++)
{
if (patterns[i] == "triangle")
{
int j = 0;
Vector2[] triangleVectors = new Vector2[] {new Vector2(coords[i].x, coords[i].y + 0.75f), new Vector2(coords[i].x - 0.75f, coords[i].y - 0.75f), new Vector2(coords[i].x + 0.75f, coords[i].y - 0.75f)};
do
{
if (punchDelayTimer <= punchDelay)
{
punchDelayTimer += Time.deltaTime; //Debug statement here for i and j both print 0
}
else
{
enemyBoxer.SendMessage("createAttack", triangleVectors[j]);
punchDelayTimer = 0; //Debug statement here outputs 9 statements for the value of i: 0,0,0,1,1,1,2,2,2
j += 1; //Debug statement here outputs 9 statements for the value of j: 0,1,2,0,1,2,0,1,2
}
} while (j < 3);
}
else if (patterns[i] == "square")
{
}
else if (patterns[i] == "circle")
{
}
else if ("verticalLine".CompareTo(patterns[i]) == -1)
{
var result = Regex.Match(patterns[i], #"\d+$", RegexOptions.RightToLeft);
if (result.Success)
{
//Debug.Log(result.Value);
}
}
}
}
}
private void firstVerse()
{
patterns = new string[] {"triangle", "triangle", "triangle", "singleRandom", "singleRandom", "verticalLine5"};
coords = new Vector2[] {new Vector2(-1.3f, 2.5f), new Vector2(0f, -2.5f), new Vector2(1.3f, 2.5f), new Vector2(0,0), new Vector2(0,0), new Vector2(0, 4f)};
punchDelay = 0.5f;
patternDelay = 0.5f;
}
Turns out the float timer was the problem. It was iterating through the array properly, but it was all happening on the same frame. I was able to solve the problem by changing attackOverdrive's return type to IEnumerator and then calling it as a coroutine from OnSwitch. I removed the if statement inside the triangle part of the loop and replaced the float timer with Unity's WaitForSeconds class. Here are the functions that changed in case anyone is interested:
public void onSwitch()
{
theRoll.SendMessage("firstVerse");
readRoll();
StartCoroutine(attackOverdrive(thePatterns, thePunchDelay, thePatternDelay, theCoords));
}
public IEnumerator attackOverdrive(string[] patterns, float punchDelay, float patternDelay, Vector2[] coords, string coordMod = "none", float modAmount = 0)
{
for(int i = 0; i < patterns.Length; i++)
{
if (patterns[i] == "triangle")
{
int j = 0;
Vector2[] triangleVectors = new Vector2[] {new Vector2(coords[i].x, coords[i].y + 0.5f), new Vector2(coords[i].x - 0.5f, coords[i].y - 0.5f), new Vector2(coords[i].x + 0.5f, coords[i].y - 0.5f)};
do
{
enemyBoxer.SendMessage("createAttack", triangleVectors[j]);
yield return new WaitForSeconds(punchDelay);
punchDelayTimer = 0f;
j += 1;
} while (j < 3);
}
else if (patterns[i] == "square")
{
}
else if (patterns[i] == "circle")
{
}
else if ("verticalLine".CompareTo(patterns[i]) == -1)
{
var result = Regex.Match(patterns[i], #"\d+$", RegexOptions.RightToLeft);
if (result.Success)
{
Debug.Log(result.Value);
}
}
else if ("single".CompareTo(patterns[i]) == -1)
{
var result = Regex.Match(patterns[i], #"\d+$", RegexOptions.RightToLeft);
if (result.Success)
{
Debug.Log(result.Value);
}
}
}
}
Thanks to rage_co on the Unity forums, who showed me this fix =]
I have this script and I need to assign a different value to vertices every frame but I can't convert Vector3 to Vector3[], I tried In many ways but I am not able to solve this
public Vector3 calculatePointOnPlanet(Vector3 directions)
{
float firstLayerValue = 0;
float elevation = 0;
directions = directions;
if (noiseFilters.Length > 0)
{
firstLayerValue = noiseFilters[0].Evaluate(directions);
if (settings.noiseLayers[0].enabled)
{
elevation = firstLayerValue;
}
}
for (int i = 0; i < noiseFilters.Length; i++)
{
if (settings.noiseLayers[i].enabled)
{
float mask = (settings.noiseLayers[i].useFirstLayerAsMask) ? firstLayerValue : 1;
elevation += noiseFilters[i].Evaluate(directions) * mask;
}
}
return directions * settings.radius * (1 + elevation);
}
public void a(Vector3[] vertices, Vector3[] directions)
{
for (int i = 0; i < noiseFilters.Length; i++)
{
vertices[i] = calculatePointOnPlanet(directions);
}
}
You probably forgot the index
// |
// V
vertices[i] = calculatePointOnPlanet(directions[i]);
Note btw that
directions = directions
does absolutely nothing
I've been making this enemy generator, which is called on Start() and it freezes most of the time. Sometimes it let's to play once but as soon as I hit play second time, it freezes, unity needs to be restarted. Standalone version doesn't fix the problem. Logs last lines:
UnityEngine.DebugLogHandler:Internal_Log(LogType, LogOption, String, Object)
UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
UnityEngine.Logger:Log(LogType, Object)
UnityEngine.Debug:Log(Object)
EnemyGenerator:Start() (at Assets\Scripts\Utils\EnemyGenerator.cs:23)
(Filename: Assets/Scripts/Utils/EnemyGenerator.cs Line: 23)
Here is my script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyGenerator : MonoBehaviour
{
public List<GameObject> camps;
public List<GameObject> enemies;
int threshold;
public List<GameObject> campAreas;
public List<GameObject> enemyAreas;
public int campsToSpawn;
public int enemiesPerAreaMin;
public int enemiesPerAreaMax;
void Start()
{
for (int i = 0; i < campsToSpawn; i++)
{
threshold = Random.Range(0, camps.Count - 1);
Debug.Log(threshold);
var campPlace = Random.Range(0, campAreas.Count - 1);
var camp = Instantiate(camps[threshold], campAreas[campPlace].transform.position, Quaternion.identity);
if (camp != null)
{
campAreas.RemoveAt(campPlace);
}
}
Debug.Break();
bool canSpawn = true;
for (int i = 0; i < enemyAreas.Count; i++)
{
threshold = Random.Range(0, enemies.Count - 1);
List<GameObject> enemiesSpawned = new List<GameObject>();
var enemyCount = Random.Range(enemiesPerAreaMin, enemiesPerAreaMax);
for (int j = 0; j < enemyCount; j++)
{
var pos = new Vector3(Random.Range(enemyAreas[i].GetComponent<BoxCollider>().bounds.min.x, enemyAreas[i].GetComponent<BoxCollider>().bounds.max.x), enemyAreas[i].transform.position.y,
Random.Range(enemyAreas[i].GetComponent<BoxCollider>().bounds.min.z, enemyAreas[i].GetComponent<BoxCollider>().bounds.max.z));
if (enemiesSpawned.Count == 0)
{
GameObject en = null;
enemiesSpawned.Add(en = Instantiate(enemies[threshold], pos, Quaternion.identity));
}
else
{
for (int x = 0; x < enemiesSpawned.Count; x++)
{
if (Vector3.Distance(enemiesSpawned[x].transform.position, pos) < 3f)
{
canSpawn = false;
}
}
if (!canSpawn)
{
j--;
}
else
{
enemiesSpawned.Add(Instantiate(enemies[threshold], pos, Quaternion.identity));
}
}
}
Debug.Break();
}
}
}
I tried to find something that would actually loop forever, but I don't think that's the problem. Wondering if it could be performance leak?
Thank you in advance.
Sry I though it was an issue with Debug.Break();
this is probably the problem
{
for (int x = 0; x < enemiesSpawned.Count; x++)
{
if (Vector3.Distance(enemiesSpawned[x].transform.position, pos) < 3f)
{
canSpawn = false;
}
}
if (!canSpawn)
{
j--;
}
else
{
enemiesSpawned.Add(Instantiate(enemies[threshold], pos, Quaternion.identity));
}
}
You getting a random position inside a certain bounds. If the position is too close u don't spawn and --j; to retry a new position. So if for some reason the bounds are wrong or barely engouth you might be running that function for a very long time.
A quick test is to add a counter before your j for loop.
int debugCounter = 0;
for (int j = 0; j < enemyCount; j++)
{
//at the very top
++debugCounter;
if(debugCounter > 100)//or a bigger number
{
Debug.Log("Too many attemps");
break;
}
...
This is a homework problem(Do not copy, copy is not smart): We need to draw a 2d turkey in unity 2d using Verlet method to update the positions of the vertices. However, We don't know the forces involved to trace the Turkey. Here is a picture of the Turkey. Is there a trick?
Here is the codes that we started:
public class GenerateTurkeys : MonoBehaviour
{
LineRenderer lineRenderer = gameObject.AddComponent<LineRenderer>();
// Start is called before the first frame update
int numberOfTurkeys;
int NUM_PARTICLES;
float fTimeStep;
Vector3[] m_position = new Vector3[NUM_PARTICLES];
Vector3[] m_acceleration = new Vector3[NUM_PARTICLES];
Vector3[] m_oldPosition = new Vector3[NUM_PARTICLES];
void Start()
{
NUM_PARTICLES = 100;
numberOfTurkeys = 0;
}
// Verlet integration step void ParticleSystem::
Verlet()
{
for (int i=0; i<NUM_PARTICLES; i++)
{
Vector3 x = m_position[i];
Vector3 temp = x;
Vector3 oldx = m_oldPosition[i];
Vector3 a = m_acceleration[i];
x += x-oldx+a* fTimeStep*fTimeStep;
oldx = temp;
}
}
void DrawLine(float[] heights)
{
LineRenderer lineRenderer = GetComponent<LineRenderer>();
var t = Time.time;
for (int i = 0; i < NUM_PARTICLES; i++)
{
lineRenderer.SetPosition(i, );
}
}
// Update is called once per frame
void Update()
{
}
}
I'm not sure that I correctly decided, due to the fact that the contour does not match, but in the solution the speed is exactly iterative. I think this is a small error of physics in Unity, Because if you use the resulting accelerations, you can draw the graph very accurately.
Here is code:
public class Drawer : MonoBehaviour
{
[SerializeField]
private Transform[] m_Dots;
private Rigidbody2D m_Dot;
private Vector2[] m_Acceler;
float deltaT = 0.5f;//for example
private void Start()
{
m_Acceler = GetAcceler();
var go = new GameObject("Tracer");
var tr = go.AddComponent<TrailRenderer>();
tr.widthMultiplier = 0.1f;
tr.time = 50f;
m_Dot = go.AddComponent<Rigidbody2D>();
m_Dot.bodyType = RigidbodyType2D.Kinematic;
m_Dot.gravityScale = 0;
StartCoroutine(VerletCoroutine());
}
private Vector2[] GetAcceler()
{
Vector2[] result = new Vector2[m_Dots.Length];
float T = deltaT;
int len = m_Dots.Length;
result[0] = An(m_Dots[1].position, m_Dots[0].position, m_Dots[0].position, T);
for (int i = 1 ; i < len - 1 ; i++, T += deltaT)
{
result[i] = An(m_Dots[i + 1].position, m_Dots[i].position, m_Dots[i].position, T);
}
result[len - 1] = An(m_Dots[0].position, m_Dots[len - 1].position, m_Dots[len - 1].position, T);
return result;
}
private Vector2 An(Vector2 Xnext, Vector2 Xn, Vector2 Xprev, float t)
{// a[n] = (x[n+1] - 2*x[n]+x[n-1])/t^2
return (Xnext - 2 * Xn + Xprev) / t * t;
}
private IEnumerator VerletCoroutine()
{
m_Dot.transform.position = m_Dots[0].position;
Vector2 Vprev = Vector2.zero;
int len = m_Acceler.Length - 1;
float t = 0;
int i = 0;
while (true)
{
t += Time.deltaTime;
if (t >= deltaT)
{
i++;
if (i > len)
{
break;
}
t = 0;
Vprev = Vector2.zero;
}
Vprev = Vnext(Vprev, m_Acceler[i], m_Acceler[i], t);
m_Dot.velocity = Vprev;
yield return new WaitForEndOfFrame();
}
m_Dot.velocity = Vector3.zero;
yield return null;
}
private Vector2 Vnext(Vector2 Vn, Vector2 Anext, Vector2 An, float t)
{//v[n+1]= v[n]+0,5(a[n+1] +a[n]) * t
var v = Vn + 0.5f * (Anext + An) * t;
return v;
}
}