Forum begins after the advertisement:
[Part 5] nullpointer exception when adding tiles
Home › Forums › Video Game Tutorial Series › Creating a Metroidvania in Unity › [Part 5] nullpointer exception when adding tiles
- This topic has 7 replies, 3 voices, and was last updated 5 months, 2 weeks ago by Ethan.
-
AuthorPosts
-
April 23, 2024 at 8:34 pm #14214EthanParticipant::
been adding some new tiles to make my level look good and when ive gone to add some tiles its thrown a null pointer exception in the player controller code . Ive pasted the code down below but this is what it says in the console
NullReferenceException: Object reference not set to an instance of an object PlayerController.Start () (at Assets/Scripts/PlayerController.cs:131)
using System.Collections; using System.Collections.Generic; using Unity.Mathematics; using Unity.VisualScripting; using UnityEngine; using UnityEngine.UI; public class PlayerController : MonoBehaviour { [Header("Horizontal Movement")] [SerializeField] private float walkSpeed = 20; [Space(5)] [Header("vertical movement")] [SerializeField] private float jumpForce = 35f; private int jumpBufferCounter=0; [SerializeField] private int jumpBufferFrames; private float coyoteTimeCounter = 0; [SerializeField] private float coyoteTime; private int airJumpCounter = 0; [SerializeField] private int maxAirJumps; [Space(5)] [Header("Ground check Settings")] [SerializeField] private Transform groundCheckPoint; [SerializeField] private float groundCheckY = 0.2f; [SerializeField] private float groundCheckX = 0.5f; [SerializeField] private LayerMask whatIsGround; [Space(5)] [Header("dash settings")] [SerializeField] private float dashSpeed; [SerializeField] private float dashTime; [SerializeField] private float dashCooldown; [SerializeField] GameObject dashEffect; [Space(5)] [Header("Attack Settings")] bool attack = false; float timeBetweenAttack, timeSinceAttack; [SerializeField] Transform SideAttackTransform, UpAttackTransform, DownAttackTransform; [SerializeField] Vector2 SideAttackArea, UpAttackArea, DownAttackArea; [SerializeField] LayerMask attackableLayer; [SerializeField] float damage; [SerializeField] GameObject slashEffect; [Space(5)] [Header("Recoil")] [SerializeField] int recoilXSteps = 5; [SerializeField] int recoilYSteps = 5; [SerializeField] float recoilXSpeed = 100; [SerializeField] float recoilYSpeed = 100; int stepsXRecoiled, stepsYRecoiled; [Space(5)] [Header("Health Settings")] public int health; public int maxHealth; [SerializeField] GameObject bloodSpurt; [SerializeField] float hitFlashSpeed; public delegate void OnHealthChangedDelegate(); [HideInInspector] public OnHealthChangedDelegate OnHealthChangedCallback; [Space(5)] bool restoreTime; float restoreTimeSpeed; [Space(5)] [Header("Healing")] float healTimer; [SerializeField] float timeToHeal; [Space(5)] [Header("Mana Settings")] [SerializeField] Image manaStorage; [SerializeField] float mana; [SerializeField] float manaDrainSpeed; [SerializeField] float manaGain; [Space(5)] [Header("Spell Casting")] [SerializeField] float manaSpellCost = 0.3f; [SerializeField] float timeBetweenCast = 0.5f; float timeSinceCast; [SerializeField] float spellDamage;//upspell explosion and downspell fireball [SerializeField] float downSpellForce; //dive only //spell cast objects [SerializeField] GameObject sideSpellFireball; [SerializeField] GameObject upSpellExplosion; [SerializeField] GameObject downSpellFireball; [Space(5)] [HideInInspector] public PlayerStateList pState; private Rigidbody2D rb; private SpriteRenderer sr; private float xAxis,yAxis; private float gravity; Animator anim; private bool canDash = true; private bool dashed; public static PlayerController Instance; private void Awake() { if(Instance != null && Instance != this) { Destroy(gameObject); } else { Instance = this; } DontDestroyOnLoad(gameObject); } // Start is called before the first frame update void Start() { pState = GetComponent<PlayerStateList>(); rb = GetComponent<Rigidbody2D>(); sr = GetComponent<SpriteRenderer>(); anim = GetComponent<Animator>(); gravity = rb.gravityScale; Mana = mana; manaStorage.fillAmount = Mana; Health = maxHealth; } private void OnDrawGizmos() { Gizmos.color = Color.yellow; Gizmos.DrawWireCube(SideAttackTransform.position,SideAttackArea); Gizmos.DrawWireCube(UpAttackTransform.position,UpAttackArea); Gizmos.DrawWireCube(DownAttackTransform.position,DownAttackArea); } // Update is called once per frame void Update() { GetInputs(); UpdateJumpVariables(); if (pState.dashing) return; Flip(); Move(); Jump(); StartDash(); Attack(); RestoreTimeScale(); FlashWhileInvincible(); Heal(); CastSpell(); } private void OnTriggerEnter2D(Collider2D _other) { if (_other.GetComponent<Enemy>() != null && pState.casting) { _other.GetComponent<Enemy>().EnemyHit(spellDamage, (_other.transform.position - transform.position).normalized, -recoilYSpeed); } } private void FixedUpdate() { if(pState.dashing) return; Recoil(); } void GetInputs() { xAxis = Input.GetAxisRaw("Horizontal"); yAxis = Input.GetAxisRaw("Vertical"); attack = Input.GetButtonDown("Attack"); } void Flip() { if(xAxis < 0) { transform.localScale = new Vector2(-1, transform.localScale.y); pState.lookingRight = false; } else if(xAxis > 0) { transform.localScale = new Vector2(1, transform.localScale.y); pState.lookingRight = true; } } private void Move() { rb.velocity = new Vector2(walkSpeed * xAxis, rb.velocity.y); anim.SetBool("Walking", rb.velocity.x != 0 && Grounded()); } void StartDash() { if(Input.GetButtonDown("Dash")&& canDash && !dashed) { StartCoroutine(Dash()); dashed = true; } if(Grounded()) { dashed = false; } } IEnumerator Dash() { canDash = false; pState.dashing = true; anim.SetTrigger("Dashing"); rb.gravityScale = 0; rb.velocity = new Vector2(transform.localScale.x * dashSpeed, 0); if (Grounded()) Instantiate(dashEffect, transform); yield return new WaitForSeconds(dashTime); rb.gravityScale = gravity; pState.dashing = false; yield return new WaitForSeconds(dashCooldown); canDash = true; } void Attack() { timeSinceAttack += Time.deltaTime; if(attack && timeSinceAttack >= timeBetweenAttack) { timeSinceAttack = 0; anim.SetTrigger("Attacking"); if(yAxis ==0 || yAxis <0 && Grounded()) { Hit(SideAttackTransform, SideAttackArea, ref pState.recoilingX,recoilXSpeed); Instantiate(slashEffect, SideAttackTransform); } else if(yAxis>0) { Hit(UpAttackTransform, UpAttackArea, ref pState.recoilingY, recoilYSpeed); SlashEffectAtAngle(slashEffect, 90, UpAttackTransform); } else if (yAxis < 0 && !Grounded()) { Hit(DownAttackTransform, DownAttackArea, ref pState.recoilingY, recoilYSpeed); SlashEffectAtAngle(slashEffect,-90,DownAttackTransform); } } } void Hit(Transform _attackTransform, Vector2 _attackArea, ref bool _recoilDir, float _recoilStrength) { Collider2D[] objectsToHit = Physics2D.OverlapBoxAll(_attackTransform.position, _attackArea, 0, attackableLayer); if(objectsToHit.Length > 0 ) { _recoilDir = true; } for(int i=0; i<objectsToHit.Length; i++) { if (objectsToHit[i].GetComponent<Enemy>() != null) { objectsToHit[i].GetComponent<Enemy>().EnemyHit(damage,(transform.position- objectsToHit[i].transform.position).normalized,_recoilStrength); if (objectsToHit[i].CompareTag("Enemy")) { Mana += manaGain; } } } } void SlashEffectAtAngle(GameObject _slashEffect, int _effectAngle,Transform _attackTransform) { _slashEffect = Instantiate(_slashEffect, _attackTransform); _slashEffect.transform.eulerAngles = new Vector3(0, 0, _effectAngle); _slashEffect.transform.localScale = new Vector3(transform.localScale.x, transform.localScale.y); } void Recoil() { if(pState.recoilingX) { if(pState.lookingRight) { rb.velocity = new Vector2(-recoilXSpeed, 0); } else { rb.velocity = new Vector2(recoilXSpeed, 0); } } if(pState.recoilingY) { rb.gravityScale = 0; if (yAxis < 0) { rb.velocity = new Vector2(rb.velocity.x, recoilYSpeed); } else { rb.velocity=new Vector2(rb.velocity.x, -recoilYSpeed); } airJumpCounter = 0; } else { rb.gravityScale = gravity; } //stop recoil if(pState.recoilingX && stepsXRecoiled < recoilXSteps) { stepsXRecoiled++; } else { StopRecoilX(); } if(pState.recoilingY && stepsYRecoiled < recoilYSteps) { stepsYRecoiled++; } else { StopRecoilY(); } if(Grounded()) { StopRecoilY(); } } void StopRecoilX() { stepsXRecoiled = 0; pState.recoilingX = false; } void StopRecoilY() { stepsYRecoiled = 0; pState.recoilingY = false; } public void TakeDamage(float _damage) { Health -= Mathf.RoundToInt(_damage); StartCoroutine(StopTakingDamage()); } IEnumerator StopTakingDamage() { pState.invincible = true; GameObject _bloodSpurtParticles = Instantiate(bloodSpurt, transform.position, quaternion.identity); Destroy(_bloodSpurtParticles,1.5f); anim.SetTrigger("TakeDamage"); yield return new WaitForSeconds(1f); pState.invincible=false; } void FlashWhileInvincible() { sr.material.color = pState.invincible ? Color.Lerp(Color.white, Color.black, Mathf.PingPong(Time.time * hitFlashSpeed, 1.0f)): Color.white; } void RestoreTimeScale() { if(restoreTime) { if(Time.timeScale <1) { Time.timeScale += Time.deltaTime * restoreTimeSpeed; } else { Time.timeScale = 1; restoreTime = false; } } } public void HitStopTime(float _newTimeScale, int _restoreSpeed, float _delay) { restoreTimeSpeed = _restoreSpeed; Time.timeScale = _newTimeScale; if(_delay > 0) { StopCoroutine(StartTimeAgain(_delay)); StartCoroutine(StartTimeAgain(_delay)); } else { restoreTime = true; } } IEnumerator StartTimeAgain(float _delay) { restoreTime = true; yield return new WaitForSeconds(_delay); } public int Health { get { return health; } set { if(health != value) { health = Mathf.Clamp(value, 0, maxHealth); if(OnHealthChangedCallback != null) { OnHealthChangedCallback.Invoke(); } } } } void Heal() { if(Input.GetButton("Healing")&& Health < maxHealth && Mana >0 && !pState.jumping && !pState.dashing) { pState.healing = true; anim.SetBool("Healing", true); //healing healTimer += Time.deltaTime; if(healTimer >= timeToHeal) { Health++; healTimer = 0; } Mana -= Time.deltaTime * manaDrainSpeed; } else { pState.healing= false; anim.SetBool("Healing", false); healTimer = 0; } } float Mana { get { return mana; } set { //if mana stats change if (mana != value) { mana = Mathf.Clamp(value, 0, 1); manaStorage.fillAmount = Mana; } } } void CastSpell() { if(Input.GetButton("CastSpell")&& timeSinceCast >= timeBetweenCast && Mana >= manaSpellCost) { pState.casting = true; timeSinceCast = 0; StartCoroutine(CastCoroutine()); } else { timeSinceCast += Time.deltaTime; } if(Grounded()) { //disable downspell if on the ground downSpellFireball.SetActive(false); } //if down spell is active,force player downwards till it reaches a grounded layer if(downSpellFireball.activeInHierarchy) { rb.velocity += downSpellForce * Vector2.down; } } IEnumerator CastCoroutine() { anim.SetBool("Casting", true); yield return new WaitForSeconds(0.15f); //side cast if(yAxis == 0 || (yAxis <0 && Grounded())) { GameObject _fireBall= Instantiate(sideSpellFireball, SideAttackTransform.position,Quaternion.identity); //flip fireball if(pState.lookingRight) { _fireBall.transform.eulerAngles = Vector3.zero; } else { _fireBall.transform.eulerAngles = new Vector2(_fireBall.transform.eulerAngles.x, 180);//if not looking right rotate the fireball sprite } pState.recoilingX = true; } //up cast else if(yAxis >0) { Instantiate(upSpellExplosion, transform); rb.velocity = Vector2.zero; } //down cast else if(yAxis <0 && !Grounded()) { downSpellFireball.SetActive(true); } Mana -= manaSpellCost; yield return new WaitForSeconds(0.35f); anim.SetBool("Casting", false); pState.casting = false; } public bool Grounded() { if (Physics2D.Raycast(groundCheckPoint.position, Vector2.down, groundCheckY, whatIsGround) ||Physics2D.Raycast(groundCheckPoint.position+new Vector3(groundCheckX,0,0),Vector2.down,groundCheckY,whatIsGround) ||Physics2D.Raycast(groundCheckPoint.position+ new Vector3(-groundCheckX,0,0),Vector2.down,groundCheckY,whatIsGround)) { return true; } else { return false; } } void Jump() { if (Input.GetButtonUp("Jump") && rb.velocity.y > 0) { rb.velocity = new Vector2(rb.velocity.x, 0); pState.jumping = false; } if (!pState.jumping) { if (jumpBufferCounter >0 && coyoteTimeCounter>0) { rb.velocity = new Vector3(rb.velocity.x, jumpForce); pState.jumping = true; } else if(!Grounded() && airJumpCounter < maxAirJumps && Input.GetButtonDown("Jump")) { pState.jumping = true; airJumpCounter++; rb.velocity = new Vector3(rb.velocity.x, jumpForce); } } anim.SetBool("Jumping", !Grounded()); } void UpdateJumpVariables() { if(Grounded()) { pState.jumping = false; coyoteTimeCounter = coyoteTime; airJumpCounter = 0; } else { coyoteTimeCounter -= Time.deltaTime; } if(Input.GetButtonDown("Jump")) { jumpBufferCounter = jumpBufferFrames; } else { jumpBufferCounter--; } } }
April 23, 2024 at 8:49 pm #14217TerenceKeymaster::Line 131 seems to be here:
// Start is called before the first frame update void Start() { pState = GetComponent
(); rb = GetComponent (); sr = GetComponent (); anim = GetComponent (); gravity = rb.gravityScale; Mana = mana; manaStorage.fillAmount = Mana; Health = maxHealth; } It looks like your
manaStorage
variable is not assigned, hence the line becomesnull.fillAmount
and causes the Null Reference Exception.April 23, 2024 at 9:21 pm #14219EthanParticipantApril 23, 2024 at 10:24 pm #14224April 23, 2024 at 10:24 pm #14225April 23, 2024 at 10:34 pm #14226EthanParticipant::i have the mana storage set on my player controller and its still returning null do you need me to send a video or is there something else that i have missed
April 24, 2024 at 12:42 am #14232Joseph TangModerator::Yes, could you send a video of you outside and in the game, with the inspector of the PlayerController.cs, Canvas and the Mana game object?
April 24, 2024 at 4:07 am #14234 -
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: