0% found this document useful (0 votes)
4 views

Unity VR Interactions-Slicing GameObjects

This document outlines a tutorial for creating a slicing mechanic in Unity VR using the Ezy-Slice framework and Valve's VelocityEstimator script. It provides step-by-step instructions for setting up a Unity project with sliceable GameObjects, configuring a slicer GameObject, and implementing slicing functionality. The course is part of CDM 176: Special Topics in Game Development - Virtual Reality at the University of California at Davis, taught by Adam Wright in Spring Quarter 2025.

Uploaded by

glwen
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views

Unity VR Interactions-Slicing GameObjects

This document outlines a tutorial for creating a slicing mechanic in Unity VR using the Ezy-Slice framework and Valve's VelocityEstimator script. It provides step-by-step instructions for setting up a Unity project with sliceable GameObjects, configuring a slicer GameObject, and implementing slicing functionality. The course is part of CDM 176: Special Topics in Game Development - Virtual Reality at the University of California at Davis, taught by Adam Wright in Spring Quarter 2025.

Uploaded by

glwen
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 36

Unity VR: Slicing GameObjects

CDM 176: Special Topics in Game Development - Virtual Reality

Course Details
Instructor: Adam Wright, PFS / CDM
Author: Adam Wright, PFS / CDM
Class: CDM 176: Special Topics in Game
Development - Virtual Reality
Academic Term Spring Quarter, 2025
Academic Institution: University of California at Davis

Technical Specifications
Unity Editor version: Unity 6 (6000.0.24f1 [LTS])
XR Interaction Toolkit version: 3.0.8
XR Plugin Management version: 4.5.1
VR Device: Meta Quest 2

Requirements
A Unity project that is:
●​ Configured for VR
●​ Containing a functional scene
●​ Containing “sliceable” GameObjects
●​ Containing a grabbable “slicer” GameObject
●​ Containing a functional XR Origin with configured right and left controllers
●​ Containing Valve’s Velocity Script (this can be found on Canvas)
●​ Containing David Arayans “Ezy Slice” procedural mesh generation framework for
slicing
●​ Configured for building to the Meta Quest 2 headset, with the correct project
settings
●​ Configured with a functional text editor

Overview
This tutorial covers how to “slice” through GameObjects in your scene. For ease-of-use,
we will be using a well-known open source mesh slicer framework for Unity created by
David Arayan called Ezy-Slice. This framework utilizes procedural mesh generation that
identifies the vertices on either side of a plane used to “slice” a GameObject. In addition
to this framework, we will also be using Valve’s VelocityEstimator script.

Getting Started

Create a Scene with Table and an Sliceable GameObject


In my example scene, I’ve created a “table” GameObject and placed a “sliceable” cube
GameObject on it

Setting Up Ezy-Slice open source mesh slicer framework


1.​ Download “Ezy-Slice” from here.
2.​ On the GitHub page, go to Code and choose Download ZIP
3.​ In the downloaded EzySlice Master folder, drag the EzySlice folder into your
Assets, in the Project Window in Unity

The EzySlice folder should contain the following items:

●​ A folder called Framework


●​ A script called SlicedHull
●​ A script called Slicer
●​ A script called SlicerExtensions
Drag “Ezy Slice” Into Your Assets Panel:

Optional: Creating and Configuring a Test Slicer


*Go to Section 1 Below if you want to skip this*

This section covers adding and configuring “slicer” mechanics in our Unity project that
can be tested using a plane intersecting with a “sliceable” GameObject on a simple
keystroke. Later we will convert this into mechanics that can work with our prefab
“slicer”.

Step 1: Create a Scene with Table and an Sliceable GameObject


In my example scene, I’ve created a “table” GameObject and placed a “sliceable” cube
GameObject on it
Step 2: Add a Plane that Intersects With the Sliceable
GameObject
In this step, we will be adding a plane as an intersection point. Eventually, we will be
using this plane to test the slicing mechanics we will be creating in the next few steps

