Skip to main content
tutorial

Unity Performance: 5 Update() Mistakes That Kill Mobile AR/VR FPS (2025 Guide)

đŸ‘€ Angry Shark Studio
📅
⏱ 4 min read
Unity Beginner Performance Tutorial Best Practices

Unity Beginner Mistake: Using Update() for Everything

Difficulty Level: 🌟 Beginner

What You’ll Learn

✅ Why overusing Update() kills mobile performance
✅ 4 proven alternatives to Update()-heavy code
✅ How to use Coroutines for efficient game logic
✅ Event-driven programming patterns for Unity
✅ Best practices for 60fps mobile AR/VR performance

Quick Answer: Replace Update() overuse with Events, Coroutines, FixedUpdate() for physics, and smart UI updates. Reserve Update() only for frame-critical operations like input and smooth animations.

Hey there, Unity developer!

Let me start by saying something important: if you’ve been putting everything in Update(), you’re not alone. I’ve seen many developers, including myself when I first started using Unity, make this exact mistake. It seems so logical at first, the Update() method runs every frame, so naturally, that’s where all the action should happen!

But here’s the thing: while this approach works initially, it’s like building a house on shaky foundations. Everything seems fine until your project grows, and suddenly you’re dealing with frame drops, battery drain, and code that’s harder to debug than a spaghetti dinner. These performance issues become especially critical for mobile AR/VR applications. They can compound with other common problems like Unity collider mistakes.

Don’t worry—this is entirely normal, and more importantly, it’s fixable! In this post, I’ll walk you through why this happens and, more importantly, how to write Unity code that performs beautifully even as your project scales.

💡 Quick Tip: Remember, every expert was once a beginner. The fact that you’re reading this means you’re already on the right path to becoming a better Unity developer!

The Unity Performance Problem with Update() Overuse

🎯 Real Talk: When I first started with Unity, I literally put EVERYTHING in Update(). Health checks, UI updates, distance calculations—you name it, it was in there. My first mobile game ran at about 15 FPS on a decent phone. Ouch! 😅

When you’re starting with Unity, it’s incredibly tempting to write code like this (and honestly, we’ve all been there):

public class PlayerController : MonoBehaviour {
    [SerializeField] private float health = 100f;
    [SerializeField] private Transform enemy;
    [SerializeField] private UI.Text healthText;
    
    private void Update() {
        // Check for input every frame
        if (Input.GetKey(KeyCode.W)) {
            transform.Translate(Vector3.forward * Time.deltaTime);
        }
            
        // Update UI every frame
        healthText.text = "Health: " + health.ToString();
        
        // Check distance to enemy every frame
        float distance = Vector3.Distance(transform.position, enemy.position);
        if (distance < 5f) {
            // Do something when close to the enemy
        }
        
        // Check for low health every frame
        if (health <= 20f) {
            // Flash screen red or something
        }
    }
}

Now, here’s the critical part: this code isn’t “wrong” in the sense that it crashes or doesn’t work. It will run, your character will move, and the UI will update. But it’s like running a marathon while carrying a heavy backpack—you’ll get there, but you’ll be exhausted and slow.

đŸ€— Encouraging Note: If you recognize your own code in the example above, don’t feel bad! This is actually a sign that you understand Unity’s basic concepts. Now we’re just going to make your code more efficient and professional.

Why This Becomes a Problem (And It’s Not Your Fault!)

đŸ“± Personal Experience: I once worked on a mobile AR project where we had 20+ objects all doing distance checks in Update(). The phone got so hot you could barely hold it! That was my wake-up call to learn better patterns.

1. The Performance Cost Sneaks Up on You

Update() runs every frame—that’s 60+ times per second! Imagine if someone asked you the same question 60 times every second. You’d get tired pretty quickly, right? That’s essentially what’s happening with your CPU.

2. The “Multiplication Effect”

Here’s where it gets tricky: one GameObject with a heavy Update() method might be acceptable. But when you have 10, 20, or 50 GameObjects all doing the same thing? Suddenly, you have hundreds or thousands of unnecessary calculations every second.

3. Mobile Devices Feel It Most

Mobile devices are incredible, but they’re not desktop computers. Excessive Update() calls can drain battery life faster than a leaky bucket, and players will notice their phone getting warm.

