OnControllerColliderHit triggering too many times - c#

I'm wrote this script to decrease the players life and move them back to their original point on contact with certain objects, but I'm finding that it will trigger anywhere from 1 to 4 times per collision taking lives from 3 down to -1.
using UnityEngine;
using System.Collections;
public class HitCar : MonoBehaviour
{
public static int lives = 3;
void OnControllerColliderHit(ControllerColliderHit col)
{
if(col.gameObject.name == "utd_car1")
{
Destroy(col.gameObject);
lives--;
if(lives <= 0)
{
Application.LoadLevel("LoseScreen");
}
else
{
var player = GameObject.Find("3rd Person Controller");
player.transform.position = new Vector3(0, 2, -26);
}
}
}
void OnLevelWasLoaded(int level)
{
lives = 3;
}
}
Any way to prevent it from triggering more than once per collision would be greatly appreciated.

OnControllerColliderHit is used when you want to hit something a few times, preferably to move it.
You can switch to this code:
void OnCollisionEnter(Collision col)
{
if(col.gameObject.name == "utd_car1")
{
}
}

Related

Object reference not set to an instance of an object C# Unity using Collider2D trigger

Just wondering if anyone could help me out with a small issue in my code. On line 60, there is an error essentially telling me what I am referencing to does not exist. To my knowledge it does, but I am very new to Unity. I am trying to create a random dungeon generator for a University project. My classes are below:
using System.Collections.Generic;
using UnityEngine;
public class RoomSpawner : MonoBehaviour
{
public int openingDirection;
//1 = need bottom door
//2 = need top door
//3 = need left door
//4 = need right door
//So for a room with a door on the right, you will type 3, as a left door is needed in the next room to connect the two rooms.
private RoomTemplates templates;
private int rand;
private bool spawned = false;
private Destroyer destroyer;
void Start()
{
templates = GameObject.FindGameObjectWithTag("Rooms").GetComponent<RoomTemplates>();
Invoke("Spawn", 0.1f);
}
void Spawn()
{
if (spawned == false)
{
rand = Random.Range(0, templates.bottomRooms.Length);
if (openingDirection == 1)
{
//Need to spawn room with BOTTOM door
Instantiate(templates.bottomRooms[rand], transform.position, templates.bottomRooms[rand].transform.rotation);
}
else if (openingDirection == 2)
{
//Need to spawn room with TOP door
Instantiate(templates.topRooms[rand], transform.position, templates.topRooms[rand].transform.rotation);
}
else if (openingDirection == 3)
{
//Need to spawn room with LEFT door
Instantiate(templates.leftRooms[rand], transform.position, templates.leftRooms[rand].transform.rotation);
}
else if (openingDirection == 4)
{
//Need to spawn room with RIGHT door
Instantiate(templates.rightRooms[rand], transform.position, templates.rightRooms[rand].transform.rotation);
}
spawned = true;
}
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Spawn Point"))
{
if (other.GetComponent<RoomSpawner>().spawned == false && spawned == false)
{
Instantiate(templates.closedRoom, transform.position, Quaternion.identity);
}
spawned = true;
}
}
}
using System.Collections.Generic;
using UnityEngine;
public class Destroyer : MonoBehaviour
{
void OnTriggerEnter2D(Collider2D other)
{
Destroy(other.gameObject);
}
}
This issue is causing the entry room to be blocked by closed rooms, which are meant to block exits out into the scene view. I have followed BlackThornProd's tutorial on this, but cannot figure out what I have done wrong. Here is a link to the tutorial.
Many thanks!
EDIT
Here I have included the RoomTemplates Class if it helps. Many Thanks!
using System.Collections.Generic;
using UnityEngine;
public class RoomTemplates : MonoBehaviour
{
public GameObject[] bottomRooms;
public GameObject[] topRooms;
public GameObject[] leftRooms;
public GameObject[] rightRooms;
public GameObject closedRoom;
}
Are templates a gameobject? If so, I think you can try something like this:
private GameObject templates;
void Start()
{
templates = GameObject.FindGameObjectWithTag("Rooms");
RoomTemplates Templates = templates.GetComponent<RoomTemplates>();
Invoke("Spawn", 0.1f);
}

Can't parent objects in unity for oculus quest grab script

