Unity2D - Rigidbody not stopping when it reaches a position - unity3d

I have this code:
public class MoveCard : MonoBehaviour
{
public float speed = 1f;
public Rigidbody2D rb;
public Vector2 pos = new Vector2(6.8f,0);
public bool move = false;
void FixedUpdate(){
if (move){
//Stops Rigidbody
if (rb.position == pos){
move = false;
}
rb.transform.position += -rb.transform.right * speed * Time.fixedDeltaTime;
}
}
public void CardMovement(){
move = true;
}
}
I have it set as so when a button is pressed, CardMovement() initiates and in FixedUpdate I have a if statement that turns move off when the Rigidbody reaches a certain position. The rb moves but it doesn't stop when it reaches the Vector2. I am new to Unity so I don't know if this is the way to do it.

Well your Rb doesn't pass exactly for each point between the initial position and Vector2.
It's prtty unlikely to have rb.position == pos because one frame it will not enough, and the next one will be too much :)
Try with MoveTowards. Some like this:
rb.position = Vector3.MoveTowards(rb.position, pos, speed * Time.fixedDeltaTime);
You dont need a statement to stop it because it will do it when reaches pos.
PD: You can do this with transform instead of rigidbody if u are not going to use physics and you only want a movement.

Don't compare the 2 vector2D values like this:
if(rb.position == pos)
Rather compare the distance between them with a value that is very small, like this:
if(Vector2.Distance(rb.position,pos) <= 0.01)
Additionally, you can set the position like this rb.postion = pos; if it is close enough so that it snaps to the right location.

Related

Trying to rotate object when key pressed

I'm attempting to make the ability to slide in an FPS game I'm working on and I've tried rotating the player object but when tested, it does nothing. Pressing the slide key I have assigned does nothing but rotate the player object in the Y and Z axis (I'm trying to rotate it on the X axis)
Here's the code I'm using (only relevant parts have been kept in):
public class SlideMovementScript : MonoBehaviour
{
public Rigidbody rig;
public CapsuleCollider capcol;
public GameObject player;
public float originalHeight;
public float reducedHeight;
public float slideSpeed = 10f;
// Start is called before the first frame update
void Start()
{
capcol = GetComponent<CapsuleCollider>();
rig = GetComponent<Rigidbody>();
originalHeight = capcol.height;
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.LeftControl) && Input.GetKey(KeyCode.W))
Sliding();
else if (Input.GetKeyUp(KeyCode.LeftControl))
GoUp();
}
private void Sliding()
{
capcol.height = reducedHeight;
player.transform.localRotation = Quaternion.Euler(1.599f, 0f, 0f);
rig.AddForce(transform.forward * slideSpeed, ForceMode.VelocityChange);
}
private void GoUp()
{
GetComponent<CapsuleCollider>().height = originalHeight;
player.transform.localRotation = Quaternion.Euler(0f, 0f, 0f);
}
}
I've gotten ZERO error messages from this but I haven't managed to find the problem
Another problem I've also had is when you start sliding, it just instantly rotates the Player object to X 1.599 Y 0 Z 0. I want it to rotate to where the player is facing but I haven't found a way to do it, even using transform.foward or other references won't work.
The Explanation (skip to script if you want) (read notes)
I reccomend using Quaternion's methods. You can use Quaternion.Slerp which can smooth out the rotation towards the player.
Slerp means spherical linear interpolation
Basically, Slerp is a 3 argumented method. the first argument is the current rotation, the second argument is the target rotation, and the third is at which point between those two you want to output.
More simply, Lerp is linear interpolation. Lerps arguments are the same, except it handles floats and not Quaternions. If you Mathf.Lerp(0,1,1); you will get 1, because 1 is all the ay to the second argument. If you do Mathf(0, 3, 0.5); you will get 1.5 because 1.5 is halfway (0.5) between them both.
You can imagine slerp like that, if you give rotation (90, 0, 0) and alerp it to (0, 0, 0) by 0.5 you will get (45, 0, 0).
On this example, I slerp from the current rotation, to the target rotation, by delta time. This way, as time goes on, it slightly increases the current rotation to be the target.
I can create the target as 1.599 rotated on the x.
To make this relative Make a parent game object to the player containing the mesh and rigidbody and maybe the collider (depends if you want the collider upright). I will call this "slidePlayer" slidePlayer must be a child of the player gameObject
Notes
Make sure to have slidePlayer to a gameObject that is a child of player, but must have the player mesh or sliding won't be visible.
Change rot to the target rotation.
Change speed to speed to rotate to the target rotation.
Here is your script I changed:
public class SlideMovementScript : MonoBehaviour
{
public Rigidbody rig;
public CapsuleCollider capcol;
public GameObject player;
public GameObject slidePlayer;
public float originalHeight;
public float reducedHeight;
public float slideSpeed = 10f;
// Start is called before the first frame update
void Start()
{
capcol = GetComponent<CapsuleCollider>();
rig = GetComponent<Rigidbody>();
originalHeight = capcol.height;
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.LeftControl) && Input.GetKey(KeyCode.W))
Sliding();
else if (Input.GetKeyUp(KeyCode.LeftControl))
GoUp();
}
private void Sliding()
{
capcol.height = reducedHeight;
Quaternion rot = Quaternion.Euler(1.599f, 0f, 0f);
float speed = 1f;
Quaternion curRot = slidePlayer.transform.localRotation;
slidePlayer.transform.localRotation = Quaternion.Slerp(curRot, rot, Time.deltaTime * speed);
rig.AddForce(transform.forward * slideSpeed, ForceMode.VelocityChange);
}
private void GoUp()
{
GetComponent<CapsuleCollider>().height = originalHeight;
Quaternion rot = Quaternion.Euler(0f, 0f, 0f);
float speed = 3f;
Quaternion curRot = slidePlayer.transform.localRotation;
slidePlayer.transform.localRotation = Quaternion.Slerp(curRot, rot, Time.deltaTime * speed);
}
}
Let me know if there are any problems in coments. Thanks.

