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)); }
/// <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); } }
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(); } } } }
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); }
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(); } }