preface: i am very new to c#, and this is an object grabbing script i have taken from a prefab and edited.
script goal: detect if a game object is in pickup distance. if it is, when the grip trigger is pressed, parent the object to the virtual hand. when the trigger is released, remove the parent relationship. Also, while the object is parented to the controller, if the B button is held, scale the object up, and if the A button is held, scale the object down, and reset the scale upon thumbstick click.
I can see when my controller collides with the game object, but when i press the designated button to parent the object, nothing seems to happen.
I'd really appreciate any help I could get with this script, as there might be a significant amount i'm doing wrong.
I should mention that I don't really need any sort of physics simulations, all i need is the ability to manipulate an object floating in space for viewing purposes.
Thanks in advance!
Here is my script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Grabber : MonoBehaviour
{
// Update is called once per frame
void Update()
{
OVRInput.Update();
if ((OVRInput.Get(OVRInput.Axis1D.SecondaryHandTrigger)) > 0.2f && CollidingObject)
{
GrabObject();
}
if ((OVRInput.Get(OVRInput.Axis1D.SecondaryHandTrigger)) < 0.2f && ObjectInHand)
{
ReleaseObject();
}
if (OVRInput.Get(OVRInput.Button.Two) && ObjectInHand)
{
ScaleUp();
}
if (OVRInput.Get(OVRInput.Button.One) && ObjectInHand)
{
ScaleDown();
}
if (OVRInput.Get(OVRInput.Button.SecondaryThumbstick) && ObjectInHand)
{
ScaleReset();
}
}
public GameObject CollidingObject;
public GameObject ObjectInHand;
public void OnTriggerEnter(Collider other)
{
if (other.gameObject.GetComponent<Rigidbody>())
{
CollidingObject = other.gameObject;
}
}
public void OnTriggerExit(Collider other)
{
CollidingObject = null;
}
private void GrabObject()
{
ObjectInHand = CollidingObject;
ObjectInHand.transform.SetParent(this.transform);
ObjectInHand.GetComponent<Rigidbody>().isKinematic = true;
}
private void ReleaseObject()
{
ObjectInHand.GetComponent<Rigidbody>().isKinematic = false;
ObjectInHand.transform.SetParent(null);
ObjectInHand = null;
}
Vector3 scaleChangeUp = new Vector3(0.01f, 0.01f, 0.01f);
Vector3 scaleChangeDown = new Vector3(-0.01f, -0.01f, -0.01f);
public void ScaleUp()
{
ObjectInHand.transform.localScale += scaleChangeUp;
}
public void ScaleDown()
{
ObjectInHand.transform.localScale += scaleChangeDown;
}
private void ScaleReset()
{
ObjectInHand.transform.localScale = Vector3.one;
}
}

Stone not spawning at correct place after player respawns

