Unity 3D how to aim a weapon to a raycast hit - c#

I have a weapon which shoots projectiles and i try to rotate the weapon towards a raycast hit. I attached two scripts too my weapon one for shooting and one for aiming the shooting script works fine.
Here my weapon script which inistiate my projectiles:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Security.Cryptography;
using System.Threading;
using UnityEngine;
using UnityEngine.AI;
using Random = UnityEngine.Random; // |source: https://community.gamedev.tv/t/solved-random-is-an-ambiguous-reference/7440/9
public class weapon_shooting : MonoBehaviour
{
//deklariere projektil |source: https://docs.unity3d.com/ScriptReference/Object.Instantiate.html
public GameObject projectilePrefab;
private float projectileSize = 0;
public Rigidbody rb;
public float projectileSpeed = 50;
public float projectileSizeRandomMin = 10;
public float projectileSizeRandomMax = 35;
void Update()
{
// Ctrl was pressed, launch a projectile
if (Input.GetButtonDown("Fire1"))
{
//random sized bullets
projectileSize = Random.Range(projectileSizeRandomMin, projectileSizeRandomMax);
projectilePrefab.transform.localScale = new Vector3(projectileSize, projectileSize, projectileSize);
// Instantiate the projectile at the position and rotation of this transform
Rigidbody clone;
clone = Instantiate(rb, transform.position, transform.rotation);
// Give the cloned object an initial velocity along the current
// object's Z axis
clone.velocity = transform.TransformDirection(Vector3.forward * projectileSpeed);
}
}
}
So then i tried to cast a ray from the middle of the screen to the mouse position and then rotate my weapon towards the point where the ray collides with my world. But when i run it it shoots in all directions :=o (no errors) need some help to figure this out :)
here is my weapon aiming script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class weapon_aiming : MonoBehaviour
{
public Camera camera;
private bool ray_hit_something = false;
void Update()
{
RaycastHit hit;
Ray ray = camera.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
ray_hit_something = true;
} else
{
ray_hit_something = false;
}
if (ray_hit_something == true) {
transform.LookAt(Camera.main.ViewportToScreenPoint(hit.point));
}
}
}

Just use LookAt(hit.point) as LookAt expects a position in world space and hit.point is already in world space:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class weapon_aiming : MonoBehaviour
{
public Camera camera;
private bool ray_hit_something = false;
void Update()
{
RaycastHit hit;
Ray ray = camera.ScreenPointToRay(Input.mousePosition);
ray_hit_something = Physics.Raycast(ray, out hit);
if (ray_hit_something) {
transform.LookAt(hit.point);
}
}
}

Related

How to throw a ball that player picked up in Unity 2d project

I have a problem with writing a function that throws spawned ball by pressing a button.
So far, i could only instantiate the ball and it is affected by gravity.
I can not wrap my head arround, how in my code should i add a force to push it with some force. Right now it just plops in front of a player.
I tried to fiddle with .AddForce() on a ball rigidbody2D and still nothing.
I would greatly appreciate if someone could take a look at the code and guide me into some directcion on where to AddForce for ball to make it move?
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerControl : MonoBehaviour
{
[Header("Player Atributes")]
[SerializeField] float runSpeed = 10f;
Vector2 moveInput;
Rigidbody2D playerRigidbody2D;
[Header("Ball Settings")]
public bool playerHasABall = false;
[SerializeField] GameObject ball;
[SerializeField] Transform ballSpawn;
void Start()
{
playerRigidbody2D = GetComponent<Rigidbody2D>();
}
void Update()
{
Run();
FlipSprite();
}
void OnMove(InputValue value)
{
moveInput = value.Get<Vector2>();
}
void OnFire(InputValue value)
{
if(!playerHasABall) {return;}
Instantiate(ball, ballSpawn.position, transform.rotation);
playerHasABall = false;
}
void Run()
{
Vector2 playerVelocity = new Vector2 (moveInput.x * runSpeed, playerRigidbody2D.velocity.y);
playerRigidbody2D.velocity = playerVelocity;
}
void FlipSprite()
{
bool playerHasHorizontalSpeed = Mathf.Abs(playerRigidbody2D.velocity.x) > Mathf.Epsilon;
if (playerHasHorizontalSpeed)
{
transform.localScale = new Vector2 (Mathf.Sign(playerRigidbody2D.velocity.x), 1f);
}
}
//Ball picking up script. It destroys the ball with a tag "Ball" and sets a bool playerHasABall to true
private void OnTriggerEnter2D(Collider2D other)
{
if(other.tag == "Ball")
{
Destroy(other.gameObject);
playerHasABall = true;
}
}
}
So, i manage to fiddle with the .AddForce() method once again and i came up with a solution to my problem.
My OnFire script looks like this:
void OnFire(InputValue value)
{
if(!playerHasABall) {return;}
Instantiate(ball, ballSpawn.position, transform.rotation);
playerHasABall = false;
}
No change here. I made another script called "BallBehaviour.cs" and added it to the ball with a code like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BallBehaviour : MonoBehaviour
{
[SerializeField] float ballForce = 20f;
PlayerControl player;
void Start()
{
player = FindObjectOfType<PlayerControl>();
Throw();
}
public void Throw()
{
GetComponent<Rigidbody2D>().AddForce(player.transform.localScale * ballForce, ForceMode2D.Impulse);
}
}
Basically, the method Throw() tells the ball to check the direction the player is facing and then multiply it by ballForce. Then, it uses the ForceMode2d.Impulse method to apply calculated force to the ball for a second and it works on left or right direction.
Thanks Jkimishere for a nudge in a propper direction!
You might need a variable to check if the player is facing right/left.
If the player is, just add force to the ball depending on the direction.
if(!playerHasABall) {return;}
Instantiate(ball,
ballSpawn.position,
transform.rotation);
//Check which way the player is facing and add forrce to that direction.
//Adding a FacingRight boolean might help.....
playerHasABall = false;

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 :)

