I'm making my very first game in unity. In this game my player is a ball which touches some question marks. When a question mark is triggered it has to display a question and the answers. Till here every thing is fine. Now i need to press a or b according to the question. If the answer is right it will add points. But here is the problem. Unity doesn't wait for me to press the keys. Unity passes through the code before i press the buttons and the game crashes.
void OnTriggerEnter(Collider collider)
{
if (collider.gameObject.CompareTag("QuestionCube1"))
{
Question.text = "Which Number is bigger?";
Answer.text = "A.5 B.10";
if (Input.GetKeyDown(KeyCode.A))
{
gameController.minusQuestionScore();
}
else if (Input.GeyKeyDown(KeyCode.B))
{
gameController.addQuestionScore();
}
Question.text = "";
Answer.text = "";
}
}
//Sorry if the code is kinda all over the place I dont know how to pass the code here exactly. The gameController and the UI texts are declared and working
Okay let's shift away from the comment section and let's try to improvise, on my current understandings of your problem.
First thing you must consider is the following.
void OnTriggerEnter(Collider collider)
Triggers ONLY once, when the other collider enters. The solution?
Use OnTriggerStay.
void OnTriggerStay(Collider collider)
This will always check for the input when the objects are colliding.
The next thing to consider is the reset of the text. As far as I understand you should remove it when they are not colliding anymore, so you could have additional method. OnTriggerExit, which will run additional code when they are not colliding anymore.
void OnTriggerExit(Collider collider)
{
if (collider.gameObject.CompareTag("QuestionCube1"))
{
Question.text = "";
Answer.text="";
}
}
So overall
void OnTriggerEnter(Collider collider)
{
if (collider.gameObject.CompareTag("QuestionCube1"))
{
Question.text = "Which Number is bigger?";
Answer.text = "A.5 B.10";
if(Input.GetKeyDown(KeyCode.A))
{
gameController.minusQuestionScore();
}
else if(Input.GeyKeyDown(KeyCode.B))
{
gameController.addQuestionScore();
}
}
}
You can use Coroutine to wait for the input after it is triggered.
void OnTriggerEnter(Collider collider)
{
if (collider.gameObject.CompareTag("QuestionCube1"))
{
Question.text = "Which Number is bigger?";
Answer.text = "A.5 B.10";
StartCoroutine(WaitForAnswer());
}
}
IEnumerator WaitForAnswer()
{
for (;;)
{
if (Input.GetKeyDown(KeyCode.A))
{
gameController.minusQuestionScore();
break;
}
else if (Input.GetKeyDown(KeyCode.B))
{
gameController.addQuestionScore();
break;
}
yield return null;
}
Question.text = "";
Answer.text = "";
yield return null;
}
Related
How to make the end of the game in pakman when all the dots have been eaten?
This is the end game code now
void OnTriggerEnter2D(Collider2D co)
{
if (co.name == "PacMan")
{
Destroy(co.gameObject);
EndMenu.SetActive(true);
GameObject.Find("EndGameConvas/EndGamePanel/Score").GetComponent<Text>().text = GameObject.Find("Canvas/Score").GetComponent<Text>().text;
Time.timeScale = 0;
}
}
This is the point eating code
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.name == "PacMan")
{
Destroy(gameObject);
GameObject.Find("Canvas/Score").GetComponent<Score>().ScoreChange(1);
}
}
If what you're asking is "how do I let the game know the level is over and trigger the end" then just have a variable to hold how many dots are in the level, and every time you eat one and that trigger collider fires, have a counter go up. When the counter equals the total, level ends.
In your class for the dots you could use something like
public class YourDotClass : MonoBehaviour
{
// Keeps track of all currently existing instances of this class
private readonly static HashSet<YourDotClass> _instances = new HashSet<YourDotClass>();
private void Awake ()
{
// Register yourself to the instances
_instances.Add(this);
}
private void OnDestroy ()
{
// Remove yourself from the instances
_instances.Remove(this);
// Check if any instance still exists
if(_instances.Count == 0)
{
// => The last dot was just destroyed!
EndMenu.SetActive(true);
GameObject.Find("EndGameConvas/EndGamePanel/Score").GetComponent<Text>().text = GameObject.Find("Canvas/Score").GetComponent<Text>().text;
Time.timeScale = 0;
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.name == "PacMan")
{
Destroy(gameObject);
GameObject.Find("Canvas/Score").GetComponent<Score>().ScoreChange(1);
}
}
}
However, you should really rethink/restructure your code and think about who shall be responsible for what.
Personally I would not like the dots be responsible for increasing scores and end the game .. I would rather have a component on the player itself, let it check the collision, increase its own points and eventually tell some manager to end the game as soon as all dots have been destroyed ;)
I think you could use something like this. Just store all of the pacdots in an array and once the array is empty you could end the game.
GameObject[] pacdots = GameObject.FindGameObjectsWithTag("pacdot");
void OnTriggerEnter2D(Collider2D collision)
{
// pacman collided with a dot
if (collision.name == "PacMan")
{
Destroy(gameObject);
GameObject.Find("Canvas/Score").GetComponent<Score>().ScoreChange(1);
if (pacdots.length == 0)
{
// All dots hit do something
}
}
}
When I pick an object up I can glitch it through the map making it fall out of the world. This happens when I pick it up and drop the object half way through the floor. The outcome I receive is not what I was expecting what can I do to fix this. Also yes the colliders and rigidbody's are setup correctly.
public GameObject PressEtoInteractText;
public bool pickup, inrange;
public Collider Playercol;
public Vector3 guide;
private GameObject temp;
private Rigidbody rb;
void Update()
{
if (Input.GetKeyDown(KeyCode.E) && inrange == true)
{
PressEtoInteractText.SetActive(false);
pickup = true;
}
if (Input.GetMouseButtonDown(0) && pickup == true)
{
pickup = false;
Playercol.isTrigger = true;
}
UpdatePickUpFollow();
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Interact")
{
PressEtoInteractText.SetActive(true);
temp = other.gameObject;
inrange = true;
}
if (other.gameObject.tag == "Interact" && temp.transform.position == guide)
{
return;
}
}
void OnTriggerExit(Collider other)
{
if (other.gameObject.name != "Interact")
{
PressEtoInteractText.SetActive(false);
inrange = false;
}
}
public void PickUp()
{
rb = temp.GetComponent<Rigidbody>();
rb.MovePosition(transform.position += guide);
Playercol.isTrigger = false;
}
public void UpdatePickUpFollow()
{
if (pickup == true)
{
PickUp();
}
}
If you deactivate the objects collider with tempCol.enabled = false; it will not register any collisions and you can just push or pull it through walls all day. Make sure to remove that from your script.
Also, using MoveTowards can cause issues with collision detection. In my experience it is best to use AddForce or MovePosition to move the Rigidbody component instead of modifying the Transform directly. Try Rigidbody.MovePosition(Vector3 position). Maybe this works better for you.
Here is a link to the documentation page. It's basically your exact use case:
Rigidbody.MoveTowards Unity Docs
(Hint: notice how they use FixedUpdate instead of regular Update. You should also always do this when working with Rigidbodies because it is synced with physics updates.)
//Edit:
This is a little implementation of your code in a cleaner and hopefully correct way. I have not tested this and there is propably something I missed. Use this as a reference to build your own working solution.
//set in inspector
public Transform guidePosition;
public GameObject pressEToInteractText;
private bool isObjectInRange;
private GameObject inRangeObject;
private bool isObjectPickedUp;
private Rigidbody pickedUpRigidBody;
/* check if button is pressed.
* If no object is in hand -> pick up;
* if object is in hand -> drop;
*/
private void FixedUpdate()
{
if (Input.GetKeyDown(KeyCode.E) && isObjectInRange && !isObjectPickedUp)
{
PickupObject();
}
if(Input.GetKeyDown(KeyCode.E) && isObjectPickedUp)
{
DropObject();
}
if (isObjectPickedUp)
{
PickedUpObjectFollow();
}
}
//save references
private void PickupObject()
{
pickedUpRigidBody = inRangeObject.GetComponent<Rigidbody>();
isObjectPickedUp = true;
}
//remove saved references
private void DropObject()
{
isObjectPickedUp = false;
pickedUpRigidBody = null;
}
//move position to guidePosition
private void PickedUpObjectFollow()
{
pickedUpRigidBody.MovePosition(guidePosition.position);
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Interact") && !isObjectPickedUp)
{
pressEToInteractText.SetActive(true);
isObjectInRange = true;
inRangeObject = other.gameObject;
}
}
private void OnTriggerExit(Collider other)
{
if(other.CompareTag("Interact") && other.gameObject == inRangeObject)
{
pressEToInteractText.SetActive(false);
isObjectInRange = false;
inRangeObject = null;
}
}
You could do multiple things. Unfortunately the way VR tracks the hands and there is no way to stop ur hands in real life your hand will defy the physics in the application.
I would either make the item drop from the users hand when it gets close enough to a certain area that you dont want it going through.
Or you can make it that if it does pass through a space you can report the item right above the floor it went through.
Ive looked long and hard for an answer to this and it doesnt seem like it can at this point be blocked from moving through if its currently grabbed.
I'm trying to get an input from the "space" button, but it looks like it doesn't take the input when I click. Here's my code:
void OnTriggerEnter2D (Collider2D other)
{
if (other.gameObject.tag == "GreenKey") {
print ("Green Key taken");
Destroy (other.gameObject);
greenKey_canvas.enabled = true;
} else if (other.gameObject.tag == "RedKey") {
print ("Red Key taken");
Destroy (other.gameObject);
redKey_canvas.enabled = true;
} else if (other.gameObject.tag == "YellowKey") {
print ("Yellow Key taken");
Destroy (other.gameObject);
yellowKey_canvas.enabled = true;
} else if (other.gameObject.tag == "Gem") {
print ("Gem taken");
gems = gems + 5;
gemsText.text = gems.ToString ();
Destroy (other.gameObject);
} else if (other.gameObject.tag == "RedGem") {
print ("Gem taken");
gems++;
gemsText.text = gems.ToString ();
Destroy (other.gameObject);
} else if (other.gameObject.tag == "LiveTaker") {
lives--;
} else if (other.gameObject.tag == "RedDoor") {
if (redKey_canvas.enabled) {
if (Input.GetKeyDown(KeyCode.Space)) {
//OPEN DOOR
other.gameObject.GetComponent<SpriteRenderer>().sprite = enterDoor;
redKey_canvas.enabled = false;
}
}
} else if (other.gameObject.tag=="YellowDoor") {
if (yellowKey_canvas.enabled) {
//OPEN DOOR
}
} else if (other.gameObject.tag=="GreenDoor") {
if (greenKey_canvas.enabled) {
//OPEN DOOR
}
}
}
In the console, I get the "entered door with key" message, but when I press Space, nothing happens.
I've also tried to add an Axis to the Input Manager named "DoorEnter", but it doesn't work either...
The reason this dosent work, is that the OnCollisionEnter function only activates the frame that the collision first happens, and that the GetKeyDown only lauches the single frame the button gets pushed.
One quite ugly fix, would be to Replace GetKeyDown with GetKey, then already hold space when walking into the door.
A better solution would be to add a variable, for an if statement in the Update loop. Make it activate when the user enters, and deactivate when the user leaves collision area.
Then make an if statement like this:
if(inCollider && Input.GetKeyDown(KeyCode.Space)){
//Open door here
}
Also, can anyone format this correctly? (Am on phone)
EDIT: Collision means trigger! Sorry!
I have two spawn spots where a player will show up upon connection.
I need that, when one of the players connects on one of the spots, any other player will always spawn at the other spot.
Here's some visual in case it helps: https://goo.gl/Y0ohZC
Thanks in advance,
IC
You can add those two possible spawn spots as empty game objects.
Then I'd make a boolean array and set its states to true or false depending on if the spot is occupied. The spots aren't directly stored in this array so you should make another array.
In C# this would approximately look like this:
public GameObject[] Spots = new GameObject[2]; //Drag the spots in here (In the editor)
bool[] OccupiedSpawnSpots = new bool[2];
int mySpotNumber = -1;
void Start() {
PhotonNetwork.ConnectUsingSettings ("v1.0.0");
}
void OnGUI() {
GUILayout.Label (PhotonNetwork.connectionStateDetailed.ToString ());
}
void OnJoinedLobby()
{
Debug.Log ("Joined the lobby");
PhotonNetwork.JoinRandomRoom ();
}
void OnPhotonRandomJoinFailed()
{
Debug.Log ("No room found. Creating a new one...");
PhotonNetwork.CreateRoom (null);
}
void OnPhotonPlayerConnected(PhotonPlayer player)
{
//If we are the MasterClient, send the list to the connected player
if (PhotonNetwork.isMasterClient)
GetComponent<PhotonView>().RPC("RPC_SendList", player, OccupiedSpawnSpots);
}
void OnApplicationQuit()
{
//If I am outside and others want to connect, my spot shouldn't be still set as occupied:
//I mean if the application quits I'm of course going to be disconnected.
//You have to do this in every possibility of getting disconnected or leaving the room
OccupiedSpawnSpots[mySpotNumber] = false;
//Send the changed List to the others
GetComponent<PhotonView>().RPC("RPC_UpdateList", PhotonTargets.All, OccupiedSpawnSpots);
}
[RPC]
void RPC_SendList(bool[] ListOfMasterClient)
{
OccupiedSpawnSpots = ListOfMasterClient;
//Get the free one
if (OccupiedSpawnSpots[0] == false)
{
//Spawn your player at 0
SpawnMyPlayer(0);
//Set it to occupied
OccupiedSpawnSpots[0] = true;
}
else //so the other must be free
{
//Spawn your player at 1
SpawnMyPlayer(1);
//Set it to occupied
OccupiedSpawnSpots[1] = true;
}
//Send the changed List to the others
GetComponent<PhotonView>().RPC("RPC_UpdateList", PhotonTargets.All, OccupiedSpawnSpots);
}
[RPC]
void RPC_UpdateList(bool[] RecentList)
{
OccupiedSpawnSpots = RecentList;
}
void SpawnMyPlayer(int SpotNumber) {
// Check if spawnspots are set OK
if (Spots == null) {
Debug.LogError ("SpawnSpots aren't assigned!");
return;
}
mySpotNumber = SpotNumber;
// The player object for the network
GameObject myPlayerGO = (GameObject)PhotonNetwork.Instantiate ("PlayerController",
Spots[SpotNumber].transform.position,
Spots[SpotNumber].transform.rotation, 0);
// Enable a disabled script for *this player only, or all would have the same camera, movement, etc
//((MonoBehaviour)myPlayerGO.GetComponent("FPSInputController")).enabled = true;
// Set a camera just for this player
//myPlayerGO.transform.FindChild ("Main Camera").gameObject.SetActive (true);
//standbyCamera.SetActive(false);
}
Note: If you have more than two players, it gets a lot more difficult. This code isn't optimized so maybe you'll find a better one, but it should give you the right idea.
Please ask if anything isn't clear to you...
I have created a random generator for a prefab called "Tunnel". Whenever i collide with a tunnel prefab, i wish to keep the game running. If i am no longer colliding with a tunnel i wish the game to stop.
The game works fine for the first tunnel, but when i reach the end of the first tunnel (start of the second - they overlap), my "OnTriggerExit2D" function stops the game.
Is there a way to tell my "OnTriggerExit2D" to check if i'm colliding with a different tunnel?
Here's my code:
void OnTriggerEnter2D(Collider2D other)
{
Debug.Log (other.gameObject.tag);
if (other.gameObject.CompareTag ("LeftTunnel")) {
touchRef.onTunnelL = true;
}
if (other.gameObject.CompareTag ("RightTunnel")) {
touchRef.onTunnelR = true;
}
}
void OnTriggerExit2D(Collider2D other)
{
if(other.gameObject.CompareTag("LeftTunnel"))
{
touchRef.OnTriggerExit2Dchild();
touchRef.onTunnelL = false;
}
if(other.gameObject.CompareTag("RightTunnel"))
{
touchRef.OnTriggerExit2Dchild();
touchRef.onTunnelR = false;
}
}
thanks!
So, after consulting with some people I decided to change the whole mechanism to work with ray casting and it works great now.
Here's the code for anyone in the future (I call cast function on Update):
void Cast()
{
for (int i = 0; i < Input.touchCount; ++i)
{
Vector2 test = Camera.main.ScreenToWorldPoint(Input.GetTouch(i).position);
RaycastHit2D hit = Physics2D.Raycast(test, (Input.GetTouch(i).position));
Vector2 touchPos = new Vector2(test.x, test.y);
onLeft(touchPos);
onRight(touchPos);
}
}
void onLeft(Vector2 touchPos){
Collider2D tempL;
if (tempL = Physics2D.OverlapPoint (touchPos)) {
if(tempL.CompareTag("LeftTunnel")){
Debug.Log ("Hit Left tunnel");
onTunnelL = true;
}
}
else{
onTunnelL = false;
}
}
void onRight(Vector2 touchPos){
Collider2D tempR;
if (tempR = Physics2D.OverlapPoint (touchPos)) {
if(tempR.CompareTag("RightTunnel")){
Debug.Log ("Hit Right tunnel");
onTunnelR = true;
}
}
else{
onTunnelR = false;
}
}