Close

Unity 3D – Object Bouncing/Acceleration/Glue Platform Behaviour

This posts describes how to make a simple object bouncing, acceleration and glue platform behaviour for Unit 3D. In action, it looks like this:



The behaviour is realised by setting a simple execution behaviour (script) on the player (red) and a property definition script on each obstacle (green) make the entire physics-based ping-pong happen. There is no additional code for movement in this scene other than the forces applied each time the player hits the green obstacles.

Currently, the script supports active bouncing (flipper effect), a glue on contact with timer and a sideways acceleration, defining the velocity amplification factor individually for each obstacle. The script also supports (set in inspector) callbacks for all major events in order to adapt animation, sounds etc. Everything is based on physics and forces in order to make it “realistic” in the play flow.

Lastest version of source code: https://github.com/imifos/unity3d-workspace/pingpongtest

Assets/ObjectVelocityModifier2DController.cs – Added to the ball/player

using System.Collections;
using UnityEngine;

public class ObjectVelocityModifier2DController : MonoBehaviour {
    
    private Rigidbody myBody;
    private Vector3 velocityBeforeImpact;
    private Vector3 velocityBeforeGlue;
    private GameObject lastCollidedObject=null;
            

    void Start()
    {
        myBody = GetComponent<Rigidbody>();
    }
        
    void FixedUpdate()
    {
        // If we need the original velocity, we need to keep the velocity because 
        // the physics engine re-sets the velocity on impact according to physics, 
        // so in "OnCollisionEnter()", we don't have access to this 'pure' velocity anymore.
        velocityBeforeImpact = myBody.velocity;
    }

    private void OnCollisionEnter(Collision c)
    {
        // The glue effect (coroutine) provoques multiple calls on same object, 
        // so we need to block this.
        if (c.gameObject==lastCollidedObject) {
            return;
        }
        lastCollidedObject = c.gameObject;
              
        // Get object bouncing properties from object hit
        // If there are no reflection properties, just let the physics engine do it thing
        ObjectVelocityModifier2DProperties properties = c.gameObject.GetComponent<ObjectVelocityModifier2DProperties>();
        if (properties == null)
            return;
        
        ObjectVelocityModifier2DProperties.Property p = properties.ForTag(gameObject.tag);
        if (p == null)
            return;
        
        if (p.onIsHit != null)
            p.onIsHit.Invoke();
        
        // Apply effects
        DoBounce(c,p);
        DoGlue(c,p);
        DoAcceleration(c, p);
    }

    /*
     * Applies the acceleration effect.
     */
    private void DoAcceleration(Collision c, ObjectVelocityModifier2DProperties.Property appliedProperty)
    {
        if (!appliedProperty.doAcceleration)
            return;

        Vector3 direction=Vector3.zero;
        switch(appliedProperty.accelerationDirection) {
            case ObjectVelocityModifier2DProperties.Direction.AWAY:
                direction = Vector3.right;
                break;
            case ObjectVelocityModifier2DProperties.Direction.LEFT:
                direction = Vector3.forward;
                break;
            case ObjectVelocityModifier2DProperties.Direction.RIGHT:
                direction = Vector3.back;
                break;
            case ObjectVelocityModifier2DProperties.Direction.TOFRONT:
                direction = Vector3.left;
                break;
        }
        myBody.AddForce(Quaternion.AngleAxis(90, direction) * c.contacts[0].normal * appliedProperty.accelerationVelocityFactor, ForceMode.Impulse);
                  
        if (appliedProperty.onIsAccelerated != null)
            appliedProperty.onIsAccelerated.Invoke();
    }


    /*
     * Applies the glue effect.
     */
    private void DoGlue(Collision c, ObjectVelocityModifier2DProperties.Property appliedProperty)
    {
        if (!appliedProperty.doGlue)
            return;
        
        IEnumerator coroutine = UnglueAfter(appliedProperty);
        StartCoroutine(coroutine);
    }

    private IEnumerator UnglueAfter(ObjectVelocityModifier2DProperties.Property appliedProperty)
    {
        // ** Start-up/Initialisation

        // Keep original physics velocity
        velocityBeforeGlue = myBody.velocity;

        // Documentaion: If isKinematic is enabled, Forces, collisions or joints will not affect the rigidbody anymore. 
        // The rigidbody will be under full control of animation or script control by changing transform.position. 
        //Kinematic bodies also affect the motion of other rigidbodies through collisions or joints. 
        myBody.isKinematic = true;
        myBody.velocity = Vector3.zero;

        if (appliedProperty.onIsGlued!=null)
          appliedProperty.onIsGlued.Invoke();

        // ** Yield flow
        yield return new WaitForSeconds(appliedProperty.glueTime);

        // ** Delayed invocation
        myBody.isKinematic = false;
        if (appliedProperty.gluePreserveVelocity)
          myBody.velocity = velocityBeforeGlue;

        if (appliedProperty.onGlueReleased!=null)
            appliedProperty.onGlueReleased.Invoke();
    }

    /*
     * Applies the bouncing effect.
     * https://answers.unity.com/questions/580867/issue-using-vector3reflect-to-bounce-a-ball.html
     */
    private void DoBounce(Collision c,ObjectVelocityModifier2DProperties.Property appliedProperty)
    {
        ContactPoint cp = c.contacts[0];

        if (!appliedProperty.doBounce)
            return;
        
        // Method 1:
        // Add velocity in direction of hit objects normale multiplied by factor. The object flies then according to physics.
        // This is the same than method 3.
        //myBody.velocity = velocityBeforeImpact + cp.normal * p.velocityFactor * velocityBeforeImpact.magnitude;

        // Method 2:
        // - Calculate the reflection angle (incoming velocity to touched object normale)
        // - Add an applification
        // (not used) myBody.velocity = Vector3.Reflect(velocityBeforeImpact, cp.normal);
        //myBody.velocity += cp.normal * bounceForce;

        // Method 3:
        // Using the physics engine.
        myBody.AddForce(c.contacts[0].normal * appliedProperty.bounceVelocityFactor, ForceMode.Impulse);
    }
}

Assets/ObjectVelocityModifier2DProperties.cs – Added to each plattform/obstacle, and configured via Inspector

using UnityEngine;
using UnityEngine.Events;

public class ObjectVelocityModifier2DProperties : MonoBehaviour {

    [System.Serializable]
    public enum Direction
    {
        LEFT, RIGHT, AWAY, TOFRONT
    }

    [System.Serializable]
    public class Property
    {
        // General
        // -------
        [Header("General")]

        // Addressed specific object tag or none for default
        [Tooltip("Target object type by tag, or empty to use as default. Specific tag has priority over default.")]
        public string tagNameOrEmpty="";

        // Ignore Behaviour
        [Tooltip("FALSE to use the bouncing properties, TRUE to apply standard physics and 'neutralise' the bouncing. Same as factor 0.")]
        public bool ignoreTheseModifiers=false;
                
        // Bouncing Behaviour
        // ------------------
        [Header("Bouncing Behaviour")]
        public bool doBounce = false;

        [Tooltip("Multiplication of incoming velocity to calculate outgoing bouncing velocity.")]
        public float bounceVelocityFactor=2f;

        // Glue Behaviour
        // --------------
        [Header("Glue Behaviour")]
        public bool doGlue = false;
        public float glueTime = 2f;

        [Tooltip("TRUE to preserve velocity from before glue full-stop and restore it afterwards.")]
        public bool gluePreserveVelocity = false;

        // Acceleration Behaviour
        // ----------------------
        [Header("Accelerate Behaviour")]
        public bool doAcceleration = false;

        public Direction accelerationDirection=Direction.LEFT;

        [Tooltip("Multiplication of incoming velocity to calculate outgoing acceleration velocity.")]
        public float accelerationVelocityFactor = 2f;

        // Callbacks
        // ---------
        [Header("Event Callbacks")]
        public UnityEvent onIsHit;
        public UnityEvent onIsGlued;
        public UnityEvent onGlueReleased;
        public UnityEvent onHasBounced;
        public UnityEvent onIsAccelerated;
    }

    [Header("Object Behaviour Properties")]
    public Property[] properties;
   


    public Property ForTag(string name)
    {
        int defaultIndex = -1;
        int foundIndex = -1;

        // Search properties
        for (int i = 0; i < properties.Length; i++)
        {
            if (properties[i].tagNameOrEmpty == name)
                foundIndex = i;
            if (properties[i].tagNameOrEmpty == "")
                defaultIndex = i;
        }

        // Found a definition for tag
        if (foundIndex != -1)
        {
            if (properties[foundIndex].ignoreTheseModifiers)
                return null; // This tag is set to not to apply modifiers
            else
                return properties[foundIndex];
        }

        // Found a default definition
        if (defaultIndex != -1)
        {
            if (properties[defaultIndex].ignoreTheseModifiers)
                return null;
            else
                return properties[defaultIndex];
        }

        // Found nothing, caller should ignore.
        return null; 
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close