Setup

Add sVisual as a dependency by adding it to the requiredAddons inside your config.cpp


class CfgPatches {
	class NAME_OF_YOUR_MOD {
		requiredAddons[] = {
			"sVisual"
		};
	};
};
						
If you don't plan to use sVisual, you could just specify sFramework as requiredAddons.

Defining an effect

sVisual currently supports 3 types of effect:

Use a SPPEffect if you want to define a normal effect


class MyStaticEffect : SPPEffect {
	override void onInit() {
		setVignetteIntensity(0.69);
		setVignetteColor(SColor.rgb(RGBColors.BLACK)); // SColor: https://simonvic.github.io/sUDE/tutorials/9
		//...
	}  
}
						

Use a SPPEffectAnimated if you want to define an animated effect which will be animated until explicitly deactivated


class MyAnimatedEffect : SPPEffectAnimated {

	override void onInit() {
		// set vignette color to red
		setVignetteColor(SColor.rgb(RGBColors.RED));
	}

	override void onAnimate(float deltaTime) {
		// we set the vignette intensity to follow a Sine function as the time passes
		setVignetteIntensity(Math.Sin(getTime()));
	}

}
						
The onInit method gets called only once when the effect gets initialized, while the onAnimate method gets called every frame. Choose wisely what to put inside onAnimate

Use a SPPEffectTimed if you want to define an animated effect which will be animated but must terminate after a certain amount of time


class MyTimedEffect : SPPEffectTimed {
	
	override void onInit() {
		setDuration(5.0); // 5 seconds
		setOverlayColor(SColor.rgb(RGBColors.GREEN));
	}

	override void onAnimate(float deltaTime) {
		setOverlayFactor(Math.Sin(getTime()));
	}

}
						

Register the effect

You've defined your effect. Before being able to use and activate it, you need to register its classname into the PPERequesterBank, which holds the definitions of all PPEffects.


modded class PPERequesterRegistrations {
	void PPERequesterRegistrations() {
		PPERequesterBank.RegisterRequester(MyStaticEffect);
		PPERequesterBank.RegisterRequester(MyAnimatedEffect);
		PPERequesterBank.RegisterRequester(MyTimedEffect);		
	}
}
						

Using the effect

We can now retrieve the effect from the bank and use it.

Let's say we want to activate our effects when the player jumps.


modded class PlayerBase {

	MyStaticEffect myStaticEffect;
	MyAnimatedEffect myAnimatedEffect;
	MyTimedEffect myTimedEffect;

	void PlayerBase() {
		Class.CastTo(myStaticEffect,   PPERequesterBank.GetRequester(MyStaticEffect));
		Class.CastTo(myAnimatedEffect, PPERequesterBank.GetRequester(MyAnimatedEffect));
		Class.CastTo(myTimedEffect,    PPERequesterBank.GetRequester(MyTimedEffect));
	}
	
	override void OnJumpStart(){
		super.OnJumpStart();
		myStaticEffect.activate();
		myAnimatedEffect.activate();
		myTimedEffect.activate();
	}
}
						

You have access to some other methods, such as


myStaticEffect.isActive(); // returns true if active, false otherwise
myStaticEffect.deactivate(); // deactivates the effect
myStaticEffect.toggle(); // toggle between activated and deactivated
						

Animated (and timed) effects have some more methods:


myAnimatedEffect.start(); // start the animation from the beginning
myAnimatedEffect.pause(); // pause the animation
myAnimatedEffect.resume(); // resume the animation from the paused state
myAnimatedEffect.stop();
myAnimatedEffect.getTime(); // returns the time (seconds) that the animation has been playing for
myAnimatedEffect.isPlaying();
myAnimatedEffect.isPaused();
myAnimatedEffect.hasStopped();
						

Timed effects have some more methods related to timing:


myAnimatedEffect.getDuration(); // returns duration in seconds
myAnimatedEffect.getRemaining(); // returns how many seconds left to deactivation
						

Advanced definition

Normalization

Some effect could accept values in the -1,1 range, some other in the -2,2 range, while some other in the 0,1000 range.
To avoid having to deal with these ranges, you can specify to use a normalized value in the range of 0.00,1.00, where 0.00 is the minimum (0%) and 1.00 is the maximum (100%)


class SomeEffect : SPPEffect {
	override void onInit() {
		normalized(true);
		setOverlayFactor(0.50); // 50%
	}
}
							
By default, normalization is set to false for backward compatibility reasons.
I highly recommend using normalization.
If you're interested in the actual value ranges, you can find them in game scripts.

Priority and Operator

You may be wondering what would happen when two different PPEffects are active at the same time, and they both set the same effect parameter (e.g. vignette intensity, chromatic aberration, radial blur etc.) to different values.

Each parameter you set in your PPEffect can be seen as a layer (like on Photoshop).
These "layers" (parameters) are put on top of each other, and they will interact with the underlying (active) layer.
Which PPEffect takes precendence and how it interacts with other PPEffects is up to you to define via two parameters:

  • Priority: defines the order of the layer.

    Higher priority, means it will be on top of those with lower priority

  • Operator: defines the way it will interact with the underlying layer

The "first layer", the one at the bottom of the stack, is the default one defined by DayZ itself.

Priority Operator Value
default 0 SET 0

Let's see a practical example with code

