Projectile motion with user specified angle and speed using RigidBody2D - c#

Edited:
I've got a slider from 0-90 for the user to select an angle, and a text field for them to enter a speed. All vids I've seen either have people using their own physics or are retrieving an angle directly from the angle of the "gun" or the mouse position, but that's not what I need. I've got it to where gravity affects it now, but it just drops after it spawns, even if I set the speed to 50. I want the speed to determine the dx and dy values at the time the ball is spawned, but it seems that's not happening.
I believe I can get the slider value with GameObject.Find("AngleSlider").GetComponent().angleSlider.value
and should be able to do something similar with the text box value for the speed if I can cast it to a float(?) so I think I'm fine there unless anyone notices a problem with that.
Any help is appreciated for this beginner. Thanks!
Edit: By request I've added the code I have atm for this part. This is the ball spawner that I've attached to the cannon. The CannonBall mentioned is another script attached to the cannonball prefab. I have public uninitialized dx, dy, and speed floats in the CannonBall script.
public GameObject cannonBallPrefab;
public Transform shotPoint;
void Start () {
}
void Update () {
if(Input.GetKeyDown("space"))
{
GameObject ball = Instantiate(cannonBallPrefab, shotPoint.position, Quaternion.identity);
CannonBall ballMovement = ball.GetComponent<CannonBall>();
ballMovement.dy = Mathf.Sin(GameObject.Find("AngleSlider").GetComponent<SliderHandler>().angleSlider.value * Mathf.Deg2Rad) * ballMovement.speed;
ballMovement.dx = Mathf.Cos(GameObject.Find("AngleSlider").GetComponent<SliderHandler>().angleSlider.value * Mathf.Deg2Rad) * ballMovement.speed;
Debug.Log("ball is spawned");
}
}

Related

Rigidbody behaves differently when I select another gameobject in the scene

This the strangest thing i ever saw, i swear! So i have a script to the player that when you click on screen the cube jumps in the direction of the arrow(the arrow rotates 360 degres).I changed some settings to the rigidbody2d to make the jump better.Now here comes the strange part,when i run the game and the player is selected in the scene or hierrachy the jump works fine,if i select something else for exemple the camera something chenges the jump,from a very good one to a very bad one,i tryed to build the project and test and is the same,the cube dosen't have the same "Jump".I think it's becouse i changed something in the rigidbody,idk.PLS HELP!!!center
P.S if u ask for the script here it is :
public void Update()
{
Lava.transform.position = new Vector2(transform.position.x, Lava.transform.position.y);//other,not important
cam.transform.position = transform.position + offset;//camera follow player
Arrow.transform.RotateAround(gameObject.transform.position, new Vector3(0, 0, 180), ArrowSpeed * Time.deltaTime); //to make the arrow rotate 360
if (Input.GetMouseButtonDown(0))
{
rb.AddForce(Arrow.transform.right * -ImpulseForce * Time.deltaTime, ForceMode2D.Impulse); //this makes the cube jump
}
//other
score = (int) transform.position.y;
ScoreUI.text = score.ToString();
}
Firstly, RotateAround method takes a normalized vector as its second parameter, such as Vector3.forward in your case.
Secondly, applying any force to a rigidbody should be done in FixedUpdate, not Update (see Rigidbody.AddForce and ForceMode)
In your case it's an impulse, and it's only called when you click, so no need to multiply by delta time.
rb.AddForce(Arrow.transform.right * -ImpulseForce, ForceMode2D.Impulse);
It may not solve all of your issues, but without further information it's tough to know. At least you wont have weird physics behaviors anymore.

Instantiate a prefab pointing in the direction of another object

