unity2D C# - player must press two switches to open door - c#

Essentially I have a door trigger event where if the player presses the switch then the door open. I wish however to do it so that if the door associated needs two switches to open well... then it only opens if two switches are pressed. Here is my code for my InteractSwitch
public class InteractSwitch : MonoBehaviour
{
Animator animator;
public DoorEventTrigger[] doorTriggers;
public bool pressed;
private bool down;
// Use this for initialization
void Start ()
{
animator = GetComponent <Animator> ();
}
// Update is called once per frame
void Update ()
{
}
void OnCollisionEnter2D (Collision2D target)
{
if (target.gameObject.tag == "Player") {
down = true;
animator.SetInteger ("SwitchTrig", 1);
foreach (DoorEventTrigger trigger in doorTriggers)
if (trigger != null)
trigger.Toggle (true);
}
}
When triggered this event checks for player, shows switch has been pressed then sends a bool to a function called Toggle which handles the operation of the door.
Next I have my DoorEventTrigger event which checks if the bool sent = true. If it is then the door will open. Here is where I am stuck. As seen in the code I have created a array of InteractSwitch which stores the amount of switches I want the player to have pressed before the door will open. I then state that if the length > 1 then this if condition is true and some code will be added here which will then open the door only if the player has selected the InteractSwitch[].length switches. My question is how would I check that all instances of InteractSwitch for this DoorEventTrigger equals true?
Here is my DoorEventTrigger code
public void Toggle (bool val)
{
if (switchTriggers.Length > 1) {
Debug.Log ("HAS THIS ACTUALLY DONE ANYTHING");
door.Open ();
}
else
{
if (val)
door.Open ();
else
door.Close ();
}
}
}

The solution I came up with is nowhere near I believe the most efficient answer as it requires code each time u add a new switch but this is what I have
firstly in the InteractSwitch script the bool down is public so it can be accessed by the switchtriggers
public bool down;
Next is the added code to the Toggle() method
public void Toggle (bool val)
{
if (switchTriggers.Length > 1) {
if((switchTriggers[0].down == true) && (switchTriggers[1].down == true)){
Debug.Log ("THIS ACTUALLY DONE SOMETHING!");
door.Open ();
}
}
else
{
if (val)
door.Open ();
else
door.Close ();
}
}
Now from the public SwitchInteract[]; I know the exact amount of elements in the array however this could be checked through switchTriggers.length; Knowing there is two elements in the array my condition switchTriggers.Length > 1 is correct so next I take the two elements in the array and check if they are down; if BOTH are down then the condition continues and the Debug.Log() validates this. Finally Open() is called which opens the door.

You can have the switches increment or decrement a required switch variable in the door object.
Give the door object a requiredSwitch variable.
Give each switch a door variable and set it to the door it controls.
When a switch is activated, have it decrement its door's requiredSwitch variable.
When the door's requiredSwitch variable is 0, then the door opens.
You can also have the switches increment the variable if they get deactivated.
This solution allows you to add switches by just setting the door variable of the switch and incrementing the door's initial requiredSwitch variable.

Related

Animation not playing when pressing E | unity 3d

I am working on this 3d game were a player has to press E to interact with objects.
the player has a collider that when touching a type of trigger that has this type of code, makes an object pop up showing that the player's is "selecting" something.
when the player is in the trigger I made it were when they press E, the objects animation plays. When adding the objecting selecting thing it made it now that I cant make the animation play when pressing E
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Fixedpress : MonoBehaviour
{
public Animator Tributton;
public GameObject GM;
private Coroutine routine;
private void Start()
{
GM.SetActive(false);
}
private void OnTriggerStay(Collider other)
{
// in general rather use CompareTag instead of ==
// it is slightly faster and also shows an error if the tag doesn't exist instead of failing silent
if (!other.CompareTag("LookTrig")) {
GM.SetActive(true);
return;
}
// just in case to prevent concurrent routines
if (routine != null) StopCoroutine(routine);
// start a new Coroutine
routine = StartCoroutine(WaitForKeyPress());
}
private IEnumerator WaitForKeyPress()
{
// check each FRAME if the key goes down
// This is way more reliable as OnTriggerStay which is called
// in the physics loop and might skip some frames
// This also prevents from holding E while entering the trigger, it needs to go newly down
yield return new WaitUntil(() => Input.GetKeyDown(KeyCode.E));
// set the trigger once and finish the routine
// There is no way to trigger twice except exit the trigger and enter again now
Tributton.SetTrigger("Fiveyon");
Debug.Log("Fiveyon!");
// If you even want to prevent this from getting triggered ever again simply add
enabled = false;
// Now this can only be triggered ONCE for the entire lifecycle of this component
// (except you enable it from the outside again of course)
}
void OnTriggerExit(Collider other)
{
if (!other.CompareTag("LookTrig")) {
GM.SetActive(false);
return;
}
// when exiting the trigger stop the routine so later button press is not handled
if (routine != null)
{
StopCoroutine(routine);
GM.SetActive(false);
}
}
}
OnTrigger methods are called when another object with a Collider enters in your object Collider.
First, make sure everything was well set up well, and maybe you want to use OnCollision instead of OnTrigger methods, if you want to detect a collision whether than a "penetration" if i may say

