Face the player towards a pushable when raycast hits with an angle - c#

I'm currently working on a pushing system and I'm having some issues. The player uses a CharacterController which can interact with Rigidbodies in the scene. Currently this works as follows: When a Raycast hits a pushable object in front of the player and presses the push button he will push the object, which all works fine. The issue occurs when the Player is slightly turned away from the pushable and starts pushing. I.e. when the character is off by like 10 degrees, but the ray still hits. The concept I had in mind was calculating the angle of the raycast that hits the pushable cube and rotating the character controller towards the pushable by that angle. The problem is I have no clue how to achieve this. I hope someone can help me with this concept. If anyone has any other concepts or ideas feel free to share them :)
Edit: I've added some scripts
The Character controller part:
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 : movementSpeed) * 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;
The Raycast setup and pushstates as off now:
if (Physics.Raycast(middle, (forward), out hit, distanceForPush))
{
if (Input.GetButton("Push") && hit.collider.tag == "PushableLight")
{
anim.SetBool("isPushing", true);
pushForce = playerPushForceLight;
movementSpeed = pushSpeedLight;
hit.transform.SendMessage("HitByPlayer", null, SendMessageOptions.DontRequireReceiver);
if (controller.velocity == Vector3.zero)
{
anim.SetFloat("pushSpeedAnim", 0f);
}
else
{
anim.SetFloat("pushSpeedAnim", 1f);
}
}
else if (Input.GetButton("Push") && hit.collider.tag == "PushableHeavy")
{
anim.SetBool("isPushing", true);
pushForce = playerPushForceHeavy;
movementSpeed = pushSpeedHeavy;
hit.transform.SendMessage("HitByPlayer", null, SendMessageOptions.DontRequireReceiver);
if (controller.velocity == Vector3.zero)
{
anim.SetFloat("pushSpeedAnim", 0f);
}
else
{
anim.SetFloat("pushSpeedAnim", 1f);
}
}
else
{
anim.SetBool("isPushing", false);
pushForce = 0f;
movementSpeed = walkSpeed;
hit.transform.SendMessage("HitStopped", null, SendMessageOptions.DontRequireReceiver);
}
}
else
{
anim.SetBool("isPushing", false);
}
AnimatorStateInfo stateInfo = anim.GetCurrentAnimatorStateInfo(0);
if (stateInfo.fullPathHash == pushStateHash)
{
turnSmoothTime = maxTurnSmoothTimePushing;
}
else
{
turnSmoothTime = 0.1f;
}
}

I would try to align the player with the hit's collision normal.
transform.rotation = Quaternion.LookRotation(-hit.normal, Vector3.up);
This will probably only work well for boxes, though.

Related

Gravity increases drastically when walking off edge when comparted to jumping

void UpdateMovement()
{
Vector2 targetDir = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
targetDir.Normalize();
currentDir = Vector2.SmoothDamp(currentDir, targetDir, ref currentDirVelocity, moveSmoothTime);
if (controller.isGrounded)
{
velocityY = -gravity;
}
// jump
if (controller.isGrounded && Input.GetKeyDown(KeyCode.Space))
{
velocityY = jumpForce;
}
// sprint
if (Input.GetKey(KeyCode.LeftShift))
{
velocity = (transform.forward * currentDir.y + transform.right * currentDir.x) * sprintSpeed + Vector3.up * velocityY;
}
else
{
velocity = (transform.forward * currentDir.y + transform.right * currentDir.x) * walkSpeed + Vector3.up * velocityY;
}
velocityY -= gravity * Time.deltaTime;
Debug.Log(velocityY);
controller.Move(velocity * Time.deltaTime);
}
This is the code that I am using to move and jump in my game. Those functions work fine, the problem is in the gravity when walking off a surface. When jumping gravity works as intended but when falling without jumping (walking off the edge) gravity is still set to it's default value (15) so the characters falls incredibly fast.
I understand that this is caused by having no function to change this value when falling from an edge but I have found no way of making this happen without breaking all of the vertical movement.
You can reset the gravity as Brackeys did in his FPS Controller Tutorial:
if (isGrounded) {
velocityY = -2f;
}