When the player reSpawns I want the stones(which can be moved by the player), to go to their original spot. This is not working for me. Instead, the stone spawn on other nearby locations and sometimes off the map. All stones are tagged stone.
I have tried reversing the Vectors and reversing the signs on the subtraction and such.
This is the player script.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerControll : MonoBehaviour
{
public bool isTouchingGround = false;
public GameObject locater1;
public GameObject locater2;
public GameObject currentCheckPoint;
public GameObject playerPrefab;
public Rigidbody2D myRigidBody;
public GameObject GameMaster;
// Start is called before the first frame update
void Start()
{
isTouchingGround = false;
GameObject MyGameMaster= Instantiate(GameMaster, new Vector3(0, 0, 0), Quaternion.identity);
GameMaster = MyGameMaster;
}
// Update is called once per frame
void Update()
{
if (Input.GetKey("a"))
{
transform.Translate(-0.1f, 0, 0);
}
if (Input.GetKey("d"))
{
transform.Translate(0.1f, 0, 0);
}
if ((Input.GetKey("w") || Input.GetKey("space")) && isTouchingGround == true)
{
myRigidBody.AddForce(new Vector2(0f, 130f));
}
if (locater1.transform.position.y <= locater2.transform.position.y)
{
ReSpawn();
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Platform" || collision.gameObject.tag == "Stone")
{
isTouchingGround = true;
}
}
private void OnCollisionExit2D(Collision2D collision)
{
if ((collision.gameObject.tag == "Platform" || collision.gameObject.tag == "Stone") && locater2.transform.position.y > collision.transform.position.y)
{
isTouchingGround = false;
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "CheckPoint")
{
currentCheckPoint = collision.gameObject;
}
if (collision.gameObject.tag == "Radiation")
{
ReSpawn();
}
}
public void ReSpawn()
{
GameMaster.gameObject.GetComponent<ReverToOriginalPositions>().RevertToOrignalPosition();
GameObject currentPlayer=Instantiate(playerPrefab, currentCheckPoint.transform.position, currentCheckPoint.transform.rotation);
currentPlayer.GetComponent<PlayerControll>().isTouchingGround = false;
Destroy(this.gameObject);
}
}
This is the RememberPositions script. This belongs to every stone to remember the position is has during the start.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RememberPositions : MonoBehaviour
{
public float StartXPosition;
public float StartYPosition;
float StartRotation;
// Start is called before the first frame update
void Start()
{
StartXPosition = transform.position.x;
StartYPosition = transform.position.y;
StartRotation = transform.rotation.z;
}
// Update is called once per frame
void Update()
{
}
}
this is the RevertToOriginalPosition Script. This reverts the stones to their original positions. This is in the GameMaster/MasterControll.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ReverToOriginalPositions : MonoBehaviour
{
public GameObject[] allStones;
// Start is called before the first frame update
void Start()
{
allStones = GameObject.FindGameObjectsWithTag("Stone");
}
// Update is called once per frame
void Update()
{
}
public void RevertToOrignalPosition()
{
for (int i = 0; i < allStones.Length; i++)
{
allStones[i].gameObject.transform.Translate((new Vector3((allStones[i].gameObject.GetComponent<Transform>().position.x- allStones[i].gameObject.GetComponent<RememberPositions>().StartXPosition) , (allStones[i].gameObject.GetComponent<Transform>().position.y- allStones[i].gameObject.GetComponent<RememberPositions>().StartYPosition), 0)));
allStones[i].gameObject.transform.Rotate(0, 0, -allStones[i].gameObject.transform.rotation.z);
}
}
}
This is the Store reciever Script. This is for another game mechanic called an Activator. This is not that important but I am including it anyway.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class StoreReciever : MonoBehaviour
{
public GameObject reciever;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
void OnTriggerEnter2D(Collider2D collision)
{
reciever.gameObject.GetComponent<PolygonCollider2D>().enabled = true;
}
void OnTriggerExit2D(Collider2D collision)
{
reciever.gameObject.GetComponent<PolygonCollider2D>().enabled = false;
}
}
No error messages at all. Just Stones reSpawning in locations they are not supposed to. Thank You for helping.
Just make some fields/properties they dont even have to be private static readonly
public static readonly Vector3 StartPos = new Vector3(1.0f, 2.0f, 3.0f);
public static readonly Vector3 StartRot_In_EulerAngles = new Vector3(1.0f, 2.0f, 3.0f);
Then you can do something like this in a method:
public static void ResetMyStones()
{
var allStones = GameObject.FindGameObjectsWithTag("Stone");
foreach(var stone in allStones)
{
stone.transform.position = StartPos;
stone.transform.eulerAngles = StartRot_In_EulerAngles;
}
}
About Euler Angle:
Why Euler Angle? - because for rotations Unity uses so called Quaternions, so Gimbal lock will not happen, but the bad thing about this is that you cannot really set x y z component individually without deep understanding of Quaternions, however if you just wanna set and get a current rotation Euler Angle is perfect, because you can use the traditional x,y,z components for setting the value.
Additional Notes:
Your code is kinda long and not well structured, like ReverToOriginalPositions should be a method and not a component, also not evrything has to be a derived from monobehaviour.
Also remove the unused Update methods, they have a small overhead if its in a script but not used(Usually its immeasurable but in bigger project it can effect performance :)), and maybe if your game is structured that way you could store all your current stones in a GameManager class list so you dont need to call GameObject.FindGameObjectsWithTag("Stone");

Unity 2D, C# - Why my OnCollisionEnter2D doesn't collide?

