Unity 2D UI element direction changes according to its game object direction - unity3d

So I've added a health bar below the main player, following a tutorial. I had a canvas with render mode set to "World Space". I added the UI elements for the healthbar to the canvas. Then I made the canvas a child of the player Mario. Now the healthbar follows the player. The problem is that whenever Mario changes direction on the x axis his sprite changes direction but also does the healthbar, because in Mario's script the localscale changes according to the direction of the player . Any ideas?
Mario's script:
public class MarioMove : MonoBehaviour
{
private Rigidbody2D rb;
private Animator anim;
private float moveSpeed;
private float dirX;
private bool facingRight = true;
private Vector3 localScale;
private bool doubleJumped;
[SerializeField] HealthBar healthbar;
private void Start()
{
rb = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
localScale = transform.localScale;
moveSpeed = 5f;
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.DownArrow)) {
PlayerTakeDamage(20);
}
if (Input.GetKeyDown(KeyCode.UpArrow))
{
PlayerHeal(20);
}
dirX = Input.GetAxisRaw("Horizontal") * moveSpeed;
if (Input.GetButtonDown("Jump") && rb.velocity.y == 0)
rb.AddForce(Vector2.up * 400f);
if (Mathf.Abs(dirX) > 0 && rb.velocity.y == 0)
anim.SetBool("isRunning", true);
else
anim.SetBool("isRunning", false);
if (rb.velocity.y == 0)
{
anim.SetBool("isJumping", false);
anim.SetBool("isFalling", false);
doubleJumped = false;
}
if (rb.velocity.y > 0 && !doubleJumped)
{
if (Input.GetButtonDown("Jump")){
anim.SetBool("isDoubleJumping", true);
rb.AddForce(Vector2.up * 100f);
anim.SetBool("isJumping", false);
doubleJumped = true;
}
else anim.SetBool("isJumping", true);
}
if (rb.velocity.y > 0 && doubleJumped)
{
anim.SetBool("isDoubleJumping", true);
}
if (rb.velocity.y < 0)
{
if (Input.GetButtonDown("Jump") && !doubleJumped)
{
anim.SetBool("isDoubleJumping", true);
rb.velocity = Vector2.zero;
rb.AddForce(Vector2.up * 200f);
anim.SetBool("isFalling", false);
doubleJumped = true;
}
else {
anim.SetBool("isJumping", false);
anim.SetBool("isDoubleJumping", false);
anim.SetBool("isFalling", true);
}
}
}
private void FixedUpdate()
{
rb.velocity = new Vector2(dirX, rb.velocity.y);
}
private void LateUpdate()
{
if (dirX > 0)
facingRight = true;
else if(dirX < 0)
facingRight = false;
if (((facingRight) && (localScale.x < 0)) || ((!facingRight) && (localScale.x > 0)))
localScale.x *= -1;
transform.localScale = localScale;
}
private void PlayerTakeDamage(int damage)
{
GameManager.gameManager.playerHealth.DamageUnit(10);
healthbar.SetHealth(GameManager.gameManager.playerHealth.Health);
}
private void PlayerHeal(int healing)
{
GameManager.gameManager.playerHealth.HealUnit(10);
healthbar.SetHealth(GameManager.gameManager.playerHealth.Health);
}
}

I solved by making the scaling of the UI independent of the scaling of the Mario object (made the mario and u elements different children of a game object and applied different transformations to them that are independent to each other).

Related

Unity: When I instantiate my character, it instantiates twice

