A complete faceoff and puck possession system for your hockey game with realistic physics, visual indicators, and AI-ready mechanics.
Controls the puck physics and possession mechanics.
Features:
Key Properties:
possessionRadius: Distance within which players can pick up the puck (default: 1.5m)carrierOffset: How far in front of player the puck stays when carried (default: 1.0m)Manages the entire faceoff sequence and outcome determination.
Faceoff Outcomes:
How Outcomes Are Determined:
Faceoff Strength = (Strength × 0.3) + (Stick Handling × 0.3) +
(Awareness × 0.2) + (Agility × 0.2)
Plus random variation (±10 points) for unpredictability.
Testing: Press F key during play mode to trigger a faceoff.
Enhanced with puck interaction methods.
New Methods:
GainPuck(PuckController): Called when player gains possessionReleasePuck(): Called when player loses possessionCanPickupPuck(): Checks if player can currently pick up puckSetFrozen(bool): Freeze/unfreeze player during faceoffShoot(direction, power): Fire the puck with physicsPass(targetPlayer): Pass to specific teammateCheck(targetPlayer): Body check opponent to dislodge puckVisual Feedback:
Create the Puck:
- Create GameObject → name it "Puck"
- Add PuckController component
- Position at center ice (y ≈ 0.5 for ice level)
Tag Your Players:
- Edit → Project Settings → Tags & Layers
- Add tags: "HomeTeam" and "AwayTeam"
- Select home team players → Inspector → Tag: "HomeTeam"
- Select away team players → Inspector → Tag: "AwayTeam"
Create Faceoff Manager:
- Create Empty GameObject → name it "FaceoffManager"
- Add FaceoffManager component
- Drag Puck into the "Puck" field in Inspector
Verify Centers:
FaceoffSetupGuide componentPuck States:
Gaining Possession: Player automatically picks up puck when:
possessionRadius (1.5m)Visual Indicator:
1. FaceoffManager.StartFaceoff() called
↓
2. Puck positioned at faceoff spot
↓
3. All players frozen (prevent movement)
↓
4. Calculate outcome based on center stats
↓
5. Execute outcome:
- Clean Win: Puck passed to defender
- Struggle: Puck bounces loose
↓
6. Unfreeze all players
↓
7. Game continues
Timing:
Puck Properties:
Force Ranges:
public class GameManager : MonoBehaviour
{
private FaceoffManager faceoffManager;
void Start()
{
faceoffManager = FindObjectOfType<FaceoffManager>();
// Start game with center ice faceoff
Vector3 centerIce = new Vector3(0, 0.5f, 0);
faceoffManager.InitiateFaceoff(centerIce);
}
}
public class PlayerAI : MonoBehaviour
{
private PlayerController playerController;
private Transform goal;
void ShootAtGoal()
{
if (playerController.HasPuck())
{
Vector3 shootDirection = (goal.position - transform.position).normalized;
playerController.Shoot(shootDirection, powerMultiplier: 1.0f);
}
}
}
public void PassToOpenPlayer()
{
if (!playerController.HasPuck()) return;
PlayerController[] teammates = FindTeammates();
PlayerController bestTarget = FindMostOpen(teammates);
if (bestTarget != null)
{
playerController.Pass(bestTarget);
}
}
void OnCollisionEnter(Collision collision)
{
PlayerController opponent = collision.gameObject.GetComponent<PlayerController>();
if (opponent != null && opponent.HasPuck() && IsOpponent(opponent))
{
playerController.Check(opponent);
}
}
In FaceoffManager.cs:
[SerializeField] private float struggleChance = 0.3f; // 30% base struggle chance
[SerializeField] private float winThreshold = 0.7f; // Higher = more clear wins needed
In PuckController.cs:
[SerializeField] private float friction = 0.3f; // Lower = slides further
[SerializeField] private float drag = 0.5f; // Higher = stops faster
In PuckController.cs:
[SerializeField] private float possessionRadius = 1.5f; // Detection range
CanPickupPuck() returns trueSetFrozen() methodTrailRenderer trail = puck.gameObject.AddComponent<TrailRenderer>();
trail.time = 0.5f;
trail.startWidth = 0.1f;
trail.endWidth = 0.02f;
// In PuckController.OnCollisionEnter()
ParticleSystem.Emit(5); // Ice spray effect
if (CompareTag("HomeTeam"))
possessionIndicator.GetComponent<Renderer>().material.color = Color.blue;
else
possessionIndicator.GetComponent<Renderer>().material.color = Color.red;
You now have:
✅ Physics-based puck with realistic ice behavior
✅ Clear visual indication of possession (yellow ring)
✅ Faceoff system with 3 outcomes based on player stats
✅ Ready-to-use shooting, passing, and checking mechanics
✅ Easy integration with your existing player system
Test it: Press F in play mode to see a faceoff in action!