添加 claude code game studios 到项目
This commit is contained in:
289
docs/engine-reference/unity/modules/animation.md
Normal file
289
docs/engine-reference/unity/modules/animation.md
Normal file
@@ -0,0 +1,289 @@
|
||||
# Unity 6.3 — Animation Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** Unity 6 animation improvements, Timeline enhancements
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Unity 6.3 animation systems:
|
||||
- **Animator Controller (Mecanim)**: State machine-based (RECOMMENDED)
|
||||
- **Timeline**: Cinematic sequences, cutscenes
|
||||
- **Animation Rigging**: Procedural runtime animation
|
||||
- **Legacy Animation**: Deprecated, avoid
|
||||
|
||||
---
|
||||
|
||||
## Key Changes from 2022 LTS
|
||||
|
||||
### Animation Rigging Package (Production-Ready in Unity 6)
|
||||
|
||||
```csharp
|
||||
// Install: Package Manager > Animation Rigging
|
||||
// Runtime IK, aim constraints, procedural animation
|
||||
```
|
||||
|
||||
### Timeline Improvements
|
||||
- Better performance
|
||||
- More track types
|
||||
- Improved signal system
|
||||
|
||||
---
|
||||
|
||||
## Animator Controller (Mecanim)
|
||||
|
||||
### Basic Setup
|
||||
|
||||
```csharp
|
||||
// Create: Assets > Create > Animator Controller
|
||||
// Add to GameObject: Add Component > Animator
|
||||
// Assign Controller: Animator > Controller = YourAnimatorController
|
||||
```
|
||||
|
||||
### State Transitions
|
||||
|
||||
```csharp
|
||||
Animator animator = GetComponent<Animator>();
|
||||
|
||||
// ✅ Trigger transition
|
||||
animator.SetTrigger("Jump");
|
||||
|
||||
// ✅ Bool parameter
|
||||
animator.SetBool("IsRunning", true);
|
||||
|
||||
// ✅ Float parameter (blend trees)
|
||||
animator.SetFloat("Speed", currentSpeed);
|
||||
|
||||
// ✅ Integer parameter
|
||||
animator.SetInteger("WeaponType", 2);
|
||||
```
|
||||
|
||||
### Animation Layers
|
||||
- **Base Layer**: Default animations (locomotion)
|
||||
- **Override Layers**: Replace base layer (e.g., weapon swap)
|
||||
- **Additive Layers**: Add on top of base (e.g., breathing, aim offset)
|
||||
|
||||
```csharp
|
||||
// Set layer weight (0-1)
|
||||
animator.SetLayerWeight(1, 0.5f); // 50% blend
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Blend Trees
|
||||
|
||||
### 1D Blend Tree (Speed blending)
|
||||
|
||||
```csharp
|
||||
// Idle (Speed = 0) → Walk (Speed = 0.5) → Run (Speed = 1.0)
|
||||
animator.SetFloat("Speed", moveSpeed);
|
||||
```
|
||||
|
||||
### 2D Blend Tree (Directional movement)
|
||||
|
||||
```csharp
|
||||
// X-axis: Strafe (-1 to 1)
|
||||
// Y-axis: Forward/Back (-1 to 1)
|
||||
animator.SetFloat("MoveX", input.x);
|
||||
animator.SetFloat("MoveY", input.y);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Animation Events
|
||||
|
||||
### Trigger Events from Animation Clips
|
||||
|
||||
```csharp
|
||||
// Add in Animation window: Right-click timeline > Add Animation Event
|
||||
// Must have matching method on GameObject:
|
||||
|
||||
public void OnFootstep() {
|
||||
// Play footstep sound
|
||||
AudioSource.PlayClipAtPoint(footstepClip, transform.position);
|
||||
}
|
||||
|
||||
public void OnAttackHit() {
|
||||
// Deal damage
|
||||
DealDamageInFrontOfPlayer();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Root Motion
|
||||
|
||||
### Character Movement via Animation
|
||||
|
||||
```csharp
|
||||
Animator animator = GetComponent<Animator>();
|
||||
animator.applyRootMotion = true; // Move character based on animation
|
||||
|
||||
void OnAnimatorMove() {
|
||||
// Custom root motion handling
|
||||
transform.position += animator.deltaPosition;
|
||||
transform.rotation *= animator.deltaRotation;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Animation Rigging (Unity 6+)
|
||||
|
||||
### IK (Inverse Kinematics)
|
||||
|
||||
```csharp
|
||||
// Install: Package Manager > Animation Rigging
|
||||
// Add: Rig Builder component + Rig GameObject
|
||||
|
||||
// Two Bone IK (Arm/Leg)
|
||||
// - Add Two Bone IK Constraint
|
||||
// - Assign Tip (hand/foot), Mid (elbow/knee), Root (shoulder/hip)
|
||||
// - Set Target (where hand/foot should reach)
|
||||
|
||||
// Runtime control:
|
||||
TwoBoneIKConstraint ikConstraint = rig.GetComponentInChildren<TwoBoneIKConstraint>();
|
||||
ikConstraint.data.target = targetTransform;
|
||||
ikConstraint.weight = 1f; // 0-1 blend
|
||||
```
|
||||
|
||||
### Aim Constraint (Look At)
|
||||
|
||||
```csharp
|
||||
// Character looks at target
|
||||
MultiAimConstraint aimConstraint = rig.GetComponentInChildren<MultiAimConstraint>();
|
||||
aimConstraint.data.sourceObjects[0] = new WeightedTransform(targetTransform, 1f);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Timeline (Cutscenes)
|
||||
|
||||
### Basic Timeline Setup
|
||||
|
||||
```csharp
|
||||
// Create: Assets > Create > Timeline
|
||||
// Add to GameObject: Add Component > Playable Director
|
||||
// Assign Timeline: Playable Director > Playable = YourTimeline
|
||||
|
||||
// Play from script:
|
||||
PlayableDirector director = GetComponent<PlayableDirector>();
|
||||
director.Play();
|
||||
```
|
||||
|
||||
### Timeline Tracks
|
||||
- **Activation Track**: Enable/disable GameObjects
|
||||
- **Animation Track**: Play animations on Animator
|
||||
- **Audio Track**: Synchronized audio playback
|
||||
- **Cinemachine Track**: Camera movement
|
||||
- **Signal Track**: Trigger events at specific times
|
||||
|
||||
### Signal System (Events)
|
||||
|
||||
```csharp
|
||||
// Create Signal Asset: Assets > Create > Signals > Signal
|
||||
// Add Signal Emitter to Timeline track
|
||||
// Add Signal Receiver component to GameObject
|
||||
|
||||
public class CutsceneEvents : MonoBehaviour {
|
||||
public void OnDialogueStart() {
|
||||
// Triggered by signal
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Animation Playback Control
|
||||
|
||||
### Play Animation Directly (No State Machine)
|
||||
|
||||
```csharp
|
||||
// ✅ CrossFade (smooth transition)
|
||||
animator.CrossFade("Attack", 0.2f); // 0.2s transition
|
||||
|
||||
// ✅ Play (instant)
|
||||
animator.Play("Idle");
|
||||
|
||||
// ❌ Avoid: Legacy Animation component
|
||||
Animation anim = GetComponent<Animation>(); // DEPRECATED
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Animation Curves
|
||||
|
||||
### Custom Property Animation
|
||||
|
||||
```csharp
|
||||
// In Animation window: Add Property > Custom Component > Your Script > Your Float
|
||||
|
||||
public class WeaponTrail : MonoBehaviour {
|
||||
public float trailIntensity; // Animated by clip
|
||||
|
||||
void Update() {
|
||||
// Intensity controlled by animation curve
|
||||
trailRenderer.startWidth = trailIntensity;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Culling
|
||||
- `Animator > Culling Mode`:
|
||||
- **Always Animate**: Always update (expensive)
|
||||
- **Cull Update Transforms**: Stop updating bones when off-screen (RECOMMENDED)
|
||||
- **Cull Completely**: Stop all animation when off-screen
|
||||
|
||||
### LOD (Level of Detail)
|
||||
- Simpler animations for distant characters
|
||||
- Reduce skeleton bone count for LOD meshes
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Check if Animation Finished
|
||||
|
||||
```csharp
|
||||
AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0);
|
||||
if (stateInfo.IsName("Attack") && stateInfo.normalizedTime >= 1.0f) {
|
||||
// Attack animation finished
|
||||
}
|
||||
```
|
||||
|
||||
### Override Animation Speed
|
||||
|
||||
```csharp
|
||||
animator.speed = 1.5f; // 150% speed
|
||||
```
|
||||
|
||||
### Get Current Animation Name
|
||||
|
||||
```csharp
|
||||
AnimatorClipInfo[] clipInfo = animator.GetCurrentAnimatorClipInfo(0);
|
||||
string currentClip = clipInfo[0].clip.name;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Animator Window
|
||||
- `Window > Animation > Animator`
|
||||
- Visualize state machine, see active state
|
||||
|
||||
### Animation Window
|
||||
- `Window > Animation > Animation`
|
||||
- Edit animation clips, add events
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unity3d.com/6000.0/Documentation/Manual/AnimationOverview.html
|
||||
- https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/index.html
|
||||
- https://docs.unity3d.com/Packages/com.unity.timeline@1.8/manual/index.html
|
||||
284
docs/engine-reference/unity/modules/audio.md
Normal file
284
docs/engine-reference/unity/modules/audio.md
Normal file
@@ -0,0 +1,284 @@
|
||||
# Unity 6.3 — Audio Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** Unity 6 audio mixer improvements
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Unity 6.3 audio systems:
|
||||
- **AudioSource**: Play sounds on GameObjects
|
||||
- **Audio Mixer**: Mix, effect processing, dynamic mixing
|
||||
- **Spatial Audio**: 3D positioned sound
|
||||
|
||||
---
|
||||
|
||||
## Basic Audio Playback
|
||||
|
||||
### AudioSource Component
|
||||
|
||||
```csharp
|
||||
AudioSource audioSource = GetComponent<AudioSource>();
|
||||
|
||||
// ✅ Play
|
||||
audioSource.Play();
|
||||
|
||||
// ✅ Play with delay
|
||||
audioSource.PlayDelayed(0.5f); // 0.5 seconds
|
||||
|
||||
// ✅ Play one-shot (doesn't interrupt current sound)
|
||||
audioSource.PlayOneShot(clip);
|
||||
|
||||
// ✅ Stop
|
||||
audioSource.Stop();
|
||||
|
||||
// ✅ Pause/Resume
|
||||
audioSource.Pause();
|
||||
audioSource.UnPause();
|
||||
```
|
||||
|
||||
### Play Sound at Position (Static Method)
|
||||
|
||||
```csharp
|
||||
// ✅ Quick 3D sound playback (auto-destroys when done)
|
||||
AudioSource.PlayClipAtPoint(clip, transform.position);
|
||||
|
||||
// ✅ With volume
|
||||
AudioSource.PlayClipAtPoint(clip, transform.position, 0.7f);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3D Spatial Audio
|
||||
|
||||
### AudioSource 3D Settings
|
||||
|
||||
```csharp
|
||||
AudioSource source = GetComponent<AudioSource>();
|
||||
|
||||
// Spatial Blend: 0 = 2D, 1 = 3D
|
||||
source.spatialBlend = 1.0f; // Fully 3D
|
||||
|
||||
// Doppler effect (pitch shift based on velocity)
|
||||
source.dopplerLevel = 1.0f;
|
||||
|
||||
// Distance attenuation
|
||||
source.minDistance = 1f; // Full volume within this distance
|
||||
source.maxDistance = 50f; // Inaudible beyond this distance
|
||||
source.rolloffMode = AudioRolloffMode.Logarithmic; // Natural falloff
|
||||
```
|
||||
|
||||
### Volume Rolloff Curves
|
||||
- **Logarithmic**: Natural, realistic (RECOMMENDED)
|
||||
- **Linear**: Steady decrease
|
||||
- **Custom**: Define your own curve
|
||||
|
||||
---
|
||||
|
||||
## Audio Mixer (Advanced Mixing)
|
||||
|
||||
### Setup Audio Mixer
|
||||
|
||||
1. `Assets > Create > Audio Mixer`
|
||||
2. Open mixer: `Window > Audio > Audio Mixer`
|
||||
3. Create groups: Master > SFX, Music, Dialogue
|
||||
|
||||
### Assign AudioSource to Mixer Group
|
||||
|
||||
```csharp
|
||||
using UnityEngine.Audio;
|
||||
|
||||
public AudioMixerGroup sfxGroup;
|
||||
|
||||
void Start() {
|
||||
AudioSource source = GetComponent<AudioSource>();
|
||||
source.outputAudioMixerGroup = sfxGroup; // Route to SFX group
|
||||
}
|
||||
```
|
||||
|
||||
### Control Mixer from Code
|
||||
|
||||
```csharp
|
||||
using UnityEngine.Audio;
|
||||
|
||||
public AudioMixer audioMixer;
|
||||
|
||||
// ✅ Set volume (exposed parameter)
|
||||
audioMixer.SetFloat("MusicVolume", -10f); // dB (-80 to 0)
|
||||
|
||||
// ✅ Get volume
|
||||
audioMixer.GetFloat("MusicVolume", out float volume);
|
||||
|
||||
// Convert linear (0-1) to dB
|
||||
float volumeDB = Mathf.Log10(volumeLinear) * 20f;
|
||||
audioMixer.SetFloat("MusicVolume", volumeDB);
|
||||
```
|
||||
|
||||
### Expose Mixer Parameters
|
||||
In Audio Mixer window:
|
||||
1. Right-click parameter (e.g., Volume)
|
||||
2. "Expose 'Volume' to script"
|
||||
3. Rename in "Exposed Parameters" tab (e.g., "MusicVolume")
|
||||
|
||||
---
|
||||
|
||||
## Audio Effects
|
||||
|
||||
### Add Effects to Mixer Groups
|
||||
|
||||
In Audio Mixer:
|
||||
- Click group (e.g., SFX)
|
||||
- Click "Add Effect"
|
||||
- Choose: Reverb, Echo, Low Pass, High Pass, Distortion, etc.
|
||||
|
||||
### Duck Music During Dialogue (Sidechain)
|
||||
|
||||
```csharp
|
||||
// Setup in Audio Mixer:
|
||||
// 1. Create "Duck Volume" snapshot
|
||||
// 2. Lower music volume in that snapshot
|
||||
// 3. Transition to snapshot when dialogue plays
|
||||
|
||||
public AudioMixerSnapshot normalSnapshot;
|
||||
public AudioMixerSnapshot duckedSnapshot;
|
||||
|
||||
public void PlayDialogue(AudioClip clip) {
|
||||
duckedSnapshot.TransitionTo(0.5f); // 0.5s transition
|
||||
audioSource.PlayOneShot(clip);
|
||||
Invoke(nameof(RestoreMusic), clip.length);
|
||||
}
|
||||
|
||||
void RestoreMusic() {
|
||||
normalSnapshot.TransitionTo(1.0f); // 1s transition back
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Audio Performance
|
||||
|
||||
### Optimize Audio Loading
|
||||
|
||||
```csharp
|
||||
// Audio Import Settings (Inspector):
|
||||
// - Load Type:
|
||||
// - Decompress On Load: Small clips (SFX), loads fully into memory
|
||||
// - Compressed In Memory: Medium clips, decompressed at runtime (RECOMMENDED)
|
||||
// - Streaming: Large clips (music), streamed from disk
|
||||
|
||||
// Compression Format:
|
||||
// - PCM: Uncompressed, highest quality, largest size
|
||||
// - ADPCM: 3.5x compression, good for SFX (RECOMMENDED for SFX)
|
||||
// - Vorbis/MP3: High compression, good for music (RECOMMENDED for music)
|
||||
```
|
||||
|
||||
### Preload Audio
|
||||
|
||||
```csharp
|
||||
// Preload audio clip before playing (avoid stutter)
|
||||
audioSource.clip.LoadAudioData();
|
||||
|
||||
// Check if loaded
|
||||
if (audioSource.clip.loadState == AudioDataLoadState.Loaded) {
|
||||
audioSource.Play();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Music Systems
|
||||
|
||||
### Crossfade Between Tracks
|
||||
|
||||
```csharp
|
||||
public IEnumerator CrossfadeMusic(AudioSource from, AudioSource to, float duration) {
|
||||
float elapsed = 0f;
|
||||
to.Play();
|
||||
|
||||
while (elapsed < duration) {
|
||||
elapsed += Time.deltaTime;
|
||||
float t = elapsed / duration;
|
||||
|
||||
from.volume = Mathf.Lerp(1f, 0f, t);
|
||||
to.volume = Mathf.Lerp(0f, 1f, t);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
from.Stop();
|
||||
}
|
||||
```
|
||||
|
||||
### Seamless Music Looping
|
||||
|
||||
```csharp
|
||||
// Audio Import Settings:
|
||||
// - Check "Loop" for seamless music loops
|
||||
audioSource.loop = true;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Random Pitch Variation (Avoid Repetition)
|
||||
|
||||
```csharp
|
||||
void PlaySoundWithVariation(AudioClip clip) {
|
||||
AudioSource source = GetComponent<AudioSource>();
|
||||
source.pitch = Random.Range(0.9f, 1.1f); // ±10% pitch variation
|
||||
source.PlayOneShot(clip);
|
||||
}
|
||||
```
|
||||
|
||||
### Footstep Sounds (Random from Array)
|
||||
|
||||
```csharp
|
||||
public AudioClip[] footstepClips;
|
||||
|
||||
void PlayFootstep() {
|
||||
AudioClip clip = footstepClips[Random.Range(0, footstepClips.Length)];
|
||||
AudioSource.PlayClipAtPoint(clip, transform.position, 0.5f);
|
||||
}
|
||||
```
|
||||
|
||||
### Check if Sound is Playing
|
||||
|
||||
```csharp
|
||||
if (audioSource.isPlaying) {
|
||||
// Sound is currently playing
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Audio Listener
|
||||
|
||||
### Single Listener Rule
|
||||
- Only ONE `AudioListener` should be active at a time
|
||||
- Usually attached to Main Camera
|
||||
|
||||
```csharp
|
||||
// Disable extra listeners
|
||||
AudioListener listener = GetComponent<AudioListener>();
|
||||
listener.enabled = false;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Audio Window
|
||||
- `Window > Audio > Audio Mixer`
|
||||
- Visualize levels, test snapshots
|
||||
|
||||
### Audio Settings
|
||||
- `Edit > Project Settings > Audio`
|
||||
- Global volume, DSP buffer size, speaker mode
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unity3d.com/6000.0/Documentation/Manual/Audio.html
|
||||
- https://docs.unity3d.com/6000.0/Documentation/Manual/AudioMixer.html
|
||||
356
docs/engine-reference/unity/modules/input.md
Normal file
356
docs/engine-reference/unity/modules/input.md
Normal file
@@ -0,0 +1,356 @@
|
||||
# Unity 6.3 — Input Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** Unity 6 uses new Input System (legacy Input deprecated)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Unity 6 input systems:
|
||||
- **Input System Package** (RECOMMENDED): Cross-platform, rebindable, modern
|
||||
- **Legacy Input Manager**: Deprecated, avoid for new projects
|
||||
|
||||
---
|
||||
|
||||
## Key Changes from 2022 LTS
|
||||
|
||||
### Legacy Input Deprecated in Unity 6
|
||||
|
||||
```csharp
|
||||
// ❌ DEPRECATED: Input class
|
||||
if (Input.GetKeyDown(KeyCode.Space)) { }
|
||||
|
||||
// ✅ NEW: Input System package
|
||||
using UnityEngine.InputSystem;
|
||||
if (Keyboard.current.spaceKey.wasPressedThisFrame) { }
|
||||
```
|
||||
|
||||
**Migration Required:** Install `com.unity.inputsystem` package.
|
||||
|
||||
---
|
||||
|
||||
## Input System Package Setup
|
||||
|
||||
### Installation
|
||||
1. `Window > Package Manager`
|
||||
2. Search "Input System"
|
||||
3. Install package
|
||||
4. Restart Unity when prompted
|
||||
|
||||
### Enable New Input System
|
||||
`Edit > Project Settings > Player > Active Input Handling`:
|
||||
- **Input System Package (New)** ✅ Recommended
|
||||
- **Both** (for migration period)
|
||||
|
||||
---
|
||||
|
||||
## Input Actions (Recommended Pattern)
|
||||
|
||||
### Create Input Actions Asset
|
||||
|
||||
1. `Assets > Create > Input Actions`
|
||||
2. Name it (e.g., "PlayerControls")
|
||||
3. Open asset, define actions:
|
||||
|
||||
```
|
||||
Action Maps:
|
||||
Gameplay
|
||||
Actions:
|
||||
- Move (Value, Vector2)
|
||||
- Jump (Button)
|
||||
- Fire (Button)
|
||||
- Look (Value, Vector2)
|
||||
```
|
||||
|
||||
4. **Generate C# Class**: Check "Generate C# Class" in Inspector
|
||||
5. Click "Apply"
|
||||
|
||||
### Use Generated Input Class
|
||||
|
||||
```csharp
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
public class PlayerController : MonoBehaviour {
|
||||
private PlayerControls controls;
|
||||
|
||||
void Awake() {
|
||||
controls = new PlayerControls();
|
||||
|
||||
// Subscribe to actions
|
||||
controls.Gameplay.Jump.performed += ctx => Jump();
|
||||
controls.Gameplay.Fire.performed += ctx => Fire();
|
||||
}
|
||||
|
||||
void OnEnable() => controls.Enable();
|
||||
void OnDisable() => controls.Disable();
|
||||
|
||||
void Update() {
|
||||
// Read continuous input
|
||||
Vector2 move = controls.Gameplay.Move.ReadValue<Vector2>();
|
||||
transform.Translate(new Vector3(move.x, 0, move.y) * Time.deltaTime);
|
||||
|
||||
Vector2 look = controls.Gameplay.Look.ReadValue<Vector2>();
|
||||
// Apply camera rotation
|
||||
}
|
||||
|
||||
void Jump() {
|
||||
Debug.Log("Jump!");
|
||||
}
|
||||
|
||||
void Fire() {
|
||||
Debug.Log("Fire!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Direct Device Access (Quick & Dirty)
|
||||
|
||||
### Keyboard
|
||||
|
||||
```csharp
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
void Update() {
|
||||
// Current state
|
||||
if (Keyboard.current.spaceKey.isPressed) { }
|
||||
|
||||
// Just pressed this frame
|
||||
if (Keyboard.current.spaceKey.wasPressedThisFrame) { }
|
||||
|
||||
// Just released this frame
|
||||
if (Keyboard.current.spaceKey.wasReleasedThisFrame) { }
|
||||
}
|
||||
```
|
||||
|
||||
### Mouse
|
||||
|
||||
```csharp
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
void Update() {
|
||||
// Mouse position
|
||||
Vector2 mousePos = Mouse.current.position.ReadValue();
|
||||
|
||||
// Mouse delta (movement)
|
||||
Vector2 mouseDelta = Mouse.current.delta.ReadValue();
|
||||
|
||||
// Mouse buttons
|
||||
if (Mouse.current.leftButton.wasPressedThisFrame) { }
|
||||
if (Mouse.current.rightButton.isPressed) { }
|
||||
|
||||
// Scroll wheel
|
||||
Vector2 scroll = Mouse.current.scroll.ReadValue();
|
||||
}
|
||||
```
|
||||
|
||||
### Gamepad
|
||||
|
||||
```csharp
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
void Update() {
|
||||
Gamepad gamepad = Gamepad.current;
|
||||
if (gamepad == null) return; // No gamepad connected
|
||||
|
||||
// Buttons
|
||||
if (gamepad.buttonSouth.wasPressedThisFrame) { } // A/Cross
|
||||
if (gamepad.buttonWest.wasPressedThisFrame) { } // X/Square
|
||||
|
||||
// Sticks
|
||||
Vector2 leftStick = gamepad.leftStick.ReadValue();
|
||||
Vector2 rightStick = gamepad.rightStick.ReadValue();
|
||||
|
||||
// Triggers
|
||||
float leftTrigger = gamepad.leftTrigger.ReadValue();
|
||||
float rightTrigger = gamepad.rightTrigger.ReadValue();
|
||||
|
||||
// D-Pad
|
||||
Vector2 dpad = gamepad.dpad.ReadValue();
|
||||
}
|
||||
```
|
||||
|
||||
### Touch (Mobile)
|
||||
|
||||
```csharp
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.EnhancedTouch;
|
||||
|
||||
void OnEnable() {
|
||||
EnhancedTouchSupport.Enable();
|
||||
}
|
||||
|
||||
void Update() {
|
||||
foreach (var touch in UnityEngine.InputSystem.EnhancedTouch.Touch.activeTouches) {
|
||||
Debug.Log($"Touch at {touch.screenPosition}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Input Action Callbacks
|
||||
|
||||
### Action Callbacks (Event-Driven)
|
||||
|
||||
```csharp
|
||||
// started: Input began (e.g., trigger pressed slightly)
|
||||
controls.Gameplay.Fire.started += ctx => Debug.Log("Fire started");
|
||||
|
||||
// performed: Input action triggered (e.g., button fully pressed)
|
||||
controls.Gameplay.Fire.performed += ctx => Debug.Log("Fire performed");
|
||||
|
||||
// canceled: Input released or interrupted
|
||||
controls.Gameplay.Fire.canceled += ctx => Debug.Log("Fire canceled");
|
||||
```
|
||||
|
||||
### Context Data
|
||||
|
||||
```csharp
|
||||
controls.Gameplay.Move.performed += ctx => {
|
||||
Vector2 value = ctx.ReadValue<Vector2>();
|
||||
float duration = ctx.duration; // How long input held
|
||||
InputControl control = ctx.control; // Which device/control triggered it
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Control Schemes & Device Switching
|
||||
|
||||
### Define Control Schemes in Input Actions Asset
|
||||
|
||||
```
|
||||
Control Schemes:
|
||||
- Keyboard&Mouse (Keyboard, Mouse)
|
||||
- Gamepad (Gamepad)
|
||||
- Touch (Touchscreen)
|
||||
```
|
||||
|
||||
### Auto-Switch on Device Change
|
||||
|
||||
```csharp
|
||||
controls.Gameplay.Move.performed += ctx => {
|
||||
if (ctx.control.device is Keyboard) {
|
||||
Debug.Log("Using keyboard");
|
||||
} else if (ctx.control.device is Gamepad) {
|
||||
Debug.Log("Using gamepad");
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rebinding (Runtime Key Mapping)
|
||||
|
||||
### Interactive Rebind
|
||||
|
||||
```csharp
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
public void RebindJumpKey() {
|
||||
var rebindOperation = controls.Gameplay.Jump.PerformInteractiveRebinding()
|
||||
.WithControlsExcluding("Mouse") // Exclude mouse bindings
|
||||
.OnComplete(operation => {
|
||||
Debug.Log("Rebind complete");
|
||||
operation.Dispose();
|
||||
})
|
||||
.Start();
|
||||
}
|
||||
```
|
||||
|
||||
### Save/Load Bindings
|
||||
|
||||
```csharp
|
||||
// Save
|
||||
string rebinds = controls.SaveBindingOverridesAsJson();
|
||||
PlayerPrefs.SetString("InputBindings", rebinds);
|
||||
|
||||
// Load
|
||||
string rebinds = PlayerPrefs.GetString("InputBindings");
|
||||
controls.LoadBindingOverridesFromJson(rebinds);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Action Types
|
||||
|
||||
### Button (Press/Release)
|
||||
- Single press/release
|
||||
- Example: Jump, Fire
|
||||
|
||||
### Value (Continuous)
|
||||
- Continuous value (float, Vector2)
|
||||
- Example: Move, Look, Aim
|
||||
|
||||
### Pass-Through (Immediate)
|
||||
- No processing, immediate value
|
||||
- Example: Mouse position
|
||||
|
||||
---
|
||||
|
||||
## Processors (Input Modifiers)
|
||||
|
||||
### Scale
|
||||
|
||||
```csharp
|
||||
// In Input Actions asset: Action > Properties > Processors > Add > Scale
|
||||
// Multiply input by value (e.g., invert Y-axis)
|
||||
```
|
||||
|
||||
### Invert
|
||||
|
||||
```csharp
|
||||
// In Input Actions asset: Action > Properties > Processors > Add > Invert
|
||||
// Flip input sign
|
||||
```
|
||||
|
||||
### Dead Zone
|
||||
|
||||
```csharp
|
||||
// In Input Actions asset: Action > Properties > Processors > Add > Stick Deadzone
|
||||
// Ignore small stick movements
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## PlayerInput Component (Simplified Setup)
|
||||
|
||||
### Automatic Input Setup
|
||||
|
||||
```csharp
|
||||
// Add Component: Player Input
|
||||
// Assign Input Actions asset
|
||||
// Behavior: Send Messages / Invoke Unity Events / Invoke C# Events
|
||||
|
||||
// Send Messages example:
|
||||
public class Player : MonoBehaviour {
|
||||
public void OnMove(InputValue value) {
|
||||
Vector2 move = value.Get<Vector2>();
|
||||
// Handle movement
|
||||
}
|
||||
|
||||
public void OnJump(InputValue value) {
|
||||
if (value.isPressed) {
|
||||
Jump();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Input Debugger
|
||||
- `Window > Analysis > Input Debugger`
|
||||
- See active devices, input values, action states
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unity3d.com/Packages/com.unity.inputsystem@1.11/manual/index.html
|
||||
- https://docs.unity3d.com/Packages/com.unity.inputsystem@1.11/manual/QuickStartGuide.html
|
||||
330
docs/engine-reference/unity/modules/navigation.md
Normal file
330
docs/engine-reference/unity/modules/navigation.md
Normal file
@@ -0,0 +1,330 @@
|
||||
# Unity 6.3 — Navigation Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** Unity 6 NavMesh improvements
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Unity 6 navigation systems:
|
||||
- **NavMesh**: Built-in pathfinding for AI agents
|
||||
- **NavMeshComponents**: Package for runtime NavMesh building
|
||||
|
||||
---
|
||||
|
||||
## NavMesh Basics
|
||||
|
||||
### Bake Navigation Mesh
|
||||
|
||||
1. Mark walkable surfaces:
|
||||
- Select GameObject (floor/terrain)
|
||||
- Inspector > Navigation > Object tab
|
||||
- Check "Navigation Static"
|
||||
|
||||
2. Bake NavMesh:
|
||||
- `Window > AI > Navigation`
|
||||
- Bake tab
|
||||
- Click "Bake"
|
||||
|
||||
3. Configure settings:
|
||||
- **Agent Radius**: How wide the agent is (0.5m default)
|
||||
- **Agent Height**: How tall the agent is (2m default)
|
||||
- **Max Slope**: Maximum walkable slope (45° default)
|
||||
- **Step Height**: Maximum climbable step (0.4m default)
|
||||
|
||||
---
|
||||
|
||||
## NavMeshAgent (AI Movement)
|
||||
|
||||
### Basic Agent Setup
|
||||
|
||||
```csharp
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
|
||||
public class Enemy : MonoBehaviour {
|
||||
private NavMeshAgent agent;
|
||||
public Transform target;
|
||||
|
||||
void Start() {
|
||||
agent = GetComponent<NavMeshAgent>();
|
||||
}
|
||||
|
||||
void Update() {
|
||||
// ✅ Move to target
|
||||
agent.SetDestination(target.position);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### NavMeshAgent Properties
|
||||
|
||||
```csharp
|
||||
NavMeshAgent agent = GetComponent<NavMeshAgent>();
|
||||
|
||||
// Speed
|
||||
agent.speed = 3.5f;
|
||||
|
||||
// Acceleration
|
||||
agent.acceleration = 8f;
|
||||
|
||||
// Stopping distance
|
||||
agent.stoppingDistance = 2f; // Stop 2m before destination
|
||||
|
||||
// Auto-braking (slow down at destination)
|
||||
agent.autoBraking = true;
|
||||
|
||||
// Rotation speed
|
||||
agent.angularSpeed = 120f; // Degrees per second
|
||||
|
||||
// Obstacle avoidance
|
||||
agent.obstacleAvoidanceType = ObstacleAvoidanceType.HighQualityObstacleAvoidance;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Check Path Status
|
||||
|
||||
```csharp
|
||||
void Update() {
|
||||
agent.SetDestination(target.position);
|
||||
|
||||
// Check if agent has a path
|
||||
if (agent.hasPath) {
|
||||
// Check if path is complete
|
||||
if (agent.pathStatus == NavMeshPathStatus.PathComplete) {
|
||||
Debug.Log("Valid path");
|
||||
} else if (agent.pathStatus == NavMeshPathStatus.PathPartial) {
|
||||
Debug.Log("Partial path (destination unreachable)");
|
||||
} else {
|
||||
Debug.Log("Invalid path");
|
||||
}
|
||||
}
|
||||
|
||||
// Check if agent reached destination
|
||||
if (!agent.pathPending && agent.remainingDistance <= agent.stoppingDistance) {
|
||||
Debug.Log("Reached destination");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Calculate Path (Don't Move Yet)
|
||||
|
||||
```csharp
|
||||
NavMeshPath path = new NavMeshPath();
|
||||
agent.CalculatePath(targetPosition, path);
|
||||
|
||||
if (path.status == NavMeshPathStatus.PathComplete) {
|
||||
// Valid path exists
|
||||
agent.SetPath(path); // Apply the path
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## NavMesh Areas (Walkable Costs)
|
||||
|
||||
### Define Areas
|
||||
`Window > AI > Navigation > Areas tab`
|
||||
- **Walkable**: Cost 1 (default)
|
||||
- **Not Walkable**: Unwalkable
|
||||
- **Jump**: Cost 2 (prefer other routes)
|
||||
- **Custom**: Define your own
|
||||
|
||||
### Assign Area Costs
|
||||
|
||||
```csharp
|
||||
// Prefer shorter paths over low-cost paths
|
||||
agent.areaMask = NavMesh.AllAreas; // Walk on all areas
|
||||
|
||||
// Only walk on "Walkable" area (avoid "Jump")
|
||||
agent.areaMask = 1 << NavMesh.GetAreaFromName("Walkable");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## NavMesh Obstacles (Dynamic Obstacles)
|
||||
|
||||
### NavMeshObstacle Component
|
||||
|
||||
```csharp
|
||||
// Add: GameObject > Add Component > NavMesh Obstacle
|
||||
|
||||
// Carve: Create hole in NavMesh (agents avoid)
|
||||
// Don't Carve: Agent pushes through (local avoidance)
|
||||
```
|
||||
|
||||
### Dynamic Carving (Moving Obstacles)
|
||||
|
||||
```csharp
|
||||
NavMeshObstacle obstacle = GetComponent<NavMeshObstacle>();
|
||||
obstacle.carving = true; // Create dynamic hole in NavMesh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Off-Mesh Links (Jumps, Teleports)
|
||||
|
||||
### Create Off-Mesh Link
|
||||
|
||||
1. `GameObject > Create Empty` (at jump start)
|
||||
2. Add `Off Mesh Link` component
|
||||
3. Set Start/End transforms
|
||||
4. Configure:
|
||||
- **Bi-Directional**: Can traverse both ways
|
||||
- **Cost Override**: Path cost for this link
|
||||
|
||||
### Detect Off-Mesh Link Traversal
|
||||
|
||||
```csharp
|
||||
void Update() {
|
||||
// Check if agent is on an off-mesh link
|
||||
if (agent.isOnOffMeshLink) {
|
||||
// Manually traverse (e.g., play jump animation)
|
||||
StartCoroutine(TraverseOffMeshLink());
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator TraverseOffMeshLink() {
|
||||
OffMeshLinkData data = agent.currentOffMeshLinkData;
|
||||
Vector3 startPos = agent.transform.position;
|
||||
Vector3 endPos = data.endPos;
|
||||
|
||||
float duration = 0.5f;
|
||||
float elapsed = 0f;
|
||||
|
||||
while (elapsed < duration) {
|
||||
agent.transform.position = Vector3.Lerp(startPos, endPos, elapsed / duration);
|
||||
elapsed += Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
agent.CompleteOffMeshLink(); // Resume normal pathfinding
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## NavMeshComponents Package (Runtime Baking)
|
||||
|
||||
### Installation
|
||||
1. `Window > Package Manager`
|
||||
2. Add from Git URL: `com.unity.ai.navigation`
|
||||
|
||||
### Runtime NavMesh Baking
|
||||
|
||||
```csharp
|
||||
using Unity.AI.Navigation;
|
||||
|
||||
public class NavMeshBuilder : MonoBehaviour {
|
||||
public NavMeshSurface surface;
|
||||
|
||||
void Start() {
|
||||
// Bake NavMesh at runtime
|
||||
surface.BuildNavMesh();
|
||||
}
|
||||
|
||||
void UpdateNavMesh() {
|
||||
// Update NavMesh after terrain changes
|
||||
surface.UpdateNavMesh(surface.navMeshData);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Patrol Between Waypoints
|
||||
|
||||
```csharp
|
||||
public Transform[] waypoints;
|
||||
private int currentWaypoint = 0;
|
||||
|
||||
void Update() {
|
||||
if (!agent.pathPending && agent.remainingDistance < 0.5f) {
|
||||
// Reached waypoint, move to next
|
||||
currentWaypoint = (currentWaypoint + 1) % waypoints.Length;
|
||||
agent.SetDestination(waypoints[currentWaypoint].position);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Chase Player
|
||||
|
||||
```csharp
|
||||
public Transform player;
|
||||
public float chaseRange = 10f;
|
||||
|
||||
void Update() {
|
||||
float distance = Vector3.Distance(transform.position, player.position);
|
||||
|
||||
if (distance <= chaseRange) {
|
||||
agent.SetDestination(player.position);
|
||||
} else {
|
||||
agent.ResetPath(); // Stop moving
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Flee from Player
|
||||
|
||||
```csharp
|
||||
public Transform player;
|
||||
public float fleeRange = 5f;
|
||||
|
||||
void Update() {
|
||||
float distance = Vector3.Distance(transform.position, player.position);
|
||||
|
||||
if (distance <= fleeRange) {
|
||||
// Run away from player
|
||||
Vector3 fleeDirection = transform.position - player.position;
|
||||
Vector3 fleeTarget = transform.position + fleeDirection.normalized * 10f;
|
||||
|
||||
agent.SetDestination(fleeTarget);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### NavMesh Visualization
|
||||
- `Window > AI > Navigation > Bake tab`
|
||||
- Check "Show NavMesh" to visualize walkable areas
|
||||
|
||||
### Agent Path Gizmos
|
||||
|
||||
```csharp
|
||||
void OnDrawGizmos() {
|
||||
if (agent != null && agent.hasPath) {
|
||||
Gizmos.color = Color.green;
|
||||
Vector3[] corners = agent.path.corners;
|
||||
|
||||
for (int i = 0; i < corners.Length - 1; i++) {
|
||||
Gizmos.DrawLine(corners[i], corners[i + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Tips
|
||||
|
||||
- **Limit Obstacle Avoidance Quality**: Use `LowQualityObstacleAvoidance` for distant agents
|
||||
- **Update Frequency**: Don't call `SetDestination()` every frame if target hasn't moved
|
||||
- **Area Masks**: Limit walkable areas to reduce pathfinding search space
|
||||
- **NavMesh Tiles**: Use tiled NavMesh for large worlds (NavMeshComponents package)
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unity3d.com/6000.0/Documentation/Manual/Navigation.html
|
||||
- https://docs.unity3d.com/Packages/com.unity.ai.navigation@2.0/manual/index.html
|
||||
351
docs/engine-reference/unity/modules/networking.md
Normal file
351
docs/engine-reference/unity/modules/networking.md
Normal file
@@ -0,0 +1,351 @@
|
||||
# Unity 6.3 — Networking Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** Unity 6 uses Netcode for GameObjects (UNet deprecated)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Unity 6 networking options:
|
||||
- **Netcode for GameObjects** (RECOMMENDED): Official Unity multiplayer framework
|
||||
- **Mirror**: Community-driven (UNet successor)
|
||||
- **Photon**: Third-party service (PUN2)
|
||||
- **Custom**: Low-level sockets
|
||||
|
||||
**UNet (Legacy)**: Deprecated, do not use.
|
||||
|
||||
---
|
||||
|
||||
## Netcode for GameObjects
|
||||
|
||||
### Installation
|
||||
1. `Window > Package Manager`
|
||||
2. Search "Netcode for GameObjects"
|
||||
3. Install `com.unity.netcode.gameobjects`
|
||||
|
||||
---
|
||||
|
||||
## Basic Setup
|
||||
|
||||
### NetworkManager
|
||||
|
||||
```csharp
|
||||
// Add to scene: GameObject > Add Component > NetworkManager
|
||||
|
||||
// Or create custom NetworkManager:
|
||||
using Unity.Netcode;
|
||||
|
||||
public class CustomNetworkManager : MonoBehaviour {
|
||||
void Start() {
|
||||
NetworkManager.Singleton.StartHost(); // Server + client
|
||||
// OR
|
||||
NetworkManager.Singleton.StartServer(); // Dedicated server
|
||||
// OR
|
||||
NetworkManager.Singleton.StartClient(); // Client only
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## NetworkObject (Networked GameObjects)
|
||||
|
||||
### Mark GameObject as Networked
|
||||
|
||||
1. Add `NetworkObject` component to GameObject
|
||||
2. Must be in root of prefab (not nested)
|
||||
3. Register prefab in `NetworkManager > NetworkPrefabs List`
|
||||
|
||||
### Spawn Network Objects
|
||||
|
||||
```csharp
|
||||
using Unity.Netcode;
|
||||
|
||||
public class GameManager : NetworkBehaviour {
|
||||
public GameObject playerPrefab;
|
||||
|
||||
[ServerRpc(RequireOwnership = false)]
|
||||
public void SpawnPlayerServerRpc(ulong clientId) {
|
||||
GameObject player = Instantiate(playerPrefab);
|
||||
player.GetComponent<NetworkObject>().SpawnAsPlayerObject(clientId);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## NetworkBehaviour (Networked Scripts)
|
||||
|
||||
### NetworkBehaviour Base Class
|
||||
|
||||
```csharp
|
||||
using Unity.Netcode;
|
||||
|
||||
public class Player : NetworkBehaviour {
|
||||
// Called when spawned on network
|
||||
public override void OnNetworkSpawn() {
|
||||
if (IsOwner) {
|
||||
// Only run on owner's client
|
||||
GetComponent<Camera>().enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Update() {
|
||||
if (!IsOwner) return; // Only owner can control
|
||||
|
||||
// Handle input
|
||||
if (Input.GetKey(KeyCode.W)) {
|
||||
MoveServerRpc(Vector3.forward);
|
||||
}
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
void MoveServerRpc(Vector3 direction) {
|
||||
// Runs on server
|
||||
transform.position += direction * Time.deltaTime;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Network Variables (Synchronized State)
|
||||
|
||||
### NetworkVariable<T>
|
||||
|
||||
```csharp
|
||||
using Unity.Netcode;
|
||||
|
||||
public class Player : NetworkBehaviour {
|
||||
// ✅ Auto-synced across clients
|
||||
private NetworkVariable<int> health = new NetworkVariable<int>(100);
|
||||
|
||||
public override void OnNetworkSpawn() {
|
||||
// Subscribe to value changes
|
||||
health.OnValueChanged += OnHealthChanged;
|
||||
}
|
||||
|
||||
void OnHealthChanged(int oldValue, int newValue) {
|
||||
Debug.Log($"Health changed: {oldValue} -> {newValue}");
|
||||
UpdateHealthUI(newValue);
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
public void TakeDamageServerRpc(int damage) {
|
||||
// Only server can modify NetworkVariable
|
||||
health.Value -= damage;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### NetworkVariable Permissions
|
||||
|
||||
```csharp
|
||||
// Server can write, clients read-only (default)
|
||||
private NetworkVariable<int> score = new NetworkVariable<int>();
|
||||
|
||||
// Owner can write
|
||||
private NetworkVariable<int> ammo = new NetworkVariable<int>(
|
||||
default,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Owner
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## RPCs (Remote Procedure Calls)
|
||||
|
||||
### ServerRpc (Client → Server)
|
||||
|
||||
```csharp
|
||||
// Client calls, server executes
|
||||
[ServerRpc]
|
||||
void FireWeaponServerRpc() {
|
||||
// Runs on server
|
||||
Debug.Log("Server: Weapon fired");
|
||||
}
|
||||
|
||||
// Call from client:
|
||||
if (IsOwner && Input.GetKeyDown(KeyCode.Space)) {
|
||||
FireWeaponServerRpc();
|
||||
}
|
||||
```
|
||||
|
||||
### ClientRpc (Server → All Clients)
|
||||
|
||||
```csharp
|
||||
// Server calls, all clients execute
|
||||
[ClientRpc]
|
||||
void PlayExplosionClientRpc(Vector3 position) {
|
||||
// Runs on all clients
|
||||
Instantiate(explosionPrefab, position, Quaternion.identity);
|
||||
}
|
||||
|
||||
// Call from server:
|
||||
[ServerRpc]
|
||||
void ExplodeServerRpc(Vector3 position) {
|
||||
// Server logic
|
||||
DealDamageToNearbyPlayers(position);
|
||||
|
||||
// Notify all clients
|
||||
PlayExplosionClientRpc(position);
|
||||
}
|
||||
```
|
||||
|
||||
### RPC Parameters
|
||||
|
||||
```csharp
|
||||
// ✅ Supported: Primitives, structs, strings, arrays
|
||||
[ServerRpc]
|
||||
void SetNameServerRpc(string playerName) { }
|
||||
|
||||
[ClientRpc]
|
||||
void UpdateScoresClientRpc(int[] scores) { }
|
||||
|
||||
// ❌ Not supported: MonoBehaviour, GameObject (use NetworkObjectReference)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Network Ownership
|
||||
|
||||
### Check Ownership
|
||||
|
||||
```csharp
|
||||
if (IsOwner) {
|
||||
// This client owns this NetworkObject
|
||||
}
|
||||
|
||||
if (IsServer) {
|
||||
// Running on server
|
||||
}
|
||||
|
||||
if (IsClient) {
|
||||
// Running on client
|
||||
}
|
||||
|
||||
if (IsLocalPlayer) {
|
||||
// This is the local player object
|
||||
}
|
||||
```
|
||||
|
||||
### Transfer Ownership
|
||||
|
||||
```csharp
|
||||
// Server transfers ownership
|
||||
NetworkObject netObj = GetComponent<NetworkObject>();
|
||||
netObj.ChangeOwnership(newOwnerClientId);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## NetworkObjectReference (Pass GameObjects in RPCs)
|
||||
|
||||
```csharp
|
||||
using Unity.Netcode;
|
||||
|
||||
[ServerRpc]
|
||||
void AttackTargetServerRpc(NetworkObjectReference targetRef) {
|
||||
if (targetRef.TryGet(out NetworkObject target)) {
|
||||
// Got the target object
|
||||
target.GetComponent<Health>().TakeDamage(10);
|
||||
}
|
||||
}
|
||||
|
||||
// Call:
|
||||
NetworkObject targetNetObj = target.GetComponent<NetworkObject>();
|
||||
AttackTargetServerRpc(targetNetObj);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Client-Server Architecture
|
||||
|
||||
### Server-Authoritative Pattern (RECOMMENDED)
|
||||
|
||||
```csharp
|
||||
public class Player : NetworkBehaviour {
|
||||
private NetworkVariable<Vector3> position = new NetworkVariable<Vector3>();
|
||||
|
||||
void Update() {
|
||||
if (IsOwner) {
|
||||
// Client: Send input to server
|
||||
Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
|
||||
MoveServerRpc(input);
|
||||
}
|
||||
|
||||
// All clients: Sync to networked position
|
||||
transform.position = position.Value;
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
void MoveServerRpc(Vector3 input) {
|
||||
// Server: Validate and apply movement
|
||||
position.Value += input * Time.deltaTime * moveSpeed;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Network Transport
|
||||
|
||||
### Unity Transport (Default)
|
||||
|
||||
```csharp
|
||||
// Configured in NetworkManager:
|
||||
// - Transport: Unity Transport
|
||||
// - Address: 127.0.0.1 (localhost) or server IP
|
||||
// - Port: 7777 (default)
|
||||
```
|
||||
|
||||
### Connection Events
|
||||
|
||||
```csharp
|
||||
void Start() {
|
||||
NetworkManager.Singleton.OnClientConnectedCallback += OnClientConnected;
|
||||
NetworkManager.Singleton.OnClientDisconnectCallback += OnClientDisconnected;
|
||||
}
|
||||
|
||||
void OnClientConnected(ulong clientId) {
|
||||
Debug.Log($"Client {clientId} connected");
|
||||
}
|
||||
|
||||
void OnClientDisconnected(ulong clientId) {
|
||||
Debug.Log($"Client {clientId} disconnected");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Tips
|
||||
|
||||
### Reduce Network Traffic
|
||||
- Use `NetworkVariable` for state that changes infrequently
|
||||
- Batch multiple changes before syncing
|
||||
- Use delta compression for large data
|
||||
|
||||
### Prediction & Reconciliation
|
||||
- Run movement locally for responsiveness
|
||||
- Reconcile with server authoritative state
|
||||
- Use interpolation for smooth movement
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Network Profiler
|
||||
- `Window > Analysis > Network Profiler`
|
||||
- Monitor bandwidth, RPC calls, variable updates
|
||||
|
||||
### Network Simulator (Test Latency/Packet Loss)
|
||||
- `NetworkManager > Network Simulator`
|
||||
- Add artificial lag and packet loss for testing
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs-multiplayer.unity3d.com/netcode/current/about/
|
||||
- https://docs-multiplayer.unity3d.com/netcode/current/learn/bossroom/
|
||||
268
docs/engine-reference/unity/modules/physics.md
Normal file
268
docs/engine-reference/unity/modules/physics.md
Normal file
@@ -0,0 +1,268 @@
|
||||
# Unity 6.3 — Physics Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** Unity 6 physics improvements, solver changes
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Unity 6.3 uses **PhysX 5.1** (improved from PhysX 4.x in 2022 LTS):
|
||||
- Better solver stability
|
||||
- Improved performance
|
||||
- Enhanced collision detection
|
||||
|
||||
---
|
||||
|
||||
## Key Changes from 2022 LTS
|
||||
|
||||
### Default Solver Iterations Increased
|
||||
Unity 6 increased default solver iterations for better stability:
|
||||
|
||||
```csharp
|
||||
// Default changed from 6 to 8 iterations
|
||||
Physics.defaultSolverIterations = 8; // Check if relying on old behavior
|
||||
```
|
||||
|
||||
### Enhanced Collision Detection
|
||||
|
||||
```csharp
|
||||
// ✅ Unity 6: Improved Continuous Collision Detection (CCD)
|
||||
rigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
|
||||
// Better handling of fast-moving objects
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Core Physics Components
|
||||
|
||||
### Rigidbody
|
||||
|
||||
```csharp
|
||||
// ✅ Best practice: Use AddForce, not direct velocity writes
|
||||
Rigidbody rb = GetComponent<Rigidbody>();
|
||||
rb.AddForce(Vector3.forward * 10f, ForceMode.Impulse);
|
||||
|
||||
// ❌ Avoid: Direct velocity assignment (can cause instability)
|
||||
rb.velocity = new Vector3(0, 10, 0); // Only use when necessary
|
||||
```
|
||||
|
||||
### Colliders
|
||||
|
||||
```csharp
|
||||
// Primitive colliders: Box, Sphere, Capsule (cheapest)
|
||||
// Mesh colliders: Expensive, use only for static geometry
|
||||
|
||||
// ✅ Compound colliders (multiple primitives) > single mesh collider
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Raycasting
|
||||
|
||||
### Efficient Raycasting (Avoid Allocations)
|
||||
|
||||
```csharp
|
||||
// ✅ Non-allocating raycast
|
||||
if (Physics.Raycast(origin, direction, out RaycastHit hit, maxDistance)) {
|
||||
Debug.Log($"Hit: {hit.collider.name}");
|
||||
}
|
||||
|
||||
// ✅ Multiple hits (non-allocating)
|
||||
RaycastHit[] results = new RaycastHit[10];
|
||||
int hitCount = Physics.RaycastNonAlloc(origin, direction, results, maxDistance);
|
||||
for (int i = 0; i < hitCount; i++) {
|
||||
Debug.Log($"Hit {i}: {results[i].collider.name}");
|
||||
}
|
||||
|
||||
// ❌ Avoid: RaycastAll (allocates array every call)
|
||||
RaycastHit[] hits = Physics.RaycastAll(origin, direction); // GC allocation!
|
||||
```
|
||||
|
||||
### LayerMask for Selective Raycasting
|
||||
|
||||
```csharp
|
||||
// ✅ Use LayerMask to filter collisions
|
||||
int layerMask = 1 << LayerMask.NameToLayer("Enemy");
|
||||
Physics.Raycast(origin, direction, out RaycastHit hit, maxDistance, layerMask);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Physics Queries
|
||||
|
||||
### OverlapSphere (Check for nearby objects)
|
||||
|
||||
```csharp
|
||||
// ✅ Non-allocating version
|
||||
Collider[] results = new Collider[10];
|
||||
int count = Physics.OverlapSphereNonAlloc(center, radius, results);
|
||||
for (int i = 0; i < count; i++) {
|
||||
// Process results[i]
|
||||
}
|
||||
```
|
||||
|
||||
### SphereCast (Thick raycast)
|
||||
|
||||
```csharp
|
||||
// Useful for character controllers
|
||||
if (Physics.SphereCast(origin, radius, direction, out RaycastHit hit, maxDistance)) {
|
||||
// Hit something with a sphere-shaped ray
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Collision Events
|
||||
|
||||
### OnCollisionEnter / Stay / Exit
|
||||
|
||||
```csharp
|
||||
void OnCollisionEnter(Collision collision) {
|
||||
// Triggered when collision starts
|
||||
Debug.Log($"Collided with {collision.gameObject.name}");
|
||||
|
||||
// Access contact points
|
||||
foreach (ContactPoint contact in collision.contacts) {
|
||||
Debug.DrawRay(contact.point, contact.normal, Color.red, 2f);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### OnTriggerEnter / Stay / Exit
|
||||
|
||||
```csharp
|
||||
void OnTriggerEnter(Collider other) {
|
||||
// Trigger collider (Is Trigger = true)
|
||||
if (other.CompareTag("Pickup")) {
|
||||
Destroy(other.gameObject);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Character Controllers
|
||||
|
||||
### CharacterController Component
|
||||
|
||||
```csharp
|
||||
CharacterController controller = GetComponent<CharacterController>();
|
||||
|
||||
// ✅ Move with collision detection
|
||||
Vector3 move = transform.forward * speed * Time.deltaTime;
|
||||
controller.Move(move);
|
||||
|
||||
// Apply gravity manually
|
||||
if (!controller.isGrounded) {
|
||||
velocity.y += Physics.gravity.y * Time.deltaTime;
|
||||
}
|
||||
controller.Move(velocity * Time.deltaTime);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Physics Materials
|
||||
|
||||
### Friction & Bounciness
|
||||
|
||||
```csharp
|
||||
// Create: Assets > Create > Physic Material
|
||||
// Assign to collider: Collider > Material
|
||||
|
||||
// PhysicMaterial settings:
|
||||
// - Dynamic Friction: 0.6 (sliding friction)
|
||||
// - Static Friction: 0.6 (starting friction)
|
||||
// - Bounciness: 0.0 - 1.0
|
||||
// - Friction Combine: Average, Minimum, Maximum, Multiply
|
||||
// - Bounce Combine: Average, Minimum, Maximum, Multiply
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Joints
|
||||
|
||||
### Fixed Joint (Attach two rigidbodies)
|
||||
|
||||
```csharp
|
||||
FixedJoint joint = gameObject.AddComponent<FixedJoint>();
|
||||
joint.connectedBody = otherRigidbody;
|
||||
```
|
||||
|
||||
### Hinge Joint (Door, wheel)
|
||||
|
||||
```csharp
|
||||
HingeJoint hinge = gameObject.AddComponent<HingeJoint>();
|
||||
hinge.axis = Vector3.up; // Rotation axis
|
||||
hinge.useLimits = true;
|
||||
hinge.limits = new JointLimits { min = -90, max = 90 };
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Physics Layer Collision Matrix
|
||||
`Edit > Project Settings > Physics > Layer Collision Matrix`
|
||||
- Disable unnecessary collision checks between layers
|
||||
- Massive performance gain
|
||||
|
||||
### Fixed Timestep
|
||||
`Edit > Project Settings > Time > Fixed Timestep`
|
||||
- Default: 0.02 (50 FPS physics)
|
||||
- Lower = more accurate, higher CPU cost
|
||||
- Match game's target framerate if possible
|
||||
|
||||
### Simplified Collision Geometry
|
||||
- Use primitive colliders (box, sphere, capsule) over mesh colliders
|
||||
- Bake mesh colliders at build time, not runtime
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Ground Check (Character Controller)
|
||||
|
||||
```csharp
|
||||
bool IsGrounded() {
|
||||
float rayLength = 0.1f;
|
||||
return Physics.Raycast(transform.position, Vector3.down, rayLength);
|
||||
}
|
||||
```
|
||||
|
||||
### Apply Explosion Force
|
||||
|
||||
```csharp
|
||||
void ApplyExplosion(Vector3 explosionPos, float radius, float force) {
|
||||
Collider[] colliders = Physics.OverlapSphere(explosionPos, radius);
|
||||
foreach (Collider hit in colliders) {
|
||||
Rigidbody rb = hit.GetComponent<Rigidbody>();
|
||||
if (rb != null) {
|
||||
rb.AddExplosionForce(force, explosionPos, radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Physics Debugger (Unity 6+)
|
||||
- `Window > Analysis > Physics Debugger`
|
||||
- Visualize colliders, contacts, queries
|
||||
|
||||
### Gizmos
|
||||
|
||||
```csharp
|
||||
void OnDrawGizmos() {
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawWireSphere(transform.position, detectionRadius);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unity3d.com/6000.0/Documentation/Manual/PhysicsOverview.html
|
||||
- https://docs.unity3d.com/ScriptReference/Physics.html
|
||||
238
docs/engine-reference/unity/modules/rendering.md
Normal file
238
docs/engine-reference/unity/modules/rendering.md
Normal file
@@ -0,0 +1,238 @@
|
||||
# Unity 6.3 — Rendering Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** LLM trained on Unity 2022 LTS; Unity 6 has major rendering changes
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Unity 6.3 LTS uses **Scriptable Render Pipelines (SRP)** as the modern rendering architecture:
|
||||
- **URP (Universal Render Pipeline)**: Cross-platform, mobile-friendly (RECOMMENDED)
|
||||
- **HDRP (High Definition Render Pipeline)**: High-end PC/console, photorealistic
|
||||
- **Built-in Pipeline**: Deprecated, avoid for new projects
|
||||
|
||||
---
|
||||
|
||||
## Key Changes from 2022 LTS
|
||||
|
||||
### RenderGraph API (Unity 6+)
|
||||
Custom render passes now use RenderGraph instead of CommandBuffer:
|
||||
|
||||
```csharp
|
||||
// ✅ Unity 6+ (RenderGraph)
|
||||
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) {
|
||||
using var builder = renderGraph.AddRasterRenderPass<PassData>("MyPass", out var passData);
|
||||
builder.SetRenderFunc((PassData data, RasterGraphContext ctx) => {
|
||||
// Rendering commands
|
||||
});
|
||||
}
|
||||
|
||||
// ❌ Old (CommandBuffer - still works but deprecated)
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData data) { }
|
||||
```
|
||||
|
||||
### GPU Resident Drawer (Unity 6+)
|
||||
Automatic batching for massive draw call reduction:
|
||||
|
||||
```csharp
|
||||
// Enable in URP Asset settings:
|
||||
// Rendering > GPU Resident Drawer = Enabled
|
||||
// Automatically batches thousands of objects with minimal CPU overhead
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## URP Quick Reference
|
||||
|
||||
### Creating a URP Asset
|
||||
1. `Assets > Create > Rendering > URP Asset (with Universal Renderer)`
|
||||
2. Assign to `Project Settings > Graphics > Scriptable Render Pipeline Settings`
|
||||
|
||||
### URP Renderer Features
|
||||
Add custom render passes:
|
||||
|
||||
```csharp
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
public class OutlineRendererFeature : ScriptableRendererFeature {
|
||||
OutlineRenderPass pass;
|
||||
|
||||
public override void Create() {
|
||||
pass = new OutlineRenderPass();
|
||||
}
|
||||
|
||||
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData data) {
|
||||
renderer.EnqueuePass(pass);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Materials & Shaders
|
||||
|
||||
### Shader Graph (Visual Shader Editor)
|
||||
Unity 6 Shader Graph is production-ready for all shader types:
|
||||
|
||||
```csharp
|
||||
// Create: Assets > Create > Shader Graph > URP > Lit Shader Graph
|
||||
// No code needed, visual node-based editing
|
||||
```
|
||||
|
||||
### HLSL Custom Shaders (URP)
|
||||
|
||||
```hlsl
|
||||
// URP Lit shader template
|
||||
Shader "Custom/URPLit" {
|
||||
Properties {
|
||||
_BaseColor ("Base Color", Color) = (1,1,1,1)
|
||||
}
|
||||
SubShader {
|
||||
Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" }
|
||||
|
||||
Pass {
|
||||
Name "ForwardLit"
|
||||
Tags { "LightMode"="UniversalForward" }
|
||||
|
||||
HLSLPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
||||
|
||||
struct Attributes {
|
||||
float4 positionOS : POSITION;
|
||||
};
|
||||
|
||||
struct Varyings {
|
||||
float4 positionCS : SV_POSITION;
|
||||
};
|
||||
|
||||
Varyings vert(Attributes input) {
|
||||
Varyings output;
|
||||
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
|
||||
return output;
|
||||
}
|
||||
|
||||
half4 frag(Varyings input) : SV_Target {
|
||||
return half4(1, 0, 0, 1); // Red
|
||||
}
|
||||
ENDHLSL
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Lighting
|
||||
|
||||
### Baked Lighting (Unity 6 Progressive Lightmapper)
|
||||
|
||||
```csharp
|
||||
// Mark objects as static: Inspector > Static > Contribute GI
|
||||
// Bake: Window > Rendering > Lighting > Generate Lighting
|
||||
```
|
||||
|
||||
### Real-Time Lights (URP)
|
||||
|
||||
```csharp
|
||||
// Main Light (Directional): Auto-handled by URP
|
||||
// Additional Lights: Limited by "Additional Lights" setting in URP Asset
|
||||
|
||||
// Check light count in shader:
|
||||
int lightCount = GetAdditionalLightsCount();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Post-Processing
|
||||
|
||||
### Volume System (Unity 6+)
|
||||
|
||||
```csharp
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
// Add Volume component to GameObject
|
||||
// Add Volume Profile asset
|
||||
// Configure effects: Bloom, Color Grading, Depth of Field, etc.
|
||||
|
||||
// Script access:
|
||||
Volume volume = GetComponent<Volume>();
|
||||
if (volume.profile.TryGet<Bloom>(out var bloom)) {
|
||||
bloom.intensity.value = 2.5f;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance
|
||||
|
||||
### SRP Batcher (Auto-batching)
|
||||
|
||||
```csharp
|
||||
// Enable: URP Asset > Advanced > SRP Batcher = Enabled
|
||||
// Batches draws with same shader variant (minimal CPU overhead)
|
||||
```
|
||||
|
||||
### GPU Instancing
|
||||
|
||||
```csharp
|
||||
// Material: Enable "Enable GPU Instancing" checkbox
|
||||
// Batches identical meshes (same material + mesh)
|
||||
|
||||
Graphics.RenderMeshInstanced(
|
||||
new RenderParams(material),
|
||||
mesh,
|
||||
0,
|
||||
matrices // NativeArray<Matrix4x4>
|
||||
);
|
||||
```
|
||||
|
||||
### Occlusion Culling
|
||||
|
||||
```csharp
|
||||
// Window > Rendering > Occlusion Culling
|
||||
// Bake occlusion data for static geometry
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Custom Camera Rendering
|
||||
|
||||
```csharp
|
||||
// Get URP camera data
|
||||
var cameraData = frameData.Get<UniversalCameraData>();
|
||||
var camera = cameraData.camera;
|
||||
|
||||
// Access render targets
|
||||
var colorTarget = cameraData.renderer.cameraColorTargetHandle;
|
||||
```
|
||||
|
||||
### Screen-Space Effects
|
||||
|
||||
```csharp
|
||||
// Create ScriptableRendererFeature
|
||||
// Inject pass at specific point: AfterRenderingOpaques, AfterRenderingTransparents, etc.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Frame Debugger
|
||||
- `Window > Analysis > Frame Debugger`
|
||||
- Step through draw calls, inspect state
|
||||
|
||||
### Rendering Debugger (Unity 6+)
|
||||
- `Window > Analysis > Rendering Debugger`
|
||||
- Live view of URP settings, overdraw, lighting
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@17.0/manual/index.html
|
||||
- https://docs.unity3d.com/6000.0/Documentation/Manual/render-pipelines.html
|
||||
377
docs/engine-reference/unity/modules/ui.md
Normal file
377
docs/engine-reference/unity/modules/ui.md
Normal file
@@ -0,0 +1,377 @@
|
||||
# Unity 6.3 — UI Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** Unity 6 UI Toolkit is production-ready for runtime UI
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Unity 6 UI systems:
|
||||
- **UI Toolkit** (RECOMMENDED): Modern, performant, HTML/CSS-like (production-ready in Unity 6)
|
||||
- **UGUI (Canvas)**: Legacy system, still supported but not recommended for new projects
|
||||
- **IMGUI**: Editor-only, deprecated for runtime UI
|
||||
|
||||
---
|
||||
|
||||
## UI Toolkit (Modern UI)
|
||||
|
||||
### Setup UI Document
|
||||
|
||||
1. Create UXML (UI structure):
|
||||
- `Assets > Create > UI Toolkit > UI Document`
|
||||
2. Create USS (styling):
|
||||
- `Assets > Create > UI Toolkit > StyleSheet`
|
||||
3. Add to scene:
|
||||
- `GameObject > UI Toolkit > UI Document`
|
||||
- Assign UXML to `UIDocument > Source Asset`
|
||||
|
||||
---
|
||||
|
||||
### UXML (UI Structure)
|
||||
|
||||
```xml
|
||||
<!-- MainMenu.uxml -->
|
||||
<ui:UXML xmlns:ui="UnityEngine.UIElements">
|
||||
<ui:VisualElement class="container">
|
||||
<ui:Label text="Main Menu" class="title" />
|
||||
<ui:Button name="play-button" text="Play" />
|
||||
<ui:Button name="settings-button" text="Settings" />
|
||||
<ui:Button name="quit-button" text="Quit" />
|
||||
</ui:VisualElement>
|
||||
</ui:UXML>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### USS (Styling)
|
||||
|
||||
```css
|
||||
/* MainMenu.uss */
|
||||
.container {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgb(30, 30, 30);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 48px;
|
||||
color: white;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
Button {
|
||||
width: 200px;
|
||||
height: 50px;
|
||||
margin: 10px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
Button:hover {
|
||||
background-color: rgb(100, 150, 200);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### C# Scripting (UI Toolkit)
|
||||
|
||||
```csharp
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
public class MainMenu : MonoBehaviour {
|
||||
void OnEnable() {
|
||||
var root = GetComponent<UIDocument>().rootVisualElement;
|
||||
|
||||
// Query elements by name
|
||||
var playButton = root.Q<Button>("play-button");
|
||||
var settingsButton = root.Q<Button>("settings-button");
|
||||
var quitButton = root.Q<Button>("quit-button");
|
||||
|
||||
// Register callbacks
|
||||
playButton.clicked += OnPlayClicked;
|
||||
settingsButton.clicked += OnSettingsClicked;
|
||||
quitButton.clicked += Application.Quit;
|
||||
}
|
||||
|
||||
void OnPlayClicked() {
|
||||
Debug.Log("Play clicked");
|
||||
// Load game scene
|
||||
}
|
||||
|
||||
void OnSettingsClicked() {
|
||||
Debug.Log("Settings clicked");
|
||||
// Open settings menu
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Common UI Elements
|
||||
|
||||
```csharp
|
||||
// Label (text display)
|
||||
var label = root.Q<Label>("score-label");
|
||||
label.text = "Score: 100";
|
||||
|
||||
// Button
|
||||
var button = root.Q<Button>("submit-button");
|
||||
button.clicked += OnSubmit;
|
||||
|
||||
// TextField (text input)
|
||||
var textField = root.Q<TextField>("name-input");
|
||||
string playerName = textField.value;
|
||||
|
||||
// Toggle (checkbox)
|
||||
var toggle = root.Q<Toggle>("music-toggle");
|
||||
bool isMusicEnabled = toggle.value;
|
||||
|
||||
// Slider
|
||||
var slider = root.Q<Slider>("volume-slider");
|
||||
float volume = slider.value; // 0-1
|
||||
|
||||
// DropdownField (dropdown menu)
|
||||
var dropdown = root.Q<DropdownField>("difficulty-dropdown");
|
||||
dropdown.choices = new List<string> { "Easy", "Normal", "Hard" };
|
||||
dropdown.value = "Normal";
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Dynamic UI Creation (No UXML)
|
||||
|
||||
```csharp
|
||||
void CreateUI() {
|
||||
var root = GetComponent<UIDocument>().rootVisualElement;
|
||||
|
||||
// Create elements
|
||||
var container = new VisualElement();
|
||||
container.AddToClassList("container");
|
||||
|
||||
var label = new Label("Hello, UI Toolkit!");
|
||||
var button = new Button(() => Debug.Log("Clicked")) { text = "Click Me" };
|
||||
|
||||
container.Add(label);
|
||||
container.Add(button);
|
||||
root.Add(container);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### USS Flexbox Layout
|
||||
|
||||
```css
|
||||
/* Horizontal layout */
|
||||
.horizontal {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
/* Vertical layout (default) */
|
||||
.vertical {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Center children */
|
||||
.centered {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Spacing */
|
||||
.spaced {
|
||||
justify-content: space-between;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## UGUI (Legacy Canvas UI)
|
||||
|
||||
### Basic Setup (Still Works in Unity 6)
|
||||
|
||||
```csharp
|
||||
// GameObject > UI > Canvas (creates Canvas, EventSystem)
|
||||
|
||||
// UI Elements:
|
||||
// - Text (use TextMeshPro instead)
|
||||
// - Button
|
||||
// - Image
|
||||
// - Slider
|
||||
// - Toggle
|
||||
// - InputField
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### UGUI Scripting
|
||||
|
||||
```csharp
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro; // TextMeshPro
|
||||
|
||||
public class LegacyUI : MonoBehaviour {
|
||||
public TextMeshProUGUI scoreText;
|
||||
public Button playButton;
|
||||
public Slider volumeSlider;
|
||||
|
||||
void Start() {
|
||||
// Update text
|
||||
scoreText.text = "Score: 100";
|
||||
|
||||
// Button click
|
||||
playButton.onClick.AddListener(OnPlayClicked);
|
||||
|
||||
// Slider value changed
|
||||
volumeSlider.onValueChanged.AddListener(OnVolumeChanged);
|
||||
}
|
||||
|
||||
void OnPlayClicked() {
|
||||
Debug.Log("Play clicked");
|
||||
}
|
||||
|
||||
void OnVolumeChanged(float value) {
|
||||
AudioListener.volume = value;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### TextMeshPro (Better Text Rendering)
|
||||
|
||||
```csharp
|
||||
// Install: Window > TextMeshPro > Import TMP Essential Resources
|
||||
|
||||
// Use TMP_Text instead of Unity's Text component
|
||||
using TMPro;
|
||||
|
||||
public TextMeshProUGUI tmpText;
|
||||
tmpText.text = "High Quality Text";
|
||||
tmpText.fontSize = 24;
|
||||
tmpText.color = Color.white;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Canvas Settings (UGUI)
|
||||
|
||||
### Render Modes
|
||||
|
||||
```csharp
|
||||
// Screen Space - Overlay: UI rendered on top of everything (no camera needed)
|
||||
// Screen Space - Camera: UI rendered by specific camera (allows effects)
|
||||
// World Space: UI in 3D world (e.g., floating health bars)
|
||||
```
|
||||
|
||||
### Canvas Scaler (Responsive UI)
|
||||
|
||||
```csharp
|
||||
// UI Scale Mode:
|
||||
// - Constant Pixel Size: UI elements have fixed pixel size
|
||||
// - Scale With Screen Size: UI scales based on reference resolution (RECOMMENDED)
|
||||
// - Constant Physical Size: UI elements have fixed physical size (cm)
|
||||
|
||||
// Example: Scale With Screen Size
|
||||
// Reference Resolution: 1920x1080
|
||||
// Screen Match Mode: Match Width Or Height (0.5 = balanced)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Layout Groups (UGUI)
|
||||
|
||||
### Horizontal Layout Group
|
||||
|
||||
```csharp
|
||||
// Auto-arranges children horizontally
|
||||
// Add: GameObject > Add Component > Horizontal Layout Group
|
||||
```
|
||||
|
||||
### Vertical Layout Group
|
||||
|
||||
```csharp
|
||||
// Auto-arranges children vertically
|
||||
```
|
||||
|
||||
### Grid Layout Group
|
||||
|
||||
```csharp
|
||||
// Arranges children in a grid
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance (UI Toolkit vs UGUI)
|
||||
|
||||
### UI Toolkit Advantages
|
||||
- ✅ Faster rendering (retained mode)
|
||||
- ✅ Better for complex UIs with many elements
|
||||
- ✅ Easier styling (CSS-like)
|
||||
- ✅ Better for dynamic UIs
|
||||
|
||||
### UGUI Advantages
|
||||
- ✅ More mature, widely documented
|
||||
- ✅ Better integration with Unity Editor
|
||||
- ✅ Easier for beginners
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Health Bar (UI Toolkit)
|
||||
|
||||
```csharp
|
||||
var healthBar = root.Q<VisualElement>("health-bar");
|
||||
healthBar.style.width = new StyleLength(new Length(healthPercent, LengthUnit.Percent));
|
||||
```
|
||||
|
||||
### Health Bar (UGUI)
|
||||
|
||||
```csharp
|
||||
public Image healthBarImage;
|
||||
|
||||
void UpdateHealth(float percent) {
|
||||
healthBarImage.fillAmount = percent; // 0-1
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Fade In/Out (UI Toolkit)
|
||||
|
||||
```csharp
|
||||
IEnumerator FadeIn(VisualElement element, float duration) {
|
||||
float elapsed = 0f;
|
||||
while (elapsed < duration) {
|
||||
elapsed += Time.deltaTime;
|
||||
element.style.opacity = Mathf.Lerp(0f, 1f, elapsed / duration);
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### UI Toolkit Debugger
|
||||
- `Window > UI Toolkit > Debugger`
|
||||
- Inspect element hierarchy, styles, layout
|
||||
|
||||
### UGUI Event System Debugger
|
||||
- Select EventSystem in Hierarchy
|
||||
- Inspector shows active input module, raycast info
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unity3d.com/6000.0/Documentation/Manual/UIElements.html
|
||||
- https://docs.unity3d.com/Packages/com.unity.ui@2.0/manual/index.html
|
||||
- https://docs.unity3d.com/Packages/com.unity.ugui@2.0/manual/index.html
|
||||
Reference in New Issue
Block a user