Krotox Engine › Docs › Scripting API › Graphics, UI & Audio

Sprites, animation, cameras & effects API

Scripting 2D visuals: sprite colour, textures and flipping, animated sprite states and the Animator, particles, lines and trails, shapes, sprite effects (hit flash, outline, dissolve), 2D lights, the camera and screen-to-world conversions.

What each component *is* is covered in the 2D component reference. This page is about changing them from C#.

Sprite Renderer

var sprite = GetComponent<SpriteRendererComponent>();

sprite.Color = new Vector4(1, 0.5f, 0.5f, 1);   // RGBA tint; alpha < 1 for transparency
sprite.TilingFactor = new Vector2(4, 1);         // repeat the texture
sprite.FlipX = m_FacingLeft;                     // face the way you move
sprite.MaskInteraction = SpriteMaskInteraction.VisibleInsideMask;

// Change the texture: a path, a handle, or a Texture2D field
sprite.Texture = "Textures/Skins/Gold.png";
public Texture2D AltSkin;          // inspector field
sprite.SetTexture(AltSkin);

There is no Color type: colours are Vector4 (red, green, blue, alpha from 0 to 1).

Fading a sprite out

IEnumerator FadeOut(SpriteRendererComponent sprite, float seconds)
{
    yield return Tween.To(1.0f, 0.0f, seconds, Ease.QuadOut,
        a => sprite.Color = new Vector4(1, 1, 1, a));
    Destroy();
}

Animated sprites

With Use State Machine on, switch between named states:

void OnUpdate(float ts)
{
    var rb = GetComponent<Rigidbody2DComponent>();
    float speed = Mathf.Abs(rb.LinearVelocity.X);

    if (!m_Grounded)          SetAnimationState("Jump");
    else if (speed > 0.1f)    SetAnimationState("Run");
    else                      SetAnimationState("Idle");

    GetComponent<AnimatedSpriteComponent>().FlipX = rb.LinearVelocity.X < 0;
}

string current = GetAnimationState();

Calling SetAnimationState every frame is safe: it ignores the state that is already playing.

Animator (state machines with parameters)

For animation authored in the State Machine panel, drive the controller's parameters and let its transitions pick the state:

var animator = GetComponent<AnimatorComponent>();
animator.SetFloat("Speed", Mathf.Abs(velocity.X));
animator.SetBool("Grounded", m_Grounded);
animator.SetTrigger("Attack");
animator.SetInt("Combo", 2);

float speed  = animator.GetFloat("Speed");
bool  exists = animator.HasParameter("Grounded");
string state = animator.CurrentState;
float t      = animator.NormalizedTime;    // 0 to 1 through the current state
animator.Speed = 1.5f;
animator.Play("Hurt");                     // jump straight to a state

Animation timelines (AnimationComponent): Play(), Pause(), Stop(), Seek(time), SetSpeed(speed), IsPlaying, CurrentTime.

Particles

var particles = GetComponent<ParticleSystemComponent>();
particles.Play();
particles.Emit(30);               // or Burst(30): an instant burst
particles.Stop();
particles.Clear();                // remove live particles

particles.EmissionRate = 50.0f;
particles.IsEmitting = false;
particles.StartSpeed = 8.0f;
particles.GravityModifier = 1.0f;
particles.LifeTime = 1.2f;
bool busy = particles.IsPlaying;
int alive = particles.AliveCount;

Other members: IsPaused, Looping, Duration, LifeTimeVariation, SimulationSpeed, Velocity, VelocityVariation. Colour and size curves are edited in the inspector only.

A one-shot effect

public Prefab ExplosionPrefab;    // a prefab with a Particle System

void Explode()
{
    Entity fx = Instantiate(ExplosionPrefab, WorldPosition);
    fx.GetComponent<ParticleSystemComponent>().Emit(40);
    Invoke(() => fx.Destroy(), 2.0f);
}

Lines and trails

var line = GetComponent<LineRenderer2DComponent>();
line.Points = new[] { Vector2.Zero, new Vector2(1, 0.5f), new Vector2(2, 0) };   // assign a new array
line.Width = 0.08f;
line.Color = new Vector4(1, 1, 0, 1);
line.UseWorldSpace = true;

// Lines that change every frame: per-point access allocates nothing
for (int i = 0; i < line.PointCount; i++)
    line.SetPoint(i, PredictPosition(i * 0.1f));

var trail = GetComponent<TrailRenderer2DComponent>();
trail.Emitting = m_Dashing;
trail.StartWidth = 0.3f; trail.EndWidth = 0.0f;
trail.Time = 0.25f;
trail.Clear();                     // drop the trail (for example after teleporting)

