Following a tutorial, I'm trying to use a grappling gun mechanic that utilizes a spring joint and Line Renderer.
I've got the line drawing upon mouseclick, but the end of the line is not drawing where the user clicks.
It looks like this:
Can anyone kind of help me out as to why it's not working? Here's the (wonky) project in action-
https://i.imgur.com/IuMsEsQ.mp4
Grappling gun code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GrapplingGun : MonoBehaviour
{
private LineRenderer lr;
private Vector3 grapplePoint; //where we grapple to
public LayerMask whatIsGrappable;
public Transform gunTip;
public Transform cam;
public Transform player;
private float maxDistance = 100f;
private SpringJoint joint;
void Awake()
{
lr = GetComponent<LineRenderer>();
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
StartGrapple();
}
else if (Input.GetMouseButtonUp(0))
{
StopGrapple();
}
}
void LateUpdate()
{
DrawRope();
}
void StartGrapple()
{
RaycastHit hit;
if (Physics.Raycast(cam.position, cam.forward, out hit, maxDistance, whatIsGrappable))
//if (Physics.Raycast(transform.position, Vector3.forward, out hit, maxDistance, whatIsGrappable))
{
grapplePoint = hit.point;
joint = player.gameObject.AddComponent<SpringJoint>();
joint.autoConfigureConnectedAnchor = false;
joint.connectedAnchor = grapplePoint;
float distanceFromPoint = Vector3.Distance(player.position, grapplePoint);
joint.maxDistance = distanceFromPoint * 0.8f;
joint.minDistance = distanceFromPoint * 0.25f;
joint.spring = 4.5f;
joint.damper = 7f;
joint.massScale = 4.5f;
lr.positionCount = 2;
}
}
void DrawRope()
{
//If not grappling, don't draw anything
if (!joint) return;
lr.SetPosition(0, gunTip.position);
lr.SetPosition(1, grapplePoint);
}
void StopGrapple()
{
lr.positionCount = 0;
Destroy(joint);
}
}
Thank you.
The underlying issue is your raycast. the second parameter is the direction of the ray, which you have as the camera direction. Currently your ray is pointing forward from the camera at all times as a result.
What you can do is use Camera.ScreenPointToRay to give a ray to cast along to give you a 3d mouse position to cast to, then use your current raycast but replace the second parameter with the direction from the player to the point hit by the raycast from the function mentioned before
Ray ray = Camera.ScreenPointToRay(Input.mousePosition);
Physics.Raycast(ray, out RaycastHit hit);
if (Physics.Raycast(transform.position, (hit.point - transform.position).normalized, out hit, maxDistance, whatIsGrappable)) {
// Your code here...
}
Related
I have been on here forever it feels like and nothing is fixing the issue. So I am relatively new to c# and unity and I set up my weapon and my crosshairs. I went through and set up ray cast and got a impact effect set. When I load in and try to shoot, my impact effect seems to be landing in different positions and it never lands in the same spot. I want to get it to where the effect happens on the target where the crosshairs are.
This line was added after the fact to try and fix it but didn't end up doing anything:
Ray ray = Camera.main.ViewportPointToRay(new Vector2(0.5f, 0.5f));
Here's my script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class shooting : MonoBehaviour
{
public float damage = 10;
public float range = 100;
public Camera cam;
public AudioSource audioSource;
public AudioSource audio1;
public AudioClip clip;
public AudioClip reload;
public float volume = 0.5f;
public ParticleSystem muzzleflash;
public GameObject hitEffect;
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
Shooting();
}
if (Input.GetKey(KeyCode.R))
{
Reload();
}
}
void Shooting()
{
muzzleflash.Play();
audioSource.PlayOneShot(clip, volume);
RaycastHit hit;
Ray ray = Camera.main.ViewportPointToRay(new Vector2(0.5f, 0.5f));
if (Physics.Raycast(cam.transform.position, cam.transform.forward, out hit, range))
{
Debug.Log("Shooting");
}
Instantiate(hitEffect, hit.point, Quaternion.LookRotation(hit.normal));
}
void Reload()
{
audio1.clip = reload;
audio1.Play();
if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.LeftShift))
{
audio1.Stop();
}
}
}
You've created a ray, you need use it.
Instantiate should be in the if block.
Specify a layer to do raycast, otherwise it can hit any collider in the path.
Ray ray = Camera.main.ViewportPointToRay(new Vector2(0.5f, 0.5f));
var mask = LayerMask.GetMask("Enemy");
if (Physics.Raycast(ray, out hit, range, mask))
{
Instantiate(hitEffect, hit.point, Quaternion.LookRotation(hit.normal));
}
If the effect is still shown at a random place, add some debug code, check the red line in the scene window.
void OnDrawGizmos()
{
Gizmos.color = Color.red;
Gizmos.DrawLine(ray.origin, hit.point);
}
What I have tried :
At the top of the script I added
[SerializeField] private LayerMask terrainLayer;
In the Inspector I added a new layer at place 8 name Terrain then changed the Terrain layer to Terrain and also selected Terrain on this script in the terrainLayer variable.
Then in the SpawnThroughPlane method :
public void SpawnThroughPlane()
{
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
//Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
var ray = new Ray(plane.transform.position, Vector3.down);
//if (plane.GetComponent<MeshCollider>().Raycast(ray, out hit, Mathf.Infinity))
if (Physics.Raycast(ray, out hit, Mathf.Infinity, terrainLayer))
{
cube = Instantiate(prefabToSpawn);
Vector3 pos = cube.transform.position;
pos.y = terrain.SampleHeight(cube.transform.position);
cube.transform.position = hit.point + pos;
}
}
}
The result is when I click it's spawning objects no matter where I click with the mouse and show the objects at one position on same place.
The full script :
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public class SpawnObjects : MonoBehaviour
{
public GameObject prefabToSpawn;
public Terrain terrain;
public CustomPlane plane;
public float yOffset = 0.5f;
[SerializeField] private LayerMask terrainLayer;
private float terrainWidth;
private float terrainLength;
private float xTerrainPos;
private float zTerrainPos;
private float planeWidth;
private float planeLength;
private float xPlanePos;
private float zPlanePos;
private float yValTerrain;
private float yValPlane;
private float randXTerrain;
private float randZTerrain;
private float randXPlane;
private float randZPlane;
private Collider terrainCollider;
void Awake()
{
if (terrain != null)
{
//Get terrain size
terrainWidth = terrain.terrainData.size.x;
terrainLength = terrain.terrainData.size.z;
//Get terrain position
xTerrainPos = terrain.transform.position.x;
zTerrainPos = terrain.transform.position.z;
terrainCollider = terrain.GetComponent<Collider>();
}
if (plane != null)
{
planeWidth = plane.width;
planeLength = plane.length;
xPlanePos = plane.transform.position.x;
zPlanePos = plane.transform.position.z;
}
}
private void Update()
{
SpawnThroughPlane();
}
public void SpawnThroughPlane()
{
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
//Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
var ray = new Ray(plane.transform.position, Vector3.down);
//if (plane.GetComponent<MeshCollider>().Raycast(ray, out hit, Mathf.Infinity))
if (Physics.Raycast(ray, out hit, Mathf.Infinity, terrainLayer))
{
var prefab = Instantiate(prefabToSpawn);
Vector3 pos = prefab.transform.position;
pos.y = terrain.SampleHeight(prefab.transform.position);
prefab.transform.position = hit.point + pos;
}
}
}
}
If the plane.transform.position is not in screen (= PIXEL) space the usage of ScreenPointToRay makes not much sense.
You probably rather want to simply use
var ray = new Ray(plane.transform.position, Vector3.down);
And as mentioned you should anyway make sure to use a LayerMask and only allow the raycast to hit the terrain by given it a dedicated Layer and select only that one in e.g.
[SerializeField] private LayerMask terrainLayer;
and then use
if(Physics.Raycast(ray, out var hit, Mathf.infinity, terrainLayer))
I'm trying to make a basic FPS game in Unity and I'm having an issue where the projectiles I shoot won't instantiate in the right place. From what I can tell, the projectile instantiates in roughly the same position relative to the player regardless of where I look (that position being a little to the left of the starting angle of the player).
Here's a 20 second video demonstration of what I'm talking about.
https://youtu.be/WLVuqUtMqd0
Even when I'm facing the exact opposite direction of where the projectile usually instantiates it still spawns in the same place, which means the projectile ends up spawning behind the player and hitting the player.
I tried using Debug.DrawRay() to see if maybe the firepoint itself is wrong (which would be shown by the ray starting somewhere other than the gun barrel). However it seems like the starting point of the ray is correct every time.
I'm not sure if this is related to the issue above, but I have noticed that the ray direction is wrong if I'm looking a little below the horizon. It seems like the projectile's direction is correct regardless though.
Ray directions when looking slightly above the horizon vs. lower
Here's my code for intantiating/shooting the projectile. I think the most relevant parts are probably shootProjectile() and instantiateProjectile(). This script is attached to the player character.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Shooting : MonoBehaviour
{
public Camera cam;
public GameObject projectile;
public Transform firePoint;//firePoint is set to the end of the barrel.
public float projectileSpeed = 40;
//private var ray;
private Vector3 destination;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
//var ray = cam.ViewportPointToRay(new Vector3(0.5f,0.5f,0));
//Debug.DrawRay(ray.origin, ray.direction);
if(Input.GetButtonDown("Fire1")) {
//player is shooting
ShootProjectile();
}
}
void ShootProjectile() {
Ray ray1 = cam.ScreenPointToRay(Input.mousePosition);
//Debug.Log(ray.direction);
RaycastHit hit;
if(Physics.Raycast(ray1, out hit))//checking whether the player is going to hit something
{
destination = hit.point;
}
else {
destination = ray1.GetPoint(1000);
}
Debug.DrawRay(firePoint.position, destination, Color.white, 10f);
InstantiateProjectile(firePoint);
}
void InstantiateProjectile(Transform firePoint) {
var projectileObj = Instantiate (projectile, firePoint.position, Quaternion.identity) as GameObject;//projectile is instantiated
projectileObj.GetComponent<Rigidbody>().velocity = (destination - firePoint.position).normalized * projectileSpeed;//projectile is set in motion
}
}
Here's the location of firePoint.
firePoint (i.e. where the projectile should instantiate)
I would appreciate any help on this, as I've been trying to fix it (on and off) for several days, and really have no idea what the problem is.
Edit: Here's my player movement script(s) as well. The first one is PlayerController.cs, and it converts the player's inputs into the appropriate movement vectors and camera rotation. It then calls methods from PlayerMotor.cs, which actually performs the movements.
PlayerController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(PlayerMotor))]
public class PlayerController : MonoBehaviour
{
[SerializeField]
public float speed = 10f;
[SerializeField]
private float lookSens = 3f;
private PlayerMotor motor;
void Start() {
motor = GetComponent<PlayerMotor>();
//Debug.Log("PlayerControllerStart");
Cursor.lockState = CursorLockMode.Locked;
}
void Update() {
//Debug.Log("PlayerControllerUpdate");
//calculate movement velocity as 3D vector.
float xMov = Input.GetAxisRaw("Horizontal");
float zMov = Input.GetAxisRaw("Vertical");
Vector3 movHorizontal = transform.right * xMov;
Vector3 movVertical = transform.forward * zMov;
Vector3 velocity = (movHorizontal + movVertical).normalized * speed;
motor.move(velocity);
//speed*=(float)1.15;
//rotation
float yRot = Input.GetAxisRaw("Mouse X");
Vector3 rotation = new Vector3 (0f, yRot, 0f) * lookSens;
motor.rotate(rotation);
float xRot = Input.GetAxisRaw("Mouse Y");
Vector3 cameraRotation = new Vector3 (xRot, 0f, 0f) * lookSens;
motor.rotateCamera(cameraRotation);
if (Input.GetKeyDown(KeyCode.Space) == true && motor.isGrounded()) {
motor.canJump=true;
}
if (Input.GetKey(KeyCode.W) == true) {
motor.accel=true;
}
else{
motor.accel=false;
}
}
}
PlayerMotor:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
public class PlayerMotor : MonoBehaviour
{
[SerializeField]
private Camera cam;
private Vector3 velocity = Vector3.zero;
private Vector3 rotation = Vector3.zero;
private Vector3 cameraRotation = Vector3.zero;
private Vector3 jumpVector = new Vector3 (0f, 5f, 0f);
private PlayerController pc;
private Rigidbody rb;
public bool canJump;
public bool accel;
public float acceleration;
int jumpCount;
void Start() {
rb = GetComponent<Rigidbody>();
pc = GetComponent<PlayerController>();
canJump=false;
jumpCount=0;
accel=false;
//acceleration = 1.0;
//distToGround = collider.bounds.extents.y;
//Debug.Log("PlayerMotorStart");
}
//sets velocity to a given movement vector.
public void move(Vector3 _velocity) {
velocity = _velocity;
}
public void rotate(Vector3 _rotation) {
rotation = _rotation;
}
public void rotateCamera(Vector3 _cameraRotation) {
cameraRotation = _cameraRotation;
}
public bool isGrounded() {
return Physics.Raycast(transform.position, -Vector3.up, (float)2.5);
}
public void jump() {
rb.AddForce(transform.up * 250f);
//Debug.Log("Jump"+jumpCount);
jumpCount++;
canJump=false;
}
void FixedUpdate() {
performMovement();
performRotation();
if (canJump) {
jump();
}
//Debug.Log("PlayerMotorUpdate");
if(accel&&pc.speed<20f&&isGrounded())
{
//Debug.Log("W Pressed");
pc.speed*=(float)1.005;
}
else if(!accel) {
pc.speed=7f;
}
}
void performMovement() {
if(velocity != Vector3.zero) {
rb.MovePosition(rb.position + velocity * Time.fixedDeltaTime);
//Debug.Log("Movement");
}
}
void performRotation() {
rb.MoveRotation(rb.rotation * Quaternion.Euler(rotation));
if(cam != null) {
cam.transform.Rotate(-cameraRotation);
}
}
}
Here are some pictures of my projectile prefab as well.
To solve one first confusion: The method Debug.DrawRay takes as paramters
a start position
a ray direction(!)
You are passing in another position which is not what you want.
This might only work "accidentally" as long as you stand on 0,0,0 so maybe it wasn't that notable but the debt ray definitely points into a wrong direction.
Rather do e.g.
Debug.DrawRay(firePoint.position, destination - firePoint.position, Color.white, 10f);
Or instead rather use Debug.DrawLine which rather actually takes
start position
end position
Then you don't need to calculate the direction first
Debug.DrawLine(firePoint.position, destination, Color.white, 10f);
Then I suspect the following is happening: The spawned projectileObj actually is placed correctly but it is the visuals inside that projectile prefab that have a local offset against the parent prefab root.
Since you always spawn the projectile in world rotation Quaternion.identity that offset stays the same no matter where you look.
I guess a simple fix would already be to also apply the same rotation like
var projectileObj = Instantiate (projectile, firePoint.position, firePoint.rotation);
I'm trying to make my Player Teleport to my mouse position but whenever I right click it seems to teleport far away from my camera and generally where my mouse pointer was.
I am not getting any error messages nor am I getting warnings.
for example, if I click in the middle of the screen with my mouse it will teleport to the center of the text Canvas.
I couldn't seem to find any solutions to my issue on the unity forums.
maybe someone here could help me
my code:
using UnityEngine;
using System.Collections;
public class Player : MonoBehaviour
{
public float moveSpeed;
public float jumpHeight;
public GameObject bullet;
public float speed = 5.0f;
public Transform groundCheck;
public float groundCheckRadius;
public LayerMask whatIsGround;
private bool grounded;
public Transform player; //Variable for Teleport funktion
void Awake()
{
player = GameObject.FindGameObjectWithTag("Player").transform; //Finding the player (Teleport)
}
void fixedUpdate()
{
grounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);
}
void Update()
{
if (Input.GetKeyDown("space"))
{
GetComponent<Rigidbody2D>().velocity = new Vector2(GetComponent<Rigidbody2D>().velocity.x, jumpHeight);
}
if (Input.GetKey(KeyCode.D))
{
GetComponent<Rigidbody2D>().velocity = new Vector2(moveSpeed, GetComponent<Rigidbody2D>().velocity.y);
}
if (Input.GetKey(KeyCode.A))
{
GetComponent<Rigidbody2D>().velocity = new Vector2(-moveSpeed, GetComponent<Rigidbody2D>().velocity.y);
}
if (Input.GetMouseButtonDown(1))
{
player.position = (Input.mousePosition); // teleporting
}
if (Input.GetMouseButtonDown(0))
{
Vector2 target = Camera.main.ScreenToWorldPoint(new Vector2(Input.mousePosition.x, Input.mousePosition.y));
Vector2 myPos = new Vector2(transform.position.x, transform.position.y);
Vector2 direction = target - myPos;
direction.Normalize();
Quaternion rotation = Quaternion.Euler(0, 0, Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg);
GameObject projectile = (GameObject)Instantiate(bullet, myPos, rotation);
projectile.GetComponent<Rigidbody2D>().velocity = direction * speed;
}
}
}
You just need to transform the input position from screen to world, like you did in your shooting code:
if (Input.GetMouseButtonDown(1))
{
Vector3 target = Camera.main.ScreenToWorldPoint(Input.mousePosition);
player.position = target; // teleporting
}
I'm currently making a sandbox game and it is able to create a object through left click. But, I am struggling to destroy a specific object when it is right clicked. I've looked on previous questions here, but they don't exactly answer my question.
using UnityEngine;
using System.Collections;
public class ControlObjects : MonoBehaviour
{
Vector3 mousePosition, targetPosition;
//To Instantiate TargetObject at mouse position
public Transform targetObject;
public GameObject Prefab;
float distance = 10f;
Ray ray;
RaycastHit hit;
//public int item_num = 1;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
transform.position = targetPosition;
//To get the current mouse position
mousePosition = Input.mousePosition;
//Convert the mousePosition according to World position
targetPosition = Camera.main.ScreenToWorldPoint(new Vector3(mousePosition.x, mousePosition.y, distance));
//Set the position of targetObject
targetObject.position = targetPosition;
//Debug.Log(mousePosition+" "+targetPosition);
//If Left Button is clicked
if (Input.GetMouseButtonDown(0))
{
//create the instance of targetObject and place it at given position.
Instantiate(targetObject, targetObject.transform.position, targetObject.transform.rotation);
}
}
}
Implement what you need, but this is the base.
using UnityEngine;
public class Test : MonoBehaviour
{
private float distance = 10;
private float offset = -4;
void Update()
{
if (Input.GetKeyDown(KeyCode.Mouse1))
{
GameObject go = GameObject.CreatePrimitive(PrimitiveType.Cube);
go.transform.position = new Vector3
{
x = offset += 1.5f,
y = 0,
z = 0
};
}
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Input.GetKeyDown(KeyCode.Mouse0))
{
if(Physics.Raycast(ray, out RaycastHit hit, distance))
{
Destroy(hit.transform.gameObject);
}
}
}
}
You should have a look at IPointerClickHandler. Attach this script to the objects you want to be able to click on
using UnityEngine;
using UnityEngine.EventSystems;
public class DestroyOnRightClick : MonoBehaviour, IPointerClickHandler
{
public void OnPointerClick (PointerEventData eventData)
{
if (eventData.button == PointerEventData.InputButton.Right)
{
Debug.Log ("Right Mouse Button Clicked on: " + name);
Destroy(gameObject);
}
}
}
Note
Ensure an EventSystem exists in the Scene to allow click detection. For click detection on non-UI GameObjects, ensure a PhysicsRaycaster is attached to the Camera.