Struggling to make a very simple 2D camera shake with the impulse listener

After days of trying, I’m really struggling to make a very simple oldschool 2D camera shake (sort of Mega Man like) with the impulse listener.
The result always looks more like a “shockwave” sort of movement, emanating from a source and spreading outwards, not evenly shaking the whole “output image” around on 2 axes.

The signal shape has no Z values. Only X and Y, so that’s not the issue.

Perhaps several extensions are working against each other, since I’m using the pixel perfect camera extension on the main cam and pixel perfect Vcam extensions. Also, even though there is no camera moving or following the player, I had to confine the Vcams to colliders that match their exact viewing size, since otherwise after the camshake the camera never returns 100% to the initial starting point position.

So perhaps things are conflicting here.

Some horizontal lines seem to be doubled while others a cut off entirely, which could also be a pixel perfect timing/rendering issue, but it does give it that weird “shockwave” look and feel.

Maybe you could post the your code that did the shake?

public class CameraShake : MonoBehaviour
{
    [SerializeField] private GameSettings gameSettings;

    private CinemachineImpulseSource cinemachineImpulseSource;

    public static event Action<float> VibrateController = delegate { };

    private void Awake()
    {
        cinemachineImpulseSource = GetComponent<CinemachineImpulseSource>();
        CinemachineImpulseManager.Instance.IgnoreTimeScale = true; //activate in latest CM version
    }

    public void CamShake(float shakeAmplitude) // between 0 and 1
    {
        VibrateController(shakeAmplitude);

        if (gameSettings.Screenshake)
        {
            cinemachineImpulseSource.m_ImpulseDefinition.m_AmplitudeGain = shakeAmplitude * 20f;
            cinemachineImpulseSource.m_ImpulseDefinition.m_TimeEnvelope.m_SustainTime = shakeAmplitude / 2f;
            cinemachineImpulseSource.GenerateImpulse();
        }
    }
}

ok thanks.

That code generates a signal with its origin at the location of whatever gameobject this is attached to. What object is that?

I don’t see any code that listens for the impulse, so the signal is going out into the void.

I presume you had some kind of listener, because you were getting movement you didn’t like. What object was that listener attached to, and what did the code look like?

1 Like

Assuming that your CameraShake script was added to the main camera, I was able to get good results with this modified version (with listening code added)

using UnityEngine;
using Cinemachine;
using System;

public class CameraShake : MonoBehaviour
{
    public bool ShakeNow;
    //[SerializeField] private GameSettings gameSettings;

    [CinemachineImpulseDefinitionProperty]
    public CinemachineImpulseDefinition ImpulseDefinition = new CinemachineImpulseDefinition();

    public static event Action<float> VibrateController = delegate { };
    private void Awake()
    {
        CinemachineImpulseManager.Instance.IgnoreTimeScale = true; //activate in latest CM version
        impulsePosLastFrame = Vector3.zero;
        impulseRotLastFrame = Quaternion.identity;
    }
    public void CamShake(float shakeAmplitude) // between 0 and 1
    {
        VibrateController(shakeAmplitude);
        //if (gameSettings.Screenshake)
        {
            ImpulseDefinition.CreateEvent(transform.position, Vector3.down * shakeAmplitude);
        }
    }

    private Vector3 impulsePosLastFrame;
    private Quaternion impulseRotLastFrame;

    private void Update()
    {
        // Unapply previous cam shake
        transform.position -= impulsePosLastFrame;
        transform.rotation = transform.rotation * Quaternion.Inverse(impulseRotLastFrame);
    }

    private void LateUpdate()
    {
        if (ShakeNow)
            CamShake(1);
        ShakeNow = false;

        // Apply the cam shake
        if (CinemachineImpulseManager.Instance.GetImpulseAt(
            transform.position, false, ImpulseDefinition.m_ImpulseChannel,
            out impulsePosLastFrame, out impulseRotLastFrame))
        {
            transform.position += impulsePosLastFrame;
            transform.rotation = transform.rotation * impulseRotLastFrame;
        }
    }
}

And with these values in the inspector:

5517718--566557--upload_2020-2-24_17-32-25.png

No impulse listeners were used on the vcams, since there is a single global listener attached to the main camera.

One important note: for this usage, the script execution order must be set to be after CinemachineBrain, or no impulses will be visible.

5517718--566566--upload_2020-2-24_17-34-36.png

2 Likes

A GameEvent (for ex. killing an enemy) invokes “public void CamShake(float shakeAmplitude)” in the CamerShake script, which is on the MainCamera, which also contains the CinemachineImpulseListener script.
It works, but with weird results.

Will try out your new solution right away, thanks.

Your solution is perfect, exactly the result I was looking for!
Many thanks for your valuable input.

1 Like