How to make mystery snack box?

When you travel around the world with your friends, you need to share and show the things you obtain in the world, and these “things” that can be shared and shown are called "Network Objects".

Now we take "Mystery Snack Box" as an example to explain how to generate and destroy a network object. Before you do that, you need to add the Para Script component to the object in your custom script. If you don’t know how to add a custom script, click here to view.

Step 1: Design a Mystery Snack Box

  1. When several friends enter this world together, they may share and show each other the things they got. So we need to design a mystery snack box that can generate different snacks for each draw.
  2. To enhance the sense of engagement in the world, the snack that a player get must be directly sent to the player's hand. At the same time, the player is also allowed to "eat", "drink" or "throw" the snack.

Step 2: Write a Mystery Snack Box Script

  1. Define 5 variables, including "Snack1", "Snack2", and "Snack3" for 3 different snacks, "randomNum" for a random number, and "Snack" for the snack that a player gets for each draw.
---@var Snack1 : UnityEngine.GameObject
---@var Snack2 : UnityEngine.GameObject
---@var Snack3 : UnityEngine.GameObject
---@end

local randomNum 
local Snack
  1. Use the random method to get a random number. Each number corresponds to one snack. When you get a random number, it means that you obtain a random snack.
  2. Since 3 different snacks are defined, the random number will be generated between 1 and 3.
  3. Note that the snack is generated by using NetSpawn. This API can be used to generate network objects so that the snack obtained by Player A can be seen by Player B and Player A can interact with Player B.
function PickSnack()
    math.randomseed(os.time())
    randomNum = math.random(3)
    local switch = {
        [1] = function()
        Snack = CS.ParaObjectService.NetSpawn(Snack1)
        end,
        [2] = function()
        Snack = CS.ParaObjectService.NetSpawn(Snack2)
        end,
        [3] = function()
        Snack = CS.ParaObjectService.NetSpawn(Snack3)
        end,
    }
    local randomFunc = switch[randomNum]
    if (randomFunc) then
        randomFunc()
    else
        print ("switch error")
    end
end
  1. To enhance players' sense of engagement, if a player uses the left hand for a lucky draw, the snack the player gets will be placed on the left hand and vice versa.
  2. Besides, there are some special situations to consider, for example, whether the hand used for a lucky draw is holding something else, in which case a hidden rule is required.
  3. The rule is that: when a player uses the left hand for a lucky draw, if the left hand is holding something else, the script will check whether the right hand is empty. If the right hand is empty, the script puts the snack on the right hand. However, if the right hand is also holding something, the script will abandon the object on the left hand and then put the snack on the left hand.
  4. Similarly, when a player uses the right hand for a lucky draw, if the right hand is holding something else, the script will check whether the left hand is empty. If the left hand is empty, the script puts the snack on the left hand. However, if the left hand is also holding something, the script will abandon the object on the right hand and then put the snack on the right hand.
function getSnack(handType)
    local player = CS.ParaPlayerService.GetLocalPlayer()
    local inLeftHand = player : GetPickupInHand(CS.HandType.Left)
    local inRightHand = player : GetPickupInHand(CS.HandType.Right)
   
    if handType == CS.HandType.Left then
        if inLeftHand == nil then
            player:Pickup(Snack.gameObject, CS.HandType.Left, CS.UnityEngine.Matrix4x4.identity)
        elseif inRightHand == nil then
            player:Pickup(Snack.gameObject, CS.HandType.Right, CS.UnityEngine.Matrix4x4.identity)
        else
            player : Drop(inLeftHand)
            player:Pickup(Snack.gameObject, CS.HandType.Left, CS.UnityEngine.Matrix4x4.identity)
        end
    elseif handType == CS.HandType.Right then
        if inRightHand == nil then
            player:Pickup(Snack.gameObject, CS.HandType.Right, CS.UnityEngine.Matrix4x4.identity)
        elseif inLeftHand == nil then
            player:Pickup(Snack.gameObject, CS.HandType.Left, CS.UnityEngine.Matrix4x4.identity) 
        else
            player : Drop(inRightHand)
            player:Pickup(Snack.gameObject, CS.HandType.Right, CS.UnityEngine.Matrix4x4.identity)
        end
    else
        print("erro handType")
    end