I'm working on a platformer game, and I instantiate my character after he gets hit by an enemy object. The problem is, my character is instantiating twice. Here's my code.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class PlayerMove : MonoBehaviour
{
public GameObject player;
private Rigidbody2D rb;
private SpriteRenderer sprite;
private BoxCollider2D coll;
private Animator anim;
public float Speed;
public float JumpForce;
private float dirX = 0f;
private float fJumpPressedRemember = 0f;
private float fJumpPressedRememberTime = 0.2f;
[SerializeField] private LayerMask jumpableGround;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
sprite = GetComponent<SpriteRenderer>();
coll = GetComponent<BoxCollider2D>();
}
// Update is called once per frame
void Update()
{
dirX = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector2(dirX * Speed, rb.velocity.y);
fJumpPressedRemember -= Time.deltaTime;
if (Input.GetButtonDown("Jump"))
{
fJumpPressedRemember = fJumpPressedRememberTime;
}
if ((fJumpPressedRemember > 0) && isGrounded())
{
fJumpPressedRemember = 0;
rb.velocity = new Vector2(rb.velocity.x, JumpForce);
}
UpdateAnimation();
}
private bool isGrounded()
{
return Physics2D.BoxCast(coll.bounds.center, coll.bounds.size, 0f, Vector2.down, .1f, jumpableGround);
}
void OnCollisionEnter2D(Collision2D spikeCol)
{
if (spikeCol.gameObject.tag.Equals("DamageDealer") == true)
{
Instantiate(player, new Vector3(-10, 1, 0), Quaternion.identity);
Destroy(player);
}
if (spikeCol.gameObject.tag.Equals("End") == true)
{
SceneManager.LoadScene("Level2", LoadSceneMode.Single);
}
if (spikeCol.gameObject.tag.Equals("End2") == true)
{
SceneManager.LoadScene("Level1", LoadSceneMode.Single);
}
}
private void UpdateAnimation()
{
if (dirX > 0f)
{
anim.SetBool("running", true);
sprite.flipX = false;
}
else if (dirX < 0f)
{
anim.SetBool("running", true);
sprite.flipX = true;
}
else
{
anim.SetBool("running", false);
}
}
}
It's supposed to instantiate once after dying. I don't know what's happening, but it only started after I added animation code, so I think that's the issue. Thanks to anyone who can help!
OnCollisionEnter2D is called twice.
Reasons why this can be described here: Why is OnCollisionEnter getting called twice?
You can add instantiate method double call check, like this:
private bool isInited;
void OnCollisionEnter2D(Collision2D spikeCol)
{
if (spikeCol.gameObject.tag.Equals("DamageDealer") == true)
{
if (isInited)
{
return;
}
isInited = true;
Instantiate(player, new Vector3(-10, 1, 0), Quaternion.identity);
Destroy(player);
}
...
}

Unity Raycast has inconsistent behavior. Sometimes it collides with the collider it starts in and sometimes not