Unity character doesn't stop sprinting

I only recently got into Unity, and I have made a character movement system; everything is working besides from sprinting. I have the user use "Left Shift" to sprint, although it seems sprinting never turns off. I'm not quite sure the reasoning as to why this happens or what part of code could be fixed to accommodate for this, as such, I have attached the source file of movement.
Here is the paste: https://pastebin.com/S1h5pEwq
In particular, this is the movement function:
void Movement()
{
Vector2 axis = new Vector2(Input.GetAxis("Vertical"), Input.GetAxis("Horizontal")) * walkSpeed;
Vector3 forward = new Vector3(-Camera.main.transform.right.z, 0.0f, Camera.main.transform.right.x);
Vector3 wishDirection = (forward * axis.x + Camera.main.transform.right * axis.y + Vector3.up * rb.velocity.y);
rb.velocity = wishDirection;
if (Input.GetKey(KeyCode.LeftShift))
{
isSprinting = true;
}
else
{
isSprinting = false;
}
if (isSprinting == true)
{
walkSpeed = 10.0f;
}
}
More specifically, my goal is to have Sprint be activated strictly while the key is pressed.
Try this:
void Movement()
{
Vector2 axis = new Vector2(Input.GetAxis("Vertical"), Input.GetAxis("Horizontal")) * walkSpeed;
Vector3 forward = new Vector3(-Camera.main.transform.right.z, 0.0f, Camera.main.transform.right.x);
Vector3 wishDirection = (forward * axis.x + Camera.main.transform.right * axis.y + Vector3.up * rb.velocity.y);
rb.velocity = wishDirection;
if (Input.GetKey(KeyCode.LeftShift))
{
isSprinting = true;
}
else
{
isSprinting = false;
}
if (isSprinting == true)
{
walkSpeed = 10.0f;
}
else
{
walkSpeed = 0.0f
}
}
The problem in your script was that you were not resetting the speed.
If(Input.GetKeyUp(KeyCode.LeftShift)){
isSprinting = false;
}
This will do, you didn't check when the player stopped pressing shift.

How to raycasting to player facing direction?

in my 2D topdown game, I'm trying to cast a ray from my player to the direction he is facing. all going great yet the problem is the moment the player stop moving the raycast stopped(which makes sense I'm using Input.GetAxis which returns value of 0 when stop moving). I've been trying using Transform.right / TransformDirection its just makes the ray to stick at the same position and not where the player facing. is there any way for me to keep the ray cast keep going even if my player is stop moving?
public Collider2D GetBarrleCollider()
{
Physics2D.queriesStartInColliders = false;
RaycastHit2D hit = Physics2D.Raycast(this.transform.position, movement, playerDistance);
if (hit.collider != null && hit.collider.gameObject.CompareTag("Pickup"))
{
Debug.Log("Return NOT null" + " " + hit.collider.tag);
return hit.collider;
}
else
{
Debug.Log("Return null");
return null;
}
}
public void MovementHandler()
{
movement.x = Input.GetAxisRaw("Horizontal");
movement.y = Input.GetAxisRaw("Vertical");
rb2D.MovePosition(rb2D.position + movement * getPlayerMovementSpeed * Time.deltaTime);
}
You would store the direction as a variable. Lets call it lookDir. Then you should check if the movement vector is not zero:
Vector2 lookDir;
public void MovementHandler()
{
movement.x = Input.GetAxisRaw("Horizontal");
movement.y = Input.GetAxisRaw("Vertical");
rb2D.MovePosition(rb2D.position + movement * getPlayerMovementSpeed * Time.deltaTime);
if (movement != Vector2.zero)
{
lookDir = movement;
}
}
Then you could raycast from this vector. This way, you only change it if the player is moving.
Something like this should work:
public void MovementHandler()
{
movement.x = Input.GetAxisRaw("Horizontal");
movement.y = Input.GetAxisRaw("Vertical");
rb2D.MovePosition(rb2D.position + movement * getPlayerMovementSpeed * Time.deltaTime);
if (movement != Vector2.zero)
{
lookDir = movement;
}
}
public Collider2D GetBarrleCollider()
{
Physics2D.queriesStartInColliders = false;
//this line was changed \/
RaycastHit2D hit = Physics2D.Raycast(this.transform.position, lookDir, playerDistance);
//
if (hit.collider != null && hit.collider.gameObject.CompareTag("Pickup"))
{
Debug.Log("Return NOT null" + " " + hit.collider.tag);
return hit.collider;
}
else
{
Debug.Log("Return null");
return null;
}
}
Let me know in the comments if this doesn't work.