Player does not move when Camera follow script is active

As soon as I add my camera follow script to the camera, my player can no longer be controlled.No error is displayed.As soon as I start the game the x and y position changes to negative during the game.As soon as I delete the follow script everything works again.Can someone help me please?
Here the scripts:
Follow Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraFollow : MonoBehaviour
{
[SerializeField]
public GameObject followObject;
public void Update()
{
if (!followObject)
{
followObject = GameObject.FindGameObjectWithTag("Player");
}
transform.position = new Vector3(followObject.transform.position.x, followObject.transform.position.y, -10);
}
}
PlayerController:
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.Profiling.Memory.Experimental;
using UnityEngine;
using UnityEngine.UI;
public class PlayerController : MonoBehaviour
{
[Range(0, .3f)] [SerializeField] private float m_MovementSmoothing = .05f;
public static PlayerController PC;
public float runSpeed = 200;
public float jumpSpeed;
private bool isJumping;
private float move;
private Vector3 m_Velocity = Vector3.zero;
void Update()
{
move = Input.GetAxis("Horizontal");
// Move the character by finding the target velocity
Vector3 targetVelocity = new Vector2(move * 10f, rb.velocity.y);
// And then smoothing it out and applying it to the character
rb.velocity = Vector3.SmoothDamp(rb.velocity, targetVelocity, ref m_Velocity, m_MovementSmoothing);
if (move < 0)
{
transform.eulerAngles = new Vector3(0, 180, 0);
}else if(move > 0)
{
transform.eulerAngles = new Vector3(0, 0, 0);
}
if(Input.GetButtonDown("Jump") && !isJumping)
{
rb.AddForce(new Vector2(0f, jumpSpeed));
isJumping = true;
}
private void OnCollisionEnter2D(Collision2D other)
{
if(other.gameObject.CompareTag("Ground"))
{
isJumping = false;
}
if (other.gameObject.CompareTag("MGround"))
{
this.transform.parent = other.transform;
isJumping = false;
}
}
private void OnCollisionExit2D(Collision2D other)
{
if (other.gameObject.CompareTag("MGround"))
{
this.transform.parent = null;
isJumping = true;
}
}
}
Try this:
Put it on camera game object
and put player game object to player window in the inspector
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraFollow : MonoBehaviour
{
[SerializeField]
public Transform player;
public void Update()
{
transform.position = new Vector3(player.position.x, player.position.y);
}
}
You´re scripts are incomplete and I could not reproduce your error. Did you copy/paste the full source?
I cleaned them up anyway and they work perfectly for me.
Here is what I did.
Camera Follow
No Changes
Player Controller
Add a Private "RigidBody2D rb" to the script since it was missing but you were
using it in Update().
Move Collision Methods out of Update
Add closing brackets.

Collision detection with character controller in Unity 3d

I have my player which uses character controller for moving. I placed a sprite in the scene and I'd like for when my player collides with the sprite to disable the sprite, like if the player grabs the sprite (which is Doom's 64 chainsaw).
The sprite's collisions of course work well with everything, but not with the player. How can I get proper collision between them?
You could do it like this:
1-Attach "Pickable" script to the sprite.
2-Attach "Player" script to the character controller.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Pickable : MonoBehaviour
{
public float radius = 1f;
private void Start()
{
SphereCollider collider = gameObject.AddComponent<SphereCollider>();
collider.center = Vector3.zero;
collider.radius = radius;
collider.isTrigger = true;
}
}
Here is the other script.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
private void OnTriggerEnter(Collider other)
{
Pickable pickable = other.GetComponent<Pickable>();
if(pickable != null)
{
Destroy(other.gameObject);
}
}
}

Categories