So, i created two scripts, one named "Stats.cs" registers the player stats and the other one named "PlayerHealth.cs" "makes" the player take damage on contact and updates the Hearts in the HUD. My problem is, whenever i collide with an object that has a tag named "Projectile" it simply doesn't work, my player doesn't take damage at all. The Stats.cs script isn't in any object, the PlayerHealth.cs is in my Player object.
Stats.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Stats{
private int health;
public int maxHP = 3;
public int Health
{
get
{
//Some code
return health;
}
set
{
//Some code
health = Mathf.Clamp(value, 0, maxHP);
}
}
public void SetHealth()
{
Health = maxHP;
}
}
PlayerHealth.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerHealth : MonoBehaviour
{
Stats playerStats = new Stats();
public int curHealth;
public int numOfHearts = 3;
public Image[] hearts;
public Sprite fullHeart;
public Sprite emptyHeart;
void OnCollisionEnter2D(Collision2D other)
{
if (other.gameObject.CompareTag("Projectile"))
{
Debug.Log("Hello");
DamagePlayer(1);
Destroy(other.gameObject);
}
}
public void DamagePlayer(int damage)
{
playerStats.Health -= damage;
}
// Start is called before the first frame update
void Start()
{
playerStats.SetHealth();
curHealth = numOfHearts;
}
// Update is called once per frame
void Update()
{
curHealth = playerStats.Health;
numOfHearts = playerStats.maxHP;
if (curHealth>numOfHearts){
curHealth = numOfHearts;
}
if(curHealth <= 0){
Die();
}
for (int i = 0; i < hearts.Length; i++)
{
if(i < curHealth){
hearts[i].sprite = fullHeart;
} else
{
hearts[i].sprite = emptyHeart;
}
if(i < numOfHearts){
hearts[i].enabled = true;
} else {
hearts[i].enabled = false;
}
}
}
void Die(){
//Restart
Application.LoadLevel(Application.loadedLevel);
}
}
curHealth is updating so it will stay as the actual Health in Stats and will change the images in HUD.
The Player has a RigidBody2D on him two colliders, one is a box for the body, and the other is a circle collider, so when the player crouches, the circle collider disables.
The Projectiles also have and RigidBody2D with 0 gravity (so it won't fall in mid air) and a BoxCollider2D.
I would check and make sure that the projectile is tagged as Projectile and that the BoxCollider doesn't have "Is Trigger" checked.
I should also say, iterating with that for loop in the Update is very bad practice performance wise. That is happening literally as fast as the machine can loop it and it is doing that every time. I would look into updating it on an event.
Hope this helps!

Add extra life HEART texture 2D

I am working on 2D game.
When player collides with BOMBPrefab lose 1 heart (initial 3 heart), if collides 3 times = GameOver.
Thats is the code (works fine for BombPrefab):
using UnityEngine;
using System.Collections;
public class Heart : MonoBehaviour {
public Texture2D[] initialHeart;
private int heart;
private int many;
// Use this for initialization
void Start () {
GetComponent<GUITexture>().texture = initialHeart[0];
heart = initialHeart.Length;
}
// Update is called once per frame
void Update () {}
public bool TakeHeart()
{
if (heart < 0) {
return false;
}
if (many < (heart - 1)) {
many += 1;
GetComponent<GUITexture> ().texture = initialHeart [many];
return true;
} else {
return false;
}
}
}
I have a second HeartPrefab, I want to check when player collides... IF I have 3 hearts do nothing, IF have 1 or 2 hearts ADD extra heart.
BombPrefab Scrip:
using UnityEngine;
using System.Collections;
public class Bomb : MonoBehaviour
{
public AudioClip clipBomba;
private Heart heart;
private Gerenciador gerenciador;
void Awake ()
{
}
// Use this for initialization
void Start ()
{
gerenciador = FindObjectOfType (typeof(Gerenciador)) as Gerenciador;
}
// Update is called once per frame
void Update ()
{
}
void OnCollisionEnter2D (Collision2D colisor)
{
if (colisor.gameObject.tag == "floor") {
Destroy (gameObject, 2f);
} else {
if (colisor.gameObject.tag == "Bee") {
Som();
heart= GameObject.FindGameObjectWithTag ("Heart").GetComponent<Vidas> () as Vidas;
if (heart.TakeHeart ()) {
Destroy (gameObject);
} else {
gerenciador.GameOver ("GameOver");
}
}
}
}
void Som()
{
GetComponent<AudioSource> ().clip = clipBomb;
AudioSource.PlayClipAtPoint (clipBomb, transform.position);
}
}
First of all try not use GetComponent<>() in update and functions that are run often. It is better to cache components in Start() method. Using FindGameObjectWith...() is also not very efficient.
I would make it differently, each heart as a separate UI element (Toggle), with two states active and inactive. Then your player should have a list of those hearts and methods that allow to take damage and gain life.
int currentHeartsCount = 3;
int maxHeartsCount = 3;
List<Heart> hearts = new List<Heart>();
public void TakeDamage(int damage) {
ChangeHearts(-damage);
}
public void GainLife(int life) {
ChangeHearts(life);
}
private void ChangeHearts(int amount) {
currentHeartsCount += amount;
if(currentHeartsCount> maxHeartsCount)
currentHeartsCount = maxHeartsCount;
if(currentHeartsCount<=0) {
// call player dead code
}
int i = 1;
foreach(Heart heart in hearts) {
heart.SetHeartActive(i<=currentHeartsCount);
i++;
}
}
This is a simplified solution to give you an idea. It is more robust than yours because you can easily change start heart count, and make things like heart "overcharge". Just add base hearts, set max to desired value, and now you can create some special power ups which increase hearts over the initial limit.

Categories

Resources