# Projectiles with TimeMod

The [`TimeMod`](broken://pages/kizdCic7NM6KBRxN0dLq) class from [**Game Modules 4**](broken://pages/-MY8JopC2EjmWaXKwSh7) enables global and local time scales to be used, meaning individual objects can have time scales unique to themselves while also being affected by a global time scale.

Projectiles can also make use of this class.

In the example below, I will demonstrate how to add a custom script to the projectiles which will track their unique `TimeMod`, and also set the Particle System simulation speed.

I will also demonstrate a new [**Movement Behavior**](/magic-pig-games/projectile-factory/projectile-factory-documentation/behaviors.md) derived from an existing one which takes into account the `GlobalTimeScale` on the projecitles.

{% hint style="success" %}
In the example below, I have a "TimeScaleZone" layer, which is used for areas on the map which may change the `TimeScale` of any object within it.
{% endhint %}

## Projectile Setup

### Add a Collider

First, add a collider to your projectile, and set the "Include" and "Exclude" layers so that only the TimeScaleZone is affected.&#x20;

*Note: Depending on your setup, you may need to create this logic in a new child object, so that there is only one collider on each object in your Projectile. If you don't have a collder, you can set this on the parent object.*

<figure><img src="/files/bv17ewzcTret4WyqI0X6" alt=""><figcaption><p>Now this will only interact with the "TimeScaleZone" layer.</p></figcaption></figure>

### Add the Custom Script

You'll want to create your own script, but you can copy this to start. You may have to change some values to align with the way you're setting up your global `TimeMod` object.

```csharp
public class ProjectileTimeScale : MonoBehaviour
{
    public TimeMod timeMod = new TimeMod();
    public float LocalTimeScale => timeMod.TimeScale;
    public float GlobalTimeScale => timeMod.CombinedTimeScale(GameState.Instance.timeMod.TimeScale);
    public float DeltaTime => timeMod.UnscaledDeltaTime(GameState.Instance.timeMod.TimeScale);

    private float _lastTimeScale = 1f;
    
    public virtual void SetTimeScale(float timeScale)
    {
        _lastTimeScale = LocalTimeScale;
        timeMod.SetTimeScale(timeScale);
    }
    
    public virtual void ResetTimeScale() => SetTimeScale(_lastTimeScale);
    
    private void OnEnable()
    {
        _lastTimeScale = 1f;
        timeMod.SetTimeScale(1f);
    }
}
```

### Create a new Movement Behavior

In this example, I'm going to create a new behavior that derives from `MovementBehaviorForward`, which comes with **Projectile Factory**. The same logic will apply to any movement behavior that moves the object via a `deltaTime` value.

Again, you can copy this, but you may need to modify things to better fit your project.

```csharp
namespace MagicPigGames.ProjectileFactory
{
    [CreateAssetMenu(fileName = "New Basic Forward Movement Behavior Battle Ages",
        menuName = "Projectile Factory/Moving Behavior/Battle Ages - Basic Forward Movement Behavior")]
    [Serializable]
    public class MovementBehaviorForwardBattleAges : MovementBehaviorForward
    {
        private ProjectileTimeScale _projectileTimeScale;
        private ProjectileTimeScale ProjectileTimeScale
        {
            get
            {
                if (_isInitialized) return _projectileTimeScale; // Only check for this value once
                
                // Set the value with GetComponent
                if (_projectileTimeScale == null)
                    _projectileTimeScale = Projectile.GetComponent<ProjectileTimeScale>();
                
                // Set isInitialized to true and return the value
                _isInitialized = true;
                return _projectileTimeScale;
            }
        }
        
        private bool _isInitialized;
        
        protected override void Move()
        {
            var deltaTime = ProjectileTimeScale == null ? Time.deltaTime : ProjectileTimeScale.DeltaTime;
            Projectile.transform.Translate(LocalDirection * (ProjectileSpeed * deltaTime), Space.Self);
        }
    }
}
```

We compute the value of `deltaTime` in the `Move()` method, using the `ProjectileTimeScale` class if it's attached to the projectile, otherwise the standard `Time.deltaTime`.

### Optional: Set the Simulation Speed of Particle Systems

You can create a new class which derives from `ProjectileTimeScale` to add additional methods, or create other classes that reference `ProjectileTimeScale`, whichever you prefer. Here we have a new child class called `ProjectileParticleSystemTimeScale`.

```csharp
public class ProjectileParticleSystemTimeScale : MonoBehaviour
{
    public ProjectileTimeScale projectileTimeScale;
    public ParticleSystem[] particleSystems;

    private float _timeScale;

    public float TimeScale => projectileTimeScale.GlobalTimeScale;

    public void Update()
    {
        var timeScale = TimeScale;
        if (Mathf.Approximately(timeScale, _timeScale))
            return;
        
        _timeScale = timeScale;
        foreach (var ps in particleSystems)
            SetSimulationSpeed(ps, timeScale);
    }

    private void SetSimulationSpeed(ParticleSystem ps, float timeScale)
    {
        var main = ps.main;
        main.simulationSpeed = timeScale;
    }

    // Automatically populate particleSystems with all ParticleSystems in children
    public void OnValidate()
    {
        if (particleSystems == null || particleSystems.Length == 0)
            particleSystems = GetComponentsInChildren<ParticleSystem>();
        
        if (projectileTimeScale == null)
            projectileTimeScale = GetComponent<ProjectileTimeScale>();
    }
}
```

The `OnValidate()` method will automatically populate the list of Particle Systems. Set the list count to `0` to force the script to recompute the list if you add additional systems later.

{% hint style="success" %}
You can use these scripts (and the collider) on other particles that aren't "Projectiles" as well, to ensure that muzzle flashes and impacts also react to the Time Scale.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://infinitypbr.gitbook.io/magic-pig-games/projectile-factory/game-modules-4-integration/projectiles-with-timemod.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