Sibling sprite doesn't appear to follow main GameObject sprite even when transform.position updates

Following this question How to align sprites of smaller sizes to a moving gameobject sprite?, I'm trying to redraw my sprites to avoid rendering transparent pixels and try to maximize performance, and as I try starting this process with the sword sprites, I need to change the way the Sword game object follows the Player, which before was handled simply by using transform.position = hero.transform.position; because since both objects use sprites that are perfect squares they would have the same pivot point despite their size difference.
At first, it seemed to me the problem lied in how the pivots are different, thus I would need to update the transform position of the Sword every time its attack animation changes sprites. For that, I made a function which updates variables that influence the position as that gets updated:
here, heroTransform is a variable which gets sent the Player's transform property on the script's Start function as heroTransform = hero.transform;, where hero is defined right above it as hero = GameObject.Find("Hero");.
I would have expected this to make the Sword which is equipped with this script, called WieldablePosition to follow the player position, but it seems stuck in the same position as it starts:
I'm not sure, but I don't think I changed anything that would stop the Sword from moving. In what cases could a GameObject remain in a single place even as the transform.position is modified? For reference, please view the script:
using UnityEngine;
public class WieldablePosition : MonoBehaviour {
GameObject hero;
HeroMovement heroMovementScript;
private Transform heroTransform;
protected Animator anim;
private bool isAirAttackSingle;
private bool isFacingLeft;
private float x = 0;
private float y = 0;
void Start() {
hero = GameObject.Find("Hero");
heroTransform = hero.transform;
heroMovementScript = hero.GetComponent<HeroMovement>();
anim = GetComponent<Animator>();
isAirAttackSingle = heroMovementScript.isAirAttackSingle;
isFacingLeft = heroMovementScript.isFacingLeft;
}
void Update() {
UpdatePosition();
isAirAttackSingle = heroMovementScript.isAirAttackSingle;
isFacingLeft = heroMovementScript.isFacingLeft;
anim.SetBool("isAirAttackSingle", isAirAttackSingle);
if (isFacingLeft) {
transform.localScale = new Vector3(-1, 1, 1);
} else {
transform.localScale = Vector3.one;
}
}
public void SetPosition(AnimationEvent eventParams) {
string[] eventPositions = eventParams.stringParameter.Split(',');
x = float.Parse(eventPositions[0]);
y = float.Parse(eventPositions[1]);
}
private void UpdatePosition() {
transform.position = new Vector2(heroTransform.position.x + x, heroTransform.position.y + y);
}
}

