I'm having a little problem on passing parameter to an object when I instantiate it, let me explain it better:
I made an automatic turret in unity, it just aim an enemy and instantiate a missile that destroy the enemy. The turret works great, I'm having some problems only with the missile...
The missile is a game object with a script attached at, in this script I have only 1 public variable the type is "GameObject" and the name is "target", when (from the turret script) I instantiate the missile, I set the variable "target" equal to the enemy the turret is aiming at.
Into the start function (missile's script) I rotate the missile towards the "target" and, incrementing the position of the missile it can hit the enemy.
Missile script:
public GameObject target;
private Vector3 targetPosition;
// Use this for initialization
void Start () {
transform.SetParent(GameObject.FindGameObjectWithTag("Canvas").transform);
targetPosition = target.transform.position;
//Rotation of the missile
Vector3 difference = this.targetPosition - transform.position;
float rotationZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0.0f, 0.0f, rotationZ);
}
// Update is called once per frame
void Update () {
// The step size is equal to speed times frame time.
float step = 1.5f * Time.deltaTime;
// Move our position a step closer to the target.
transform.position += transform.right * step;
}
private void OnCollisionEnter2D(Collision2D collision)
{
Destroy(collision.gameObject);
Destroy(this.gameObject);
}
Turret script
public GameObject missile; //this is the missile prefab
void OnTriggerEnter2D(Collider2D collision)
{
if(collision.gameObject.tag == "enemy") //The turret is aiming an enemy
{
Target = collision.gameObject;
StartCoroutine(openFire());
}
}
IEnumerator openFire()
{
GameObject newMissile;
newMissile = Instantiate(missile, this.transform.position, Quaternion.identity, GameObject.FindGameObjectWithTag("Canvas").transform);
missile.GetComponent<missileScript>().target = Target;
yield return new WaitForSeconds(1);
if (Target != null)
StartCoroutine(openFire());
}
Everything works fine, the problem arises when I have more than one turret: If I have two turret (turret A and turret B) and at the same time both turrets shoots to two different enemies, for some reasons, both missiles will have the same target.
So, for example if the turret "A" shoots to the enemy "1", and, at the same time, the turret "B" shoots to the enemy "2", both missiles will go toward the same enemy.
I hope I was clear. Any ideas on what the problem could be?
Thanks.
-----FIXED-----
I simply have edited the variable "target" by public to private and I made a "setter" public method.
Sincerely I don't know why it fixed my problem...
You're overwriting your prefab:
missile = Instantiate(missile, ...)
You do declare a GameObject newMissile but you're not using it.
Second:
if (Target != null)
You're restarting the OpenFire method if the target isn't null...but you still instantiate a missile! You probably want to change this.
Related
I am trying to make a projectile that spawns and when it hits the player he gets destroyed. I have to mention that the projectile would be spawned with the "Instantiate" command making it a "cloned gameobject". In the script I wrote that if the projectile would hit another gameobject with the tag "player" the gameobject it hits would get destroyed but after running the code and the projectile hit the player he didn't get destroyed. I checked and the tag does say "player". I threw in a debug command into the code and managed to find out that the tag doesn't get detected. The script for the projectile spawner and the projectile itself are separate so I'm going to only show the projectile script since it is the problematic script. I have to mention that the script doesn't generate any errors and that the simulation runs fine except for the things I have mentioned above.
public class Bulletboi : MonoBehaviour
{
public float speed;
private Transform player;
private Vector2 target;
public GameObject Elven;
void Start()
{
player = GameObject.FindGameObjectWithTag("player").transform;
target = new Vector2(player.position.x, player.position.y);
}
void Update()
{
transform.position = Vector2.MoveTowards(transform.position, target, speed * Time.deltaTime);
if(transform.position.x == target.x && transform.position.y == target.y)
{
DestroyProjectile();
}
}
void OnEnterTrigger2D(Collision2D other)
{
if (other.gameObject.tag.Equals("player"))
{
Debug.Log("bbbb");
DestroyProjectile();
Destroy(other.gameObject);
}
}
void DestroyProjectile()
{
Destroy(gameObject);
}
}
Never mind I decided to change the script a little bit and I put it on the player and made it detect the tag of the projectile and now it works.
I am having trouble getting the enemy's projectile to fly from the enemy to the player's position. When I play the game, the enemy bullet projectiles fly off in one direction on the screen and not toward the player. I think the issue might be in how I am assigning direction to the projectile prefab? Any suggestions would be much appreciated.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyController : MonoBehaviour
{
public float speed;
public Rigidbody enemyRb;
[SerializeField] float rateOfFire;
private GameObject player;
public GameObject projectilePrefab;
float nextFireAllowed;
public bool canFire;
Transform enemyMuzzle;
void Awake()
{
enemyRb = GetComponent<Rigidbody>();
player = GameObject.Find("Player");
enemyMuzzle = transform.Find("EnemyMuzzle");
}
void Update()
{
//move enemy rigidbody toward player
Vector3 lookDirection = (player.transform.position - transform.position).normalized;
enemyRb.AddForce(lookDirection * speed);
//overallSpeed
Vector3 horizontalVelocity = enemyRb.velocity;
horizontalVelocity = new Vector3(enemyRb.velocity.x, 0, enemyRb.velocity.z);
// turns enemy to look at player
transform.LookAt(player.transform);
//launches projectile toward player
projectilePrefab.transform.Translate(lookDirection * speed * Time.deltaTime);
Instantiate(projectilePrefab, transform.position, projectilePrefab.transform.rotation);
}
public virtual void Fire()
{
canFire = false;
if (Time.time < nextFireAllowed)
return;
nextFireAllowed = Time.time + rateOfFire;
//instantiate the projectile;
Instantiate(projectilePrefab, enemyMuzzle.position, enemyMuzzle.rotation);
canFire = true;
}
}
It looks like what is actually happening is that you create a bunch of bullets but don't store a reference to them. So each bullets sits in one place while the enemy moves closer to the player ( which might give the appearance that the bullets are moving relative to the enemy. ) I also assume the enemy is moving very fast since it is not scaled by delta time but is being updated every frame.
I think projectilePrefab is just the template object you're spawning, so you probably don't want to move it directly and you certainly don't want to instantiate a new bullet every frame.
If you want to move the object you spawned the least changes ( but still problematic ) from your example code might be:
public class EnemyController : MonoBehaviour
{
// add a reference
private GameObject projectileGameObject = null;
void Update()
{
//Update position of spawned projectile rather than the template
if(projectileGameObject != null ) {
projectileGameObject.transform.Translate(lookDirection * speed * Time.deltaTime);
}
// Be sure to remove this extra instantiate
//Instantiate(projectilePrefab, transform.position, projectilePrefab.transform.rotation);
}
public virtual void Fire()
{
//instantiate the projectile
projectileGameObject = Instantiate(projectilePrefab, enemyMuzzle.position, enemyMuzzle.rotation);
}
}
Or keep multiple bullets in a list. This implementation has the bug that it will always use the current enemy to player vector as the direction rather than the direction that existed when it was fired.
What you will probably want eventually is that each projectile is has it's own class script to handle projectile logic. All the enemyController class has to do is spawn the projectile and sets it's direction and position on a separate monobehavior that lives on the Projectile objects that handles it's own updates.
When I set the speed of Ball = 10 OnTriggerEnter2D to test the ball hit on the floor work fine, but when I set the speed higher (20), OnTriggerEnter2D doesn't work and the ball falls down through the floor
My code:
void Start () {
rigiBody = GetComponent<Rigidbody2D>();
ballLayer = 1 << LayerMask.NameToLayer("Ball");
}
void OnTriggerEnter2D(Collider2D other) {
if (other.CompareTag(Constants.FLOOR_TAG))
{
Debug.Log("FLOOR_TAG");
if (HitFloor != null)
HitFloor(this);
}
}
void FixedUpdate() {
Vector2 tempVect = Direction;
tempVect = tempVect.normalized * Speed * Time.deltaTime;
Vector2 newPos = rigiBody.position + tempVect;
rigiBody.MovePosition(newPos);
timer += Time.deltaTime;
RaycastHit2D hit = Physics2D.Raycast(newPos, Direction, Speed * Time.deltaTime * 1.2f, ~(ballLayer));
if (!hit)
return;
...
Inspector of Ball below
What's wrong with this code?
ps I'm using Unity 2017.1.1f1 Personal
You have to change the "Collision Detection" property of rigidbody. It should be "continuous" not "discrete". If you choose discrete value you are telling rigidbody to check collision in discrete time intervals. If you move in a high speed, rigidbody will probably miss collision.
Set the collision detection mode in the Rigidbody2D component to Continuous. Documentation
And maybe changing
RaycastHit2D hit = Physics2D.Raycast(newPos, Direction, Speed * Time.deltaTime * 1.2f, ~(ballLayer));
to
RaycastHit2D hit = Physics2D.Raycast(newPos, Direction, Speed * 1.2f, ~(ballLayer));
will fix the problem aswell.
Why do you cast the Ray from the newPosition? Imo. You should cast it from its current Position.
Solution of my problem was really close
A couple of lines were added and changed
add [RequireComponent(typeof(Rigidbody2D))] for class Ball
replace variable RigiBody to property
Now everything work well, and balls not fall through the floor on high speed
Code after changes look like this
[RequireComponent(typeof(Rigidbody2D))] // Added this code
public class Ball : MonoBehaviour {
private Rigidbody2D _rigiBody;
public Rigidbody2D RigidBody { //And this property
get {
if (_rigiBody == null)
_rigiBody = GetComponent<Rigidbody2D>();
return _rigiBody;
}
}
I got a small arena where the player can move on. At the sides of the area there are spawners. These spawners instantiate bombs and should throw them at the player.
For the direction I actually use
transform.lookAt(playerTransform);
So this is a rough map
So the spawners are rotating around the map. They move from one point to the next point.
My bomb object got a rigidbody attached and the gravity is activated. I just need to find out how to make a spawner throwing a bomb to the player.
public class BombSpawner : MonoBehaviour
{
[Range(0, 3)]
[SerializeField]
private int nextPointIndex; // set the first targetpoint
private Vector3[] targetPoints = {
new Vector3(-15,0,15),
new Vector3(15,0,15),
new Vector3(15,0,-15),
new Vector3(-15,0,-15)};
private float movementSpeed = 10;
GameObject bombPrefab;
Transform player;
private void Start()
{
bombPrefab = Resources.Load(StringCollection.BOMB) as GameObject;
player = Globals.GetPlayerObject().transform;
}
private void Update()
{
transform.LookAt(player); // set the object rotation
Vector3 nextPoint = targetPoints[nextPointIndex]; // get the target point
transform.position = Vector3.MoveTowards(transform.position, nextPoint, movementSpeed * Time.deltaTime); // move the spawner
if (transform.position == nextPoint) // point reached? set a new point
{
if (nextPointIndex < targetPoints.Length - 1)
nextPointIndex++;
else
nextPointIndex = 0;
}
}
}
So I could write a method like this
void SpawnBomb()
{
GameObject spawnedBomb = Instantiate(bombPrefab);
}
but how do I achieve the throwing mechanic? For a first try, the targetPoint is player.position that should be fine.
You need to get the direction from spawner to the current position of the target, spawn the bomb and add force to that bomb using the direction you just got.
In order to do this you should substract your spawner's position from your target position.
Vector3 dir = target.transform.position - transform.position;
Now that you have the direction you can spawn your bomb and AddForce() to it. To add force you need to call the Rigidbody component of your spawned Bomb, like this:
spawnedBomb.GetComponent<Rigidbody>().AddForce(dir.normalized * force, ForceMode.Impulse);
Where dir is the direction towards the target (normalized - so the distance doesn't matter) and force is pretty much the speed of the bomb.
Here you can read more about Rigidbody.AddForce.
I am creating a game in Unity where I have to have a player (a ball) and three enemies (in this case three rotating cylinders). Whenever the player hits an enemy, I need it to die (which I have already done) and then print out Game over, which I don't know how to do. I also need the player to respawn after it dies, which is another think I don't know how to do. I also need to create three "virtual holes" where when the player rolls over them, it respawns, but not dies. I figure I can simulate holes by creating flat cylinders, but I don't know how to make the ball respawn but rolling over them. Thank you in advance!! Please be clear about which part does what in your answer.
//My player script
public class PlayerController : MonoBehaviour
{
public float speed;
private Rigidbody rb;
public float threshold;
public Text gameOver;
// Use this for initialization
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
//rolls the player according to x and z values
void FixedUpdate()
{
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
rb.AddForce(movement * speed);
}
}
//my script that makes the enemy rotate and kills the player but after the
player dies it just disappears
public class Rotater : MonoBehaviour {
public Text gameOver;
// Update is called once per frame
void Update ()
{
transform.Rotate(new Vector3(15, 30, 45) * Time.deltaTime);
}
//kills player
void OnCollisionEnter(Collision Col)
{
if (Col.gameObject.name == "Player")
{
Destroy(Col.gameObject);
gameOver.text = "Game over!";
}
}
//my script that respawns the play if it falls off the maze
public class Respawn : MonoBehaviour
{
// respawns player if it goes below a certain point (falls of edge)
public float threshold;
void FixedUpdate()
{
if (transform.position.y < threshold)
transform.position = new Vector3(-20, 2, -24);
}
}
You need to create a UI to draw text over your game. You can learn about it here.
When you have the UI in place, you can just activate/deactivate the relevant parts with SetActive(bool).
To kill and respawn the player, I suggest you not to destroy it and re-instantiate it. Instead, you can simply deactivate it and reactivate it in the new position using SetActive(bool) again.
For the holes, you can create other objects with colliders and use OnCollisionEnter as you already did but to change player's position.