How to make 2d sprite move in a smooth wide arc in the direction of keypress?

I'm working on a small experimental project in Unity. I have a 2d sprite that moves forward with a velocity but I want it to turn left or right in a wide arc and keep moving in that direction on keypress.
I've tried to tweak its angular velocity to get the desired affect. Looks unnatural and it won't stop rotating.
Tried Lerping. Looks unnatural as well.
Code Snippet 1:
bool forward = true;
Vector3 movement;
void FixedUpdate()
{
if (forward)
{
//Moves forward
movement = new Vector3(0.0f, 0.1f, 0.0f);
rb.velocity = movement * speed;
}
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
forward = false;
movement = new Vector3(-0.05f, 0.05f, 0.0f);
rb.velocity = movement * speed;
rb.angularVelocity = 30;
}
if (transform.rotation.z == 90)
{
movement = new Vector3(-0.1f, 0.0f, 0.0f);
rb.velocity = movement * speed;
rb.angularVelocity = 0;
}
}
Code Snippet 2:
void Update(){
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
Vector3 target = transform.position + new Vector3(-0.5f, 0.5f, 0);
transform.position
=Vector3.Lerp(transform.position,target,Time.deltaTime);
transform.eulerAngles = Vector3.Lerp(transform.rotation.eulerAngles,
new Vector3(0, 0, 90), Time.deltaTime);
}
}
Can anyone point me in the right direction of what is the actual correct way to implement this?
Not entirely sure if this is what youre trying to accomplish but here's some pseudo code of what I came up with to get you started...
Essentially, when a direction is pressed, you want to increase the velocity in that direction until all of the velocity is pointed that way. At the same time you want to decrease the velocity in the direction you were previously going until it is zero.
This is a simplified formula however - if you truly wanted the velocity to be constant throughout the entirety of the arc you would have to use some geometry, knowing that V=(velX^2 + velY^2)^.5 but this would get you pretty close...
float yvel = 1f, xvel;
float t;
void Update()
{
GetComponent<Rigidbody2D>().velocity = new Vector2(xvel, yvel);
t += Time.deltaTime;
if (Input.GetKeyDown(KeyCode.D))
{
t = 0;
StartCoroutine(Move());
}
}
private IEnumerator Move()
{
while (t < 2) // at time t, yvel will be zero and xvel will be 1
{
yvel = 1 - .5f * t; // decrease velocity in old direction
xvel = .5f * t; // increase velocity in new direction
yield return null;
}
}

How can i make the spaceship to land automatic on the base?

