Unity prefab movement - c#

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.

Related

How do I change an object based on Camera rotation in Unity?

I'm trying to make a 2D sprite flip in a 3D scene, whenever the camera reaches an Y rotation of 180.
I can't get it to work, even though the TEST debug text shows whenever it reaches 180.
This is the code I have:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rotate_enemy : MonoBehaviour {
[SerializeField] SpriteRenderer spriteRenderer;
public bool flipX;
void Update()
{
float fYRot = Camera.main.transform.eulerAngles.y;
if (fYRot >= 180)
{
Debug.Log("TESTING");
spriteRenderer.flipX = true;
}
else
{
spriteRenderer.flipX = false;
}
}
}
I have a similiar script, that instead changes rotation based on velocity (the direction to which the character is going), and that script works just fine.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Sprite_flip : MonoBehaviour {
[SerializeField] SpriteRenderer spriteRenderer;
public bool flipX;
Vector3 pos, velocity;
void Awake()
{
pos = transform.position;
}
// Update is called once per frame
void Update()
{
velocity = (transform.position - pos) / Time.deltaTime;
pos = transform.position;
if (velocity.x >= 0)
{
spriteRenderer.flipX = true;
}
else
{
spriteRenderer.flipX = false;
}
}
}
Anyone who might have an idea why the Rotate_Enemy script isn't doing what I want to achieve? Tried to find solutions but I couldn't make any of them work, so there must be something I don't understand. Super grateful for any help! :)

Destroyed Bullet Won't Make Clones and Shoot After around 3 seconds

I made a script that shoots a bullet and destroys it after 3 seconds, however it destroys the original bullet after it is shot which makes unity unable to make copies of it to shoot another bullet, An okay solution might be to make a copy of the bullet and shoot the copy however I do not know how to do that.
This is the script for the gun
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunShootingScript : MonoBehaviour
{
public ParticleSystem MuzzleFlash;
public float damage = 10f;
public float range = 100f;
public GameObject bulletPrefab;
public float bulletLife = 3f;
public Camera playerCamera;
private void Update()
{
if(Input.GetKeyDown(KeyCode.Mouse0))
{
Destroy(bulletPrefab, bulletLife);
Shoot();
}
}
void Shoot()
{
MuzzleFlash.Play();
Instantiate(bulletPrefab, playerCamera.transform.position, playerCamera.transform.rotation);
RaycastHit hit;
if(Physics.Raycast(playerCamera.transform.position, playerCamera.transform.forward, out hit, range))
{
TakeDamage target = hit.transform.GetComponent<TakeDamage>();
if(target != null)
{
target.GiveDamage(damage);
}
}
}
}
This is the script for the bullet.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Shot : MonoBehaviour
{
// Start is called before the first frame update
public float speed = 3000f;
void Update()
{
transform.position += transform.forward * speed * Time.deltaTime;
}
}
From what I see, you destroy the bulletPrefab but you never actually assign an object for it. You can have a separate GameObject and do that when instantiating inside the Shoot method. Try this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunShootingScript : MonoBehaviour
{
public ParticleSystem MuzzleFlash;
public float damage = 10f;
public float range = 100f;
public GameObject bulletPrefab;
public float bulletLife = 3f;
private GameObject bulletShot // This is what I added
public Camera playerCamera;
private void Update()
{
if(Input.GetKeyDown(KeyCode.Mouse0))
{
Destroy(bulletShot); // Removed the timer from here
Shoot();
}
}
void Shoot()
{
MuzzleFlash.Play();
bulletShot = Instantiate(bulletPrefab, playerCamera.transform.position, playerCamera.transform.rotation); // Assigned the bullet to the separate GameObject
RaycastHit hit;
if(Physics.Raycast(playerCamera.transform.position, playerCamera.transform.forward, out hit, range))
{
TakeDamage target = hit.transform.GetComponent<TakeDamage>();
if(target != null)
{
target.GiveDamage(damage);
}
}
}
}
That way, when you try to destroy it, there will actually be something to destroy. However, this will create a new issue. The previously instantiated object will be destroyed instantly when you shoot again. You can fix that by creating a list and adding the bullets there. However, that is NOT efficient. And since you are going to start all over, I suggest you go for an Object Pool. What is that? Glad you asked!
There is a very good video by Jason Weimann on YouTube about object pooling. You might want to give it a go, it is old but definitely not outdated.
https://www.youtube.com/watch?v=uxm4a0QnQ9E
You might need to keep a reference to the shot bullet, to be able to destroy with a coroutine after 3 seconds, it later on like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunShootingScript : MonoBehaviour
{
public ParticleSystem MuzzleFlash;
public float damage = 10f;
public float range = 100f;
public GameObject bulletPrefab;
public float bulletLife = 3f;
private Queue<GameObject> myQ = new Queue<GameObject>(); //queue to keep track of the shot bullet and handle the delayed destroy
private IEnumerator coroutine;
void Start() {
coroutine = DelayedDestroy(3f);
}
public Camera playerCamera;
private void Update()
{
if(Input.GetKeyDown(KeyCode.Mouse0))
{
//Destroy(bulletPrefab, bulletLife); Commented to avoid immediate destruction
Shoot();
}
}
void Shoot()
{
MuzzleFlash.Play();
myQ.Enqueue(Instantiate(bulletPrefab, playerCamera.transform.position, playerCamera.transform.rotation));
RaycastHit hit;
if(Physics.Raycast(playerCamera.transform.position, playerCamera.transform.forward, out hit, range))
{
TakeDamage target = hit.transform.GetComponent<TakeDamage>();
if(target != null)
{
target.GiveDamage(damage);
}
}
StartCoroutine(coroutine);
}
private IEnumerator DelayedDestroy(float waitTime) {
yield return new WaitForSeconds(waitTime);
GameObject nullcheck = myQ.Dequeue();
if (nullcheck != null) { //in case the queue is empty
Destroy(nullcheck );
}
}
}
Did not debug that, it is to give you the idea. You can check the Queue data structure and coroutines.
Check pooling to achieve what you are making even better :)

