C# How to make a smooth jump in unity3d without mo

2019-09-06 09:08发布

I would like to make a smooth jump towards the nearest cube. I already have a script to detect the closest cube. I want that the X-axis is locked, so only the Y-axis and the Z-axis change when jumping. I would like to use a Jump animation when jumping. I already tried to use Vector3MoveTowards, but that didn't really work well, maybe I didn't use it properly.

Detect nearest cube where the player should jump to (C#)

void Update()
{
  FindClosestCube ();
  GameObject closestCube = FindClosestCube ();
  Debug.Log (closestCube);
}

GameObject FindClosestCube() {

		GameObject[] gos;
		gos = GameObject.FindGameObjectsWithTag("cube");
		GameObject closest = null;
		float distance = Mathf.Infinity;
		float position = transform.position.z;
		foreach (GameObject go in gos) {
			float diff = go.transform.position.z - position;

			float curDistance = diff;
			if (curDistance < distance) {
				closest = go;
				distance = curDistance;
			}
		}
		return closest;
	}

The tricky part is that at some cubes you have to jump up (y+1), with some cubes you jump towards the same Y (y+0) and with some cubes you jump down (y-1). How do I do this?

Image of how it looks like:

enter image description here

EDIT: I have this code right now:

	----------------C#-----------------

    Rigidbody rb;
	public int clicks = 0;
	Vector3 target;
	public Animation jumpAnimation;
	bool jump = false;
	float cubeDiffY;
	bool movePlayer;
	public float smoothTime = 0.3f;
	public float yVelocity = 0.0f;

	void Start()
	{
		rb = GetComponent<Rigidbody> ();
	}

	void Update () 
	{
		FindClosestCube ();
		GameObject closestCube = FindClosestCube ();
		Debug.Log ("Closestcube = " + closestCube);					

		target = closestCube.transform.position + new Vector3 (0f, 0.7f, 0f);

		cubeDiffY = target.y - transform.position.y;
		movePlayer = true;

		Debug.Log("Cube Difference Y-axis = " + Mathf.Round(cubeDiffY));

		if (Input.GetMouseButtonDown (0)) 
		{
			clicks += 1;

			jump = true;
			jumpAnimation = gameObject.GetComponent<Animation>();
			//jumpAnimation.Play ();
		}

		if (jump == true) 
		{
			Jump ();
		}
	}

	void Jump()
	{

		float newPosition = Mathf.SmoothDamp (transform.position.y, target.y, ref yVelocity, smoothTime);
		transform.position = new Vector3 (0, newPosition, transform.position.z);
	}

I calculated the difference in Y-axis between the cube where the player is standing on and the closestCube. But the Jump() doesn't work. How do I fix that?

标签: c# unity3d 3d
1条回答
姐就是有狂的资本
2楼-- · 2019-09-06 09:17

Okay I set up a quick version of your game and got what you wanted to work, it is not exactly a quick solution, because what your doing doesn't have built in functionality for other than using animations.

Here is the character script that has all the code you need and commented thoroughly so it should explain itself.

using UnityEngine;

public class Character : MonoBehaviour
{
    //the collider for the player
    private new BoxCollider collider;

    //the jump box collider on a empty game object that is a child to the player object
    public BoxCollider JumpBox;

    //the offset of the cube so it doesn't stop inside of it
    public Vector3 cubeOffset;

    //how high the jump will be
    public float JumpHeight;

    //how fast the jump will be
    public float JumpSpeed;

    //holds the change in position the jump will produce
    private Vector3 jumpDelta;

    //holds the destination cube the jump is attempting to hit
    private Cube destinationCube;

    //true if a jumping animation is currently playing
    private bool jumping = false;

    //used to swap the jump direction from up to down
    private bool jumpDirection = true;

    //used to hold the position of the jump so it knows when to stop
    private float jumpPosition = 0;

    // Use this for initialization
    void Start()
    {
        collider = GetComponent<BoxCollider>();
    }

    // Update is called once per frame
    void Update()
    {
        if(jumping)
        {
            //move straight towards the cube
            transform.position = transform.position + (JumpSpeed * jumpDelta);

            //move up and down to simulate a jump
            //check the current move direction
            if (jumpDirection)
            {
                //add to the jump position twice product of the JumpHeight the JumpSpeed so that it will 
                //rise and fall the same amount of time it takes to move to the destination
                jumpPosition += JumpHeight * JumpSpeed * 2;
                //if it has passed the jump height reverse the jump direction
                if (jumpPosition >= JumpHeight)
                    jumpDirection = !jumpDirection;
                transform.position += transform.up * JumpHeight * JumpSpeed * 2;
            }
            //the jump direction is going down
            else
            {
                jumpPosition -= JumpHeight * JumpSpeed * 2;
                transform.position -= transform.up * JumpHeight * JumpSpeed * 2;
            }
            //check if the character collider intersects witht he cubes collider
            //if it has then stop jumping and set the final position as the destination position
            if (collider.bounds.Intersects(destinationCube.BoxCollider.bounds))
            {
                jumping = false;
                transform.position = destinationCube.transform.position + cubeOffset;
            }
        }
        //detect a jump
        if (Input.GetKeyDown(KeyCode.Space))
        {
            //detect all hits on the jump box
            Collider[] hits = Physics.OverlapBox(JumpBox.center, JumpBox.size * 0.5f);
            //get the closest collider with the right tag
            Collider result = GetClosestColliderWithTag(hits, "Cube");

            //if we have a result then begin the jumping animation
            if(result != null)
            {
                //gets the destination cubes cube component(the custom class you have on your cubes)
                destinationCube = result.gameObject.GetComponent<Cube>();

                //calculate the jump delta
                jumpDelta = (result.transform.position + cubeOffset) - transform.position;

                //remove the left and right components so the jumping doesnt move to the left or right of the player
                Vector3 component = Vector3.Project(jumpDelta, -transform.right);
                jumpDelta -= component;
                component = Vector3.Project(jumpDelta, transform.right);
                jumpDelta -= component;

                //setup the jump animation control fields to the initial values
                jumpPosition = 0;
                jumpDirection = true;
                jumping = true;
            }
        }
    }
    private Collider GetClosestColliderWithTag(Collider[] colliders, string tag)
    {
        //just gets the closest collider
        float distance = float.MaxValue;
        int result = -1;
        for (int i = 0; i < colliders.Length; i++)
        {
            if (colliders[i].tag == tag)
            {
                float distanceTemp = Vector3.Distance(transform.position, colliders[i].transform.position);
                if (distanceTemp < distance)
                {
                    distance = distanceTemp;
                    result = i;
                }
            }
        }
        if (result != -1)
            return colliders[result];
        else return null;
    }
}

And here is my cube script which has some things you will need to add

using UnityEngine;

public class Cube : MonoBehaviour {
    //these arent important just fields I used to set up a quick version of your game
    public GameObject StartPoint;
    public GameObject EndPoint;
    public float Speed;
    private Vector3 directionVector;
    private bool direction;

    //YOU WILL NEED THIS!!
    [HideInInspector]
    public BoxCollider BoxCollider;


    // Use this for initialization
    void Start() {
        //not important
        directionVector = EndPoint.transform.position - StartPoint.transform.position;
        directionVector.Normalize();

        //DONT FORGET TO SET YOUR BOX COLLIDER
        BoxCollider = GetComponent<BoxCollider>();
    }

    // Update is called once per frame
    void Update()
    {
        float distance = 0;
        if (direction)
        {
            distance = Vector3.Distance(EndPoint.transform.position, transform.position);
            transform.position += directionVector * Speed;
            if (distance < Vector3.Distance(EndPoint.transform.position, transform.position))
                direction = !direction;
        }
        else
        {
            distance = Vector3.Distance(StartPoint.transform.position, transform.position);
            transform.position -= directionVector * Speed;
            if (distance < Vector3.Distance(StartPoint.transform.position, transform.position))
                direction = !direction;
        }
    }
}

Previous Answer

I would say you need to calculate the perceived position of the object in the future.

Vector3 futurePos = cubePos + (cubeMoveDirection * cubeMoveSpeed);

Once you have the future position, even if it is not exact, you should aim your animation towards that position. To do this I would have the animation change a speed vector instead of an actual transforms position that way we can rotate this speed vector in any direction you want while keeping the orientation of the block. Otherwise you have to rotate the entire block to point towards the direction you want. If this is what you want then put your block under a empty gameobject, rotate the empty gameobject to point to where you want and do the speed calculations only.

Next your animation should have a net move vector which should be pre-calculated and scaled down or up to meet the distance to the future position. It will look something like this(note this is not tested)

//class fields
Vector3 AnimatedSpeed;

Vector3 AnimationDelta;

//basic calculation

//get the direction vector from the players current position to the future 
block position

Vector3 dirVector = futurePos - transform.position;
//find the rotation from the current orientation to the direction vector
Quaternion rotation = Quaternion.FromToRotation(transform.forward, dirVector);

//calculate the distance from you to the cube and scale it with the magnitude of the AnimationDelta
float result = Vector3.Distance(transform.position, futurePos);

result = result / animationDelta.magnitude;

//finally rotate the forward vector by the rotation and multiply it by the 
//animation speed and the result to get the step by step movement as
//the animation plays. NOTE: The animation should be based on forward direction

transform.position += (AnimationSpeed * rotation) * result * Time.deltaTime;

Hopefully this does it, like I said I haven't tested it at all so you may have to do some tweaking based on your particular case as this is essentially psuedo-code.

Good luck! I'm off to bed I'll check back when I wake up.

查看更多
登录 后发表回答