VR Factory Training Simulation

Submitted by zach on Sat, 05/01/2021 - 03:03
VR Factory Training Thumbnail

For my senior capstone I worked with a small group of people to develop an interactive factory training simulation with Unity. In large scale production factories it can often be costly to train new workers and can use up valuable factory resources. With the advent of Virtual Reality technology, it is now possible to create virtual scenarios that can allow new workers to learn their job before ever stepping foot on the factory floor. The goal of this project was to create a VR simulation to teach workers how to work in one specific production unit for manufactured housing. The goal of this project was to create a simulation that would teach new workers with simple instructions and guidelines, but also be easily adaptable to other production units in the factory.

For this project I was both a mentor to the rest of the group, and a developer for part of the project. During the early stages of this project I helped my group members get up to speed and familiar with using Unity. None of my group members had any experience with 3D simulations or engines such as Unity, so getting them comfortable with the engine was our leading priority at the beginning of the project.

I was also responsible for designing and implementing some of the major interactive systems. I developed our first VR interactive demo early on with allowed the user to pick up and fire a nail gun using VR controls. The work I did for this demo became the basis for our later interactions in the full simulation. I was also responsible for designing and implementing the station prefab that users interact with to simulate building a section of a manufactured home. This prefab was built up from smaller modular prefabs with scripted interactions which allow for easy modification of the station and process. Building the prefab in a modular way meant that the implementation was very straight forward and interactions and states are predictable. To further provide a modular framework, many of the components of this station use the same script which means granular components can be swapped out without impacting the rest of the system and changes are localized to only the groups that contain the changed component. 

 

Since this project is intended as a product for a customer I cannot make the full source code available, however below is an example of what the code for an individual component would look like.


 
	//The tag that an item must have in order to be used
	//to "build" this particular component.
    public string BuildItemTag;


	//The current state of this component.
	//Posible values:
	//				Hidden: The component cant be built yet because a prerequisite is not yet built
	//				Ghost: The component can be built and a ghost image of the part is shown to help the user
	//				Placed: The component has already been built and is now a solid object
    public FloorStationHelper.SubpartState CurrentState = FloorStationHelper.SubpartState.Hidden;
    

	//This allows the materials to be set in editor for the two visible states.
    public Material SolidMaterial;
    public Material GhostMaterial;

	//This allows the part to be activated from Start() regardless of any outstanding prerequisites
    public bool EnableOnStart = false;



	//Private internal state to keep track of if this componenet has been completed
    private bool IsEnabled = false;

   

    // Start is called before the first frame update
    void Start()
    {
		//This allows the starting state to be set from the editor.
		//For instance the designer may always want this piece to be solid for some reason.
        this.SetState(this.CurrentState);

        if (this.EnableOnStart)
        {
            this.Enable();
        }
    }

    // Update is called once per frame
    void Update()
    {
		//Everything is event driven so there is no update method for a component.
    }

	//Enables the component to be built through OnTriggerEnter interactions.
	//Sets the current state to Ghost if it is currently Hidden.
    public void Enable()
    {
        this.IsEnabled = true;
        if (this.CurrentState == FloorStationHelper.SubpartState.Hidden)
        {
            this.SetState(FloorStationHelper.SubpartState.Ghost);
        }
    }

	//Disables the component so it cannot be built through OnTriggerEnter interactions.
	//If the component is not in the Placed state, it returns to the Hidden state.
    public void Disable()
    {
        this.IsEnabled = false;
        if (this.CurrentState != FloorStationHelper.SubpartState.Placed)
        {
            this.SetState(FloorStationHelper.SubpartState.Hidden);
        }
    }


	//The method that handles trigger volume collision.
	//This method drives the component build cycle.
    public void OnTriggerEnter(Collider other)
    {
        if (this.IsEnabled)
        {
            if (other.gameObject.tag == BuildItemTag && this.CurrentState != FloorStationHelper.SubpartState.Placed)
            {
				//If the colliding object has the right tag and this componenet is not already placed, then change
				//the state to placed, and consume the colliding object so it appears as if the object has actually
				//been placed.
				this.SetState(FloorStationHelper.SubpartState.Placed);
                Destroy(other.gameObject);
            }
        }
    }

	//A helper method to easily change the state of the component.
    public void SetState(FloorStationHelper.SubpartState newState)
    {
        this.CurrentState = newState;

        switch (newState)
        {
            case FloorStationHelper.SubpartState.Hidden:
                this.MakeHidden();
                break;
            case FloorStationHelper.SubpartState.Ghost:
                this.MakeGhost();
                break;
            case FloorStationHelper.SubpartState.Placed:
                this.MakeSolid();
                break;
        }
    }

  
	//Hides the mesh of the object and disables collision.
    private void MakeHidden()
    {
        this.gameObject.SetActive(false);
        this.gameObject.GetComponent<BoxCollider>().enabled = false;
    }

	//Shows the mesh with the GhostMaterial but disables collision.
    private void MakeGhost()
    {
        this.gameObject.SetActive(true);
        this.gameObject.GetComponent<MeshRenderer>().material = GhostMaterial;
        this.gameObject.GetComponent<BoxCollider>().enabled = false;
    }

	//Shows the mesh with the SolidMaterial and enables collision.
    private void MakeSolid()
    {
        this.gameObject.SetActive(true);
        this.gameObject.GetComponent<MeshRenderer>().material = SolidMaterial;
        this.gameObject.GetComponent<BoxCollider>().enabled = true;
    }


	//Returns true if the component has been fully placed, false otherwise.
    public bool GetCompleted()
    {
        if (this.CurrentState == FloorStationHelper.SubpartState.Placed)
        {
            return true;
        }
        return false;
    }

	//Sets the component to the placed state without using trigger volume interaction.
	//This is used to allow larger modules to auto-complete once a certain percentage
	//of the module is complete.
    public void MakeComplete()
    {
        this.SetState(FloorStationHelper.SubpartState.Placed);
        this.Disable();
    }