The text in this image pretty much explains everything.
This is the code that creates the rays. You may ignore Debug.DrawRay().
RaycastHit hitInfo;
RaycastHit wall;
RaycastHit backFace;
if(Physics.Raycast(barrelEnd.transform.position, barrelEnd.transform.forward, out hitInfo)) {
Debug.DrawRay(barrelEnd.transform.position, barrelEnd.transform.forward*5, Color.green, 5f, false);
Instantiate(rayOneHit, hitInfo.point, Quaternion.identity);
Enemy target = hitInfo.transform.GetComponent<Enemy>();
if(target != null) {
if(Physics.Raycast(hitInfo.point, barrelEnd.transform.forward, out wall)) {
Debug.DrawRay(hitInfo.point, (hitInfo.point - barrelEnd.transform.position)*2, Color.blue, 5f, false);
Instantiate(rayTwoHit, wall.point, Quaternion.identity);
if(Physics.Raycast(wall.point, ((hitInfo.point - wall.point)/Vector3.Distance(hitInfo.point,wall.point)), out backFace)) {
Debug.DrawRay(wall.point, (hitInfo.point - wall.point)*10, Color.red, 5f, false);
Vector3 hitLocation = new Vector3(backFace.point.x, backFace.point.y, backFace.point.z);
Instantiate(hitPoint, hitLocation, Quaternion.identity);
}
}
target.dealDamage(damage);
}
lineRenderer.SetPosition(0, aim.transform.position);
lineRenderer.SetPosition(1, hitInfo.point);
}
This is all inside of a function Shoot() which is called as follows:
void Update() {
if(Input.GetButtonDown("Fire1")) {
Shoot();
}
}
Full code if needed:
using System.Collections;
using UnityEngine;
public class Shooting : MonoBehaviour {
[SerializeField] Transform aim;
[SerializeField] LineRenderer lineRenderer;
public float damage = 30f;
public Transform barrelEnd;
public Transform crosshair;
public Light muzzleFlash;
public Object hitPoint;
public Object rayOneHit;
public Object rayTwoHit;
public LayerMask enemyLayer;
//public Light muzzleFlashSelf;
// public GameObject projectile;
public float force = 100f;
void Start() {
Cursor.visible = false;
muzzleFlash.enabled = false;
//muzzleFlashSelf.enabled = false;
}
void Update() {
if(Input.GetButtonDown("Fire1")) {
Shoot();
}
}
void Shoot() {
RaycastHit hitInfo;
RaycastHit wall;
RaycastHit backFace;
StartCoroutine(DrawLine());
StartCoroutine(Flash());
if(Physics.Raycast(barrelEnd.transform.position, barrelEnd.transform.forward, out hitInfo)) {
Debug.DrawRay(barrelEnd.transform.position, barrelEnd.transform.forward*5, Color.green, 5f, false);
Instantiate(rayOneHit, hitInfo.point, Quaternion.identity);
Enemy target = hitInfo.transform.GetComponent<Enemy>();
if(target != null) {
if(Physics.Raycast(hitInfo.point, barrelEnd.transform.forward, out wall)) {
Debug.DrawRay(hitInfo.point, (hitInfo.point - barrelEnd.transform.position)*2, Color.blue, 5f, false);
Instantiate(rayTwoHit, wall.point, Quaternion.identity);
if(Physics.Raycast(wall.point, ((hitInfo.point - wall.point)/Vector3.Distance(hitInfo.point,wall.point)), out backFace)) {
Debug.DrawRay(wall.point, (hitInfo.point - wall.point)*10, Color.red, 5f, false);
Vector3 hitLocation = new Vector3(backFace.point.x, backFace.point.y, backFace.point.z);
Instantiate(hitPoint, hitLocation, Quaternion.identity);
}
}
target.dealDamage(damage);
}
lineRenderer.SetPosition(0, aim.transform.position);
lineRenderer.SetPosition(1, hitInfo.point);
}
}
IEnumerator DrawLine() {
lineRenderer.enabled = true;
float t = 0;
float time = 0.01f;
Vector3 orig = lineRenderer.GetPosition(0);
Vector3 orig2 = lineRenderer.GetPosition(1);
lineRenderer.SetPosition(1, orig);
for (; t < time; t += Time.deltaTime) {
yield return null;
}
lineRenderer.SetPosition(1, orig2);
lineRenderer.enabled = false;
}
IEnumerator Flash() {
float t = 0;
float time = 0.1f;
muzzleFlash.enabled = true;
//muzzleFlashSelf.enabled = true;
for (; t < time; t += Time.deltaTime) {
yield return null;
}
muzzleFlash.enabled = false;
//muzzleFlashSelf.enabled = false;
}
}
I've tried to start the 2nd raycast slightly further inside of the enemy, because I thought maybe it is hitting the very edge of the collider but this changed nothing. I've also tried to use ~enemyMask to exclude the enemy, but this just made the second ray not fire at all.

Sticky Projectile and Explosive Projectile problem c#