Unity forcing physics engine to check collisions

I am creating a game in unity where the player can drag one object (a card) over another (an enemy). If they drag the card over the enemy I want to run some code, and if not I want to return the card to its initial position. I placed a collider on both objects but it isnt working the way it should. I am assuming this is because the object is getting moved back to its initial location before the physics engine sees the collision, but I don't know how to fix this. I am deactivating the collider on the card while moving it to avoid having it trigger collisions until the player has placed it using the onmousedown and onmouseup events. Any tips on how to fix this behavior? can I force the physics engine to check collisions with the onmouseup event?
I know the collisions are working because when I turn off the return to initial position behavior the game functions as expected.
How about useing the collider as trigger and do not use rigidbodys or anything.
Then if there is a trigger enter event set bool as true. If there trigger exit reset the bool to false.
Now if you "Release" the card check if bool is true or false.
true: Set the cards position to the player or what you want
false: Reset the cards position to the start
Now beeing a little mroe fancy you can set a lighted border around the card when bool is active (just check in update)
Example:
public class Card : MonoBehaviour {
private bool isHolding;
private bool isHovering;
public Vector3 startPos;
public void Start() {
startPos = transform.position;
}
public void Update() {
// Code where you check if the card is Clicked and Moved by the player
// If so set isHolding = true
// dont enter the check if holding blablabla when animation stuff is happening
if (doAnimationStuff) {
// do animation
// Destroy Object
return;
}
// Code to check if isHolding == false
if (!isHolding) {
if (!isHovering) {
transform.position = startPos;
} else {
doAnimationStuff = true;
}
}
}
private void OnTriggerEnter(Collider other) {
// Check if other is a player
// if so set isHovering = true
}
private void OnTriggerExit(Collider other) {
// Check if other is a player
// if so set isHovering = false
}
}

