Creating in-game programmed components in Unity

In a previous post, I discussed the setup of a robotic factory game using colliders and triggers to manage interaction events.  Extending the concept, I am including simple programming as a game element to implement the player tasks similar to the game Human Resource Machine or Shenzhen I/O.

Here’s a video capture of the re-programmable sample:

The initial setup is to define the supported commands , in this example I start with simple package movement

“input” – retrieve a package
“output” – send out a package
“put” – place the package in a temporary position
“get” – get the package from the temporary position

Program-1-Program

The commands are for data movement, it will eventually need conditionals, operators and branching. In the meantime, I’ll setup the scene to handle the interactions.

Program-2-Scene

The scene is setup with colliders used as triggers to manage the interaction between the player controlled object. The Trigger event invokes an implementation of:

interface IAction
{
    void Action(Transform objTransform, string cmd);
}

This allows a Transform and additional string that contains a command payload — the event is coordinated between the player object and the interaction area.   The player object follows the program to move data around the scene and the interaction objects performs the animations and command functions.

In this example, I defined the player “program” during coding and stored them in a List using the Unity inspector, commands are then processed line per line in the Unity script and progresses through multiple states until completion.

enum State { Ready, WaitingForPath, Navigating, Rotate, Rotating, Rotated, Processing, Complete }

When the player operator is in the “Ready” state, it retrieves the next command, parses and executes the action then transitions to the next state, usually moving to the interaction point. The player then goes through the various states such as rotating to the target direction, invoking the action handler and receive the completion callback.

void Update()
{
	if (isStopped)
	{
		return;
	}

	switch (CurrentState)
	{
		case State.Ready:
			CurrentState = State.WaitingForPath;
			CurrentCommand = Program[CurrentCommandIndex];
			ExecuteCommand(CurrentCommand);
			break;
		case State.WaitingForPath:
			if (navMeshAgent.hasPath)
			{
				CurrentState = State.Navigating;
			}
			break;
		case State.Navigating:
			if (navMeshAgent.pathStatus == NavMeshPathStatus.PathComplete &&
				navMeshAgent.remainingDistance < DistanceDelta)
			{
				CurrentState = State.Rotate;
			}
			break;
		case State.Rotate:
			Vector3 relativePos = navDestination.parent.position - transform.position;
			Quaternion rotation = Quaternion.LookRotation(relativePos);
			CurrentState = State.Rotating;
			StartCoroutine(RotateCoroutine(rotation));
			break;
		case State.Rotating:
			break;
		case State.Rotated:            
			CurrentState = State.Processing;
			if (otherAction != null)
			{
				otherAction.Action(CargoTransform, cmdString);
			}
			break;
		case State.Complete:
			CurrentCommandIndex = (CurrentCommandIndex + 1) % Program.Count;
			CurrentState = State.Ready;
			break;
	}        
}

Following is the script for the “input” interaction. The Action() implementation receives the package location Transform that will later become the GameObject parent and null “cmd” value since there is only one interaction type.

public void Action(Transform objTransform, string cmd)
{
    action = objTransform.GetComponentInParent();
    StartCoroutine(ProcessCoroutine(objTransform));
}

The coroutine is handles the animation and transfer the instantiated prefab object to the player object.

IEnumerator ProcessCoroutine(Transform obj)
{
	var actionObj = Instantiate(ObjectPrefab, transform);
	actionObj.transform.localPosition = Vector3.up * 0.5f;

	Vector3 destination = actionObj.transform.position + Vector3.up * 0.5f;
	while ((actionObj.transform.position - destination).magnitude > float.Epsilon)
	{
		actionObj.transform.position = Vector3.MoveTowards(
			actionObj.transform.position,
			destination,
			MoveRate * Time.deltaTime);
		yield return null;
	}

	actionObj.transform.SetParent(obj);
	actionObj.transform.localPosition = Vector3.zero;

	// Update player action/operation is completed
	action.Action(null, null);

	yield return null;
}

An interactive WebGL demo can be viewed at:

http://orbitalfoundry.com/WebGL/Program1/index.html

The camera view can be controlled using Keyboard Arrow Keys.

Program-3-Demo

For questions, comments or contact – follow/message me on Twitter @rlozada