on scene I have 3 balls with Ball.cs script attached to each of them. And when I push ball with mouse all balls start moving, but I need that only one that I touch moves.
Here is my FixedUpdate:
void FixedUpdate() {
if(!isMoving) {
if (Input.GetMouseButtonDown (0)) {
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100)) {
if(hit.collider.tag == "Ball") {
startPos = hit.point;
}
}
}
if (startPos != Vector3.zero && Input.GetMouseButtonUp(0)) {
endPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector3 direction = endPos - startPos;
direction.Normalize();
float distance = Vector3.Distance(endPos, startPos);
rigidbody.AddForce(direction * distance * force * Time.deltaTime, ForceMode.Impulse);
isMoving = true;
}
} else {
if(rigidbody.velocity.sqrMagnitude == 0) {
isMoving = false;
startPos = endPos = Vector3.zero;
}
}
}
Like Nick Udell already mentioned, your comparison of the tag is the source of your problem. All three balls are executing the same script at nearly the same time. So if you click on of the balls, all three scripts will cast a ray at your mouse position and check if they hit a ball. Guess what? All of them hit a ball but not the ball they belong to.
You need to check if they are hitting the collider attached to their GameObject
if (hit.collider == collider) {
// do stuff
}
Related
After my player object touches the wall it sometimes starts to move and rotate on it's own. I tried to increase player object weight and it helps, but i don't think it's a good approach as tiny movement never dissapears.
Player is rigidbody with box collider attached.
isKinematic - false;
useGravity - true;
XYZ rotation is fixed and Y coordinate position is fixed.
Walls have box collider but have no rigidbody.
Ground has no collider as Y position is fixed and player object doesn't touch the ground.
Game is network based so i have photon rigidbody view component attached to player as well.
Code (C#) which moves player:
public void Update()
{
if (!photonView.IsMine || !controllable)
{
return;
}
if (shootingTimer > 0.0)
{
shootingTimer -= Time.deltaTime;
}
m_MovementInputValue = Input.GetAxis(m_MovementAxisName);
if (m_MovementInputValue == 0.0f)
{
m_MovementInputValue = joystick.Vertical;
}
m_TurnInputValue = Input.GetAxis(m_TurnAxisName);
if (m_TurnInputValue == 0.0f)
{
m_TurnInputValue = joystick.Horizontal;
}
Vector3 vector = new Vector3(
m_TurnInputValue,
rigidbody.velocity.y,
m_MovementInputValue
);
MovementVector = Quaternion.AngleAxis(60, Vector3.up) * vector;
EngineAudio();
}
public void FixedUpdate()
{
if (!photonView.IsMine || !controllable)
{
return;
}
Move();
Turn();
}
private void Move()
{
// Adjust the position of the tank based on the player's input.
// Vector3 movement = transform.forward * m_MovementInputValue * m_Speed * Time.deltaTime;
// rigidbody.MovePosition(rigidbody.position + movement);
Vector3 movement = MovementVector * m_Speed * Time.deltaTime;
rigidbody.MovePosition(rigidbody.position + movement);
}
private void Turn()
{
// Adjust the rotation of the tank based on the player's input.
// float turn = m_TurnInputValue * m_TurnSpeed * Time.deltaTime;
// Quaternion turnRotation = Quaternion.Euler(0f, turn, 0f);
// rigidbody.MoveRotation(rigidbody.rotation * turnRotation);
if (m_TurnInputValue != 0.0f || m_MovementInputValue != 0.0f)
{
rigidbody.rotation = Quaternion.LookRotation(MovementVector);
}
}
Check to make sure the component (such as a rigidbody) is attached to the right object (gameobject - parent vs. child objects). Also, the fact that it is constantly rotating is curious... it makes me think it has something to do with the Time.DeltaTime, but I see that's only being applied to your Vector3 movement, and not in your rotation function... Just curious, what happens when you remove the Time.DeltaTime part from your movement Vector3?
The projectiles of my game are too fast; some of bullets are passing through the collider.
I did a RayCast ray to detect the colliders and it's working. I'm having trouble trying to do a RayCast2D to destroy the bullet when it hits the collider.
if (Input.GetButtonDown("Fire1") && Time.time > nextfire)
{
nextfire = Time.time + firerate;
anim.SetTrigger("Shoot");
GameObject tempBullet = Instantiate(bulletPrefab, shotSpawner.position,
shotSpawner.rotation);
RaycastHit2D hit = Physics2D.Raycast(shotSpawner.position, shotSpawner.right);
Debug.DrawLine(transform.position, transform.right * 300f, Color.cyan);
if (hit.collider.tag != null)
{
nextfire = Time.time;
print(hit.collider.name);
}
else
{
print(hit.collider.name);
}
}
I'm working on a third person controller and I'm trying to prevent movement when a raycast in front of the player hits something. Currently when the player collides with a block the blend tree is set to 0 so that it shows the Idle animation. In this case the player appears to be not moving, which it obviously isn't because it's colliding with something, but the input of the user is still there.
This is visible when the player presses the jump button, because the player moves in the direction the user was initially trying to move. In the player controller there is a part that prevents the user from moving after the jump button has been pressed during the length of the jump (Until the player is grounded again). What I'm trying to achieve is that when the user is moving against something and presses the jump button the player will just jump up and won't go in the direction the user was initially trying to go, even if there is no more collider or obstruction.
Something like if the raycast hits something the user won't be able to add movement in that direction, but is still able to turn the player away from the object and move in that direction as long as the raycast doesn't hit a collider.
I'm no c# wizard, so most of the code below comes from a tutorial. I can write a raycast myself, but have no clue how to write the rest of what I'm trying to achieve.
Edit: Current script
public RaycastHit _Hit;
public LayerMask _RaycastCollidableLayers; //Set this in inspector, makes you able to say which layers should be collided with and which not.
public float _CheckDistance = 5f;
void Update()
{
PushStates();
// Input
Vector2 input = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
Vector2 inputDir = input.normalized;
bool running = Input.GetButton("Run");
if (IsNotBlocked())
{
Move(inputDir, running);
}
float animationSpeedPercent = ((running) ? currentSpeed / runSpeed : currentSpeed / walkSpeed * .5f);
anim.SetFloat("speedPercent", animationSpeedPercent, speedSmoothTime, Time.deltaTime);
anim.SetFloat("speedPercentPush", animationSpeedPercent, speedSmoothTime, Time.deltaTime);
// Check if walking or running
if (animationSpeedPercent < 0.51f)
{
if (ShortJumpRaycast())
{
if (Input.GetButtonDown("Jump") && Time.time > canJump)
{
ShortJump();
}
}
else
{
if (Input.GetButtonDown("Jump") && Time.time > canJump)
{
JumpWalk();
}
}
}
else
{
//Debug.Log("You are Running");
if (Input.GetButtonDown("Jump") && Time.time > canJump)
{
JumpRun();
}
}
JumpCheck();
}
void Move(Vector2 inputDir, bool running)
{
if (inputDir != Vector2.zero)
{
float targetRotation = Mathf.Atan2(inputDir.x, inputDir.y) * Mathf.Rad2Deg + cameraT.eulerAngles.y;
transform.eulerAngles = Vector3.up * Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref turnSmoothVelocity, GetModifiedSmoothTime(turnSmoothTime));
}
float targetSpeed = ((running) ? runSpeed : walkSpeed) * inputDir.magnitude;
currentSpeed = Mathf.SmoothDamp(currentSpeed, targetSpeed, ref speedSmoothVelocity, GetModifiedSmoothTime(speedSmoothTime));
velocityY += Time.deltaTime * gravity;
Vector3 velocity = transform.forward * currentSpeed + Vector3.up * velocityY;
controller.Move(velocity * Time.deltaTime);
currentSpeed = new Vector2(controller.velocity.x, controller.velocity.z).magnitude;
// Checks if player is not grounded and is falling
if (GroundCheck())
{
velocityY = 0;
anim.SetBool("onAir", false);
}
else
{
anim.SetBool("onAir", true);
}
}
bool IsNotBlocked()
{
Vector3 forward = transform.TransformDirection(Vector3.forward);
if (Physics.Raycast(transform.position, forward, out _Hit, _CheckDistance + 0.1f, _RaycastCollidableLayers))
if (_Hit.collider == null)
{
Debug.Log("Raycast hit nothing");
return true;
}
GameObject go = _Hit.collider.gameObject;
if (go == null) //If no object hit, nothing is blocked.
return true;
else //An object was hit.
return false;
}
Directly before move you can raycast in the direction of the movement and if it hits something you can cancel movement.
Something like:
Vector3 velocity = transform.forward * currentSpeed + Vector3.up * velocityY;
if(!Physics.Raycast(transform.position, transform.forward, distance)
{
controller.Move(velocity * Time.deltaTime);
}
else
{
controller.Move(Vector3.up * velocityY * Time.deltaTime);
}
Something like if the raycast hits something the user won't be able to add movement in that direction
Here is an example of how you can perform a raycast to check whether something was hit or not.
//Example raycast code
//Variables
public RayCastHit _Hit;
public LayerMask _RaycastCollidableLayers; //Set this in inspector, makes you able to say which layers should be collided with and which not.
public float _CheckDistance = 5f;
//Method
bool IsNotBlocked(){
Vector3 forward = transform.TransformDirection(Vector3.forward);
if (Physics.Raycast(transform.position, forward, out _Hit, _CheckDistance + 0.1f, _RaycastCollidableLayers))
if (_Hit.collider == null)
{
Debug.Log("Raycast hit nothing");
return true;
}
GameObject go = _Hit.collider.gameObject;
if (go == null) //If no object hit, nothing is blocked.
return true;
else //An object was hit.
return false;
}
Basically,
The length of the ray is CheckDistance.
RayCastCollidableLayers determines which layers an object can be in for it to be collidable with the ray we create.
The code sets a direction stored in "forward", a raycast is then performed from the transform.position (position of object this script is attached to) in direction Vector3.Forward.
_Hit saves whatever the raycast hits. An object can then be accessed through it and stored as a GameObject. I call this object 'go'.
Feel free to ask questions.
I'm new to Unity and have been following a tutorial on how to make a Captain Blaster 2D game, however I want to convert it to Android, I want to make the player controllable by dragging him across the screen with one finger and don't understand what's wrong with my code, anything helps, thanks
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class ShipControl : MonoBehaviour {
public float playerSpeed = 10f;
public GameControl gameController;
public GameObject bulletPrefab;
public float reloadTime = 1f;
private float elapsedTime = 0;
void Update()
{
elapsedTime += Time.deltaTime;
if (Input.touchCount >= 1)
{
foreach (Touch touch in Input.touches)
{
Ray ray = Camera.main.ScreenPointToRay (touch.position);
RaycastHit hit;
if (Physics.Raycast (ray, out hit, 100)) {
}
}
if (elapsedTime > reloadTime)
{
Vector3 spawnPos = transform.position;
spawnPos += new Vector3 (0, 1.2f, 0);
Instantiate (bulletPrefab, spawnPos, Quaternion.identity);
elapsedTime = 0f;
}
}
}
void OnTriggerEnter2D(Collider2D other)
{
gameController.PlayerDied ();
}
}
What I would do is add a bool called "dragging" and after you check if Raycast hit anything you also check if hit object is the player GameObject.
If it is then as long as user is not releasing the touch - make player's rigidbody move towards the touch position (so if there are any obstacles it simply doesn't move right through them).
Code would probably look like this (you should also add some timer to check if player released touch and set dragging bool to false):
public float playerSpeed = 10f;
public GameControl gameController;
public GameObject bulletPrefab;
public float reloadTime = 1f;
private float elapsedTime = 0;
private bool dragging = false;
void Update()
{
if (Input.touchCount >= 1)
{
foreach (Touch touch in Input.touches)
{
Ray ray = Camera.main.ScreenPointToRay (touch.position);
RaycastHit hit;
if (Physics.Raycast (ray, out hit, 100))
{
if(hit.collider.tag == "Player") // check if hit collider has Player tag
{
dragging = true;
}
}
if(dragging)
{
//First rotate the player towards the touch (should do some checks if it's not too close so it doesn't glitch out)
Vector3 _dir = Camera.main.ScreenToWorldPoint(touch.position) - transform.position;
_dir.Normalize();
float _rotZ = Mathf.Atan2(_dir.y, _dir.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0f, 0f, _rotZ - 90);
//Move towards the touch
transform.GetComponent<Rigidbody>().AddRelativeForce(direction.normalized * playerSpeed, ForceMode.Force);
}
}
}
}
For a 3D first person controller game, I am converting a swipe on the screen in a direction vector.
An object is shot into this direction.
My camera can rotate based on the input of a virtual joystick.
When I don't rotate and shoot the object using swipe it goes in the right direction.
However when I rotate the camera it doesn't go in the intended direction.
The direction should be adapted to the rotation of the camera.
How do I correct the direction of my vector to the rotation of the camera?
PS: Message me for further clarification
//Converting swipe direction to 3D direction
public class TouchPair
{
public Vector2 startPos;
public int fingerId;
}
private TouchPair touchPair;
void Update()
{
foreach (Touch touch in Input.touches)
{
Vector2 touchPos = touch.position;
if (touch.phase == TouchPhase.Began)
{
Ray ray = cam.ScreenPointToRay(touchPos);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
//The player is wielding a bomb that is visible on the screen.
//only swipes that start from this object should count
if (hit.transform.tag == "Bomb")
{
touchPair = new TouchPair();
touchPair.startPos = touchPos;
touchPair.fingerId = touch.fingerId;
}
}
}
else if (touch.phase == TouchPhase.Ended)
{
if (touchPair.fingerId == touch.fingerId)
{
Vector2 endPos = touchPos;
Vector2 swipeDirectionRaw = endPos - touchPair.startPos;
float magnitude = swipeDirectionRaw.magnitude;
if (magnitude >= minSwipeLength)
{
BombController BombController = GameObject.FindWithTag("Bomb").GetComponent<BombController>();
BombController.Throw(swipeDirectionRaw.normalized, magnitude);
}
}
}
}
}
public void Throw(Vector2 direction, float magnitude)
{
//Setup variables for throw
throwDirection = new Vector3(direction.x, 0.0f, direction.y);
throwSpeed = magnitude * throwForce;
}
You need raycast with ground when touch.phase == TouchPhase.Ended then get direction from your character to raycast.hit.
Raycasthit hitInfo;
Physic.Raycast;
I had to get the view vector of the camera. By taking the amount of angles
between the player's forward and the camera's view vector the amount of angles is received that is required to get a correct direction vector.
Finally rotate the shoot direction by this amount of angles.
Vector2 endPos = touchPos;
Vector2 swipeDirectionRaw = endPos - touchPair.startPos;
float magnitude = swipeDirectionRaw.magnitude;
swipeDirectionRaw.Normalize();
if (magnitude >= minSwipeLength)
{
GameObject player = GameObject.FindWithTag("Player");
Vector3 shootOrigin = player.transform.position;
Vector3 uncorrectedShootDirection = new Vector3(swipeDirectionRaw.x, 0.0f, swipeDirectionRaw.y);
Vector3 originVector = player.transform.forward;
Vector3 viewVector = cam.transform.forward;
//Shoot direction gets corrected by angle between origin- and view vector
float angleBetweenOriginAndView = Vector3.Angle(originVector, viewVector);
//There is no clockwise or counter-clockwise in 3d space,
//hence mirroring is needed. In my case it's done to what suits my needs
if (viewVector.x < 0.0f)
{
angleBetweenOriginAndView *= -1f;
}
Vector3 correctedShootDirection = Quaternion.Euler(0, angleBetweenOriginAndView, 0) * uncorrectedShootDirection;
}
touchPair = null;
}