Unity how to detect if a specific Game Object is near you - c#

I'm creating a test game because I'm getting ready to create my first game but I want to make sure I get all the simple mechanics down that my first game will require. One of the mechanics that will be included in the game is picking up items if they are a certain distance to you. Sometimes there might be multiple of the same object in the game, I figured the code would work for all coins however that is just not the case. The Debug.Log() only works on one specific coin, how do I make it so it will fire no matter what coin I'm near?
Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerController : MonoBehaviour {
//Player Variables
public float moveSpeed;
public float jumpHeight;
public float raycastDistanceGround;
public Text moneyText;
private bool isGrounded;
private Rigidbody _rgb;
private GameObject player;
private GameObject[] coin;
private float distanceToCollectCoin;
private float distanceToCoin;
void Start () {
moveSpeed = 7f;
jumpHeight = 9f;
raycastDistanceGround = 0.5f;
isGrounded = true;
_rgb = GetComponent<Rigidbody>();
player = GameObject.FindGameObjectWithTag("Player");
coin = GameObject.FindGameObjectsWithTag("Coin");
distanceToCollectCoin = 2f;
Cursor.lockState = CursorLockMode.Locked;
}
void FixedUpdate () {
IsGrounding();
Move();
Jump();
SetMoneyText();
NearCoin();
}
//Player Moving Mechanics
void Move() {
var moveHorizontal = Input.GetAxis("Horizontal") * moveSpeed * Time.fixedDeltaTime;
var moveVertical = Input.GetAxis("Vertical") * moveSpeed * Time.fixedDeltaTime;
transform.Translate(moveHorizontal, 0f, moveVertical);
if (Input.GetKeyDown(KeyCode.Escape)) {
Cursor.lockState = CursorLockMode.None;
}
}
//Player Jump Mechanics
void Jump() {
var jump = new Vector3(0f, _rgb.position.y, 0f);
if (Input.GetKey(KeyCode.Space) && isGrounded == true) {
for (float i = 0; i <= jumpHeight; i++) {
jump.y += i;
_rgb.AddForce(jump);
}
}
}
void IsGrounding() {
if (Physics.Raycast(transform.position, Vector3.down, raycastDistanceGround)) {
isGrounded = true;
} else {
isGrounded = false;
}
}
void SetMoneyText() {
moneyText.text = ("Money: " + EconomyController.Money);
}
void NearCoin() {
for (int i = 0; i < coin.Length; i++) {
distanceToCoin = Vector3.Distance(coin[i].transform.position, player.transform.position);
}
if (distanceToCoin < distanceToCollectCoin) {
Debug.Log("Near Coin");
}
}
}

Looks like you just bracketed some stuff wrong. You need to move your if-statement into the for-loop. Right now it's only checking the distance for the last coin in the array.
void NearCoin()
{
for (int i = 0; i < coin.Length; i++)
{
distanceToCoin = Vector3.Distance(coin[i].transform.position, player.transform.position);
if (distanceToCoin < distanceToCollectCoin)
Debug.Log("Near Coin");
}
}

Related

player collision does not work with objects

