Capsule collider out of sync with animation - c#

I am creating a player jump function and the collider does not achieve the same height as my players feet on jump.
I am currently trying to manually position the collider based on the position of the player. However, this has caused the capsule to now go way too high and because of the time it takes to fall, the player ends up halfway submerged in the ground.
void Jump() {
if (Input.GetKeyDown(KeyCode.Space)&& isGrounded == true) {
isGrounded = false;
actions.Jump();
rigidbody.AddRelativeForce(Vector3.up * jumpSpeed);
startTime = Time.time;
}
else {
if (transform.position.y < .6f) {
isGrounded = true;
}
}
}
private void Update() {
if (!isGrounded) {
dist = (Time.time - startTime) * capSpeed;
fracOfJourney = dist / journeyLength;
capCollider.center = Vector3.Lerp(start, end, fracOfJourney);
}
else {
// capCollider.center = Vector3.Lerp(end, start, fracOfJourney);
}
}

Rather than calculating the capsule collider position yourself a better solution would be to separate the rendering components of your player from the capsule collider. As a possible solution, you could have in your heirarchy:
Player Root: (rigidbody, player animator, Movement Script)
Renderer: (Mesh of player and renderer here)
Collider: (Capsule Colider here)
After doing this you could match the boundaries of your capsule collider by changing the scale and position as needed during your jumping animation. Separating the collider into it's own gameobject allows you to change the scale without effecting the appearance or position of the player.

Related

Move Object to the last position or position just outside the collision area

I am using a Player rigid body object and there are walls around the Player. These walls are restricting the Player to go through. The Player gets collided with these walls and then falls. The Player uses teleport function to jump from one area to next. Is there a way to make the Player jump to a position just outside the collision area after the Player is collided with these walls?
That is, Player A gets collided with the wall and does not jump to last position, but the position before the collision happened?
public GameObject Player;
public Vector3 PlayerPos;
public bool RecordPos = true;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if(RecordPos == true)
{
PlayerPos = Player.transform.position;
}
}
public void OnTriggerEnter(Collider col)
{
if(col.gameObject.name == "Cube(3)" )
{
RecordPos = false;
Player.transform.position = PlayerPos;
}
}
In this script, the Player moves to last position it teleported from.
The "last" position before colliding is 1 frame before the collision. If you capture this position, the character will probably just fall on the obstacle again. Imagine you have a platform ___...___ and an obstacle. The easiest solution is to have 1 trigger at the left side of the obstacles and 1 trigger at the right side. If the player hasn't overcome the obstacle yet, he will be teleported to a chosen destination by you (before the obstacle) and if he's already overcome the obstacle, he will be teleported at the right side. __S_..._S__ (S stands for save/checkpoint trigger)
You need the following script on the gameobject with the Trigger collider. You also need to create a child object to the gameobject with the trigger:
private void OnTriggerEnter2D(Collider2D collision)
{
SaveManager.Instance.LastCheckpointOnHit = transform.GetChild(0).position;
}
And I suppose you have some sort of a singleton for data persistance. Now you can move the child gameobject whereever you want to teleport the player. And BTW I named the property LastCheckpointOnHit, because I was thinking of Holow Knight where if you get hit by spikes it instantly teleports you.
Then you just move the player: Player.transform.position = SaveManager.Instance.LastCheckpointOnHit;
In general when dealing with Rigidbody you shouldn't apply positions through the Transform component but rather through the Rigidbody.
I could imagine something like if you collide with a wall you get pushed away from the wall a bit in the direction where you came from like e.g.
[SerializeField] private float someDistanceThreshold = 0.01f;
[SerializeField] private Rigidbody _rigidbody;
private void Start()
{
if(!_rigidbody) _rigidbody = Player.GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
PlayerPos = _rigidbody.position;
}
public void OnTriggerEnter(Collider col)
{
// instead of the name I would rather use a tag later to cover all obstacles
if(col.gameObject.name == "Cube(3)")
{
_rigidbody.position -= (_rigidbody.position - PlayerPos).normalized * someDistanceThreshold;
// For stopping the player here
_rigidbody.velocity = Vector3.zero;
}
}

How to Move and Jump in Unity

