Unity Value Assigned in Inspector Throws Null in Code - c#

I'm a beginner at Unity with a problem for which I've not been able to find an answer on any of the boards. Creating a very basic Unity C# script, I have the following lines of code in my Awake() function:
Assert.IsNotNull(sfxJump);
Assert.IsNotNull(sfxDeath);
Assert.IsNotNull(sfxCoin);
The third assertion "
Assert.IsNotNull(sfxCoin) throws as null, even though the coin AudioClip is set in the Inspector:
Inspector script values:
However -- and this is the part that has me baffled -- for some reason sfxCoin is not null when invoked in the same script from an OnCollisionEnter() routine
So it appears Unity does register the object with the code -- eventually -- but the assertions fail with the initial Awake(), Start(), and Update() methods.
And this only is happening with sfxCoin. sfxJump and sfxDeath do not have this issue.
Any help would be appreciated
Entire script is below:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
public class Player : MonoBehaviour
{
[SerializeField] private float jumpForce = 100f;
[SerializeField] private float forwardMomentum = 5f;
[SerializeField] private AudioClip sfxJump;
[SerializeField] private AudioClip sfxDeath;
[SerializeField] private AudioClip sfxCoin;
private Animator anim;
private Rigidbody Rigidbody;
private bool jump = false;
private AudioSource audioSource;
private void Awake()
{
Assert.IsNotNull(sfxJump);
Assert.IsNotNull(sfxDeath);
Assert.IsNotNull(sfxCoin);
}
// Start is called before the first frame update
void Start()
{
anim = GetComponent<Animator>();
Rigidbody = GetComponent<Rigidbody>();
audioSource = GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
if (!GameManager.instance.GameOver() && GameManager.instance.GameStarted())
{
if (Input.GetMouseButton(0))
{
GameManager.instance.PlayerStartedGame();
anim.Play("Jump");
audioSource.PlayOneShot(sfxJump);
Rigidbody.useGravity = true;
jump = true;
}
}
}
private void FixedUpdate()
{
if (jump)
{
jump = false;
Rigidbody.velocity = new Vector2(0, 0);
Rigidbody.AddForce(new Vector2(forwardMomentum, jumpForce), ForceMode.Impulse);
}
}
private void OnCollisionEnter(Collision collision)
{
switch (collision.gameObject.tag)
{
case "obstacle":
Rigidbody.AddForce(new Vector2(-50, 20), ForceMode.Impulse);
Rigidbody.detectCollisions = false;
audioSource.PlayOneShot(sfxDeath);
GameManager.instance.PlayerCollided();
break;
case "coin":
audioSource.PlayOneShot(sfxCoin);
GameManager.instance.Score(1);
print("GOT COIN");
break;
}
}
}

sorry, I found out what the problem was.
There was a second instance of a game object that was also using the same script that did not have sfxCoin set. It was hidden under a node in the hierarchy so I didn't see it.
Like I said, I'm a beginner at this.

Related

When I press space, my character doesn't always jump in my Unity2D project

https://www.youtube.com/watch?v=gB1F9G0JXOo&t=11865s
I'm watching this tutorial and I did everything the same as the guy in the video did until the "Player Jumping" part, when his character can jump whenever he presses the space button, but for me it doesn't work the same way. Can somebody help or tell me what is the problem?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
[SerializeField]
private float moveForce = 10f;
[SerializeField]
private float jumpForce = 11f;
private float movementX;
private Rigidbody2D myBody;
private SpriteRenderer sr;
private Animator anim;
private string WALK_ANIMATION = "Walk";
private void Awake()
{
myBody = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
sr = GetComponent<SpriteRenderer>();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
PlayerMoveKeyboard();
AnimatePlayer();
}
private void FixedUpdate()
{
PlayerJump();
}
void PlayerMoveKeyboard()
{
movementX = Input.GetAxisRaw("Horizontal");
transform.position += new Vector3(movementX, 0f, 0f) * moveForce * Time.deltaTime;
}
void AnimatePlayer()
{
// we are going to the right side
if (movementX > 0)
{
anim.SetBool(WALK_ANIMATION, true);
sr.flipX = false;
}
// we are going to the left side
else if (movementX < 0)
{
anim.SetBool(WALK_ANIMATION, true);
sr.flipX = true;
}
else
{
anim.SetBool(WALK_ANIMATION, false);
}
}
void PlayerJump()
{
if (Input.GetButtonDown("Jump"))
{
myBody.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
}
}
} // class
Try moving your jump function from the FixedUpdate into the normal Update Function.
That should fix it.
Update gets called every frame and thus captures every input while FixedUpdate gets called in a fixed offset to update the physics logic of the engine. Now when you are looking for keyboard input, you want to always do that in Update, never in FixedUpdate because you'll miss input that was given to the player between the fixed update frames.
But since you want to do physics related changes only in FixedUpdate, you want to split the code between the two methods.
bool shouldJump = false;
void Update()
{
if (Input.GetButtonDown("Jump"))
shouldJump = true;
}
void FixedUpdate()
{
if (shouldJump)
{
myBody.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
shouldJump = false;
}
}
I have looked into the code in the video and he does the jump-logic in FixedUpdate. It seems weird to me and this is definitely bad practice. But one of the comments below addresses your issue as well and proposes a similar solution.