StartCoroutine is being called so many times (C# Unity)

I'm creating a Pop up menu Option in Unity. Now my Problem here is that the coroutine i made in void update is being called so many times. What i mean by that is on my Unity Console the Debug.Logs are incrementing . It should not right because its already coroutine. Could some help me understand more coroutine and help me solve my little problem .
Here is my code:
[SerializeField]
GameObject Option;
[SerializeField]
Button btn,btn2;
[SerializeField]
GameObject open, close;
[SerializeField]
GameObject[] opt;
bool startFinding = false;
void Start()
{
Option.SetActive(false);
Button popUp = btn.GetComponent<Button>();
Button popUp2 = btn2.GetComponent<Button>();
popUp.onClick.AddListener(PopUpOption);
popUp2.onClick.AddListener(ClosePopUp);
}
void Update()
{
if (startFinding)
{
StartCoroutine(GameOptions());
}
}
IEnumerator GameOptions()
{
//Get All the tags
opt = GameObject.FindGameObjectsWithTag("MobileOptions");
if (opt[0].GetComponent<Toggle>().isOn == true && opt[1].GetComponent<Toggle>().isOn == true)
{
Debug.Log("Disable first the check box then choose only 1 option between" + "'rendering'"+ "and" + "'livestreaming'");
}
//Livestreaming
if (opt[0].GetComponent<Toggle>().isOn == true)
{
Debug.Log("Livestreaming Activate");
} else
{
Debug.Log("Livestreaming Deactivate");
}
//Rendering
if (opt[1].GetComponent<Toggle>().isOn == true)
{
Debug.Log("Rendering Activate");
} else
{
Debug.Log("Rendering Deactivate");
}
//Fog
if (opt[2].GetComponent<Toggle>().isOn == true)
{
Debug.Log("Fog Activated");
} else
{
Debug.Log("Fog Deactivated");
}
//Camera Effect
if (opt[3].GetComponent<Toggle>().isOn == true)
{
Debug.Log("Camera Effect Activated");
} else {
Debug.Log("Camera Effect Deactivated");
}
yield return null;
}
void PopUpOption()
{
startFinding = true;
//Disable The Mobile Option Button
open.SetActive(false);
//Enable the Close Option Button
close.SetActive(true);
//activate the Mobile Options
Option.SetActive(true);
}
void ClosePopUp()
{
startFinding = false;
//eanble the mobile option button
open.SetActive(true);
//disable the close option button
close.SetActive(false);
//deactivate the Mobile Option
Option.SetActive(false);
}
Here is how coroutines work:
Let's say I have a couroutine function called MyRoutine (in your case, you called it GameOptions)
private IEnumerator MyRoutine()
Then, anywhere in my code, calling
StartCoroutine(MyRoutine));
Is going to simply call MyRoutine like any usual method. So if you call it in update, it will be called all the time, as any method would. This is not what you want. What make coroutines special is that you can use the yield keyword in them. There are many ways to use it but the most used (and simple) one is to do yield return null
yield return null means "Stop this coroutine, but resume the execution on next frame". You don't need to call any other function (certainly not StartCoroutine). The execution will resume next frame.
To go back to what you posted in your question, you wrote yield return null at the end. So your method is executing, and just at the end, stops and resumes next frame, but since there is nothing left to do, it exits on the next frame.
A typical way to use coroutines is to have the yield return null in a while loop, so when it resumes, it continues the loop. Here is an example that do it
private IEnumerator MyRoutine()
{
while(running) //running is a member bool that you could set to false to exit
{
// Do all the stuff you want to do in ONE frame
// ...
yield return null;
}
}
Typically, the StartCoroutine would be called in the Start() function, or later when an event is triggered.
If you want to know more about coroutine, or check that you understood them properly, check out this page: https://docs.unity3d.com/Manual/Coroutines.html
or this video https://unity3d.com/learn/tutorials/topics/scripting/coroutines
// Edit: quickly present one useful option
In the snippet above, the while loop is very similar to the Update function (the inside of the loop is executed each frame). One nice option is to replace
yield return null
by
yield return new WaitForSeconds(waitTime)
where waitTime is a the time you want to wait before resuming, in seconds
// End of edit
Do not use StartCoroutine() in the Update method. Call it in another method and use a while loop inside your coroutine function if needed. Just control your StartCoroutine() outside of Update method
Update is called every frame, if your condition is ever true, you launch your coroutine every frame.
Just set down your flag to only join 1 time.
void Update()
{
if (startFinding)
{
startFinding = false;
StartCoroutine(GameOptions());
}
}

Pause don't works (Unity c#)