I am making a First Person Shooter Game and am currently working on the jumping. I am using animation to jump as I have struggled with gravity before. However my problem is that my player cannot move if my jump script is attached to the player as well as the movement. However I know that it has nothing to do with the scripts as both scripts work but not simultaneously.
I believe it has something to do with the animation. There are 3 parts to the animation, Base, Running and Jump. If the player is moving the running animation is active, if the player has pressed the space bar then the jump animation is active. Jump animation is prioritised.
Is there any reason why the player cannot move and jump?
Here is the codes and animation controller:
Animation and Jump
public class Jump : MonoBehaviour
{
public bool run;
public float speed;
public GameObject player;
public Animator dgbanim;
public bool jump;
public float rejump;
public float rejumper;
// Start is called before the first frame update
void Start()
{
speed = 50;
run = false;
jump = false;
rejumper = 0;
rejump = 0;
}
// Update is called once per frame
void Update()
{
if (jump == false && Input.GetKey("space")) { dgbanim.SetBool("Jump", true); jump = true; rejump = 21; }
if (rejump > 0) { rejump = rejump - 1; }
if(rejump == 0) { jump = false; dgbanim.SetBool("Jump", false); }
if (Input.GetKey("w") || Input.GetKey("s") || Input.GetKey("a") || Input.GetKey("d") || Input.GetKey("up") || Input.GetKey("down") || Input.GetKey("left") || Input.GetKey("right")) { run = true; }
if (run == true) { dgbanim.SetBool("Running", true); run = false; }
else if (run == false) { dgbanim.SetBool("Running", false); }
}
private void OnCollisionEnter(Collision collision)
{
//if (collision.gameObject.tag != "EnemyBullets") { jump = true; rejump = 0; }
if (collision.gameObject.tag != "EnemyBullets") { jump = false; }
}
}
Movement
public class Movement : MonoBehaviour
{
public GameObject player;
public float speed;
// Start is called before the first frame update
void Start()
{
speed = 50;
}
// Update is called once per frame
void Update()
{
player.transform.Translate(Input.GetAxis("Horizontal") * speed * Time.deltaTime, 0, Input.GetAxis("Vertical") * speed * Time.deltaTime);
}
}
It seems like your animator have ‘apply root motion’ checked and its taking movement from animations rather than code, try unchecking it.
Animation can control some properties of a GameObject, such as the position. If you try to edit a GameObject's position in a script but an animation controls the position, the animation will override the script's changes to the position.
To see if this is the problem, you can open the animation window and select your player. If the player's position (in the inspector) is red, then the animation is controlling it.
I know of 2 ways to fix this:
Method 1 (not recommended): Edit the animation to move the root bone instead of the player
Most character models have a GameObject called "Armature" or "root" as a child. The position of your character should not be in the animation. If you need to move the character up in your animation, you can animate the "Armature" or "root" GameObject. In the screenshot below, note that in the animation window you don't see "PlayerPref: position" but instead see "Armature: position". This allows my movement script to move PlayerPref without the animation overriding the position.
Method 2 (recommended): Use the physics engine to jump
In your animation, don't make the player jump. Instead make the player "jump in place" i.e. maybe swing arms upward as if jumping but don't actually move upward. Just like Method 1, your animation should not set the player's position. The easiest way to do this is to click on "player: position" in the animation window and press delete.
Add a Rigidbody to your character. In your script:
private Rigidbody rig;
...
void Start(){
rig = GetComponent<Rigidbody>();
...
}
In your script when you trigger jumping, you can add rig.AddForce(jumpPower * transform.up);. This will make the physics system handle the jumping. If the player is jumping but there is something above, the physics engine will stop the player. With animation, the player will jump through any obstacles.

Ball slows down in unity2D game

I have a blockbreaker type game coded using C# that mainly works fine but upon play testing i have found the ball will slow right down in certain situations! i.e if it wedges between to game object bricks the force of the ball rapidly slows down! 8 times out of ten this will not happen but other times it does and im unsure why! i will post the Ball script i think you will need to help solve this but should you need anymore information then please ask.
public class Ball : MonoBehaviour {
private Paddle paddle;
private bool hasStarted = false;
private Vector3 paddleToBallVector;
void Start () {
paddle = GameObject.FindObjectOfType<Paddle> ();
paddleToBallVector = this.transform.position - paddle.transform.position;
}
// Update is called once per frame
void Update () {
if (!hasStarted) {
//lock ball relative to the paddle
this.transform.position = paddle.transform.position + paddleToBallVector;
//wait for mouse press to start
if (Input.GetMouseButtonDown (0)) {
//if (Input.GetTouch(0).phase == TouchPhase.Ended){
hasStarted = true;
this.GetComponent<Rigidbody2D> ().velocity = new Vector2 (2f, 10f);
}
}
}
void OnCollisionEnter2D(Collision2D collision){
Vector2 tweak = new Vector2 (Random.Range(0f,0.2f),Random.Range(0f,0.2f));
if (hasStarted) {
GetComponent<AudioSource> ().Play ();
GetComponent<Rigidbody2D>().velocity += tweak;
}
}
}
You are adding directly to the velocity of the ball. The velocity variable defines a direction and speed, not just a speed as you are thinking here.
So, when the ball collides with a block it has a velocity of (+x, +y). After the collision and bounce is has a velocity of (+x, -y). So by adding a random positive value to the velocity when it has the -y velocity means that it will slow down the ball.
This does not happen every time because you are moving the ball in your Update() method. Change that to 'FixedUpdated()'. Fixed Update handles all physics calculations.
Also, I would recommend having a RigidBody2D on your Ball object. Moving physics objects should always have RigidBodies. With this, you can then use AddForce in the forward direction of your ball and that should solve your problem.
EDIT: I see you have a RigidBody2D already. I missed that the first time. Instead of having GetComponent<RigidBody2D>().velocity try GetComponent<RigidBody2D>().AddForce( transform.forward * impulseAmount, ForceMode.Impluse );