💭 Think About It: Would you check your email 60 times per second? Of course not! The same principle applies to your code—only check things when you actually need to.

Better Unity Performance Alternatives (The Good Stuff!)

🌟 Mindset Shift: Instead of asking “How can I make Update() do everything?”, start asking, “What actually needs to happen every frame?” Spoiler alert: it’s usually less than you think!

Solution 1: Use Events and Callbacks (My Personal Favorite!)

This was a game-changer for me when I first learned it. Instead of constantly asking “Did the health change? Did it change now? How about now?”, we set up a system that says “Hey, tell me WHEN the health changes.”

🎯 Quick Tip: Think of events like setting up a doorbell instead of constantly checking if someone’s at the door.

public class PlayerController : MonoBehaviour {
    [SerializeField] private float health = 100f;
    public UnityEvent OnHealthChanged;
    public UnityEvent OnLowHealth;
    
    public float Health {
        get { return health; }
        set {
            health = value;
            OnHealthChanged?.Invoke();
            
            if (health <= 20f) {
                OnLowHealth?.Invoke();
            }
        }
    }
}

Solution 2: Use Coroutines for Periodic Checks (The “Check Every So Often” Approach)

Coroutines are like setting a reminder on your phone. Instead of constantly thinking “Do I need to check the enemy distance?”, you set a reminder to check it every 100 milliseconds or so.

đŸ€” When to Use This: Perfect for things like enemy AI checks, resource gathering, or any logic that doesn’t need split-second precision.

private void Start() {
    StartCoroutine(CheckEnemyDistance());
}

private IEnumerator CheckEnemyDistance() {
    while (true) {
        float distance = Vector3.Distance(transform.position, enemy.position);
        if (distance < 5f) {
            // Do something when close to the enemy
        }
        yield return new WaitForSeconds(0.1f); // Check every 100ms instead of every frame
    }
}

Solution 3: Use FixedUpdate() for Physics (The Right Tool for the Job)

I used to put physics code in Update() too—until I learned that Unity has a special method just for physics! FixedUpdate() runs at consistent intervals, which is precisely what physics simulations need.

🧠 Memory Tip: Think “Fixed” = “Physics”. They go together like peanut butter and jelly.

private void FixedUpdate() {
    // Physics-based movement
    if (Input.GetKey(KeyCode.W)) {
        rb.AddForce(Vector3.forward * moveSpeed);
    }
}

Solution 4: Smart UI Updates (Only When Things Actually Change)

This one’s simple but powerful: only update the UI when the value actually changes. It’s like only repainting a wall when it’s actually dirty, not every day, “just in case.”

💡 Pro Tip: This pattern alone can dramatically improve performance in UI-heavy games or applications.

public class HealthUI : MonoBehaviour {
    [SerializeField] private Text healthText;
    private float lastHealthValue = -1f;
    
    public void UpdateHealthDisplay(float newHealth) {
        if (newHealth != lastHealthValue) {
            healthText.text = $"Health: {newHealth:F0}";
            lastHealthValue = newHealth;
        }
    }
}

The Right Way: Your Code Transformed! ✹

🎉 Celebration Time: Look how much cleaner and more professional this looks! This is the kind of code that makes other developers nod in approval.

Here’s how we can transform that original code into something efficient and maintainable:

public class PlayerController : MonoBehaviour {
    [SerializeField] private float moveSpeed = 5f;
    [SerializeField] private float health = 100f;
    [SerializeField] private Transform enemy;
    
    public UnityEvent<float> OnHealthChanged;
    public UnityEvent OnLowHealth;
    public UnityEvent OnNearEnemy;
    
    private Rigidbody rb;
    
    private void Start() {
        rb = GetComponent<Rigidbody>();
        StartCoroutine(CheckEnemyProximity());
    }
    
    private void Update() {
        // Only input handling in Update - this needs frame-perfect response
        HandleInput();
    }
    
    private void HandleInput() {
        Vector3 moveDirection = Vector3.zero;
        
        if (Input.GetKey(KeyCode.W)) {
            moveDirection += Vector3.forward;
        }
        if (Input.GetKey(KeyCode.S)) {
            moveDirection += Vector3.back;
        }
        if (Input.GetKey(KeyCode.A)) {
            moveDirection += Vector3.left;
        }
        if (Input.GetKey(KeyCode.D)) {
            moveDirection += Vector3.right;
        }
        
        if (moveDirection != Vector3.zero) {
            transform.Translate(moveDirection.normalized * moveSpeed * Time.deltaTime);
        }
    }
    
