[Add] Synaptic AI Pro
https://assetstore.unity.com/packages/tools/generative-ai/synaptic-ai-pro-natural-language-control-for-unity-336030
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Synaptic.Water
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies buoyancy forces to make objects float on the ocean
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Rigidbody))]
|
||||
public class Buoyancy : MonoBehaviour
|
||||
{
|
||||
[Header("Buoyancy Settings")]
|
||||
[Tooltip("Reference to the ocean system")]
|
||||
public OceanSystem ocean;
|
||||
|
||||
[Tooltip("Buoyancy force multiplier")]
|
||||
public float buoyancyForce = 10f;
|
||||
|
||||
[Tooltip("Water drag coefficient")]
|
||||
public float waterDrag = 1f;
|
||||
|
||||
[Tooltip("Angular water drag")]
|
||||
public float waterAngularDrag = 0.5f;
|
||||
|
||||
[Header("Float Points")]
|
||||
[Tooltip("Points where buoyancy is sampled. If empty, uses object center.")]
|
||||
public Transform[] floatPoints;
|
||||
|
||||
[Header("Wave Response")]
|
||||
[Tooltip("How much the object responds to wave normal")]
|
||||
public float waveAlignmentStrength = 0.5f;
|
||||
|
||||
[Tooltip("Maximum rotation speed when aligning to waves")]
|
||||
public float maxAlignmentTorque = 5f;
|
||||
|
||||
private Rigidbody rb;
|
||||
private float originalDrag;
|
||||
private float originalAngularDrag;
|
||||
private bool isInWater;
|
||||
|
||||
void Start()
|
||||
{
|
||||
rb = GetComponent<Rigidbody>();
|
||||
originalDrag = rb.linearDamping;
|
||||
originalAngularDrag = rb.angularDamping;
|
||||
|
||||
// Auto-find ocean if not set
|
||||
if (ocean == null)
|
||||
ocean = FindFirstObjectByType<OceanSystem>();
|
||||
|
||||
// Create default float points if none specified
|
||||
if (floatPoints == null || floatPoints.Length == 0)
|
||||
{
|
||||
floatPoints = new Transform[] { transform };
|
||||
}
|
||||
}
|
||||
|
||||
void FixedUpdate()
|
||||
{
|
||||
if (ocean == null) return;
|
||||
|
||||
float submergedAmount = 0f;
|
||||
Vector3 totalForce = Vector3.zero;
|
||||
Vector3 averageWaveNormal = Vector3.zero;
|
||||
int submergedPoints = 0;
|
||||
|
||||
foreach (Transform point in floatPoints)
|
||||
{
|
||||
if (point == null) continue;
|
||||
|
||||
Vector3 pointPos = point.position;
|
||||
float waterHeight = ocean.GetWaveHeight(pointPos);
|
||||
float depth = waterHeight - pointPos.y;
|
||||
|
||||
if (depth > 0)
|
||||
{
|
||||
// Point is underwater
|
||||
submergedPoints++;
|
||||
submergedAmount += Mathf.Clamp01(depth);
|
||||
|
||||
// Buoyancy force proportional to submersion depth
|
||||
float forceMagnitude = buoyancyForce * Mathf.Clamp01(depth) * Physics.gravity.magnitude;
|
||||
Vector3 force = Vector3.up * forceMagnitude;
|
||||
|
||||
// Apply force at float point position
|
||||
rb.AddForceAtPosition(force, pointPos, ForceMode.Force);
|
||||
totalForce += force;
|
||||
|
||||
// Sample wave normal
|
||||
averageWaveNormal += ocean.GetWaveNormal(pointPos);
|
||||
}
|
||||
}
|
||||
|
||||
// Update water state
|
||||
bool wasInWater = isInWater;
|
||||
isInWater = submergedPoints > 0;
|
||||
|
||||
// Apply water drag when in water
|
||||
if (isInWater)
|
||||
{
|
||||
float normalizedSubmersion = (float)submergedPoints / floatPoints.Length;
|
||||
rb.linearDamping = Mathf.Lerp(originalDrag, waterDrag, normalizedSubmersion);
|
||||
rb.angularDamping = Mathf.Lerp(originalAngularDrag, waterAngularDrag, normalizedSubmersion);
|
||||
|
||||
// Align to wave normal
|
||||
if (waveAlignmentStrength > 0 && submergedPoints > 0)
|
||||
{
|
||||
averageWaveNormal = (averageWaveNormal / submergedPoints).normalized;
|
||||
Vector3 currentUp = transform.up;
|
||||
Vector3 targetUp = Vector3.Lerp(currentUp, averageWaveNormal, waveAlignmentStrength);
|
||||
|
||||
Quaternion targetRotation = Quaternion.FromToRotation(currentUp, targetUp) * transform.rotation;
|
||||
Vector3 torque = CalculateAlignmentTorque(transform.rotation, targetRotation);
|
||||
rb.AddTorque(Vector3.ClampMagnitude(torque, maxAlignmentTorque), ForceMode.Force);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rb.linearDamping = originalDrag;
|
||||
rb.angularDamping = originalAngularDrag;
|
||||
}
|
||||
|
||||
// Water entry/exit events
|
||||
if (isInWater && !wasInWater)
|
||||
{
|
||||
OnWaterEnter();
|
||||
}
|
||||
else if (!isInWater && wasInWater)
|
||||
{
|
||||
OnWaterExit();
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 CalculateAlignmentTorque(Quaternion current, Quaternion target)
|
||||
{
|
||||
Quaternion delta = target * Quaternion.Inverse(current);
|
||||
delta.ToAngleAxis(out float angle, out Vector3 axis);
|
||||
|
||||
if (angle > 180f) angle -= 360f;
|
||||
|
||||
return axis * (angle * Mathf.Deg2Rad);
|
||||
}
|
||||
|
||||
protected virtual void OnWaterEnter()
|
||||
{
|
||||
// Override for splash effects, sounds, etc.
|
||||
}
|
||||
|
||||
protected virtual void OnWaterExit()
|
||||
{
|
||||
// Override for exit effects
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if object is currently in water
|
||||
/// </summary>
|
||||
public bool IsInWater => isInWater;
|
||||
|
||||
/// <summary>
|
||||
/// Get current water height at object position
|
||||
/// </summary>
|
||||
public float GetWaterHeightAtPosition()
|
||||
{
|
||||
if (ocean == null) return 0f;
|
||||
return ocean.GetWaveHeight(transform.position);
|
||||
}
|
||||
|
||||
void OnDrawGizmosSelected()
|
||||
{
|
||||
if (floatPoints == null) return;
|
||||
|
||||
Gizmos.color = Color.cyan;
|
||||
foreach (Transform point in floatPoints)
|
||||
{
|
||||
if (point != null)
|
||||
{
|
||||
Gizmos.DrawWireSphere(point.position, 0.2f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ceebf94a476b54c36b1118cedb42bda6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 336030
|
||||
packageName: Synaptic AI Pro - Natural Language Control for Unity
|
||||
packageVersion: 1.2.23
|
||||
assetPath: Assets/Synaptic AI Pro/Runtime/Water/Buoyancy.cs
|
||||
uploadId: 920982
|
||||
@@ -0,0 +1,194 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Synaptic.Water
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an infinite ocean plane that follows the camera
|
||||
/// </summary>
|
||||
[ExecuteAlways]
|
||||
public class OceanSystem : MonoBehaviour
|
||||
{
|
||||
[Header("Ocean Settings")]
|
||||
public Material oceanMaterial;
|
||||
public int gridSize = 128;
|
||||
public float tileSize = 100f;
|
||||
public int tilesAroundCamera = 3;
|
||||
|
||||
[Header("LOD Settings")]
|
||||
public bool useLOD = true;
|
||||
public float lodDistance = 200f;
|
||||
public int lodLevels = 3;
|
||||
|
||||
[Header("Camera")]
|
||||
public Transform followCamera;
|
||||
|
||||
private MeshFilter meshFilter;
|
||||
private MeshRenderer meshRenderer;
|
||||
private Mesh oceanMesh;
|
||||
private Vector3 lastCameraPosition;
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (followCamera == null)
|
||||
followCamera = Camera.main?.transform;
|
||||
|
||||
CreateOceanMesh();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (followCamera == null) return;
|
||||
|
||||
// Snap to grid position following camera
|
||||
Vector3 camPos = followCamera.position;
|
||||
float snapX = Mathf.Floor(camPos.x / tileSize) * tileSize;
|
||||
float snapZ = Mathf.Floor(camPos.z / tileSize) * tileSize;
|
||||
|
||||
transform.position = new Vector3(snapX, transform.position.y, snapZ);
|
||||
}
|
||||
|
||||
void CreateOceanMesh()
|
||||
{
|
||||
meshFilter = GetComponent<MeshFilter>();
|
||||
if (meshFilter == null)
|
||||
meshFilter = gameObject.AddComponent<MeshFilter>();
|
||||
|
||||
meshRenderer = GetComponent<MeshRenderer>();
|
||||
if (meshRenderer == null)
|
||||
meshRenderer = gameObject.AddComponent<MeshRenderer>();
|
||||
|
||||
oceanMesh = GenerateOceanMesh(gridSize, tileSize * tilesAroundCamera * 2);
|
||||
meshFilter.sharedMesh = oceanMesh;
|
||||
|
||||
if (oceanMaterial != null)
|
||||
meshRenderer.sharedMaterial = oceanMaterial;
|
||||
}
|
||||
|
||||
Mesh GenerateOceanMesh(int resolution, float size)
|
||||
{
|
||||
Mesh mesh = new Mesh();
|
||||
mesh.name = "OceanMesh";
|
||||
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
|
||||
|
||||
int vertCount = (resolution + 1) * (resolution + 1);
|
||||
Vector3[] vertices = new Vector3[vertCount];
|
||||
Vector2[] uvs = new Vector2[vertCount];
|
||||
Vector3[] normals = new Vector3[vertCount];
|
||||
|
||||
float halfSize = size * 0.5f;
|
||||
float step = size / resolution;
|
||||
|
||||
for (int z = 0; z <= resolution; z++)
|
||||
{
|
||||
for (int x = 0; x <= resolution; x++)
|
||||
{
|
||||
int i = z * (resolution + 1) + x;
|
||||
float xPos = x * step - halfSize;
|
||||
float zPos = z * step - halfSize;
|
||||
|
||||
vertices[i] = new Vector3(xPos, 0, zPos);
|
||||
uvs[i] = new Vector2((float)x / resolution, (float)z / resolution);
|
||||
normals[i] = Vector3.up;
|
||||
}
|
||||
}
|
||||
|
||||
int[] triangles = new int[resolution * resolution * 6];
|
||||
int t = 0;
|
||||
|
||||
for (int z = 0; z < resolution; z++)
|
||||
{
|
||||
for (int x = 0; x < resolution; x++)
|
||||
{
|
||||
int i = z * (resolution + 1) + x;
|
||||
|
||||
triangles[t++] = i;
|
||||
triangles[t++] = i + resolution + 1;
|
||||
triangles[t++] = i + 1;
|
||||
|
||||
triangles[t++] = i + 1;
|
||||
triangles[t++] = i + resolution + 1;
|
||||
triangles[t++] = i + resolution + 2;
|
||||
}
|
||||
}
|
||||
|
||||
mesh.vertices = vertices;
|
||||
mesh.uv = uvs;
|
||||
mesh.normals = normals;
|
||||
mesh.triangles = triangles;
|
||||
mesh.RecalculateBounds();
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get wave height at world position (for buoyancy)
|
||||
/// </summary>
|
||||
public float GetWaveHeight(Vector3 worldPos)
|
||||
{
|
||||
if (oceanMaterial == null) return transform.position.y;
|
||||
|
||||
float time = Time.time * oceanMaterial.GetFloat("_WaveSpeed");
|
||||
float oceanScale = oceanMaterial.HasProperty("_OceanScale") ? oceanMaterial.GetFloat("_OceanScale") : 1f;
|
||||
float waveHeight = oceanMaterial.HasProperty("_WaveHeight") ? oceanMaterial.GetFloat("_WaveHeight") : 1f;
|
||||
|
||||
Vector3 scaledPos = worldPos * oceanScale;
|
||||
float height = transform.position.y;
|
||||
|
||||
// Sample waves A through H
|
||||
height += SampleGerstnerWave(oceanMaterial.GetVector("_WaveA"), scaledPos, time * 0.8f) * waveHeight;
|
||||
height += SampleGerstnerWave(oceanMaterial.GetVector("_WaveB"), scaledPos, time * 0.9f) * waveHeight;
|
||||
height += SampleGerstnerWave(oceanMaterial.GetVector("_WaveC"), scaledPos, time) * waveHeight;
|
||||
height += SampleGerstnerWave(oceanMaterial.GetVector("_WaveD"), scaledPos, time * 1.1f) * waveHeight;
|
||||
|
||||
if (oceanMaterial.HasProperty("_WaveE"))
|
||||
{
|
||||
height += SampleGerstnerWave(oceanMaterial.GetVector("_WaveE"), scaledPos, time * 1.2f) * waveHeight;
|
||||
height += SampleGerstnerWave(oceanMaterial.GetVector("_WaveF"), scaledPos, time * 1.4f) * waveHeight;
|
||||
height += SampleGerstnerWave(oceanMaterial.GetVector("_WaveG"), scaledPos, time * 1.6f) * waveHeight;
|
||||
height += SampleGerstnerWave(oceanMaterial.GetVector("_WaveH"), scaledPos, time * 1.8f) * waveHeight;
|
||||
}
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
float SampleGerstnerWave(Vector4 wave, Vector3 pos, float time)
|
||||
{
|
||||
float steepness = wave.z;
|
||||
float wavelength = wave.w;
|
||||
|
||||
if (wavelength <= 0) return 0;
|
||||
|
||||
float k = 2f * Mathf.PI / wavelength;
|
||||
float c = Mathf.Sqrt(9.8f / k);
|
||||
Vector2 d = new Vector2(wave.x, wave.y).normalized;
|
||||
float f = k * (Vector2.Dot(d, new Vector2(pos.x, pos.z)) - c * time);
|
||||
float a = steepness / k;
|
||||
|
||||
return a * Mathf.Sin(f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get wave normal at world position
|
||||
/// </summary>
|
||||
public Vector3 GetWaveNormal(Vector3 worldPos)
|
||||
{
|
||||
float delta = 0.1f;
|
||||
float h = GetWaveHeight(worldPos);
|
||||
float hX = GetWaveHeight(worldPos + Vector3.right * delta);
|
||||
float hZ = GetWaveHeight(worldPos + Vector3.forward * delta);
|
||||
|
||||
Vector3 tangentX = new Vector3(delta, hX - h, 0).normalized;
|
||||
Vector3 tangentZ = new Vector3(0, hZ - h, delta).normalized;
|
||||
|
||||
return Vector3.Cross(tangentZ, tangentX).normalized;
|
||||
}
|
||||
|
||||
void OnValidate()
|
||||
{
|
||||
if (Application.isPlaying && oceanMesh != null)
|
||||
{
|
||||
CreateOceanMesh();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f02a1388f7cb640c0811bba5b3794e55
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 336030
|
||||
packageName: Synaptic AI Pro - Natural Language Control for Unity
|
||||
packageVersion: 1.2.23
|
||||
assetPath: Assets/Synaptic AI Pro/Runtime/Water/OceanSystem.cs
|
||||
uploadId: 920982
|
||||
Reference in New Issue
Block a user