Enemy follow player when player is not looking. OnBecameInvisible not working

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);
}
}

Firing Projectiles in Unity

Im trying to build a weapon in a game using Unity. My bullets spawn but i cant seem to get the force to apply on instantiation to get them to actually fire.
My weapon script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Weapon : MonoBehaviour {
public Rigidbody2D projectile;
public float forceMultiplier;
public Vector2 direction;
public Transform firePoint;
private float timeBtwShots;
public float startTimeBtwShots;
public void Fire(float force, Vector2 direction)
{
Instantiate(projectile, firePoint.position, transform.rotation);
projectile.AddForce(direction * force);
}
// Update is called once per frame
void Update () {
if (timeBtwShots <= 0)
{
if (Input.GetKeyDown(KeyCode.Return))
{
Fire(forceMultiplier, direction);
timeBtwShots = startTimeBtwShots;
}
}
else
{
timeBtwShots -= Time.deltaTime;
}
}
}
You need to add the force to the spawned object and not the prefab.
Your code should be something like this:
public void Fire(float force, Vector2 direction)
{
Rigidbody2D proj = Instantiate(projectile, firePoint.position, transform.rotation);
proj.AddForce(direction * force);
}

unity3D, enemy following issue

I'm trying to make enemies follow my player when the player enters the radius area of an enemy, but make the enemy stop following when my bullet hits object or enters radiusArea.
See my gif for more detail:
Gif
script:
using UnityEngine;
using System.Collections;
public class FlyEnemyMove : MonoBehaviour
{
public float moveSpeed;
public float playerRange;
public LayerMask playerLayer;
public bool playerInRange;
PlayerController thePlayer;
// Use this for initialization
void Start()
{
thePlayer = FindObjectOfType<PlayerController>();
}
// Update is called once per frame
void Update()
{
flip();
playerInRange = Physics2D.OverlapCircle(transform.position, playerRange, playerLayer);
if (playerInRange)
{
transform.position = Vector3.MoveTowards(transform.position, thePlayer.transform.position, moveSpeed * Time.deltaTime);
//Debug.Log(transform.position.y);
}
//Debug.Log(playerInRange);
}
void OnDrawGizmosSelected()
{
Gizmos.DrawWireSphere(transform.position, playerRange);
}
void flip()
{
if (thePlayer.transform.position.x < transform.position.x)
{
transform.localScale = new Vector3(0.2377247f, 0.2377247f, 0.2377247f);
}
else
{
transform.localScale = new Vector3(-0.2377247f, 0.2377247f, 0.2377247f);
}
}
}
I hope someone can help me :(
Physics2D.OverlapCircle detects only the collider with the lowest z value (if multiple are in range). So you either need to change the z values so the player has the lowest or you need to work with Physics2D.OverlapCircleAll and check the list to find the player. Or you could change your layers so only the player itself is on that specific layer you feed into the overlap test.

Categories