UNITY Lose one life when player hits the water - unity3d

I want to change the number of lives every time the player hits the water. I wrote this code so far:
public var dieSound:AudioClip;
static var lives = 3;
function Start () {
}
function Update () {
if(lives == 0)
{
Application.LoadLevel("menu");
}
}
public function OnGUI()
{
GUI.backgroundColor = Color.blue;
GUI.Button (Rect (10, 10, 100, 30), "Lives: " + lives);
}
function OnControllerColliderHit (hit : ControllerColliderHit)
{
if (hit.collider.tag == "Water")
{
// play dying sound
audio.PlayOneShot(dieSound);
// show mission fail UI
GameObject.Find("MissionTXT").guiText.enabled = true;
// wait until it's ended
yield WaitForSeconds(dieSound.length + 0.01);
transform.position = GameObject.FindWithTag("Respawn").transform.position;
if (transform.position == GameObject.FindWithTag("Respawn").transform.position)
{
GameObject.Find("MissionTXT").guiText.enabled = false;
lives = lives - 1;
}
}
}
The problem is that when the player hits the water, lives change from 3 to -120. I think that happens because the player is on the water for like 6-7 seconds. So character may hits the water 120 times until he goes back to the original position (Respawn position).
Can anyone help me with that please?

The first thing that comes to mind is as follows:
On your water GameObject, add a Collider component. I would think that a BoxCollider would fit in this scenario. Don't forget to mark the Is Trigger checkbox.
On your player GameObject, add both a RigidBody and a CharacterController (since it looks like you are using the CharacterController component). Make sure to check the RigidBody's Is Kinematic checkbox. Also, give your GameObject a meaningful tag like "Player".
Back to the water GameObject, add a new script that should look something like this:
public class Water : MonoBehaviour {
void OnTriggerEnter(Collider collider) {
if(collider.CompareTag("Player")) {
collider.SendMessage("Kill");
}
}
}
Back to the player GameObject, add a new script that should look something like this:
public class Player : MonoBehaviour {
public void Kill() {
//Perform all necessary steps to kill the player such as...
//Reduce the amount of lives by 1
//Play the death sound
//etc. etc. etc.
}
}
That's the "jist" of things, or this should at least get you started. Unity has some really good documentation and practically anything you need is there, you just have to know where to look. I'm not going to go in-depth of each thing I have mentioned above because as I've said, "Unity has some really good documentation." With that in mind, I highly recommend looking into each of the things I have mentioned. Hope this helps! =)

Related

Am I understanding something incorrectly about Physics.CheckBox?

Basically on my map I'm trying to use Physics.CheckBox detect when the player is in certain areas to determine where my enemies will spawn at. I am using a layer mask to detect when its colliding with the player and Gizmos to visualize this box in the editor. The issue I'm having is that it will return true even when the player isn't inside the box. I have verified every single other game item does not have the player layer mask causing it to return true when it hits something else. The kicker is Physics.CheckSphere works perfectly except for the fact that my map is square, not circle, so I can't use check sphere because I can't cover all of the areas I need to cover.
Code for both is as follows, note that both of these lines are not in my script at the same time I alternated them out for testing:
atNeighborhood = Physics.CheckSphere(spawnAreas[0].transform.position, neighborhoodRange, playerLayer);
atNeighborhood = Physics.CheckBox(spawnAreas[0].transform.position, neighborhoodRange, Quaternion.identity, playerLayer);
Why would the CheckBox return true when colliding with items not in the layer mask but the CheckSpere works perfectly and only returns true when colliding with the player? Anyone have any idea?
LET ME KNOW IF THERE ARE ANY PROBLEMS OR ERRORS IN COMMENTS. THANKS!
Ok. CheckBox can get kind of confusing sometimes. I would reccomend something else.
You could use Empty Game Objects with colliders on them and put them where ever you want. IsTrigger must be set to true. Imagine these as "zones", where whenever you step in one, something can happen.
All you have to do is set a certain tag to each zone to activate different things.
Note: Player does not need rigidbody, but it would be a whole lot less messy if you did.
Here is a script if your player does have a rigidbody (put this script on your player):
void OnTriggerEnter(Collider obj)
{
if (obj.gameObject.CompareTag("Zone 1"))
{
SpawnZombies();
}
}
Player doesn't have rigidbody:
If your player does not have a rigidbody, you could put a bunch a script on each one called "zone activator".
Important Notes for this version:
Your player must have a collider and a unique tag.
On each zone add a rigidbody.
Make sure detectCollisions is false!
Make sure useGravity is false!
This zone detector should have it's collider be a trigger;
(You do not want this thing to move!)
You can now create a script that goes on each zone:
public string message;
public bool inZone;
void OnTriggerEnter(Collider obj)
{
if (obj.gameObject.CompareTag("player"))
//Or set it to whatever tag the player has
{
inZone = true;
}
}
void OnTriggerExit(Collider obj)
{
if (obj.gameObject.CompareTag("player"))
//Or set it to whatever tag the player has
{
inZone = false;
}
}
You must then reference this in the player's script
public ZoneDetector[] allZones;
void Update()
{
//.....
foreach (ZoneDetector zone in allZones)
{
if (zone.inZone == true)
{
if (zone.message == "zone 1")
{
DoZone1();
}
if (zone.message == "zone 2")
{
DoZone2();
}
}
}
}

