I have one issue in unity 2d with my object , when i shoot the arrow to the box collider to the other element , and when it hit and go into child i mean arrow become child of parent (BOX) the child start to rotate to the left and to the right .. I really want to disable left and right rotation of the child .. the box (parent) still need to rotate as same as it was before.. i have code like this and my arrow in rigidbody2d is on kinematic mode...
this is script of the arrow ..
{
public float flySpeed = 20f;
private Rigidbody2D arrowBody;
private bool shouldFly;
// Start is called before the first frame update
void Start()
{
shouldFly = true;
arrowBody = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
if (shouldFly == true)
{
//make our pin fly
arrowBody.MovePosition(arrowBody.position + Vector2.up * flySpeed * Time.deltaTime);
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.tag == "target")
{
shouldFly = false;
transform.SetParent(collision.gameObject.transform);
} else if(collision.tag == "arrow")
{
SceneManager.LoadScene("quickGameOverScene");
}
}
}
I am really confused what you are trying to do. I do not understand if you want to freeze rotation or movement so i will post answers for both. In order to prevent translations and rotations caused by parent object you can use LateUpdate like this:
Quaternion InitRot;
Vector3 InitPos;
void Start () {
InitRot = transform.rotation;
InitPos = transform.position;
}
void Update()
{
//figuring out when to save position when attached to BOX
if(gameObject.transform.parent == null)
{
InitRot = transform.rotation;
InitPos = transform.position;
}
}
void LateUpdate () {
//If attached to box do not translate do not rotate
if (gameObject.transform.parent != null)
{
transform.rotation = InitRot;
transform.position = InitPos;
}
}
You can have more information about this solution from here
EDIT
Since the answer above did not work out for OP. I figured out what the actual problem is. OP's code is perfectly fine for moving the arrow but the problem is most likely where he rotates the box.
If he rotates the box using transform.Rotate(Vector3.forward* 90), there will be a distortion caused by same amount of rotation in each Update since frame times are not same in each Update. Therefore, he needs to rotate the box using Time.deltaTime for consistent rotation like this: transform.Rotate(Vector3.forward* 90*Time.deltaTime); This rotate the box same amount in each time interval and eliminate distortion. These are the scripts i used for the task and it works for me.
Script for the arrow:
public float flySpeed = 20f;
private Rigidbody2D arrowBody;
private bool shouldFly;
private Vector2 initPos;
private Quaternion initRot;
// Start is called before the first frame update
void Start()
{
shouldFly = true;
arrowBody = GetComponent<Rigidbody2D>();
//arrowBody.isKinematic = true;
initPos = gameObject.transform.position;
initRot = gameObject.transform.rotation;
}
// Update is called once per frame
void Update()
{
if (shouldFly == true)
{
//make our pin fly
arrowBody.MovePosition(arrowBody.position + Vector2.up * flySpeed * Time.deltaTime);
}
if(gameObject.transform.parent == null)
{
initPos = gameObject.transform.position;
initRot = gameObject.transform.rotation;
}
}
void LateUpdate()
{
if (gameObject.transform.parent != null)
{
gameObject.transform.position = initPos;
gameObject.transform.rotation = initRot;
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
Debug.Log("Collision happened");
if (collision.tag == "target")
{
shouldFly = false;
transform.SetParent(collision.gameObject.transform);
}
else if (collision.tag == "arrow")
{
SceneManager.LoadScene("quickGameOverScene");
}
}
And The script for rotating the box:
public float rotationSpeed = 70f;
void Update()
{
transform.Rotate(Vector3.back, rotationSpeed * Time.deltaTime);
}
Related
C# Character Controller
I'm new to using unity and I having difficulty with the force being applied to the sprite. The jump vector 2 variable is fine but I'm having issue with the horizontal movement. The code is not detecting if the key is held down,it will only add force to either side if you constantly tap the key, then it will move in a direction.
I'm not sure if I cant use the rigid body for horizontal movement or the vector is not written correctly. If you could please respond with the possible issues and or solution that would be helpful, thanks.
public float JumpForce;
public float HorizontalForce;
bool isGrounded = false;
Rigidbody2D RB;
void Start()
{
RB = GetComponent<Rigidbody2D>();
}
void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
if(isGrounded == true)
{
RB.AddForce(Vector2.up * JumpForce);
isGrounded = false;
}
}
if (Input.GetKeyDown(KeyCode.A))
{
RB.AddForce(Vector2.left * HorizontalForce);
}
if (Input.GetKeyDown(KeyCode.D))
{
RB.AddForce(Vector2.right * HorizontalForce);
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("ground")) ;
{
if(isGrounded == false)
{
isGrounded = true;
}
}
}
}
You need GetButton() instead of GetButtonDown().
GetButtonDown is only true for a single frame, in which the button was pressed. GetButton returns true as long as you hold the button
I assigned the spacebar as jump key in my game but it doesn't seem to work 100% of the time. Also there is a slight delay when the character jumps and button is pressed. The jump key is like a gamble in the game. Here's the script that i attached to the character.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MonsterScript : MonoBehaviour
{
[SerializeField] private float walkStrength = 10f;
[SerializeField] private float jumpStrength = 11f;
private float movementX;
private bool onCloud = true;
private string CLOUD_TAG = "Cloud";
private string WALK_ANIMATION = "walk";
private Rigidbody2D mybody;
private SpriteRenderer sr;
private Animator anim;
private void Awake()
{
mybody = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
sr = GetComponent<SpriteRenderer>();
}
// Update is called once per frame
void Update()
{
monsterWalk();
}
private void FixedUpdate()
{
monsterJump();
}
void monsterWalk()
{
movementX = Input.GetAxisRaw("Horizontal");
transform.position += walkStrength * Time.deltaTime * new Vector3(movementX, 0f, 0f);
}
void monsterJump()
{
if (Input.GetKeyUp(KeyCode.Space) && onCloud == true)
{
mybody.AddForce(new Vector3(0, jumpStrength,0), ForceMode2D.Impulse);
onCloud = false;
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag(CLOUD_TAG))
{
onCloud = true;
Debug.Log("Landed!");
}
}
}
I tried changing the collider of my player thinking it might have got stuck with other collider. Again I trying changing the key thinking it was a problem of my keyboard but wasn't.
In general: get single event input in Update!
If you check GetKeyUp within FixedUpdate - which is not called every frame - you might miss an Input.
So you should do e.g.
private bool isJump;
private void Update ()
{
...
if(Input.GetKeyUp(KeyCode.Space) && onCloud)
{
isJump = true;
}
}
private void FixedUpdate ()
{
...
if(isJump)
{
mybody.AddForce(new Vector3(0, jumpStrength,0), ForceMode2D.Impulse);
onCloud = false;
isJump = false;
}
}
However, further
transform.position += walkStrength * Time.deltaTime * new Vector3(movementX, 0f, 0f);
is a problem as well when using a Rigidbody/Rigidbody2D since you completely overwrite the position here => the physics has no effect whatsoever so your adding force is good for nothing.
You also want to shift this into FixedUpdate and only go through the myBody here as well
private void FixedUpdate ()
{
var movementX = Input.GetAxisRaw("Horizontal") * walkStrength * Time.deltaTime;
// Keep Y velocity untouched and only overwrite X
var vel = mybody.velocity;
vel.x = movementX;
mybody.velocity = vel:
}
as soon as physics is involved you do not want to get or set anything through the Transform component.
https://www.youtube.com/watch?v=gB1F9G0JXOo&t=11865s
I'm watching this tutorial and I did everything the same as the guy in the video did until the "Player Jumping" part, when his character can jump whenever he presses the space button, but for me it doesn't work the same way. Can somebody help or tell me what is the problem?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
[SerializeField]
private float moveForce = 10f;
[SerializeField]
private float jumpForce = 11f;
private float movementX;
private Rigidbody2D myBody;
private SpriteRenderer sr;
private Animator anim;
private string WALK_ANIMATION = "Walk";
private void Awake()
{
myBody = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
sr = GetComponent<SpriteRenderer>();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
PlayerMoveKeyboard();
AnimatePlayer();
}
private void FixedUpdate()
{
PlayerJump();
}
void PlayerMoveKeyboard()
{
movementX = Input.GetAxisRaw("Horizontal");
transform.position += new Vector3(movementX, 0f, 0f) * moveForce * Time.deltaTime;
}
void AnimatePlayer()
{
// we are going to the right side
if (movementX > 0)
{
anim.SetBool(WALK_ANIMATION, true);
sr.flipX = false;
}
// we are going to the left side
else if (movementX < 0)
{
anim.SetBool(WALK_ANIMATION, true);
sr.flipX = true;
}
else
{
anim.SetBool(WALK_ANIMATION, false);
}
}
void PlayerJump()
{
if (Input.GetButtonDown("Jump"))
{
myBody.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
}
}
} // class
Try moving your jump function from the FixedUpdate into the normal Update Function.
That should fix it.
Update gets called every frame and thus captures every input while FixedUpdate gets called in a fixed offset to update the physics logic of the engine. Now when you are looking for keyboard input, you want to always do that in Update, never in FixedUpdate because you'll miss input that was given to the player between the fixed update frames.
But since you want to do physics related changes only in FixedUpdate, you want to split the code between the two methods.
bool shouldJump = false;
void Update()
{
if (Input.GetButtonDown("Jump"))
shouldJump = true;
}
void FixedUpdate()
{
if (shouldJump)
{
myBody.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
shouldJump = false;
}
}
I have looked into the code in the video and he does the jump-logic in FixedUpdate. It seems weird to me and this is definitely bad practice. But one of the comments below addresses your issue as well and proposes a similar solution.
I have a simple jumping game. In this game there are rotating platforms, and a player object.
Whenever the player clicks the mouse button, it should jump and sticks to the next platform and rotates with it, until he clicks again. I want the game object to jump perpendicular to the rotating platforms.
If i use Vector3.up the game object will fall down instead. But I want the player to jump in the direction of a green arrow and stick to the next platform.
I'm posting here, because I've posted on the Unity Forms 2 weeks ago and still got no answer.
TLDR:
here is what i've worked on recently :
my player code :
Rigidbody2D Rig;
public float Force =500;
public bool gamejump = true;
public Transform platformParent;
bool playerforce = false;
bool setpos = false;
Vector2 pos = new Vector2(0, 0);
public Collider2D Ccollider;
public bool bottom =false;
void Start()
{
Rig = GetComponent<Rigidbody2D>();
Ccollider = GetComponent<CircleCollider2D>();
}
private void FixedUpdate()
{
if (gamejump == true)
{
transform.SetParent(null);
Rig.isKinematic = false;
setpos = false;
}
else
{
transform.SetParent(platformParent);
Rig.AddForce(new Vector2(0, 0));
Rig.isKinematic = true;
if (setpos == false)
{
setpos = true;
transform.position = pos;
}
}
}
void OnTriggerStay2D(Collider2D other)
{
if (other.tag == "Rotate")
{
//if (Input.GetKey(KeyCode.Space))
if (Input.GetMouseButton(0))
{
gamejump = true;
if (bottom == true)
{
Rig.AddForce(other.transform.up * Force);
}
else
{
Rig.AddForce(other.transform.up * -Force);
}
}
}
}
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Rotate")
{
ContactPoint2D contact = collision.contacts[0];
pos = contact.point;
if (collision.contacts.Length>0)
{
bottom = true;
}
else
{
bottom = false;
}
gamejump = false;
}
}
}
and my platform code :
public bool counterclockwise;
Transform player;
player2 playerCode;
public Collider2D collidPlatform;
private int speed=100;
// Start is called before the first frame update
void Start()
{
player = GameObject.Find("player").GetComponent<Transform>();
playerCode = FindObjectOfType<player2>();
if (counterclockwise)
speed = -speed;
}
void FixedUpdate()
{
// float currentZ = transform.eulerAngles.z;
/* if (Limit == true)
{
if (currentZ > 180)
{
currentZ = 180;
}
Vector3 newEuler = new Vector3(0, 0, currentZ);
transform.eulerAngles = newEuler;
}
//transform.Rotate(new Vector3(0, 0, speed * Time.deltaTime));
}
*/
}
private void OnCollisionEnter2D(Collision2D collision)
{
playerCode.platformParent = transform;
playerCode.Ccollider = collidPlatform;
}
}
and still got crazy results, suddenly the player rotates in the air or dose not sticks to a platform and falls or when it sticks to a platform ,it
Increases platform's speed (i know it's because of rigid body that attaches to platforms but if i remove it and try to control it manually it dose not work the way i want ,so if you could give me suggestion on how to rotate platforms manually and without rigid body so i be able to control the speed .
if you make the player a child object of the platform using transform.parent, then jump using transform.up on the players local axis, if you want the player to land on the same position where they jumped, the player can remain parented to the rotating platform, otherwise you need to remove the player as a child object to the platform after you jump. But since you are using rigidbody physics, I think you will get mixed results from gravity.
I am working on solo project that is horizontal 2D infinite runner. I have a problem in jumping mechanics where player can hold the jump button and jump as soon as he touches the ground. I want to force player to release the button to be able to jump again. I want to make same mechanics when he is floating(at the end of the jump when player starts falling down its y velocity is being reduced for few bits of second). I am following single responsibility principle so jumping and floating are 2 seperate scripts.
I have tried to implement a timer that will count while player is touching the ground and after some time the player would be able to jump again, but didn't manage to get asked result because you can hold the jump button all the time and after determined time spent on ground player would just jump again without releasing the button.
public class Jumping : MonoBehaviour
{
public bool isJumping;
public bool isGrounded;
public float speedUp;
public float verticalAxis;
public float timePassed;
public float jumpLimit;
private Rigidbody2D rb;
// Start is called before the first frame update
void Start()
{
rb = gameObject.GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
verticalAxis = Input.GetAxisRaw("Vertical");
if (verticalAxis > 0 && timePassed == 0)
{
isJumping = true;
}
}
private void FixedUpdate()
{
if (isJumping)
{
Jump();
}
}
private void OnCollisionStay2D(Collision2D collision)
{
if (collision.collider.tag == "Ground")
{
isGrounded = true;
timePassed = 0f;
}
}
private void Jump()
{
isGrounded = false;
//Modifying y component of players rigidbody(jumping)
Vector2 velocity = Vector2.up * speedUp * verticalAxis;
rb.velocity = velocity;
//Counting time when jumping
timePassed += Time.deltaTime;
if (timePassed >= jumpLimit)
{
isJumping = false;
}
}
}
P.S. I have tried to shrink the code in this question as stackoverflow suggests but I didn't know what to cut out because according to me everything is relevant and crucial to solving the problem. Thanks!
without knowing the rest of your code I would use an additional flag and obviously something for getting the cool down delay like
public float jumpCoolDownTime;
private float coolDownTimer;
private bool canJump;
void Update()
{
// if no can jump you can't jump again
if(!canJump)
{
verticalAxis = Input.GetAxisRaw("Vertical");
if (verticalAxis > 0)
{
canJump = false;
isJumping = true;
coolDownTimer = 0;
}
}
else
{
// run timer to enable jump again
if(isGrounded) coolDownTimer += Time.deltaTime;
if(coolDownTimer >= jumpCoolDownTime)
{
canJump = true;
}
}
}
You could however simplify the code a bit using Coroutines
public bool isJumping;
public float speedUp;
public float verticalAxis;
public float jumpLimit;
public float coolDownTime;
private Rigidbody2D rb;
// Start is called before the first frame update
void Start()
{
rb = gameObject.GetComponent<Rigidbody2D>();
}
void Update()
{
// do nothing is already jumping
if(isJumping) return;
verticalAxis = Input.GetAxisRaw("Vertical");
if (verticalAxis > 0)
{
// start jumping
StartCoroutine(Jump());
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.collider.tag != "Ground") return;
isGrounded = true;
StartCoroutine(CoolDown());
}
private IEnumerator Jump()
{
isJumping = true;
isGrounded = false;
coolDownTimer = 0;
var timePassed = 0f;
while(timePassed < jumpLimit)
{
// wait for the FixedUpdate call
yield return new WaitForFixedUpdate();
Vector2 velocity = Vector2.up * speedUp * verticalAxis;
rb.velocity = velocity;
}
}
private IEnumerator CoolDown()
{
yield return new WaitForSeoncds(coolDownTime);
isJumping = false;
}
If you are using the axis to get the jump, you can see if the player have released the button by:
if (GetAxis("Vertical") == 0.0) { //Button up
// do stuff
}
if(GetAxis("Vertical") > 0)
{
//Button down, is jumping
}
In Unity axis aren't booleans, they are coefficients, if you play with a controller you can set different behaviors for different intensities.