I'm making an endless runner game and i have a question about my player colliding with so,e obstacles, I used Raycast but when i try to debug this collision doesn't occur.
Here my player Code.
public class Player : MonoBehaviour
{
private CharacterController controller;
public float speed;
public float jumpHeight;
private float jumpVelocity;
public float gravity;
public float rayRadius;
public LayerMask layer;
public float horizontalSpeed;
private bool isMovingLeft;
private bool isMovingRight;
private bool isDead;
// Start is called before the first frame update
void Start()
{
controller = GetComponent<CharacterController>();
}
// Update is called once per frame
void Update()
{
Vector3 direction = Vector3.forward * speed;
if(controller.isGrounded)
{
if(Input.GetKeyDown(KeyCode.Space))
{
jumpVelocity = jumpHeight;
}
if(Input.GetKeyDown(KeyCode.RightArrow)&& transform.position.x < 3.58f && !isMovingRight)
{
isMovingRight = true;
StartCoroutine(RightMove());
}
if(Input.GetKeyDown(KeyCode.LeftArrow)&& transform.position.x > -3.58f && !isMovingLeft)
{
isMovingLeft = true;
StartCoroutine(LeftMove());
}
}
else
{
jumpVelocity -= gravity;
}
OnCollision();
direction.y = jumpVelocity;
controller.Move(direction * Time.deltaTime);
}
IEnumerator LeftMove()
{
for(float i = 0; i < 10; i += 0.1f)
{
controller.Move(Vector3.left * Time.deltaTime * horizontalSpeed);
yield return null;
}
isMovingLeft = false;
}
IEnumerator RightMove()
{
for (float i = 0; i < 10; i += 0.1f)
{
controller.Move(Vector3.right * Time.deltaTime * horizontalSpeed);
yield return null;
}
isMovingRight = false;
}
void OnCollision()
//The player will collide with obstacles that have a specific type of layer and dead.
{
RaycastHit hit;
if(Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out hit, rayRadius, layer) && !isDead)
{
Debug.Log("GameOver!");
speed = 0;
jumpHeight = 0;
isDead = true;
}
}
}
Instead of using raycast inside OnCollision function. You can do this with very much by following these steps:
Add a tag to your obstacles let say its "Obstacle"
Add collider to them
Check when you collide with someone that if it is obstacle or not if yes then you can call your dead function.
void OnCollisionEnter(Collision collision)
{
if(collision.transform.CompareTag("Obstacle")){
Debug.Log("GameOver!");
speed = 0;
jumpHeight = 0;
isDead = true;
}
}
In this way you can detect collision with super ease instead of using raycast.

how can I make the game object disappear?

when I was watching on YouTube, tutorials about endless runner on part 2, the game object wouldn't disappear when it hits the player
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Obstacle : MonoBehaviour
{
public int damage = 1;
public float speed;
private void Update()
{
transform.Translate(Vector2.left * speed * Time.deltaTime);
}
void onTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("rocket"))
{
//rocket takes damage 1
other.GetComponent<rocket>().health -= damage;
Debug.Log(other.GetComponent<rocket>().health);
Destroy(gameObject);
}
}
}
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class rocket : MonoBehaviour
{
private Vector2 targetPos;
public float Yincrement;
public float speed;
public float maxHeight;
public float minHeight;
public int health = 3;
void Update()
{
if (health <= 0)
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
transform.position = Vector2.MoveTowards(transform.position, targetPos, speed * Time.deltaTime);
if (Input.GetKeyDown(KeyCode.UpArrow) && transform.position.y < maxHeight)
{
targetPos = new Vector2(transform.position.x, transform.position.y + Yincrement);
}
else if (Input.GetKeyDown(KeyCode.DownArrow) && transform.position.y > minHeight)
{
targetPos = new Vector2(transform.position.x, transform.position.y - Yincrement);
}
}
}
from this one https://www.youtube.com/watch?v=FVCW5189evI and I'm confused why it didn't work, can someone tell me what is wrong?
if you want to destroy use this, Correct Method to get the game object when collide is: other.gameObject
void onTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("rocket"))
{
//rocket takes damage 1
other.gameObject.GetComponent<rocket>().health -= damage;
Debug.Log(other.gameObject.GetComponent<rocket>().health);
Destory(other.gameObject);
}
}
public class megaStar : MonoBehaviour{
private Rigidbody2D rb;
private Animator _ani;
public bool canAttack = true;
[SerializeField] private Attack _attackObject;
private AudioSource _as;
public checkpoint lastCheckpoint;
public bool isDead = false;
private float _timer = 1.0f;
public float attackTimer = 2.0f;
public GameObject projectile;
public float projectileSpeed = 18.0f;
public Transform projectileAttackPoint;
public float timeDelayForNextShoot = 0.1f;
private float CONST_timeDelayForNextShoot;
// Start is called before the first frame update
void Start(){
CONST_timeDelayForNextShoot = timeDelayForNextShoot;
rb = GetComponent<Rigidbody2D>();
_as = GetComponent<AudioSource>();
_ani = GetComponent<Animator>();
_timer = attackTimer;
}
void Update(){
if (Input.GetKey(KeyCode.W))
{
rb.velocity = new Vector2(0f, 10f);
}
else if (Input.GetKey(KeyCode.S))
{
rb.velocity = new Vector2(0f, -10f);
}
else
{
rb.velocity = new Vector2(0f, 0f);
}
timeDelayForNextShoot -= Time.deltaTime;
if (Input.GetKeyDown(KeyCode.K) && timeDelayForNextShoot <= 0f){
_ani.SetBool("projectileAttack", true);
GameObject go = Instantiate(projectile, projectileAttackPoint.position, projectileAttackPoint.rotation) as GameObject;
go.GetComponent<Rigidbody2D>().velocity = new Vector2(-projectileSpeed, 0.0f);
Destroy(go, 2.0f);
timeDelayForNextShoot = CONST_timeDelayForNextShoot;
canAttack = false;
// new WaitForSeconds(1);
return;
}
}
void FixedUpdate()
{
if (!isDead)
{
Update();
}
else{
rb.velocity = Vector2.zero;
RigidbodyConstraints2D newRB2D = RigidbodyConstraints2D.FreezePositionY;
rb.constraints = newRB2D;
}
}
private void OnCollisionEnter2D(Collision2D collision){
if (collision.gameObject.CompareTag("Enemy"))
{
GetComponent<HitPoints>().TakeDamage(1);
}
}
}
Use SetActive instead of Destroy Function.
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("rocket"))
{
//rocket takes damage 1
other.gameObject.GetComponent<rocket>().health -= damage;
Debug.Log(other.gameObject.GetComponent<rocket>().health);
gameObject.SetActive(false);// it'll Hide GameObject instead of Destroying
// gameObject.SetActive(true);// to show again
}
}

