anthropic-skills/unity3d-enhanced/reference/physics-system.md

549 lines
13 KiB
Markdown

# Unity Physics System
## Overview
Understanding Unity's physics system is crucial for creating responsive gameplay. This guide covers Rigidbody API changes, the critical difference between triggers and collisions, and best practices for physics-based games.
## Rigidbody API Changes (Unity 2023.1+)
### The Change
Unity 2023.1 renamed `Rigidbody.velocity` to `Rigidbody.linearVelocity` for clarity and consistency.
### Old API (Pre-2023.1)
```csharp
// Setting velocity
rb.velocity = moveDirection;
// Getting velocity
Vector3 currentVelocity = rb.velocity;
// Modifying velocity
rb.velocity += Vector3.up * jumpForce;
```
### New API (Unity 2023.1+)
```csharp
// Setting velocity
rb.linearVelocity = moveDirection;
// Getting velocity
Vector3 currentVelocity = rb.linearVelocity;
// Modifying velocity
rb.linearVelocity += Vector3.up * jumpForce;
```
### Related Changes
| Old API | New API | Notes |
|---------|---------|-------|
| `Rigidbody.velocity` | `Rigidbody.linearVelocity` | Renamed for clarity |
| `Rigidbody.angularVelocity` | `Rigidbody.angularVelocity` | Unchanged (already clear) |
### Migration
**Automatic**: Unity auto-upgrades scripts when opened in 2023.1+
**Manual**: Use Find & Replace
```csharp
// Find: rb.velocity
// Replace: rb.linearVelocity
```
**Best Practice**: Always use `linearVelocity` for Unity 2023.1+ projects
## Triggers vs Collisions
Understanding when to use triggers vs collisions is critical for game physics.
### Collision Detection (Solid Colliders)
**Setup**:
- Collider: `IsTrigger = false`
- Requires: At least one Rigidbody on interacting objects
**Callback**:
```csharp
private void OnCollisionEnter(Collision collision)
{
Debug.Log($"Collided with {collision.gameObject.name}");
// Access collision information
ContactPoint contact = collision.contacts[0];
Vector3 normal = contact.normal;
float impulse = collision.impulse.magnitude;
}
```
**Physics Behavior**:
- Objects physically interact
- Bouncing, pushing, momentum transfer
- Realistic physics simulation
- More expensive computationally
**When to Use**:
- Realistic physics interactions needed
- Objects should bounce or push
- Slower-moving objects
- Vehicle physics
- Ragdoll systems
- Destructible objects that react physically
### Trigger Detection (Ghost Colliders)
**Setup**:
- Collider: `IsTrigger = true`
- Requires: At least one Rigidbody on interacting objects
**Callback**:
```csharp
private void OnTriggerEnter(Collider other)
{
Debug.Log($"Triggered by {other.gameObject.name}");
// No collision information available
// Just detection
}
```
**Physics Behavior**:
- No physical interaction
- Objects pass through each other
- Detection-only
- Cheaper computationally
**When to Use**:
- Fast-moving projectiles (bullets, arrows)
- Zone detection (powerups, checkpoints, sensors)
- Detection-only systems
- Performance-critical scenarios
- Guaranteed detection without tunneling
## Comparison Table
| Aspect | Collision (Solid) | Trigger (Ghost) |
|--------|------------------|-----------------|
| **Detection reliability** | Can miss fast objects (tunneling) | Always detects overlaps |
| **Physics response** | Objects bounce/react | No physical interaction |
| **Performance** | More expensive | Cheaper |
| **Information** | Contact points, impulse, normal | Basic overlap detection |
| **Use case** | Physics reactions | Detection only |
| **Rigidbody required** | Yes (at least one) | Yes (at least one) |
| **Fast objects** | Risk of tunneling | Guaranteed detection |
## Fast-Moving Objects & Tunneling
### The Tunneling Problem
Fast objects can pass through colliders between physics frames.
**Example**:
- Bullet speed: 50 units/sec
- Physics update: 0.02s (50 Hz)
- Bullet travels: 1 unit per frame
- Thin wall: 0.5 units thick
- Result: Bullet **tunnels through** wall
### Solutions
#### 1. Use Triggers (Recommended for Bullets)
```csharp
// Barrel with trigger collider
public class BarrelDestruction : MonoBehaviour
{
[SerializeField] private LayerMask bulletLayer;
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.layer == bulletLayer)
{
Explode();
}
}
private void Explode()
{
// Destruction logic
Destroy(gameObject);
}
}
```
**Setup**:
- Barrel: CapsuleCollider with `IsTrigger = true`, Rigidbody present
- Bullet: SphereCollider (solid), Rigidbody with velocity
**Benefits**:
- Guaranteed detection
- No tunneling
- Better performance
- Simple implementation
#### 2. Continuous Collision Detection
```csharp
Rigidbody rb = GetComponent<Rigidbody>();
rb.collisionDetectionMode = CollisionDetectionMode.Continuous;
// or
rb.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
```
**Modes**:
- **Discrete**: Default, cheapest, can tunnel
- **Continuous**: Prevents tunneling against static colliders
- **ContinuousDynamic**: Prevents tunneling against all colliders (most expensive)
- **ContinuousSpeculative**: Good balance (Unity 2022+)
**Use for**: Important physics objects that must not tunnel
#### 3. Increase Fixed Timestep
```csharp
// Edit → Project Settings → Time
Time.fixedDeltaTime = 0.01f; // 100 Hz (default: 0.02 = 50 Hz)
```
**Tradeoffs**:
- More frequent physics updates
- Better collision detection
- Higher CPU usage
- Affects entire project
#### 4. Use Raycasting
For instant-hit weapons:
```csharp
public class RaycastGun : MonoBehaviour
{
[SerializeField] private float range = 100f;
[SerializeField] private LayerMask hitLayers;
void Shoot()
{
if (Physics.Raycast(transform.position, transform.forward, out RaycastHit hit, range, hitLayers))
{
// Instant hit, no tunneling possible
if (hit.collider.TryGetComponent<IDamageable>(out var damageable))
{
damageable.TakeDamage(10);
}
}
}
}
```
**Benefits**:
- No tunneling ever
- Instant hit
- Very accurate
- Good for hitscan weapons
**Drawbacks**:
- No bullet travel time
- Less visual feedback
- Different gameplay feel
## Bullet Detection Best Practices
### Why Triggers for Bullets
**Bullets are**:
- Fast (50+ units/sec)
- Lightweight (mass 0.01)
- Destroyed on impact
- Don't need physics reactions
**Benefits of Triggers**:
1. Guaranteed detection (no tunneling)
2. Better performance (no physics calculation)
3. Simple implementation
4. Stationary targets don't move
### Example Setup
**Bullet**:
```csharp
public class Bullet : MonoBehaviour
{
[SerializeField] private float speed = 50f;
[SerializeField] private float lifetime = 5f;
[SerializeField] private int damage = 10;
private Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
rb.linearVelocity = transform.forward * speed;
Destroy(gameObject, lifetime);
}
// Destroy on any trigger collision
private void OnTriggerEnter(Collider other)
{
if (other.TryGetComponent<IDamageable>(out var damageable))
{
damageable.TakeDamage(damage);
}
Destroy(gameObject);
}
}
```
**Component Setup**:
- SphereCollider: `IsTrigger = false` (solid bullet)
- Rigidbody: `Use Gravity = false`, mass = 0.01
- Layer: "Bullet" (custom layer)
**Target (Barrel)**:
```csharp
public class Barrel : MonoBehaviour, IDamageable
{
[SerializeField] private int health = 1;
public void TakeDamage(int damage)
{
health -= damage;
if (health <= 0)
{
Explode();
}
}
private void Explode()
{
// Spawn explosion effect
Destroy(gameObject);
}
}
```
**Component Setup**:
- CapsuleCollider: `IsTrigger = true` (ghost collider for bullets)
- Rigidbody: Required for trigger detection
- Layer: "Damageable"
## Rigidbody Requirements
Both triggers and collisions require **at least one Rigidbody**.
### Valid Combinations
| Object A | Object B | Collision Detected? | Trigger Detected? |
|----------|----------|---------------------|-------------------|
| Rigidbody + Collider | Static Collider | ✅ Yes | ✅ Yes |
| Rigidbody + Trigger | Static Collider | ❌ No | ✅ Yes |
| Rigidbody + Collider | Rigidbody + Collider | ✅ Yes | ✅ Yes |
| Static Collider | Static Collider | ❌ No | ❌ No |
| Static Trigger | Static Collider | ❌ No | ❌ No |
**Rule**: At least one object must have a Rigidbody
### Kinematic Rigidbodies
```csharp
rb.isKinematic = true;
```
**Behavior**:
- Not affected by physics forces
- Can be moved via transform
- Still participates in collisions/triggers
- Good for moving platforms, elevators
**Use Cases**:
- Character controllers (root motion)
- Moving platforms
- Doors and elevators
- Objects that move via animation
## Multiple Colliders
You can have multiple colliders on one GameObject:
```csharp
// Example: Enemy with both trigger and collision
Enemy (Root)
├── BoxCollider (IsTrigger = false) // Physics body
└── SphereCollider (IsTrigger = true) // Detection zone
// Example: Barrel with layered detection
Barrel (Root)
├── CapsuleCollider (IsTrigger = true) // Bullet detection
└── BoxCollider (IsTrigger = false) // Physics with environment
```
**Use Cases**:
- Detection zones around physical objects
- Different collision layers
- Separate bullet vs melee detection
## Layer-Based Collision
Configure which layers interact:
**Edit → Project Settings → Physics → Layer Collision Matrix**
```csharp
// Example layer setup
Layer 6: Bullet
Layer 7: Enemy
Layer 8: Environment
// Disable bullet-bullet collisions
// Enable bullet-enemy collisions
// Enable bullet-environment collisions
```
**Benefits**:
- Performance optimization
- Prevents unwanted interactions
- Clear collision logic
## Physics Materials
Control bounciness and friction:
```csharp
// Create Physics Material
// Right-click → Create → Physics Material
PhysicMaterial bouncyMaterial = new PhysicMaterial();
bouncyMaterial.bounciness = 0.8f;
bouncyMaterial.dynamicFriction = 0.3f;
bouncyMaterial.staticFriction = 0.3f;
bouncyMaterial.frictionCombine = PhysicMaterialCombine.Average;
bouncyMaterial.bounceCombine = PhysicMaterialCombine.Maximum;
// Assign to collider
GetComponent<Collider>().material = bouncyMaterial;
```
**Properties**:
- **Bounciness**: 0 (no bounce) to 1 (perfect bounce)
- **Dynamic Friction**: Friction when moving
- **Static Friction**: Friction when stationary
- **Combine**: How materials combine
## Performance Optimization
### Best Practices
**For Bullets**:
✅ Use triggers (cheaper than collisions)
✅ Use object pooling (avoid Instantiate/Destroy)
✅ Set short lifetime (clean up automatically)
✅ Layer-based collision matrix (avoid unnecessary checks)
**For Static Objects**:
✅ No Rigidbody if never moving
✅ Combine meshes when possible
✅ Use simple collider shapes (box, sphere, capsule)
**For Physics Objects**:
✅ Use appropriate collision detection mode
✅ Disable gravity if not needed
✅ Set objects to sleep when stationary
✅ Use compound colliders sparingly
### Collision Detection Mode by Use Case
| Use Case | Detection Mode |
|----------|---------------|
| Slow-moving objects | Discrete |
| Fast bullets | Continuous |
| Player/Characters | ContinuousSpeculative |
| Static objects | N/A (no Rigidbody) |
| Kinematic controllers | Discrete |
## Common Patterns
### Projectile Pattern
```csharp
// Fast bullet: Use trigger on target
Bullet: Solid collider + Rigidbody
Target: Trigger collider + Rigidbody
// Why: Guaranteed detection, good performance
```
### Pickup Pattern
```csharp
// Pickup item: Use trigger
Pickup: Trigger collider + Rigidbody
Player: Solid collider + Rigidbody
// Why: No physics interaction needed, detection only
```
### Explosive Force Pattern
```csharp
// Explosion affects nearby objects
public void Explode()
{
Collider[] colliders = Physics.OverlapSphere(transform.position, radius);
foreach (Collider col in colliders)
{
if (col.TryGetComponent<Rigidbody>(out var rb))
{
rb.AddExplosionForce(force, transform.position, radius);
}
}
}
```
## Debugging Physics
### Visualize Colliders
```csharp
// Gizmos in Scene view
private void OnDrawGizmos()
{
Gizmos.color = Color.green;
Gizmos.DrawWireSphere(transform.position, radius);
}
// Debug rays
Debug.DrawRay(origin, direction * distance, Color.red, duration);
```
### Physics Debugger
**Window → Analysis → Physics Debugger**
Shows:
- All Rigidbodies
- Collision shapes
- Contacts
- Forces
### Common Issues
**Objects fall through floor**:
- Check collision layers
- Verify Rigidbody settings
- Check for kinematic flag
- Ensure colliders are large enough
**Triggers not detecting**:
- Verify at least one Rigidbody
- Check layer collision matrix
- Ensure `IsTrigger = true`
- Check trigger callback methods exist
## Additional Resources
- [Unity Manual - Physics](https://docs.unity3d.com/Manual/PhysicsSection.html)
- [Unity Manual - Colliders](https://docs.unity3d.com/Manual/CollidersOverview.html)
- [Unity Manual - Rigidbody](https://docs.unity3d.com/Manual/class-Rigidbody.html)
- [Physics Best Practices](https://docs.unity3d.com/Manual/BestPracticeUnderstandingPerformanceInUnity6.html)