Shopping Frenzy
Wednesday, Sep 22, 2021 | Update at Tuesday, Nov 30, 2021

My first public game jam, VimJam2. With the requirement of “Boss” and Theme “On The Edge”, in my team of two we created Shopping Frenzy.

I was in a team of two with another artist called George. He created all the models using 3ds max and Adobe Substance 3D Painter and imported them into Unity. Once in Unity, I set up the game scenes and did all of the programming. For VimJam2 we had ten days to hand in after being told the theme.

How the checkout worked

Player's Checkout

Image of the player's checkout

The belt of the checkouts was separated by the artist, this allowed me to move products only touching the belt instead of the whole checkout. To move products in a predictive and expected manner I used physics with rigidbodies.

To achieve this, in FixedUpdate, I would teleport the belt’s rigidbody (_rigidbody) away from the scanner using _rigidbody.position and then move it back in the same frame with _rigidbody.MovePosition. This would apply forces to the products sitting on top of the belt towards the scanner but not away from it.

Then I would just stop the movement of the belt when something blocked the raycast.

private void FixedUpdate()
{
    if (SensorBlocked())
        return;
    
    _rigidbody.position += _rigidbody.transform.right * (_speed * Time.fixedDeltaTime); 
    _rigidbody.MovePosition(_position);
}


private bool SensorBlocked() => Physics.Raycast(_sensorObject.position, _sensorObject.right, _sensorDistance);

Belt with collider visable

Image of the belt with its box collider visible

The scanner was just a simple box collider set to be a trigger. When an item enters with the correct tag and script it would invoke a unity event. Using a unity event and setting it in the editor meant the other checkouts, which were linked by prefabs, could easily be unlinked to the main Checkout script.

public UnityEvent<Item> ItemScanned;
[SerializeField] private AudioClip _beepClip;
[SerializeField] private AudioSource _audioSource;

private void OnTriggerEnter(Collider other)
{
    if (!other.CompareTag("Item"))
        return;

    Item item = other.GetComponent<Item>();
    if (item == null)
        return;
    
    _audioSource.PlayOneShot(_beepClip);
    ItemScanned?.Invoke(item);
}

Scanner

Image of the scanner with its box collider visible.

Use of scriptable Objects for products

For the info of each product, I used scriptable objects to store and edit the info. This made it simple for the artist to add more products without assistance from me.

These scriptable objects were also stored on the each items instance so that the checkout could find the information such as price and icon to display when the player scans it.

[CreateAssetMenu(fileName = "New Product", menuName = "ScriptableObjects/New Product")]
public class Product : ScriptableObject
{
    public string Name;
    public float Price;
    public Sprite Icon;
    public GameObject Prefab;
    public float HoldDistance = 1;
}

Picking up items

Items picked up by the player was held in front of the camera using a fixed joint. It made it so that the player could not drag the item inside of other colliders such as the checkout. I also made use of a tweens using the libary, DoTween. This was to create a smooth pickup transision instead of instantlly teleport into their hands.

_heldItem = hitInfo.collider.GetComponent<Item>();
_heldItemRB = _heldItem.GetComponent<Rigidbody>();
_holderT.localPosition = Vector3.forward * _heldItem.HoldDistance;
_heldItemRB.DOMove(_holderT.position, _pickupSpeed)
    .OnComplete(ConnectObject);
private void ConnectObject()
{
    _holderJoint = _holderT.gameObject.AddComponent<FixedJoint>();
    _holderJoint.connectedBody = _heldItemRB;
    _holdingItem = true;
    Debug.Log($"The player has picked up {_heldItem.Name}");
}

Images