Grenade spawns in the wrong location

In my game I want to have a floating monster that's attack throws a grenade at the player. My problem is that the grenade only spawns in 0, 0, 0. In my script I make it so that the zombies spawns in on its own location but for some reason that doesn't work. I tried making it spawn by having the spawn location equal new Vector3(100, 100, 100) but it still spawned at 0, 0, 0. I know that the co-routine runs because I put a Debug.Log. Thanks for the help!
Edit #2: I can't have a rigidbody on the script. I have edited the movement script and I have found that no mater what if a rigidbody is added then it will go to 0, 0, 0.
Edit #3: I updated the scripts
Here is my script: (Sorry if the code is bad)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ZKAttack_lvl3 : MonoBehaviour
{
public Transform Player;
public float MoveSpeed = 2.0f;
public float InRadius = 10.0f;
public float AttackRange = 15.0f;
private Coroutine hasCourutineRunYet;
public GameObject grenade;
public GameObject FloatingMonster;
private Vector3 FloatingMon;
private Animator anim;
private Rigidbody rigid;
private void Start()
{
anim = GetComponent<Animator>();
Player = GameObject.FindGameObjectsWithTag("Player")[0].transform;
rigid = GetComponent<Rigidbody>();
}
void Update()
{
Player = GameObject.FindGameObjectsWithTag("Player")[0].transform;
transform.LookAt(Player);
float dstSqr = (Player.position - transform.position).sqrMagnitude;
bool inRadius = (dstSqr <= InRadius * InRadius);
bool inAttackRange = (dstSqr <= AttackRange * AttackRange);
anim.SetBool("AttackingPlayer", inAttackRange);
if (inRadius)
{
transform.position += transform.forward * MoveSpeed * Time.deltaTime;
}
rigid.AddForce(1, 10, 1);
if (inAttackRange)
{
if (hasCourutineRunYet == null)
{
hasCourutineRunYet = StartCoroutine(GrenadeAttack());
}
}
}
IEnumerator GrenadeAttack()
{
FloatingMon = FloatingMonster.transform.position;
GameObject bulletObject = Instantiate(grenade, FloatingMonster.transform.position, Quaternion.identity);
yield return new WaitForSeconds(2.0f);
}
}
Edit: This is the code for the movement:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GrenadeMovement : MonoBehaviour
{
public float speed = 10f;
public float lifeDuration = 4.0f;
private float lifeTimer;
private Coroutine hasCourutineRunYet;
private Transform Player;
public SphereCollider sphereCollider;
// Use this for initialization
void Start()
{
lifeTimer = lifeDuration;
sphereCollider.enabled = false;
Player = GameObject.FindGameObjectsWithTag("Player")[0].transform;
}
// Update is called once per frame
void Update()
{
lifeTimer -= Time.deltaTime;
if (lifeTimer >= 3f)
{
Player = GameObject.FindGameObjectsWithTag("Player")[0].transform;
transform.LookAt(Player);
transform.position += transform.forward * speed * Time.deltaTime;
}
if (lifeTimer >= 0f)
{
transform.position = speed * transform.up * Time.deltaTime;
transform.position = speed * transform.forward * Time.deltaTime;
}
if (lifeTimer <= 0f)
{
if (hasCourutineRunYet == null)
{
hasCourutineRunYet = StartCoroutine(GrenadeExplosion());
}
}
}
private void OnTriggerEnter(Collider coll)
{
if (coll.gameObject.tag == "Player")
{
if (hasCourutineRunYet == null)
{
hasCourutineRunYet = StartCoroutine(GrenadeExplosion());
}
}
}
IEnumerator GrenadeExplosion()
{
sphereCollider.enabled = true;
yield return new WaitForSeconds(1.0f);
Destroy(gameObject);
}
}
With the help of #ken I figured out that I couldn't use a rigidbody so I changed the insantiation to this: GameObject bulletObject = Instantiate(grenade, FloatingMonster.transform.position, Quaternion.identity);. I then changed the movement script for it to:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GrenadeMovement : MonoBehaviour
{
public float speed = 10f;
public float lifeDuration = 4.0f;
private float lifeTimer;
private Coroutine hasCourutineRunYet;
private Transform Player;
public SphereCollider sphereCollider;
public Vector3 velocity;
// Use this for initialization
void Start()
{
lifeTimer = lifeDuration;
sphereCollider.enabled = false;
Player = GameObject.FindGameObjectsWithTag("Player")[0].transform;
}
// Update is called once per frame
void Update()
{
lifeTimer -= Time.deltaTime;
if (lifeTimer >= 2f)
{
Player = GameObject.FindGameObjectsWithTag("Player")[0].transform;
transform.LookAt(Player);
transform.position += transform.forward * 5 * Time.deltaTime;
}
if (lifeTimer >= 0f && lifeTimer <= 2f)
{
transform.position = 9.18f * transform.up * Time.deltaTime;
transform.position = 9.18f * transform.forward * Time.deltaTime;
}
if (lifeTimer <= 0f)
{
if (hasCourutineRunYet == null)
{
hasCourutineRunYet = StartCoroutine(GrenadeExplosion());
}
}
}
private void OnTriggerEnter(Collider coll)
{
if (coll.gameObject.tag == "Player" || coll.gameObject.tag == "Terrain")
{
if (hasCourutineRunYet == null)
{
hasCourutineRunYet = StartCoroutine(GrenadeExplosion());
}
}
}
IEnumerator GrenadeExplosion()
{
sphereCollider.enabled = true;
yield return new WaitForSeconds(1.0f);
Destroy(gameObject);
}
}
Thank you for all your help, I have been trying to fix this all week.
You could set its position in the Instantiate line. Instantiate has several arguments. You can set its position in Instantiate, as well as its rotation and parent.
Set it to this:
IEnumerator GrenadeAttack()
{
GameObject bulletObject = Instantiate(grenade, FloatingMonster.transform.position, Quaternion.identity);
yield return new WaitForSeconds(2.0f);
}