I writing runner game on Unity (C#) for mobile phones.
I made Pause button on screen, using Canvas - Button.
Also I made code for Pause in Platformer2DUserControl script.
Here it is code of this script:
using UnityEngine;
using UnitySampleAssets.CrossPlatformInput;
namespace UnitySampleAssets._2D
{
[RequireComponent(typeof(PlatformerCharacter2D))]
public class Platformer2DUserControl : MonoBehaviour
{
private PlatformerCharacter2D character;
private bool jump;
public bool paused;
private void Awake()
{
character = GetComponent<PlatformerCharacter2D>();
paused = false;
}
private void Update()
{
/*if (Input.GetButton("Fire1"))
{
}*/
if (!jump)
// Read the jump input in Update so button presses aren't missed.
jump = Input.GetButton("Fire1"); //&& CrossPlatformInputManager.GetButtonDown("Jump");
}
private void FixedUpdate()
{
// Read the inputs.
bool crouch = Input.GetKey(KeyCode.LeftControl);
// float h = CrossPlatformInputManager.GetAxis("Horizontal");
// Pass all parameters to the character control script.
character.Move(1, false, jump);
jump = false;
}
public void Pause()
{
if (!jump)
// Read the jump input in Update so button presses aren't missed.
jump = Input.GetButton("Fire1"); //&& CrossPlatformInputManager.GetButtonDown("Jump");
paused = !paused;
if (paused)
{
jump = !jump;
Time.timeScale = 0;
}
else if (!paused)
{
// jump = Input.GetButton("Fire1");
Time.timeScale = 1;
}
}
}
}
My Pause Button is WORKS well. But when I tap it , my character is jumping and game is pausing.
I want to make that , when I tapping the button game just pausing and character don't jump.
How I can make it. Thank's for help.
I would advise you not to use the same input for jumping and pausing. Also, separate your jump and pause functionalities into separate functions. For pausing, create a UI button on the screen and make it call a public function on a Pause script, that will toggle pause. Then, in the same function, check if you are paused or not and adjust Time.timescale accordingly
You will have to attach the script with pause functionality on to an object that will always be in a screen (Say, a panel in your canvas or your MainCamera). Under the button, add a new onClick() function after dragging the GO with the apt script to the box. Then, select the public function aforementioned.
private bool paused = false;
//The function called by the button OnClick()
public void TogglePause()
{
paused = !paused;
if(paused)
Time.timescale = 0f;
else
Time.timescale = 1f;
}
Hope this helped!
Well, your code is messed up, but just remove the jump script from the pause method. So it will just pause...
public void Pause()
{
paused = !paused;
if (paused)
{
Time.timeScale = 0;
}
else
{
Time.timeScale = 1;
}
}
Note that Input.GetButton should only be called in Update
EDIT
The problem that you have to tap your screen to press the button. And the code jump = Input.GetButton("Fire1"); basically means "Am I tapping the screen?" So both JUMP and PAUSE are triggered.
One solution would be to put a canvas filling the whole screen under your buttons. You will trigger a JUMP action only when this canvas is clicked. So when you click your pause button, it will stop the propagation and won't click the full screen canvas, which won't trigger the jump action.
EDIT2
Try changing the lines (in the Update function) :
if (!jump)
// Read the jump input in Update so button presses aren't missed.
jump = Input.GetButton("Fire1");
for :
if (!jump && !paused)
// Read the jump input in Update so button presses aren't missed.
jump = Input.GetButton("Fire1");
I think the button event is called before the Update, see reference
One solution would be possible make sure the raycast on of pause button.
I have always done it like this, essentially surround the entire fixed update (or wherever you have your game motion) with the paused-bool
private void FixedUpdate()
{
if (paused != true){
// Read the inputs.
bool crouch = Input.GetKey(KeyCode.LeftControl);
// float h = CrossPlatformInputManager.GetAxis("Horizontal");
// Pass all parameters to the character control script.
character.Move(1, false, jump);
jump = false;
}
}

Return Gameobject to original position

I am using Unity for my project. What I am trying to do is when a button is pressed it moves a game object to a new position. Then when the same button is press again, the object returns to its original position.
My problem is with the while loop I am using; it just crashes Unity when I press this button. Am I using the while loop wrong or is there a better way of going about this?
Here is what I have so far:
public Gameobject TroubleMove;
public Gameobject TroubleAnchor;
public Gameobject TroubleMain;
public Gameobject CauseMain;
public Gameobject Cause;
public int OnOff = 1;
public void Change ()
{
switch (OnOff)
{
Case 1:
CauseMain.setActive (true);
Cause.setActive (true);
While (CauseMain == true)
{
CauseMove.transform.position = CauseAnchor.tranform.position;
}
OnOff += 1;
break;
Case 2:
CauseMain.setActive (false);
Cause.setActive (false);
OnOff _=1;
break;
}
}
I assume CauseMain is never being set to false and your while loop never exits.
Why do you need the while loop? Don't you want to change the cursor once? I would change while to if()
I will assume that the Change() is called in the OnClick() method.
Firsly, you should not use a while(true) loop when waiting for a user input or response. The while(true) loop will become a never ending loop and crash Unity / Device.
I am confuse with what your GameObjects really are but instead of while(true) loop, you should be using the Update() or FixedUpdate() method to transform the position (can be in relation to Time.DeltaTime). If I am not mistaken, this would be the skeleton of what could be done:
void Update () {
if(CauseMain) {
//TODO: Apply Transformation Changes
CauseMove.transform.position = CauseAnchor.tranform.position;
CauseMain = false;
}
}
This code snippet will only apply the transformation change whenever the player presses a button and once the position is set to "CauseMove", the check is disabled preventing an endless loop which will crash Unity.

Categories