Forum begins after the advertisement:
[Part 7] SaveData
Home › Forums › Video Game Tutorial Series › Creating a Metroidvania in Unity › [Part 7] SaveData
- This topic has 11 replies, 2 voices, and was last updated 7 months, 1 week ago by Joseph Tang.
-
AuthorPosts
-
May 9, 2024 at 10:57 pm #14581::
The save system not working when i build the game. Whenever i play the game and quit. The game load me back to first scene
May 10, 2024 at 11:47 am #14583::Do you mean that when you play the game, you don’t go to the Main Menu or, after starting from the Main Menu you don’t start at your first level?
If you could show a video of how it looks that would help.
Could you also send your SaveData.cs and PlayerController.cs?
May 10, 2024 at 1:01 pm #14584::i mean when i export to the game to desktop (the exe file). I run the game and the data wouldnot save. it does work when i play it in the editor.
May 10, 2024 at 1:27 pm #14585::I understand your issue is in the build, however the code in the editor works the same for the build itself. Thus, I’d like to see how the issue is going on your side of things with a video, and Scripts if it’s a coding issue. There’s also a likelihood it is neither and it leads me to this:
Without the video and scripts I can only guess the causes:
- Your Bench/Save point is not reliable for interaction and thus the code for interaction can be improved.
- You are quiting the game by the pause menu which does not have a code for saving the player’s data. Thus entering the game again brings you to your last save point [death or bench].
- Your PlayerController.cs is not calling
SaveData.Instance.LoadPlayerData();
- If your health is being saved, but not the scene, your SaveData.cs does not include a code for
lastScene
at least for loading player data.
May 10, 2024 at 2:19 pm #14593::Oh sorry its acttualy my fault that i commented out the LoadSaveData line of code in the playerController Script. However there is a bug i just found out that i cant not hit the shade. It works fine in the editor
May 10, 2024 at 2:20 pm #14594::PlayerController
<code>using System.Collections; using System.Collections.Generic; // using System.Numerics; using System.Security.Cryptography; using TMPro; using Unity.Mathematics; // using Microsoft.Unity.VisualStudio.Editor; // using System.Numerics; using Unity.VisualScripting; using UnityEngine; using UnityEngine.UI; public class PlayerController : MonoBehaviour { public Rigidbody2D rb; [Header("Horizontal Movement Settings")] [SerializeField] private float walkSpeed = 1f; [Space(5)] [Header("Vertical Movement Settings")] [SerializeField] private float jumptForce = 45f; // [SerializeField] private float jumpDistance = 5f; private int jumpBufferCounter = 0; //* How long the input buffer will be [SerializeField] private int jumpBufferFrames; //* CoyoteTime give the player a brief amount of time to jump after leaving a platform (fall off a platform, run off a legde) private float coyoteTimeCounter = 0; [SerializeField] private float coyoteTime; //* How many time you jump while in the air 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")] private bool canDash = true; private bool dashed; [SerializeField] private float dashSpeed; [SerializeField] private float dashTime; [SerializeField] private float dashCooldown; [SerializeField] GameObject dashEffect; [Space(5)] [Header("Attack Settings")] private bool attack = true; float timeSinceAttack; [SerializeField] float timeBetweenAttack; [SerializeField] Transform sideAttackTransform, upAttackTransform, downAttackTransform; [SerializeField] Vector2 SideAttackArea, UpAttackArea, DownAttackArea; [SerializeField] LayerMask attackableLayer; [SerializeField] public int damage; [SerializeField] GameObject slashEffect; [Space(5)] [Header("Recoiling Settings")] //* How far the player will recoil [SerializeField] int recoilXSteps = 5; [SerializeField] int recoilYSteps = 5; [SerializeField] float recoilXSpeed = 100; [SerializeField] float recoilYSpeed = 100; //* Stop the player from recoilling forever int stepsXRecoiled, stepsYRecoiled; [Space(5)] [Header("Health Setting")] public int health; public int maxHealth; public int maxTotalHealth = 8; public int heartShards; [SerializeField] GameObject bloodSpurt; [SerializeField] float hitFlashSpeed; //* Delegate used to call multiple method public delegate void OnHealthChangeDelegate(); [HideInInspector] public OnHealthChangeDelegate onHealthChangeCallBack; float healTimer; [SerializeField] float timeToHeal; [Space(5)] [Header("Mana Settings")] [SerializeField] Image manaStorage; [SerializeField] float mana; [SerializeField] float manaDrainSpeed; [SerializeField] float manaGain; public bool halfMana; // public ManaOrbsHandle manaOrbsHandle; // public int orbShard; // public int manaOrbs; [Space(5)] [Header("Spell Settings")] [SerializeField] float manaSpellCost = 0.3f; [SerializeField] float timeBetweenCast = 0.5f; float timeSinceCast; //used for upspell and downspell [SerializeField] int spellDamage; [SerializeField] float downSpellForce; [SerializeField] GameObject sideSpellFireball; [SerializeField] GameObject upSpellFireball; [SerializeField] GameObject downSpellFireball; [Space(5)] [Header("Camera Settings")] [SerializeField] private float playerFallSpeedThreshold = -10; [Space(5)] [Header("Audio Settings")] [SerializeField] AudioClip landingSound; [SerializeField] AudioClip jumpSound; [SerializeField] AudioClip dashSound; [SerializeField] AudioClip attackSound; [SerializeField] AudioClip hurtSound; [SerializeField] AudioClip spellSound; [Space(5)] [Header("Wall Jump Settings")] [SerializeField] private float wallSlidingSpeed = 2f; [SerializeField] private Transform wallCheck; [SerializeField] private LayerMask wallLayer; [SerializeField] private float wallJumpingDuration; [SerializeField] private Vector2 wallJumpingPower; float wallJumpingDirection; bool isWallSliding; bool isWallJumping; [Space(5)] [Header("Unlocking Stuffs")] //*unlocking abilities public bool unlockedWallJump; public bool unlockedDash; public bool unlockedDoubleJump; public bool unlockedSideSpell; public bool unlockedUpCast; public bool unlockedDownCast; public bool purchasedHeartShard1; public bool purchasedHeartShard2; public bool purchasedMagicSpell; public bool purchasedUpgradeSword1; public bool purchasedUpgradeSword2; [Space(5)] [Header("Money")] public int money; [SerializeField] private TextMeshProUGUI moneyText; [Space(5)] bool restoreTime; float restoreTimeSpeed; Animator anim; private float gravity; private SpriteRenderer sr; private AudioSource audioSource; // Input private float xAxis, yAxis; bool openMap; bool openInventory; private bool landingSoundPlayed; [HideInInspector] public PlayerStateList pState; public static PlayerController Instance; //*Destroy duplicated object that is not player private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); } else { Instance = this; } DontDestroyOnLoad(gameObject); } void Start() { pState = GetComponent<PlayerStateList>(); sr = GetComponent<SpriteRenderer>(); rb = GetComponent<Rigidbody2D>(); anim = GetComponent<Animator>(); audioSource = GetComponent<AudioSource>(); // manaOrbsHandle = FindObjectOfType<ManaOrbsHandle>(); SaveData.Instance.LoadPlayerData(); gravity = rb.gravityScale; Mana = mana; manaStorage.fillAmount = Mana; Health = maxHealth; } //* See the attack area in the scene private void OnDrawGizmos() { Gizmos.color = Color.blue; Gizmos.DrawWireCube(sideAttackTransform.position, SideAttackArea); Gizmos.DrawWireCube(upAttackTransform.position, UpAttackArea); Gizmos.DrawWireCube(downAttackTransform.position, DownAttackArea); Vector3 _ledgeCheckStart = transform.localScale.x > 0 ? new Vector3(groundCheckX, 0, 0) : new Vector3(-groundCheckX, 0, 0); Gizmos.color = Color.red; Gizmos.DrawRay(groundCheckPoint.transform.position + _ledgeCheckStart, Vector2.down * groundCheckY); } // private void OnDrawGizmosSelected() { // Vector3 _ledgeCheckStart = transform.localScale.x > 0 ? new Vector3(groundCheckX, 0) : new Vector3(-groundCheckX, 0); // Gizmos.color = Color.blue; // Gizmos.DrawRay(transform.position + _ledgeCheckStart, Vector2.down * groundCheckY); // } void Update() { if (GameManager.Instance.gameIsPause) return; if (pState.cutscene) return; if(pState.alive) { GetInputs(); ToggleMap(); ToggleInventory(); } UpdateJumpVariables(); RestoreTimeScale(); UpdateCameraYDampForPlayerFall(); //* keep dashing from being interupted by other actions if (pState.dashing) return; if(pState.alive) { if(!isWallJumping) { Flip(); Move(); Jump(); } if(unlockedWallJump) { WallSide(); WallJump(); } if(unlockedDash) { StartDash(); } Attack(); Heal(); CastSpell(); } FlashWhileInvincible(); if(Input.GetKeyDown(KeyCode.L)) { StartCoroutine(Death()); } moneyText.text = money.ToString(); } private void FixedUpdate() { if (pState.cutscene) return; if (pState.dashing) return; Recoil(); } private void OnTriggerEnter2D(Collider2D _other) { // for up and down spell cast if(_other.GetComponent<Enemy>() != null && pState.casting) { _other.GetComponent<Enemy>().EnemyHit(spellDamage, (_other.transform.position - transform.position).normalized, -recoilYSpeed); } } void ToggleMap() { if(openMap) { UIManager.Instance.mapHandler.SetActive(true); } else { UIManager.Instance.mapHandler.SetActive(false); } } void ToggleInventory() { if(openInventory) { UIManager.Instance.inventory.SetActive(true); } else { UIManager.Instance.inventory.SetActive(false); } } void GetInputs() { xAxis = Input.GetAxisRaw("Horizontal"); yAxis = Input.GetAxisRaw("Vertical"); openMap = Input.GetButton("Map"); openInventory = Input.GetButton("Inventory"); } 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); //* Set walking animator to true only when the player is walking and on the ground anim.SetBool("Walking", rb.velocity.x != 0 && Grounded()); } void UpdateCameraYDampForPlayerFall() { //if falling past a certain speed threshold if(rb.velocity.y < playerFallSpeedThreshold && !CamerasManager.Instance.isLerpingYDamping && !CamerasManager.Instance.hasLerpedYDamping) { //reset camera function StartCoroutine(CamerasManager.Instance.LerpYDamping(true)); } //if standing still or moving up if(rb.velocity.y >= 0 && !CamerasManager.Instance.isLerpingYDamping && CamerasManager.Instance.hasLerpedYDamping) { //reset Camera function CamerasManager.Instance.hasLerpedYDamping = false; StartCoroutine(CamerasManager.Instance.LerpYDamping(false)); } } 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"); audioSource.PlayOneShot(dashSound); //*Player will dash forward without falling rb.gravityScale = 0; int _dir = pState.lookingRight ? 1 : -1; rb.velocity = new Vector2(_dir * dashSpeed, 0); // if (Grounded()) Instantiate(dashEffect, transform); //* wait for dashtime in seconds to keep dashing in dashTime yield return new WaitForSeconds(dashTime); rb.gravityScale = gravity; pState.dashing = false; //* Wait for seconds to dash again yield return new WaitForSeconds(dashCooldown); canDash = true; } public IEnumerator WalkIntoNewScene(Vector2 _exitDir, float _delay) { //if exit direction is upwards if (_exitDir.y > 0) { rb.velocity = jumptForce * _exitDir; } //if exit direction is forward if (_exitDir.x != 0) { xAxis = _exitDir.x > 0 ? 1 : -1; Move(); } Flip(); yield return new WaitForSeconds(_delay); pState.cutscene = false; } void Heal() { //* Heal when the health is not max and the player not jumping or dashing if (Input.GetButton("Heal") && 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; } //drain mana // manaOrbsHandle.usedMana = true; // manaOrbsHandle.countDown = 3f; Mana -= Time.deltaTime * manaDrainSpeed; } else { pState.healing = false; anim.SetBool("Healing", false); healTimer = 0; } } //*Check if the player is standing on the ground public float Mana { get { return mana; } set { // if mana statics change if (mana != value) { if(!halfMana) { mana = Mathf.Clamp(value, 0, 1); } else { mana = Mathf.Clamp(value, 0, 0.5f); } manaStorage.fillAmount = Mana; } } } void CastSpell() { if (Input.GetButtonDown("CastSpell") && timeSinceCast >= timeBetweenCast && Mana >= manaSpellCost) { pState.casting = true; timeSinceCast = 0; StartCoroutine(CastCoroutine()); } else { timeSinceCast += Time.deltaTime; // pState.casting = false; } if(Grounded()) { //disable downspell if on the ground downSpellFireball.SetActive(false); } //if downspell is active force player down until grounded if(downSpellFireball.activeInHierarchy) { rb.velocity += downSpellForce * Vector2.down; } } IEnumerator CastCoroutine() { audioSource.PlayOneShot(spellSound); // anim.SetBool("Casting", true); // yield return new WaitForSeconds(0.15f); //Side cast if ((yAxis == 0 || (yAxis < 0 && Grounded())) && unlockedSideSpell ) { print("Side"); anim.SetBool("Casting", true); yield return new WaitForSeconds(0.15f); 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 facing right, rotate the fireball 180 deg } pState.recoilingX = true; Mana -= manaSpellCost; // manaOrbsHandle.usedMana = true; // manaOrbsHandle.countDown = 3f; yield return new WaitForSeconds(0.35f); anim.SetBool("Casting", false); pState.casting = false; } //up cast else if( yAxis >0 && unlockedUpCast) { print("Up"); anim.SetBool("Casting", true); yield return new WaitForSeconds(0.15f); Instantiate(upSpellFireball, transform); rb.velocity = Vector2.zero; Mana -= manaSpellCost; // manaOrbsHandle.usedMana = true; // manaOrbsHandle.countDown = 3f; yield return new WaitForSeconds(0.35f); anim.SetBool("Casting", false); pState.casting = false; } //downCast else if(yAxis < 0 && !Grounded() && unlockedDownCast) { print("down"); anim.SetBool("Casting", true); yield return new WaitForSeconds(0.15f); downSpellFireball.SetActive(true); Mana -= manaSpellCost; // manaOrbsHandle.usedMana = true; // manaOrbsHandle.countDown = 3f; 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 StopRecoilX() { stepsXRecoiled = 0; pState.recoilingX = false; } void StopRecoilY() { stepsYRecoiled = 0; pState.recoilingY = false; } public void TakeDamage(float _damage) { if(pState.alive) { audioSource.PlayOneShot(hurtSound); Health -= Mathf.RoundToInt(_damage); StartCoroutine(StopTakingDamage()); if(Health <= 0) { Health = 0; StartCoroutine(Death()); } else { 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.unscaledDeltaTime * restoreTimeSpeed; } else { Time.timeScale = 1; restoreTime = false; } } } public void HitStopTime(float _newTimeScale, int _restoreSpeed, float _delay) { restoreTimeSpeed = _restoreSpeed; Time.timeScale = _newTimeScale; //* if we have been attacked if(_delay > 0){ StopCoroutine(StartTimeAgain(_delay)); StartCoroutine(StartTimeAgain(_delay)); } else { restoreTime = true; } } IEnumerator Death() { pState.alive = false; Time.timeScale = 1f; GameObject _bloodSpurtParticles = Instantiate(bloodSpurt, transform.position, Quaternion.identity); Destroy(_bloodSpurtParticles, 1.5f); anim.SetTrigger("Death"); yield return new WaitForSeconds(0.9f); StartCoroutine(UIManager.Instance.ActivateDeathScreen()); yield return new WaitForSeconds(0.9f); Instantiate(GameManager.Instance.shade, transform.position, quaternion.identity); } public void Respawned() { if(!pState.alive) { pState.alive = true; halfMana = true; UIManager.Instance.SwitchMana(UIManager.ManaState.HalfMana); Mana = 0; Health = maxHealth; anim.Play("Player_Idle"); } } public void RestoreMana() { halfMana = false; UIManager.Instance.SwitchMana(UIManager.ManaState.FullMana); } IEnumerator StartTimeAgain(float _delay) { yield return new WaitForSecondsRealtime(_delay); restoreTime = true; } public int Health { get { return health; } set { if(health != value) { health = Mathf.Clamp(value, 0, maxHealth); if(onHealthChangeCallBack != null) { onHealthChangeCallBack.Invoke(); } } } } void Attack() { timeSinceAttack += Time.deltaTime; if(Input.GetButtonDown("Attack") && attack && timeSinceAttack >= timeBetweenAttack) { timeSinceAttack = 0; anim.SetTrigger("Attacking"); audioSource.PlayOneShot(attackSound); //* The player is not holding down or up if(yAxis == 0 || yAxis < 0 && Grounded()) { int _recoilLeftOrRight = pState.lookingRight ? 1 : -1; Hit(sideAttackTransform, SideAttackArea, ref pState.recoilingX,Vector2.right * _recoilLeftOrRight, recoilXSpeed); SlashEffectSide(slashEffect,0, sideAttackTransform); } else if(yAxis > 0) { Hit(upAttackTransform, UpAttackArea, ref pState.recoilingY,Vector2.up, recoilYSpeed); SlashEffectAtAngle(slashEffect, 90, upAttackTransform); } else if(yAxis < 0 && !Grounded()) { Hit(downAttackTransform, DownAttackArea,ref pState.recoilingY,Vector2.down, recoilYSpeed); SlashEffectAtAngle(slashEffect, -90, downAttackTransform); } } } private void Hit(Transform _attackTransform, Vector2 _attackArea, ref bool _recoilBool,Vector2 _recoilDir, float _recoilStrength) { Collider2D[] objectsToHit = Physics2D.OverlapBoxAll(_attackTransform.position, _attackArea, 0, attackableLayer); if(objectsToHit.Length > 0) { _recoilBool = true; } for(int i= 0; i < objectsToHit.Length; i++) { if(objectsToHit[i].GetComponent<Enemy>() != null) { objectsToHit[i].GetComponent<Enemy>().EnemyHit(damage, _recoilDir, _recoilStrength ); if(objectsToHit[i].CompareTag("Enemy") || objectsToHit[i].CompareTag("GruzMother")) { if(Mana < 1) { Mana += manaGain; // manaOrbsHandle.usedMana = true; // manaOrbsHandle.countDown = 3f; } else { // manaOrbsHandle.UpdateMana(manaGain * 3); } } } } } void SlashEffectAtAngle(GameObject _slashEffect, int _effectAngle, Transform _attackTransform) { _slashEffect = Instantiate(_slashEffect, _attackTransform); _slashEffect.transform.eulerAngles = new Vector3(0, 0, _effectAngle); //*Stretch the effect to fit the area _slashEffect.transform.localScale = new Vector2(transform.localScale.x, transform.localScale.y); } void SlashEffectSide(GameObject _slashEffect, int _effectAngle, Transform _attackTransform) { _slashEffect = Instantiate(_slashEffect, _attackTransform); _slashEffect.transform.eulerAngles = new Vector3(0, 0, _effectAngle); //*Stretch the effect to fit the area if(pState.lookingRight) { _slashEffect.transform.localScale = new Vector2(transform.localScale.x, transform.localScale.y); } else { _slashEffect.transform.localScale = new Vector2(-transform.localScale.x, transform.localScale.y); } } void Recoil() { // if(pState.recoilingX) { // if(pState.lookingRight) { // rb.velocity = new Vector2(-recoilXSpeed, 0); // } else { // //*recoil left if they are facing right; // rb.velocity = new Vector2(recoilXSpeed, 0); // } // } if(pState.recoilingY) { // print("RecoilY is running"); //*if player is pressing Down, the player is attacking downward rb.gravityScale = 0; if(yAxis < 0) { //* gravity wont effect recoiling rb.velocity = new Vector2(rb.velocity.x, recoilYSpeed); // } else { // rb.velocity = new Vector2(rb.velocity.x, -recoilYSpeed); } airJumpCounter = 0; } else { // print("RecoilY is not running"); rb.gravityScale = gravity; } //* StopRecoiLX //* if we havent recoiled full distanced yet we continue to recoil until we do if(pState.recoilingX && stepsXRecoiled < recoilXSteps) { stepsXRecoiled++; } else { StopRecoilX(); } if(pState.recoilingY && stepsYRecoiled < recoilYSteps) { stepsYRecoiled++; } else { StopRecoilY(); } if(Grounded() ) { StopRecoilY(); } } void Jump() { //! vector3 //* The player has limited amount of frame to input the jump input before the jumpbuffer reach 0 if(jumpBufferCounter > 0 && coyoteTimeCounter > 0 && !pState.jumping) { audioSource.PlayOneShot(jumpSound); rb.velocity = new Vector3(rb.velocity.x, jumptForce); pState.jumping = true; } //* The player will jump in the air only when press the jump button, not in the air and not exceed the maxium of airjump allowed if(!Grounded() && airJumpCounter < maxAirJumps && Input.GetButtonDown("Jump") && unlockedDoubleJump) { audioSource.PlayOneShot(jumpSound); pState.jumping = true; airJumpCounter++; rb.velocity = new Vector3(rb.velocity.x, jumptForce); } //* cancel the jump when release the jump button allow the player to control their jump height known as: variable jump height; if(Input.GetButtonUp("Jump") && rb.velocity.y > 3) { pState.jumping = false; rb.velocity = new Vector2(rb.velocity.x, 0); } //* Creating jumping buffer: allow the player's input to be registered and stored for a brief period of time or a number of frame even if the input was executed slightly before the intened time anim.SetBool("Jumping", !Grounded()); } void UpdateJumpVariables() { //* when the player is on the ground reset ... if (Grounded()) { if(!landingSoundPlayed) { audioSource.PlayOneShot(landingSound); landingSoundPlayed = true; } pState.jumping = false; coyoteTimeCounter = coyoteTime; airJumpCounter = 0; } else { //*Time.deltaTime is the amount of time between each frame coyoteTimeCounter -= Time.deltaTime; landingSoundPlayed = false; } if(Input.GetButtonDown("Jump")) { jumpBufferCounter = jumpBufferFrames; } else { jumpBufferCounter--; } } private bool Walled() { return Physics2D.OverlapCircle(wallCheck.position, 0.2f, wallLayer); } void WallSide() { Debug.Log("Wall Slide is functioning"); if(Walled() && !Grounded() && xAxis != 0) { isWallSliding = true; Debug.Log("Wall Slide is true"); rb.velocity = new Vector2(rb.velocity.x, Mathf.Clamp(rb.velocity.y, -wallSlidingSpeed, float.MaxValue)); } else { isWallSliding = false; } } void WallJump() { Debug.Log("Wall Jump is functioning"); if(isWallSliding) { isWallJumping = false; wallJumpingDirection = !pState.lookingRight ? 1 : -1; CancelInvoke(nameof(StopWallJumping)); } if(Input.GetButtonDown("Jump") && isWallSliding) { isWallJumping = true; rb.velocity = new Vector2(wallJumpingDirection * wallJumpingPower.x, wallJumpingPower.y); dashed = false; airJumpCounter = 0; if((pState.lookingRight && transform.eulerAngles.y == 0) || (!pState.lookingRight && transform.eulerAngles.y != 0)) { pState.lookingRight = !pState.lookingRight; int _yRotation = pState.lookingRight ? 0 : 180; transform.eulerAngles = new Vector2(transform.eulerAngles.x, _yRotation); } Invoke(nameof(StopWallJumping), wallJumpingDuration); } } void StopWallJumping() { isWallJumping = false; } } </code>
May 10, 2024 at 2:20 pm #14595::save data code
<code> using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; using UnityEngine.SceneManagement; // using UnityEditor.MPE; using System.Xml.Linq; [System.Serializable] //* structs dont need to be present in the scene to be called public struct SaveData { // public Transform StartPoint; public static SaveData Instance; //map Relations public HashSet<string> sceneNames; //Save Data Relations public string saveSceneName; public Vector2 saveDataPos; public int playerMoney; //Player data public int playerHealth; public int playerDamage; public int playerHeartShards; public float playerMana; public int playerManaOrbs; public int playerManaOrbShard; public float playerOrb0fill, playerOrb1fill, playerOrb2fill; public bool playerHalfMana; public Vector2 playerPosition; public string lastScene; public bool playerUnlockedWallJump; public bool playerUnlockedDash; public bool playerUnlockedDoubleJump; public bool playerUnlockedSideSpell; public bool playerUnlockedUpSpell; public bool playerUnlockedDownSpell; //Enemy Data //Shade public Vector2 shadePos; public string sceneWithShade; public Quaternion shadeRot; public void Initialize() { if(!File.Exists(Application.persistentDataPath + "/save.savePoint.data")) { BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.savePoint.data")); } if(!File.Exists(Application.persistentDataPath + "/save.player.data")) { BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.player.data")); } if(!File.Exists(Application.persistentDataPath + "/save.shade.data")) { BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.shade.data")); } if(sceneNames == null) { sceneNames = new HashSet<string>(); } } public void SaveSavePoint() { using(BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.savePoint.data"))) { writer.Write(saveSceneName); writer.Write(saveDataPos.x); writer.Write(saveDataPos.y); } } public void LoadSavePoint() { // if (File.Exists(Application.persistentDataPath + "/save.savePoint.data")) string savePath = Application.persistentDataPath + "/save.savePoint.data"; if(File.Exists(savePath) && new FileInfo(savePath).Length > 0) { using(BinaryReader reader = new BinaryReader(File.OpenRead(Application.persistentDataPath + "/save.savePoint.data"))) { saveSceneName = reader.ReadString(); saveDataPos.x = reader.ReadSingle(); saveDataPos.y = reader.ReadSingle(); } } } public void SavePlayerData() { using(BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.player.data"))) { playerMoney = PlayerController.Instance.money; writer.Write(playerMoney); playerHealth = PlayerController.Instance.Health; writer.Write(playerHealth); playerDamage = PlayerController.Instance.damage; writer.Write(playerDamage); playerHeartShards = PlayerController.Instance.heartShards; writer.Write(playerHeartShards); playerMana = PlayerController.Instance.Mana; writer.Write(playerMana); playerHalfMana = PlayerController.Instance.halfMana; writer.Write(playerHalfMana); // playerManaOrbs = PlayerController.Instance.manaOrbs; // writer.Write(playerManaOrbs); // playerManaOrbShard = PlayerController.Instance.orbShard; // writer.Write(playerManaOrbShard); // playerOrb0fill = PlayerController.Instance.manaOrbsHandle.orbFills[0].fillAmount; // writer.Write(playerOrb0fill); // playerOrb1fill = PlayerController.Instance.manaOrbsHandle.orbFills[1].fillAmount; // writer.Write(playerOrb0fill); // playerOrb2fill = PlayerController.Instance.manaOrbsHandle.orbFills[2].fillAmount; // writer.Write(playerOrb0fill); //*unlocked abilities playerUnlockedWallJump = PlayerController.Instance.unlockedWallJump; writer.Write(playerUnlockedWallJump); playerUnlockedDash = PlayerController.Instance.unlockedDash; writer.Write(playerUnlockedDash); playerUnlockedDoubleJump = PlayerController.Instance.unlockedDoubleJump; writer.Write(playerUnlockedDoubleJump); playerUnlockedSideSpell = PlayerController.Instance.unlockedSideSpell; writer.Write(playerUnlockedSideSpell); playerUnlockedUpSpell = PlayerController.Instance.unlockedUpCast; writer.Write(playerUnlockedUpSpell); playerUnlockedDownSpell = PlayerController.Instance.unlockedDownCast; writer.Write(playerUnlockedDownSpell); playerPosition = PlayerController.Instance.transform.position; writer.Write(playerPosition.x); writer.Write(playerPosition.y); lastScene = SceneManager.GetActiveScene().name; writer.Write(lastScene); } } public void LoadPlayerData() { // if(File.Exists(Application.persistentDataPath + "/save.player.data")) string savePath = Application.persistentDataPath + "/save.player.data"; if(File.Exists(savePath) && new FileInfo(savePath).Length > 0) { using (BinaryReader reader = new BinaryReader(File.OpenRead(Application.persistentDataPath + "/save.player.data"))) { playerMoney = reader.ReadInt32(); playerHealth = reader.ReadInt32(); playerDamage = reader.ReadInt32(); playerHeartShards = reader.ReadInt32(); playerMana = reader.ReadSingle(); playerHalfMana = reader.ReadBoolean(); // playerManaOrbs = reader.ReadInt32(); // playerManaOrbShard = reader.ReadInt32(); // playerOrb0fill = reader.ReadSingle(); // playerOrb1fill = reader.ReadSingle(); // playerOrb2fill = reader.ReadSingle(); playerUnlockedWallJump = reader.ReadBoolean(); playerUnlockedDash = reader.ReadBoolean(); playerUnlockedDoubleJump = reader.ReadBoolean(); playerUnlockedSideSpell = reader.ReadBoolean(); playerUnlockedUpSpell = reader.ReadBoolean(); playerUnlockedDownSpell = reader.ReadBoolean(); playerPosition.x = reader.ReadSingle(); playerPosition.y = reader.ReadSingle(); lastScene = reader.ReadString(); SceneManager.LoadScene(lastScene); PlayerController.Instance.transform.position = playerPosition; PlayerController.Instance.halfMana = playerHalfMana; PlayerController.Instance.money = playerMoney; PlayerController.Instance.Health = playerHealth; PlayerController.Instance.damage = playerDamage; PlayerController.Instance.heartShards = playerHeartShards; PlayerController.Instance.Mana = playerMana; // PlayerController.Instance.manaOrbs = playerManaOrbs; // PlayerController.Instance.orbShard = playerManaOrbShard; // PlayerController.Instance.manaOrbsHandle.orbFills[0].fillAmount = playerOrb0fill; // PlayerController.Instance.manaOrbsHandle.orbFills[1].fillAmount = playerOrb1fill; // PlayerController.Instance.manaOrbsHandle.orbFills[2].fillAmount = playerOrb2fill; PlayerController.Instance.unlockedWallJump = playerUnlockedWallJump; PlayerController.Instance.unlockedDash = playerUnlockedDash; PlayerController.Instance.unlockedDoubleJump = playerUnlockedDoubleJump; PlayerController.Instance.unlockedSideSpell = playerUnlockedSideSpell; PlayerController.Instance.unlockedUpCast = playerUnlockedUpSpell; PlayerController.Instance.unlockedDownCast = playerUnlockedUpSpell; } } else { Debug.Log("File doesnt exist"); PlayerController.Instance.halfMana = false; PlayerController.Instance.money = 0; PlayerController.Instance.Health = PlayerController.Instance.maxHealth; PlayerController.Instance.damage = 2; PlayerController.Instance.heartShards = 0; PlayerController.Instance.Mana = 0.5f; PlayerController.Instance.unlockedWallJump = false; PlayerController.Instance.unlockedDash = false; PlayerController.Instance.unlockedDoubleJump = false; PlayerController.Instance.unlockedSideSpell = false; PlayerController.Instance.unlockedUpCast = false; PlayerController.Instance.unlockedDownCast = false; } } public void SaveShadeData() { using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.shade.data"))) { sceneWithShade = SceneManager.GetActiveScene().name; shadePos = Shade.Instance.transform.position; shadeRot = Shade.Instance.transform.rotation; writer.Write(sceneWithShade); writer.Write(shadePos.x); writer.Write(shadePos.y); writer.Write(shadeRot.y); writer.Write(shadeRot.y); writer.Write(shadeRot.z); writer.Write(shadeRot.w); } } public void LoadShadeData() { // if(File.Exists(Application.persistentDataPath)) string savePath = Application.persistentDataPath + "/save.shade.data"; if(File.Exists(savePath) && new FileInfo(savePath).Length > 0) { using (BinaryReader reader = new BinaryReader(File.OpenRead(Application.persistentDataPath + "/save.shade.data"))) { sceneWithShade = reader.ReadString(); shadePos.x = reader.ReadSingle(); shadePos.y = reader.ReadSingle(); float rotationX = reader.ReadSingle(); float rotationY = reader.ReadSingle(); float rotationZ = reader.ReadSingle(); float rotationW = reader.ReadSingle(); shadeRot = new Quaternion(rotationX, rotationY, rotationZ, rotationW); } } else { Debug.Log("Shade doesnt exists"); } } public void ResetGame() { // Delete all save files if they exist if (File.Exists(Application.persistentDataPath + "/save.savePoint.data")) File.Delete(Application.persistentDataPath + "/save.savePoint.data"); if (File.Exists(Application.persistentDataPath + "/save.player.data")) File.Delete(Application.persistentDataPath + "/save.player.data"); if (File.Exists(Application.persistentDataPath + "/save.shade.data")) File.Delete(Application.persistentDataPath + "/save.shade.data"); using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.player.data"))) { playerMoney = 0; writer.Write(playerMoney); playerHealth = 5; writer.Write(playerHealth); playerDamage = 2; writer.Write(playerDamage); playerHeartShards = 0; writer.Write(playerHeartShards); playerMana = 1; writer.Write(playerMana); playerHalfMana = false; writer.Write(playerHalfMana); // playerManaOrbs = 0; // // writer.Write(playerManaOrbs); // playerOrbShard = 0; // writer.Write(playerOrbShard); // playerOrb0fill = 0; // writer.Write(playerOrb0fill); // playerOrb1fill = 0; // writer.Write(playerOrb1fill); // playerOrb2fill = 0; // writer.Write(playerOrb2fill); playerUnlockedWallJump = false; writer.Write(playerUnlockedWallJump); playerUnlockedDash = false; writer.Write(playerUnlockedDash); playerUnlockedDoubleJump = false; writer.Write(playerUnlockedDoubleJump); playerUnlockedSideSpell = false; writer.Write(playerUnlockedSideSpell); playerUnlockedUpSpell = false; writer.Write(playerUnlockedUpSpell); playerUnlockedDownSpell = false; writer.Write(playerUnlockedDownSpell); playerPosition = new Vector2 (-126.4f,34.5f); writer.Write(playerPosition.x); writer.Write(playerPosition.y); lastScene = "Cave_1"; writer.Write(lastScene); } // Optionally reload the scene to reset the game SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex); } } </code>
May 10, 2024 at 2:21 pm #14596May 10, 2024 at 2:34 pm #14597::Can you send a picture or video of the Shade’s inspector as well as it’s script? Likely the issue lies with the shade and not the player since the player can attack the other enemies fine.
May 10, 2024 at 3:06 pm #14598::shade code
<code>using System.Collections; using System.Collections.Generic; using UnityEngine; public class Shade : Enemy { [SerializeField] private float chaseDistance; [SerializeField] private float stunDuration; float timer; public static Shade Instance; public void Awake() { if(Instance != null && Instance != this) { Destroy(gameObject); } else { Instance = this; } SaveData.Instance.SaveShadeData(); } protected override void Start() { base.Start(); ChanegStates(EnemyStates.Shade_Idle); } protected override void Update() { base.Update(); if(!PlayerController.Instance.pState.alive) { ChanegStates(EnemyStates.Shade_Idle); } } // Update is called once per frame // void Update() // { // } protected override void UpdateEnenmyStates() { float _dist = Vector2.Distance(transform.position, PlayerController.Instance.transform.position); switch(GetCurrentEnemyStates) { case EnemyStates.Shade_Idle: if(_dist < chaseDistance) { ChanegStates(EnemyStates.Shade_Chase); } break; case EnemyStates.Shade_Chase: rb.MovePosition(Vector2.MoveTowards(transform.position, PlayerController.Instance.transform.position, Time.deltaTime * speed)); FlipShade(); break; case EnemyStates.Shade_Stunned: timer += Time.deltaTime; if(timer > stunDuration) { ChanegStates(EnemyStates.Shade_Idle); timer = 0; } break; case EnemyStates.Shade_Death: Death(Random.Range(5,10)); break; } } protected override void Death(float _destroyTime) { rb.gravityScale = 12; base.Death(_destroyTime); } public override void EnemyHit(int _damageDone, Vector2 _hitDirection, float _hitForce) { base.EnemyHit(_damageDone, _hitDirection, _hitForce); if(health > 0){ ChanegStates(EnemyStates.Shade_Stunned); } else { ChanegStates(EnemyStates.Shade_Death); } } protected override void ChangeCurrentAnimation() { if(GetCurrentEnemyStates == EnemyStates.Shade_Idle) { anim.Play("Player_Idle"); } anim.SetBool("Walking", GetCurrentEnemyStates == EnemyStates.Shade_Chase); // anim.SetBool("Idle", GetCurrentEnemyStates == EnemyStates.Shade_Idle); // anim.SetBool("Chase", GetCurrentEnemyStates == EnemyStates.Shade_Chase); // anim.SetBool("Stunned", GetCurrentEnemyStates == EnemyStates.Shade_Stunned); if(GetCurrentEnemyStates == EnemyStates.Shade_Death) { PlayerController.Instance.RestoreMana(); SaveData.Instance.SavePlayerData(); anim.SetTrigger("Death"); Destroy(gameObject, 0.5f); } } protected override void Attack() { base.Attack(); anim.SetTrigger("Attack"); PlayerController.Instance.TakeDamage(damage); } void FlipShade() { if(PlayerController.Instance.transform.position.x < transform.position.x) { sr.flipX = true; } else { sr.flipX = false; } } } </code>
May 10, 2024 at 3:07 pm #14601May 10, 2024 at 3:45 pm #14614::I’ve tested out your code, it seems to work fine.
I’ve looked at your video again and I think you should test this out again in your Editor. Dying, respawning then fighting the Shade again.
I believe the problem may lie in the fact that the spawned Shade has missing variables, aside from the fact that you should equip it with the “Enemy Hurt” Sound clip. [The console error tells you that there’s a missing audio clip, which you can see in the image you sent of the inspector].
Check that the Shade’s prefab (and by extension the spawned shade from dying) in your prefabs folder has it’s
- Layer set to Attackable
- Tag set to Enemy
- Inspector values all set
- It’s blood effect
- It’s hurt Audio Clip
-
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: