Pick Up and Throw – Materials, Scale and Selection Updates

After this and this recent post, the player PickUpandThrow mechanic has now been updated to address some issues including:

  • Collisions when ‘holding’ an interactable object
  • Better throwing/firing mechanics
  • Selection outline for interactables
  • Swapping materials

There are a few other updates, especially related to destructable objects that I’ll cover elsewhere.

Pick up and Throw updates


One of the main problems I wanted to address in this update was how to change the PBR material of the interactable object so that when it is held, the player can ‘see through’ it. This was no porblem with just using the base color option of the attached material but when using an Albedo texture, an alpha animation was more problematic.

My first effort was to create a custom shader using the Unity ShaderGraph – a very neat tool that I’ll no doubt come back to at a later point. While creating a basic PBR setup with Albedo, Smooth, Metallic, AO etc channels was straight forward there seems to be an issue with the way in which SG deals with normal map calculation (see this unity forum post). So while I was able to create an alpha slider for the albedo channel and expose that parameter in the Inspector, the normal mapping was a bit weird.

That led me into Blender at first and experimenting with trying to UV unwrap and export a project specific cube/sphere (which in turn led to this post) but I was still stuck with the normals issue.

The solution (at the mo) is Unity’s built in URP lit shader that includes Albedo, Metallic, Normal and AO channels that render nicely but accessing an alpha solution was a bit of a pain. In the end I opted for a second (lit shader) material slot in the Object class that was a copy of the original apart from the surface type set to Transparent instead of Opaque. Transparent materials have easy access to the alpha channel (in the same way as using just the color channel) that in turn can be run through an Animator component in order to achieve the fade up/down effect I need.

In the Object class, the relevant lines look like this:


    //Set the 2 materials in the Inspector:
    public Material defaultMaterial;
    public Material alphaMaterial;

            //if is holding this object:
            if (pickUpAndThrow.isHoldingObject)
                if (pickUpAndThrow.hitObjectIsAtHand)
                    //switch off outline:
                    outline.enabled = false;

                    //change material surface type to transparent:
                    renderer.material = alphaMaterial;

                    //fade down alpha animation:
                    animator.SetBool("isHolding", true);


                if (!pickUpAndThrow.hitObjectIsAtHand)
                    //switch on outline:
                    outline.enabled = true;

                    //change surface to opaque
                    renderer.material = defaultMaterial;

                    //fade up alpha animation:
                    animator.SetBool("isHolding", false);

I’m not posting the whole code here as I’m only interested in what has changed compared to the script posted in Change Color On Raycast Hit.

The main changes are using the second (transparent) material and accessing the animator component that simply fades the alpha value of the material.

There may be issues in the future as transparent materials are expensive, plus I’m not altogether sure if I’m spending resources in duplicating shaders here, but as this effect is applied to one object at a time, and based on my current tests it does the job for now.

Selection Highlighting

I wanted to add an outline to selectable objects to indicate that they can be picked up. Fortunately this solution was an easy (if lazy) one. After wrestling with the SG once again, and coming to the conclusion that while fresnel was a great option for spheres, it wasn’t so effective with cubes – what with it being fresnel!!!! – I found this FREE asset that was easy to integrate, access and control from the Object class.

While I do try my best not to rely on asset store resources this plug in is great…and I’m using it!!!!

One Thing At A Time!

Another easy fix (that should also save a bit of performance…until I need multi-tasking interactions!!!) was to limit the raycast call from the player so there are no other raycast trigger events while holding an object.

This fix is in the PlayerManager() class, in Update():

        //Only perform a raycast if NOT
        //holding an interactable object
        if (!pickUpandThrow.hitObjectIsAtHand)
        { rayCastHit = rayCast.DoRaycast(cam, scanRange); }

Collisons and Physics

In this final section of this post I want to highlight the physics interactions that include collisions, throwing, pick up move/rotate and moving playerhand based on object scale.

As an additional note, this unity answers post was a help in determining the rotation/position maths. The main methods to get a grip of are the MovePosiiton() and RotatePosition() calculations that eliminate the isKinematic calls I was making before.

Fortunately for me I’ve been employing good practice and the comments in the PickUpandThrow class, (that manages all related interactions/mechanics) explain the whole process in a much more concise and demonstrable manner than I would manage using this text…

private void FixedUpdate()
        if (input.isInteracting && !isHoldingObject)
            //we can pick up the object if we can see it:
            if (playerManager.rayCastHit != null
                && playerManager.rayCastHit.CompareTag("Interactable"))
                //set the RB of target object received from the raycast
                //in PlayerManager:
                hitObjectRigidBody =

                //move the player hand forward by size of
                //object * offset
                hitObjectScale = hitObjectRigidBody.transform.localScale;

                playerHand.transform.localPosition = new Vector3(
                    0, 0, hitObjectScale.z * playerHandOffset);

                //toggle interact button
                input.isInteracting = false;

                //we are now holding the object
                isHoldingObject = true;

        //Pick up the object
        if (isHoldingObject && !hitObjectIsAtHand)
            //move towards player hand
                pickUpSpeed * Time.fixedDeltaTime));

            //rotate towards zero:
            Vector3 targetDirection =
                (hitObjectRigidBody.position - Vector3.zero).normalized;

            Quaternion targetRotation = Quaternion.LookRotation(targetDirection);

                (hitObjectRigidBody.transform.rotation, targetRotation,
                pickUpSpeed * Time.fixedDeltaTime));

            //turn off gravity
            hitObjectRigidBody.useGravity = false;

        if (isHoldingObject && hitObjectIsAtHand)
            //if object has reached player hand disable its movement
            //by 'teleporting it into position. 

            //is the object is != rotation zero
            //slow rotation by lerping the angular velocity
            //to zero:
            if (hitObjectRigidBody.rotation
                != Quaternion.Euler(Vector3.zero))
                hitObjectRigidBody.angularVelocity = Vector3.Lerp
                    pickUpSpeed * Time.fixedDeltaTime);

        if (input.isInteracting && isHoldingObject)
            //turn gravity back on:
            hitObjectRigidBody.useGravity = true;

            //stop objects rotation by setting
            //it to zero to make sure it fires forward
            hitObjectRigidBody.rotation = Quaternion.Euler(Vector3.zero);

            //set direction based on camera transform:
            Vector3 pushDirection = cam.transform.forward;

            //add upwards multiplier only to Y transform
            pushDirection.y *= upMultiplier;

            //Fire the object away
                pushDirection * throwSpeed, ForceMode.Impulse);

            //reset hand position
            playerHand.transform.localPosition = Vector3.zero;

            //we are no longer holding the object
            isHoldingObject = false;

            //toggle interact button
            input.isInteracting = false;