I have made a Raycast that goes from my camera to the point of the object clicked. However, I am trying to make an object (in this case a bullet) to fly along the path of the ray. At the moment it flies straight forwards from the camera no matter where on the object you click because of the vector 3. How would I get it to follow the Ray?
C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RaycastShot : MonoBehaviour {
public Camera camera;
private Ray ray;
private RaycastHit hit;
public GameObject bullet;
private GameObject createBullet;
private Collider collider;
void Update () {
if (Input.GetMouseButtonDown (0)) {
ray = camera.ScreenPointToRay (Input.mousePosition);
createBullet = Instantiate (bullet, camera.transform.position, bullet.transform.rotation);
createBullet.AddComponent<Rigidbody>();
createBullet.GetComponent<Rigidbody>().AddRelativeForce (new Vector3(0, 1500, 0));
createBullet.GetComponent<Rigidbody>().useGravity = false;
collider = createBullet.GetComponent<Collider> ();
Destroy (collider);
if (Physics.Raycast (ray, out hit)) {
}
}
Debug.DrawLine (ray.origin, hit.point, Color.red);
}
}
You would want to use ray.direction property instead of (0,1500,0) as the direction of the force.
The add force should occur in FixedUpdate, and should only occur if the Ray hits something. Where you have it now is probably not the best spot.
Of course, make sure the bullet gets instantiated at the camera's location first.
Ray.direction gives you the vector3 direction of the ray object. If you need the distance at which it hit, you could also use ray.distance.
Edit: I'm near my computer now, so here's a more detailed answer relating to your comments.
First off: Here's the way I set up the test Project:
I Created a prefab bullet. This is just a sphere with a rigidbody, with my "BulletController" script attached to it. The point of prefabs is to avoid all of those lines where you have to add components. For testing purposes I set the rigibody to ignore gravity and its mass to 0.1.
Next, I created the BulletController script, which will be attached to the bullet prefab.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletController : MonoBehaviour {
Rigidbody rb;
public float bulletForce;
bool firstTime = false;
Vector3 direction;
// Use this for initialization
void Start () {
rb = GetComponent<Rigidbody> ();
}
public void SetDirection (Vector3 dir) {
direction = dir;
firstTime = true;
}
void OnCollisionEnter () {
//code for when bullet hits something
}
void FixedUpdate () {
if (firstTime) {
rb.AddForce (direction * bulletForce);
firstTime = false;
}
}
}
This script is is charge of controlling bullets. The (later on) script that will create the bullets doesn't really care what happens to them afterwards, since its job is just to create bullets. This BulletController script is in charge of dealing with bullets once they're created.
The main parts are the SetDirection method which tells the bullet which direction to travel in. Also it adds a one-time force in its FixedUpdate method that pushes it in the direction you just set. FixedUpdate is used for physics changes like adding forces. Don't use Update to do this kind of thing. It multiplies the force by a force that you set called "bulletForce".
Finally the BulletListener Script, which is simply attached to an empty game object in the scene. This script is in charge of listening for mouse clicks and creating bullets towards them.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletListener : MonoBehaviour {
public Camera mainCamera;
public BulletController bulletPrefab;
void Update () {
if (Input.GetMouseButtonDown (0)) {
//create ray from camera to mousePosition
Ray ray = mainCamera.ScreenPointToRay (Input.mousePosition);
//Create bullet from the prefab
BulletController newBullet = Instantiate (bulletPrefab.gameObject).GetComponent<BulletController> ();
//Make the new bullet start at camera
newBullet.transform.position = mainCamera.transform.position;
//set bullet direction
newBullet.SetDirection (ray.direction);
}
}
}
In the inspector for this empty game object, I added this script, and then dragged the camera, and the bulletPrefab into the appropriate fields. Be sure to drag the prefab from the FOLDER, not from the SCENE. Since this will use the prefab, not an object in the scene.
Now click around and you'll see the bullets flying! Note that using a low force is good to test, and then increase it later.
The main things to take away from this is to split up your logic. A script should only be in charge of one thing. For example, your enemies might also fire bullets. You can now reuse your bulletController script for those bullets as well. Also, say you have different sized or shaped bullets, you can just drag the bulletcontroller script onto the different prefabs you've made for your bullets. This will not affect your listener script which will still create bullets where you click.
If you have the end point then you can move along the vector with MoveTowards:
Vector3 target = hit.point;
StartCoroutine(MoveAlong(target));
private IEnumerator MoveAlong(Vector3 target){
while(this.transform.position != target){
this.transform.position = MoveTowards(this.transform.position, target, step);
yield return null;
}
}
Related
I'm new, and I've got hard time to respawn my AI (right now he just a cube that follow my player) after he been destroy. I believe its because the script sits on the object that get destroyed. but what I need to do to respawn it?
(although I'm sure my respawn code is not good :\ (It's mobile-android project) )
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyTesting : MonoBehaviour
{
[SerializeField]
public GameObject player;
public GameObject enemy;
private Rigidbody body;
Vector3 accelerationDir;
// Use this for initialization
void Start()
{
body = GetComponent<Rigidbody>();
}
private void Update()
{
accelerationDir = Input.acceleration;
if (accelerationDir.sqrMagnitude>=5)
{
EnemyDead();
}
}
void EnemyDead()
{
Destroy(enemy);
Invoke("Respawn", 5);
}
void Respawn()
{
enemy = (GameObject)Instantiate(enemy);
enemy.transform.position = transform.position;
}
// Update is called once per frame
void FixedUpdate()
{
Vector3 toTarget = player.transform.position - transform.position;
float speed = 1.5f;
transform.Translate(toTarget * speed * Time.deltaTime);
}
}
Thanks very much!
I am supposing that your enemy GameObject is inside the scene holding your EnemyTesting MonoBehaviour instance (correct me if I'm wrong).
If this is the case, you cannot instantiate a gameObject that is destroyed.
As #derHugo pointed out, you should not use Destroy and Instantiate for your use case. It would be better to set inactive the enemy GameObject, move it to the position that you want, an (re)set it active. It will look like the enemy respawned.
If you want to dig the subject later, look at the object pooling game optimization pattern.
Otherwise, if you still want to use Instantiate for respawning, I would create a prefab of your enemy GameObject. The enemy GameObject referenced in the EnemyTesting field (in the Inspector view), would be your prefab from the project hierarchy instead of a GameObject inside the scene.
This way, you would be able to instantiate an enemy GameObject as many times as you want (and use it in other scenes!). Don't forget to hold a reference to the instantiated enemy GameObject so you can know which one you want to destroy. It would looke like this :
enemy = Instantiate(enemyPrefab, transform.position, transform.rotation);
You can replace transform with the transform of your choice, for example the transform of an empty enemyRespawnPoint GameObject in your Scene.
Do you see any error in the console ?
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.
I have a rectangle player sprite with a Box Collider 2D and a Rigidbody2D attached. I also have a script for point-and-click movement attached to the player object (i.e. player moves to mouse click position). However as soon as the player character hits a collider, it starts to jitter rather than just fully stop. I don't know a lot about Unity physics other than what I've picked up in a few tutorials, so I'll include as much relevant information as I can.
The Rigidbody 2D component has all forces set to 0, except for mass being 0.0001. The body type is dynamic, and collision detection is set to continuous. My movement script looks like this, got it straight from a tutorial:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerControls : MonoBehaviour
{
public float speed = 1;
private Vector3 target;
void Start()
{
target = transform.position;
}
void Update()
{
if (Input.GetMouseButtonDown(0)) {
target = Camera.main.ScreenToWorldPoint(Input.mousePosition);
target.z = transform.position.z;
}
transform.position = Vector3.MoveTowards(transform.position, target, speed * Time.deltaTime);
}
}
Is there an easier way to implement smooth point-and-click movement?
I want to shoot a raycast out of the tip of enemies gun and see if it hits the player. To do this I created an empty game object placed inside of the gun in the hierachy and moved the empty game object which I called TipOfGun not I attached script to TipOfGun called Gun with rayCast but it does nothing as far as I'm concerned and after all the testing I've done. I can't figure out why raycast never hits anything. Below is my code and i'm also posting a picture of my hierarchy.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Gun : MonoBehaviour
{
private ParticleSystem muzzleFlash;
Vector3 tipOfGun = new Vector3(0.009f, 0.329f, 0.017f);
// Use this for initialization
void Start()
{
GameObject muzzleFlashObj = GameObject.Find("muzzleFlash");
muzzleFlash = muzzleFlashObj.GetComponent<ParticleSystem>();
}
public void ShootWeapon()
{
muzzleFlash.Play();
Ray ray = new Ray(transform.position, transform.forward);
RaycastHit hit;
if (Physics.SphereCast(ray, 0.75f, out hit))
{
Debug.Log("Name of component hit:" + hit.collider.gameObject.name);
GameObject hitObject = hit.transform.gameObject;
if (hitObject.GetComponent<PlayerController>())
{
muzzleFlash.Play();
}
else
muzzleFlash.Stop();
}
}
}
You are using Physics.Spherecast which isn't what you are looking for.
From your description I think you should use Physics.Raycast
Try something like this instead of Spherecast:
if (Physics.Raycast(transform.position, transform.forward, out hit))
Give it a try.
After breaking my head a few hours in between days I decided to see if I can see the actual Raycast to determine why it was not hitting my player. I should've thought of this way before for those of you that run into a similar problem you can always draw the actual ray and view it in the scene, with something similar to this.
Debug.DrawRay(transform.position, transform.TransformDirection(Vector3.forward) * hit.distance, Color.yellow);
Debug.Log("Did Hit");
I started making a simple game in Unity3d: a tank to shoot at a wall (see image).
A GameObject is attached to the turret of the tank, and to this GameObject is attached the following script :
using UnityEngine;
using System.Collections;
public class Shooter : MonoBehaviour {
public Rigidbody bullet;
public float power = 1500f;
void Update () {
if (Input.GetButtonDown ("Fire1")) {
Rigidbody bulletRB = Instantiate (bullet, transform.position, transform.rotation) as Rigidbody;
Vector3 fwd = transform.TransformDirection(Vector3.forward);
bulletRB.AddForce(fwd*power);
}
}
}
When I press on the Fire1 button the bullet does not shoot. I put (for test) a Debug.Log("BULLET SHOOT") after bulletRB.addForce(). The message is displayed, so the script reached this point. What is wrong with my code?
Based on this somewhat similar question on Unity Answers, you should probably be instantiating the GameObject of the bullet prefab/instance, rather than its Rigidbody directly. Then, access the Rigidbody component of that new bullet and add the force.
Your adjusted Update() method would then look like:
void Update () {
if (Input.GetButtonDown ("Fire1")) {
GameObject newBullet = Instantiate (bullet.gameObject, transform.position, transform.rotation) as GameObject;
RigidBody bulletRB = newBullet.GetComponent<Rigidbody>();
Vector3 fwd = transform.TransformDirection(Vector3.forward);
bulletRB.AddForce(fwd*power);
}
}
Another thing you may want to change is using transform.forward (aka. Forward vector of the turret) rather than Vector3.forward (global forward vector Vector3(0, 0, 1), which may not match the direction of the turret).
Hope this helps! Let me know if you have any questions.
Force can be applied only to an active rigidbody. If a GameObject is inactive, AddForce has no effect.
Wakes up the Rigidbody by default. If the force size is zero then the Rigidbody will not be woken up.
The above description is taken from Unity
Therefore, I would suggest to check if the GameObject is active first.
You can test it by doing the following:
if (newBullet.activeInHierarchy === true)
{
//active
}else{
//inactive
}