Enemy follow player when player is not looking. OnBecameInvisible not working - c#

So I want to create a weeping angels effect on my enemies in my game. So when the player can see the enemy, they don't move and when the player can't see them, they move closer to the player. This is my code thats attached to the enemy and its not working. Would really appreciate some help!
using System.Collections.Generic;
using UnityEngine;
public class WeepingAngel : MonoBehaviour
{
public GameObject Player;
// Start is called before the first frame update
void Start()
{
Player = GameObject.FindGameObjectWithTag("Player");
}
void OnBecameInvisible()
{
if (Player)
{
transform.position = Player.transform.position - Player.transform.forward;
Vector3 lookPos = Player.transform.position - transform.position;
lookPos.y = 0;
transform.rotation = Quaternion.LookRotation(lookPos);
}
}
}

I'm gonna assume that the OnBecameInvisible doesn't get called in situations where you think it should. The reason why that may happen is because it will react to ANY camera. That includes even the scene view.
Alternative solution
Instead of OnBecameInvisible you can use the method available in this wiki article:
http://wiki.unity3d.com/index.php?title=IsVisibleFrom
Modified version of your code
using UnityEngine;
public class WeepingAngel : MonoBehaviour
{
public GameObject player;
private new Camera camera;
private new Renderer renderer;
private void Start()
{
player = GameObject.FindGameObjectWithTag("Player");
camera = Camera.main;
renderer = GetComponent<Renderer>();
}
private void Update()
{
bool isVisible = GeometryUtility.TestPlanesAABB(
GeometryUtility.CalculateFrustumPlanes(camera),
renderer.bounds);
if (!isVisible)
TryMovingTowardsPlayer();
}
private void TryMovingTowardsPlayer()
{
if (player == null)
return;
transform.position = player.transform.position - player.transform.forward;
Vector3 lookPos = player.transform.position - transform.position;
lookPos.y = 0;
transform.rotation = Quaternion.LookRotation(lookPos);
}
}

Related

Sprite not Appearing in Unity 2D Game