How to find out who shot a projectile

So I am trying to make a multiplayer game with abilities sort of like Overwatch/Paladins. All in all, one ability should be a sort of projectile that moves across the ground and allows that player to teleport to its position at any time while it is alive. I can't find the solution to teleporting only the player that shot it since thus far in my tests, when one player activated their ability, all players would teleport. How can I solve this?
My code:
void Update()
{
if (Input.GetKeyDown(KeyCode.E))
GetComponent<playerController>().heldAbility = "gateCrash";
if (GetComponent<playerController>().heldAbility == "gateCrash")
holding = true;
else
holding = false;
if (holding && Input.GetMouseButtonDown(0))
PhotonNetwork.Instantiate(Path.Combine("PhotonPrefabs", "GateCrashModel"), spawnPos, transform.rotation, 0);
}
This code is attached to the projectile:
public float speed = 10;
PhotonView pv;
private void Awake()
{
pv = transform.GetComponent<PhotonView>();
}
private void FixedUpdate()
{
transform.Translate(transform.forward * speed * Time.fixedDeltaTime);
}
I guess that I should make have something as instatntiation parameter but idk what.
A simple approach would be to add a shotOwner property to each of your projectiles. Every time a projectile is fired, update shotOwner to point to the player object that fired the shot. (This will also let you implement "Player_X killed Player_Y" functionality, among other things.)

GameObject Follow cursor yet also follows enemies?

I'm making a simple character that follows the player's cursor. What I also want is for when the game object "enemy" appears the character then goes to that location to alert the player. Once the enemy is gone the character continues to follow the cursor like normal. Is there a reason why my script won't work. How else can I paraphrase it?
public class FollowCursor : MonoBehaviour
{
void Update ()
{
//transform.position = Camera.main.ScreenToWorldPoint( new Vector3(Input.mousePosition.x,Input.mousePosition.y,8.75f));
if (gameObject.FindWithTag == "Enemy")
{
GameObject.FindWithTag("Enemy").transform.position
}
if (gameObject.FindWithTag != "Enemy")
{
transform.position = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,Input.mousePosition.y,8.75f));
}
}
}
You are not using FindWithTag correctly, as it is a method that takes a string as parameter you need to use it like this:
GameObject.FindwithTag("Something") as stated in the Unity scripting API
Now to apply this to your code you would need to do the following to set your players position based on wether or not an enemy is found (assuming this script is on your actual player object):
if(GameObject.FindWithTag("Enemy"))
{
//If an enemy is found with the tag "Enemy", set the position of the object this script is attatched to to be the same as that of the found GameObject.
transform.position = GameObject.FindWithTag("Enemy").transform.position;
}
else
{
//when no enemy with the tag "Enemy" is found, set this GameObject its position to the the same as that of the cursor
transform.position = Camera.main.ScreenToWorldPoint( new Vector3(Input.mousePosition.x,Input.mousePosition.y,8.75f));
}
However this code will just snap your player instantly to the position of the found Enemy. If this is not the desired behaviour you could use a function like Vector3.MoveTowards instead to make the player move to it gradually.
This code also has room for optimisation as searching for a GameObject every update frame is not the ideal solution. But for now it should work.
I'm going to code coding all the function for you, I'm not pretty sure about the beavihour of your code, I understand a gameobject will be attached to the mouse position, so not really following....
Vector3 targetPosition;
public float step = 0.01f;
void Update()
{
//if there is any enemy "near"/close
//targetPosition = enemy.position;
//else
//targetPosition = MouseInput;
transform.position = Vector3.MoveTowards(transform.position, targetPosition , step);
}
For the f you can use a SphereCast and from the enemies returned get the closest one.

OnTrigger Events Work With Delay

