I have a ball that jumps up a ladder, but when the jump ends, the ball still moves a short distance. It used to travel long distances and I just set freezeRotation = true, but the inertia still remained.
I would be glad to any advice. Here is my code
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public class ballcontroller : MonoBehaviour
{
//public int speed = 3;
public int jumpForce;
public bool IsGround;
private Rigidbody rb;
private void Start()
{
rb = GetComponent<Rigidbody>();
}
private void Update()
{
//float Vm = Input.GetAxis("Vertical");
//float Hm = Input.GetAxis("Horizontal");
//Vector3 V3M = new Vector3(Hm, 0, Vm);
//rb.AddForce(V3M * speed);
Jump();
}
public void Jump()
{
Ray ray = new Ray(gameObject.transform.position, Vector3.down);
RaycastHit rh;
if (Physics.Raycast(ray, out rh, 0.5f))
{
IsGround = true;
}
else
{
IsGround = false;
}
if (Input.GetKeyDown(KeyCode.Space) && IsGround)
{
rb.AddForce(Vector3.one * jumpForce);
rb.freezeRotation = true;
}
}
}
You can directly manipulate the velocity of your rigidbody using Rigidbody.velocity and Rigidbory.angularVelocity.
You could set it to Vector3.zero (or decrease it slowly to avoid hard freezing) after you hit the ground.
Related
I am making a game involving orbital physics. I was successfully able to implement this with a slightly modified version of Brackeys gravity tutorial https://youtu.be/Ouu3D_VHx9o, this is the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class gravity : MonoBehaviour
{
public GameObject self;
public Rigidbody rb;
public Vector3 initialVelocity;
const float G = 66.74f;
public static List<gravity> Attractors;
public bool isAttractable;
private void Awake()
{
rb.AddForce(initialVelocity);
}
private void FixedUpdate()
{
//planets
if (isAttractable == false)
{
foreach (gravity attractor in Attractors)
{
if (attractor != this)
Attract(attractor);
}
}
//players, spaceships, astroids, ect
if (isAttractable == true)
{
foreach (gravity attractor in Attractors)
{
if (attractor != this)
Attract(attractor);
}
}
}
void OnEnable()
{
if( isAttractable == false)
{
if (Attractors == null)
Attractors = new List<gravity>();
Attractors.Add(this);
}
}
void OnDisable()
{
if (isAttractable == false)
{
Attractors.Remove(this);
}
}
void Attract(gravity objToAttract)
{
Rigidbody rbToAttract = objToAttract.rb;
Vector3 direction = -1 * (rb.position - rbToAttract.position);
Vector3 Force = direction.normalized * (G * ((rb.mass * rbToAttract.mass) / direction.sqrMagnitude));
rb.AddForce(Force);
}
public GameObject GetClosestPlanet()
{
GameObject close = null;
float minDist = Mathf.Infinity;
foreach (gravity attracor in Attractors)
{
float dist = Vector3.Distance(attracor.transform.position, transform.position);
if (dist < minDist)
{
close = attracor.transform.gameObject;
minDist = dist;
}
}
return close;
}
}
Then for player movement I used (and modified) Sebastian Lagues tutorial https://youtu.be/TicipSVT-T8,
this resulted in this code for the player controller:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class playerController : MonoBehaviour
{
public float mouseSensitivityX = 250f;
public float mouseSensitivityY = 250f;
Transform cameraT;
float verticalLookRot;
private Rigidbody rb;
Vector3 moveAmount;
Vector3 smootgMoveVelocity;
public float moveSpeed = 15;
public float jumpForce = 220;
public LayerMask groundedMask;
public bool grounded;
public GameObject currentPlanet;
private gravity playerGravity;
private void Awake()
{
rb = GetComponent<Rigidbody>();
playerGravity = GetComponent<gravity>();
Cursor.lockState = CursorLockMode.Locked;
cameraT = Camera.main.transform;
}
void Update()
{
currentPlanet = playerGravity.GetClosestPlanet();
//camera
transform.Rotate(Vector3.up * Input.GetAxis("Mouse X") * Time.deltaTime * mouseSensitivityX);
verticalLookRot += Input.GetAxis("Mouse Y") * Time.deltaTime * mouseSensitivityY;
verticalLookRot = Mathf.Clamp(verticalLookRot, -60, 60);
cameraT.localEulerAngles = Vector3.left * verticalLookRot;
//move input
Vector3 moveDir = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")).normalized;
Vector3 targetMoveAmount = moveDir * moveSpeed;
moveAmount = Vector3.SmoothDamp(targetMoveAmount, targetMoveAmount, ref smootgMoveVelocity, .15f);
//level on planet
if(currentPlanet != null)
{
transform.rotation = Quaternion.FromToRotation(transform.up, (transform.position - currentPlanet.transform.position).normalized) * transform.rotation;
}
//jump
if (Input.GetButtonDown("Jump"))
{ if(grounded)
{
rb.AddForce(transform.up * jumpForce);
print("u jumped");
}
}
}
private void FixedUpdate()
{
//move
rb.MovePosition(rb.position + transform.TransformDirection(moveAmount) * Time.fixedDeltaTime);
//check if on ground
Ray ray = new Ray(transform.position, -transform.up);
RaycastHit hit;
grounded = Physics.Raycast(ray, out hit, transform.localScale.y + 1.1f, groundedMask);
}
}
Now for the issue, this systems works fine when the planet the player is walking on is stationary. As in there are no other attracting bodys in the system and the planet has no initial velocity. However if the planet is moving the player will bounce up and down uncontrollably and will not be able to walk a certain distance away from the planets farthest point from its direction of movement. Here is a recording of this: https://youtu.be/noMekosb7CU
Does anyone know what is causing the bouncing and walking restrictions and how I can fix it?
Some notes on suggested solutions that haven't worked:
-set the planet as the players parent object, same results
-increase players mass, same results
-set the players velocity to += the planets velocity, same results or player goes into infinity
For me it seems to be working "correctly".
Looking like your player is attracted correctly and when the planet moves, your player is quickly moving towards the planet.
I think you could temporarily assign the player as a child gameobject to the planet he's walking on and he should probably move correctly along the planet coordinates and not on global coordinates. (If it works, you could just always assign the player as a child gameObject to every new planet that he visits)
There was a problem with Unity animations. When the character jumps, the animation is played, but if the character jumps in either direction, the animation does not stop and the character with this animation rolls on the ground.
https://ibb.co/hLKmK6W
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovent : MonoBehaviour
{
public GameObject player;
public float speed = 10f;
private Rigidbody2D rb;
public int jump = 350 ;
Animator animation;
private bool inground;
// Start is called before the first frame update
void Start()
{
animation = GetComponent<Animator>();
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
}
void FixedUpdate()
{
var moveX = Input.GetAxis("Horizontal");
if (Input.GetKey(KeyCode.Space) && rb.velocity.y == 0)
{
rb.AddForce(Vector2.up * jump);
animation.SetTrigger("Jump");
}
rb.velocity = new Vector2(moveX * speed, rb.velocity.y);
if (animation)
{
animation.SetBool("Run", Mathf.Abs(moveX) >= 0.1f);
}
Vector3 charecterScale = transform.localScale;
if (Input.GetAxis("Horizontal") < 0)
{
charecterScale.x = -7.215315f;
}
if (Input.GetAxis("Horizontal") > 0)
{
charecterScale.x = 7.215315f;
}
transform.localScale = charecterScale;
}
}
You need to check if you are grounded or not. there is an example in this video:
https://youtu.be/CSu7MWv8qEY
The solution to this would be to check if the player is grounded, using Physics2D.Raycast() or Physics2D.OverlapCircle() and passing it as a boolean to the animator, like this:
public float radius = .5f;
public Vector2 offset = new Vector2(0, -.5f, 0);
public LayerMask layermask;
Animator animation;
Rigidbody2D rb;
void Start()
{
animation = GetComponent<Animator>();
rb = GetComponent<Rigidbody2D>();
}
bool IsGrounded() => Physics2D.OverlapCircle((Vector2)transform.position + offset, radius, layermask);
// to visualize the circle
void OnDrawGizmosSelected()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position + (Vector3)offset, radius);
}
void FixedUpdate()
{
animation.SetBool("isGrounded", IsGrounded());
}
I'm new to unity and I'm trying to create a game where there's a ball that can move
in the direction by dragging and releasing on the screen and that change direction randomly when hitting a prefab, I already created that kind of movement but couldn't figure out how to make the ball change direction randomly when hitting the prefab.
Sorry if this isn't the right place to ask.
Here's my script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
[SerializeField] private float power = 2;
[SerializeField] private Vector2 minPow, maxPow;
public Vector3 force;
private Vector3 startPoint, endPoint;
private Rigidbody2D rb;
private Camera cam;
private Aim aim;
void Start()
{
cam = Camera.main;
rb = GetComponent<Rigidbody2D>();
aim = GetComponent<Aim>();
}
void Update()
{
if(Input.GetMouseButtonDown(0))
{
startPoint = cam.ScreenToWorldPoint(Input.mousePosition);
startPoint.z = 15;
}
if(Input.GetMouseButton(0))
{
Vector3 currentPos = cam.ScreenToWorldPoint(Input.mousePosition);
startPoint.z = 15;
aim.RenderLine(startPoint, currentPos);
}
if(Input.GetMouseButtonUp(0))
{
endPoint = cam.ScreenToWorldPoint(Input.mousePosition);
endPoint.z = 15;
force = new Vector3(Mathf.Clamp(startPoint.x - endPoint.x, minPow.x, maxPow.x), Mathf.Clamp(startPoint.y - endPoint.y, minPow.y, maxPow.y));
rb.AddForce(force * power, ForceMode2D.Impulse);
aim.EndLine();
}
}
public void BoostUp(float pow)
{
rb.velocity *= pow;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Boost : MonoBehaviour
{
[SerializeField] float pow;
private void OnTriggerEnter2D(Collider2D other)
{
if(other.tag == "Player")
{
Player player = other.GetComponent<Player>();
if(player != null)
{
player.BoostUp(pow);
Destroy(this.gameObject);
}
}
}
}
You can get a random angle in radians using Random.Range(0f, 2f * Mathf.PI) and then pass it to Mathf.Cos and Mathf.Sin functions to get a direction with that angle:
float angle = Random.Range( 0f, 2f * Mathf.PI );
Vector2 direction = new Vector2( Mathf.Cos( angle ), Mathf.Sin( angle ) );
I am making a 2d game. My problem is that while playing, if the player holds jump and is under a BoxCollider2D, the player will not fall until they release jump.
My player GameObject consists of a sprite renderer, a dynamic rigidbody2d with gravity on and a boxcollider2d.
Here are my movement scripts:
1:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class JumpScript : MonoBehaviour {
[Range(1, 10)]
public float jumpVelocity;
[Range(0,10)]
public float speed;
private bool jumpQueue = false;
private bool boolin=false;
void Update()
{
//Friggin fall, loser
//Jumping
///*
if (Input.GetButton("Jump")&& GetComponent<Rigidbody2D> ().velocity.y==0)
{
GetComponent<Rigidbody2D>().velocity = Vector2.up * jumpVelocity;
}
//*/
//jumpQueue?
/*
if(Input.GetButtonDown("Jump"))
{
jumpQueue = true;
}*/
//Right Movement
if (Input.GetKey(KeyCode.D))
{
GetComponent<Rigidbody2D>().velocity = new Vector2(1*speed, GetComponent<Rigidbody2D>().velocity.y);
boolin = true;
}
if(Input.GetKeyUp(KeyCode.D))
{
GetComponent<Rigidbody2D>().velocity = new Vector2(0, GetComponent<Rigidbody2D>().velocity.y);
boolin = false;
}
//Left Movement
if (Input.GetKey(KeyCode.A))
{
GetComponent<Rigidbody2D>().velocity = new Vector2(-1*speed, GetComponent<Rigidbody2D>().velocity.y);
boolin = true;
}
if (Input.GetKeyUp(KeyCode.A))
{
GetComponent<Rigidbody2D>().velocity = new Vector2(0, GetComponent<Rigidbody2D>().velocity.y);
boolin = false;
}
//No movement?
if (Input.GetKey(KeyCode.D) && Input.GetKey(KeyCode.A))
{
GetComponent<Rigidbody2D>().velocity = new Vector2(0, GetComponent<Rigidbody2D>().velocity.y);
boolin = false;
}
//Time to handle animation, boios.
Rigidbody2D rb = GetComponent<Rigidbody2D>();
bool schwomp = false;
bool schwift = false;
if(rb.velocity.y>0)
{
schwomp = true;
}
if(rb.velocity.y<0)
{
schwift = true;
}
Animator anim = GetComponent<Animator>();
if (boolin)
{
anim.SetInteger("Boolin", 1);
/*if (!anim.GetBool("expand"))
{
anim.SetBool("expand", true);
anim.Play("running");
}*/
}
else
{
anim.SetInteger("Boolin", 0);
/*
if(anim.GetBool("expand"))
{
anim.SetBool("expand", false);
anim.Play("Idle");
}*/
}
if(schwomp)
{
//anim.SetInteger("Boolin", 2);
}
if(schwift)
{
//anim.SetInteger("Boolin", 3);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BetterJumper : MonoBehaviour {
public float fallMultiplier = 2.5f;
public float lowJumpMultiplier = 2f;
Rigidbody2D rb;
void Awake()
{
rb = GetComponent<Rigidbody2D>();
}
void Update()
{
if(rb.velocity.y<0)
{
rb.velocity += Vector2.up*Physics2D.gravity.y*(fallMultiplier-1)*Time.deltaTime;
}
else if(rb.velocity.y>0&&!Input.GetButton("Jump"))
{
rb.velocity += Vector2.up * Physics2D.gravity.y * (lowJumpMultiplier - 1) * Time.deltaTime;
}
}
}
Thank you so much in advance!
You're using Input.GetKey() which will poll the key every frame. This means more and more velocity is added the longer you hold jump. You've effectively built a jetpack rather than a jump force.
You should use Input.GetKeyDown() which will only fire once when a key is pressed down, then has to be released and pressed again in order to re-trigger. You then need to apply a single sufficiently strong vertical force using RigidBody.AddForce() to make the character jump, rather than adding continuously to the velocity.
Additionally, you should really be caching the result of your GetComponent<Rigidbody2D>() call when the script either wakes up or starts so that you're not calling it continuously; Each one of those calls takes processing time. Also, you should be using FixedUpdate() for physics.
public class ExampleClass : MonoBehaviour {
public float thrust;
public Rigidbody rb; // make the rigidbody variable available anywhere in the class
void Start() {
// cache the rigidbody component to the variable once on start
rb = GetComponent<Rigidbody>();
}
void FixedUpdate() {
// use the variable reference from now on rather than making GetComponent calls
rb.AddForce(transform.up * thrust);
}
}
Soviut's answer explains it quite nicely, but there's more that you need to know. You're directly manipulating the velocity, which overrides the effect of any forces, including gravity(despite the fact you're applying it manually). See the documentation.
As demonstrated in Soviut's answer you should be applying forces and impulses, letting the physics engine determine the velocity. The only time you should set velocity directly is when you're building a simulation that intentionally has unrealistic physics(i.e. retro platformers). Even in that case, bear in mind that doing so creates a lot more work, because you'll need to factor in every little thing that creates movement. This means you're essentially re-inventing the physics engine.
I have a basic 2D-oriented Character Controller - custom, that I'm writing for a 2.5D game with 3D models (Therefore I can't use the Unity2D physics and collision volumes).
My controller mostly works, however I'm hitting a strange little issue where every so often - apparently at a certain speed - it skips the collision check and falls through the floor or platform. Can someone spot what I'm doing wrong?
using UnityEngine;
using System.Collections;
public class PlayerController : MonoBehaviour
{
public float PlayerSpeed;
public float JumpPower;
public float _Gravity = 9.89f;
public Vector3 Flip = Vector3.zero;
private Vector3 MoveDirection = Vector3.zero;
private bool FacingLeft = false;
private bool FacingRear = false;
public bool GroundContact = false;
private Rigidbody RigidBody;
private void Awake()
{
RigidBody = GetComponent<Rigidbody>();
}
private void Update()
{
DetectionRays();
MoveDirection.x = PlayerSpeed * Input.GetAxis("Horizontal");
if (GroundContact) {
MoveDirection.y = 0;
if (Input.GetButtonDown("Jump")) {
MoveDirection.y = JumpPower;
}
} else {
MoveDirection.y -= _Gravity * Time.deltaTime;
}
Vector3 movementVector = new Vector3(MoveDirection.x * Time.deltaTime, MoveDirection.y * Time.deltaTime, 0);
transform.Translate(movementVector);
}
private void DetectionRays()
{
DetectDown();
}
private void OnCollisionEnter(Collision Collide)
{
if (Collide.transform.tag == "Ground")
{
GroundContact = true;
}
}
private void DetectDown()
{
RaycastHit Obsticle;
Vector3 RayDownPosit = transform.position;
RayDownPosit.y += 0.8f;
Ray RayDown = new Ray(transform.position, Vector3.down);
Debug.DrawRay(RayDownPosit, Vector3.down, Color.red, 0.05f, false);
GroundContact = false;
if (Physics.Raycast(RayDown, out Obsticle, 0.05f))
{
if (Obsticle.transform.tag == "Ground")
{
GroundContact = true;
}
}
}
}
First, you can attach Box Collider 2D and Rigidbody 2D to 3D models. Try it.
Are you sure you need a custom character controller? CharacterController.Move (or SimpleMove) does collision detection for you.
http://docs.unity3d.com/ScriptReference/CharacterController.Move.html
Alternatively, since you are using Rigidbodies, you should consider using ApplyForce or adding velocity.