I'm creating a 2D Top Down game for practice and I need a little bit of help. For context, it's a 2D Top Down Shooter game, where you can move and shoot enemies. The enemies have a basic radius system where if the player gets within the radius, it'll approach the player.
Now I'm making a game mechanic where the player can hide in a cardboard box, the player can press 'E' and he'll suddenly become a cardboard box, where if the player is in the cardboard box, the enemy doesn't detect him even if the player's within the radius. Yes, just like in Metal Gear. Now I've created the prefabs and everything and functionality-wise, it works perfectly. If you press 'E' the enemy cannot detect you.
Now the small problem is that the cardboard box didn't appear, so it's just the player disappearing entirely. I do not know what caused this problem.
For context, these are my scripts. Feel free to read them, or not :)
PlayerController:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public float moveSpeed;
public GameObject bulletPrefab;
public GameObject player;
private Rigidbody2D rb2d;
private Vector2 moveDirection;
[SerializeField] private Camera cam;
[SerializeField] private GameObject gunPoint;
public bool isHiding = false;
[SerializeField] private GameObject cardboardBox;
[SerializeField] private GameObject gunSprite;
// Start is called before the first frame update
void Start()
{
rb2d = GetComponent<Rigidbody2D>();
cardboardBox.SetActive(false); // Hide the cardboard box when the game starts
gunSprite.SetActive(true); // Show the gun sprite when the game starts
}
// Update is called once per frame
void Update()
{
CheckCursor();
ProcessInputs();
// Make the camera follow the player
cam.transform.position = new Vector3(player.transform.position.x, player.transform.position.y, cam.transform.position.z);
// Check if player pressed the "E" key to toggle the cardboard box
if (Input.GetKeyDown(KeyCode.E))
{
isHiding = !isHiding; // Toggle the isHiding variable
cardboardBox.SetActive(isHiding); // Show/hide the cardboard box accordingly
// If player is hiding, stop player movement
if (isHiding)
{
moveDirection = Vector2.zero;
player.GetComponent<SpriteRenderer>().enabled = false;
cardboardBox.GetComponent<SpriteRenderer>().enabled = true;
gunSprite.SetActive(false); // Hide the gun sprite when the player is hiding
}
else
{
player.GetComponent<SpriteRenderer>().enabled = true;
cardboardBox.GetComponent<SpriteRenderer>().enabled = false;
gunSprite.SetActive(true); // Show the gun sprite when the player is not hiding
}
}
}
private void FixedUpdate()
{
if (!isHiding) // Only allow player to move if they are not hiding in the cardboard box
{
Movement();
}
}
private void CheckCursor()
{
Vector3 mousePos = cam.ScreenToWorldPoint(Input.mousePosition);
Vector3 characterPos = transform.position;
if (mousePos.x > characterPos.x)
{
this.transform.rotation = new Quaternion(0, 0, 0, 0);
}
else if (mousePos.x < characterPos.x)
{
this.transform.rotation = new Quaternion(0, 180, 0, 0);
}
}
private void Movement()
{
// TODO : Implementasi movement player
rb2d.velocity = new Vector2(moveDirection.x * moveSpeed, moveDirection.y * moveSpeed);
}
private void ProcessInputs()
{
float moveX = Input.GetAxisRaw("Horizontal");
float moveY = Input.GetAxisRaw("Vertical");
moveDirection = new Vector2(moveX, moveY);
if (Input.GetMouseButtonDown(0))
{
Shoot();
}
}
private void Shoot()
{
Vector3 mousePos = cam.ScreenToWorldPoint(Input.mousePosition);
Vector3 gunPointPos = gunPoint.transform.position;
Vector3 direction = (mousePos - gunPointPos).normalized;
GameObject bullet = Instantiate(bulletPrefab, gunPointPos, Quaternion.identity);
bullet.GetComponent<Bullet>().Init(direction);
}
}
EnemyController script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyController : MonoBehaviour
{
public Transform player;
public float moveSpeed = 5f;
public float detectionRadius = 5f;
public int maxHealth = 1;
private int currentHealth;
private Rigidbody2D rb2d;
private Vector2 movement;
private void Start()
{
rb2d = this.GetComponent<Rigidbody2D>();
currentHealth = maxHealth;
}
private void Update()
{
float distanceToPlayer = Vector2.Distance(transform.position, player.position);
if (player != null && distanceToPlayer <= detectionRadius && !player.GetComponent<PlayerController>().isHiding)
{
Vector3 direction = player.position - transform.position;
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
rb2d.rotation = angle;
direction.Normalize();
movement = direction;
}
else
{
movement = Vector2.zero;
}
}
void FixedUpdate()
{
moveCharacter(movement);
}
private void moveCharacter(Vector2 direction)
{
rb2d.MovePosition((Vector2)transform.position + (direction * moveSpeed * Time.deltaTime));
}
public void DestroyEnemy()
{
Destroy(gameObject);
}
}
Bullet script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : MonoBehaviour
{
// Start is called before the first frame update
[SerializeField] private float speed;
[SerializeField] private Vector3 direction;
public void Init(Vector3 direction)
{
this.direction = direction;
this.transform.SetParent(null);
transform.rotation = Quaternion.Euler(0, 0, Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg);
}
// Update is called once per frame
void Update()
{
this.transform.position += transform.right * speed * Time.deltaTime;
}
// TODO : Implementasi behaviour bullet jika mengenai wall atau enemy
private void OnTriggerEnter2D(Collider2D other)
{
switch(other.gameObject.tag)
{
case "Wall":
Destroy(gameObject);
break;
case "Enemy":
Destroy(gameObject);
other.gameObject.GetComponent<EnemyController>().DestroyEnemy();
break;
}
}
}
I've tried tinkering my scripts, I've tried checking if there are any missing components in the cardboard box game object but to no avail. Although I might be wrong on the Unity part since I'm fairly certain that the script isn't the problem here, again might be wrong.
I appreciate all the help I can get, thank you for reading until here

How to throw a ball that player picked up in Unity 2d project

