I've made boids in unity but when trying to render a 1000 of them the performance is really bad, in my update function i use Physics.OverlapCircleAll to check all surroundiing boids. Is there any way to do this more optimized? Here is my update function:
void Update()
{
Collider2D[] hitColliders = Physics2D.OverlapCircleAll(Position, radius,layerMask.value);
List<Boid> boids = hitColliders.Select(o => o.GetComponent<Boid>()).ToList();
boids.Remove(this);
Flock(boids.ToArray());
}
Absolutely! Physics.OverlapCircleAll creates a lot of garbage every time it is called. What you're looking for is Physics.OverlapCircleNonAlloc, which will not create any garbage as it uses a buffer:
Collider2D[] hitsBuffer = new Collider2D[30]; //limit the amout of possible boid interations
void Update()
{
int numHits = Physics2D.OverlapCircleNonAlloc(Position, radius, hitsBuffer, layerMask.value);
Flock(hitsBuffer,numHits);
}
void Flock(Collider2D[] hitsBuffer, int numHits){
for(int i = 0; i < numHits; i++){
var boid = hitsBuffer[i].GetComponent<Boid>();
if(boid == this)
continue;
//flocking algorith here
}
}
Note how in the above code no additional arrays are created each frame, which is quite expensive. To check how much time is being spent where check out the Profiler:
Orange is 'Physics', working out the overlaps
Cyan is 'Scripts', calcuations in code, ie the flocking algorithm
Dark green is 'GarbageCollector', handling arrays created and destroyed each frame
PS If not already, ensure that the boids are using a CircleCollider2D, this is the easiest for Unity to calculate.
PPS You may want to double check that if(boid == this) actually gets called. I thought that Physics.Overlap... ignores this collider.
Related
I want to move a rigidBody2D for a fixed amount of time with a fixed amount of velocity, when the player presses a button. I am calling the method in FixedUpdate() and the results are not consistent. Sometimes the rigidBody will travel more and sometimes less (Here is a relevant part of my code how i went about do this)
void Update()
{
GetPlayerInput();
}
private void FixedUpdate()
{
GroundCheck();
ApplayNormalPlayerMovement();
ApplyMove(); // This is the method of interest - I tried calling this in Update() with Time.DeltaTime - still inconsistent results.
MoveCooldownCounter(); // I tried calling this in Update() - inconsistent results also
}
IEnumerator MoveRB()
{
savedVelocityX = rb.velocity.x;
rb.gravityScale = 0;
savedXinput = xInput;
while (moveTimer >= 0)
{
if (xInput != 0)
xInput = 0;
cc.sharedMaterial = noFriction;
if (facingRight)
{
rb.velocity = new Vector2(moveSpeed, .0f); // I tried multiplying the moveSpeed by Time.DeltaTime and FixedDeltaTime - still inconsistent results.
} else
{
rb.velocity = new Vector2(-moveSpeed, .0f);
}
moveTimer -= Time.fixedDeltaTime;
yield return null;
}
moveTimer= moveDuration;
rb.gravityScale = gravity;
rb.velocity = new Vector2 (savedVelocityX, .0f);
xInput = savedXinput;
moveCooldownInternal -= Time.deltaTime; // I tried changing this to a specific float - still inconsistent results in physics..
}
private void MoveCooldownCounter()
{
if (moveCooldownInternal != moveCooldown)
{
moveCooldownInternal -= Time.deltaTime;
if (moveCooldownInternal <= 0)
{
moveCooldownInternal = moveCooldown;
}
if (moveCooldownInternal == moveCooldown && isGrounded)
canMove = true;
}
}
private void ApplyMove()
{
if (b_Fire3 && canMove)
{
StartCoroutine("MoveRB");
canMove= false;
}
}
Side note: right now i experience player input loss on occasions because i call this ApplyMove() in FixedUpdate() (will have to find a workaround for that as well - naturally if I can call the ApplyMove in Update() and get consistent results than this issue would be fixed).
Pleas excise my newbiness, i am trying to learn :)
Thank you in advance!
Add comment
Unity is a game engine. All game engines are based on the game loop - however Unity somewhat hides if from you, behind the 3 Update functions.
Not all parts are equally important. Drawing is generally skipped when the GPU is not ready or the FPS are capped. And Drawing and physics are generally calculated independantly of one another (unless you really messed up the development).
With Physics and Animations, you generally want to stuff to be consistent to the Game Tick Counter, not real time. All movement of game pieces and all animation should count in game ticks. Real Time is a nice figure and all, but ticks can have wildly different processing times. Especially since the CPU side drawing work also has to run in the same thread.
Just figure out your maximum ticks/second. And if you want to run something to run for "roughly" 10 ingame seconds, you multiply that value. It does not mater if the game on this machine needs 2 seconds for 200 ticks or 2 minutes for 200 ticks - with the number of ticks, the results will be consistent (unless the math itself is inprecise, like floating point math).
If it's not multiplayer game, it does not have to be hackproof and thus easiest fix is to capture the first frame of the button pressed and apply the force only on that frame, because of that we have to capture input on the Update function and also there immadietly apply the force even though it's suggested to do physics stuff in fixed update, but if you use delta time in update function for calculation it should be fine!
Now I think you know how to do that but I'll write it anyway we can either use apply force or set the velocity,
void Update()
{
if(Input.GetKeyDown("w"))
rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y + 5f*Time.deltaTime);
}
Note: I didn't test it but it should be allright since GetKeyDown returns true only on the first frame when the button was pressed,
If it was just GetKey and in case of jump we would check the grounding of player, we could be during jump and still be grounded on the second frame even if we jumped because of not really precise ground checking.
PS. Use fixedDeltaTime in fixedUpdate method!
I've been raking my brain for the last day on how to calculate a full rotation with how to count a full rotation of an object along the X axis thats using the base circular drive from SteamVR.
I thought a simple 3d cube, with the mesh turned off in the the path of the rotation with collision code on it would be a barebone way of doing it, but it doesn't even seem to be registering the detection when the object hits the placed cubes, and i know its not because of me being stupid, as its recycled code from a working part of the project.
Below i have a small piece of code that basically detects when the object has reached the end of the rotation, and then increments the Count by one.
My main problem is that sometimes it manages to clock more than once, and if you can find the right spot, you can just keep it there and it'll keep on adding the count up by. Im wondering how i can stop it and increment only by one, until another full rotation has been made?
EDIT: to be more clear in case there is any confusion, Once the angle is clocked in between 359 and 360, i want it to increment once, whereas currently if you get the angle to sit anywhere in between 359-360 it will carry on adding one to the rotation count, despite no full rotation having been made, so im trying to figure out how to make my code only increment once, and once it does increment once it resets the position to zero, so therefore no more Increments can happen. It's a crank mechanism in VR, along the X axis.
Any help is appreciated, Thanks!
float Test;
float RotationCount = 0;
// Start is called before the first frame update
void Start()
{
// Test = transform.localRotation.eulerAngles.x;
}
// Update is called once per frame
void Update()
{
if (Test > 359 && Test < 360)
{
Debug.Log("Clocked");
count();
}
else
{
// Debug.Log("Nope");
}
if (Test == 0)
{
Debug.Log("Yes");
}
Test = transform.localRotation.eulerAngles.x;
}
void count()
{
RotationCount++;
}
sometimes it manages to clock more than once, and if you can find the right spot, you can just keep it there and it'll keep on adding the count up by.
well in
if (Test > 359 && Test < 360)
{
Debug.Log("Clocked");
count();
}
what happens if your angle is e.g. 359.5?
It is very difficult to just take a current rottaion and know whether it was turned more or less than a certain angle.
I'ld rather store the last rotation, compare it to the current one and add the difference to a variable. Than if the variable exceeds 360 a full rotation was done. Since I also don't like to calculate anything with Quaternion and eulerAngles, way simplier is to use the methods provided by Vector3.
For the local rotation around X (== transform.right) I would use the angle between the current and the last transfrm.up vector.
Something like
public class RotationCheck : MonoBehaviour
{
public int RotationCount;
public float rotatedAroundX;
public Vector3 lastUp;
public UnityEvent On3TimesRotated;
private void Awake()
{
rotatedAroundX = 0;
// initialize
lastUp = transform.up;
}
private void Update()
{
var rotationDifference = Vector3.SignedAngle(transform.up, lastUp, transform.right);
rotatedAroundX += rotationDifference;
if (rotatedAroundX >= 360.0f)
{
Debug.Log("One positive rotation done", this);
RotationCount++;
rotatedAroundX -= 360.0f;
}
else if (rotatedAroundX <= -360.0f)
{
Debug.Log("One negative rotation done", this);
RotationCount--;
rotatedAroundX += 360.0f;
}
// update last rotation
lastUp = transform.up;
// check for fire the event
if (RotationCount >= 3)
{
On3TimesRotated?.Invoke();
RotationCount = 0;
}
}
}
You can use a UnityEvent to get the same thing the Button uses for onClick so you can reference callbacks there via the inspector.
BUT if you don't care about the single rotations but actually only wnat the final RotationCount >= 3 I would actually use
private void Update()
{
var rotationDifference = Vector3.SignedAngle(transform.up, lastUp, transform.right);
rotatedAroundX += rotationDifference;
RotationCount = Mathf.RoundToInt(rotatedAroundX / 360.0f);
// update last rotation
lastUp = transform.up;
// check for fire the event
if (RotationCount >= 3)
{
On3TimesRotated?.Invoke();
RotationCount = 0;
rotatedAroundX = 0;
}
}
which directly reduces the value by one if rotated under the 360 mark instead of waiting for a full negative rotation
*as you can see instead of reaching 3 the RotationCount is reset to 0. This is where the On3TimesRotated event is/would get fired.
Please look at example - http://www.mathplayground.com/mancala.html
Can anyone suggest the logic to :
1) spawn objects at positions
2) Pick up all objects on click and distribute them one by one.
3) Is it better to create all objects or instantiate them on the fly. ?
I tried code below but it just instantiates all objects at once.
if (HoleHandler.gemCount_Hole1_Update_Flag == true)
{
foreach (GameObject g in gemList1)
{
Destroy(g);
//want to add a time delay of 2 secs here
}
if (gemCount_Hole1 > 0)
{
for (int i = 0; i < gemCount_Hole1; i++)
{
int Gem_prefabIndex = UnityEngine.Random.Range(0, 9);
gemList1.Add(Instantiate(Gem_prefabList[Gem_prefabIndex], new Vector2((xPos_Hole1 + (Random.Range(-20, 20))) * 2.0F, (-229 + (20 * i))), Quaternion.identity));
}
}
}
I'm not 100% sure of what you're trying to achieve but I will answer as best I can.
For a start, any gameobject you are going to be instantiating (spawning) at run time should ideally be done so from a prefab.
Secondly, to spawn them at random intervals you want to be checking if they should be spawned at different time frames. This can be achieved through a co-routine or the Update function. I would recommend Update if this is new to you.
Update is called every frame.. and it's with this that you can achieve timed events. You can use a variety of helper methods to determine the time since the last frame or the real time elapsed.
For example
public class MyGameObject : Monobehaviour {
void Start() {
//This is called first, use it to set up whatever you want.
}
void Update() {
//This will be called every frame.
//Each frame or time lapse will determine if I should spawn
// a new gameobject.
}
}
Update
After looking at the game you have linked in your post I can offer the following advice.
Something like the following may point you in the right direction.
public int[] gemsInCups = new int [] {4,4,4,4,4,4,0,4,4,4,4,4,4,0};
public void Distribute(int position){
int gems = gemsInCups[position];
for(int i = position + 1; gems > 0; i++){
gemsInCups[position] ++;
gems --;
//Check the end of the array has not been reached.
//If it has, start distributing again from the first position provided
// there are still gems to distribute.
}
}
You will need some additional logic to finish this.
What you should remember is, I usually find it much more manageable keeping my data and my view (gameobjects) under different scopes... but the view will change to reflect the data and does not directly alter it. Now you know how many gems there are in each cup, you can simply update this each frame.
I need to know the number of enemyCans left, so when there are none remaining I can trigger my win condition. Right now, I have a script on my explosion component that functions properly meaning it removes any Enemy Cannon that comes within a certain distance of an explosion.
enemyCans is declared and assigned the following value in the start() method, accordingly:
GameObject[] enemyCans; //Before start
void Start() {
enemyCans = GameObject.FindGameObjectsWithTag("EnemyCannon");
}
Then, I use that value in a method called CannonKiller() which iterates through the Enemy Cannon's transforms to check and see if the explosion comes near them. I'm sure this isn't the most elegant way of doing that, but the aforemention method is listed below:
void CannonKiller()
{
foreach(var cannon in GameObject.FindGameObjectsWithTag("EnemyCannon").Select(enemyCans => enemyCans.transform).ToArray())
{
foreach (var aCan in enemyCans)
{
float enemyDis = Vector3.Distance(cannon.position, transform.position);
if (enemyDis <= 4)
{
Destroy(aCan);
}
}
}
}
I would like to be able to have the ability to check and see in the nested foreach loops to see if the number of enemy is zero so I can call my finish method. I assumed something like this would work:
if (enemyCans == 0) //placed inside the foreach
{
finish("enemy");
}
but I was incorrect. How can i check to see if their are no remaining enemy cannons in order to call my finish method.
If possible, I'd suggest avoiding having too many nested foreach loops like that - although Jerry's answer does work, in the worst case you would basically have an O(n3) complexity algorithm, and it's a bit tough to read.
If you have colliders on all your turrets, then you should leverage the physics engine instead. The intent of your code will at least be much clearer if you use a method like Physics.OverlapSphere to identify the turrets hit by the explosion.
So adjusting CannonKiller() to destroy hit turrets and determine whether they all have been destroyed (but in an arguably neater way), your method might look like:
void CannonKiller()
{
// Grab colliders in vicinity of explosion
Collider[] hitColliders = Physics.OverlapSphere(transform.position, 4);
foreach (Collider hitCollider in hitColliders){
// Only act if collider belongs to an enemy cannon
if (hitCollider.gameObject.tag == "EnemyCannon"){
Destroy(hitCollider.gameObject);
// If there are no non-null references to cannon objects, they're all destroyed
if (enemyCans.FirstOrDefault(cannon => cannon != null) == null){
// Execute finishing code, then probably break
}
}
}
}
Since I saw you were already familiar with LINQ, I used it for the "all destroyed" check.
This may not be the best approach, but I think it's as good as it will get without heavily changing your implementation. (Having a manager class as Joe suggested is a good way to split up responsibilities between classes, and make your code more testable/maintainable - so definitely look into that, since it will scale much better as your project grows.)
Well, this code will work:
void CannonKiller()
{
foreach(var cannon in GameObject.FindGameObjectsWithTag("EnemyCannon").Select(enemyCans => enemyCans.transform).ToArray())
{
foreach (var aCan in enemyCans)
{
float enemyDis = Vector3.Distance(cannon.position, transform.position);
if (enemyDis <= 4)
{
Destroy(aCan);
bool allDestoyed = true;
foreach (GameObject o in enemyCans)
{
if (o != null && o != aCan)
{
allDestoyed = false;
break;
}
}
if (allDestoyed)
{
// Here you know all are destroyed
}
}
}
}
}
But I must say it is very ugly way of programing ;)
I have a compute shader and the C# script which goes with it used to modify an array of vertices on the y axis simple enough to be clear.
But despite the fact that it runs fine the shader seems to forget the first vertex of my shape (except when that shape is a closed volume?)
Here is the C# class :
Mesh m;
//public bool stopProcess = false; //Useless in this version of exemple
MeshCollider coll;
public ComputeShader csFile; //the compute shader file added the Unity way
Vector3[] arrayToProcess; //An array of vectors i'll use to store data
ComputeBuffer cbf; //the buffer CPU->GPU (An early version with exactly
//the same result had only this one)
ComputeBuffer cbfOut; //the Buffer GPU->CPU
int vertexLength;
void Awake() { //Assigning my stuff
coll = gameObject.GetComponent<MeshCollider>();
m = GetComponent<MeshFilter>().sharedMesh;
vertexLength = m.vertices.Length;
arrayToProcess = m.vertices; //setting the first version of the vertex array (copy of mesh)
}
void Start () {
cbf = new ComputeBuffer(vertexLength,32); //Buffer in
cbfOut = new ComputeBuffer(vertexLength,32); //Buffer out
csFile.SetBuffer(0,"Board",cbf);
csFile.SetBuffer(0,"BoardOut",cbfOut);
}
void Update () {
csFile.SetFloat("time",Time.time);
cbf.SetData(m.vertices);
csFile.Dispatch(0,vertexLength,vertexLength,1); //Dispatching (i think there is my mistake)
cbfOut.GetData(arrayToProcess); //getting back my processed vertices
m.vertices = arrayToProcess; //assigning them to the mesh
//coll.sharedMesh = m; //collider stuff useless in this demo
}
And my compute shader script :
#pragma kernel CSMain
RWStructuredBuffer<float3> Board : register(s[0]);
RWStructuredBuffer<float3> BoardOut : register(s[1]);
float time;
[numthreads(1,1,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
float valx = (sin((time*4)+Board[id.x].x));
float valz = (cos((time*2)+Board[id.x].z));
Board[id.x].y = (valx + valz)/5;
BoardOut[id.x] = Board[id.x];
}
At the beginning I was reading and writing from the same buffer, but as I had my issue I tried having separate buffers, but with no success. I still have the same problem.
Maybe I misunderstood the way compute shaders are supposed to be used (and I know I could use a vertex shader but I just want to try compute shaders for further improvements.)
To complete what I said, I suppose it is related with the way vertices are indexed in the Mesh.vertices Array.
I tried a LOT of different Blocks/Threads configuration but nothing seems to solve the issue combinations tried :
Block Thread
60,60,1 1,1,1
1,1,1 60,60,3
10,10,3 3,1,1
and some others I do not remember. I think the best configuration should be something with a good balance like :
Block : VertexCount,1,1 Thread : 3,1,1
About the closed volume: I'm not sure about that because with a Cube {8 Vertices} everything seems to move accordingly, but with a shape with an odd number of vertices, the first (or last did not checked that yet) seems to not be processed
I tried it with many different shapes but subdivided planes are the most obvious, one corner is always not moving.
EDIT :
After further study i found out that it is simply the compute shader which does not compute the last (not the first i checked) vertices of the mesh, it seems related to the buffer type, i still dont get why RWStructuredBuffer should be an issue or how badly i use it, is it reserved to streams? i cant understand the MSDN doc on this one.
EDIT : After resolution
The C# script :
using UnityEngine;
using System.Collections;
public class TreeObject : MonoBehaviour {
Mesh m;
public bool stopProcess = false;
MeshCollider coll;
public ComputeShader csFile;
Vector3[] arrayToProcess;
ComputeBuffer cbf;
ComputeBuffer cbfOut;
int vertexLength;
// Use this for initialization
void Awake() {
coll = gameObject.GetComponent<MeshCollider>();
m = GetComponent<MeshFilter>().mesh;
vertexLength = m.vertices.Length+3; //I add 3 because apparently
//vertexnumber is odd
//arrayToProcess = new Vector3[vertexLength];
arrayToProcess = m.vertices;
}
void Start () {
cbf = new ComputeBuffer(vertexLength,12);
cbfOut = new ComputeBuffer(vertexLength,12);
csFile.SetBuffer(0,"Board",cbf);
csFile.SetBuffer(0,"BoardOut",cbfOut);
}
// Update is called once per frame
void Update () {
csFile.SetFloat("time",Time.time);
cbf.SetData(m.vertices);
csFile.Dispatch(0,vertexLength,1,1);
cbfOut.GetData(arrayToProcess);
m.vertices = arrayToProcess;
coll.sharedMesh = m;
}
}
I had already rolled back to a
Blocks VCount,1,1
Before your answer because it was logic that i was using VCount*VCount so processing the vertices "square-more" times than needed.
To complete, you were absolutely right the Stride was obviously giving issues could you complete your answer with a link to doc about the stride parameter? (from anywhere because Unity docs are VOID and MSDN did not helped me to get why it should be 12 and not 32 (as i thought 32 was the size of a float3)
so Doc needed please
In the mean time i'll try to provide a flexible enough (generic?) version of this to make it stronger, and start adding some nice array processing functions in my shader...
I'm familiar with Compute Shaders but have never touched Unity, but having looked over the documentation for Compute Shaders in Unity a couple of things stand out.
The cbf and cbfOut ComputeBuffers are created with a stride of 32 (bytes?). Both your StructuredBuffers contain float3s which have a stride of 12 bytes, not 32. Where has 32 come from?
When you dispatch your compute shader you're requesting a two-dimensional dispatch (vertexLength,vertexLength, 1) but you're operating on a 1D array of float3s. You will end up with a race condition where many different threads think they're responsible for updating each element of the array. Although awful for performance, if you want a thread group size of [numthreads(1,1,1)] then you should dispatch (vertexLength, 1, 1) numbers of waves/wavefronts when calling Dispatch (ie, Dispatch (60,1,1) with numThreads(1,1,1)).
For best/better performance the number of threads in your thread group / wave should at least be a multiple of 64 for best efficiency on AMD hardware. You then need only dispatch ceil(numVertices/64) wavefronts and then simply insert some logic into the shader to ensure id.x is not out of bounds for any given thread.
EDIT:
The documentation for the ComputeBuffer constructor is here: Unity ComputeBuffer Documentation
While it doesn't explicitly say "stride" is in bytes, it's the only reasonable assumption.