The Issue
I'm making a 2D Unity game where the main weapon of your character is a fireball gun. The idea is that a fireball will shoot out of the player's hand at the same angle the player's hand is pointing. I have 3 issues:
When I shoot the fireball, since the fireball is a RididBody, it pushes the player. This is because I've made the centre of the player's arm (the same place where the fireball shoots from) the point at which the arm rotates around the player (what is meant to be the shoulder);
To instantiate the fireball prefab on the arm, the only way I know how to do it is by using a piece of code which requires the arm to be a RigidBody. This means that the arm is affected by gravity and falls off the player on start unless I freeze the arm's y-axis movement, which means that when the player jumps, while the arm does not fall, it floats at the same y-position as where it started while moving along the x-axis; and
When the fireball is shot, the angle from which it is propelled after being shot is not the same angle as the angle of the player's arm.
Instantiating the Fireball
if (Input.GetKeyDown(KeyCode.Space))
{
pew.Play();
var fireballTransform = Instantiate(fireballPrefab); //creates a new shot sprite
fireballTransform.position = new Vector3(transform.position.x + horizMultiplier, transform.position.y, transform.position.z);
fireballTransform.rotation = orientation;
fireballTransform.transform.Rotate(0, 0, transform.rotation.z);
}
if (Input.GetKeyDown(KeyCode.D)) // moves right
{
orientation = 0;
horizMultiplier = 0.08F;
}
if (Input.GetKeyDown(KeyCode.A)) // moves left
{
orientation = 180;
horizMultiplier = -0.08F;
}
This piece of code is located within the script applied to the player's arm. The movement of the arm works fine and the problem seems to be either within this piece of code or the code for my fireball (which I will put next). A few definitions:
pew is a sound effect played when the fireball is shot;
horizMultiplier is the distance from the arm's centre which I would like the fireball to instantiate (also dependant of if the player) is facing left or right); and
orientation is which direction the player is facing (left or right). The fireball is then instantiated facing that same direction.
Fireball Script
public Vector2 speed = new Vector2(); // x and y forces respectively
private Rigidbody2D rb; // shorthand
private float rotation;
void Start()
{
rb = GetComponent<Rigidbody2D>(); // shorthand
rotation = rb.rotation;
if (rotation == 0)
{
rb.AddForce(Vector3.right * speed.x); // propels right
}
if (rotation == 180)
{
rb.AddForce(Vector3.left * speed.x); // propels left
}
}
I believe this code is explanatory enough with comments (if not please comment and I'll address any question). I believe an issue could also be in this piece of code because of the lines: rb.AddForce(Vector3.right * speed.x); and rb.AddForce(Vector3.left * speed.x); as these add directional forces to the object. I don't know is this is objective direction (right or left no matter what direction the object the force is being applied to is facing) or if it's right or left in terms of the object-- say if an object was rotated 90 degrees clockwise and that object had a force applied so that it moves right making the object move downwards.
What I'm expecting to happen is the player's arm will turn so that when a fireball is fired it is fired in the direction the arm is facing. The arms turning mechanics are fine, it's just trying to properly instantiate the fireball. Can anyone help with any of the issues I've laid out?
How I would go about this:
Add an empty transform as child of the arm and move it to where the fireball should spawn. Also make sure the rotation of it is such that its forward vector (the local z-axis) points to where the fireball should go. To see this, you need to set "Local" left of the play button.
Spawn the fireball at the transform's position with its rotation.
Ignore collision between the arm's/player's collider and the fireball's collider with https://docs.unity3d.com/ScriptReference/Physics.IgnoreCollision.html. If necessary, you can enable the collision between the two colliders again after 1s or so.
Set the fireball rigidbody's velocity to speed * rigidbody.forward.
If you need help with the code, please post it so I can see what's going on.
Edit:
The arm definitely doesn't require a Rigidbody.
You can just use Instantiate(prefab, position, rotation) as shorthand.
Is this for a 2D game?
Also, I'm going to sleep now but I'll gladly try to help tomorrow.
Your Issues
1) You should be masking the fireball's layer not to collide with your player's layer.
You can find more info about this here: https://docs.unity3d.com/Manual/LayerBasedCollision.html
(note: make sure you're on the Physics2D panel, and not the Physics one, as that's for 3D)
2) There is a setting called gravityScale in RigidBody2D. You should set this to 0 if you don't want gravity to be affecting your object. More info: https://docs.unity3d.com/Manual/class-Rigidbody2D.html
Fireball Instantiate
if (Input.GetKeyDown(KeyCode.Space))
{
pew.Play();
var position = hand.transform.position + hand.transform.forward * horizMultiplier;
var rotation = hand.transform.rotation;
var fireball = Instantiate<Fireball>(fireballPrefab, position, rotation);
fireball.StartMoving();
}
Fireball Script
private Rigidbody2D rb; // shorthand
private float rotation;
public float Speed;
public void StartMoving()
{
rb = GetComponent<Rigidbody2D>(); // shorthand
rb.velocity = transform.forward * Speed;
}
void OnTriggerEnter(....) { .... }

I have no syntax errors in Unity, How can I find errors so I learn why my crosshair stays in the center of the screen?