I have a problem with writing a function that throws spawned ball by pressing a button.
So far, i could only instantiate the ball and it is affected by gravity.
I can not wrap my head arround, how in my code should i add a force to push it with some force. Right now it just plops in front of a player.
I tried to fiddle with .AddForce() on a ball rigidbody2D and still nothing.
I would greatly appreciate if someone could take a look at the code and guide me into some directcion on where to AddForce for ball to make it move?
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerControl : MonoBehaviour
{
[Header("Player Atributes")]
[SerializeField] float runSpeed = 10f;
Vector2 moveInput;
Rigidbody2D playerRigidbody2D;
[Header("Ball Settings")]
public bool playerHasABall = false;
[SerializeField] GameObject ball;
[SerializeField] Transform ballSpawn;
void Start()
{
playerRigidbody2D = GetComponent<Rigidbody2D>();
}
void Update()
{
Run();
FlipSprite();
}
void OnMove(InputValue value)
{
moveInput = value.Get<Vector2>();
}
void OnFire(InputValue value)
{
if(!playerHasABall) {return;}
Instantiate(ball, ballSpawn.position, transform.rotation);
playerHasABall = false;
}
void Run()
{
Vector2 playerVelocity = new Vector2 (moveInput.x * runSpeed, playerRigidbody2D.velocity.y);
playerRigidbody2D.velocity = playerVelocity;
}
void FlipSprite()
{
bool playerHasHorizontalSpeed = Mathf.Abs(playerRigidbody2D.velocity.x) > Mathf.Epsilon;
if (playerHasHorizontalSpeed)
{
transform.localScale = new Vector2 (Mathf.Sign(playerRigidbody2D.velocity.x), 1f);
}
}
//Ball picking up script. It destroys the ball with a tag "Ball" and sets a bool playerHasABall to true
private void OnTriggerEnter2D(Collider2D other)
{
if(other.tag == "Ball")
{
Destroy(other.gameObject);
playerHasABall = true;
}
}
}
So, i manage to fiddle with the .AddForce() method once again and i came up with a solution to my problem.
My OnFire script looks like this:
void OnFire(InputValue value)
{
if(!playerHasABall) {return;}
Instantiate(ball, ballSpawn.position, transform.rotation);
playerHasABall = false;
}
No change here. I made another script called "BallBehaviour.cs" and added it to the ball with a code like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BallBehaviour : MonoBehaviour
{
[SerializeField] float ballForce = 20f;
PlayerControl player;
void Start()
{
player = FindObjectOfType<PlayerControl>();
Throw();
}
public void Throw()
{
GetComponent<Rigidbody2D>().AddForce(player.transform.localScale * ballForce, ForceMode2D.Impulse);
}
}
Basically, the method Throw() tells the ball to check the direction the player is facing and then multiply it by ballForce. Then, it uses the ForceMode2d.Impulse method to apply calculated force to the ball for a second and it works on left or right direction.
Thanks Jkimishere for a nudge in a propper direction!
You might need a variable to check if the player is facing right/left.
If the player is, just add force to the ball depending on the direction.
if(!playerHasABall) {return;}
Instantiate(ball,
ballSpawn.position,
transform.rotation);
//Check which way the player is facing and add forrce to that direction.
//Adding a FacingRight boolean might help.....
playerHasABall = false;

I'm trying to make a Instance at the mouse position but it doesn't work

I'm trying to make an instance at the mouse pos, but when it makes the instance, it's way off and is really far out from my mouse. How could I fix this? Here is my code:
using UnityEngine;
using System.Collections;
// note
// note
public class attack : MonoBehaviour
{
public Transform prefab;
void Update()
{
Vector3 mousePos = Input.mousePosition;
if (Input.GetMouseButtonDown(0))
Instantiate(prefab, mousePos, Quaternion.identity);
}
}
Input.mousePosition is in screen pixel space!
You most probably rather want to use Camera.ScreenToWorldSpace
public class attack : MonoBehaviour
{
public Transform prefab;
// you will need to figure this out
public float desiredDistanceInFrontOfCamera;
[SerializeFiel] private Camera _camera;
private void Awake()
{
if(!_camera) _camera = Camera.main;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector3 mousePos = Input.mousePosition;
mousePos.z = desiredDistanceInFrontOfCamera;
Vector3 spawnPos = _camera.ScreenToWorldPoint(mousePos);
Instantiate(prefab, spawnPos, Quaternion.identity);
}
}
}

Unity prefab movement

