Pickup / Collectible
Collectible item with bobbing animation, rotation, magnet pull, and event callbacks for coins, health, ammo, etc.
How to Use
Attach Collectible to any pickup object
Add a Trigger Collider (Box, Sphere, or Circle for 2D)
Tag your player as 'Player'
Set item ID ('coin', 'health', 'ammo') and value
Hook OnCollected event to your score/inventory system
Enable magnet for auto-attract towards player
Works with both 3D (OnTriggerEnter) and 2D (OnTriggerEnter2D)
Features
- Configurable bob and rotate animation with adjustable speed and height
- Magnet attraction that pulls collectibles toward the player automatically
- Supports both 3D and 2D trigger detection out of the box
- UnityEvent callback on collection for easy integration
- Customizable item ID and value for different pickup types
- Optional destroy-on-collect toggle for reusable pickups
When to Use This
Essential for any game with collectible items — coins in platformers, power-ups in shooters, gems in endless runners, or resources in survival games. Works with both 2D and 3D projects. Pair with the Simple Inventory script for full item management.
Common Mistakes
The collectible needs a Collider (or Collider2D) with 'Is Trigger' checked — without it, OnTriggerEnter won't fire. Your player must have a Rigidbody (or Rigidbody2D) and be tagged 'Player' for detection to work. If magnet isn't pulling, make sure enableMagnet is checked and the player tag matches the playerTag field.
Source Code
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// Collectible item with bob/rotate animation and magnet attraction.
/// Use for coins, health pickups, ammo, keys, etc.
/// </summary>
public class Collectible : MonoBehaviour
{
[Header("Animation")]
[SerializeField] private float bobHeight = 0.2f;
[SerializeField] private float bobSpeed = 2f;
[SerializeField] private float rotateSpeed = 90f;
[Header("Collection")]
[SerializeField] private string playerTag = "Player";
[SerializeField] private bool destroyOnCollect = true;
[Header("Magnet")]
[SerializeField] private bool enableMagnet = false;
[SerializeField] private float magnetRange = 3f;
[SerializeField] private float magnetSpeed = 8f;
[Header("Value")]
[SerializeField] private int value = 1;
[SerializeField] private string itemId = "coin";
[Header("Events")]
public UnityEvent<Collectible> OnCollected;
private Vector3 startPosition;
private Transform magnetTarget;
private bool isCollected;
/// <summary>Item ID for identification.</summary>
public string ItemId => itemId;
/// <summary>Value of this collectible.</summary>
public int Value => value;
private void Start()
{
startPosition = transform.position;
}
private void Update()
{
if (isCollected) return;
// Bob animation
float newY = startPosition.y + Mathf.Sin(Time.time * bobSpeed) * bobHeight;
transform.position = new Vector3(transform.position.x, newY, transform.position.z);
// Rotate
transform.Rotate(Vector3.up, rotateSpeed * Time.deltaTime);
// Magnet attraction
if (enableMagnet && magnetTarget != null)
{
float dist = Vector3.Distance(transform.position, magnetTarget.position);
if (dist < magnetRange)
{
Vector3 dir = (magnetTarget.position - transform.position).normalized;
float speed = magnetSpeed * (1f - dist / magnetRange);
startPosition += dir * speed * Time.deltaTime;
}
}
}
private void OnTriggerEnter(Collider other)
{
if (isCollected) return;
if (other.CompareTag(playerTag))
{
Collect(other.gameObject);
}
}
private void OnTriggerEnter2D(Collider2D other)
{
if (isCollected) return;
if (other.CompareTag(playerTag))
{
Collect(other.gameObject);
}
}
/// <summary>
/// Collect this item. Called automatically on trigger or manually.
/// </summary>
public void Collect(GameObject collector)
{
if (isCollected) return;
isCollected = true;
OnCollected?.Invoke(this);
if (destroyOnCollect)
Destroy(gameObject);
}
private void OnEnable()
{
// Find player for magnet
if (enableMagnet)
{
GameObject player = GameObject.FindGameObjectWithTag(playerTag);
if (player != null) magnetTarget = player.transform;
}
}
}