For Unity Game: shouldn't the cross-hair move accordingly as the player rig does to point at enemies? how can I find error with no syntax error messages?
I tried visiting Unity forums I found support about raycasting followed directions add some code to the crosshair.cs script I receive no syntax errors.
public class CrossHair: MonoBehaviour{
[SerializeField] private GameObject standardCross;
[SerializeField] private GameObject redCross;
float moveForce = 1.0f;
float rotateTorque = 1.0f;
float hoverHeight = 4.0f;
float hoverForce = 5.0f;
float hoverDamp = 0.5f;
Rigidbody rb;
private RaycastHit raycastHit;
void Start()
{
standardCross.gameObject.SetActive(true);
rb = GetComponent<Rigidbody>();
// Fairly high drag makes the object easier to control.
rb.drag = 0.5f;
rb.angularDrag = 0.5f;
}
void Update()
{
// Push/turn the object based on arrow key input.
rb.AddForce(Input.GetAxis("Vertical") * moveForce * transform.forward);
rb.AddTorque(Input.GetAxis("Horizontal") * rotateTorque * Vector3.up);
RaycastHit hit;
Ray downRay = new Ray(transform.position, -Vector3.up)
if (Physics.Raycast(downRay, out hit))
{
//The "error" in height is the difference between the desired height
// and the height measured by the raycast distance.
float hoverError = hoverHeight - hit.distance;
// Only apply a lifting force if the object is too low (ie, let
// gravity pull it downward if it is too high).
if (hoverError > 0)
{
// Subtract the damping from the lifting force and apply it to
// the rigidbody.
float upwardSpeed = rb.velocity.y;
float lift = hoverError * hoverForce - upwardSpeed * hoverDamp;
rb.AddForce(lift * Vector3.up);
}
}
Ray targettingRay = new Ray(transform.position, transform.forward)
if (Physics.Raycast(targettingRay, out raycastHit, 100))
{
if (raycastHit.transform.tag == "Enemies")
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
redCross.gameObject.SetActive(true);
standardCross.gameObject.SetActive(false);
}
}
else
{
redCross.gameObject.SetActive(false);
standardCross.gameObject.SetActive(true);
}
}
}
I except for the crosshair in my game to follow the player rig camera as the code explains. Any guidance is appreciated.
Syntax errors are those kind of errors which prevent your game/program from running at all. They are like grammar mistakes that need to be fixed before you can run your code.
A syntax error is your pc telling you:
I can't run this because I don't understand this. or I can't run this because this is not allowed.
One example would be int x = "Hello World". This in not allowed because I can not assign an string value to an integer (like that).
But your code not having any syntax errors does not mean it will do what you intended it for, it just means your code will run.
A good and easy way of debugging your code in Unity is to add Debug.Log("My Log Message"); statements to your code where you think it would be beneficial. These are going to be logged to your console output in Unity while you are in play mode. You can for example do something like this to constantly get your cross's position and rotation logged:
Debug.Log("Cross Position: " + standardCross.transform.position);
Debug.Log("Cross Rotation: " + standardCross.transform.rotation);
Just be sure to remove them once you are done with them because having Debug.Logs in your code takes as significant hit to your performance.
Another, more sophisticated way of debugging your code is through the usage of breakpoints in Visual Studio or whatever IDE/Editor you are using.
It basically comes down to declaring points where your program should pause execution for you to look at values in your program itself.
It's quite handy but goes above and beyond what I could tell you via this text, so please have a look at this Unity-specific use case: Debugging Unity games with Visual Studio
Now to your code:
First: There is a Vector3.down so you don't have to use -Vector3.up.
Second: Do you need a GameObject as crosshair? Why not just add a UI-Crosshair instead?
That way it always stays in the middle of your screen wherever you turn your camera.
Just add a new Image to your UI via GameObject -> UI -> Image, give it some kind of crosshair look in the inspector and lock it to the middle of the screen by left-clicking on the little crosshair in the top left of the Inspector while you have your Image selected and then Shift + Alt + Left click the middle option.
If you really want to use a separate gameObject as crosshair then maybe attatch it to your player object as a child. That way it will move and rotate with your player automatically and you do not have to do this via script.
Hope this helps!

Rotate player with platform without parenting

