i try to make a simple 3D Rail Shooter, and i currently stuck on how to spawn the bullets for my Ship.
I want when i press on fire that the bullet gets spawned and gets shooted.
I created an empty gameObject as a child of my ship and put a script on it.
The problem is i currently stuck and i can't figure out how to get it done.
So im asking for help, what did i miss, what did i wrong?
Here is the script i came up with:
public Rigidbody rb;
private bool isMoving = false;
private bool isFirePressed = false;
void Start()
{
rb = GetComponent<Rigidbody>();
rb.useGravity = false;
}
void Update()
{
isFirePressed = Input.GetButtonDown("Fire1");
}
void FixedUpdate()
{
if (Input.GetButtonDown("Fire1"))
{
// the cube is going to move upwards in 10 units per second
rb.velocity = new Vector3(0, 0, 100);
isMoving = true;
Debug.Log("fire");
}
}
First I guess you wanted to use your variable isFirePressed.
Then if that is a prefab I guess you rather want to Instantiate a new bullet:
if (isFirePressed)
{
var newBullet = Instantiate (rb, transform);
// the cube is going to move upwards in 10 units per second
newBullet.velocity = new Vector3(0, 0, 100);
newBullet.useGravity = false;
isMoving = true;
Debug.Log("fire");
}
You changed the velocity on the prefab which doesn't work.
Additionally note that the velocity is in World-Space coordinates. So currently you are shooting in world Z direction no matter where your plane is facing to.
I would rather use e.g.
newBullet.velocity = transform.forward * 100;
to shoot it in the direction your BulletEmitter is facing towards.
Related
The main goal of the script in this case is when the player is getting too close to the fire to slowly stop walking automatic wait some seconds then automatic rotate and start walking again and then stop again slowly.
The script is working when the player is entering the collider area this is the target variable position so the player is rotating back to where he was entered from.
The idea is to make something nice to prevent from the player to get into the fire. but i'm facing two problems.
The first problem is that in the script i'm using a box collider as the area to prevent from the player to get closer to the fire and it's hard to dynamic change the collider area shape to cover all sides.
This is a screenshot of the collider area :
I marked to possible places if the player is coming from closer to the fire when he enter/exit the collider area he is already inside the fire and it's hard to change the collider size and scale to cover this places too.
I wonder if using distance check instead of the box collider will be easier ? and if it is how to change the script to use distance check instead the box collider trigger enter and exit events ?
The second problem is that sometimes when the player got close to the fire and then rotating back outside the collider area when he exit at that point(position) the player is making a walking circle around the target position and then continue outside. not sure why the player is making sometimes a moving around the target point ?
The script is attached to the player :
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityStandardAssets.Characters.ThirdPerson;
public class SpreadedFireDistanceCheck : MonoBehaviour
{
public Vector3 target;
public float rotationSpeed;
public GameObject descriptionTextImage;
public TextMeshProUGUI text;
public Transform player;
private Animator anim;
private ThirdPersonUserControl thirdPersonUserControl;
private bool startRotBack = false;
private bool rot = false;
private bool doOnce = true;
private bool getTargetPos = false;
float angle = 10;
// Start is called before the first frame update
void Start()
{
anim = player.GetComponent<Animator>();
thirdPersonUserControl = player.GetComponent<ThirdPersonUserControl>();
}
// Update is called once per frame
void Update()
{
if (startRotBack)
{
if (doOnce)
{
anim.SetTrigger("Idle Exit");
StartCoroutine(WaitTime());
doOnce = false;
}
if (rot)
{
anim.SetBool("Walk", true);
if (Vector3.Angle(player.transform.forward, target - player.transform.position) > angle)
{
Vector3 dir = target - player.transform.position;
Quaternion lookRotation = Quaternion.LookRotation(dir);
Vector3 rotation = Quaternion.Lerp(player.transform.rotation, lookRotation, Time.deltaTime * rotationSpeed).eulerAngles;
player.transform.rotation = Quaternion.Euler(0f, rotation.y, 0f);
}
}
}
}
private void OnTriggerExit(Collider other)
{
if (other.name == "kid_from_space")
{
if (rot)
{
rot = false;
anim.SetBool("Walk", false);
anim.SetTrigger("Idle Enter");
thirdPersonUserControl.enabled = true;
getTargetPos = false;
}
}
}
private void OnTriggerEnter(Collider other)
{
if (other.name == "kid_from_space")
{
if (getTargetPos == false)
{
target = player.transform.position;
getTargetPos = true;
}
descriptionTextImage.SetActive(true);
text.text = "This flames are so hot, i better stay away.";
thirdPersonUserControl.enabled = false;
doOnce = true;
startRotBack = true;
}
}
private IEnumerator WaitTime()
{
yield return new WaitForSeconds(5f);
text.text = "";
descriptionTextImage.SetActive(false);
rot = true;
}
}
In general the script is working but this two problem i mentioned above is hard to solve.
The player have this components :
Animator , Rigidbody , Capsule Collider , Third Person User Control (Script) , Third Person Character (Script)
I have a problem, I am trying to make the enemy shoot at the player every 5 seconds and right now it is just shooting lots of bullets constantly in a line.
This is my code, I would really appreciate some help!
void ShootAtPlayer()
{
StartCoroutine(bulletshooting());
IEnumerator bulletshooting()
{
shooting = true;
if (shooting == true)
{
GameObject tempBullet = Instantiate(enemyBullet, eyes.gameObject.transform.position, eyes.gameObject.transform.rotation) as GameObject; //shoots from enemies eyes
Rigidbody tempRigidBodyBullet = tempBullet.GetComponent<Rigidbody>();
tempRigidBodyBullet.AddForce(tempRigidBodyBullet.transform.forward * enemyBulletSpeed);
Destroy(tempBullet, 0.1f);
yield return new WaitForSeconds(5f);
}
shooting = false;
}
From your comment
shootAtPlayer is called in the update when the enemy is close enough to the player
The issue is: You are starting a new Coroutine every frame!
In your case I wouldn't use a Coroutine at all since you already have a code that is executed every frame. (Because otherwise you would need some code to also stop the running routine)
Rather use a simple timer:
[SerializeField] private float cooldown = 5;
private float cooldownTimer;
void ShootAtPlayer()
{
cooldownTimer -= Time.deltaTime;
if(cooldownTimer > 0) return;
cooldownTimer = cooldown;
GameObject tempBullet = Instantiate(enemyBullet, eyes.gameObject.transform.position, eyes.gameObject.transform.rotation) as GameObject; //shoots from enemies eyes
Rigidbody tempRigidBodyBullet = tempBullet.GetComponent<Rigidbody>();
tempRigidBodyBullet.AddForce(tempRigidBodyBullet.transform.forward * enemyBulletSpeed);
Destroy(tempBullet, 0.1f);
}
If you really want to use a Coroutine for that it would probably look like
private bool isShooting;
void ShootAtPlayer()
{
// Only allow a new routine if there is none running already
if(isShooting) return;
StartCoroutine (shootRoutine());
}
private IEnumerator shootRoutine()
{
// Just in case avoid concurrent routines
if(isShooting) yield break;
isShooting = true;
GameObject tempBullet = Instantiate(enemyBullet, eyes.gameObject.transform.position, eyes.gameObject.transform.rotation) as GameObject; //shoots from enemies eyes
Rigidbody tempRigidBodyBullet = tempBullet.GetComponent<Rigidbody>();
tempRigidBodyBullet.AddForce(tempRigidBodyBullet.transform.forward * enemyBulletSpeed);
Destroy(tempBullet, 0.1f);
yield return new WaitForSeconds (5f);
isShooting = false;
}
I am trying to get my GameObject (asteroid) to spawn continuously in the game. I looked up and followed this tutorial: https://pressstart.vip/tutorials/2018/09/25/58/spawning-obstacles.html. To make the asteroid move, I created this script (AsteroidObject) and to spawn the objects, I created this script (DeployAsteroids). There are no errors and the Debug.Log appears in the console. But the asteroid game object cannot be seen and will not spawn. Anyone can help? Thanks in advance!
Asteroid Object Codes:
public class AsteroidObject : MonoBehaviour
{
public float speed = 10.0f; //how fast the asteroid will move
private Rigidbody2D rb;
private Vector2 screenBounds; //screenbounds calculation
// Start is called before the first frame update
void Start()
{
rb = this.GetComponent<Rigidbody2D>(); //find rigidbody 2d and set it to rb reference by using the getcomponent
rb.velocity = new Vector2(-speed, 0); //moving the asteroid from right to left by setting the x value, leaving the y value 0 so that it will not move up and down
screenBounds = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, Camera.main.transform.position.z)); //defines the boundaries of the screen on the x and y axis
}
// Update is called once per frame
void Update()
{
//transform.position = new Vector3(Mathf.Clamp(transform.position.x, -9f, 9f),
//Mathf.Clamp(transform.position.y, -4f, 4f), transform.position.z);
if (transform.position.x < screenBounds.x) //check if it is moving to the left of the screen
{
Destroy(this.gameObject);
Debug.Log("hello world");
}
}
}
DeployAsteroid codes:
public class DeployAsteroids : MonoBehaviour
{
public GameObject asteroidPrefab;
public float respawnTime = 1.0f;
private Vector2 screenBounds;
// Start is called before the first frame update
void Start()
{
screenBounds = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, Camera.main.transform.position.z));
StartCoroutine(asteroidWave());
}
private void spawnEnemy()
{
GameObject a = Instantiate(asteroidPrefab) as GameObject;
a.transform.position = new Vector2(screenBounds.x * -2, Random.Range(-screenBounds.y, screenBounds.y));
}
IEnumerator asteroidWave()
{
while (true)
{
yield return new WaitForSeconds(respawnTime);
spawnEnemy();
//Debug.Log("Hello World");
}
}
}
You even say it yourself
the Debug.Log appears in the console.
Well, you log in the moment you destroy your object => it is already gone.
Destroy(this.gameObject);
Debug.Log("hello world");
Unless you mean the out commented log after the spawn method. You still immediately destroy the object. I think you would spot it immediately if you would use useful logs and not twice the same one
Debug.Log("Spawned a new object");
and
Debug.Log("Destroyed an object");
So what exactly is happening then?
You immediately destroy the new spawned objects!
you do
screenBounds = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, Camera.main.transform.position.z));
which stores the position at the right border of the screen.
Let's say e.g. somewhere at x = 2; (total random imaginary example number).
Then right after spawning you set it to
a.transform.position = new Vector2(screenBounds.x * -2, Random.Range(-screenBounds.y, screenBounds.y));
So for the x you set it to (using our example value) x = -4;
This is not even the left boarder of the screen but even beyond!
Additionally you tell the asteroid to move
rb.velocity = new Vector2(-speed, 0);
so assuming the value speed is positive even more into negative x direction.
And finally you do
// Even without the Rigidbody this is already comparing
// if(-2 * screenBounds.x < screenBounds.x)
// Or with our example numbers
// if(-4 < 2)
if (transform.position.x < screenBounds.x)
{
Destroy(this.gameObject);
Debug.Log("hello world");
}
=> This condition will always be true and immediately destroys your objects in the next frame.
So what should I do instead?
I assume you are trying to spawn the asteroid at the right boarder.
And want to destroy it after it passed the left boarder. So it should probably be
private void spawnEnemy()
{
GameObject a = Instantiate(asteroidPrefab);
Vector3 rightEdgeWorldPoint = Camera.main.ScreenToWorldPoint(
new Vector3(Screen.width, Random.Range(0, Screen.height),
Camera.main.nearClipPlane);
rightEdgeWorldPoint.z = 0f;
a.transform.position = rightEdgeWorldPoint;
}
And in the asteroid
if (Camera.main.WorldToScreenPoint(transform.position).x < 0)
{
Destroy(this.gameObject);
Debug.Log("Left the screen -> destroyed");
}
Note: Typed on smartphone but I hope the idea gets clear
Go to the scene hierarchy and check if the asteroids can be seen there. May be you do not see them within the game or the scene camera, but if they are in the scene they are spawn somewhere. It would be interesting info to update in the question.
I did not dig into the code but if they are there, you need to check or set the position in which they are spawned respect to the camera, for the asterioids to be in the field of view.
Check the posistion given in this line: a.transform.position = new Vector2(screenBounds.x * -2, Random.Range(-screenBounds.y, screenBounds.y)); and if it fits in the camera field of view.
I am making a small game that is based on the Roll-A-Ball tutorial from Unity, though I haven't used the actual tutorial. I incorporated a respawn mechanic, but if you are moving around when you die, then after you respawn, you still have that momentum after you land. I have tried to fix this, but I am not sure how since I am still pretty new at using Unity. I have a video that shows this: https://drive.google.com/open?id=1752bPBDVOe2emN_hmnlPaD4uaJQITpsP
Here is the C# script that handles respawn:
public class PlayerBehavior : MonoBehaviour
{
Rigidbody PlayerRB;
public bool Dead;
private int timer;
public GameObject Particles;
public bool InRespawn;
void Update()
{
PlayerRB = GetComponent<Rigidbody>();
if (Dead)
{
StartCoroutine("Respawn");
}
}
IEnumerator Respawn()
{
InRespawn = true; //Used to prevent movement during respawn.
PlayerRB.useGravity = false;
transform.position = new Vector3(0, 4, 0);
transform.rotation = new Quaternion(-80, 0, 0, 0); // Resets position.
Dead = false;
Instantiate(Particles, transform); // Adds respawn particle effect.
yield return new WaitForSeconds(2);
Destroy(this.gameObject.transform.GetChild(0).gameObject);
PlayerRB.useGravity = true;
PlayerRB.AddForce(0, 400, 0); // Does a little hop.
InRespawn = false; // Tells the game that respawn is finished.
}
}
Zero out the rigidbody's velocity when the respawn occurs:
IEnumerator Respawn()
{
PlayerRB.velocity = Vector3.zero;
// ... rest of method
}
As a sidenote, you probably don't need to run GetComponent on every frame. It's an expensive operation so it's best to do it as infrequently as you can get away with:
void Start()
{
PlayerRB = GetComponent<Rigidbody>();
}
void Update()
{
if (Dead)
{
StartCoroutine("Respawn");
}
}
If instead you would like to disable all physics interactions with the player while it is dead, you can set it to be kinematic during that time. Just be sure to unset isKinematic before adding force to it.
IEnumerator Respawn()
{
PlayerRB.isKinematic = true;
// ... rest of method
PlayerRB.isKinematic = false;
PlayerRB.useGravity = true;
PlayerRB.AddForce(0, 400, 0); // Does a little hop.
InRespawn = false; // Tells the game that respawn is finished.
}
make a bool in your code like this
bool isDead=false;
then make it true when you die
the add this into you update
if(isDead){
rb.velocity=vector3.zero;
}
this will stop you objects movements if it is dead
I Have a gun that spawn a projectile that bounces of colliders (a Ricochet). It is supposed to be shooting to the direction of where the gun is facing but what I am getting is the projectile always shoots 45 degrees upwards to the right I know this is because of my constant declared vector 2.
I tried using Vector2.up but it prevents the projectile to do the ricochet effect because it always wants to go upwards.
How should I implement those things? I just want the projectile to shoot to the direction where my gun is facing and bounces of on colliders. This is a 2D game btw. I have my codes attached below so you can see. Thanks!
Projectile Script:
private Rigidbody2D rb;
public static bool canMove = true;
void Start()
{
rb = GetComponent<Rigidbody2D>();
rb.velocity = new Vector2(10f, 10f);
}
void Update()
{
//transform.Translate(Vector2.up * speed * Time.deltaTime);
if (canMove)
{
rb.isKinematic = false;
}
else if (!canMove)
{
rb.isKinematic = true;
}
}
Gun Script:
float offset = -90f;
public GameObject projectile;
public Transform shotPoint;
public GameObject child;
void Start()
{
}
void Update()
{
Vector3 diff = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
float rotZ = Mathf.Atan2(diff.y, diff.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0f, 0f, rotZ + offset);
if (Input.GetMouseButtonDown(0))
{
Instantiate(projectile, shotPoint.position, transform.rotation);
Projectile.canMove = true;
}
}
The Rigodbody.velocity is in World-Space coordinates.
When you pass in
rb.velocity = new Vector2(10f, 10f);
it will go in world space 10 in X and 10 in Y direction.
In order to pass it in as local coordinates in general you can not always rely on Tramsform.InverseTransformDirection as suggested here since the Transform component. In this specific case it might work but in general you set velocities in FixedUpdate and in that moment the Transform component might not be updated yet!
But the Rigidbody2D component is so in general you can use Rigidbody2D.GetRelativeVector in order to convert a local vector relative to the Rigidbody into world coordinates:
// Might also be Vector.up depending on your setup
rb.velocity = rb.GetRelativeVector(Vector2.right * speed);
Note: it would be better you make
[SerializeField] private Rigidbody2D rb;
and already reference it via the Inspector. Then you can get rid of the expensive GetComponent call.
Because you are telling it to do so.
rb.velocity = new Vector2(10f, 10f);
10 to the right, and 10 upwards.
Unless your projectile has a constant force applied to it, like a missile, get rid of everything related to forces or velocity in the projectile script. It will do you no good.
Then, on the gun script:
//...
if (Input.GetMouseButtonDown(0)) {
var projectileInstance = Instantiate(projectile, shotPoint.position, transform.rotation);
var rigidbody = projectileInstance.GetComponent<Rigidbody2D>();
rigidbody.velocity = transform.TransformDirection(yourDirectionVector);
Projectile.canMove = true;
}
Where Transform.TransformDirection is what makes yourDirectionVector, which is a direction relative to the gun, be transformed into one relative to world-space.