UNITY : Ball showing odd behaviour

I have a Ball(sphere) and a floor in my game scene. Ball.cs is attached to the ball to control its movement in the game(Ball only moves in the vertical direction). Both Ball and floor have colliders attached to them and whenever ball touches the floor, the game should ideally end.
OnCollisionEnter2D Method from the Ball.cs script.
private void OnCollisionEnter2D(Collision2D collision)
{
// Zero out the ball's velocity
rb2d.velocity = Vector2.zero;
// If the ball collides with something set it to dead...
isDead = true;
//...and tell the game control about it.
GameController.instance.PlayerDied();
}
Update function
void Update () {
//Don't allow control if the bird has died.
if (isDead == false)
{
//Look for input to trigger a "flap".
if (Input.GetMouseButtonDown(0))
{
//...zero out the birds current y velocity before...
rb2d.velocity = Vector2.zero;
// new Vector2(rb2d.velocity.x, 0);
//..giving the bird some upward force.
rb2d.AddForce(new Vector2(0, upForce));
}
}
}
But what's happening is whenever ball touches the ground, it starts rolling on the ground. It moves few units on +X-axis then rolls back and then ultimately stops.
position.X should ideally be 0(as the ball is moving only in Y-axis and it is during the game) but as soon as ball collides with the floor it starts moving.
I am new to Unity and I have no idea what is wrong.
Why is this happening?
EDIT:
Programmer's answer does work but I still don't understand where the horizontal velocity is coming from(there is horizontal velocity component associated with the ball). I need to know why ball is moving in horizontal direction.
I noticed that you are setting isDead to true when collision happens. If you don't want the ball to move again then set velocity to Vector2.zero; in the Update not only in the OnCollisionEnter2D function. Do this only if isDead is true.
void Update()
{
if (isDead)
{
rb2d.velocity = Vector2.zero;
}
}
Another option is to freeze the constraints when the collision happens. If you want the ball to start rolling again then unfreeze it.
private void OnCollisionEnter2D(Collision2D collision)
{
//Zero out the ball's velocity
rb2d.velocity = Vector2.zero;
//Freeze constraints
rb2d.constraints = RigidbodyConstraints2D.FreezeAll;
// If the ball collides with something set it to dead...
isDead = true;
//...and tell the game control about it.
GameController.instance.PlayerDied();
}
Execute rb2d.constraints = RigidbodyConstraints2D.None; to unfreeze it after.

Jump particle effect

I have a question about making a particle for jumping, like a dust cloud when the player jumps, here is my player script:
public class PlayerMovement : MonoBehaviour {
public float speed = 5f;
public Transform groundCheck;
public LayerMask groundLayer;
bool grounded = false;
Animator anim;
Rigidbody2D rgbd;
void Start () {
anim = GetComponent<Animator> ();
rgbd = GetComponent<Rigidbody2D> ();
}
void Update () {
}
void FixedUpdate (){
grounded = Physics2D.OverlapCircle (groundCheck.position, 0.2f, groundLayer);
float movex = Input.GetAxis ("Horizontal");
rigidbody2D.velocity = new Vector2 (movex * speed, rigidbody2D.velocity.y);
if (movex > 0){
transform.localScale = new Vector2(1,transform.localScale.y);
} else if (movex < 0){
transform.localScale = new Vector2(-1,transform.localScale.y);
}
if (Input.GetKey (KeyCode.UpArrow)){
if (grounded == true){
rgbd.AddForce (new Vector2(0f, 4f),ForceMode2D.Impulse);
} else {
grounded = false;
}
}
anim.SetFloat ("speed", Mathf.Abs (movex));
anim.SetBool ("grounded", grounded);
}
}
I want him to activate the particle system only once while in midair. I've tried a few things but when the player is in the air the particle system never stopped.
What most people do is create a new copy of the particle system on every need and destroy it later.
So what you would need is a brand new particle system. Expand the Emission tab. In there set the Rate to 0 (0 particles per second). Below Rate there should be an empty list called Bursts. Add one burst. Set Time to 0.0 (should be set by default) and number of particles to whatever you need. That will shoot 1 burst of particles whenever the particle system runs. Note that if Looping is ON than the burst will happen on beginning of every loop.
So far so good. Now make a prefab from it (watch a tutorial if you need). Then, in your code declare a Game Object variable that will serve you as a particle system:
public GameObject jumpParticles;
back to Unity, feed your prefab into the Jump Particles slot in inspector. Now it's all ready to be copied and pasted wherever you need it. So create a method for this:
void SpawnJumpParticles(Vector3 pos){
GameObject tmpParticles = (GameObject)Instantiate(jumpParticles, pos, Quaternion.identity); //look up how to use Instantiate, you'll need it a lot
Destroy(tmpParticles, 3f);
}
this code will spawn particles and auto-destory them in 3 seconds. The pos argument in the function is where the particles will get created. All that's left is to call it from your code where you start the jump. I'll leave that to you :)) good luck.

Categories