I've got a quick question regarding 2D Sprite animations that I haven't been able to find specifically answered anywhere:
I have a sprite with walk animations to the right. However, I obviously want to flip the animation to the left when he walks left (2D side-scroller).
I can easily flip the sprite itself, using transform.localscale.x, however, that only flips the sprite. Not the animation clip. (This no longer happens in Unity)
So, while the sprite flips, the minute the animation clip begins playing, it flips back right (as the only animation clip I have is for the right facing sprite).
Is the only way to do this to flip the sprites in Photoshop, or is there a way to do it in Unity?
Thanks!
UPDATE: With the actual versions of unity if you scale the transform by multiplying it by -1, the animation frames are also scaled.
I finally figured it out by doing this:
void Flip()
{
// Switch the way the player is labelled as facing
facingRight = !facingRight;
// Multiply the player's x local scale by -1
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
This is from Unity's 2D Platformer example.
To implement some sort of checking which makes use of the Flip method, you can do something similar to the below example which is basic movement code. facingRight is set as a value on the class so that the other methods can use it, and it is defaulted to false.
void Update()
{
//On X axis: -1f is left, 1f is right
//Player Movement. Check for horizontal movement
if (Input.GetAxisRaw ("Horizontal") > 0.5f || Input.GetAxisRaw("Horizontal") < -0.5f)
{
transform.Translate (new Vector3 (Input.GetAxisRaw ("Horizontal") * moveSpeed * Time.deltaTime, 0f, 0f));
if (Input.GetAxisRaw ("Horizontal") > 0.5f && !facingRight)
{
//If we're moving right but not facing right, flip the sprite and set facingRight to true.
Flip ();
facingRight = true;
} else if (Input.GetAxisRaw("Horizontal") < 0.5f && facingRight)
{
//If we're moving left but not facing left, flip the sprite and set facingRight to false.
Flip ();
facingRight = false;
}
//If we're not moving horizontally, check for vertical movement. The "else if" stops diagonal movement. Change to "if" to allow diagonal movement.
} else if (Input.GetAxisRaw ("Vertical") > 0.5f || Input.GetAxisRaw("Vertical") < -0.5f)
{
transform.Translate (new Vector3 (0f, Input.GetAxisRaw ("Vertical") * moveSpeed * Time.deltaTime, 0f));
}
//Variables for the animator to use as params
anim.SetFloat ("MoveX", Input.GetAxisRaw ("Horizontal"));
anim.SetFloat ("MoveY", Input.GetAxisRaw ("Vertical"));
}
void FlipHorizontal()
{
animator.transform.Rotate(0, 180, 0);
}
You could also do that on transform itself (without animator). But in that case rotation value can be overriden by animator
This is how I did it - almost the same as the other technique by Jestus with unity script.
var facing : String = "right";
function updateFacing(curr : String){
if(curr != facing){
facing = curr;
var theScale : Vector3 = gameObject.transform.localScale;
theScale.x *= -1;
gameObject.transform.localScale = theScale;
}
}
//put to use
function controls(){
if(Input.GetKey (KeyCode.LeftArrow)){
updateFacing("left");
} else if(Input.GetKey (KeyCode.RightArrow)){
updateFacing("right");
}
}
If you're animating in Unity:
Copy all frames (sprites) of the animation that you want to flip over.
Paste those frames into your new animation and select everything on the first frame.
Change the x scale of the first frame from 1 to -1.
Do the same thing with the very last frame of your animation.
Now it should play facing the other direction!
This is my C# implementation. It uses a string as the direction facing to make it a little easier to debug.
public string facing = "right";
public string previousFacing;
private void Awake()
{
previousFacing = facing;
}
void Update()
{
// store movement from horizontal axis of controller
Vector2 move = Vector2.zero;
move.x = Input.GetAxis("Horizontal");
// call function
DetermineFacing(move);
}
// determine direction of character
void DetermineFacing(Vector2 move)
{
if (move.x < -0.01f)
{
facing = "left";
}
else if (move.x > 0.01f)
{
facing = "right";
}
// if there is a change in direction
if (previousFacing != facing)
{
// update direction
previousFacing = facing;
// change transform
gameObject.transform.Rotate(0, 180, 0);
}
}
Related
So I have a script for my player where I have disabled diagonal move. I then created a script for his dog companion that follows him in the world and it's almost perfected except when I do a direction change from left/right to up/down or vice versa the dog will follow at a diagonal and looks really weird since there is no animations and he basically slides there. I tried to disable diagonal like how I did for the player but it doesn't work. Is there a way to go about this or would it be better to just add in a diagonal animation for just the dog?
public class Bowser : MonoBehaviour
{
public float speed;
private Transform target;
private Vector2 move;
private Animator anim;
private void Awake()
{
anim = GetComponent<Animator>();
}
// Start is called before the first frame update
void Start()
{
target = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>();
}
// Update is called once per frame
void Update()
{
move.x = Input.GetAxisRaw("Horizontal");
move.y = Input.GetAxisRaw("Vertical");
if (Vector2.Distance(transform.position, target.position) > 1.5)
{
// Code attempt to get rid of diagonal movement
if (move.x != 0) move.y = 0;
if (move != Vector2.zero)
{
anim.SetFloat("moveX", move.x);
anim.SetFloat("moveY", move.y);
anim.SetBool("moving", true);
}
transform.position = Vector2.MoveTowards(transform.position, target.position, speed * Time.deltaTime);
}
else
anim.SetBool("moving", false);
}
}
Instead of just checking for 0 you could just use the bigger value
if (Mathf.Abs(move.x) > (Mathf.Abs(move.y)) move.y = 0;
else move.x = 0;
it would be better if you just provided diagonal animations for the dog
Thanks for the advice in the end I found that it became a smoother transition to just add this
if (Mathf.Abs(move.x) > .01f)
targetPosition.y = transform.position.y;
if (Mathf.Abs(move.y) > .01f)
targetPosition.x = transform.position.x;
and then change all my target.Position to the targetPosition
whenever the player uses its jetpack or when there's a lot of velocity, the player stutters. I tried using interpolate and other things like that but the only outcome was even more stutter. If anyone knows what the cause for the stuttering is, please tell me and if you want to, maybe even explain your answer :)
here is what i mean by the player stuttering :
https://www.youtube.com/watch?v=k6q3vvQtwjM
here is my player code :
using UnityEngine;
using UnityEngine.SceneManagement;
public class Player : MonoBehaviour
{
//basic variables
[SerializeField]
private float speed = 5f;
[SerializeField]
private float Jumpforce = 5f;
[SerializeField]
private float JetPackForce = 5f;
[SerializeField]
public bool canUseJetpack = true;
[SerializeField]
private Rigidbody2D rb;
[SerializeField]
private Animator animator;
private bool isFacingRight = true;
//runs when game starts
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
//runs every frame
void Update()
{
//applies force thus making player to move left or right
var move = Input.GetAxis("Horizontal");
transform.position = transform.position + new Vector3(move * speed * Time.deltaTime, 0, 0);
//changes player animation state
animator.SetFloat("Speed", Mathf.Abs(move));
//flip the player if facing right or left
if (move < 0 && isFacingRight)
{
flip();
}
else if (move > 0 && !isFacingRight)
{
flip();
}
//checks if the space key is pressed and that the player velocity on the y axis is smaller than 0.001
if(Input.GetKeyDown("space") && Mathf.Abs(rb.velocity.y) < 0.001f)
{
//adds force from below the player thus making the player jump
rb.AddForce(new Vector2(0, Jumpforce), ForceMode2D.Impulse);
}
//checks for the key 'Q' to be pressed and that there is enough fuel in the jetpack
if (Input.GetKey(KeyCode.Q) && canUseJetpack == true)
{
//ads force from below the player thus making the player fly using its jetpack
rb.AddForce(Vector2.up * JetPackForce);
//decreases the fuel in the jetpack
JetpackBar.instance.UseFuel(0.1f);
//makes the player run the using jetpack animation
animator.SetBool("UsingJetpack", true);
}
else
{
//if the player isn't using the jetpack, switch to the idle animation or the run animation depending if the player is moving or not
animator.SetBool("UsingJetpack", false);
}
//checks if the player health is less than 0
if (HealthBar.instance.currentHealth <= 0)
{
//if so, restart the game
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
}
//checks if someting collided with the player
private void OnCollisionEnter2D(Collision2D collision)
{
//if the thing collided with the player has a tag of 'Enemy'
if (collision.gameObject.CompareTag("Enemy"))
{
//then, make the player take damage
HealthBar.instance.takeDamage(25);
}
}
//flip the player
void flip()
{
isFacingRight = !isFacingRight;
transform.Rotate(0f, 180f, 0f);
}
}
Thanks :D
First of all whenever dealing with Rigidbodies you do not want to use the Transform component to move an object. You rather want to only move it via the Rigidbody/Rigidbody2D component!
Then there is a general issue. You can either move your object hard based on the input or use the physics and forces. Both at the same time is not possible because both transform.position or the actually "correct" Rigidbody2D.MovePosition will overrule the forces so these two systems are clashing all the time.
Therefore I would rather simply overwrite the velocity directly like e.g.
void Update()
{
// Store the current velocity
var velocity = rb.velocity;
var move = Input.GetAxis("Horizontal");
// Only overwrite the X component
// We don't need Time.deltaTime since a velocity already
// moves the object over time
velocity.x = move * speed;
animator.SetFloat("Speed", Mathf.Abs(move));
if (move < 0 && isFacingRight)
{
flip();
}
else if (move > 0 && !isFacingRight)
{
flip();
}
if(Input.GetKeyDown("space") && Mathf.Abs(rb.velocity.y) < 0.001f)
{
// simply overwrite the Y velocity
// You'll have to adjust that value of course
// and might want to rename it ;)
velocity.y = Jumpforce;
}
if (Input.GetKey(KeyCode.Q) && canUseJetpack)
{
// Here we want to use Time.deltaTime since the jetpack
// shall increase the Y velocity over time
// again you'll have to adjust/rename that value
velocity.y += JetPackForce * Time.deltaTime;
// Also adjust this value!
// To be frame-rate independent you want to use Time.deltaTime here
JetpackBar.instance.UseFuel(0.1f * Time.deltaTime);
animator.SetBool("UsingJetpack", true);
}
else
{
animator.SetBool("UsingJetpack", false);
}
// After having calculated all changes in the velocity now set it again
rb.velocity = velocity;
if (HealthBar.instance.currentHealth <= 0)
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
}
I have start a project and I attached a rigidbody to my player for apply some force on it.So when I run my project then in FixedUpdate() function I apply a force on player to moving it in forward direction.So when I pressed leftarrow or rightarrow it perform 'tilt' means rotate it's wings.
But now I want to move my player up or down smoothly by pressing Uparrow or Downarrow,and when I press both uparrow and downarrow then it must be affect on player.
Here is my code.Please help me. :(
void FixedUpdate ()
{
rb.AddForce(transform.forward *1000f);
float movedir = Input.GetAxis ("Horizontal");
Vector3 movement = new Vector3 (movedir, 0.0f, 0.0f);
rb.velocity = movement * movingspeed;
rb.rotation = Quaternion.Euler (0.0f, 0.0f, rb.velocity.x * -3f);//perform tilt
if (Input.GetKeyDown (KeyCode.UpArrow))
{
//code for smoothly move up from current position to = current position + 2f
}
if (Input.GetKeyDown (KeyCode.DownArrow))
{
//code for smoothly move down from current position to = current position - 2f
}
}
Your question is not really clear. Move smoothly could mean many things.
In general you should use RigidBody.MovePosition and Rigidbody.MoveRotation to set a Rigidbody's transforms instead of rb.rotation and rb.position in order to get "smooth" movements:
Use Rigidbody.MovePosition to move a Rigidbody, complying with the Rigidbody's interpolation setting.
If Rigidbody interpolation is enabled on the Rigidbody, calling Rigidbody.MovePosition results in a smooth transition between the two positions in any intermediate frames rendered. This should be used if you want to continuously move a rigidbody in each FixedUpdate.
Set Rigidbody.position instead, if you want to teleport a rigidbody from one position to another, with no intermediate positions being rendered.
So as you can see depending on your settings using Rigidbody.MovePosition might already result in a "smooth" movement.
rb.MovePosition(transform.position + Vector3.up * 2.0f);
Also you use Input.GetKeyDown so it works like a trigger ... it is not called continously like Input.GetKey
If you want to move continously while the key stays pressed use e.g.
// set e.g. in the inspector
public float verticalMoveSpeed;
// ...
if (Input.GetKey(KeyCode.UpArrow))
{
rb.MovePosition(transform.position + Vector3.up * verticalMoveSpeed * Time.deltaTime);
}
if (Input.GetKey(KeyCode.DownArrow))
{
rb.MovePosition(transform.position - Vector3.up * verticalMoveSpeed * Time.deltaTime);
}
If you want to trigger the movement only with GetKeyDown instead you could also do something like e.g.
// set e.g. in the inspector
public float verticalMoveSpeed;
// ...
if (Input.GetKeyDown(KeyCode.UpArrow))
{
StartCoroutine(MoveVertical(2.0f, verticalMoveSpeed));
}
else if (Input.GetKeyDown(KeyCode.DownArrow))
{
StartCoroutine(MoveVertical(-2.0f, verticalMoveSpeed));
}
// ...
private IEnumerator MoveVertical(float distance, float speed)
{
var originalY = transform.position.y;
var targetY = originalY + distance;
var currentY = originalY;
do
{
rb.MovePosition(new Vector 3(transform.position.x, currentY, transform.positiom.z);
// Update currentY to the next Y position
currentY = Mathf.Clamp(currentY + speed * Time.deltaTime, originalY, targetY);
yield return null;
}
while(currentY < originalY);
// make sure you didn't move to much on Y
rb.MovePosition(new Vector3(transform.position.x, targetY, transform.position,z));
}
Than there are two options to prevent concurrent routines:
use a flag. This also prevents the routine from beeing interrupted/called twice/called concurrent
privtae bool isMovingVertical;
// ...
if (Input.GetKeyDown(KeyCode.UpArrow) && !isMovingVertical )
{
StartCoroutine(MoveVertical(2.0f, verticalMoveSpeed));
}
else if (Input.GetKeyDown(KeyCode.DownArrow) && !isMovingVertical )
{
StartCoroutine(MoveVertical(-2.0f, verticalMoveSpeed));
}
// ...
private IEnumerator MoveVertical(float distance, float speed)
{
isMovingVertical = true;
// ...
isMovingVertical = false;
}
use StopAllCoroutines to interrupt a running routine (attention this might lead to "infinite" moves in one direction - at least without you preventing it with additional checks)
if (Input.GetKeyDown(KeyCode.UpArrow))
{
StopAllCoroutines();
StartCoroutine(MoveVertical(2.0f, verticalMoveSpeed));
}
if (Input.GetKeyDown(KeyCode.DownArrow))
{
StopAllCoroutines();
StartCoroutine(MoveVertical(-2.0f, verticalMoveSpeed));
}
I'm some hours already trying to solve this, I tried many ways but none of them worked...
So I have an enemy that follows the player using an AI and I need the sprite to flip when the enemy turn left or turn right.
Here's a part of my code (A lot of the code is about the AI so I'll post just part of it)
Vector3 dir = ( path.vectorPath[currentWaypoint] - transform.position ).normalized;
dir *= speed * Time.fixedDeltaTime;
//Move the AI
rb.AddForce (dir, fMode);
void Update () {
if (rb.AddForce > 0) {
sr.flipX = false;
} else if (rb.AddForce < 0)
sr.flipX = true;
animate.SetFloat ("pMove", Mathf.Abs(rb.AddForce));
}
The easiest way to flip a sprite is using localScale.x *= -1; And instead of checking the AddForce you should check the velocity of the rigidbody in the x axis (or y axis if you flip as well the sprite depending on if it is jumping or falling)
Basically in Update() you can do something like this: vx = rigidbody.velocity.x; to store the velocity in the x axis of the sprite. Then in LastUpdate() you check if it is necessary to flip the sprite or not:
if (vx > 0) {
facingRight = true;
} else if (vx < 0) {
facingRight = false;
}
if (((facingRight) && (localScale.x<0)) || ((!facingRight) && (localScale.x>0))) {
localScale.x *= -1;
}
Here you have a whole example where the sprite moves based on the player's inputs. You will need to addapt it for your AI.
//store references to components on the gameObject
Transform transform;
Rigidbody2D rigidbody;
public float MoveSpeed = 3f;
// hold player motion in this timestep
float vx;
float vy;
Awake () {
// get a reference to the components we are going to be changing and store a reference for efficiency purposes
transform = GetComponent<Transform> ();
rigidbody = GetComponent<Rigidbody2D> ();
}
void Update()
{
// determine horizontal velocity change based on the horizontal input
vx = Input.GetAxisRaw ("Horizontal");
//Change in case you are jumping or falling
vy = rigidbody.velocity.y;
// Change the actual velocity on the rigidbody
rigidbody.velocity = new Vector2(_vx * MoveSpeed, _vy);
}
// Checking to see if the sprite should be flipped
// this is done in LateUpdate since the Animator may override the localScale
// this code will flip the player even if the animator is controlling scale
void LateUpdate()
{
// get the current scale
Vector3 localScale = transform.localScale;
if (vx > 0) // moving right so face right
{
facingRight = true;
} else if (vx < 0) { // moving left so face left
facingRight = false;
}
// check to see if scale x is right for the player
// if not, multiple by -1 which is an easy way to flip a sprite
if (((facingRight) && (localScale.x<0)) || ((!facingRight) && (localScale.x>0))) {
localScale.x *= -1;
}
// update the scale
transform.localScale = localScale;
}
Assuming, sr is a SpriteRenderer component, sr.flipX approach is fine. However, your assessment of the force on the Rigidbody is not right. rb.AddForce has the return type of void, and a proper way of reading whether the Rigidbody had an applied force on it is to read rb.velocity.magnitude. Also, rb.velocity would give you the direction of the GameObject's velocity as a Vector3. Assuming that you are working on the X-axis, putting this in your LateUpdate method:
sr.flipX = rb.velocity.magnitude > 0 && rb.velocity.x < 0 ? true : false;
instead of the if-else block at your Update method would flip the sprite along the X-axis if the Rigidbody is moving (rb.velocity.magnitude > 0) and it is moving towards the left-hand side (rb.velocity.x < 0).
In your question, you have asked only to flip the sprite: As Unity documentation states, flipX only affects rendering, not the other components (such as colliders and animators).
Got this simple character controller that moves my platformer character left and right with a flip.
Was hoping someone could integrate or show me how to add in touch controls for android/ios.
Just a simple touch left side of screen to go left and right side to go right.
Thanks
using UnityEngine;
using System.Collections;
public class RobotController : MonoBehaviour {
//This will be our maximum speed as we will always be multiplying by 1
public float maxSpeed = 2f;
//a boolean value to represent whether we are facing left or not
bool facingRight = false;
//a value to represent our Animator
Animator anim;
// Use this for initialization
void Start () {
//set anim to our animator
anim = GetComponent<Animator>();
}
// Update is called once per frame
void FixedUpdate () {
float move = Input.GetAxis ("Horizontal");//Gives us of one if we are moving via the arrow keys
//move our Players rigidbody
rigidbody2D.velocity = new Vector3 (move * maxSpeed, rigidbody2D.velocity.y);
//set our speed
anim.SetFloat ("Speed",Mathf.Abs (move));
//if we are moving left but not facing left flip, and vice versa
if (move < 0 && !facingRight) {
Flip ();
} else if (move > 0 && facingRight) {
Flip ();
}
}
//flip if needed
void Flip(){
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
}
In the Update() method, you'll want to use a foreach loop which loops through all the touch events in that particular frame update.
foreach(Touch touch in Input.touches)
{
if(leftButton.HitTest(touch.position))
{
//move character left
}
if(rightButton.HitTest(touch.position))
{
//move character right
}
}
This assumes that you're setting up two GUITexture buttons for your left and right controls in the start() method.