Issue with animator in Unity 2d

I'm trying to solve an issue in Unity 2d game creation.
So, my issue is when the subject is idle, the 2d player object should be idle and when I move the character, it must animate like walking.
But in my case, the walking animation is not working but the character is moving.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
[SerializeField]
private float jumpmovement = 11f;
[SerializeField]
private float movement = 10f;
private float movementx;
[SerializeField]
private Rigidbody2D mybody;
private Animator anim;
[SerializeField]
private string Walk_Ani = "Player is walking";
private SpriteRenderer sr;
private void Awake()
{
mybody = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
sr = GetComponent<SpriteRenderer>();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Playerkeymove();
animateplayer1();
}
void Playerkeymove()
{
movementx = Input.GetAxisRaw("Horizontal");
// Debug.Log(movementx);
transform.position += new Vector3(movementx, 0f, 0f) * movement * Time.deltaTime;
// Debug.Log(transform.position);
}
void animateplayer1()
{
// anim.SetBool(Walk_Ani , true);
// we are going to the right side
if (movementx > 0)
{
anim.SetBool(Walk_Ani, true);
sr.flipX = false;
}
else if (movementx < 0)
{
// we are going to the left side
anim.SetBool(Walk_Ani, true);
sr.flipX = true;
}
else
{
anim.SetBool(Walk_Ani, false);
}
}
}
I can't understand, why I have a warning issue with boolean part where I guess that's where my animation takes part.
You are trying to set a boolean parameter in the Animator that doesn't exists. The code is correct. Check what name you choose for the bool parameter to active correctly the animation.

Unity Mirror Multiplayer RTS, client ignoring hasAuthority check