I've got a bullet script with a particle system and a decal.
I think that it has something to do with these events not being able to fire in time or with fps in update. Not sure yet.
So, it's being late.
The ellow points are where the particles start to play. They should be right on these wooden walls. There should be three particles working and three bullet holes, kinda bullet penetrating one wall and getting destroyed on the second one.
THE QUESTION IS HOW TO MAKE IT WORK NORMAL, SO THAT THE TRIGGERS WORK WHEN NEEDED AS WELL AS THE PARTICLES AND THE DECALS? Maybe there's a way to excellerate the code to work on time? Or maybe there's another problem with that?
The screenshot:
The Code:
public class BulletScript : MonoBehaviour {
public bool isThrough = true;
public float BulletSpeed = 100;
public float CurrentDamage;
public float EnterLuft = -0.005f;
public float ExitLuft = 0.05f;
public GameObject woodParticle;
private ContactPoint CollisionPoint;
public GameObject BulletMarkPref;
Rigidbody bullet;
private void Start()
{
bullet = this.GetComponent<Rigidbody>();
}
void FixedUpdate () {
bullet.velocity = Vector3.forward * BulletSpeed;
//this.transform.Translate(Vector3.forward * BulletSpeed * Time.deltaTime);
}
private void OnTriggerEnter(Collider other)
{
Transform hitPoint = this.transform;
LevelObject.LvlObjectType objectType = other.gameObject.GetComponent<LevelObject>().objType;
if(objectType == LevelObject.LvlObjectType.obstacle)
{
if (isThrough)
{
Instantiate(woodParticle, hitPoint.localPosition, Quaternion.LookRotation(-hitPoint.forward)).GetComponent<ParticleSystem>().Play();
LeaveBulletMark(this.transform, true);
}
else
{
Instantiate(woodParticle, hitPoint.localPosition, Quaternion.LookRotation(-hitPoint.forward)).GetComponent<ParticleSystem>().Play();
LeaveBulletMark(hitPoint, true);
Destroy(this.gameObject);
}
}
else if(objectType == LevelObject.LvlObjectType.obstacle)
{
Destroy(this.gameObject);
}
else if(objectType == LevelObject.LvlObjectType.wall)
{
LeaveBulletMark(hitPoint, true);
Destroy(this.gameObject);
}
}
private void OnTriggerExit(Collider other)
{
Transform hitPoint = this.transform;
Instantiate(woodParticle, hitPoint.localPosition, hitPoint.rotation).GetComponent<ParticleSystem>().Play();
LeaveBulletMark(hitPoint, false);
}
void LeaveBulletMark(Transform hitPoint, bool ifEnter)
{
GameObject TemporaryBulletMarkHandler;
TemporaryBulletMarkHandler = Instantiate(BulletMarkPref, hitPoint.localPosition, Quaternion.LookRotation(ifEnter ? hitPoint.forward : CollisionPoint.normal)) as GameObject;
isThrough = false;
TemporaryBulletMarkHandler.transform.Translate(hitPoint.forward * (ifEnter ? 0.005f : -0.005f));
}
}
I don't think your problem is something simple with the code. There is an inherent issue with calculating fast moving objects like bullets with true physics calculations especially if they are small. Often between physics updates, they pass through wall colliders completely without registering.
You have to think of it like this to see the problem. The bullet isn't tracked continuously along its trajectory. It has a starting location, a formula for its movement and it calculates a new location at each physics update. You could fire a bullet straight at a wall, and in one update the bullet would be several meters in front of the wall, and in the next, it would be several meters behind the wall without ever triggering a collision. This is why so many game use ray tracing to calculate bullet trajectories. The bullet movement isn't perfectly accurate, especially for long shots, but obstructions to the bullet path are registered.
By default unity's Physics Engine runs at 50 frames per second. A modern bullet travels between 1200 and 1700 m/s. That gives you a distance between 24 and 34 meters traveled between frames. Even a small object falling at terminal velocity (54 m/s) might pass through a collider unnoticed. If you made a 1-meter thick box collider, you would likely register a falling object but not a bullet.
I think you could do some clever combination of ray tracing and bullet physics to get the best of both worlds. Maybe you could ray trace from the bullet at each fixed update or there may be some better technique already invented for this exact situation that I don't know about.

2D player damage on trigger collider

So I'm making a little top down space shooter and everything works great so far. My issue is that when I change weapons I would like the secondary to do more damage than the primary (changeable with the number keys). It's been a real head scratcher and any help would be greatly appreciated. this is what I tried;
adding the weapons to individual layers. in the OnTriggerEnter2D function I make an if statement stating if the gameObject (bullet) is firing from layer 13, take away x amount of HP. Doesn't do anything though. below is my current code for the enemy.
using UnityEngine;
using System.Collections;
public class enemyDamage : MonoBehaviour {
public int health = 2;
void OnTriggerEnter2D(){
health--;
}
void Start () {
}
void Update () {
if (health <= 0){
Die();
}
}
void Die(){
Destroy (gameObject);
}
}
You can change your OnTriggerEnter2D function by adding Collider2D parameter to it. That way you can get access to the gameObject that triggered the function.
You didn't post the code that is instantiating the bullet. But if you set bullets name to different values you can decrease the health based on that.
void OnTriggerEnter2D(Collider2D other) {
if(other.gameObject.name == "missile"){
health -= 10;
}else if(other.gameObject.name == "bullet"){
health -= 1;
}
}
Or even better idea might be adding a script to the bullet, which knows the amount of damage the bullet is going to make.
void OnTriggerEnter2D(Collider2D other) {
health -= other.gameObject.GetComponent<BulletScript>().damage;
}