I'm currently making a small platformer 3D game, but unfortunately I can't make the player to rotate properly when it is riding the platform, the thing here is that I don't want to make the player child of the platform, so far I've managed to make him move smoothly along with the platform, but the rotation is still going nowhere, here is the code I'm using for the rotation:
player.transform.rotation *= platform.rotation;
and here is the effect I got:
Rotation Error
not very nice :(
I guess the solution is something simple, some formula, but unfortunately I'm not very good with math :( So, thank you guys, I hope you can help me.
I'll show you a simple script example which makes a cube rotate by input while reacting to the rotation of the platform on which it stands:
using UnityEngine;
public class CubeRotation : MonoBehaviour {
public GameObject Platform;
Quaternion PreviousPlatformRotation;
public float rotationSpeed = 50;
private void Start() {
PreviousPlatformRotation = Platform.transform.rotation;
}
private void Update() {
//Rotate the cube by input
if (Input.GetKey(KeyCode.A)) {
transform.Rotate(Vector3.up, Time.deltaTime * rotationSpeed);
}
if (Input.GetKey(KeyCode.D)) {
transform.Rotate(Vector3.up, -Time.deltaTime * rotationSpeed);
}
//Adjust rotation due to platform rotating
if (Platform.transform.rotation != PreviousPlatformRotation) {
var platformRotatedBy = Platform.transform.rotation * Quaternion.Inverse(PreviousPlatformRotation);
transform.rotation *= platformRotatedBy;
PreviousPlatformRotation = Platform.transform.rotation;
}
}
}
The logic of the adjustment to the platform rotation is this:
Get at start the rotation quaternion of the platform (in your case, get it when the cube object climbs on the platform)
With A and D rotate the cube normally around the local Y axis.
Afterwards check if the platform's rotation has changed, if yes:
3.a Get how much the platform rotated since the previous frame, with the operation Actual rotation * Inverse(Previous Rotation); this operation it's akin to a difference between two quaternions
3.b Add that quaternion to the cube's rotation with the *= operator
3.c Set the platform's previous rotation value to the new one.
That's pretty much it.

Building a rotatable lift in Unity

I got a "cube" in Unity. This cube has a Trigger, when moving into it, the object gets grabbed into the air. This is working as a lift, you can see an example what I mean here. It is a small scene taken from Spyro 2.
https://youtu.be/f8wWMa4N5mE?t=643
The code I use is really small for now
private float liftSpeed = 10; // the speed, the object is flying up
private void OnTriggerStay(Collider col)
{
Rigidbody objectRigid = col.gameObject.GetComponent<Rigidbody>(); // get the rigidbody from the object in the trigger
if (objectRigid != null) // does it have a rigidbody?
objectRigid.velocity = new Vector3(objectRigid.velocity.x, liftSpeed, objectRigid.velocity.z); // make it fly in the air
}
So I have a lift, that totally works fine. But I when I rotate my lift I want it to work aswell.
Some examples (my gamne is 3D)
So how can I make my lift work for all "rotations"?
You can use transform.up to get the up direction of your lift, and then multiply by the lift speed.
objectRigid.velocity = transform.up * liftSpeed;
transform.up changes depending on how the object is rotated, so if your lift is rotated to the left, then the lift will carry objects to the left.
You can use the transforms RotateAround Method for a rotation like in the video clip.
Transform t = col.gameObject.GetComponent<Transform>();
transform.RotateAround(Vector3.zero, Vector3.up, 20 * Time.deltaTime);
The given snipet lets the object rotate around it's axis, which is pointing up.
Make the player child of the lift OnTriggerEnter.
Move the lift as you do
You can rotate the lift using RotateAround function. The player should also rotate along with the lift, as it's a child of the lift now.
make player's parent to null when it's out of lift trigger using OnTriggerExit
This is my solution to the problem.
The lift and the EndPoint will be Empty GameObjects. If you want the lift has a platform or a base you can add a GameObject as child of the lift with the shape you prefer.
You need to attach a Collider to the lift and set it as trigger.
Then you add the following script to the lift Empty GameObject
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveLift : MonoBehaviour {
public Transform end;
float speed = 4f;
bool liftActivated = false;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if(liftActivated)
{
float step = speed * Time.deltaTime;
//To move up the lift
transform.position = Vector3.MoveTowards(transform.position, end.position, step);
//To spin the lift
transform.RotateAround(Vector3.up, 2 * Time.deltaTime);
//To stop spining the lift when it reaches the end of the path
if(transform.position.Equals(end.position))
liftActivated = false;
}
}
void OnTriggerEnter(Collider other){
liftActivated = true;
other.gameObject.transform.parent = this.transform;
other.gameObject.GetComponent<Rigidbody>().isKinematic=true;
}
}
Then you should decide if once the lift reaches its destination it is the player who has to move out of the platform, or if you unparent it and let it fall down as the end of the trip

Categories