I'm trying to lerp forward the player a fixed distance but it only works once. How do I allow the player to be moved more than once?

Please check the code. Once the player moves the fixed distance once, he doesn't move again even when the space input is given.
How Do I make it such that I can keep moving the player after the player has been moved once?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovementPC : MonoBehaviour
{
[Header ("Move Settings")]
private Vector3 StartingPosition, EndingPosition;
public float moveDistance = 30f;
public float LerpTime = 1f;
private float CurrentLerpTime = 0;
public bool movePressed = false;
private void Start()
{
StartingPosition = transform.position;
EndingPosition = transform.position + Vector3.forward * moveDistance;
}
void Update()
{
if (Input.GetKeyDown("space"))
{
movePressed = true;
Debug.Log("dash pressed");
}
if (movePressed == true)
{
CurrentLerpTime += Time.unscaledDeltaTime;
if (CurrentLerpTime >= LerpTime)
{
CurrentLerpTime = LerpTime;
}
float LerpPercentage = CurrentLerpTime / LerpTime;
transform.position = Vector3.Lerp(StartingPosition, EndingPosition, LerpPercentage);
}
}
Any Help would be appreciated. Thanks for your time.
The issue is you are not resetting the variables that control the start/end positions of the movement. The Start() method is called when the object is added to the level, or Play is pressed and that is currently the only method that resets those values. Therefore if you want those values reset you need to determine when the movement has finished, something like:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovementPC : MonoBehaviour
{
[Header ("Move Settings")]
private Vector3 StartingPosition, EndingPosition;
public float moveDistance = 30f;
public float LerpTime = 1f;
private float CurrentLerpTime = 0;
private void Start()
{
ResetPositions();
}
void Update()
{
if (Input.GetKeyDown("space"))
{
CurrentLerpTime += Time.unscaledDeltaTime;
if (CurrentLerpTime >= LerpTime)
{
ResetPositions();
return;
}
float LerpPercentage = CurrentLerpTime / LerpTime;
transform.position = Vector3.Lerp(StartingPosition, EndingPosition, LerpPercentage);
}
}
void ResetPositions()
{
StartingPosition = transform.position;
EndingPosition = transform.position + Vector3.forward * moveDistance;
CurrentLerpTime = 0;
}

IEnumerator not waiting for seconds

I have created an IEnumerator so that the enemy doesn't kill the player immediately on contact. the code worked fine yesterday, but now the IEnumerator seems to be completely ignored. The Unity 3d console is not showing any errors either.
I have tried to lower the damage amount to check if maybe it was too high but it wasn't the case.
the following is the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class enemyAI : MonoBehaviour {
[SerializeField]
float chaseDistance = 5.0f;
public float damageAmount = 1.0f;
void Update () {
dist = Vector3.Distance(target.position, transform.position);
float distance = Vector3.Distance(transform.position, target.position);
if (distance < chaseDistance )
{
AttackPlayer();
}
}
void AttackPlayer()
{
agent.updateRotation = false;
Vector3 direction = target.position - transform.position;
direction.y = 0;
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(direction), turnSpeed * Time.deltaTime);
agent.updatePosition = false;
anim.SetBool("isWalking", false);
anim.SetBool("isAttacking", true);
StartCoroutine(AttackTime());
}
IEnumerator AttackTime()
{
canAttack = false;
yield return new WaitForSeconds(0.5f);
Player.singleton.Damage(damageAmount);
yield return new WaitForSeconds(2.0f);
canAttack = true;
}
}
//Player Script {
public class Player : MonoBehaviour {
public static Player singleton;
public float currentHealth;
public static float maxHealth = 100f;
public bool isDead = false;
private void Awake()
{
singleton = this;
}
// Use this for initialization
void Start () {
currentHealth = maxHealth;
}
// Update is called once per frame
void Update () {
if (currentHealth < 0)
{
currentHealth = 0;
}
}
public void Damage(float damage)
{
if(currentHealth > 0)
{
currentHealth -= damage;
}
else
{
currentHealth = 0;
}
}
void Dead()
{
currentHealth = 0;
isDead = true;
}
}
You are starting the "AttackPlayer" Coroutine in "Update()" - so when the enemy is in range, you will start ~60 Coroutines per second. While you want a single one.
You are already setting "canAttack" to "false" - maybe add "&& canAttack" to your range-condition in Update?
Like
if (distance < chaseDistance && canAttack)
{
AttackPlayer();
}
Try putting "canAttack = false;" beneath yield command

Categories