private void ApplyAnimationConstraints(Vector2 newSize)
        {
            float xFactor = 0f;
            float yFactor = 0f;

            bool hasBottomConstraint   = HasConstraintDefined(WaterAnimationConstraint.Bottom);
            bool hasVerticalConstraint = hasBottomConstraint || HasConstraintDefined(WaterAnimationConstraint.Top);

            if (hasVerticalConstraint)
            {
                yFactor = hasBottomConstraint ? 1f : -1f;
            }

            bool hasLeftConstraint       = HasConstraintDefined(WaterAnimationConstraint.Left);
            bool hasHorizontalConstraint = hasLeftConstraint || HasConstraintDefined(WaterAnimationConstraint.Right);

            if (hasHorizontalConstraint)
            {
                xFactor = hasLeftConstraint ? 1f : -1f;
            }

            // Calculating new water position = currentPosition ± deltaChange * 0.5f
            // we're working here in local space so the current water position is always equal to (0,0)
            // the newPosition = ± deltaChange * 0.5f
            // ± : the sign depends on the defined constraints
            float x = ((newSize.x - _lastWaterSize.x) * 0.5f) * xFactor;
            float y = ((newSize.y - _lastWaterSize.y) * 0.5f) * yFactor;

            _mainModule.Position = _mainModule.TransformLocalToWorld(new Vector2(x, y));
        }
Beispiel #2
0
        /// <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="smooth">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 smooth, float smoothingFactor = 0.5f)
        {
            float xPosition = _mainModule.TransformWorldToLocal(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 (smooth)
            {
                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.TransformLocalToWorld(new Vector2(xPosition, _mainModule.Height * 0.5f));

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

            if (playSoundEffect)
            {
                _soundEffect.PlaySoundEffect(spawnPosition, disturbanceFactor);
            }
        }
Beispiel #3
0
        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.TransformWorldToLocal(objectColliderBounds.min).x;
            float  maxColliderBoundsX   = _mainModule.TransformWorldToLocal(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.TransformLocalToWorld(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();
                    }
                }
            }
        }
Beispiel #4
0
        internal void UpdateArea(SimpleFixedSizedList <Vector2> points, WaterRenderingCameraFrustum cameraFrustrum, bool isFullyContainedInWaterBox, float zFar, bool isReflectionEnabled, float reflectionAxis, float viewingFrustrumHeightScalingFactor = 1f)
        {
            _isValid = true;

            if (isFullyContainedInWaterBox)
            {
                MatchVisibleAreaToCameraFrustum(cameraFrustrum, zFar, isReflectionEnabled, reflectionAxis, viewingFrustrumHeightScalingFactor);
                return;
            }

            _isValid = points.Count > 0;
            if (!_isValid)
            {
                return;
            }

            // Compute the AABB of provided points
            Vector2 boundingBoxMin = points[0];
            Vector2 boundingBoxMax = points[0];

            for (int i = 1, imax = points.Count; i < imax; i++)
            {
                Vector2 point = points[i];

                if (point.x < boundingBoxMin.x)
                {
                    boundingBoxMin.x = point.x;
                }

                if (point.x > boundingBoxMax.x)
                {
                    boundingBoxMax.x = point.x;
                }

                if (point.y < boundingBoxMin.y)
                {
                    boundingBoxMin.y = point.y;
                }

                if (point.y > boundingBoxMax.y)
                {
                    boundingBoxMax.y = point.y;
                }
            }

            float boundingBoxArea = (boundingBoxMax.x - boundingBoxMin.x) * (boundingBoxMax.y - boundingBoxMin.y);

            _isValid = boundingBoxArea > 0f;
            if (!_isValid)
            {
                return;
            }

            if (boundingBoxArea > cameraFrustrum.WaterLocalSpace.Area)
            {
                MatchVisibleAreaToCameraFrustum(cameraFrustrum, zFar, isReflectionEnabled, reflectionAxis, viewingFrustrumHeightScalingFactor);
                return;
            }

            Vector2 boundsCenter = (boundingBoxMin + boundingBoxMax) * 0.5f;

            _position = _mainModule.TransformLocalToWorld(boundsCenter);
            _rotation = Quaternion.Euler(0f, 0f, _mainModule.ZRotation);

            if (isReflectionEnabled)
            {
                _positionReflection = _mainModule.TransformLocalToWorld(new Vector3(boundsCenter.x, 2f * reflectionAxis - boundsCenter.y));
                _rotationReflection = _rotation;
            }

            if (_mainModule.ZRotation != 0f)
            {
                Vector2 topLeft    = _mainModule.TransformLocalToWorld(new Vector2(boundingBoxMin.x, boundingBoxMax.y));
                Vector2 bottomLeft = _mainModule.TransformLocalToWorld(boundingBoxMin);
                Vector2 topRight   = _mainModule.TransformLocalToWorld(boundingBoxMax);
                _width  = Vector2.Distance(topLeft, topRight);
                _height = Vector2.Distance(bottomLeft, topLeft);
            }
            else
            {
                boundingBoxMin = _mainModule.TransformLocalToWorld(boundingBoxMin);
                boundingBoxMax = _mainModule.TransformLocalToWorld(boundingBoxMax);
                _width         = boundingBoxMax.x - boundingBoxMin.x;
                _height        = boundingBoxMax.y - boundingBoxMin.y;
            }

            float halfWidth  = _width * 0.5f;
            float halfHeight = _height * 0.5f;

            _projectionMatrix = Matrix4x4.Ortho(-halfWidth, halfWidth, -halfHeight, halfHeight * viewingFrustrumHeightScalingFactor, 0f, zFar);
        }
