I have script which makes character (gameobject) move right.
How do I make so when he reaches right border, and character starts moving left with playing another animation (moving left)?
Here is my script of character's moving right and left
void MovementRight()
{
_character.transform.Translate (Vector2.right 100f Time.deltaTime);
}
void MovementLeft()
{
_character.transform.Translate (Vector3.left 100f Time.deltaTime);
}
I would suggest you handle animations with Animator, and make the horizontal animation to be handled by a float variable in Animator, (so when it is -1 animate walking left, and 1 animate walking right).
So you will need to make 3 scripts.
//This will handle the animator getting the speed of the character from MovingHandler
AnimationHandler.cs
//This will actually move the character based on input handler
MovingHandler.cs
//Here you will hear to inputs
InputHandler.cs
And to make suere the character won't move outside the screen you will need to get the screenbounds on MovingHandler and make a check before moving the character, if it is in the same spot in x as the bounds of the screen you will make a return; also if it is going to the left and the character is <= than left bound then return; and the same on the right bound >= then return;
Something like this:
Transform charTransform;
float leftHorizontalBound;
float rightHorizontalBound;
void Start()
{
charTransform = this.transform;
leftHorizontalBound = camera.ViewportToWorldPoint (new Vector3 (0,0, camera.nearClipPlane)).x;
rightHorizontalBound= camera.ViewportToWorldPoint (new Vector3 (1,0, camera.nearClipPlane)).x;
}
void Update()
{
if(charTransform.position.x <= leftHorizontalBound)
{
charTransform.position = new vector2(leftHorizontalBound + 0.1f);
return;
}
if(charTransform.position.x >= rightHorizontalBound)
{
charTransform.position = new vector2(rightHorizontalBound - 0.1f);
return;
}
//MAKE HERE YOUR MOVEMENT BASED ON INPUT.
}
Dont use that code just use the idea, since i didn't test it, i just code it here.
I hope it helped.
Related
I am using the Unity Engine with C#.
I have a 1x1 cube which moves forward on a grid of 49, 1x1 cubes (screenshot below) - when I press the start button on the controller.
The movement code for the cube is below.
void MovePlayerCube()
{
transform.Translate(direction * moveSpeed * Time.deltaTime);
}
When this cube passes over a cube with an arrow on it, the cube will change direction to where the arrow is pointing (staying on the same Y axis).
I need to detect the exact point at which the cube is directly over the cube with the arrow on it, and run the 'change direction' code at that point.
I'm currently using Vector3.Distance to check if the X and Z coordinates of the 2 cubes are close enough together (if they are less than 0.03f in distance), I can't check if they are equal due to floating point imprecision.
However this is really ineffective as half the time this code doesn't register for probably the same reason, and if I increase the 0.03f to a point where it never misses it becomes really noticeable that the cube isn't aligned with the grid anymore.
There has to be a proper solution to this and hopefully I've clarified the situation enough?
Any advice is appreciated.
You are moving your cube via
transform.Translate(direction * moveSpeed * Time.deltaTime);
which will never be exact an might overshoot your positions.
=> I would rather implement a coroutine for moving the cube exactly one field at a time, ensuring that after each iteration it fully aligns with the grid and run your checks once in that moment.
It doesn't even have to match exactly then, you only need to check if you are somewhere hitting a cube below you.
So something like e.g.
private Vector3Int direction = Vector3Int.left;
private IEnumerator MoveRoutine()
{
// depends on your needs if this runs just forever or certain steps
// or has some exit condition
while(true)
{
// calculate the next position
// optional round it to int => 1x1 grid ensured on arrival
// again depends a bit on your needs
var nextPosition = Vector3Int.RoundToInt(transform.position) + direction;
// move until reaching the target position
// Vector3 == Vector3 uses a precision of 1e-5
while(transform.position != nextPosition)
{
transform.position = Vector3.MoveTowards(transform.position, nextPosition, moveSpeed * Time.deltaTime);
yield return null;
}
// set target position in one frame just to be sure
transform.position = nextPosition;
// run your check here ONCE and adjust direction
}
}
start this routine only ONCE via
StartCoroutine(MoveRoutine());
or if you have certain exit conditions at least only run one routine at a time.
A Corouine is basically just a temporary Update routine with a little bit different writing => of course you could implement the same in Update as well if you prefer that
private Vector3Int direction = Vector3Int.left;
private Vector3 nextPosition;
private void Start()
{
nextPosition = transform.position;
}
private void Update()
{
if(transform.position != nextPosition)
{
transform.position = Vector3.MoveTowards(transform.position, nextPosition, moveSpeed * Time.deltaTime);
}
else
{
transform.position = nextPosition;
// run your check here ONCE and adjust direction
// then set next position
nextPosition = Vector3Int.RoundToInt(transform.position) + direction;
}
}
Then regarding the check you can have a simple raycast since you only run it in a specific moment:
if(Physics.Raycast(transform.position, Vector3.down, out var hit))
{
direction = Vector3Int.RountToInt(hit.transform.forward);
}
assuming of course your targets have colliders attached, your moved cube position (pivot) is above those colliders (assumed it from your image) and your targets forward actually points int the desired new diretcion
I would do it this way. First I would split the ability of certain objects to be "moving with certain speed" and "moving in a certain direction", this can be done with C# interfaces. Why? Because then your "arrow" cube could affect not only your current moving cube, but anything that implements the interfaces (maybe in the future you'll have some enemy cube, and it will also be affected by the arrow modifier).
IMovingSpeed.cs
public interface IMovementSpeed
{
float MovementSpeed{ get; set; }
}
IMovementDirection3D.cs
public interface IMovementDirection3D
{
Vector3 MovementDirection { get; set; }
}
Then you implement the logic of your cube that moves automatically in a certain direction. Put this component on your player cube.
public class MovingStraight: MonoBehaviour, IMovementSpeed, IMovementDirection3D
{
private float _movementSpeed;
Vector3 MovementSpeed
{
get { return _movementSpeed; }
set { _movementSpeed = value; }
}
private Vector3 _movementDirection;
Vector3 MovementDirection
{
get { return _movementDirection; }
set { _movementDirection= value; }
}
void Update()
{
// use MovementSpeed and MovementDirection to advance the object position
}
}
Now to implement how the arrow cube modifies other objects, I would attach a collision (trigger) component for both moving cube and the arrow cube.
In the component of the arrow cube, you can implement an action when something enters this trigger zone, in our case we check if this object "has direction that we can change", and if so, we change the direction, and make sure that the object will be aligned, by forcing the arrow cube's position and the other object's position to be the same on the grid.
public class DirectionModifier: MonoBehaviour
{
private Vector3 _newDirection;
private void OnTriggerEnter(Collider collider)
{
IMovementDirection3D objectWithDirection = collider as IMovementDirection3D ;
if (objectWithDirection !=null)
{
objectWithDirection.MovementDirection = _newDirection;
// to make sure the object will continue moving exactly
// from where the arrow cube is
collider.transform.position.x = transform.position.x;
collider.transform.position.y = transform.position.y;
}
}
}
If you made your trigger zones too large, however, then the moving cube will "jump" abruptly when it enters the arrow cube's trigger zone. You can fix it by either starting a coroutine as other answers suggested, or you could make the trigger zones pretty small, so that the jump is not noticeable (just make sure not to make them too small, or they may not intersect each other)
You could then similarly create many other modifying blocks, that would change speed or something
I think that it is enough for you to check if the X and Z coordinates are equal, since the movement occurs only along them
Example
if(_player.transfom.position.x == _gameSquare.transfom.position.x && _player.transfom.position.z == _gameSquare.transfom.position.z)
{
Debag.Log("Rotate!")
}
i want to make a simple script that when u clicked the screen the ball in the game will move (20000 * Time.deltaTime) to the right, and then if i'll click again, it will move to the left side and then right and so on.
I managed to get the ball to move to the right, but i need it to wait after the animation is finish because i need to check if the player clicked again (if he did i need to check to what direction to move the ball).
I tried many methods i found online like checking if Rigidbody.velocity.magnitude == 0.0f that means the ball is not moving..
public Rigidbody rb;
public Transform PlayerPosition;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
rb.AddForce(20000 * Time.deltaTime, 0, 0); // moving ball to the right
while (rb.velocity.magnitude != 0.0f) // I tried to check until the ball is not moving
{
}
Debug.Log(PlayerPosition.position.x);
}
}
And here is my latest try:
void Update()
{
if (Input.GetMouseButtonDown(0))
{
rb.AddForce(20000 * Time.deltaTime, 0, 0); // moving ball to the right
if(rb.velocity.magnitude < 0.05f) // if i click the ball it just prints it and not wating for the ball to not move
{
Debug.Log(PlayerPosition.position.x);
}
}
}
I expected the output to wait until the animation is finished but instead, its printing the vaule(x) the moment i click the mouse.
Edit
You need to check if your animation is still playing. You are checking only if your velocity is greater than 0.05f, which is correctly printing out the statement.
Use Animation.IsPlaying(string name). One caveat is that this method will return false for the same frame of Update that it was invoked, since the animation hasn't technically started until afterward.
void Update()
{
if (!rb.velocity.magnitude <= 0.01f && !Animation.IsPlaying(nameOfAnimation))
{
Debug.Log("We're not moving and the animation is not playing");
}
}
Original
You should not need to use while in your Update method.
Use an if statement inside of your Update
void Update()
{
if (rb.velocity.magnitude > 0.01f) Debug.Log("We're moving!");
}
First
rb.velocity.magnitude != 0.0f
will almost allways be true due to single precision floatong point : Two float values even if they seem to be equal logical are most likely not.
So you can either use a threshold how you tried already
if(rb.velocity.magnitude <= 0.5f)
or use Mathf.Approximately which uses a very small Epsilon or threshold for the comparing
if(Mathf.Approximately(rb.velocity.magintude, 0))
Than it sounds like you want to wait until the ball has stopped moving and than output the position - like e.g. for a billard game. So actually there seems to be no Animation involved.
In most cases where you think/speek of of an "animation" you actually mean "doing something over time" not to confuse with using an Animator or Animation component with AnimationClips in Unity.
You can/should use a Coroutine for that:
public Rigidbody rb;
public Transform PlayerPosition;
// a flag to make sure there is only one animation at a time
private bool isMoving;
// a flag for altering between left and right movement
private bool isMovingRight;
// Update is called once per frame
void Update()
{
// only allow clicks while not moving already
if (!isMoving && Input.GetMouseButtonDown(0))
{
// stop further input until not moving anymore
isMoving = true;
// add the force
// (you might btw want to skip that Time.deltaTime here it makes no sense)
rb.AddForce(isMovingRight ? 20000 : -20000 * Time.deltaTime, 0, 0);
// alter the direction for the next call
isMovingRight = !isMovingRight;
// if you rather want to be able to interrupt the current animation by clicking again
// remove the isMoving flag and instead use this
//StopCoroutine(WaitForMoveStops());
// Start the routine
StartCoroutine(WaitForMoveStops());
}
}
private IEnumerator WaitForMoveStops()
{
// Inside a Coroutine while is okey now
// as long as you yield somwhere
// check if velocity is below threshold
while (!Mathf.Approximately(rb.velocity.magnitude, 0)
{
// yield in simple words means "leave" this method here, render the frame
// and than continue from here in the next frame
yield return null;
}
// I would now hard reset the velocity just to be sure
rb.velocity = Vector3.zero;
Debug.Log(PlayerPosition.position.x);
// whatever you want to do now
// reset the flag to allow input again
isMoving = false;
}
I think you want to move it if it's stopped, then call AddForce only when it's idle:
var wasMovingLastTime = false;
void Update()
{
var isMoving = rb.velocity.magnitude > 0f;
if (wasMovingLastTime && !isMoving)
{
/// Has just finished moving
Debug.Log(PlayerPosition.position.x);
}
if (Input.GetMouseButtonDown(0))
{
if (!isMoving)
{
rb.AddForce(20000 * Time.deltaTime, 0, 0);
}
}
wasMovingLastTime = isMoving;
}
i currently have a gameobject named "block" with two long cubes next to each other with a gap between them. now when a user touches the screen and drags left or right the block also moves in that direction. now i am trying to setup a boundary so they can go up to a certain amount in the left or right direction. however how can i code this so it is the same for different screens eg iphone ipad etc.
the code i am currently using makes the block overlap each other getting rid of the gap which needs to be there. how do i go about to fix this. the code is below:
public float speed = 0.0F;
void Update() {
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved) {
// Get movement of the finger since last frame
Vector3 touchDeltaPosition = Input.GetTouch(0).deltaPosition;
// Move object across XY plane
transform.Translate(touchDeltaPosition.x * speed, 0, 0);
Vector3 boundaryVector = transform.position;
boundaryVector.x = Mathf.Clamp (boundaryVector.x, -1f, 1f);
transform.position = boundaryVector;
}
}
For screen size problem I recommend u put some Image Ui at the background and then according to different resolution set it Using anchor property and .
Make height to width ratio 0.5,Set UI scale to screen size...all these in Inspector Only.
Get the left and right boundary values from Transform and then in block gameobj script use it as follows :
//windows specific code
float x=Input.GetAxis ("Horizontal");
if (x == 0) {
Stop ();
} else if (x > 0) {
MoveRight ();
} else if (x < 0) {
MoveLeft ();
}
pos = transform.position; //left //right
pos.x=Mathf.Clamp (pos.x, "your boundry min to", "Your boundry max");
transform.position = pos;
for overlap problem u can make use of Parent/ child property in Block .Make one the child of another..in this way If u move left the right block will also move left and they will not overlap....
I'm making a 2D game. I have a Cube Object falling down from the top of the screen, and I have pipes which the cube should pass by reversing gravity.
Well my object is falling down from the top of the screen. I tap on the screen to reverse gravity but it's not going immediately up: it takes time to change the gravity orientation. When i tap the screen my object continues falling and then goes up. My movement is forming the shape of a U when I tap the screen. The same thing happens when it goes up: I tap it to go down and in that case my movement forms the shape of a ∩.
What I want to achieve is that when I tap the screen my object's movement has instant response.
In addition, I want some sort of attenuation, damping, or smoothing.
I've tried these examples without success:
http://docs.unity3d.com/ScriptReference/Vector2.Lerp.html
http://docs.unity3d.com/Documentation/ScriptReference/Vector3.SmoothDamp.html
This is my code:
public class PlayerControls : MonoBehaviour
{
public GameObject playerObject = null;
Rigidbody2D player;
public float moveSpeed;
public float up;
Vector2 targetVelocity;
void Start()
{
player = playerObject.GetComponent<Rigidbody2D>();
// Set the initial target velocity y component to the desired up velocity.
targetVelocity.y = up;
}
void Update()
{
for (int i = 0; i < Input.touchCount; i++)
{
Touch touch = Input.GetTouch(i);
if (touch.phase == TouchPhase.Ended && touch.tapCount == 1)
{
// Flip the target velocity y component.
targetVelocity.y = -targetVelocity.y;
}
}
}
void FixedUpdate()
{
// Ensure if moveSpeed changes, the target velocity does too.
targetVelocity.x = moveSpeed;
// Change the player's velocity to be closer to the target.
player.velocity = Vector2.Lerp(player.velocity, targetVelocity, 0.01f);
}
}
This script is attached to the falling cube.
When affected by a constant force (in your code, gravity), an object will move in a parabola. You're observing a parabola: that U shape. To avoid the parabola and get instant response (a V shape), don't use forces. Replace the gravityScale code with something like:
Vector2 velocity = player.velocity;
velocity.y = -velocity.y;
player.velocity = velocity;
This inverts the velocity's y component, making the player move upwards.
You also mentioned you want to smooth this out. I suggest adding another variable: the target velocity.
To implement this, add Vector2 targetVelocity to your component. Then, during each FixedUpdate you can interpolate from the current velocity towards the target velocity to gradually change speed. Replace this line in your current code:
player.velocity = new Vector2(moveSpeed, player.velocity.y);
With this:
player.velocity = Vector2.Lerp(player.velocity, targetVelocity, 0.01f);
This will slowly change the velocity to be the same as the target velocity, smoothing out the movement. 0.01f is the place between the old velocity and the new velocity that the player's velocity is set to. Choose a larger number to interpolate more quickly, and a smaller number to change direction more slowly.
Then, instead of changing velocity when the screen is tapped, change targetVelocity. Make sure that the x component of targetVelocity is equal to moveSpeed, or the object won't move horizontally at all!
Combining these two changes, the Start and FixedUpdate methods should look similar to this:
void Start()
{
player = playerObject.GetComponent<Rigidbody2D>();
// Set the initial target velocity y component to the desired up velocity.
targetVelocity.y = up;
}
void Update()
{
for (int i = 0; i < Input.touchCount; i++)
{
Touch touch = Input.GetTouch(i);
if (touch.phase == TouchPhase.Ended && touch.tapCount == 1)
{
// Flip the target velocity y component.
targetVelocity.y = -targetVelocity.y;
}
}
}
void FixedUpdate()
{
// Ensure if moveSpeed changes, the target velocity does too.
targetVelocity.x = moveSpeed;
// Change the player's velocity to be closer to the target.
player.velocity = Vector2.Lerp(player.velocity, targetVelocity, 0.01f);
}
Note that you shouldn't use Input in FixedUpdate, because inputs are updated every frame and FixedUpdate may run multiple times per frame. This could cause the input to be read twice, especially if the frame rate gets slower and the fixed update is more likely to need to run multiple times per frame.
I am working on a 3D side scroller game in which my camera is following my character using Vector3.lerp.
Currently my camera is looking at the player from a side but at certain points I want the camera to transition to TopView (look at the character from the top) while also keeping a certain height from the character.
I have done this by creating camera settings naming SideView and TopView. Now the problem is that the camera does transitions from SideView to TopView but during transition the camera shakes when it is at the end of the lerp (when the camera is almost at the target position).
How to make this smooth (stop camera shake)?
Note: both camera and character are moving.
Here the source code of my camera follow:
void LateUpdate ()
{
if(currentCameraSettings != null && target.transform != null)
{
targetPos = SetCameraTargetPos(target.transform);
if(duration != 0)
{
transform.position = Vector3.Lerp(transform.position,targetPos,(Time.time - startTime ) / duration );
}
else
{
transform.position = targetPos;
}
SetCameraRotation();
}
}
SetCameraTargetPos returns the target position after adding height and z-axis distance from the target character.
Sounds like you have the wrong situation for a Lerp. I think what you want to use here is MoveTowards.
float step = speed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, target.position, step);
if its still "jaggedy" for you, try using Time.smoothDeltaTime.
Sorry for bad english.
If you want prevent to camera shaking your use rigidbody properties.
Freeze x,y,z.
or
Create empty object inside the your object with all the same control settings and followed the empty space not the your object.