I'm doing a platformer game with Unity 2D and I wanted to flip the character sprite left when the player move him left, but for some reasons it doesn't work. I tried to make this one script:
transform.rotation = new Vector3(0f, 180f, 0f);
but it didn't work. So then I wrote this:
transform.localScale = new Vector3(-0.35f, 0.35f, 1f); //the player's scale x and y are 0.35 by default
but it didn't work too. Then I found this error message in the console: NullReferenceException: Object reference not set to an instance of an object
UnityEditor.Graphs.Edge.WakeUp () (at C:/buildslave/unity/build/Editor/Graphs/UnityEditor.Graphs/Edge.cs:114).
What should I do? I'm doing this game for a game jam so I need to resolve this problem quickly. Thank you.
EDIT: I noticed that I can flip the sprite in the editor, but that I can't do that using scripts.
From this thread I found, it seems like an old bug from Unity's UnityEditor.Graphs.DLL code.
Try restarting Unity completely.
This bug seems to occur only in the editor, and not after a game is built, so you should be safe.
I haven't worked on any 2D Unity projects in a while but here is a piece of code I used to resolve that issue in the past. Let me know if it helps.
private void FlipSprite()
{
bool playerHasHorizontalSpeed = Mathf.Abs(myRigidBody.velocity.x) > Mathf.Epsilon;
if(playerHasHorizontalSpeed)
{
transform.localScale = new Vector2(Mathf.Sign(myRigidBody.velocity.x), 1f);
}
}
This is a bit old so be sure to let me know how this works. You'll need to finish the rest of the controls, but this should work.
public class SpriteFlipper : MonoBehaviour
{
// variable to hold a reference to our SpriteRenderer component
private SpriteRenderer mySpriteRenderer;
// This function is called just one time by Unity the moment the component loads
private void Awake()
{
// get a reference to the SpriteRenderer component on this gameObject
mySpriteRenderer = GetComponent<SpriteRenderer>();
}
// This function is called by Unity every frame the component is enabled
private void Update()
{
// if the A key was pressed this frame
if(Input.GetKeyDown(KeyCode.A))
{
// if the variable isn't empty (we have a reference to our SpriteRenderer
if(mySpriteRenderer != null)
{
// flip the sprite
mySpriteRenderer.flipX = true;
}
}
}
}
Related
I have a script for that Instantiates 2 game object but when something happens to one of them it also happens to the another one even when the conditions are not met for it. How can I make the script act separately for every Game Object?
GO script:
private Transform target;
public float speed = 2f;
private Animator anim;
public float H2Damage = 5f;
private healthBar Hp;
void Start()
{
target = GameObject.FindGameObjectWithTag("enemy").GetComponent<Transform>();
anim = gameObject.GetComponent<Animator>();
Hp = GameObject.FindGameObjectWithTag("enemy").GetComponentInChildren<healthBar>();
}
void Update()
{
target = GameObject.FindGameObjectWithTag("enemy").GetComponent<Transform>();
if (Hp.Died == true)
{
Hp.Died = false;
anim.SetBool("Hero2Attack", false);
anim.SetBool("Hero2Move", true);
}
if (!target || this.anim.GetCurrentAnimatorStateInfo(0).IsName("Hero2ATTACK"))
return;
transform.position = Vector2.MoveTowards(transform.position, target.position, speed * Time.deltaTime);
anim.SetBool("Hero2Move", true);
}
private void OnTriggerEnter2D(Collider2D col)
{
if (col.gameObject.CompareTag("enemy"))
{
anim.SetBool("Hero2Attack", true);
healthBar hp = col.transform.GetComponent<healthBar>();
hp.GiveDamage(H2Damage);
}
}
I believe they act the same way because they are getting the same GetComponentWithTag(), so they will Get the same objects. You also instantiate the animator, which is the exact same one, so they will do the same things. --> If it was as simple as changing the position by 1 meter per second, they would have different behavior (ie. be at different positions) But.... If you instantiate it, the current position is also instantiated, so you are going to have to get the current postion and set it to what you want, so both of these moving boxes aren't at the exact same position. You will have to do something similar here, you are going to want to either (create different scripts for the objects, or set the script values to different things)
TL;DR: You are copying the exact same script with the exact same components, so they will act similarly.
Fix: The only way to fix this is by setting their values after you instantiate them using GetComponent<>()or changing the script you assign them. (or any components for that matter)
Let me know if you have any problems or questions in the comments! :)
update - Added a video
I've made things like you can see in many tutorials and forums:
created Physic Material I called "No bounce"
set Dynamic Friction = 0 and Static Friction = 0
created a cube and add a Box collider with Material = "No Bounce"
set Mass = 1, Drag = 0 and Angular Drag = 0
Now I add another cube for the ground, made it very large and added a Box collider with Material = "No Bounce"
I have 2 problems:
- when they collide, the cube bounces (whereas it shouldn't with my configuration)
- I've made a script and attached it to the cube, to change the velocity, and set it to 0 when there's a collision:
using UnityEngine;
public class CubeProperties : MonoBehaviour
{
private Rigidbody _rb;
private bool _landing;
private void Start()
{
_rb = GetComponentInParent<Rigidbody>();
}
public void OnCollisionEnter(Collision collision)
{
Debug.Log("Collision");
_landing = true;
}
public void FixedUpdate()
{
if (!_landing) {
return;
}
_rb.velocity = Vector3.zero;
_landing = false;
}
}
So at the first collision, I try to instant stop the cube with _rb.velocity = Vector3.zero;. But changing velocity has no effect, I dont understand why. I've tried with many value to see what happens... but nothing happened.
The only thing I can add, and it's working, is: AddForce() I tried to a negative value, but this doesn't work either.
What did I forgot?
Here's a video I hope this is easy to understand (and I hope I'm allowed to help with a video):
https://youtu.be/I3C1KBmm5yw
Looks like your mixing 2D physics and 3D physics together. If it's a 2D scene, you'll actually want to use the 2D Rigidbody and Box Collider 2D.
If it's a 3D Scene which is what it seems like, then you just want to make sure you're using the normal OnCollisionEnter. As it stands, the OnCollisionEnter2D won't get called in that setup.
Just to help see if things are getting called, a good tip in Unity is the Debug.Log. It'll send a message to the console if it gets fired off.
I have problems accessing the enabled variable in children of my Player GameObject.
-Human holds Rigidbody2D, BoxCollider, PlayerController script and Animator
-body and range_attack_body only hold SpriteRenderer
What I want to do:
I want to change the SpriteRenderer of my Player Object when mouse button is on hold. There are SpriteRenderers in body and range_attack_body. Both GameObjects are part of animations.
body.SpriteRenderer is active and range_attack_body.SpriteRenderer inactive during normal motion.
In my PlayerController script I wrote a routine that will trigger an attack animation when mouse button is on hold. In this routine I wanted to change the enabled states of the SpriteRenderers. However nothing is happening, meaning the varibales are not changing during runtime. I already checked if the GameObjects and SpriteRenderers are accessed correctly during Awake() and I can find both renderers in my SpriteRenderer array using debug messages.
On top of that I checked what happens if I add a SpriteRenderer to my Human GameObject. It will appear in my SpriteRenderer array and I have full access to enabled variable meaning I can change them in my routine. So I figured there might be a conflict with body and range_attack_body due to their SpriteRenderers being part of animations. I added Human.SpriteRenderer to an animation and can still change variables.
I have no clue what is going on, please help. Here is some code:
public class PlayerController2D : PhysicsObject {
public float maxSpeed = 7f;
public float jumpTakeOffSpeed = 7f;
public float posOffset = 1;
protected bool flipSprite = false;
protected bool flipState = true;
private Animator animator;
private SpriteRenderer[] spriteRenderers;
void Awake ()
{
animator = GetComponent<Animator> ();
GameObject human = GameObject.Find("Human");
spriteRenderers = human.GetComponentsInChildren<SpriteRenderer> ();
}
protected override void Attack ()
{
if(Input.GetMouseButton(0))
{
spriteRenderers[0].enabled = false;
spriteRenderers[1].enabled = true;
Debug.LogError("Inhalt:" + spriteRenderers[0].ToString());
Debug.LogError("Inhalt:" + spriteRenderers[1].ToString());
animator.SetBool("attack", true); // boolean to start hold animation
}
else if(!Input.GetMouseButton(0))
{
spriteRenderers[0].enabled = true;
spriteRenderers[1].enabled = false;
animator.SetBool("attack", false);
}
}
}
Your script code is ok, the problem should be elsewhere.
First of all, disable the Animator component and run the script: if enabling/disabling the sprite renderers work, than you must look in the animation clips if you have the Sprite Renderer.Enabled property anywhere, most probably you'll have it - remove it so you can enable/disable the renderers only via script.
If the script still doesn't work, then the problem is elsewhere (another script which is accessing the enabled property of the renderers).
But I'd bet that you're changing the enabled property from the animation clips, which supercedes the script due to how the Unity execution order works (animations are always updated after the scripts and before rendering).
I am making a 2D platform game in Untiy for android and i am having some issues with a section of code i have. When i jump onto a platform the first time i can land onto the platform but when i jump again i fall through the platform. i have it so the box collider is inactive if the player is less then the height of the platform and active when the player is higher then the platform. I thought the box collider was to small and it was just missing the collider so i have tried different sizes of colliders and i have tried adjusting different heights at which it activates. Also when i set the height to low the player does a double jump. So what am i doing wrong?
public class Rock : MonoBehaviour
{
private BoxCollider2D platform;
private PlayerScript player;
public float height;
void Awake() {
player = GameObject.Find("Player").GetComponent<PlayerScript>();
platform = GetComponent<BoxCollider2D>();
}
void Start () {
platform.enabled = false;
}
// Update is called once per frame
void Update () {
if(player.transform.position.y > height){
platform.enabled = true;
} else if(player.transform.position.y < height){
platform.enabled = false;
}
}
}
Could it be that you're not covering the case where player.transform.position.y == height?
I can see that you're checking for greater / smaller than height but not equality. This could lead to unwanted behavior like the one you're describing.
Let me know if this was the problem.
This code actually worked. The issue ended up being in my playerscript where I had added a chunk of code where I was having a bug if you held the jump button down the player would get stuck in the jump animation.
void OnCollisionStay2D(Collision2D target) {
if (target.gameObject.tag == "Ground") {
grounded = true;
anim.SetBool("Jump",false);
}
}
public void Jump () {
if(grounded){
grounded = false;
anim.SetBool("Jump",true);
myBody.velocity = new Vector2(myBody.velocity.x, 0);
myBody.AddForce( new Vector2(0, jumpForce));
}
}
After some troubleshooting and going and removing pieces of code trying to see why I doubled jump I finally came across this piece then when I was trying with the touch controls instead of with the keyboard I noticed I am not actually able to hold the jump button down like you can with a keyboard so I didn't really need this piece of code. So I was my own worst enemy this time.
I am having an issue respawning a prefab after it has been destroyed. I can't seem to get it to respawn back at its original start position after a second of being destroyed. I have created an empty game object and attached the SpawnTargets.cs script to it. I'm not sure of what the best methodology to approach this situation. Another object with a script attached to it does the actual destroy of the prefab. BulletCollisionHandler.cs works fine though. Thanks for any help. Code is below:
SpawnTargets.cs:
using UnityEngine;
using System.Collections;
public class SpawnTargets : MonoBehaviour
{
public GameObject targetCircle;
public GameObject targetSquare;
public GameObject targetStar;
private Vector3 circleSpawnPosition = new Vector3(0.0f, 1.227389f, -7.5f);
private Vector3 squareSpawnPosition = new Vector3(0.0f, 1.027975f, -7.993299f);
private Vector3 starSpawnPosition = new Vector3(0.0f, 1.8f, -7f);
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
SpawnTarget ();
}
void SpawnTarget()
{
}
}
BulletCollisionHandler.cs:
using UnityEngine;
using System.Collections;
public class BulletCollisionHandler : MonoBehaviour
{
public GameObject targetCircle;
// Use this for initialization
void Start ()
{
Destroy (gameObject, 2);
}
// Update is called once per frame
void Update ()
{
}
void OnCollisionEnter(Collision other)
{
if(other.gameObject.name == "TargetSquare")
{
other.gameObject.rigidbody.isKinematic = false;
((TargetMovementHorizontal)other.gameObject.GetComponent<TargetMovementHorizontal>()).enabled = false;
Destroy (other.gameObject, 1);
Debug.Log("Hit square");
}
else if(other.gameObject.name == "TargetCircle")
{
other.gameObject.rigidbody.isKinematic = false;
((TargetMovementHorizontal)other.gameObject.GetComponent<TargetMovementHorizontal>()).enabled = false;
Destroy (other.gameObject, 1);
Debug.Log("Hit circle");
}
else if(other.gameObject.name == "TargetStar")
{
other.gameObject.rigidbody.isKinematic = false;
((TargetMovementHorizontal)other.gameObject.GetComponent<TargetMovementHorizontal>()).enabled = false;
((TargetMovementVertical)other.gameObject.GetComponent<TargetMovementVertical>()).enabled = false;
Destroy (other.gameObject, 1);
Debug.Log("Hit star");
}
}
}
You're not calling Instantiate() anywhere, so it's hard to see where the new object would come from in the code you've supplied.
In any case, it might be better not to use Destroy. If you want to immediately reset the object, why not simply recycle it back to the start position? It's a good idea to avoid instantiating and destroying lots of objects, it's better to hide/disable the ones your don't need and unhide/re-enable them.
Here's a tutorial on the general idea. The tutorial is about groups of objects but the same trick would work for recycling single objects too.
You are better of using gameObject.SetActive( true/false ); for activating / deactivating the gameObject instead of just using Destroy.
Then if you are using Destroy you have 3 options that comes to mind for getting it into the desire position before the Player sees it.
1) You enable the game object after disabling its Renderer component. Then you equalize the transform's position / rotation the one you need. After this you re-enable the Renderer component. It should be placed where you want it.
2) You Instantiate the gameObject, but first making sure the Renderer component is disabled on its Prefab, by default, so you can re-assign its Transform values then - re-enable the Renderer again.
3) You make an invisible gameObject (an Empty gameObject) and Instantiate the wanted gameObject, you then make the Empty to be the parent of the newly created gameObject.. Provided that the parent Empty is exactly where you want it to be, when you instantiate and reset the child's position it should jump off right on top the the Empty parent.
I'm not giving code since you haven't and I don't have no idea of which method you might end up liking more. In terms of performance the Enable/Disable are the best option.
And as theodox says Object Pooling is your best friend for things like bullets, although it might be applied to many other gameObjects that might work as 'collections of objects' on your game's logic. It's totally worth learning.