●​ Create a Plane GameObject


●​ Scale it to 0.05 for x, y, and z in the Transform Component
●​ Move the Plane GameObject to intersect with the sliceable GameObject
Step 3: Create the SliceObject Script
In this step we will create and write the first part of the SliceObject script that will be
attached to the “slicer” object.

Right click in your Scripts Folder and create a script called “SliceObject”
Open your script. Add the following reference in the header that refers to the EzySlice
framework:

●​ using EzySlice;

Next, add two public variables under the public class SliceObject:

●​ public Transform planeDebug


●​ public GameObject target
Step 4: Add “Slice” Function under Update with Test Plane
In this step we will add a function that uses the EzySlice script reference. This logic will
use the “target” public variable, and add a .Slice method that also uses the
“planeDebug” public Transform variable that gets its position from the “up” arrow:

This logic performs a check to see if there is GameObject (target variable) to be sliced,
and if so, will create an upper and lower “hull” GameObjects, on either side of the plane.
Step 5: Add Test Logic to Perform a Split Along the Axis of the
Plane Intersection
In this section we will add some test logic (logic we will replace in later steps) to see if
the split mechanics are working. We will be using a spacebar keypress as our input for
this test.

Add the Input System to the Header:

●​ “Using UnityEngine.InputSystem”

Next, in the Update() function add the following logic:

●​ If (Keyboard.current.spaceKey.wasPressedThisFrame)
{
​ ​ ​ Slice(target);
}
Step 6: Add Script to “Slicer” GameObject and Configure
In this step, we will add the SliceObject Script to our “Slicer” GameObject and add the
Plane Debug GameObject and Target to the Script Component.

1.​ In your Hierarchy, click on your chosen “Slicer” Prefab GameObject

2.​ In the Inspector Window, click Add Component and add the script called
“SliceObject”

3.​ In the SliceObject Script component, add the plane to the Plane Debug
Transform field