I cannot figure out what the problem is, suppose to be, when both explodeOnTouch and isSticky is true, it will call the parameter below, first stop the projectile movement with isKinematic then call the coroutine, but what happens is that only isKinematic is being called. Also extra questions, why is that the first projectile I instantiate is very fast, faster than the normal force I applied, and also why is that after the projectile lifeSpan is done and the projectile gameObject is destroyed, the force is still being applied making the for example enemy gameObject flying.
[Header("==BULLET SETTINGS==")]
[SerializeField] bool useGravity;
[SerializeField] float lifeSpan = 3.2f;
[SerializeField] List<string> tagName;
[Header("==BOUNCING==")]
[Range(0, 1)]
[SerializeField] float bounciness;
[SerializeField] bool isBouncing;
[Header("==EXPLOSIVE==")]
[SerializeField] bool explodeOnTouch = true;
[SerializeField] bool isSticky = true;
[SerializeField] float explosionRange;
[SerializeField] float explosionForce;
[SerializeField] float triggerForce;
[Header("==FREEZE==")]
[SerializeField] bool isFrozen;
[Header("==REFERENCE==")]
[SerializeField] LayerMask layer;
[SerializeField] Rigidbody rb;
PhysicMaterial physics_mat;
private void Start()
{
Setup();
}
private void Update()
{
BulletLifeSpan();
}
private void OnCollisionEnter(Collision collision)
{
Rigidbody enemyRb = collision.gameObject.GetComponent<Rigidbody>();
var collided = collision.gameObject.CompareTag(tagName[0]) || collision.gameObject.CompareTag(tagName[1]);
var explode = collision.relativeVelocity.magnitude >= triggerForce && explodeOnTouch;
if (explode && !isSticky)
{
Explode();
}
if (collided && isSticky && !explodeOnTouch)
{
rb.isKinematic = true;
}
if (explode && isSticky)
{
rb.isKinematic = true;
StartCoroutine(Delay());
}
if (collided && isFrozen)
{
enemyRb.isKinematic = true;
}
else if(collided && !isBouncing && !isSticky && !explodeOnTouch)
{
Destroy(gameObject);
}
Debug.Log("Player collided with: " + collision.gameObject.name + " with powerup set to none");
//Destroy(gameObject);
}
void Explode()
{
var surroundingObjects = Physics.OverlapSphere(transform.position, explosionRange);
foreach (var obj in surroundingObjects)
{
var rb = obj.GetComponent<Rigidbody>();
if (rb == null) continue;
rb.AddExplosionForce(explosionForce, transform.position, explosionRange);
}
Destroy(gameObject);
}
IEnumerator Delay()
{
yield return new WaitForSeconds(lifeSpan);
Explode();
}
void BulletLifeSpan()
{
//Bullet lifespan
lifeSpan -= Time.deltaTime;
if (lifeSpan <= 0)
{
Destroy(gameObject);
}
}
void Setup()
{
//Create a new Physic material
physics_mat = new PhysicMaterial();
physics_mat.bounciness = bounciness;
physics_mat.frictionCombine = PhysicMaterialCombine.Minimum;
physics_mat.bounceCombine = PhysicMaterialCombine.Maximum;
//Assign material to collider
GetComponent<SphereCollider>().material = physics_mat;
//Set gravity
rb.useGravity = useGravity;
}
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, explosionRange);
}

restrict camera movement, Camera moves out of bounds

I'm creating a simple camera pan and scroll for my mobile game and need some help.
Right now with the code I have a player can scroll of the map I created.
I got the code from this youtube video (for context) https://gist.github.com/ditzel/836bb36d7f70e2aec2dd87ebe1ede432
https://www.youtube.com/watch?v=KkYco_7-ULA&ab_channel=DitzelGames
Do you know how I could fix this?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ScrollAndPinch : MonoBehaviour
{
#if UNITY_IOS || UNITY_ANDROID
public Camera Camera;
public bool Rotate;
protected Plane Plane;
public Transform Map;
float distance;
private void Awake()
{
if (Camera == null)
Camera = Camera.main;
Input.multiTouchEnabled = true;
}
private void Update()
{
//Update Plane
if (Input.touchCount >= 1)
Plane.SetNormalAndPosition(transform.up, transform.position);
var Delta1 = Vector3.zero;
var Delta2 = Vector3.zero;
//Scroll
if (Input.touchCount >= 1)
{
Delta1 = PlanePositionDelta(Input.GetTouch(0));
if (Input.GetTouch(0).phase == TouchPhase.Moved)
Camera.transform.Translate(Delta1, Space.World);
}
}
protected Vector3 PlanePositionDelta(Touch touch)
{
//not moved
if (touch.phase != TouchPhase.Moved)
return Vector3.zero;
//delta
var rayBefore = Camera.ScreenPointToRay(touch.position - touch.deltaPosition);
var rayNow = Camera.ScreenPointToRay(touch.position);
if (Plane.Raycast(rayBefore, out var enterBefore) && Plane.Raycast(rayNow, out var enterNow))
return rayBefore.GetPoint(enterBefore) - rayNow.GetPoint(enterNow);
//not on plane
return Vector3.zero;
}
protected Vector3 PlanePosition(Vector2 screenPos)
{
//position
var rayNow = Camera.ScreenPointToRay(screenPos);
if (Plane.Raycast(rayNow, out var enterNow))
return rayNow.GetPoint(enterNow);
return Vector3.zero;
}
#endif
}
You can put in the update a control with the bounds of your area with a bool like this :
if(Camera.transform.position.x > valuexmax || Camera.transform.position.x < valuexmin
|| Camera.transform.position.y > valueymax || Camera.transform.position.y < valueymin)
{
InBounds = false; //just a bool variable set to true in start
}
And then you can put in the scroll something like this
if (Input.touchCount >= 1 && InBounds == true)
{
Delta1 = PlanePositionDelta(Input.GetTouch(0));
if (Input.GetTouch(0).phase == TouchPhase.Moved)
Camera.transform.Translate(Delta1, Space.World);
}
if(InBounds == false)
{
// reset position
}

