/// <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 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(); }
internal void PhysicsUpdate(float deltaTime) { if (!_isActive || (!_updateWhenOffscreen && !_mainModule.IsVisible)) { return; } _elapsedTime += 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 UNITY_EDITOR if (!Application.isPlaying) { continue; } #endif if (_soundEffect.IsActive || _particleEffect.IsActive) { Vector3 spawnPosition = _mainModule.TransformPointLocalToWorld(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(); } }