/// <summary>
        /// Generate a ripple at a particular position.
        /// </summary>
        /// <param name="position">Ripple position.</param>
        /// <param name="disturbanceFactor">Range: [0..1]: The disturbance is linearly interpolated between the minimum disturbance and the maximum disturbance by this factor.</param>
        /// <param name="pullWaterDown">Pull water down or up?</param>
        /// <param name="playSoundEffect">Play the sound effect.</param>
        /// <param name="playParticleEffect">Play the particle effect.</param>
        /// <param name="smoothRipple">Disturb neighbor vertices to create a smoother ripple (wave).</param>
        /// <param name="smoothingFactor">Range: [0..1]: The amount of disturbance to apply to neighbor vertices.</param>
        public void GenerateRipple(Vector2 position, float disturbanceFactor, bool pullWaterDown, bool playSoundEffect, bool playParticleEffect, bool smoothRipple, float smoothingFactor = 0.5f)
        {
            float xPosition = _mainModule.TransformPointWorldToLocal(position).x;

            float leftBoundary                = _simulationModule.LeftBoundary;
            float rightBoundary               = _simulationModule.RightBoundary;
            int   surfaceVertexCount          = _meshModule.SurfaceVerticesCount;
            int   leftMostSurfaceVertexIndex  = _simulationModule.IsUsingCustomBoundaries ? 1 : 0;
            int   rightMostSurfaceVertexIndex = _simulationModule.IsUsingCustomBoundaries ? surfaceVertexCount - 2 : surfaceVertexCount - 1;

            if (xPosition < leftBoundary || xPosition > rightBoundary)
            {
                return;
            }

            float disturbance = (pullWaterDown ? -1f : 1f) * Mathf.Lerp(_minimumDisturbance, _maximumDisturbance, Mathf.Clamp01(disturbanceFactor));

            float subdivisionsPerUnit = (surfaceVertexCount - (_simulationModule.IsUsingCustomBoundaries ? 3 : 1)) / (rightBoundary - leftBoundary);
            float delta = (xPosition - leftBoundary) * subdivisionsPerUnit;
            int   nearestVertexIndex = leftMostSurfaceVertexIndex + Mathf.RoundToInt(delta);

            _simulationModule.DisturbSurfaceVertex(nearestVertexIndex, disturbance);

            if (smoothRipple)
            {
                smoothingFactor = Mathf.Clamp01(smoothingFactor);
                float smoothedDisturbance = disturbance * smoothingFactor;

                int previousNearestIndex = nearestVertexIndex - 1;
                if (previousNearestIndex >= leftMostSurfaceVertexIndex)
                {
                    _simulationModule.DisturbSurfaceVertex(previousNearestIndex, smoothedDisturbance);
                }

                int nextNearestIndex = nearestVertexIndex + 1;
                if (nextNearestIndex <= rightMostSurfaceVertexIndex)
                {
                    _simulationModule.DisturbSurfaceVertex(nextNearestIndex, smoothedDisturbance);
                }
            }

            Vector3 spawnPosition = _mainModule.TransformPointLocalToWorld(new Vector3(xPosition, _mainModule.Height * 0.5f));

            if (playParticleEffect)
            {
                _particleEffect.PlayParticleEffect(spawnPosition);
            }

            if (playSoundEffect)
            {
                _soundEffect.PlaySoundEffect(spawnPosition, disturbanceFactor);
            }
        }
