How to make card movement behave like those in Hearthstone and Eternal CCG?

How to make card movement behave like those in Hearthstone and Eternal CCG?

I'm trying to achieve the same effect that cards in Hearthstone and Eternal CCG does, for example, cards behave as they were a type of "crane", when the cards move, their body is tilted towards the direction they are moving, making it behave like a "crane".
Does anyone have any idea whatsoever on how I could achieve this? I'm trying to do everything manually, e.g., get the card position, detect if the card is moving left or right, change the card Y axis to achieve the tilt effect relative to the direction the card is moving, clamping the value so it doesn't over-rotate, etc., etc.
I would really appreciate and would LOVE to know how I could be doing this. Do I need to create an animation and play after giving it the direction? I don't know you help me out please!

Solutions/Answers:

Answer 1:

I’ve got the perfect solution, see the scripts at the end of the post.

These scripts are those actually used in Hearthstone client. You need to attach a DragRotator script to the object you want to rotate. In the Awake() function of DragRotator.cd I just added a Reset() that wasn’t here, but you need to to a Reset() before using the script anyway. You need to adapt it to your game, in my case when the object move one the y axis, there is a rotation following the x axis, and when it move on the x axis, the rotation follows the y axis (it wasn’t the same on the hearthstone script).

You also need a reference to a DragRotatorInfo script, made by the function SetInfo(DragRotatorInfo) info using the script. Personaly, I used a singleton to have one DragRotatorInfo script for all my DragRotator scripts.
Thanks to that you can modify the values of the two DragRotatorAxisInfo of the DragRotatorInfos.

These infos are very simple and they control the two rotations (when you move up/down and left/right).

Force multiplier : the more it is, the less you have to move the object quickly to make it rotate. I use values of 10-15.

Min Degrees/Max Degrees : obviously the min & max values of the rotation in degress. I use values of 40-50.

Rest seconds : the time the object take to go back to its original position. I use values of 2-3.

Hope it helps someone!

DragRotator.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DragRotator : MonoBehaviour
{
    private const float EPSILON = 0.0001f;
    private const float SMOOTH_DAMP_SEC_FUDGE = 0.1f;
    private DragRotatorInfo m_info;
    private float m_pitchDeg;
    private float m_rollDeg;
    private float m_pitchVel;
    private float m_rollVel;
    private Vector3 m_prevPos;
    private Vector3 m_originalAngles;

    private void Awake()
    {
        Reset();
        this.m_prevPos = this.transform.position;
        this.m_originalAngles = this.transform.localRotation.eulerAngles;

    }

    private void Update()
    {
        Vector3 position = this.transform.position;
        Vector3 vector3 = position - this.m_prevPos;
        if ((double)vector3.sqrMagnitude > 9.99999974737875E-05)
        {
            this.m_pitchDeg += vector3.y * this.m_info.m_PitchInfo.m_ForceMultiplier;
            this.m_pitchDeg = Mathf.Clamp(this.m_pitchDeg, this.m_info.m_PitchInfo.m_MinDegrees, this.m_info.m_PitchInfo.m_MaxDegrees);
            this.m_rollDeg -= vector3.x * this.m_info.m_RollInfo.m_ForceMultiplier;
            this.m_rollDeg = Mathf.Clamp(this.m_rollDeg, this.m_info.m_RollInfo.m_MinDegrees, this.m_info.m_RollInfo.m_MaxDegrees);
        }
        this.m_pitchDeg = Mathf.SmoothDamp(this.m_pitchDeg, 0.0f, ref this.m_pitchVel, this.m_info.m_PitchInfo.m_RestSeconds * 0.1f);
        this.m_rollDeg = Mathf.SmoothDamp(this.m_rollDeg, 0.0f, ref this.m_rollVel, this.m_info.m_RollInfo.m_RestSeconds * 0.1f);
        this.transform.localRotation = Quaternion.Euler(this.m_originalAngles.x + this.m_pitchDeg, this.m_originalAngles.y + this.m_rollDeg, this.m_originalAngles.z);
        this.m_prevPos = position;
    }

    public DragRotatorInfo GetInfo()
    {
        return this.m_info;
    }

    public void SetInfo(DragRotatorInfo info)
    {
        this.m_info = info;
    }

    public void Reset()
    {
        this.m_prevPos = this.transform.position;
        this.transform.localRotation = Quaternion.Euler(this.m_originalAngles);
        this.m_rollDeg = 0.0f;
        this.m_rollVel = 0.0f;
        this.m_pitchDeg = 0.0f;
        this.m_pitchVel = 0.0f;
    }
}

DragRotatorInfo.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using System;

[Serializable]
public class DragRotatorInfo
{
    public DragRotatorAxisInfo m_PitchInfo;
    public DragRotatorAxisInfo m_RollInfo;
}

DragRotatorAxisInfo.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

[Serializable]
public class DragRotatorAxisInfo
{
    public float m_ForceMultiplier;
    public float m_MinDegrees;
    public float m_MaxDegrees;
    public float m_RestSeconds;
}

Answer 2:

You can use Unity’s Physics. “Joints”.

Which allows you to move your object like attached to a rope or spring.

Here is a video from Unity, about Physics Joint.

Here is the docs from Unity.

You need to make some adjustments to values in order to get the best effect you want.

I don’t know how much Unity’s Physics use your resource but this should be the easiest and fastest way.

Hope this helps! Cheers!

Answer 3:

Use SmoothDamp to create a delay movement toward your mouse position, change the z to the same level of your card to avoid the card stick to the camera.

Create a virtual point by using mouse x,y and the card z – an offset. It is because you want the point in front of your card. Then use LookAt or LookRotation to make your card look at that virtual point.

Vector3 targetPos = new Vector3(point.x, point.y ,CardBaseNode.transform.position.z);
        CardBaseNode.transform.position = Vector3.SmoothDamp(CardBaseNode.transform.position, targetPos, ref velocity, 0.1f);
        CardBaseNode.transform.rotation = Quaternion.LookRotation(CardBaseNode.transform.position - point);

The point is a Vector3 with mouse x,y and the card position with z offset already.

References