Player Controller constantly rotates player when there is no input?

Player controller constantly rotates the player when there is no input. The player is idle, not rotating, until the up arrow is pressed. Then it begins rotating constantly.
This happens whether mouseRotate is true or not.
If tried a number of things, including commenting out the mouserotate line altogether, as well as taking out the animations temporarily.
using UnityEngine;
//using UnityStandardAssets.CrossPlatformInput;
using UnityEngine.Networking;
public class PlayerController : MonoBehaviour
{
// Updated 2019-11-06 //
public float movementSpeed=1;
public float runSpeed=2;
bool isOnGround;
Rigidbody rb;
private Vector3 moveDirection = Vector3.zero;
private Animator anim;
public bool mouseRotate = true;
public float rotationSpeed = 200f;
void Start()
{
rb = GetComponent<Rigidbody>();
anim = GetComponent<Animator>();
}
void Update()
{
updateAnim();
ProcessJumping();
moveDirection.y -= 10f * Time.deltaTime;
if (mouseRotate)
{
transform.Rotate(Vector3.up * (Input.GetAxis("Mouse X")) * Mathf.Sign(Input.GetAxis("Horizontal")), Space.World);//mouse rotate
if (Input.GetKey("up") || Input.GetKey("down"))
{
transform.Translate(0, 0, Input.GetAxis("Vertical") * Time.deltaTime * movementSpeed);
}
if (Input.GetKey("left") || Input.GetKey("right"))
{
transform.Translate(Input.GetAxis("Horizontal") * Time.deltaTime * runSpeed, 0, 0);
}
}
else//traditional keyboard controls-- can implement menu to choose rotation style
{
// updated by Yizhi 11/10/2019
if (Input.GetKey("up") || Input.GetKey("down"))
{
transform.Translate(0, 0, Input.GetAxis("Vertical") * Time.deltaTime * movementSpeed);
}
if (Input.GetKey("right") || Input.GetKey("left"))
{
transform.Rotate(0, Input.GetAxis("Horizontal") * Time.deltaTime * rotationSpeed, 0);
}
}
}
void ProcessJumping()
{
CheckIfOnGround();
if (Input.GetKeyDown(KeyCode.Space) && isOnGround)//(Input.GetKeyDown(KeyCode.Space) && isOnGround)//removed until network control implememnted
{
transform.Translate(0, 0.75f, 0);
isOnGround = false;
anim.SetBool("Jump_b", true);
}
}
void CheckIfOnGround() {
Ray[] rays = new Ray[3];
rays[0] = new Ray(transform.position - Vector3.right * .45f, Vector3.down);
rays[1] = new Ray(transform.position, Vector3.down);
rays[2] = new Ray(transform.position + Vector3.right * .45f, Vector3.down);
RaycastHit hit;
float maxD = .1f;
foreach (Ray ray in rays)
{
if (Physics.Raycast(ray, out hit, maxD))
{
if (hit.collider != null)
{
isOnGround = true;
}
else
{
isOnGround = false;
}
}
}
}
void updateAnim()
{
if ( (Input.GetKey(KeyCode.UpArrow) && Input.GetKey(KeyCode.RightShift)))//(Input.GetKey(KeyCode.UpArrow) && Input.GetKey(KeyCode.LeftShift) ||//temporarily removed until network controls are added. Left keyboard belongs to julie, right keyboard belongs to dot
{
// Updated 2019-11-06 //
anim.SetFloat("Speed_f", runSpeed);
}
else if (Input.GetKey(KeyCode.UpArrow) || Input.GetKey(KeyCode.RightArrow) || Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.DownArrow))
{
// Updated 2019-11-06 //
anim.SetFloat("Speed_f", movementSpeed);
}
else
{
//ELSE idle
anim.SetFloat("Speed_f", 0);
}
}
}
You setup your rigidbody to not rotate your object by locking those rotational axis on the rigidbody component, then if you do need to rotate your player you can manually do it through code. This has been the approach I have successfully used in the past. :)