Beispiel #5
0
        internal void Update()
        {
#if UNITY_EDITOR
            if (!Application.isPlaying)
            {
                return;
            }
#endif

            _soundEffect.Update();
            _particleEffect.Update();

            if (!_isActive || (!_updateWhenOffscreen && !_mainModule.IsWaterVisible))
            {
                return;
            }

            _elapsedTime += Time.deltaTime;

            if (_elapsedTime >= _currentInterval)
            {
                if (_randomizeRipplesSourcePositions)
                {
                    RandomizeIndices();
                }

                int surfaceVerticesCount = _meshModule.SurfaceVerticesCount;
                int startIndex           = _simulationModule.IsUsingCustomBoundaries ? 1 : 0;
                int endIndex             = _simulationModule.IsUsingCustomBoundaries ? surfaceVerticesCount - 2 : surfaceVerticesCount - 1;

                Vector3[] vertices   = _meshModule.Vertices;
                float[]   velocities = _simulationModule.Velocities;

                bool playSoundEffect    = _soundEffect.IsActive;
                bool playParticleEffect = _particleEffect.IsActive;

                for (int i = 0, imax = _ripplesSourcesIndices.Count; i < imax; i++)
                {
                    int index = _ripplesSourcesIndices[i];

                    float disturbance = _randomizeDisturbance ? Random.Range(_minimumDisturbance, _maximumDisturbance) : _disturbance;

                    disturbance *= _simulationModule.StiffnessSquareRoot;

                    velocities[index] -= disturbance;

                    if (_smoothRipples)
                    {
                        float smoothedDisturbance = disturbance * _smoothingFactor;
                        int   previousIndex, nextIndex;
                        previousIndex = index - 1;
                        nextIndex     = index + 1;

                        if (previousIndex >= startIndex)
                        {
                            velocities[previousIndex] -= smoothedDisturbance;
                        }

                        if (nextIndex <= endIndex)
                        {
                            velocities[nextIndex] -= smoothedDisturbance;
                        }
                    }

                    if (_soundEffect.IsActive || _particleEffect.IsActive)
                    {
                        Vector3 spawnPosition = _mainModule.TransformLocalToWorld(vertices[index]);

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

                        if (playSoundEffect)
                        {
                            float disturbanceFactor = Mathf.InverseLerp(_minimumDisturbance, _maximumDisturbance, disturbance);
                            _soundEffect.PlaySoundEffect(spawnPosition, disturbanceFactor);
                        }
                    }
                }

                _currentInterval = _randomizeTimeInterval ? Random.Range(_minimumTimeInterval, _maximumTimeInterval) : _timeInterval;
                _elapsedTime     = 0f;

                _simulationModule.MarkVelocitiesArrayAsChanged();
            }
        }