Throw a GameObject

Hi,

while waiting for my question getting to be published, I think I got what I want:
I tried to throw a GameObject like in the old zelda games (let Player grab something and trhow it to the left or right, up or down).

I tried with AddForce, but that didn’t do it, it had no curve.

Now I just set the rigidbody2d’s Position to a specific Vector2(3f,6f) to get it a little higher while moving on the x axis and let gravity handle the fall.
Falling will be stopped, when the object reaches the y Position of the Player (set kinematic to true, gravity to 0).

My question now is: could this be handled in a better way? Because in other threads some said, that you should not access velocity or position of a rigidbody2d directly, instead use AddForce.

Best regards,
Christopher

EDIT:

Ok, so here is the code:

First the Player class(only the throw relevant part):

void FixedUpdate () {
		//Debug.Log ("Height: " + Screen.height + " Width: " + Screen.width);
		//Debug.Log ("Bounds Height: " + height + " Bounds Width: " + width);
		Resolution[] res = Screen.resolutions;
		//Debug.Log ("Screen resolution: " + Screen.currentResolution.width);
		Movement ();
		EnforceBounds ();
	}

	void Movement(){
		anim.SetBool ("walk", false);

		if (Input.GetKey (KeyCode.D)) {
			anim.SetBool ("walk", true);
			transform.Translate(Vector2.right *3f * Time.deltaTime);
			transform.eulerAngles = new Vector2(0,0);
			//Disable diagonal walking
			//return;
		}
		if (Input.GetKey (KeyCode.A)) {
			anim.SetBool ("walk", true);
			transform.Translate(Vector2.right *3f * Time.deltaTime);	
			transform.eulerAngles = new Vector2(0,180);
			//transform.GetChild(0).transform.eulerAngles = new Vector2(0,180);
			//return;
		}
		if (Input.GetKey (KeyCode.W)) {
			anim.SetBool ("walk", true);
			transform.Translate(Vector2.up *3f * Time.deltaTime);	
			//return;
		}
		if (Input.GetKey (KeyCode.S)) {
			anim.SetBool ("walk", true);
			transform.Translate(-Vector2.up *3f * Time.deltaTime);
			//return;
		}
		if (Input.touchCount> 0) {
			anim.SetBool ("walk", true);
			transform.Translate(-Vector2.up *3f * Time.deltaTime);
			return;
		}
		if (Input.GetKey (KeyCode.E)) {
			if(PickUp.pickUpObject && !PickUp.picked){
				PickUp.picked = true;
				//PickUp.throwItem = false;
			}
		}
		if (Input.GetKey (KeyCode.R)) {
			if(PickUp.pickUpObject && PickUp.picked){

				PickUp.picked = false;
				PickUp.throwItem = true;
			}
		}

	}

Now the PickUp Script (which includes the throwing logic):

public class PickUp : MonoBehaviour {
	public static bool pickUpObject = true;
	public static bool picked = false;
	public static bool throwItem = false;
	GameObject player;
	// Use this for initialization
	void Start () {
		player = GameObject.Find("Player");
	}
	
	// Update is called once per frame
	void Update () {
		if (picked) {
			collider2D.isTrigger = true;

			transform.parent = player.transform;
			Vector3 pos = GameObject.Find("startCurve").transform.position;
			transform.position = pos;
		}


	}

	void FixedUpdate(){
		if (throwItem) {
			StartCoroutine ("ThrowItem");

		}
	}

	IEnumerator ThrowItem(){
		rigidbody2D.gravityScale = 1f;
		rigidbody2D.isKinematic = false;
		float playLowerY = player.transform.position.y - player.renderer.bounds.size.y/2+renderer.bounds.size.y/2; 
		//			Vector3 targetVec = transform.position + new Vector3(3f,6f,0f);
		//			Vector3 force2 = (rigidbody2D.position-targetVec).normalized*20;
		//	Debug.Log ("forceX: " + force2.x + " forceY: " + force2.y);
		transform.parent = null;

At this point I first tried with AddForce instead of rigidbody2D.position, but that didn’t get me a curve

		Vector2 force = new Vector2(3f,6f);
		rigidbody2D.position += force*Time.fixedDeltaTime;
		if(transform.position.y < playLowerY){
			rigidbody2D.isKinematic = true;
			rigidbody2D.gravityScale = 0f;
			throwItem = false;
			collider2D.isTrigger = false;
		}
		yield return null;
	}
/*	void OnTriggerEnter2D(Collider2D other){
		pickUpObject = true;
	}

	void OnTriggerExit2D(Collider2D other){
		pickUpObject = false;
	}
	*/
}

Hope it becomes clear now.

Well, What i’m seeing is a misconception about forces and velocities. This won’t be a direct answer to your question, but it will address the issue about not having a curve to your thrown item.

I’ll be as precise and clear as I can, if you have any question, do ask, I’ll try my best to answer.

Your item needs to have forces applied to it in order to move. these forces can be applied at any time, they’re actually additive (they add to the velocity). Let me “illustrate” :

1 : if you have force applied once,

  • it will take that as its velocity, and move forever unless another force stops it (or you kill its velocity - either by accessing it directly, or making it “hit” something that stops it).
  • this movement is linear. it’s always the same, it’s never faster or slower, it’s just straight.

2 : if you have force applied every frame,

  • it will take the force, add it to its current velocity, and then move accordingly. What this means is that it will start off slowly, but the force will keep “stacking” and “stacking” so it goes progressively faster. - Again, you can stop it by killing its velocity and stop adding forces.
  • this movement is progressive. it will go faster every frame.

Now, to actually simulate the effect you want, You need :

  • a horizontal force to “Throw” the object. - This force should be 1 big force applied once. The throw.
  • a vertical force to simulate gravity - This force should be a small force, applied every frame.

The result should be an object moving forward, and gradually going down, faster and faster every frame, until it “hits” the ground.

Now, if you already knew all that, I am very sorry for misunderstanding.

1 tiny little edit : When i say every frame, I mean called in FixedUpdate()… that function is there to actually deal with those kind of situation


EDIT - here’s some example code :

public class PickUp : MonoBehaviour {
	
	float power = 100;
	Vector3 gravity = Vector3.down * 0.01f; //this is the same as new Vector3(0,-0.01f,0);
	
	void FixedUpdate()
	{
		if (thrown)
		{
			rigidbody2D.AddForce(gravity);
		}
		//Detect when it lands to actually kill velocity, and stop applying gravity
	}
	
	public void Throw()
	{
		rigidbody2D.AddForce(transform.forward * power); //this vector should be the player's forward vector
		thrown = true;
	}
}

now, when in your player class, you will need to call Pickup.Throw() when you want to throw it.

if (Input.GetKey (KeyCode.R)) {
			if(PickUp.pickUpObject && PickUp.picked){
				
				PickUp.picked = false;
				PickUp.Throw();
			}
		}

A better way to use this would be to calculate the “power” as a vector entirely inside the player script, and then pass that as a parameter to your pickup. Then you’d get something like :

public void Throw(Vector3 power)
	{
		rigidbody2D.AddForce(power)
		thrown = true;
	}

Typically it is a one-time AddForce()…no IEnumerator. The amount of force needed is significant. To figure things out, start with an empty scene and a projectile where you do a Rigidbody2D.AddForce() in the Start method of that projectile. Something like:

void Start() {
    rigidbody2D.AddForce(new Vector2(1,1) * 400f);
}

Isn’t it bad practice to do Physics stuff all over the place?
You can simply do

    if(CrossPlatformInputManager.GetButtonDown("Jump"))
    {
        forceToAply = 25.0f;
        isThrown = true;
    }

and then in fixed update do

        playerRb.AddForce ( new Vector2 ( playerRb.velocity.x, forceToApply ), ForceMode2D.Impulse );

        if ( forceToApply > 0 && isThrown )
            forceToApply = 0;
            isThrown = false;

Assuming this is 2D and If you are trying to avoid using physics and want a tighter mechanic you could take lessons from this mario jump implementation

###1