Vector2.MoveTowards -- Move To Target Position and Some More

I almost created game but there is one thing which is hard for me. I want to move player to target and some more.
The red dot is the goal, but I want to move the player to the goal and a little further.
P.S
If the player goes to the right then I want him to reach the goal and a little further to the right
same to the left, top, bottom
Look Attachment: https://imgur.com/a/RF0xIQq
Red dot is a target but i want player move to target and else more on the facing side (green dot)
i tried something like move forward but i dont have any idea.
void Start()
{
}
void Update()
{
target = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>();
transform.position = Vector2.MoveTowards(transform.position, target.position, (speed * Time.deltaTime));
}
//Mob have "Player" TAG (Player is not a player) |Targeting is fine|
You could add an offset value
// ajdust this in the inspector
public float offset = 0.1f;
and than add it to the position in the direction from the player to the target. As Foggzie mentioned this might not be a copy-past-able code yet since there might occure some hickups. To atleast prevent that the player turn around after overshooting the target and move back and forth you could use a setter method to get the direction only once:
public float offset;
public float threshold = 0.0001f;
public float speed;
private GameObject target;
private Vector3 direction;
private Vector3 targetPosition;
public void SetTarget(GameObject newTarget)
{
target = newTarget;
// adding the offset in that direction
targetPosition = target.transform.position + direction * offset;
// direction from the player to the target
direction = (target.transform.position - transform.position).normalized;
}
private void Update()
{
if (!target) return;
// make Player overshoot the target by offset
transform.position = Vector2.MoveTowards(transform.position, targetPosition, (speed * Time.deltaTime));
// stop if target is reached
if (Vector3.Distance(transform.position, targetPosition) <= threshold)
{
target = null;
}
}
I don't know when and how you change the target so currently it doesn't limit the player movement to only X and Y like in your pictures ... but you would than do e.g.
// Note that 'transform' is a built-in property of 'GameObject' and you shouldn't use `GetComponent` for it
SetTarget(GameObject.FindGameObjectWithTag("Player").transform);

my character jumps too strong sometimes in unity 2d

My character's jump is working fine in a straight line collider, then I noticed that when every-time my character jumps in a curve shape collider, my jump always turns too strong abnormaly. How can I fix this? I'll provide my initial code for jump movement below.
Here is the img :
Here is jump code:
float checkgroundradius = 0.50f;
public bool isgrounded2;
public Transform grouncheckslot;
public LayerMask LM;
public float JumpPower;
public float movespeed;
Rigidbody2d RB;
void Update () {
if (isgrounded && Input.GetKeyDown(KeyCode.UpArrow))
{
isgrounded = false;
jumping = true;
myanim.SetBool("groundedanim", isgrounded);
myrb.AddForce(new Vector2(0, jumpower));
}
}
void FixedUpdate()
{
isgrounded = Physics2D.OverlapCircle(checkslot.position, groundradius, LM);
myanim.SetBool("groundedanim", isgrounded);
myanim.SetFloat("verticalspeed", myrb.velocity.y);
float move = Input.GetAxis ("Horizontal");
RB.velocity = new Vector 2 (move * movespeed, RB.velocity.y);
}
When jumping up the slope, your Physics2D.OverlapCircle intersects with the slope, thus making isgrounded=true, making you apply more force if the UpArrow is pressed.
Once solution could be to check that your character is not already moving upwards before jumping
if (isgrounded && Input.GetKeyDown(KeyCode.UpArrow) && myrb.velocity.y <= 0.0f)
this way you wont jump again if you are already heading upwards.
Maybe you could play around with a smaller groundradius and/or try moving the checkslot.position in a way that it doesnt intersect with the slope in the same manner.
For jumping people use Rigidbody2D.AddForce with Forcemode.Impulse.
Also, change
if (isgrounded && Input.GetKeyDown(KeyCode.UpArrow))
to
if (Input.GetKeyDown(KeyCode.UpArrow) && myrb.velocity.y == 0)
So you don't need to bool isgrounded
I hope it helps you :)

Moving object from one postion to another in unity

