I have looked for this and tried this several times on and off for the last several months but never got anywhere, and I thought it was time to ask about this.
Does anyone know how to create 3D puzzles and buttons in the game world?
I am developing a 3D first-person puzzle demo for my portfolio and need to know exactly how this is achieved and if possible, have a working version.
The puzzles I am talking about are the kinds of things in Skyrim, and a good example of this in Unity 5 (and more like what I am looking for) is Quern, which is a 3D puzzle game on Kickstarter.
https://www.kickstarter.com/projects/1255217702/quern-undying-thoughts
The puzzle system in this would be absolutely perfect for what I am looking to do.
Well, it’s just a matter of programming. 
Seriously though, these kinds of puzzles are all different. You need to be more specific about what you want to accomplish. Does the player need to push some sort of levers or buttons in a certain order? What exactly should happen when he gets it right, or gets it wrong? Does he need to rearrange objects to sort them in some way?
There can (and should) be endless variety to these puzzles, but a lot of them boil down to a state machine. Assuming you’re an artist, the easiest way to set it up might be using the Animator. You’ll still need a bit of script to tie player controls (whatever “manipulate” or “grab” or “push” input the player has) to the objects, so that when they do, it activates some trigger parameter in your animator.
Then you can respond to that trigger both by playing an appropriate animation, and by moving to a different state in the machine. The puzzle for the player is to find the right sequence of triggers to move the state machine to the “success” state.
Essentially what I am looking for at the moment is to have a series of buttons, and for the player to have to click these in a specific order for an animation to play and things to become available.
I am primarily a 3D artist, but know quite a lot of C# and what I don’t know I can search for.
What I have so far is below, essentially my current setup will have the PuzzleSwitch script on every button, and a PuzzleController script on the parent, I need to have an event for once the button is clicked and for that event to be sent to the PuzzleController and to be able to tell what button was clicked (telling what button has been clicked is what I am having issues with).
PuzzleController.cs
using UnityEngine;
using System.Collections;
[System.Serializable]
public class puzzleswitch
{
public int switchValue;
public bool switchHover = false;
public bool switchClicked = false;
}
public class PuzzleController : MonoBehaviour {
public puzzleswitch[] puzzleSwitch;
private int puzzleSwitchAmount;
void Start()
{
puzzleSwitchAmount = puzzleSwitch.Length;
}
}
PuzzleSwitch.cs
using UnityEngine;
using System.Collections;
public class PuzzleSwitch : MonoBehaviour {
public int switchValue;
private PuzzleController puzzleController;
void Start()
{
puzzleController = (PuzzleController) FindObjectOfType(typeof(PuzzleController));
}
void OnMouseEnter()
{
}
}
Well, you already have a switchValue property. I’m assuming this is a number indicating which switch it is. So, in PuzzleController, simply make a public method that takes that the switch value:
public void SwitchFlipped(int which) {
...
}
Then, when the player does whatever they do to flip the switch, PuzzleSwitch merely needs to invoke that method and pass in its value:
puzzleController.SwitchFlipped(switchValue);
Though personally I would probably connect these through UnityEvents, but that’s just a personal preference… in this case I see no real disadvantage to directly invoking the SwitchFlipped method, as described here.
I now have most of the system working, I can type in a code I want to be used for the correct pattern, and use that to trigger an animation on an object, the only issue now is that the buttons don’t move properly.
PuzzleController
using UnityEngine;
using System.Collections.Generic;
[System.Serializable]
public class puzzleswitch
{
public bool switchClicked = false;
}
public class PuzzleController : MonoBehaviour
{
public GameObject triggerObject;
public string animationName;
public string pattern;
public bool inCollider;
public Vector3 buttonMovement;
public bool codeOk = false;
public puzzleswitch[] puzzleSwitch;
private int puzzleSwitchAmount;
private List<string> patternMatch = new List<string>();
private bool tempOk = true;
void Start()
{
puzzleSwitchAmount = puzzleSwitch.Length;
}
void OnTriggerEnter(Collider theCollider)
{
if (theCollider.tag == "Player")
{
inCollider = true;
}
}
void OnTriggerExit(Collider theCollider)
{
if (theCollider.tag == "Player")
{
inCollider = false;
}
}
public void checkCode()
{
string[] paternList = pattern.Split(' ');
for (int i = 0; i < puzzleSwitch.Length; i++)
{
if (puzzleSwitch[i].switchClicked)
{
patternMatch.Add(i.ToString());
for (int j = 0; j < patternMatch.Count; j++)
{
if (j <= paternList.Length)
{
if (paternList[j] != patternMatch[j])
{
patternMatch = new List<string>();
tempOk = false;
}
}
}
puzzleSwitch[i].switchClicked = false;
if (tempOk)
{
Debug.Log("before");
if (patternMatch.Count == paternList.Length)
{
Debug.Log("after");
codeOk = true;
isCorrect();
patternMatch = new List<string>();
}
}
}
}
if (patternMatch.Count >= paternList.Length || tempOk == false)
{
patternMatch = new List<string>();
tempOk = true;
}
}
void isCorrect()
{
triggerObject.GetComponent<Animation>().Play(animationName);
}
}
PuzzleSwitch
using UnityEngine;
public class PuzzleSwitch : MonoBehaviour
{
public int switchValue;
private Vector3 currentPlacement;
public Vector3 buttonDown;
private Vector3 downPos;
private PuzzleController puzzleController;
private bool updateNow = false;
void Start()
{
currentPlacement = this.transform.localPosition;
}
void Update()
{
puzzleController = (PuzzleController)FindObjectOfType(typeof(PuzzleController));
if (updateNow)
{
this.gameObject.transform.localPosition = Vector3.MoveTowards(currentPlacement, downPos, Time.deltaTime);
updateNow = false;
}
}
void OnMouseOver()
{
if (Input.GetKeyDown(KeyCode.Mouse0))
{
if (puzzleController.inCollider == true && !puzzleController.codeOk)
{
puzzleController.puzzleSwitch[switchValue].switchClicked = true;
updateNow = true;
ButtonDown();
ButtonUp();
}
}
}
void ButtonDown()
{
puzzleController.checkCode();
// downPos = currentPlacement + buttonDown;
//this.transform.localPosition = Vector3.Lerp(currentPlacement, downPos, i);
}
void ButtonUp()
{
// this.gameObject.transform.localPosition = Vector3.MoveTowards(downPos, currentPlacement,Time.deltaTime);
}
}
That Update function seems pretty superfluous. You’re trying to interpolate the position but You’re resetting the flag each time it is set, so it will probably never reach your “downPos”. Why not just set the position directly?
I was looking for a smooth movement for this, like a Lerp or MoveTowards but it never worked properly (not sure why).
Its not that it isnt reaching it, it is taking a completely different approach, if I set the Vector 3 to 0, 0, 0.2, it will move down and to the right, no matter what I put in there it always jumps to the lower right.
In your example code you’re not setting downPos to anything. You know that right?
Try a simple test of:
transform.localPosition +=Vector3.Down;
make sure that only runs once though so it doesn’t keep going down.