Hello i want to create a group of the same prefab following the player in my game. I already got the prefab intantiation to follow the player but when there is more than one they just follow the exact same path on top of each other. is there a way where they can follow the player but act like a bunch of bees moving?
Thanks!
This is the script on my prefab:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class KillerMovement : MonoBehaviour {
public GameObject player;
void FixedUpdate()
{
Vector2 toTarget = player.transform.position - transform.position;
float speed = 0.5f;
transform.Translate(toTarget * speed * Time.deltaTime);
}
}
The best solution depends on you game logic, but you may consider having a delay before starting following (you can tweak the delay depending on the position you want to the particular object to assume in the trail.
using System.Collections;
using UnityEngine;
public class Follower : MonoBehaviour
{
public GameObject player;
public float delay = 0f;
public float speed = .5f;
bool isReady = false;
void Start()
{
StartFollowing();
}
public void StartFollowing()
{
StartCoroutine(WaitThenFollow(delay));
}
IEnumerator WaitThenFollow(float delay)
{
yield return new WaitForSeconds(delay);
isReady = true;
Debug.Log(gameObject.name);
Debug.Log(Time.time);
}
void FixedUpdate()
{
if (isReady && player != null)
{
Vector2 toTarget = player.transform.position - transform.position;
transform.Translate(toTarget * speed * Time.deltaTime);
}
}
}
I've called StartFollowing in the Start method for you to test the code. You should call this method whenever approrpiate in your game logic.

How can i make that the user can select if the camera will be behind the player or not?

This is the original code make the camera follow the player:
using UnityEngine;
using System.Collections;
public class CameraFollow : MonoBehaviour
{
public GameObject objectToFollow; //Public variable to store a reference to the player game object
private Vector3 offset; //Private variable to store the offset distance between the player and camera
// Use this for initialization
void Start()
{
//Calculate and store the offset value by getting the distance between the player's position and camera's position.
offset = transform.position - objectToFollow.transform.position;
}
// LateUpdate is called after Update each frame
void LateUpdate()
{
// Set the position of the camera's transform to be the same as the player's, but offset by the calculated offset distance.
transform.position = objectToFollow.transform.position + offset;
transform.LookAt(objectToFollow.transform);
}
}
And this is what i tried to do but then the player it self(ThirdPersonController) is not rotating according to where he move.
With the original script above he does.
using UnityEngine;
using System.Collections;
public class CameraFollow : MonoBehaviour
{
public GameObject objectToFollow; //Public variable to store a reference to the player game object
public bool behindPlayer = false;
private Vector3 cameraStartPos;
// Use this for initialization
void Start()
{
cameraStartPos = transform.position;
// Put the camera behind the player
if (behindPlayer == true)
{
transform.position = (objectToFollow.transform.position - (objectToFollow.transform.forward * 5) + (objectToFollow.transform.up * 2));
}
}
private void Update()
{
if (behindPlayer == true)
{
transform.position = (objectToFollow.transform.position - (objectToFollow.transform.forward * 5) + (objectToFollow.transform.up * 2));
}
else
{
transform.position = cameraStartPos;
}
}
// LateUpdate is called after Update each frame
void LateUpdate()
{
transform.position = objectToFollow.transform.position;
transform.LookAt(objectToFollow.transform);
}
}
I want that the camera will be automatic behind the player already without the need to change the camera position in the scene if the user want by using the bool variable.
But the script now is not like in the above:
With this line in the LateUpdate:
transform.position = objectToFollow.transform.position;
Using the bool false/true it's not changing the camera position at all.
Without this line the player will move and the camera will follow but the player will not rotate and not move as above.
What i want is just like the first script but with the behindPlayer variable.
A working script:
using UnityEngine;
using System.Collections;
public class CameraFollow : MonoBehaviour
{
[SerializeField]
private Transform target;
[SerializeField]
private Vector3 offsetPosition;
[SerializeField]
private Space offsetPositionSpace = Space.Self;
[SerializeField]
private bool lookAt = true;
private void Update()
{
Refresh();
}
public void Refresh()
{
if (target == null)
{
Debug.LogWarning("Missing target ref !", this);
return;
}
// compute position
if (offsetPositionSpace == Space.Self)
{
transform.position = target.TransformPoint(offsetPosition);
}
else
{
transform.position = target.position + offsetPosition;
}
// compute rotation
if (lookAt)
{
transform.LookAt(target);
}
else
{
transform.rotation = target.rotation;
}
}
}
In the inspector set the offset for example to 0,5,-12 and as target set for example ThirdPersonController. And attach the script to the Camera(Camera must be tagged as MainCamera if the target is ThirdPersonController since the ThirdPersonController ThirdPersonUserControl script is looking for the MainCamera).
And the camera doesn't have to be a child of the target the camera can be in any place in the Hierarchy.
Anyway it's working.

Categories