I'm working on an RTS game using mirror and ran into an issue.
I'm working on auto-attack function for the units.
Everything works for the host but not on clients.
Any help would be very much appreciated, I've been at it for a few days already.. Help!
Or at least point me in right direction here.
I will optimize this a bit better later on I just need to understand why it's not working on client. I've also noticed that Client will add itself to enemies list, it seems to be ignoring "hasAuthority" check
It's a bit of a write up so I'll try to make it as understandable as possible.
Unit gets instantiated and this is the script attached to it:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;
using System.Linq;
public class UnitFiring : NetworkBehaviour
{
[SerializeField] private Targeter targeter = null;
[SerializeField] private GameObject projectilePrefab = null;
[SerializeField] private Transform projectileSpawnPoint = null;
[SerializeField] private float fireRange = 10f;
[SerializeField] private float fireRate = 1f;
[SerializeField] private float rotationSpeed = 20f;
private float lastFireTime;
//auto attack
[SerializeField] private Transform ownAimAtPoint = null;
[SerializeField] private LayerMask layerMask;
[SerializeField] private int updateFunctionFrequency = 60; //in frames
[ServerCallback]
private void Update()
{
if (Time.frameCount % this.updateFunctionFrequency != 0) return;
//runs update every 60 frames
enemyColliders.RemoveAll(Collider => Collider == null);
//if enemyCollider List has GameObject that was destroyed or null, it removes it from the list.
Targetable target = targeter.GetTarget();
if(target == null)
{
ArrayDetect();
AttackUnit();
return;
}
if (!CanFireAtTarget()) { return; }
//look at target
Quaternion targetRotation =
Quaternion.LookRotation(target.transform.position - transform.position);
//Rotate
transform.rotation = Quaternion.RotateTowards
(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
if(Time.time > (1 / fireRate) +lastFireTime)
{
Quaternion projectileRotation = Quaternion.LookRotation(
target.GetAimAtPoint().position - projectileSpawnPoint.position);
GameObject projectileInstance = Instantiate(
projectilePrefab, projectileSpawnPoint.position, projectileRotation);
NetworkServer.Spawn(projectileInstance, connectionToClient);
lastFireTime = Time.time;
}
}
[Client]
private bool CanFireAtTarget()
{
return (targeter.GetTarget().transform.position - transform.position).sqrMagnitude
<= fireRange * fireRange;
}
[SerializeField] private Collider[] colliderArray;
[SerializeField] private List<GameObject> enemyColliders;
Next is the ArrayDetect Function
Detect unit with Physics.OverlapSphere, make sure it has the authority and add it to enemyCollider List, I also added layerMask to make sure it's only checking for Units.
[Server]
private void ArrayDetect()
{
colliderArray = Physics.OverlapSphere(ownAimAtPoint.position, fireRange, layerMask);
foreach (Collider collider in colliderArray)
{
Debug.Log("we hit a", collider);
if (!collider.TryGetComponent<Targetable>(out Targetable potentialTarget))
{
return;
}
if (potentialTarget.hasAuthority)
{
return; //if the hit target is the players, do nothing
}
else
{
enemyColliders = enemyColliders.Distinct().ToList();
enemyColliders.Add(collider.gameObject);
Debug.Log("Found an enemy", potentialTarget);
}
}
}
Now AttackUnit Function, it will go thought enemyColliders List and set them as target to attack
[ServerCallback]
private void AttackUnit()
{
foreach (GameObject enemy in enemyColliders)
{
Debug.Log("We got confirmed enemy", enemy);
//GetComponent<Targeter>().CmdSetTarget(enemy);
targeter.CmdSetTarget(enemy);
//attack the enemy
}
}
This is Targetable Script, it simply returns AimAtPoint:
public class Targetable : NetworkBehaviour
{
[SerializeField] private Transform aimAtPoint = null;
public Transform GetAimAtPoint()
{
return aimAtPoint;
}
}
This is the CmdSetTarget Command in Targeter Script:
[Command]
public void CmdSetTarget(GameObject targetGameObject)
{
if(!targetGameObject.TryGetComponent<Targetable>(out Targetable newTarget)) { return; }
//if game object does not have a target, return
target = newTarget;
}
This is the error I get in the console, but only on the client Units, the host runs as it should:
Trying to send command for object without authority. Targeter.CmdSetTarget
Thanks for taking time to read this

Instantiated object doesn't contain colliders

I am trying to make a script that Instantiates a ball when called. However the balls RigidBody2D and Collider2D are already in the scene upon build, prior to Instantiation. Why is this happening?
Ball Script:
public class Ball : MonoBehaviour
{
[SerializeField]
private Rigidbody2D rb;
[SerializeField]
private float speed = 10;
void Awake()
{
rb = GetComponent<Rigidbody2D>();
Launch();
this.gameObject.name = "Ball";
BoxCollider boxCollider = gameObject.AddComponent<BoxCollider>();
Rigidbody2D gameObjectsRigidBody = this.gameObject.AddComponent<Rigidbody2D>();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private void Launch()
{
//code used to determine in which direction the ball should go when it spawns
float x = Random.Range(0, 2) == 0 ? -1 : 1;
float y = Random.Range(0, 2) == 0 ? -1 : 1;
rb.velocity = new Vector2(speed * x, speed * y);
}
}
Thanks in advance!
The Awake function will be called even if the program is disabled. Therefore the RigidBody2D and Collider2D were already instantiated and were not updated. You should move the code from Awake to Start and enable the script using the snippet below:
private Scriptname script;
private void Awake()
{
script = GetComponent<ScriptName>();
script.enabled = true;
}
More info on this subject here

Unity Animation doesn't play, error instead

I have this code:
public class BasicMovement : MonoBehaviour {
private Animation anim;
public float jumpPower = 15f;
private Rigidbody2D rb;
bool canJump;
// Use this for initialization
void Start () {
anim = this.gameObject.GetComponent<Animation>();
anim.Play("Run");
rb = gameObject.GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update () {
// FIX LATER for collider, not position
if (rb.position.y <= -2.95) // this is line 23
{
canJump = true;
}
if (canJump)
{
if (Input.GetKeyDown(KeyCode.Space))
{
rb.velocity = new Vector2(rb.velocity.x, jumpPower);
canJump = false;
}
}
}
}
I get this error:
Object reference not set to instance of object: 23
For some reason, if I take anim.Play("Run"); out, then there is no error at all.
Also, I made the RUN and JUMP animations to the gameObject this script is assigned to.
The GameObject which has this script doesn't have the Animation component added to it, giving you null when you're trying to fetch it with anim = this.gameObject.GetComponent<Animation>();
Make sure your Animation component is added to the GameObject and then try again!
I had to use anim = this.gameObject.GetComponent<Animator>(); instead of Animation, and it worked.

Categories

Resources