Thank you! Your PDF is on the way! Check your Email for a link.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
Unity
Game Dev
How to CircleCast in Unity
CircleCast in Unity is a 2D physics method that detects objects within a circular area moving along a specified direction. Use Physics2D.CircleCast(origin, radius, direction, distance, layerMask) for area-based collision detection, enemy detection systems, and predictive movement. Essential for creating sophisticated gameplay mechanics like AoE abilities and AI behavior. Optimize performance using layer masks, appropriate update frequencies, and limited detection ranges for smooth gameplay.
CircleCasting in Unity is a really useful 2D physics feature that lets you check for objects inside a circle as it moves through the game world. You can think of it like casting an invisible bubble in a certain direction to see what it bumps into. While it is mainly designed for 2D projects, getting comfortable with CircleCast can open the door to more advanced collision detection and gameplay mechanics, from detecting nearby enemies to building responsive movement systems. In this guide, we will break down what CircleCast is, how it works, and different ways you can use it, from simple setups to more advanced techniques that can make your games feel smoother and more interactive.
What is CircleCast in Unity?
CircleCast is a physics query method in Unity's 2D physics system that casts a circle through the scene in a specified direction. Think of it as sweeping a circular area through space to detect what objects it encounters along its path. Unlike a simple point-based raycast, CircleCast provides area-based detection, making it ideal for detecting objects within a radius.
Key Characteristics of CircleCast:
Shape: Uses a circular detection area
Dimension: Primarily for 2D physics (Physics2D)
Direction: Can be cast in any direction
Distance: Controllable casting distance
Filtering: Supports layer masks and collision filtering
When to Use CircleCast
CircleCast is particularly useful for:
Character Detection: Finding nearby enemies or allies within a specific range
Area of Effect (AoE) Abilities: Detecting targets for spells or special attacks
Collision Prediction: Preventing objects from overlapping by checking future positions
Environmental Queries: Scanning areas for interactive objects or obstacles
AI Behavior: Helping NPCs detect players or objects within their awareness radius
origin (Vector2): The starting point of the circle
radius (float): The radius of the circle being cast
direction (Vector2): The direction to cast the circle
distance (float): How far to cast the circle
layerMask (int): Which layers to include in the detection (optional)
Implementing Basic CircleCast
Here's a simple example of how to implement CircleCast in a Unity script:
using UnityEngine;
publicclassBasicCircleCast : MonoBehaviour
{
[Header("CircleCast Settings")]
publicfloat castRadius = 1.0f;
publicfloat castDistance = 5.0f;
public LayerMask detectionLayers = -1;
voidUpdate(){
PerformCircleCast();
}
voidPerformCircleCast(){
Vector2 origin = transform.position;
Vector2 direction = transform.right; // Cast to the right RaycastHit2D hit = Physics2D.CircleCast(
origin,
castRadius,
direction,
castDistance,
detectionLayers
);
if (hit.collider != null)
{
Debug.Log($"CircleCast hit: {hit.collider.name}");
Debug.Log($"Hit point: {hit.point}");
Debug.Log($"Hit distance: {hit.distance}");
}
}
// Visualize the CircleCast in Scene viewvoidOnDrawGizmos(){
Vector2 origin = transform.position;
Vector2 direction = transform.right;
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(origin, castRadius);
Gizmos.color = Color.yellow;
Vector2 endPosition = origin + direction * castDistance;
Gizmos.DrawWireSphere(endPosition, castRadius);
Gizmos.color = Color.green;
Gizmos.DrawLine(origin, endPosition);
}
}
Here's an image of the script in action with some basic 2D objects.
We've added some code to change the SpriteRenderer's color on hit. Notice how the image shows multiple potential hits, but it only updates 1 object. This is the basic behavior of CircleCast. If we want to detect multiple collisions, then we'll need to use CircleCastAll.
Advanced CircleCast Techniques
Multiple Hit Detection with CircleCastAll
Sometimes you need to detect all objects within the CircleCast area, not just the first one. Below is a script that does precisely that.
using UnityEngine;
publicclassMultipleHitCircleCast : MonoBehaviour
{
[Header("Multi-Hit Settings")]
publicfloat castRadius = 2.0f;
publicfloat castDistance = 10.0f;
public LayerMask targetLayers = -1;
publicbool showDebug = true;
voidFixedUpdate(){
DetectAllTargets();
}
voidDetectAllTargets(){
Vector2 origin = transform.position;
Vector2 direction = transform.right;
RaycastHit2D[] hits = Physics2D.CircleCastAll(
origin,
castRadius,
direction,
castDistance,
targetLayers
);
foreach (RaycastHit2D hit in hits)
{
Debug.Log($"Detected: {hit.collider.name} at distance {hit.distance}");
// Process each detected objectProcessDetectedObject(hit.collider.gameObject);
}
}
voidProcessDetectedObject(GameObject detectedObject){
// YOUR DETECT CODE HERE// Our image shows this logic// detectedObject.GetComponent<SpriteRenderer>().color = Color.green; }
voidOnDrawGizmos(){
if (!showDebug) return;
Vector2 origin = transform.position;
Vector2 direction = transform.right;
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(origin, castRadius);
Gizmos.color = Color.yellow;
Vector2 endPosition = origin + direction * castDistance;
Gizmos.DrawWireSphere(endPosition, castRadius);
Gizmos.color = Color.green;
Gizmos.DrawLine(origin, endPosition);
}
}
In our image below, you can see that everything between the red and yellow circle now gets detected. Imagine that the circle started at the red position and quickly scanned all the way to the yellow position, marking anything along its path. But this is really all happening inside of a single frame.
Conditional CircleCast with Filtering
You can create more sophisticated detection systems by combining CircleCast with custom filtering:
using UnityEngine;
using System.Collections.Generic;
publicclassSmartCircleCast : MonoBehaviour
{
[System.Serializable]
publicclassDetectionSettings {public string targetTag = "Enemy";
publicfloat maxDistance = 5.0f;
publicbool requireLineOfSight = true;
public LayerMask obstacleLayers = 1;
}
[Header("Smart Detection")]
publicfloat detectionRadius = 3.0f;
public DetectionSettings settings;
publicbool showDebug = false;
voidFixedUpdate(){
GameObject[] validTargets = FindValidTargets();
foreach (GameObject target in validTargets)
{
// YOUR DETECTION CODE HERE// tint the object green//target.GetComponent<SpriteRenderer>().color = Color.green; }
}
public GameObject[] FindValidTargets()
{
Vector2 origin = transform.position;
Vector2 direction = Vector2.zero; // 360-degree detection RaycastHit2D[] hits = Physics2D.CircleCastAll(
origin,
detectionRadius,
direction,
0f// No movement, just area detection );
List<GameObject> validTargets = new List<GameObject>();
foreach (RaycastHit2D hit in hits)
{
if (IsValidTarget(hit.collider.gameObject, origin))
{
validTargets.Add(hit.collider.gameObject);
}
}
return validTargets.ToArray();
}
boolIsValidTarget(GameObject target, Vector2 origin){
// Check tagif (!target.CompareTag(settings.targetTag))
returnfalse;
// Check distancefloat distance = Vector2.Distance(origin, target.transform.position);
if (distance > settings.maxDistance)
returnfalse;
// Check line of sight if requiredif (settings.requireLineOfSight)
{
Vector2 directionToTarget = (target.transform.position - transform.position).normalized;
RaycastHit2D losCheck = Physics2D.Raycast(
origin,
directionToTarget,
distance,
settings.obstacleLayers
);
if (losCheck.collider != null)
returnfalse; // Obstacle blocking line of sight }
returntrue;
}
voidOnDrawGizmos(){
if (!showDebug) return;
Vector2 origin = transform.position;
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(origin, detectionRadius);
}
}
Below is an example of this script. You can see the 2 red circles tagged as "Enemy" were inside the CircleCast, but only the one in line of sight was detected as hit and turned green. It's important to note that the obstacleLayers should be different then Default, or the layer of the enemy. Otherwise, this script's RayCast would have thought either the white circle, or even the red "enemy" circles themselves were obstacles. In this example, our blue boxes are on the Obstacles layer.
Performance Optimization Tips
1. Use Appropriate Update Frequency
Don't perform CircleCast every frame unless necessary, it's good to have a sample rate, which limits the amount of times you cast to a set interval. This will make your games more performant:
Always use reasonable detection ranges to avoid unnecessary calculations:
[Range(0.1f, 10.0f)]
public float maxDetectionRange = 5.0f;
3. Use Layer Masks Effectively
Properly configure layer masks to only detect relevant objects:
[Header("Layer Configuration")]
public LayerMask enemyLayers = (1 << 8); // Only layer 8public LayerMask environmentLayers = (1 << 0) | (1 << 9); // Layers 0 and 9
Common CircleCast Use Cases and Examples
Enemy Detection System
using UnityEngine;
public classEnemyDetector : MonoBehaviour{
[Header("Detection Settings")]
public float detectionRadius = 4.0f;
public LayerMask enemyLayer = 1 << 8;
private bool enemyDetected = false;
voidUpdate() {
CheckForEnemies();
}
voidCheckForEnemies() {
RaycastHit2D hit = Physics2D.CircleCast(
transform.position,
detectionRadius,
Vector2.zero, // No direction needed for area detection 0f,
enemyLayer
);
if (hit.collider != null && !enemyDetected)
{
OnEnemyDetected(hit.collider.gameObject);
enemyDetected = true;
}
elseif (hit.collider == null && enemyDetected)
{
OnEnemyLost();
enemyDetected = false;
}
}
voidOnEnemyDetected(GameObject enemy) {
Debug.Log($"Enemy detected: {enemy.name}");
// Trigger alert state, change music, etc. }
voidOnEnemyLost() {
Debug.Log("Enemy lost from detection range");
// Return to normal state }
}
Predictive Collision Detection
using UnityEngine;
publicclassPredictiveMovement : MonoBehaviour
{
[Header("Movement Prediction")]
publicfloat moveSpeed = 5.0f;
publicfloat collisionCheckRadius = 0.6f;
publicfloat predictiveDistance = 0.1f;
// NOTE: Make sure this layer isn't shared with your gameobject, or it won't ever move!public LayerMask obstacleLayer;
voidFixedUpdate(){
Vector2 inputDirection = GetInputDirection();
if (inputDirection != Vector2.zero && CanMoveInDirection(inputDirection))
{
transform.Translate(inputDirection * moveSpeed * Time.deltaTime);
}
}
boolCanMoveInDirection(Vector2 direction){
RaycastHit2D hit = Physics2D.CircleCast(
transform.position,
collisionCheckRadius,
direction,
predictiveDistance,
obstacleLayer
);
Debug.Log(hit.collider);
// Can move if no collision detectedreturn hit.collider == null;
}
Vector2 GetInputDirection(){
float horizontal = Input.GetAxisRaw("Horizontal");
float vertical = Input.GetAxisRaw("Vertical");
returnnewVector2(horizontal, vertical).normalized;
}
}
Troubleshooting Common Issues
Issue 1: CircleCast Not Detecting Objects
Possible Solutions:
Check if objects have 2D colliders attached
Verify layer mask settings
Ensure objects are on the correct layers
Check if colliders are set as triggers when they shouldn't be
Issue 2: Performance Problems
Possible Solutions:
Reduce casting frequency
Limit detection range
Use more specific layer masks
Consider using Coroutines for expensive operations
Issue 3: Inconsistent Detection
Possible Solutions:
Verify collider sizes and positions
Check for floating-point precision issues
Ensure consistent FixedUpdate timing for physics-related code
Add Debug Drawing to help see what's happening.
Best Practices for CircleCast Implementation
Always visualize your casts using Gizmos during development
Use layer masks to improve performance and accuracy
Cache results when possible to avoid redundant calculations
Consider the physics timestep when using CircleCast in FixedUpdate
Test edge cases like objects entering/exiting detection range
Document your layer configurations for team collaboration
Conclusion
CircleCast is a versatile tool in Unity's physics arsenal that enables sophisticated area-based detection systems. By understanding its parameters, implementing proper optimization techniques, and following best practices, you can create responsive and efficient gameplay mechanics.
Whether you're developing enemy AI, collision prediction systems, or interactive environmental elements, CircleCast provides the foundation for robust spatial queries in your 2D Unity projects. Remember to always profile your code and optimize for your specific use case to ensure smooth gameplay performance.
Start with simple implementations and gradually add complexity as your understanding grows. With practice, CircleCast will become an invaluable tool in your Unity development toolkit.
Looking for a reliable partner for your next project?
At SLIDEFACTORY, we’re dedicated to turning ideas into impactful realities. With our team’s expertise, we can guide you through every step of the process, ensuring your project exceeds expectations. Reach out to us today and let’s explore how we can bring your vision to life!