    private IEnumerator CheckEnemyProximity() {
        while (true) {
            if (enemy != null) {
                float distance = Vector3.Distance(transform.position, enemy.position);
                if (distance < 5f) {
                    OnNearEnemy?.Invoke();
                }
            }
            yield return new WaitForSeconds(0.2f);
        }
    }
    
    public void TakeDamage(float damage) {
        health -= damage;
        OnHealthChanged?.Invoke(health);
        
        if (health <= 20f) {
            OnLowHealth?.Invoke();
        }
    }
}

Frequently Asked Questions

Q: Is it ever okay to use Update() in Unity?

A: Yes! Update() is perfect for input handling, camera movement, and smooth animations that need frame-perfect timing. The problem is using it for everything else.

Q: What’s the performance difference between Update() and Coroutines?

A: Massive! One object with Update() = 60+ method calls per second. A coroutine checking every 0.1 seconds = 10 calls per second. That’s 6x fewer calls.

Q: Can I use both Update() and Coroutines in the same script?

A: Absolutely! Use Update() for frame-critical operations and Coroutines for periodic checks. This gives you the best of both worlds.

Q: How do I convert existing Update() code safely?

A: 1) Identify what actually needs frame-perfect timing, 2) Move periodic checks to Coroutines, 3) Convert state polling to events, 4) Test thoroughly on target devices.

Q: What about FixedUpdate() vs Update()?

A: Use FixedUpdate() for physics (Rigidbody movement, force application) and Update() for input and visual effects. They serve different purposes.

Key Takeaways (Your New Unity Superpowers!) 🚀

📚 Study Guide: These are the principles that separate beginner Unity developers from the pros. Bookmark this section!

  1. Reserve Update() for frame-critical operations like input handling and smooth animations

    • Think: “Does this need to happen 60+ times per second?”
  2. Use events for state changes instead of constant polling

    • Think: “Tell me WHEN something changes, don’t make me ask constantly”
  3. Use Coroutines for periodic checks that don’t need frame-perfect timing

    • Think: “Set a reminder instead of constant checking”
  4. Cache references and avoid unnecessary calculations

    • Think: “Work smarter, not harder”
  5. Profile your code regularly using Unity’s Profiler

    • Think: “Measure twice, optimize once”

🎯 Challenge: Try applying just ONE of these principles to your current project. You’ll be amazed at the difference!

From Our Studio to Yours: A Personal Note 💝

🎼 Real-World Story: In our VR project Pipe Craft, we initially had frame drops that were literally making people nauseous. After refactoring our Update() usage following these principles, we achieved a rock-solid 90 FPS. The difference was huge.

Here’s what I’ve learned after years of Unity development and helping other developers:

You’re not behind, you’re learning. Every optimization technique I’ve shared in this post, I realized it by making the same mistakes first. The difference between a beginner and an expert isn’t that the expert never made mistakes—it’s that they learned from them and kept improving. This applies to all aspects of development, from Unity performance to proper collider setup.

🌟 Remember: Good code isn’t just code that works. It’s code that works efficiently, is easy to understand, and doesn’t make the next developer (who might be future you!) want to cry. You’re building that skill right now by reading this post!

Keep experimenting, keep learning, and most importantly, be patient with yourself. Every Unity master was once exactly where you are now. As you progress, you might even explore transitioning to Kotlin development to expand your mobile development skills, or dive deeper into Unity AR/VR development trends for cutting-edge opportunities.


Need help optimizing your Unity project? Our Unity Certified Expert team can help you identify and fix performance bottlenecks. Contact us for a free performance consultation.

Angry Shark Studio Logo

About Angry Shark Studio

Angry Shark Studio is a professional Unity AR/VR development studio specializing in mobile multiplatform applications and AI solutions. Our team includes Unity Certified Expert Programmers with extensive experience in AR/VR development.

Related Articles

More Articles

Explore more insights on Unity AR/VR development, mobile apps, and emerging technologies.

View All Articles

Need Help?

Have questions about this article or need assistance with your project?

Get in Touch