So I want to know how to instantiate a GameObject without intersection in Unity3D. As of now, the sphere instantiates intersected with the object that I hit with the raycast. I am using the location of hit.point, but would like to know if there is a way to spawn it on the collisions instead of the origin of the object. Here is my code for reference:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BallSpawn : MonoBehaviour
{
public int castLength = 100;
public GameObject spawnObject;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
//Input.GetMouseButton for infinite
if (Input.GetMouseButton(0))
{
SpawnBall();
}
}
public void SpawnBall()
{
RaycastHit hit;
Debug.DrawRay(transform.position, transform.TransformDirection(Vector3.forward) * castLength, Color.green, Mathf.Infinity);
if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out hit, castLength))
{
Instantiate(spawnObject, hit.point, hit.transform.rotation);
}
}
}
As of now, the spheres clip into the ground and bounce out when they are spawned. I would like it for the spheres to be "placed" so to speak, so that they spawn without clipping.
You can try to replace hit.point with hit.point + hit.normal * ballRadius (of course you must know or be able to get what the ball radius is). But this will result with some spawn positions being off, especially if you hit a surface under small angle.
Better alternative is to replace ray cast with Physics.SphereCast (transform.position, sphereRadius, transform.forward, out hit, castDistance). This should give you good results but again you will still have to add the ball radius along the hit normal just like the raycast.
Related
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.
The animator make the object to rotate so the script I'm using with a raycast is not working.
Only if I disable the animator then when the raycast hit an item the head of the character will rotate look at the item.
but is there a way to make that the player head will rotate look at the item even if the animator is still working ?
The script that attach to the player :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Interactable : MonoBehaviour
{
public Transform objToRotateLookAT;
private bool raycastSucceed;
// Start is called before the first frame update
void Start()
{
}
void FixedUpdate()
{
int layerMask = 1 << 8;
RaycastHit hit;
// Does the ray intersect any objects excluding the player layer
if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out hit, Mathf.Infinity, layerMask))
{
if (!raycastSucceed)
Debug.Log("Did Hit");
raycastSucceed = true;
Debug.DrawRay(transform.position, transform.TransformDirection(Vector3.forward) * hit.distance, Color.red);
Vector3 relativePos = hit.transform.position - objToRotateLookAT.position;
// the second argument, upwards, defaults to Vector3.up
Quaternion rotation = Quaternion.LookRotation(relativePos, Vector3.up);
objToRotateLookAT.rotation = rotation;
}
else
{
if (raycastSucceed)
Debug.Log("Did not Hit");
raycastSucceed = false;
Debug.DrawRay(transform.position, transform.TransformDirection(Vector3.forward) * 1000, Color.yellow);
}
}
}
The player head will rotate facing the item detected only if the animator is disabled. Is there a way to keep the animator active and also to rotate the head facing the detected item ?
Well, you can't make that in Player's animator because you disabled it. But you can change its rotation in another game object. For example, you can put a HeadManager gameObject with an Animator inside. And whenever you want to make the head rotate, you can just make the Bool in animator of HeadManager.
If you have more than 1 object that you want to rotate their head, you can make this HeadManager child of that game object. And when you hit, you can just simply get it's children's animator and make the bool true.
https://docs.unity3d.com/ScriptReference/Component.GetComponentInChildren.html
You can check this to get the children's animator.
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");
Im trying to make 2 types of enemies, melees and ranged, so im trying that both have a raycast to the player to know if they can see the player and are enought near, but I have two problems. The first one is that when I check the distance between the enemy and the player is always 0, and the second is that Raycast doesnt print when it should.
Let me explain, Im in front of the enemies and it prints that sees me, but other times not, in the same conditions, so i dont know what im missing.
Here is my code :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyMelee : EnemyBase {
public int rangoAttack;
RaycastHit hit;
int layerMask = 1 << 2;
void Update () {
perseguir();
attackMele();
}
void attackMele() {
if (Physics.Raycast(transform.position, player.transform.position, layerMask)){
print(hit.distance);
}
Debug.DrawLine(transform.position, player.transform.position, Color.red);
}
}
Enemies are on Layer 2
Pd: EnemyBase script just have movement to the player with nav.SetDestination (player.position);
Image of the enemy (cant upload images yet): https://ibb.co/eq7pMV
First problem occurs because you don't use hit in the code. So hit.distance = 0.
Second problem occurs because in Physics.Raycast(transform.position, player.transform.position, layerMask) you should set direction in 2nd argument (Not endpoint). You can calculate direction by this: Vector3 direction = player.transform.position - transform.position.
Try this code:
RaycastHit hit;
int layerMask = 1 << 2;
public float maxDistance;
void Update ()
{
attackMele();
}
void attackMele()
{
Vector3 direction = player.transform.position - transform.position;
if (Physics.Raycast(transform.position, direction, out hit, maxDistance, layerMask))
print(hit.distance);
Debug.DrawLine(transform.position, player.transform.position, Color.red);
}
I hope it helps you.
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;
}
}