I have this code here ,i am trying to move an object from one position to another ,on mouse click ,but everytime i run it it justs instantiate the projectile object in a specific position while it had to instantiate it in a the ffor object position
using UnityEngine;
using System.Collections;
public class Shoot : MonoBehaviour
{
public GameObject projectile;
public GameObject foot;
public GameObject mouse;
void Start()
{
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector2 Target = Input.mousePosition;
Vector2 pos1 = Camera.main.ScreenToWorldPoint(Target);
GameObject newObj = (GameObject)GameObject.Instantiate(projectile);
Vector2 pos2 = foot.transform.position;
transform.position=Vector2.Lerp(pos2,pos1,Time.deltaTime);
}
}
There are several issues here, I'll address them one at a time. First, the code for moving the projectile is wired up wrong:
transform.position=Vector2.Lerp(pos2,pos1,Time.deltaTime);
You are moving the object that "Shoot" is attached to, not the new game object (newObj, as you have named it.)
Second, it's important to understand the Update pattern and how to properly use it. Update is run every frame. Time.deltaTime is how much time has passed between the last frame render and this one. This number is usually very small. Lastly, Input.GetMouseButtonDown is true only on the first frame that the mouse is pressed.
The current code you have only attempts to (but fails, due to other code problems) move the projectile the one frame the mouse is clicked. What we want is the mouse click to spawn a projectile, and the projectile to move forward EVERY update.
This will be best accomplished with two classes. I will call them Gun and SeekerBullet. The Gun class will be responsible for creating a bullet every time the mouse button is pressed. The SeekerBullet class will be responsible for moving the bullet to it's target.
Gun
public SeekerBullet ProjectilePrefab;
void Update()
{
if (Input.GetMouseButton(0))
{
Vector2 target = Camera.main.ScreenToWorldPoint(Input.mousePosition);
FireBullet(target);
}
}
void FireBullet(Vector2 target)
{
GameObject projectile = (GameObject)GameObject.Instantiate(ProjectilePrefab, transform.position, Quaternion.Identity);
projectile.GetComponent<SeekerBullet>().Target = target;
}
SeekerBullet
public float MoveSpeed = 5;
public Vector2 Target { get; set; }
public void Update()
{
transform.position = Vector3.MoveTowards(transform.position, Target, MoveSpeed * Time.deltaTime);
if (transform.position == Target)
OnReachTarget();
}
void OnReachTarget()
{
// Do whatever you want here
Destroy(gameObject); // delete this seekerbullet
}
The main Idea I'm trying to stress is isolating your code to perform it's one function well. The gun shouldn't be responsible for moving the projectile, just creating it and telling it where to go.
Also, note that the projectile is starting wherever the Gun is. See this line
GameObject projectile = (GameObject)GameObject.Instantiate(ProjectilePrefab, transform.position, Quaternion.Identity);
The transform.position is the position of the gun object. If you want it to start at the foot, like you have in your code, you can re-implement that just like you have in your example.
Why don't you use Raycast. It's pretty simple. Here's an example (Moves the object (transform) in the direction and distance of translation.):
var obj:Transform;
var hit:RaycastHit;
var move: boolean = false;
var moveSpeed:float;
var moveTime:float;
private var startTime:float;
function Update ()
{
if (Input.GetButtonDown ("Fire1"))
{
var ray = Camera.main.ScreenPointToRay (Input.mousePosition);
if (Physics.Raycast (ray, hit, 10000)){
move = true;
startTime = Time.time;
}
}
if(move){
var curTime = Time.time;
var elapsedTime = curTime - startTime;
var amountToMove = elapsedTime / moveTime;
print(amountToMove);
obj.transform.position = Vector3.Lerp(obj.transform.position, hit.point, amountToMove);
if(obj.transform.position == hit.point){
move = false;
}
This code creates a plane, and a cube. Assign the above code to the plane, assign the cube to the obj var in the inspector. Click on the plane, and watch the fun.
This is pretty simple. You can use the Vector3.Lerp function to achieve this. Use raycasting to get the mouse click position or the touch position. Then use the initial and the final position in the lerp function. The initial position being the position that the gameobject is at now and the final position being the click / touch position.
You can find the article by The Game Contriver on the same here
Move to Touch / Click Position - The Game Contriver