Se añadieron configuraciones para Hot Reload en CtrEditor.csproj y se ajustaron las propiedades de depuración en los grupos de configuración. En MainViewModel.cs, se corrigió un error en el tiempo de simulación. En Aether.cs, se implementaron nuevas propiedades y métodos para gestionar la presión entre botellas, mejorando la lógica de colisiones y evitando superposiciones. Estas mejoras optimizan la simulación y la interacción entre objetos.
This commit is contained in:
parent
256d86aca5
commit
2cb90ec2dc
|
@ -7,14 +7,24 @@
|
|||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UseWPF>true</UseWPF>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<!-- Configuración para Hot Reload -->
|
||||
<EnableHotReload>true</EnableHotReload>
|
||||
<HotReloadLogger>true</HotReloadLogger>
|
||||
<ForceGenerateMetadataAssemblyFormat>true</ForceGenerateMetadataAssemblyFormat>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<Optimize>True</Optimize>
|
||||
<Optimize>False</Optimize>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>portable</DebugType>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<Optimize>True</Optimize>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -947,7 +947,7 @@ namespace CtrEditor
|
|||
simSampleCount++;
|
||||
|
||||
// Eliminar el diseño de Debug luego de 2 segundos
|
||||
if (TiempoDesdeStartSimulacion > 12000)
|
||||
if (TiempoDesdeStartSimulacion > 1200)
|
||||
simulationManager.Debug_ClearSimulationShapes();
|
||||
else
|
||||
TiempoDesdeStartSimulacion += (float)elapsedMilliseconds;
|
||||
|
|
|
@ -514,6 +514,9 @@ namespace CtrEditor.Simulacion
|
|||
public float OverlapPercentage;
|
||||
public float _neckRadius;
|
||||
PrismaticJoint _activeJoint;
|
||||
public bool isBeingPressedByOtherBottles;
|
||||
public int bottleContactCount;
|
||||
public float debugPenetrationLevel; // Para debug: nivel de penetración actual
|
||||
private List<Action> _deferredActions;
|
||||
public simBotella(World world, List<Action> deferredActions, float diameter, Vector2 position, float mass, float neckRadius = 0)
|
||||
{
|
||||
|
@ -583,6 +586,8 @@ namespace CtrEditor.Simulacion
|
|||
RemoverBody();
|
||||
isOnTransports = 0;
|
||||
_activeJoint = null;
|
||||
isBeingPressedByOtherBottles = false;
|
||||
bottleContactCount = 0;
|
||||
Body = _world.CreateCircle(Radius, 1f, position);
|
||||
Body.BodyType = BodyType.Dynamic;
|
||||
// Restablecer manejador de eventos de colisión
|
||||
|
@ -648,6 +653,11 @@ namespace CtrEditor.Simulacion
|
|||
|
||||
return true; // No aplicar respuestas físicas
|
||||
}
|
||||
else if (fixtureB.Body.Tag is simBotella)
|
||||
{
|
||||
bottleContactCount++;
|
||||
return true; // Permitir respuestas físicas entre botellas
|
||||
}
|
||||
|
||||
return true; // No aplicar respuestas físicas
|
||||
}
|
||||
|
@ -702,6 +712,11 @@ namespace CtrEditor.Simulacion
|
|||
Sensor.LuzCortada -= 1;
|
||||
Sensor.ListSimBotellaContact.Remove(this);
|
||||
}
|
||||
|
||||
if (fixtureB.Body.Tag is simBotella)
|
||||
{
|
||||
bottleContactCount = Math.Max(0, bottleContactCount - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -753,8 +768,17 @@ namespace CtrEditor.Simulacion
|
|||
return true;
|
||||
}
|
||||
|
||||
// Aplicar la fuerza de fricción en función del porcentaje de superficie sobrepuesta
|
||||
Body.LinearVelocity += frictionCoefficient * desiredVelocity;
|
||||
// Si la botella está siendo presionada por múltiples botellas, reducir la aplicación de fuerza
|
||||
float pressureReductionFactor = 1.0f;
|
||||
if (isBeingPressedByOtherBottles)
|
||||
{
|
||||
// Reducir el factor basándose en la cantidad de contactos
|
||||
pressureReductionFactor = Math.Max(0.1f, 1.0f / (1.0f + bottleContactCount * 0.5f));
|
||||
}
|
||||
|
||||
// Aplicar la fuerza de fricción en función del porcentaje de superficie sobrepuesta y la presión
|
||||
Vector2 finalVelocityChange = frictionCoefficient * desiredVelocity * pressureReductionFactor;
|
||||
Body.LinearVelocity += finalVelocityChange;
|
||||
//Body.ApplyForce(frictionForce * overlapPercentage);
|
||||
return false;
|
||||
}
|
||||
|
@ -842,6 +866,9 @@ namespace CtrEditor.Simulacion
|
|||
_deferredActions = new List<Action>();
|
||||
stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
// Configurar el callback PreSolve para manejar contactos entre botellas
|
||||
world.ContactManager.PreSolve += OnPreSolve;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
|
@ -862,6 +889,15 @@ namespace CtrEditor.Simulacion
|
|||
|
||||
public void Step()
|
||||
{
|
||||
// Resetear las flags de presión al inicio del frame
|
||||
ResetBottlePressureFlags();
|
||||
|
||||
// Verificar y corregir el espaciado entre botellas antes del step
|
||||
PreventBottleOverlapping();
|
||||
|
||||
// Actualizar el estado de presión basado en contactos reales
|
||||
UpdateBottlePressureStatus();
|
||||
|
||||
// Detener el cronómetro y obtener el tiempo transcurrido en milisegundos
|
||||
float elapsedMilliseconds = (float)(stopwatch.Elapsed.TotalMilliseconds - stopwatch_last);
|
||||
stopwatch_last = stopwatch.Elapsed.TotalMilliseconds;
|
||||
|
@ -876,7 +912,7 @@ namespace CtrEditor.Simulacion
|
|||
{
|
||||
botella.CenterFixtureOnConveyor();
|
||||
botella.Body.Inertia = 0;
|
||||
botella.Body.Mass = 100;
|
||||
botella.Body.Mass = botella.OriginalMass * 100;
|
||||
}
|
||||
else if (botella.isNoMoreRestricted)
|
||||
{
|
||||
|
@ -1048,5 +1084,212 @@ namespace CtrEditor.Simulacion
|
|||
|
||||
return polygon;
|
||||
}
|
||||
|
||||
private void OnPreSolve(Contact contact, ref nkast.Aether.Physics2D.Collision.Manifold oldManifold)
|
||||
{
|
||||
var fixtureA = contact.FixtureA;
|
||||
var fixtureB = contact.FixtureB;
|
||||
|
||||
// Verificar si el contacto es entre dos botellas
|
||||
if (fixtureA.Body.Tag is simBotella botellaA && fixtureB.Body.Tag is simBotella botellaB)
|
||||
{
|
||||
// Calcular la distancia entre centros de las botellas
|
||||
Vector2 centerA = botellaA.Body.Position;
|
||||
Vector2 centerB = botellaB.Body.Position;
|
||||
Vector2 direction = centerB - centerA;
|
||||
float distance = direction.Length();
|
||||
|
||||
// Distancia mínima permitida (suma de radios + margen de seguridad)
|
||||
float minDistance = botellaA.Radius + botellaB.Radius + 0.01f; // 1cm de margen
|
||||
|
||||
// Calcular la velocidad relativa
|
||||
Vector2 relativeVelocity = botellaB.Body.LinearVelocity - botellaA.Body.LinearVelocity;
|
||||
float relativeSpeed = relativeVelocity.Length();
|
||||
|
||||
// Solo marcar como presionadas si hay verdadera interpenetración O múltiples contactos
|
||||
if (distance < minDistance * 0.95f) // Solo si están muy cerca (interpenetración significativa)
|
||||
{
|
||||
botellaA.isBeingPressedByOtherBottles = true;
|
||||
botellaB.isBeingPressedByOtherBottles = true;
|
||||
|
||||
// Ajustar propiedades del contacto para reducir fuerzas explosivas
|
||||
float penetrationRatio = Math.Max(0, (minDistance - distance) / minDistance);
|
||||
|
||||
// Reducir restitución progresivamente según la penetración
|
||||
contact.Restitution = Math.Min(contact.Restitution, 0.1f * (1.0f - penetrationRatio));
|
||||
|
||||
// Aumentar fricción para disipar energía
|
||||
contact.Friction = Math.Max(contact.Friction, 0.7f + penetrationRatio * 0.2f);
|
||||
}
|
||||
|
||||
// Si la velocidad relativa es muy alta, también ajustar contacto
|
||||
if (relativeSpeed > 2.0f)
|
||||
{
|
||||
contact.Restitution = 0.0f; // Sin rebote
|
||||
contact.Friction = Math.Max(contact.Friction, 0.9f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetBottlePressureFlags()
|
||||
{
|
||||
foreach (var cuerpo in Cuerpos)
|
||||
{
|
||||
if (cuerpo is simBotella botella)
|
||||
{
|
||||
botella.isBeingPressedByOtherBottles = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBottlePressureStatus()
|
||||
{
|
||||
var botellas = new List<simBotella>();
|
||||
|
||||
// Recopilar todas las botellas
|
||||
foreach (var cuerpo in Cuerpos)
|
||||
{
|
||||
if (cuerpo is simBotella botella)
|
||||
{
|
||||
botellas.Add(botella);
|
||||
}
|
||||
}
|
||||
|
||||
// Verificar el estado de presión de cada botella basado en sus contactos reales
|
||||
foreach (var botella in botellas)
|
||||
{
|
||||
int proximityCount = 0;
|
||||
float totalPenetration = 0;
|
||||
|
||||
foreach (var otherBottle in botellas)
|
||||
{
|
||||
if (botella == otherBottle) continue;
|
||||
|
||||
Vector2 direction = otherBottle.Body.Position - botella.Body.Position;
|
||||
float distance = direction.Length();
|
||||
float minDistance = botella.Radius + otherBottle.Radius + 0.01f; // 1cm margen
|
||||
|
||||
if (distance < minDistance)
|
||||
{
|
||||
proximityCount++;
|
||||
totalPenetration += (minDistance - distance);
|
||||
}
|
||||
}
|
||||
|
||||
// Actualizar información de debug
|
||||
botella.debugPenetrationLevel = totalPenetration;
|
||||
|
||||
// Solo marcar como presionada si tiene múltiples contactos cercanos O penetración significativa
|
||||
// Y además la botella está en un transportador (para evitar marcar botellas libres)
|
||||
if ((proximityCount >= 2 || totalPenetration > 0.01f) && botella.IsOnAnyTransport())
|
||||
{
|
||||
botella.isBeingPressedByOtherBottles = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PreventBottleOverlapping()
|
||||
{
|
||||
var botellas = new List<simBotella>();
|
||||
|
||||
// Recopilar todas las botellas
|
||||
foreach (var cuerpo in Cuerpos)
|
||||
{
|
||||
if (cuerpo is simBotella botella)
|
||||
{
|
||||
botellas.Add(botella);
|
||||
}
|
||||
}
|
||||
|
||||
// Verificar cada par de botellas múltiples veces para resolver interpenetraciones complejas
|
||||
int iterations = 3; // Múltiples iteraciones para resolver conflictos
|
||||
|
||||
for (int iter = 0; iter < iterations; iter++)
|
||||
{
|
||||
bool anyCorrection = false;
|
||||
|
||||
for (int i = 0; i < botellas.Count; i++)
|
||||
{
|
||||
for (int j = i + 1; j < botellas.Count; j++)
|
||||
{
|
||||
var botellaA = botellas[i];
|
||||
var botellaB = botellas[j];
|
||||
|
||||
Vector2 centerA = botellaA.Body.Position;
|
||||
Vector2 centerB = botellaB.Body.Position;
|
||||
Vector2 direction = centerB - centerA;
|
||||
float distance = direction.Length();
|
||||
|
||||
// Distancia mínima permitida (suma de radios + margen de seguridad)
|
||||
float minDistance = botellaA.Radius + botellaB.Radius + 0.005f; // 5mm de margen
|
||||
|
||||
// Si están demasiado cerca
|
||||
if (distance < minDistance && distance > 0.001f)
|
||||
{
|
||||
anyCorrection = true;
|
||||
|
||||
// Normalizar la dirección
|
||||
direction.Normalize();
|
||||
|
||||
// Calcular la corrección necesaria
|
||||
float penetration = minDistance - distance;
|
||||
float correction = penetration * 0.5f;
|
||||
|
||||
// Factor de corrección que decrece con las iteraciones
|
||||
float correctionFactor = 0.3f / (iter + 1); // Más agresivo en la primera iteración
|
||||
Vector2 correctionVector = direction * correction * correctionFactor;
|
||||
|
||||
// Aplicar corrección considerando masas (botellas más pesadas se mueven menos)
|
||||
float massA = botellaA.Body.Mass;
|
||||
float massB = botellaB.Body.Mass;
|
||||
float totalMass = massA + massB;
|
||||
|
||||
float ratioA = massB / totalMass; // Botella más liviana se mueve más
|
||||
float ratioB = massA / totalMass;
|
||||
|
||||
// Solo mover si las botellas no están restringidas por transportadores
|
||||
if (!botellaA.isRestricted)
|
||||
{
|
||||
Vector2 newPosA = centerA - correctionVector * ratioA;
|
||||
if (!float.IsNaN(newPosA.X) && !float.IsNaN(newPosA.Y))
|
||||
{
|
||||
botellaA.Body.Position = newPosA;
|
||||
}
|
||||
}
|
||||
|
||||
if (!botellaB.isRestricted)
|
||||
{
|
||||
Vector2 newPosB = centerB + correctionVector * ratioB;
|
||||
if (!float.IsNaN(newPosB.X) && !float.IsNaN(newPosB.Y))
|
||||
{
|
||||
botellaB.Body.Position = newPosB;
|
||||
}
|
||||
}
|
||||
|
||||
// Solo marcar como presionadas si hay interpenetración significativa
|
||||
if (penetration > 0.005f) // Solo si penetran más de 5mm
|
||||
{
|
||||
botellaA.isBeingPressedByOtherBottles = true;
|
||||
botellaB.isBeingPressedByOtherBottles = true;
|
||||
}
|
||||
|
||||
// Reducir velocidades para evitar acumulación de energía
|
||||
float velocityDamping = 0.9f;
|
||||
if (penetration > 0.01f) // Si la penetración es significativa (> 1cm)
|
||||
{
|
||||
velocityDamping = 0.7f; // Reducción más agresiva
|
||||
}
|
||||
|
||||
botellaA.Body.LinearVelocity *= velocityDamping;
|
||||
botellaB.Body.LinearVelocity *= velocityDamping;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Si no hubo correcciones en esta iteración, podemos parar
|
||||
if (!anyCorrection)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue