- Home /
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.
It is likely your original question was rejected. Usually when a question is rejected a message is 'sent,' but the Send $$anonymous$$essage feature of the moderation queue is broken (and has been for a very long time). The message is appended to your question, but it is not emailed to you. If you leave your question open in your browser, you can hit refresh and see the text of any send message.
As for this question, typically it also would be rejected. Without your script attached, we can only guess at your problem. It is likey that you were using AddForce() every frame rather than a single impulse force, but again that is only a guess.
Please update your question with your code.
Hi robertbu,
thanks for your answer, I will post a Little code when I'm home.
$$anonymous$$eanwhile, a Little more Detail: I have two scripts that go together here, a Player and a Throw script. Player is attached to the Player and Throw to the GameObject that could be thrown.
In the PlayerScript's Update-$$anonymous$$ethod, I only set some bool Parameters which are in the Throw-Script and can be accessed from Player. The logic (if picked up and can be thrown) is in the Throw Script: -Pickup logic in update because I only set the transform.parent and no rigidbody -Throw-Logic in FixedUpdate because of the rigidbody changes
So, as you assumed, I added the AddForce in the FixedUpdate-$$anonymous$$ethod. But where should I put AddForce? In my opinion, the force should be set by the Player who will throw the object, but then it would be located in the Players' update-method, which is also called every frame, am I correct?
As I said, I will post the code later, but maybe you understand the Details and can suggest another way...otherwise feel free to wait until I posted my code;)
The object is going forward only ? with no curve to make it land ? that sounds like a gravity force is not being added...
You can use AddForce to initiate the push it needs to go forward, and nothing prevents you from adding a gravity force to pull it down a little bit every frame
Hi jokim,
no, if I replace the rigidbody2d.position with AddForce(force), then it will fall down and while falling down, move to the right, because force = new Vector2(3f,6f), but I'd like to have a curve, that's why I added the 6f as the y value...it seems logical to me, but not to unity;)
Answer by jokim · Aug 28, 2014 at 07:14 PM
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;
}
Thanks for your answer.
To 1. I think, from my experience until now, that if I apply a force once, I'll achieve the effect I want...it's not additive if I understand it correctly
Whereas
if applied in every update, it will go straight through, because it's applied over and over.
But I took your last sentence and applied a much stronger x force and less force for y to get a better looking effect.
if applied in every update, it will go straight through, because it's applied over and over.
Things are not that simple. Gravity is also a force being applied every frame. So your object will only continue up if the amount of up force you are applying each frame is greater than gravity. Put another away, imagine a powered missile fired horizontally off a cliff. A horizontal (but not vertical) force would be added each frame. The missile would drop due to gravity.
Yeah you're right...Since I only apply a horizontal force, but in my case, I applied a force in x and y...that's why it goes straight up and right, although gravity should be applied...and gravity is in my case 9.81...and the y force in my case is 4...so I'm completely confused^^
errrr, I'm not sure we're on the same wavelength... I added a little code snippet to my answer to demonstrate what i meant, tell me if it helps any
I didn't mean you had to choose between one way or the other to add forces, you can actually combine both.
Ok, let me rethink: velocity is for horizontal movement and gravity is for vertical.
On Throw you give a one time horizontal force (cause transform.forward equals (1,0,0))...and you set thrown to true, it's a one time Event.
In the update, you let gravity do its work to pull it down, while having the horizontal movement of the Throw method...right? But at this time, if I would comment out your AddForce, nothing will happen, because it is not applied on the object yet. This is done by your AddForce...
And I think, to Combine both without extra variables, I could write:
AddForce(100,gravity);
executed one time? I have to add, that in my Project Settings, the y gravity value has been set to -9.81 (Default) and the gravity scale of the object is 1, otherwise it would not work, right? Then I had to do it like you showed me.
And it sounds good to calculate the power in the player's class, cause the Player could have a strength property and the objects to be thrown have different masses.
So, I'm really sorry if I bother you with things that seem simple.
Answer by robertbu · Aug 28, 2014 at 03:18 PM
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);
}
Ok, I tried that in an empty scene, but in the following way:
void Start () {
rigidbody2D.AddForce (new Vector2(1,4),Force$$anonymous$$ode2D.Impulse);
}
If I replace the "Force$$anonymous$$ode2D.Impulse" with 400f, Unity gives me an error message: The best overloaded method match for ... has some invalid arguments, because it wants a Force$$anonymous$$ode2D. But yeah, it will give me the result that I wanted (with the Force$$anonymous$$ode2D.Impulse), and that's ok for the start method, but I'd like to have it when the player presses a key...and therefore it has to be in the players' update method, am I right?
Actually, no. I had a mistake in my code. If you want to use Inpulse and do it in Update(), your code could be:
void Update() {
if (Input.Get$$anonymous$$ouseButtonDown(0)) {
rigidbody2D.AddForce (new Vector2(1,4) * 400f,Force$$anonymous$$ode2D.Impulse);
}
}
$$anonymous$$y point in having you do it in Start() was to give you a $$anonymous$$imal working example. That way you'd know the concept was good, and if the object was not flying the way you want, it was a problem with your other logic, not with the AddForce().
Alright, I agree the concept is fine:)
I now tried the following in the Update method of the PickUp script attached to the object that could be thrown:
void Update () {
if (picked) {
collider2D.isTrigger = true;
transform.parent = player.transform;
Vector3 pos = GameObject.Find("startCurve").transform.position;
transform.position = pos;
}
if (throwItem) {
rigidbody2D.gravityScale = 1f;
rigidbody2D.is$$anonymous$$inematic = false;
//StartCoroutine ("ThrowItem");
rigidbody2D.AddForce(new Vector2(1,4),Force$$anonymous$$ode2D.Impulse);
}
}
It will go straight to the top, because Update is called every frame and throwItem has not been set to false...but if I add
throwItem = false;
in the if block, it will fall down and it seems as if the addForce is not able to "fulfill" its work.
In the IEnumerator with the yield return null in it it has the time to end. So where should I put the AddForce? Is there some concept I haven't read about yet (sure there is, cause I started learning Unity two weeks ago;) ) or am I thinking completely wrong?
You should use something like the code I outlined. Replace 'if (throwItem) {' with:
if (Input.Get$$anonymous$$ouseButtonDown(0)) {
This 'Input.Get$$anonymous$$ouseButtonDown(0)' will only be true for one frame, so you will only get one application of force per mouse button press. You could use a key press ins$$anonymous$$d:
if (Input.Get$$anonymous$$eyDown($$anonymous$$eyCode.Space)) {
Note longer term, you'll likely have to introduce the concept of grounded to prevent users from making the object jump again while it is in the air (assu$$anonymous$$g you want to limit that behavior).
Aaaah, I got it...the problem has been my understanding of setting a bool to true or false and a $$anonymous$$eyDown...I tried with $$anonymous$$eyDown in the update of the object and now it's like I wanted it to look like:) How can I upvote this as the correct answer? There is no "Thumbs Up" Button In the comment...
$$anonymous$$aybe you can tell me if it's better to apply the force in the Player script (meaning to get a reference of the object to be thrown in the Player class) or adding the force in the object's script? Is maybe more of a good structure question...
Answer by diliupg · Aug 10, 2019 at 10:34 AM
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;
Answer by sacredgeometry · Aug 10, 2019 at 10:36 AM
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
Video
Your answer
![](https://koobas.hobune.stream/wayback/20220613161730im_/https://answers.unity.com/themes/thub/images/avi.jpg)