GithubHelp home page GithubHelp logo

simplephysicsdemo's Introduction

Hi there ๐Ÿ‘‹ Buy Me a Coffee at ko-fi.com Become a Patron!

Hi im Lotte and i make stuff! Mostly videogames and Unity stuff, you can find a whole bunch of it here ๐Ÿ’–

  • ๐Ÿ”ญ Iโ€™m currently working on Hyper Gunsport @ Necrosoft

  • ๐Ÿ˜„ Pronouns: She/Her ๐Ÿ‘ฉโ€๐Ÿ’ป

  • ๐Ÿ“ซ How to reach me: ๐Ÿค @LotteMakesStuff ๐Ÿ“ง [email protected]

  • ๐ŸŽฎ Some stuff ive made:

Hyper Gunsport Gunsport John Wick Hex PlagueInc The Swindle

simplephysicsdemo's People

Contributors

lottemakesstuff avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

simplephysicsdemo's Issues

InvalidOperationException in v2018.1.0b8 (and how to fix it!)

First of all thank you for giving us this little demo :)
I tried it today with the latest beta version 2018.1.0b8 and this error shows up at runtime:

InvalidOperationException: The previously scheduled job SimpleJobifiedPhysics:PrepareRaycastCommands writes to the NativeArray PrepareRaycastCommands.Raycasts. You must call JobHandle.Complete() on the job SimpleJobifiedPhysics:PrepareRaycastCommands, before you can write to the NativeArray safely. Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle.CheckWriteAndThrowNoEarlyOut (AtomicSafetyHandle handle) Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle.CheckWriteAndThrow (AtomicSafetyHandle handle) (at C:/buildslave/unity/build/Runtime/Export/AtomicSafetyHandle.bindings.cs:155) Unity.Collections.LowLevel.Unsafe.NativeArrayUnsafeUtility.GetUnsafePtr[RaycastCommand] (NativeArray1 nativeArray) (at C:/buildslave/unity/build/Runtime/Export/NativeArray/NativeArray.cs:353)
UnityEngine.RaycastCommand.ScheduleBatch (NativeArray1 commands, NativeArray1 results, Int32 minCommandsPerJob, JobHandle dependsOn) (at C:/buildslave/unity/build/Runtime/Dynamics/ScriptBindings/RaycastCommand.bindings.cs:35)
SimpleJobifiedPhysics.Update () (at Assets/SimpleJobifiedPhysics.cs:237)
`

The setupRaycastsJob need to be completed, l.237:
var setupDependency = setupRaycastsJob.Schedule(objectCount, 32, gravityDependency); setupDependency.Complete();

Compatible with Unity 2018.3+?

Hi, how can you move the code to Unity 2018.3+?
If you update the versions of mathematics and collections packs the concurrent queue types are no longer valid, they don't allow you to dequee, it should be done inside the job...so, how would you do it this way?

struct FindSleepingObjects : IJobParallelFor
    {
        public NativeQueue<int>.Concurrent SleepQueue; //<--concurrent
        [ReadOnly]
        public NativeArray<int> Sleeping;
            
        public void Execute(int index)
        {
            if (Sleeping[index] > 15)
            {
                SleepQueue.Enqueue(index);
            }
        }
    }

you have to change the asleep to a concurrent queue to match SleepQueue in FindSleepingObjects to be able to do this

var sleepJob = new FindSleepingObjects()
        {
            Sleeping = sleepingTimer,
            SleepQueue = asleep
        };

but then it wont work because asleep.Dequeue(); and asleep.Count(); is not available now.

Raycast hit failing

Looks like the collision is not being detected for the majority of the rays.
I've made a change ,now it's using a layer for the raycast,


using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.Profiling;

public class SimpleJobifiedPhysics : MonoBehaviour
{
    // When passing a data collection into a Unity Job it has to be one of the new Unity Native types.
    // .net genrics (like List<T>) and plain arrays (like T[]) wil not work in jobs! 
    private NativeArray<Vector3> velocities;
    private NativeArray<Vector3> positions;
    private NativeArray<Matrix4x4> renderMatrices;
    private Matrix4x4[] renderMatrixArray;
    private NativeArray<int> sleepingTimer;
    private NativeQueue<int> asleep;
    private Vector3 gravity;
    private int objectCount = 1023; // the most we can fit into a single call to Graphics.DrawInstance

    public Mesh mesh;
    public Material material;

    public Transform spawnDirection;

    // This job applies gravity to each objects vecocity in the simulation
    struct GravityJob : IJobParallelFor
    {
        public float DeltaTime;

        public Vector3 Gravity;
        public NativeArray<Vector3> Velocities;
        public NativeArray<int> Sleeping;

        public void Execute(int i)
        {
            // has the object been marked as sleeping?
            // sleeping objects are object that have settled, so they dont
            // move at all in this case we skip adding gravity to make them
            // appear more stable
            if (Sleeping[i] > 0)
            {
                Velocities[i] = new Vector3(0, 0, 0);
                return;
            }

            // simply integrate gravity based on time since last step.
            Velocities[i] += Gravity * DeltaTime;
        }
    }

    // this job creates a batch of RaycastCommands we are going to use for
    // collision detection against the world. these can be sent to PhysX
    // as a batch that will be executed in a job, rather than us having to
    // call Physics.Raycast in a loop just on the main thread!
    struct PrepareRaycastCommands : IJobParallelFor
    {
        public float DeltaTime;
        public int layer;
        public NativeArray<RaycastCommand> Raycasts;
        [ReadOnly]
        public NativeArray<Vector3> Velocities;
        [ReadOnly]
        public NativeArray<Vector3> Positions;


        public void Execute(int i)
        {
            // figure out how far the object we are testing collision for
            // wants to move in total. Our collision raycast only needs to be
            // that far.
            float distance = (Velocities[i] * DeltaTime).magnitude;
            Raycasts[i] = new RaycastCommand(Positions[i], Velocities[i], distance, layer);
        }
    }

    // Integrate the velocity into all the objects positions. We use the
    // Raycast hit data to decide how much of the velocity to integrate -
    // we dont want to tunnel though the colliders in the scene!
    struct IntegratePhysics : IJobParallelFor
    {
        public float DeltaTime;
        [ReadOnly]
        public NativeArray<Vector3> Velocities;
        [ReadOnly]
        public NativeArray<RaycastHit> Hits;
        public NativeArray<int> Sleeping;
        public NativeArray<Vector3> Positions;

        public void Execute(int i)
        {
            if (Sleeping[i] > 0) // if the object is sleeping, we dont have to integrate anything
            {
                Sleeping[i]++;
                return;
            }

            if (Hits[i].normal == Vector3.zero)
            {
                // if there has been no colision, intergrate all of the velocity we want for this frame
                Positions[i] += Velocities[i] * (Velocities[i] * DeltaTime).magnitude;
            }
            else
            {
                // there has been a collision! just move up to the point of the collion with a tiny offset for stability
                Positions[i] = Hits[i].point + new Vector3(0, 0.1f, 0);
            }
        }
    }

    // Respond to collisions
    struct CalculateCollisionResponse : IJobParallelFor
    {
        [ReadOnly]
        public NativeArray<RaycastHit> Hits;

        public NativeArray<Vector3> Velocities;
        public NativeArray<int> Sleeping;

        public void Execute(int i)
        {
            // If there has been a collision, the RaycastHit normal will be
            // non zero. When we know there has been a collisoin we can
            // respond to it.
            if (Hits[i].normal != Vector3.zero)
            {
                // first, lets check if the velocity has got very low before this collision. if so
                // we are going to put the object to sleep. this will stop it from
                // reciveing gravity and will make the simulation appear more stable
                if (Velocities[i].magnitude <= 2f)
                {
                    Velocities[i] = Vector3.zero;
                    Sleeping[i]++; // increment the sleeping counter. any value over 1 is sleeping. We can use this to respawn objects after they have been static for while
                }
                // lets implement a very simple bounce effect - just be reflecting the velocity by the
                // collison normal (the normal is a vector pointing perpendicular away from the surface
                // that we have hit).
                // were also going to apply a damping effect to the velocity by removing force from the resulting
                // reflected velocity. You can think of this as a really simple simulation of force lost
                // due to friction against the surface we have collided with.
                else
                {
                    Velocities[i] = Vector3.Reflect(Velocities[i], Hits[i].normal);

                    var angleBetweenNormalAndUp = Vector3.Angle(Hits[i].normal, Vector3.up); // returns a value between 0-180
                    var lerp = angleBetweenNormalAndUp / 180f;
                    Velocities[i] = Velocities[i] * Mathf.Lerp(0.5f, 1f, lerp);
                }
            }
        }
    }

    // Calculate and store a matrix we can use to draw each object. As we dont support rotation in this demo, we pretty
    // much only supply the position. A fixed scale is also applied to make sure the meshes we draw arnt giant.
    struct CalculateDrawMatricies : IJobParallelFor
    {
        [ReadOnly]
        public NativeArray<Vector3> positions;
        public NativeArray<Matrix4x4> renderMatrices;

        public void Execute(int i)
        {
            renderMatrices[i] = Matrix4x4.TRS(positions[i], Quaternion.identity, new Vector3(0.5f, 0.5f, 0.5f));
        }
    }

    struct FindSleepingObjects : IJobParallelFor
    {
        public NativeQueue<int>.Concurrent SleepQueue;
        [ReadOnly]
        public NativeArray<int> Sleeping;
            
        public void Execute(int index)
        {
            if (Sleeping[index] > 15)
            {
                SleepQueue.Enqueue(index);
            }
        }
    }

    CustomSampler sampler;
    IEnumerator Start()
    {
        // lets wait for half a second before we set off the system -- just to avoid any stutters
        yield return new WaitForSeconds(0.5f);

        // create a sampler so we can mesure performance
        sampler = CustomSampler.Create("SimplePhysics");
        
        // lets define an rough approximation for gravity
        gravity = new Vector3(0, -9f, 0);

        // and set up all our NativeArrays with initial values
        velocities = new NativeArray<Vector3>(objectCount, Allocator.Persistent);
        positions = new NativeArray<Vector3>(objectCount, Allocator.Persistent);
        sleepingTimer = new NativeArray<int>(objectCount, Allocator.Persistent);
        renderMatrices = new NativeArray<Matrix4x4>(objectCount, Allocator.Persistent);
        renderMatrixArray = new Matrix4x4[objectCount];
        asleep = new NativeQueue<int>(Allocator.Persistent);

        for (int i = 0; i < objectCount; i++)
        {
            Respawn(i);
        }
    }

    private void Respawn(int i)
    {
        // Random cannot be used from jobs so this always has to execute on the main thread.
        
        //velocities[i] = Random.onUnitSphere * Random.Range(2, 10); // spawn objects with velocities spreading them out in a sphere
        velocities[i] = (Random.onUnitSphere + (((spawnDirection.position - transform.position).normalized).normalized)*1.3f).normalized * Random.Range(2f, 10f); // spawn with velocities arching towards a target object
        positions[i] = transform.position;
        sleepingTimer[i] = 0;
        renderMatrices[i] = Matrix4x4.identity;
    }

    public LayerMask layerMask;
    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()
        {
            layer = layerMask.value,
            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.ToConcurrent()
        };
        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();
    }

    private void OnDisable()
    {
        velocities.Dispose();
        positions.Dispose();
        renderMatrices.Dispose();
        sleepingTimer.Dispose();
        asleep.Dispose();
    }
}

but the result is the same, a lot of rays are not hitting the boxes (does not matter the layer ,they are all in Water layer).
raycast_failing

Exceptions thrown in current beta builds of Unity 2018.1

Unity throws thread safety exceptions disputed the fact the raycast jobs are scheduled with dependencies. This shouldnโ€™t happen! Possible bug in Unity?

InvalidOperationException: The previously scheduled job SimpleJobifiedPhysics:PrepareRaycastCommands reads from the NativeArray PrepareRaycastCommands.Velocities. You are trying to schedule a new job SimpleJobifiedPhysics:GravityJob, which writes to the same NativeArray (via GravityJob.Velocities). To guarantee safety, you must include SimpleJobifiedPhysics:PrepareRaycastCommands as a dependency of the newly scheduled job.
http://Unity.Jobs.LowLevel.Unsafe.JobsUtility.ScheduleParallelFor (http://Unity.Jobs.LowLevel.Unsafe.JobScheduleParameters& parameters, Int32 arrayLength, Int32 innerloopBatchCount)
http://Unity.Jobs.IJobParallelForExtensions.Schedule[GravityJob] (GravityJob jobData, Int32 arrayLength, Int32 innerloopBatchCount, JobHandle dependsOn) (at C:/buildslave/unity/build/Runtime/Jobs/Managed/IJobParallelFor.cs:51)
SimpleJobifiedPhysics.Update () (at Assets/SimpleJobifiedPhysics.cs:212)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.