Beispiel #2
0
        internal Bounds GetWaterObjectsBoundsRelativeToSpecifiedWaterObject(WaterMainModule currentWaterObject)
        {
            Vector2 waterSize     = currentWaterObject.WaterSize;
            Vector2 halfWaterSize = waterSize * 0.5f;

            float leftMostWaterPos  = currentWaterObject.TransformPointWorldToLocal(_leftMostWaterSimulationModule.MainModule.Position).x;
            float rightMostWaterPos = leftMostWaterPos + waterSize.x * (_waterObjectCount - 1);

            Vector2 min = new Vector2(-halfWaterSize.x + leftMostWaterPos, -halfWaterSize.y);
            Vector2 max = new Vector2(halfWaterSize.x + rightMostWaterPos, _waterSurfaceHeighestPoint);

            _waterObjectsBounds.SetMinMax(min, max);
            return(_waterObjectsBounds);
        }
        /// <summary>
        /// Generate a ripple at a particular position.
        /// </summary>
        /// <param name="position">Ripple position.</param>
        /// <param name="disturbanceFactor">Range: [0..1]: The disturbance is linearly interpolated between the minimum disturbance and the maximum disturbance by this factor.</param>
        /// <param name="pullWaterDown">Pull water down or up?</param>
        /// <param name="playSoundEffect">Play the sound effect.</param>
        /// <param name="playParticleEffect">Play the particle effect.</param>
        /// <param name="smoothRipple">Disturb neighbor vertices to create a smoother ripple (wave).</param>
        /// <param name="smoothingFactor">Range: [0..1]: The amount of disturbance to apply to neighbor vertices.</param>
        public void GenerateRipple(Vector2 position, float disturbanceFactor, bool pullWaterDown, bool playSoundEffect, bool playParticleEffect, bool smoothRipple, float smoothingFactor = 0.5f)
        {
            float xPosition = _mainModule.TransformPointWorldToLocal(position).x;

            float leftBoundary = _simulationModule.LeftBoundary;
            float rightBoundary = _simulationModule.RightBoundary;
            int surfaceVerticesCount = _meshModule.SurfaceVerticesCount;
            int startIndex = _simulationModule.IsUsingCustomBoundaries ? 1 : 0;
            int endIndex = _simulationModule.IsUsingCustomBoundaries ? surfaceVerticesCount - 2 : surfaceVerticesCount - 1;

            if (xPosition < leftBoundary || xPosition > rightBoundary)
                return;

            float disturbance = Mathf.Lerp(_minimumDisturbance, _maximumDisturbance, Mathf.Clamp01(disturbanceFactor));
            float velocity = (pullWaterDown ? -1f : 1f) * _simulationModule.StiffnessSquareRoot * disturbance;

            float delta = (xPosition - leftBoundary) * _meshModule.SubdivisionsPerUnit;
            int nearestVertexIndex = startIndex + Mathf.RoundToInt(delta);

            var velocities = _simulationModule.Velocities;

            velocities[nearestVertexIndex] += velocity;
            if (smoothRipple)
            {
                smoothingFactor = Mathf.Clamp01(smoothingFactor);
                float smoothedVelocity = velocity * smoothingFactor;

                int previousNearestIndex = nearestVertexIndex - 1;
                if (previousNearestIndex >= startIndex)
                    velocities[previousNearestIndex] += smoothedVelocity;

                int nextNearestIndex = nearestVertexIndex + 1;
                if (nextNearestIndex <= endIndex)
                    velocities[nextNearestIndex] += smoothedVelocity;
            }

            _simulationModule.MarkVelocitiesArrayAsChanged();

            Vector3 spawnPosition = _mainModule.TransformPointLocalToWorld(new Vector3(xPosition, _mainModule.Height * 0.5f));

            if (playParticleEffect)
                _particleEffect.PlayParticleEffect(spawnPosition);

            if (playSoundEffect)
                _soundEffect.PlaySoundEffect(spawnPosition, disturbanceFactor);
        }
        private void AffectRigidbodies()
        {
            var inWaterColliders = GetInWaterColliders();

            if (inWaterColliders.Count > 0)
            {
                float waterRestPosition  = _mainModule.Height * 0.5f;
                float waterBuoyancyLevel = _mainModule.Height * (0.5f - _waterObject.AttachedComponentsModule.BuoyancyEffectorSurfaceLevel);
                float offset             = waterRestPosition - waterBuoyancyLevel;

                float leftBoundary                = LeftBoundary;
                int   surfaceVertexCount          = _meshModule.SurfaceVerticesCount;
                int   leftMostSurfaceVertexIndex  = _isUsingCustomBoundaries ? 1 : 0;
                int   rightMostSurfaceVertexIndex = _isUsingCustomBoundaries ? surfaceVertexCount - 2 : surfaceVertexCount - 1;
                float subdivisionsPerUnit         = _meshModule.SurfaceVerticesCount / _mainModule.Width;
                float cellWidth = 1f / subdivisionsPerUnit;

                float   density = _attachedComponentsModule.BuoyancyEffector.density;
                Vector2 gravity = Physics2D.gravity;

                Vector3[] vertices = _meshModule.Vertices;

                foreach (var collider in inWaterColliders)
                {
                    if (collider == null)
                    {
                        _destroyedCollidersToRemoveFromInWaterCollidersSet.Enqueue(collider);
                        continue;
                    }

                    Bounds  colliderBounds = collider.bounds;
                    Vector2 colliderBoundsCenterWaterSpace = _mainModule.TransformPointWorldToLocal(colliderBounds.center);
                    Vector2 colliderBoundsExtents          = colliderBounds.extents;

                    float yMaxColliderBounds = colliderBoundsCenterWaterSpace.y + colliderBoundsExtents.y;
                    float yMinColliderBounds = colliderBoundsCenterWaterSpace.y - colliderBoundsExtents.y;

                    if (yMaxColliderBounds > waterBuoyancyLevel)
                    {
                        int nearestStartVertexIndex = Mathf.Clamp(Mathf.RoundToInt((colliderBoundsCenterWaterSpace.x - colliderBoundsExtents.x - leftBoundary) * subdivisionsPerUnit), leftMostSurfaceVertexIndex, rightMostSurfaceVertexIndex);
                        int nearestEndVertexIndex   = Mathf.Clamp(Mathf.RoundToInt((colliderBoundsCenterWaterSpace.x + colliderBoundsExtents.x - leftBoundary) * subdivisionsPerUnit), leftMostSurfaceVertexIndex, rightMostSurfaceVertexIndex);

                        Vector2 centroid  = Vector2.zero;
                        float   totalArea = 0f;

                        for (int i = nearestStartVertexIndex; i < nearestEndVertexIndex; i++)
                        {
                            Vector2 currentSurfaceVertexPosition = vertices[i];

                            currentSurfaceVertexPosition.y -= offset;

                            if (currentSurfaceVertexPosition.y > yMaxColliderBounds)
                            {
                                currentSurfaceVertexPosition.y = yMaxColliderBounds;
                            }
                            else if (currentSurfaceVertexPosition.y < yMinColliderBounds)
                            {
                                currentSurfaceVertexPosition.y = yMinColliderBounds;
                            }

                            float cellHeight = currentSurfaceVertexPosition.y - waterBuoyancyLevel;

                            Vector2 cellCenter = currentSurfaceVertexPosition + 0.5f * new Vector2(cellWidth, -cellHeight);
                            float   cellArea   = cellHeight * cellWidth;

                            totalArea += cellArea;

                            if (Mathf.Abs(totalArea) > 0f)
                            {
                                centroid += (cellArea / totalArea) * (cellCenter - centroid);
                            }
                        }

                        var attachedRigidbody = collider.attachedRigidbody;

                        Vector2 force = (_wavesStrengthOnRigidbodies * totalArea * density * attachedRigidbody.gravityScale) * -gravity;

                        attachedRigidbody.AddForceAtPosition(force, _mainModule.TransformPointLocalToWorld(centroid));
                    }
                }

                while (_destroyedCollidersToRemoveFromInWaterCollidersSet.Count > 0)
                {
                    inWaterColliders.Remove(_destroyedCollidersToRemoveFromInWaterCollidersSet.Dequeue());
                }
            }
        }
        internal void ResolveCollision(Collider2D objectCollider, bool isObjectEnteringWater)
        {
            if ((isObjectEnteringWater && !_isOnWaterEnterRipplesActive) || (!isObjectEnteringWater && !_isOnWaterExitRipplesActive))
            {
                return;
            }

            if (_collisionMask != (_collisionMask | (1 << objectCollider.gameObject.layer)))
            {
                return;
            }

            Vector3[] vertices            = _meshModule.Vertices;
            float[]   velocities          = _simulationModule.Velocities;
            float     stiffnessSquareRoot = _simulationModule.StiffnessSquareRoot;
            Vector3   raycastDirection    = _mainModule.UpDirection;

            int surfaceVerticesCount = _meshModule.SurfaceVerticesCount;
            int subdivisionsPerUnit  = _meshModule.SubdivisionsPerUnit;

            Bounds objectColliderBounds = objectCollider.bounds;
            float  minColliderBoundsX   = _mainModule.TransformPointWorldToLocal(objectColliderBounds.min).x;
            float  maxColliderBoundsX   = _mainModule.TransformPointWorldToLocal(objectColliderBounds.max).x;

            int   firstSurfaceVertexIndex    = _simulationModule.IsUsingCustomBoundaries ? 1 : 0;
            int   lastSurfaceVertexIndex     = _simulationModule.IsUsingCustomBoundaries ? surfaceVerticesCount - 2 : surfaceVerticesCount - 1;
            float firstSurfaceVertexPosition = vertices[firstSurfaceVertexIndex].x;
            int   startIndex = Mathf.Clamp(Mathf.RoundToInt((minColliderBoundsX - firstSurfaceVertexPosition) * subdivisionsPerUnit), firstSurfaceVertexIndex, lastSurfaceVertexIndex);
            int   endIndex   = Mathf.Clamp(Mathf.RoundToInt((maxColliderBoundsX - firstSurfaceVertexPosition) * subdivisionsPerUnit), firstSurfaceVertexIndex, lastSurfaceVertexIndex);

            int     hitPointsCount         = 0;
            float   hitPointsVelocitiesSum = 0f;
            Vector2 hitPointsPositionsSum  = new Vector2(0f, 0f);

            for (int surfaceVertexIndex = startIndex; surfaceVertexIndex <= endIndex; surfaceVertexIndex++)
            {
                Vector2      surfaceVertexPosition = _mainModule.TransformPointLocalToWorld(vertices[surfaceVertexIndex]);
                RaycastHit2D hit = Physics2D.Raycast(surfaceVertexPosition, raycastDirection, _collisionRaycastMaximumDistance, _collisionMask, _collisionMinimumDepth, _collisionMaximumDepth);
                if (hit.collider == objectCollider && hit.rigidbody != null)
                {
                    float velocity = hit.rigidbody.GetPointVelocity(surfaceVertexPosition).y *_velocityMultiplier;
                    velocity = Mathf.Clamp(Mathf.Abs(velocity), _minimumDisturbance, _maximumDisturbance);
                    velocities[surfaceVertexIndex] += velocity * stiffnessSquareRoot * (isObjectEnteringWater ? -1f : 1f);
                    hitPointsVelocitiesSum         += velocity;
                    hitPointsPositionsSum          += hit.point;
                    hitPointsCount++;
                }
            }

            if (hitPointsCount > 0)
            {
                _simulationModule.MarkVelocitiesArrayAsChanged();

                Vector2 hitPointsPositionsMean = hitPointsPositionsSum / hitPointsCount;
                Vector3 spawnPosition          = new Vector3(hitPointsPositionsMean.x, hitPointsPositionsMean.y, _mainModule.Position.z);

                float hitPointsVelocitiesMean = hitPointsVelocitiesSum / hitPointsCount;
                float disturbanceFactor       = Mathf.InverseLerp(_minimumDisturbance, _maximumDisturbance, hitPointsVelocitiesMean);

                if (isObjectEnteringWater)
                {
                    OnWaterEnterRipplesParticleEffect.PlayParticleEffect(spawnPosition);
                    OnWaterEnterRipplesSoundEffect.PlaySoundEffect(spawnPosition, disturbanceFactor);

                    if (_onWaterEnter != null)
                    {
                        _onWaterEnter.Invoke();
                    }
                }
                else
                {
                    OnWaterExitRipplesParticleEffect.PlayParticleEffect(spawnPosition);
                    OnWaterExitRipplesSoundEffect.PlaySoundEffect(spawnPosition, disturbanceFactor);

                    if (_onWaterExit != null)
                    {
                        _onWaterExit.Invoke();
                    }
                }
            }
        }
        internal void CreateRipples(Vector2 left, Vector2 right, float disturbance, float spread, bool smooth, float smoothingFactor = 0.5f)
        {
            Vector2 leftVertexPos  = _mainModule.TransformPointWorldToLocal(left);
            Vector2 rightVertexPos = _mainModule.TransformPointWorldToLocal(right);

            float halfWaterHeight = _mainModule.Height * 0.5f;

            if (leftVertexPos.y > halfWaterHeight || leftVertexPos.y < -halfWaterHeight)
            {
                return;
            }

            int leftVertexIndex;
            int rightVertexIndex;

            float leftBoundary         = _simulationModule.LeftBoundary;
            float rightBoundary        = _simulationModule.RightBoundary;
            int   surfaceVerticesCount = _meshModule.SurfaceVerticesCount;
            int   startIndex           = _simulationModule.IsUsingCustomBoundaries ? 1 : 0;
            int   endIndex             = _simulationModule.IsUsingCustomBoundaries ? surfaceVerticesCount - 2 : surfaceVerticesCount - 1;

            if (leftVertexPos.x > leftBoundary)
            {
                leftVertexIndex = startIndex + Mathf.RoundToInt((leftVertexPos.x - leftBoundary) * _meshModule.SubdivisionsPerUnit);
                if (leftVertexIndex > endIndex)
                {
                    leftVertexIndex = endIndex;
                }
            }
            else
            {
                leftVertexIndex = startIndex;
            }

            if (rightVertexPos.x < rightBoundary)
            {
                rightVertexIndex = startIndex + Mathf.RoundToInt((rightVertexPos.x - leftBoundary) * _meshModule.SubdivisionsPerUnit);
                if (rightVertexIndex < startIndex)
                {
                    rightVertexIndex = startIndex;
                }
            }
            else
            {
                rightVertexIndex = endIndex;
            }

            if (leftVertexIndex == rightVertexIndex)
            {
                return;
            }

            float velocity = -disturbance * _simulationModule.StiffnessSquareRoot;

            var velocities = _simulationModule.Velocities;
            var vertices   = _meshModule.Vertices;

            _affectedVerticesIndices.Clear();

            int affectedVerticesCount = Mathf.CeilToInt(Mathf.Abs(rightVertexIndex - leftVertexIndex) * spread);

            for (int i = 0; i < affectedVerticesCount + 1;)
            {
                int vertexIndex = Random.Range(leftVertexIndex, rightVertexIndex + 1);

                if (!_affectedVerticesIndices.Contains(vertexIndex))
                {
                    _affectedVerticesIndices.Add(vertexIndex);
                    i++;
                }
            }

            for (int i = 0; i < affectedVerticesCount; i++)
            {
                int vertexIndex = _affectedVerticesIndices[i];

                velocities[vertexIndex] += velocity;
                if (smooth)
                {
                    smoothingFactor = Mathf.Clamp01(smoothingFactor);
                    float smoothedVelocity = velocity * smoothingFactor;

                    int previousNearestIndex = vertexIndex - 1;
                    if (previousNearestIndex >= startIndex)
                    {
                        velocities[previousNearestIndex] += smoothedVelocity;
                    }

                    int nextNearestIndex = vertexIndex + 1;
                    if (nextNearestIndex <= endIndex)
                    {
                        velocities[nextNearestIndex] += smoothedVelocity;
                    }
                }
            }

            _simulationModule.MarkVelocitiesArrayAsChanged();
        }