The base is a red cube.
The spaceship is moving already when the game start.
When I click/press the L button the spaceship rotates to face the base and starts moving to it but then when it's getting close to the base it's behaving unexpectedly and the spaceship starts rolling around the base nonstop.
What I want is to make the landing automatic like this youtube video of blender.
I don't want the graphics but the way it's landing.
Blender landing spaceship
And this is a short video clip showing my spaceship when it's start landing:
Landing test
This is the script i'm using for controlling the spaceship and the landing part should be automatic.
The script is attached to the spaceship.
using UnityEngine;
using System.Collections;
public class ControlShip : MonoBehaviour {
public int rotationSpeed = 75;
public int movementspeed = 10;
public int thrust = 10;
public float RotationSpeed = 5;
private bool isPKeyDown = false;
private float acceleration = .0f;
private Vector3 previousPosition = Vector3.zero;
private Rigidbody _rigidbody;
private bool landing = false;
private Vector3 originPosition;
private Vector3 lastPosition;
private const float minDistance = 0.2f;
private Transform baseTarget;
// Use this for initialization
void Start () {
baseTarget = GameObject.Find("Base").transform;
originPosition = transform.position;
_rigidbody = GetComponent<Rigidbody>();
Debug.Log("Acc Speed: " + thrust);
}
// Update is called once per frame
void Update()
{
if (landing == false)
{
var v3 = new Vector3(Input.GetAxis("Vertical"), Input.GetAxis("Horizontal"), 0.0f);
transform.Rotate(v3 * rotationSpeed * Time.deltaTime);
transform.position += transform.forward * Time.deltaTime * movementspeed;
if (Input.GetKey(KeyCode.Z))
transform.Rotate(Vector3.forward * rotationSpeed * Time.deltaTime);
if (Input.GetKey(KeyCode.R))
transform.Rotate(Vector3.right * rotationSpeed * Time.deltaTime);
if (Input.GetKey(KeyCode.P))
{
isPKeyDown = Input.GetKey(KeyCode.P);
float distance = Vector3.Distance(previousPosition, transform.position);
acceleration = distance / Mathf.Pow(Time.deltaTime, 2);
previousPosition = transform.position;
_rigidbody.AddRelativeForce(0f, 0f, thrust, ForceMode.Acceleration);
}
}
else
{
transform.position += transform.forward * Time.deltaTime * movementspeed;
var targetRotation = Quaternion.LookRotation(baseTarget.position - transform.position);
var str = Mathf.Min(.5f * Time.deltaTime, 1);
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, str);
}
if (landed == true)
TakeOff();
if (Input.GetKey(KeyCode.L))
{
landing = true;
lastPosition = transform.position;
}
}
void OnTriggerEnter(Collider other)
{
if (landing == true && other.gameObject.name == "Base")
{
StartCoroutine(Landed());
}
}
bool landed = false;
IEnumerator Landed()
{
yield return new WaitForSeconds(5);
Debug.Log("Landed");
landed = true;
}
private void TakeOff()
{
if (transform.position != originPosition)
{
_rigidbody.AddForce(transform.up * 10);
}
if ((transform.position - originPosition).sqrMagnitude <= (1f * 1f))
{
landed = false;
_rigidbody.useGravity = false;
}
}
void OnGUI()
{
if (isPKeyDown)
{
GUI.Label(new Rect(100, 100, 200, 200), "Acc Speed: " + acceleration);
}
}
}
This is the part of the landing, should be the part of the landing:
transform.position += transform.forward * Time.deltaTime * movementspeed;
var targetRotation = Quaternion.LookRotation(baseTarget.position - transform.position);
var str = Mathf.Min(.5f * Time.deltaTime, 1);
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, str);
The spaceship have two components: Rigidbody, Use Gravity set to true. And a Box Collider.
The Base have a box collider component.
You appear to have a box collider on your vehicle and it looks as though it is colliding with the terrain when your code tries to bring it in for a landing. Try switching the collider to be a trigger(tick option on collider component).
Then try it again, as this will not cause physical collisions. If it works or fails for a totally different reason you know this is the cause or a contributing factor.
EDIT: It is worth also noting that when trying to achieve this kind of effect it can be much easier to trigger an animation than try to achieve it in physics based code. Unity offers some great animation controllers and you can call an animation when you need to land since you are taking control away from the player anyway.
While doing this you could turn off the collider so you don't get any strange collision then turn it on when the ship needs to take off, provided you need that of course.
Hope it helps.

Categories

Resources