For the tutorial sake, I chose low priorities; it's advised to choose higher priorities value (e.g. 100, 200, 500) so that it will be easier to interoperate with other mods.
  • Let's pretend the player got hurt; we activate a PainEffect
    The PainEffect will ADD 0.70 to the current vignette intensity.

    
    class PainEffect : SPPEffect {
    	override void onInit() {
    		normalized(true);
    		priority(1);
    		op(PPOperators.ADD);
    		setVignetteIntensity(0.70);
    	}
    }
    									
    Priority Operator Value
    final result 0.70
    PainEffect 1 ADD 0.70
    default 0 SET 0
  • Now the player assumed a pain killer and we activate a PainKillerEffect.
    The PainKillerEffect will SUBTRACT0.30 from the vignette intensity.

    
    class PainKillerEffect : SPPEffect {
    	override void onInit() {
    		normalized(true);
    		priority(2);
    		op(PPOperators.SUBSTRACT);
    		setVignetteIntensity(0.30);
    	}
    }
    									
    Priority Operator Value
    final result 0.40
    PainKillerEffect 2 SUBTRACT 0.30
    PainEffect 1 ADD 0.70
    default 0 SET 0
  • Now the player got inside a bunker and it's dark; we activate a DarknessEffect.
    The DarknessEffect will MULTIPLY by 2.0

    
    class DarknessEffect : SPPEffect {
    	override void onInit() {
    		normalized(true);
    		priority(3);
    		op(PPOperators.MULTIPLICATION);
    		setVignetteIntensity(2);
    	}
    }
    									
    Priority Operator Value
    final result 0.80
    DarknessEffect 3 MULTIPLICATIVE 2
    PainKillerEffect 2 SUBTRACT 0.30
    PainEffect 1 ADD 0.70
    default 0 SET 0
  • Now pain killer effectiveness has vanished (but the player is still in pain and in the bunker).
    So we deactivate the PainKillerEffect

    Priority Operator Value
    final result 1.40
    DarknessEffect 3 MULTIPLICATIVE 2
    PainEffect 1 ADD 0.70
    default 0 SET 0
  • The player is no longer in pain.
    We deactivate the PainEffect

    Priority Operator Value
    final result 0.00
    DarknessEffect 3 MULTIPLICATIVE 2
    default 0 SET 0

    Currently the DarknessEffect is not affecting the vignette intensity at all; that's because of its operator (2 * 0 = 0).
    As you can see, the priority and the operator play a major role in definining how the final resul will be.
    Be mindful when choosing them, as they could affect other mods too.

For backward compatibility reasons, the default priority is 1000 and the default operator is SET if they're not specified.
If you're upgrading your mod, it's highly recommended to choose the appropriate priority and operator for your PPEffects.

Operator types

Not every operator is applicable to all parameters type.
Some may work only for color parameters or for float parameters etc.
  • ADD

    Linear addition between the current value and the value you set.

  • ADD_RELATIVE

    Linear addition between the current value and the value you set, but it's relative to difference between current and the max, where applicable. Otherwise it will be a normal addition

  • SUBTRACT

    Linear subtraction. Subtract the value you set from the current value.

  • SUBTRACT_RELATIVE

    Linear subtraction. Subtract the value you set from the current value, but it's relative to difference between current and the min, where applicable. Otherwise it will be a normal subtraction

  • SUBTRACT_REVERSE

    Linear subtraction. Subtract the current value from the value you set

  • SUBTRACT_REVERSE_RELATIVE

    Linear subtraction. Subtract the current value from the value you set, but it's relative to difference between current and the min, where applicable. Otherwise it will be a normal subtraction

  • MULTIPLICATIVE

    Linear multiplication between the current value the the value you set

  • HIGHEST

    Only the highest betweem the current value and the value you set

  • LOWEST

    Only the lowest betweem the current value and the value you set

  • SET

    It ignores underlying layers and will set the value you specified

    Unless strictly required, other operators are recommended, since this could possibly "hide" other effects
  • OVERRIDE

    It ignores all other layers and set the value you specified

    Highly discouraged; use less destructive operators.

Multiple parameters in the same PPEffect

A PPEffect can "request" multiple parameters at the same time.

You're free to set a different priority and a different operator for each parameter, since each parameter will "affect its own stack of layers".
Altough most of the time it's recommended to use the same for the sake of simplicity.


class Effect_A : SPPEffect {
	override void onInit() {
		normalized(true);
		
		priority(1);
		op(PPOperators.ADD);
		setVignetteIntensity(0.50);
		
		priority(420);
		op(PPOperators.MULTIPLICATIVE);
		setOverlayFactor(2.0);

	}
}

class Effect_B : SPPEffect {
	override void onInit() {
		normalized(true);
		
		priority(69);
		op(PPOperators.SUBTRACT);
		setVignetteIntensity(0.20);
		
		priority(1);
		op(PPOperators.ADD);
		setOverlayFactor(0.40);

	}
}
							
(vignette) Priority Operator Value
final result 0.30
Effect_B 69 SUBTRACT 0.20
Effect_A 1 ADD 0.50
default 0 SET 0
(overlay) Priority Operator Value
final result 0.80
Effect_A 420 MULTIPLICATIVE 2.00
Effect_B 1 ADD 0.40
default 0 SET 0

Effects intensity option

Some players may enjoy more intense effects for immersion, while some other may find them too distracting.
With sVisual, the players can change an option to specify their preferred "effects intensity".

You can get the player preference by invoking the getEffectsIntensity() method, which will return a value between 0 and 2 (inclusive)

How this value will affect your effects it's up to you; you may want a specific effect to scale differently based on the player preference.

As a rule of thumb:

  • An effect intensity value of 0, means that the player want the least possible effects on the screen
  • Effect intensity of 1 is the default
  • An effect intensity of 2, means the player like very strong effects
This method is only available if you're using sVisual; so make sure to have specified sVisual in the requiredAddons

Let's see an example


class PainEffect : SPPEffect {
	override void onInit() {
		normalized(true);
		priority(1);
		op(PPOperators.ADD);
		setVignetteIntensity(0.20 * getEffectsIntensity());
	}
}