void Update() { // only do an update if the data has been initialized if (!positions.IsCreated) { return; } var deltaTime = Time.deltaTime; // FORCE ACCUMILATION // First off lets apply gravity to all the object in our scene that arnt currently asleep var gravityJob = new GravityJob() { DeltaTime = deltaTime, Gravity = gravity, Velocities = velocities, Sleeping = sleeping }; var gravityDependency = gravityJob.Schedule(objectCount, 32); // TODO accumilate other forces here! gravity is just the simplest force to apply to our objects, // but theres no limit on the kind of forces we can simulate. We could add friction, air resistnace, // constant acceleration, joints and constrainsts, boyancy.. anything we can think of that can effect // the velocity of an object can have its own job scheduled here. // INTERGRATION AND COLLISION // Create temporary arrays for the raycast info. Lets use the TempJob allocator, // this means we have to dispose them when the job has finished - its short lived data var raycastCommands = new NativeArray <RaycastCommand>(objectCount, Allocator.TempJob); var raycastHits = new NativeArray <RaycastHit>(objectCount, Allocator.TempJob); // Lets schedule jobs to do a collision raycast for each object. One job Prepare s all the raycast commands, // the second actually does the raycasts. var setupRaycastsJob = new PrepareRaycastCommands() { DeltaTime = deltaTime, Positions = positions, Raycasts = raycastCommands, Velocities = velocities }; var setupDependency = setupRaycastsJob.Schedule(objectCount, 32, gravityDependency); // BUG| WORKAROUND - Checked against Unity 2018.1b8 // BUG| RaycastCommand seems to be ignoring any passed in dependencies. We have to explicity wait here. // BUG| This has been reported to Unity and fixed. When the fix appears in public betas this can be removed. setupDependency.Complete(); // BUG| END WORKAROUND var raycastDependency = RaycastCommand.ScheduleBatch(raycastCommands, raycastHits, 32, setupDependency); // Now we know if there is a collision along our velocity vector, its time to integrate the velocity into // our objects for the current timeset. var integrateJob = new IntegratePhysics() { DeltaTime = deltaTime, Positions = positions, Velocities = velocities, Sleeping = sleeping, Hits = raycastHits }; var integrateDependency = integrateJob.Schedule(objectCount, 32, raycastDependency); // finally, respond to any collisions that happened in the lsat update step. var collisionResponeJob = new CalculateCollisionResponse() { Hits = raycastHits, Velocities = velocities, Sleeping = sleeping }; var collisionDependency = collisionResponeJob.Schedule(objectCount, 32, integrateDependency); // Now the physics is done, we need to create a drawing matrix for every object. This simple demo dosnt // implment roation, so only the translation values in the matrix reallllly matter. var renderMatrixJob = new CalculateDrawMatricies() { positions = positions, renderMatrices = renderMatrices }; var matrixJob = renderMatrixJob.Schedule(objectCount, 32, collisionDependency); // All the jobs we want to execute have been scheduled! By calling .Complete() on the last job in the // chain, Unity makes the main thread help out with scheduled jobs untill they are all complete. // then we can move on and use the data caluclated in the jobs safely, without worry about data being changed // by other threads as we try to use it - we *know* all the work is done matrixJob.Complete(); // make sure we dispose of the temporary NativeArrays we used for raycasting raycastCommands.Dispose(); raycastHits.Dispose(); // Well, all the updating is done! lets actually issue a draw! Graphics.DrawMeshInstanced(mesh, 0, material, renderMatrices.ToArray()); // DEBUG 1 - draw red lines showing object velocity //for (int i = 0; i < objectCount; i++) //{ /// Debug.DrawLine(positions[i], positions[i] + velocities[i], Color.red, 0.016f, true); //} // DEBUG 2 - draw a trail for a few objects, really helps to visualize the bounce! //var duration = 01f; //Debug.DrawLine(positions[0], positions[0] + velocities[0], Color.red, duration, true); //Debug.DrawLine(positions[200], positions[200] + velocities[200], Color.cyan, duration, true); //Debug.DrawLine(positions[400], positions[400] + velocities[400], Color.green, duration, true); //Debug.DrawLine(positions[600], positions[600] + velocities[600], Color.magenta, duration, true); //Debug.DrawLine(positions[800], positions[800] + velocities[800], Color.yellow, duration, true); //Debug.DrawLine(positions[1000], positions[1000] + velocities[1000], Color.blue, duration, true); //Debug.DrawLine(positions[100], positions[100] + velocities[100], Color.red, duration, true); //Debug.DrawLine(positions[300], positions[300] + velocities[300], Color.cyan, duration, true); //Debug.DrawLine(positions[500], positions[500] + velocities[500], Color.green, duration, true); //Debug.DrawLine(positions[700], positions[700] + velocities[700], Color.magenta, duration, true); //Debug.DrawLine(positions[900], positions[900] + velocities[900], Color.yellow, duration, true); // finally lets respawn any object that has been asleep and stable for a while // TODO as of Unity 2018.1b6, NativeArray is the only native collection type. When // NativeQueue is included in the beta this last task can be jobified! for (int i = 0; i < objectCount; i++) { if (sleeping[i] > 15) { Respawn(i); } } }
void Update() { // only do an update if the data has been initialized if (!positions.IsCreated) { return; } sampler.Begin(); var deltaTime = Time.deltaTime; // FORCE ACCUMILATION // First off lets apply gravity to all the object in our scene that arnt currently asleep var gravityJob = new GravityJob() { DeltaTime = deltaTime, Gravity = gravity, Velocities = velocities, Sleeping = sleepingTimer }; var gravityDependency = gravityJob.Schedule(objectCount, 32); // TODO accumilate other forces here! gravity is just the simplest force to apply to our objects, // but theres no limit on the kind of forces we can simulate. We could add friction, air resistnace, // constant acceleration, joints and constrainsts, boyancy.. anything we can think of that can effect // the velocity of an object can have its own job scheduled here. // INTERGRATION AND COLLISION // Create temporary arrays for the raycast info. Lets use the TempJob allocator, // this means we have to dispose them when the job has finished - its short lived data var raycastCommands = new NativeArray <RaycastCommand>(objectCount, Allocator.TempJob); var raycastHits = new NativeArray <RaycastHit>(objectCount, Allocator.TempJob); // Lets schedule jobs to do a collision raycast for each object. One job Prepare s all the raycast commands, // the second actually does the raycasts. var setupRaycastsJob = new PrepareRaycastCommands() { DeltaTime = deltaTime, Positions = positions, Raycasts = raycastCommands, Velocities = velocities }; var setupDependency = setupRaycastsJob.Schedule(objectCount, 32, gravityDependency); var raycastDependency = RaycastCommand.ScheduleBatch(raycastCommands, raycastHits, 32, setupDependency); // Now we know if there is a collision along our velocity vector, its time to integrate the velocity into // our objects for the current timeset. var integrateJob = new IntegratePhysics() { DeltaTime = deltaTime, Positions = positions, Velocities = velocities, Sleeping = sleepingTimer, Hits = raycastHits }; var integrateDependency = integrateJob.Schedule(objectCount, 32, raycastDependency); // finally, respond to any collisions that happened in the lsat update step. var collisionResponeJob = new CalculateCollisionResponse() { Hits = raycastHits, Velocities = velocities, Sleeping = sleepingTimer }; var collisionDependency = collisionResponeJob.Schedule(objectCount, 32, integrateDependency); // Now the physics is done, we need to create a drawing matrix for every object. This simple demo dosnt // implment roation, so only the translation values in the matrix reallllly matter. var renderMatrixJob = new CalculateDrawMatricies() { positions = positions, renderMatrices = renderMatrices }; var matrixDependency = renderMatrixJob.Schedule(objectCount, 32, collisionDependency); // All the jobs we want to execute have been scheduled! By calling .Complete() on the last job in the // chain, Unity makes the main thread help out with scheduled jobs untill they are all complete. // then we can move on and use the data caluclated in the jobs safely, without worry about data being changed // by other threads as we try to use it - we *know* all the work is done matrixDependency.Complete(); // make sure we dispose of the temporary NativeArrays we used for raycasting raycastCommands.Dispose(); raycastHits.Dispose(); // lets schedule a job to figure out which objects are sleeping - this can run in the background whilst // we dispach the drawing commands for this frame. var sleepJob = new FindSleepingObjects() { Sleeping = sleepingTimer, SleepQueue = asleep.AsParallelWriter() }; var sleepDependancy = sleepJob.Schedule(objectCount, 32, matrixDependency); // lets actually issue a draw! renderMatrices.CopyTo(renderMatrixArray); // copy to a preallocated array, we would get garbage from ToArray() Graphics.DrawMeshInstanced(mesh, 0, material, renderMatrixArray); // DEBUG 1 - draw red lines showing object velocity //for (int i = 0; i < objectCount; i++) //{ /// Debug.DrawLine(positions[i], positions[i] + velocities[i], Color.red, 0.016f, true); //} // DEBUG 2 - draw a trail for a few objects, really helps to visualize the bounce! //var duration = 01f; //Debug.DrawLine(positions[0], positions[0] + velocities[0], Color.red, duration, true); //Debug.DrawLine(positions[200], positions[200] + velocities[200], Color.cyan, duration, true); //Debug.DrawLine(positions[400], positions[400] + velocities[400], Color.green, duration, true); //Debug.DrawLine(positions[600], positions[600] + velocities[600], Color.magenta, duration, true); //Debug.DrawLine(positions[800], positions[800] + velocities[800], Color.yellow, duration, true); //Debug.DrawLine(positions[1000], positions[1000] + velocities[1000], Color.blue, duration, true); //Debug.DrawLine(positions[100], positions[100] + velocities[100], Color.red, duration, true); //Debug.DrawLine(positions[300], positions[300] + velocities[300], Color.cyan, duration, true); //Debug.DrawLine(positions[500], positions[500] + velocities[500], Color.green, duration, true); //Debug.DrawLine(positions[700], positions[700] + velocities[700], Color.magenta, duration, true); //Debug.DrawLine(positions[900], positions[900] + velocities[900], Color.yellow, duration, true); // finally lets respawn any object that has been found to be asleep (ie not moved for 15 frames) // were going to respawn that object so there is a constant flow sleepDependancy.Complete(); for (int i = asleep.Count; i != 0; i--) { int index = asleep.Dequeue(); Respawn(index); } sampler.End(); }