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.
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'm trying to make a patrolling AI character that will move from point to point.
The patrol part works perfectly. However, the problem is that the sprite only faces right. When it turned the sprite stays facing the same direction.
I have tried to change the transform rotation using transform.rotate, transform.rotation, transform.Quaternion and making a variable to store the rotation value yet they all kick errors back. The errors are usually made from the rotate/rotation functions not being compatible with any of the attempts I have tried.
Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// To do:
/// - make rotation of enemy sprite work when reaching the end of patrol area
/// - create attack function
/// </summary>
public class Enemy : MonoBehaviour
{
public int health;
public float speed;
public GameObject bloodEffect;
public Transform[] moveSpots; //patrol spots
public float startWaitTime; //start countdown till move to next spot
private Rigidbody2D rb;
private Animator anim;
private int randomSpot; //number of patrol spots
private float waitTime; //how long enemy stays at patrol spot for
// Start is called before the first frame update
void Start()
{
waitTime = startWaitTime; //make waittime equal to startwaittime
anim = GetComponent<Animator>();
randomSpot = Random.Range(0, moveSpots.Length); //choose a random first spot
}
// Update is called once per frame
void Update()
{
Vector3 spriteRotation = new Vector3(0, randomSpot, 0);
transform.position = Vector2.MoveTowards(transform.position, moveSpots[randomSpot].position, speed * Time.deltaTime); //move toward first patrol area
transform.eulerAngles = spriteRotation;
if (Vector2.Distance(transform.position, moveSpots[randomSpot].position) < 0.5f) //asks if patrol point is further that .5f away from enemy
{
if (waitTime <= 0) //if waitTime less than or equal to 0
{
randomSpot = Random.Range(0, moveSpots.Length); //picks new patrol point
waitTime = startWaitTime; //restarts countdown clock
}
else
{
waitTime -= Time.deltaTime; //counts down clock till next point
}
}
if (health <= 0)
{
Destroy(gameObject);
}
}
public void TakeDamage(int damage)
{
Instantiate(bloodEffect, transform.position, Quaternion.identity);
Debug.Log("Blood effect played");
health -= damage;
Debug.Log("Damage Taken");
}
}
The expected results for this code is that a random point will be chosen and the AI will move toward that chosen point. Once there it will stay idle for a specified amount of time before turning and moving to a new spot.
The actual result is mostly the same as expected only the sprite does not turn around but instead continues to face to the right even when the AI is moving left.
Image of area
the enemy is the dark red cube, the movepoints are the points that the enemy patrols between. when it reaches the left point, he should turn to the right and go back but this is not what happens, instead he just moves back and forth with no rotation. ive tried the SpriteRenderer.flipX route and it only works one time and then sticks with that direction.
The SpriteRenderer Component has a Flip attribute you could use for this.
You can access it in code
SpriteRenderer.flipX = true;
It will only flip the sprite and won't change anything else, so double check if your colliders are still in the right space :) See more in the documentation
Good luck
randomSpot is an index not an angle. So using
transform.eulerAngles = new Vector3(0, randomSpot, 0);
doens't make any sense to me ...
Instead of rotating you could also flip the sprite/Image by using a negative scale like e.g.
// Update is called once per frame
private void Update()
{
// however you determin if facing left or right
// you e.g. simply check whether the target position
// is left or right of you
var difference = moveSpots[randomSpot].position - transform.position;
var isFacingRight = difference.x > 0;
if (isFacingRight && transform.localScale.x < 0
|| !isFacingRight && transform.localScale.x > 0)
{
FlipSprite();
}
}
private void FlipSprite()
{
// invert the local X-axis scale
transform.localScale = new Vector3(-spriteTransform.localScale.x, spriteTransform.localScale.y, spriteTransform.localScale.z);
}
Script used for the example
private void Update()
{
// works only in a ScreenOverlay Canvas
var targetPosition = Input.mousePosition;
var difference = targetPosition - transform.position;
var isFacingRight = difference.x > 0 ? true : false;
if (isFacingRight && transform.localScale.x < 0
|| !isFacingRight && transform.localScale.x > 0)
{
FlipSprite();
}
// simply only move left or right on the x-axis towards the mouse
transform.position = Vector3.MoveTowards(transform.position, new Vector3(targetPosition.x, 218, 0), Time.deltaTime * 100);
}
private void FlipSprite()
{
// invert the local X-axis scale
transform.localScale = new Vector3(-transform.localScale.x, transform.localScale.y, transform.localScale.z);
}
You can try:
if (moveSpot[randomSpot].transform.position.x > transform.position.x) {
transform.localScale = new Vector3(-1, 1, 1);
}
else {
transform.localScale = new Vector3(1, 1, 1);
}
Below is a copy of my player movement script which contains functions to animate my character moving left and right, jumping and shooting his bow. I have also been able to get my character to transition from shooting his bow to being in an alert animation, but I am wondering how to write the code so that for 5 seconds after I shoot my bow I will be alert before going back to my default "idle" animation.
I would also like my character to be able to walk around (i.e. transition the alert animation to the walk animation) while being alert, but if he stops moving he goes back to alert. Currently my character will go back to idle if he walks during his alert stance. The script below, specifically lines 98-102 (my "playerAlert" function) present another problem in that my character cannot perform his "shoot" animation after this function has occurred, but I do not know/can't wrap my head around how to code what I mentioned above.
I am using Unity 5.6.
I appreciate any help you guys may be able to give me.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class player_1_controller : MonoBehaviour {
Rigidbody2D myRB;
Animator myAnim;
bool facingRight;
//movement variables
public float maxSpeed;
//jumping variables
bool grounded = false;
float groundCheckRadius = 0.2f;
public LayerMask groundLayer;
public Transform groundCheck;
public float jumpHeight;
//arrow shooting variables
bool firing = true;
public float arrowShoot;
public Transform arrowTip;
public GameObject arrow;
public float fireRate = 0.5f;
public float nextFire = 0f;
// Use this for initialization
void Start () {
myRB = GetComponent<Rigidbody2D> ();
myAnim = GetComponent<Animator> ();
facingRight = true;
}
// Update is called once per frame
void Update () {
//player jump
if (grounded && Input.GetButtonDown ("Jump")) {
grounded = false;
myAnim.SetBool ("isGrounded", grounded);
myRB.AddForce (new Vector2 (0, jumpHeight));
}
//player shooting
if (grounded && Input.GetButtonDown ("Fire1")) {
myAnim.SetBool ("arrowShoot", firing);
Invoke ("fireArrow", 1);
}
}
void FixedUpdate() {
//player movement
float move = Input.GetAxis ("Horizontal");
myAnim.SetFloat ("speed", Mathf.Abs (move));
myRB.velocity = new Vector2 (move * maxSpeed, myRB.velocity.y);
if (move > 0 && !facingRight) {
flip ();
} else if (move < 0 && facingRight) {
flip ();
}
//player jump; check if we are grounded - if not, then we are falling
grounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundLayer);
myAnim.SetBool ("isGrounded", grounded);
myAnim.SetFloat ("verticalSpeed", myRB.velocity.y);
}
void flip () {
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
void fireArrow() {
if (Time.time > nextFire) {
nextFire = Time.time + fireRate;
if (facingRight) {
Instantiate (arrow, arrowTip.position, Quaternion.Euler (new Vector3 (0, 0, 0)));
} else if (!facingRight) {
Instantiate (arrow, arrowTip.position, Quaternion.Euler (new Vector3 (0, 0, 180)));
}
}
playerAlert ();
}
void playerAlert () {
firing = false;
myAnim.SetBool ("arrowShoot", firing);
}
}
Invoke can help you here. excerpt:
void Start()
{
Invoke("LaunchProjectile", 2);
}
void LaunchProjectile()
{
Rigidbody instance = Instantiate(projectile);
instance.velocity = Random.insideUnitSphere * 5;
}
You can use that to call(invoke) the "back to idle" function after 5 seconds.
Have you considered implementing it inside the animator?
You could create a transition from your Alert animation to your Idle animation, with a fixed exit time of 5 seconds.
This way, after 5 seconds of being in the Alert animation, it will transition to the Idle animation.
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);
}
}