Trajectory preview

void DrawTrajectory(Vector2 start, Vector2 velocity)
{
    var line = GetComponent<LineRenderer2DComponent>();
    Vector2 gravity = Physics2D.Gravity;
    var points = new Vector2[20];
    for (int i = 0; i < points.Length; i++)
    {
        float t = i * 0.05f;
        points[i] = start + velocity * t + gravity * (0.5f * t * t);
    }
    line.UseWorldSpace = true;
    line.Points = points;
}

Shapes and circles

var shape = GetComponent<ShapeRendererComponent>();
shape.Type = Shape2DType.Star;
shape.Sides = 5;
shape.InnerRadius = 0.4f;
shape.Thickness = 1.0f;          // 1 filled, less for an outline
shape.Color = new Vector4(1, 0.8f, 0.2f, 1);

var circle = GetComponent<CircleRendererComponent>();
circle.Thickness = 0.1f;         // a ring
circle.Fade = 0.05f;             // soft edge

Sprite effects: hit flash, outline, dissolve

var fx = GetComponent<SpriteEffects2DComponent>();

fx.HitFlash(0.1f);                                  // flash white for 0.1 s when hit
fx.FlashColor = new Vector3(1, 0, 0);               // ...or red

fx.Outline = m_Selected;                            // highlight a selected unit
fx.OutlineColor = new Vector4(0.6f, 1, 0.2f, 1);
fx.OutlineWidth = 2.0f;                             // screen pixels

// Burn away over one second
StartCoroutine(Tween.To(0.0f, 1.0f, 1.0f, Ease.Linear, v => fx.DissolveAmount = v));

2D lights

var light = GetComponent<Light2DComponent>();
light.Type = Light2DType.Point;          // Point, Spot, Global
light.Color = new Vector3(1.0f, 0.8f, 0.5f);
light.Intensity = 1.5f;
light.OuterRadius = 6.0f;
light.Enabled = !m_PowerOut;

// A flickering torch
light.Intensity = 1.2f + 0.2f * Mathf.Sin(Time.ElapsedTime * 15.0f);

SceneManager.AmbientLight2D = new Vector3(0.15f, 0.15f, 0.25f);   // darken the scene

Spot lights use InnerAngle and OuterAngle (radians in scripts). ShadowSoftness is the light's size in world units (0 = hard shadows).

Parallax and camera zones

GetComponent<ParallaxLayer2DComponent>().Factor = new Vector2(0.2f, 0.0f);   // far background
GetComponent<CameraZone2DComponent>().Enabled = m_BossActive;                // lock the arena camera

Cameras

var cam = GetComponent<CameraComponent>();
cam.Projection = CameraComponent.ProjectionType.Orthographic;
cam.OrthographicSize = 8.0f;          // smaller = zoomed in
cam.MakeMain();                       // make this the primary camera
Entity main = CameraComponent.Main;   // static get and set

// The follow camera's target
GetComponent<CameraFollow2DComponent>().Target = m_Player;

The static Camera class works through the primary camera, so gameplay code does not need to find it:

Vector2 world  = Camera.ScreenToWorldPoint(Input.MousePosition);   // screen pixels to world
Vector2 screen = Camera.WorldToScreenPoint(new Vector2(3, 4));     // world to screen pixels
Vector2 vp     = Camera.ScreenToViewportPoint(Input.MousePosition); // 0 to 1
Vector3 center = Camera.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 0));
Camera.BackgroundColor = new Vector4(0.1f, 0.1f, 0.2f, 1);          // the clear colour

The same conversion methods exist on CameraComponent and on any Entity that has a camera.

Screen positions are in the game's own pixels, the same space as Input.MousePosition and touches, so no correction is needed for letterboxing or a zoomed Game view.

Keeping the player on screen

void OnLateUpdate(float ts)
{
    Vector3 min = Camera.ViewportToWorldPoint(new Vector3(0, 0, 0));   // bottom-left of the view
    Vector3 max = Camera.ViewportToWorldPoint(new Vector3(1, 1, 0));   // top-right
    Vector3 p = Translation;
    p.X = Mathf.Clamp(p.X, min.X + 0.5f, max.X - 0.5f);
    Translation = p;
}

World text

var text = GetComponent<TextComponent>();
text.Text = quot;x{m_Combo}";
text.Color = new Vector4(1, 1, 1, 1);
text.Bold = true;

For HUD text, use UITextComponent (see UI scripting API).