4.​ In the SliceObject Script component, add the “sliceable” GameObject to the
Target GameObject field (in my case, I am using a blue cube

Step 7: Test in the Unity Editor


In this step, we will test to see if the mechanics are working by playing the simulation in
Unity and pressing the spacebar while in play mode (in the game view).
Do the following:
●​ Play the simulation in the Unity Editor
●​ Switch to the Game View
●​ Hit the Spacebar
●​ Switch back to the Scene View

While in Play Mode, after hitting the spacebar in the Game View, and
switching back to the Scene View, you should be able to see and do the
following:

●​ An “Upper_Hull” GameObject and a “Lower_Hull” GameObject should populate


in the Hierarchy Panel
●​ While still in Play Mode, you should be able to select the “Upper_Hull” and
“Lower_Hull” and move them apart from each other, while still seeing the original
GameObject.*

Note: at this point this “splitting” has produced two new game objects that are
essentially parts of the original GameObject. However, you’ll notice that the original
GameObject is still present in the scene. This is because “slicing” a GameObject is
really just creating two new GameObjects, and then destroying the original to produce
the illusion of “slicing.”

Step 8: Fix the Slice Material and Destroy the Original


GameObject
In this step, we will fix the material that is exposed after a GameObject “slice” takes
place, and destroy the original sliced GameObject after a “slice” has been completed.

In the Script, do the following:

Add a public variable called “crossSectionMaterial”:

●​ public Material crossSectionMaterial;

In the “upperHull” and “lowerHull” logic, in the Slice add the following to create a new
material for the exposed side of the sliced GameObject:
Additionally, in the “SlicedHull” function’s “if” statement, add a line at the end to destroy
the original GameObject:

●​ Destroy(target);

In the Unity Editor, do the following:

1.​ Create a new material for the Cross Section Material public variable you’ve just
implemented in the script

2.​ Click on the “Slicer” Prefab / GameObject (in my case its the “Sword” prefab)

3.​ In the Inspector window, locate the Slice Object Script component and drag the
new material into the Cross Section Material field
Test this the same way you tested in Step 5.

Step 9: Add “Breaking Apart” logic to SliceObject Script


In this step, we will add logic to the script that gives the “sliced” GameObject the illusion
of breaking apart in a similar way to physical objects being cut.

Add a new public float variable called “cutForce” and give it a value of 2000:

●​ public float cutForce = 2000;


Create a new function called “SetupSlicedComponent” that adds both a Rigidbody and
Convex Mesh Collider components to the sliced GameObjects, and utilizes an
“.AddExplosionForce” method (that uses the cutForce variable) to “throw” the objects
apart after being cut.

Add a reference to the “SetupSlicedComponent” function in the Slice functions “if”


statement to evoke the “breaking apart” action
Now, test this the same way you tested in Step 5. After testing, check to see if both
Upper_Hull and Lower_Hull GameObjects have a Rigidbody and a Convex
MeshCollider components.

____________________________________________________________________

Section 1: Building a Slicer Interaction with a Prefab


GameObject
In this section we will cover how to build a Slicer interaction that includes building a
configuring a Slicer GameObject and a step by step tutorial on how to write a script to
accomplish this.

Step 1: Acquire or Build a Prefab GameObject to use as a “Slicer”


In my example project, I’ve constructed a Prefab Sword GameObject to use as a
“slicer.” In addition to box colliders that “form fit” the prefab, I’ve added and configured
the following components:
XR Grab Interactable component
●​ In this component, I’ve added the “handle” (capsule collider) of the prefab into the
colliders list
●​ Additionally, I’ve checked the box for “Dynamic Attach” which allows me to grab
the handle from any orientation

Rigidbody component
●​ Checked the box for “is kinematic”
●​ Changed the Collision Detection to “Continuous”
Add the Prefab GameObject “Slicer” to your scene
In your scene, add the prefab “slicer”. Place it near the “sliceable” GameObject

Step 2: Add the VelocityEstimator Script to Your Unity Project


Add the VelocityEstimator script to your Unity project. Make sure to store it in your
Scripts folder. The VelocityEstimator script will be used in the tip of the Slicer
GameObject to obtain the velocity so that it can be used to determine cut force.
Step 3: Create a Script called SliceObject (if you have not
already), add NameSpace, Variables, and Attach this to your
Slicer GameObject.
In the previous section we created a test slicer interaction that tested the basic
mechanics of how the Slicer works. However, for this section we will assume you are
starting this script from scratch. We will add Namespaces (libraries) and Public
Variables in the top of the Public Class. Attach this Script to your Slicer GameObject (in
my case, the Sword prefab)

●​ Create a new script in your Scripts folder called SliceObject

Add NameSpace (libraries) to the header of your script:


●​ UnityEngine;
●​ UnityEngine.XR.Interaction.Toolkit.Interactables;

Add the following public variables in the Public Class:


●​ public Transform startSlicePoint;
●​ public Transform endSlicePoint;
●​ public LayerMask sliceableLayer;
●​ public VelocityEstimator velocityEstimator;
●​ public Material crossSectionMaterial;
●​ public Float cutForce = 500f;
Step 3: Add FixedUpdate() Function and New Logic in
FixedUpdate() Function
Here we will convert the Update() function into FixedUpdate() and add boolean and
ray cast logic that uses new public variables. This initial logic will determine if our
GameObject “slicer” has hit the “sliceable” GameObject. We will also be referencing
Valve’s VelocityEstimator script as a public variable and creating a Layer Mask for
“slicing” GameObjects.

Step 4: Create a Slice Function


Create a Slice Function.To start the “Slice” function should utilize startSlicePoint and
endSlicePoint variables. We will also write a new function that utilizes the
VelocityEstimator. In addition we will be utilizing Vector3 for both getting the velocity
of the controllers (“slicer” prefab), and calculating the startSlicePoint and
endSlicePoint positions before normalizing the cutting plane in the “slicer.”

In the top of the Slice Function included the following:


●​ Vector3 velocity = velocityEstimator.GetVelocityEstimate();
●​ Vector3 planeNormal = Vector3.Cross(endSlicePoint.position -
startSlicePoint.position, velocity);
●​ planeNormal.Normalize();
●​ SlicedHull hull = target.Slice(endSlicePoint.position, planeNormal);

Add an“If” Statement to handle Material References for


CrossSectionMaterial Material to Slice Function
In the “upperHull” and “lowerHull” logic, in the Slice add the following to create a new
material for the exposed side of the sliced GameObject. Additionally, in the “SlicedHull”
function’s “if” statement, add a line at the end to destroy the original GameObject.

Step 4: Create SetupSlicedComponent function


Create a new function called “SetupSlicedComponent” under FixedUpdate() that adds
both a Rigidbody and Convex Mesh Collider components to the sliced GameObjects,
and utilizes an “.AddExplosionForce” method (that uses the cutForce variable) to
“throw” the objects apart after being cut.
Step 5: Add SetupSliceComponent reference to Slice Function
Add a reference to the “SetupSlicedComponent” function in the Slice functions “if”
statement to evoke the “breaking apart” action.

Step 5: Create Empty GameObjects to Use for Start and End


Slice Points
In this step we will create empty GameObjects in our “slicer” prefab and position them
at the base and top of the “blade” portion of the prefab. These will serve to create the
plane that is necessary for slicing. In addition we will add the VelocityEstimator to the
End Slice Point empty GameObject as a means of tracking velocity.
In your “slicer prefab (a “Sword” prefab, in my case)
1.​ Create an Empty GameObject in the Hierarchy of the “slicer” object and name it
“Start Slice Point”.

2.​ Make another and call it “End Slice Point”.

3.​ Move the “Start Slice Point” Empty GameObject to the base of the “blade”
section of the prefab.

4.​ Move the “End Slice Point” Empty GameObject to the tip of the “blade” section
of the prefab.

5.​ Click on the “End Slice Point” Empty Game Object and add the
VelocityEstimator script to it as a component. After added check the box for
“Estimate On Awake”
Step 6: Configure Slicer Mechanics in Unity Editor Inspector
In this step, we will configure the Slicer Mechanics made available to the Unity Editor
through our public variables. Additionally, we will make a new layer that masks the slicer
interaction and assign it to our “slicer” prefab SliceObject script component.

In your “slicer” prefab GameObject


Click on your Slicer GameObject. Locate the SliceObject script component in the
Inspector. Configure the following:

1.​ In the “Start Slice Point” field, add the “Start Slice Point” Empty GameObject
you’ve just created.

2.​ In the “End Slice Point” field, add the “End Slice Point” Empty GameObject
you’ve just created.

3.​ In the “Velocity Estimator” field, add the “End Slice Point” Empty
GameObject (that the VelocityEstimator is attached to.)
Step 7: Create and Add a New Layer Called “Sliceable” to
GameObjects you want to be able to slice
In this step we will create a new Layer called “Sliceable” and add this layer to all the
GameObjects you wish to be able to slice. This compartmentalizes slicer interactions
from other types of interactions using Unity’s Layer Mask feature.

1.​ In Edit / Project Settings / Tags and Layers add a new layer called “Sliceable”

2.​ Pick a slot (I used slot 6 here) and type in “Sliceable”

3.​ Select your Slicer GameObject. In the Slice GameObject Script component
add the “Sliceable” layer to the Sliceable Layer dropdown.
Step 8: Add all GameObjects for Slicing to the “Sliceable” Layer
In this step, we will add all of the GameObjects we want to be sliceable to the
“Sliceable” layer we’ve just created.

●​ Select the GameObject(s) you want to be sliceable.


●​ In the upper right corner of the inspector click on the Layer dropdown
●​ Select “Sliceable” to make them be seen by the SliceObject script

Step 9: Add “Sliceable” Layer Mask to Physics Layer Mask list in


Left and Right controllers / Near-Far Interactor to Preserve Grab
Interactions
This will add the “Sliceable” layer to the Physics Layer Mask list. After adding it should
read “Default, Sliceable” after the “Sliceable” layer is also selected. This will preserve
your grab interactions and allow them to function as normal.
●​ In XR Origin (XR Rig) / Main Camera / Left and Right Controllers / Near-Far
Interactor / Sphere Interaction Caster and Curve Interaction Caster
components click on the Physics Layer Mask dropdown and select “Sliceable”
so that you can still grab the “Sliceable” GameObjects before they are sliced
Additional Considerations / Modifications

Slicing A GameObject Over and Over


If you want to slice a GameObject over and over again, you’ll need to pass the layer of
the original “Sliceable” GameObject onto the “Upper_Hull” and “Lower_Hull”. This can
be accomplished in a couple of different few ways:

●​ Add the following to the “upperHull” and “lowerHull” logic in the Slice function’s
“if” statement:

upperHull.layer = target.layer;
lowerHull.layer = target.layer;

And add this:

●​ Add the following to the “SetupSlicedComponent” function:

Int inheritLayer = LayerMask.NameToLayer(“Sliceable”)


slicedObject.layer = inheritLayer;
Making Each Sliced GameObject Piece Grabbable
It is possible to grab a piece of a sliced GameObject. In order to do so, you must do the
following in the script:

Add the following to the “SetupSlicedComponent” function, under


“collider.convex = true”:

●​ XRGrabInteraction grabbable =
slicedObject.AddComponent<XRGrabInteractable>()

Updating the Dynamic Attach on Sliced GameObject Pieces


However, in its present state, since you are inheriting from the original, the Grab
Dynamic Attach gets thrown out when a new GameObject is produced, and replaced
with a default attach that mirrors the GameObject before it was sliced. This results in
weird Grab behavior, where the new GameObjects appear to be offset from the grab
point. In order to fix this we need to make the following changes:

In the Slice() Function:

In the Slice() Function “If” statement add a new local variable called originalGrab to
preserve the dynamic attach:

●​ var originalGrab = target.GetComponent<XRGrabInteractable>()

…and replace the SetupSlicedComponent(lowerHull) and (upperHull) lines with a


reference to that new variable:

●​ SetupSlicedComponent(lowerHull, originalGrab)
●​ SetupSlicedComponent(upperHull, originalGrab)

In the SetupSlicedComponent() Function itself:

In the parameters list, we will add a new XRGrabInteractable parameter, like this:

●​ public void SetupSlicedComponent(GameObject slicedObject,


XRGrabInteractable originalGrab)

Add an “if” statement that reestablishes the Grab with Dynamic Attach properties:
If (originalGrab != null)
{
​ grabbable.interactionLayers = originalGrab.interactionLayers;
grabbable.movementType = originalGrab.movementType;
grabbable.throwOnDetach = originalGrab.thowOnDetach;
grabbable.useDynamicAttach = originalGrab.useDynamicAttach;

If (originalGrab.atttachTransform != null && !originalGrab.useDynamicAttach)


{
​ var newAttach = new GameObject(“AttachTransform”).transform;
​ newAttach.SetParent(slicedObject.transform, false);

​ newAttach.localPosition = originalGrab.attachTransform.localPosition;
​ newAttach.localRotation = originalGrab.attachTransform.localRotation;
​ grabbable.attachTransform = newAttach;
}
else
{
​ grabbable.attachTransform = null;
​ grabbable.useDynamicAttach = true;
}
}
else
{​
grabbable.useDynamicAttach = true:
grabbable.attachTransform = null;
}
Final Script

You might also like