end
  1. Finally, you need to set the triggering mode of the Mystery Snack Box. You can configure to trigger the box by clicking an UI element or directly clicking the 3D object.
function OnUIPointerClick(uiType, handType)
    if uiType == CS.UIType.Pickup then
        getSnack(handType)
    end
end

function On3DClick(handType, location)
    getSnack(handType)
end

Step 3: Write a "Snack Eating" Script

  1. To enhance players' sense of engagement, when "eating" snacks, the action of "eating" shall be played simultaneously and the "eaten" snack must be destroyed.
  2. Define the variables, including "eatHandAnimation" for left hand actions upon eating snacks, "eatHandAnimationRight" for right hand actions upon eating snacks, "isPickedUp" for current pick-up state, "curPlayer" for the player’s object, "curPlayerAnimator" for the player’s actions, and "pickPlayer" for the player.
---@var eatHandAnimation :UnityEngine.AnimationClip
---@var eatHandAnimationRight :UnityEngine.AnimationClip
---@end
local isPickedUp = nil
local curPlayer = nil
local curPlayerAnimator = nil
local pickPlayer = nil
  1. Set the initial pick-up state.
function Start()
    isPickedUp = true
end
  1. Obtain the player who picks up the snack and the player's action when the snack is picked up.
function OnPickup(player) 
    pickPlayer = player;
    curPlayer = player.gameObject;
    curPlayerAnimator = curPlayer:GetComponentInChildren(typeof(CS.UnityEngine.Animator))
end
  1. Reset the pick-up state when the snack is discarded.
function OnDrop(player)
    isPickedUp = true;
end
  1. Customize a method called "Cheer" that plays the corresponding actions based on the player’s hand clicking on UI and destroys the corresponding snack at the same time.

Note that the snack is destroyed by using NetDestroy. This API is used to destroy network objects. As snacks are network objects generated with NetSpawn, they need to be destroyed using NetDestroy. Therefore, the snack eaten by Player A will not be seen by or interacted with Player B.

function Cheer(hand)
    if hand == CS.HandType.Left then
        pickPlayer:SetOverrideAnimation("proxy_hand_left_cheer",eatHandAnimation,function(eatHand)
            pickPlayer:SetGesture(hand, 1)
            pickPlayer:SetOverrideAnimation("proxy_hand_left_cheer",nil,nil);
            pickPlayer:Drop(self.gameObject)
            CS.ParaObjectService.NetDestroy(self.gameObject)
        end);
        pickPlayer:SetGesture(hand, 2);
    end
    if hand == CS.HandType.Right then
        pickPlayer:SetOverrideAnimation("proxy_hand_right_cheer",eatHandAnimationRight,function(eatHand)
            pickPlayer:SetGesture(hand, 1)
            pickPlayer:SetOverrideAnimation("proxy_hand_right_cheer",nil,nil)
            pickPlayer:Drop(self.gameObject)
            CS.ParaObjectService.NetDestroy(self.gameObject)
        end);
        pickPlayer:SetGesture(hand, 2)
    end
end
  1. Obtain the hand the player is using upon clicking UI and execute the Cheer method.
function OnUIPointerClick(uiType, handType)
    local a = self.gameObject
    if uiType == CS.UIType.Main and isPickedUp == true then
        if curPlayerAnimator ~= nil then
            Cheer(handType);
        end
    end
end

Step 4: Configure the Scripts

  1. Make a basic interactive object as Mystery Snack Box and attach the Mystery Snack Box script.
  1. Associate the Mystery Snack Box script with the basic interactive object using the method you have learned in the previous section.
  1. Make 3 pickup objects to represent 3 different snacks named as "Snack1", "Snack2", and "Snack3", respectively, then attach the snack-eating script to each snack.
  1. Attach 3 snacks to the Mystery Snack Box.

Until now, a Mystery Snack Box that can randomly generate 3 different snacks has been successfully made, and all of them can be eaten in the world~
Just make your own box and place it in your world, and do not forget to invite your friends to try it out!