Forum begins after the advertisement:
[Part11]Game freezes
Home › Forums › Video Game Tutorial Series › Creating a Metroidvania in Unity › [Part11]Game freezes
- This topic has 12 replies, 4 voices, and was last updated 2 weeks, 2 days ago by MI NI.
-
AuthorPosts
-
November 17, 2024 at 1:27 pm #16372::
-
After the pause is released, pressing Continue Game will cause the game to freeze, but it will be unfrozen by pressing ESC repeatedly.
-
Since
UnPausedGame()
is deleted and you choose to return to the main screen, you will not automatically return to the main screen. You need to press ESC again to unpause before returning to the main screen. -
I saw that
GameManager.cs pauseMenu.Fade(fadeTime, b);
was removed at the back of the article. But there is no label. When I remove it and press ESC, the screen will freeze. After I restore it, when I press ESC again, my character can only flip left and right, but it will not be unfrozen. And we don’t seem to have given new settings for exiting the gameGameManager.UnPausedGame()
-
In a previous article, I asked you how to pause the game when opening the inventory and map. Later, I wrote the inventory and map to
GameManager.cs
, but sinceUnPausedGame()
was deleted, I needed to update my code. If I To enable the game to be paused to avoid being attacked by monsters when these two functions are enabled, should I write a new function for these two functions?
The following is the video that I recorded with errors, but some problems 1.2 occurred in the middle of production. I followed the entire article before recording the video, resulting in some errors that could not be recorded. I can provide the code and new videos for review if needed. thank you very much
GameManager.cs
<code>using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; public class GameManager : MonoBehaviour { public string transitionedFromScene; //儲存前一個場景 public Vector2 platformingRespawnPoint; //重生點 public Vector2 respawnPoint; [SerializeField] SavePoint savePoint; [SerializeField] private FadeUI pauseMenu; [SerializeField] private FadeUI mapPause; [SerializeField] private float fadeTime; public bool isPaused; float lastTimeScale = -1; static Coroutine stopGameCoroutine; public static bool isStopped {get { return stopGameCoroutine != null; }} bool openMap = false; bool openInventory = false; public static GameManager Instance { get; private set; } private void Awake() { SaveData.Instance.Initialize(); if(Instance != null && Instance != this) { Destroy(gameObject); } else { Instance = this; } DontDestroyOnLoad(gameObject); SaveData.Instance.LoadSceneData(); SaveScene(); SaveData.Instance.LoadMapData(); savePoint = FindObjectOfType<SavePoint>(); print("Loaded everything"); } private void Update() { /* //退出 if (Input.GetKeyDown(KeyCode.Escape) && !gameIsPaused) { pauseMenu.FadeUIIn(fadeTime); Time.timeScale = 0; GameManager.Instance.gameIsPaused = true; } else if(Input.GetKeyDown(KeyCode.Escape) && gameIsPaused) { UnpauseGame(); pauseMenu.FadeUIOut(fadeTime); } //地圖 if (Input.GetButtonDown("Map") && !openMap && !gameIsPaused) { openMap = true; if (openMap) { Time.timeScale = 0; GameManager.Instance.gameIsPaused = true; } UIManager.Instance.mapHandler.SetActive(true); } else if (Input.GetButtonDown("Map") && openMap) { openMap = false; if (!openMap) { UnpauseGame(); } UIManager.Instance.mapHandler.SetActive(false); } //背包 if (Input.GetButtonDown("Inventory") && !openInventory && !gameIsPaused) { openInventory = true; if(openInventory) { Time.timeScale = 0; GameManager.Instance.gameIsPaused = true; } UIManager.Instance.inventory.SetActive(true); } else if (Input.GetButtonDown("Inventory") && openInventory) { openInventory = false; if(!openInventory) { UnpauseGame(); } UIManager.Instance.inventory.SetActive(false) ; } */ if(Input.GetKeyDown(KeyCode.Escape)) { Pause(!isPaused); } /* if (Input.GetButtonDown("Inventory")) { Pause(!isPaused); } if (Input.GetButtonDown("Map")) { Pause(!isPaused); } */ } public void Pause(bool b) { if(b) { //儲存我們將還原到的時間刻度。 if (lastTimeScale < 0) { lastTimeScale = Time.timeScale; } Time.timeScale = 0f; } else { if(isStopped) { Time.timeScale = lastTimeScale; lastTimeScale = -1; } } //pauseMenu.Fade(fadeTime, b); isPaused = b; } //此函數可在任何地方調用 //例 GameManager.Stop(); // 停止遊戲0.5秒,並在0.1秒(預設)內逐漸恢復原來的時間尺度。 //例 GameManager.Stop(1.2f, 0.2f); // 停止結束後 0.2 秒內恢復時間刻度。 //例 GameManager.Stop(1.2f, 0.2f, 0.05f); // 停止將導致時間以原始速度的 5% 移動。 //若不想完全停止時間,第三個參數設置為非浮點數。 public static void Stop(float duration = 0.5f, float restoreDelay = 0.1f, float slowMultiplier = 0f) { if (stopGameCoroutine != null) return; stopGameCoroutine = Instance.StartCoroutine(HandleStopGame(duration, restoreDelay, slowMultiplier)); } // 用來建立點擊停止效果。 // <duration> 指定持續時間。 // <restoreDelay> 指定我們回到原始時間尺度的速度。 // <stopMultiplier> 控制停止的程度。 static IEnumerator HandleStopGame(float duration, float restoreDelay, float slowMultiplier = 0f) { if (Instance.lastTimeScale < 0) Instance.lastTimeScale = Time.timeScale; // 儲存原始時間刻度以供稍後恢復。 Time.timeScale = Instance.lastTimeScale * slowMultiplier; // 每一幀倒數計時,直到停止遊戲結束。 WaitForEndOfFrame w = new WaitForEndOfFrame(); while (duration > 0) { // 若遊戲暫停,不倒數計時,也不循環。 if (Instance.isPaused) { yield return w; continue; } // 將時間設定回零,因為取消暫停會將其設定回 1 Time.timeScale = Instance.lastTimeScale * slowMultiplier; // 倒數計時。 duration -= Time.unscaledDeltaTime; yield return w; } // 保存我們想要恢復的最後一個時間尺度。 float timeScaleToRestore = Instance.lastTimeScale; // 發出停止已完成的訊號。 Instance.lastTimeScale = -1; stopGameCoroutine = null; // 如果設定了恢復延遲,則逐漸恢復時間刻度。 if (restoreDelay > 0) { // 將時間刻度從設定的值移至原始值。 float currentTimeScale = Instance.lastTimeScale * slowMultiplier; float restoreSpeed = (Instance.lastTimeScale - currentTimeScale) / restoreDelay; while (currentTimeScale < Instance.lastTimeScale) { // 如果遊戲暫停則停止此操作。 if (Instance.isPaused) { yield return w; continue; } // 如果另一個停止已開始,則結束此協程。 if (isStopped) yield break; // 將時間刻度設定為本幀的目前值。 currentTimeScale += restoreSpeed * Time.unscaledDeltaTime; Time.timeScale = currentTimeScale; // 等待一幀。 yield return w; } } // 如果沒有再次停止,則僅還原 timeScale。 // 如果在恢復時間刻度時另一個停止觸發,可能會發生。 if (!isStopped) Time.timeScale = timeScaleToRestore; } public void SaveGame() { SaveData.Instance.SavePlayerData(); } /* public void UnpauseGame() { //Resumed = true; Time.timeScale = 1; GameManager.Instance.gameIsPaused = false; //沒有正確銷毀gamemanager 導致需要加GameManager.Instance Debug.Log(gameIsPaused); } */ public void SaveScene() { //獲取當前場景名稱 string currentSceneName = SceneManager.GetActiveScene().name; SaveData.Instance.sceneNames.Add(currentSceneName); SaveData.Instance.SaveSceneData(); //Debug.Log("saved" + currentSceneName); } public void RespawnPlayer() { SaveData.Instance.LoadSavePoint(); if(SaveData.Instance.savePointSceneName != null) //載入重生點的場景 { print(SaveData.Instance.savePointSceneName + "by GameManager"); SceneManager.LoadScene(SaveData.Instance.savePointSceneName); } if(SaveData.Instance.savePointPos != null) //設定重生位置為savePoint { respawnPoint = SaveData.Instance.savePointPos; } else { respawnPoint = platformingRespawnPoint; } PlayerController.Instance.transform.position = respawnPoint; StartCoroutine(UIManager.Instance.DeactiveateDeathScreen()); //重生點淡入淡出 PlayerController.Instance.Respawned(); //使角色重生 } } </code>
PlayerController.cs
<code>using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UIElements; using UnityEngine.UI; public class PlayerController : MonoBehaviour { [Header("水平移動")] [SerializeField] private float walkSpeed = 1; //走路速度 [Space(5)] [Header("垂直移動")] [SerializeField] private float jumpForce = 45; //跳躍速度 private int jumpBufferCounter = 0; //按下按鈕儲存指令 [SerializeField] private int jumpBufferFrames; //按下按鈕儲存指令時間/偵 private float coyoteTimeCounter = 0; //土狼時間 [SerializeField] private float coyoteTime; //土狼時間/偵 private int airJumpCounter = 0; [SerializeField] private int maxAirJumps; [SerializeField] private float fallMultiplier = 2.5f; [SerializeField] private float lowJumpMulitiplier = 2f; [Space(5)] [Header("WallJump")] [SerializeField] private float wallSlidingSpeed; //滑行速度 [SerializeField] private Transform wallCheck; //牆壁檢查點 [SerializeField] private LayerMask wallLayer; [SerializeField] private float wallJumpingDuration; //蹬牆跳持續時間 [SerializeField] private Vector2 wallJumpPower; float wallJumpingDirection; bool isWallSliding; bool isWallJumping; [Space(5)] [Header("地面檢查")] [SerializeField] private Transform groundCheckPoint; //地面檢查點 [SerializeField] private float groundCheckY = 0.2f; // [SerializeField] private float groundCheckX = 0.5f; // [SerializeField] private LayerMask whatIsGround; //檢查是否在地面 [Space(5)] [Header("衝刺")] [SerializeField] private float dashSpeed; //衝刺速度 [SerializeField] private float dashTime; //衝刺時間 [SerializeField] private float dashCooldown; //衝刺冷卻 [Space(5)] [Header("攻擊")] [SerializeField] private float timeBetweenAttack; //攻擊時間 [SerializeField] Transform SideAttackTransform, UpAttackTransform, DownAttackTransform; //不同方向攻擊位置 [SerializeField] Vector2 SideAttackArea, UpAttackArea, DownAttackArea; //不同方向攻擊範圍 [SerializeField] LayerMask attackableLayer; //可攻擊的對象圖層 [SerializeField] float damage; //傷害 [SerializeField] GameObject slashEffect; //劍氣效果 private bool attack = false; private float timeSinceAttack; bool restoreTime; public float restoreTimeSpeed; [Space(5)] [Header("後座力")] [SerializeField] int recilXSteps = 5; [SerializeField] int recilYSteps = 5; [SerializeField] float recilXSpeed = 5; [SerializeField] float recilYSpeed = 5; private int stepsXRecoiled, stepsYRecoiled; [Space(5)] [Header("血量")] public float health; //當前血量 public float maxHealth; //最大血量 [SerializeField] GameObject hitEffect; //被擊效果 [SerializeField] float hitFlashSpeed; //受傷角色閃爍 float healTimer; [SerializeField] float timeToheal; [Space(5)] [Header("血量圖片")] public Text healthText; //血量文字 public UnityEngine.UI.Image healthImage; //血量圖片 public UnityEngine.UI.Image healthEffect; //血量緩衝特效 public float effectTime = 0.5f; //緩衝持續時間 private Coroutine updateCoroutine; [Space(5)] [Header("魔力")] [SerializeField] float mana; //魔力量 [SerializeField] float maxMana; //最大魔力量 [SerializeField] float manaDrainSpeed; //魔力消耗速度 [SerializeField] float manaGain; //獲得魔力 [SerializeField] UnityEngine.UI.Image manaImage; //魔力圖片 [Header("技能")] [SerializeField] float manaSpellCost = 0.3f; //法術消耗 [SerializeField] float timeBetweenCast = 0.5f; //攻擊間隔速度 [SerializeField] GameObject sideSpellFireball; //火球物件Prefab float timeSinceCast; float castOrHealtimer; [Header("相機")] [SerializeField] private float playerFallSpeedTheshold = -10; [HideInInspector] public PlayerStateList pState; //取得緩衝輸入腳本 [HideInInspector] public Rigidbody2D rb; private Animator anim; private SpriteRenderer sr; private float gravity; //重力 private float xAxis, yAxis; //取得XY軸輸入 private bool canDash = true; private bool dashed; private bool canFlash = true; //public bool openMap = false; //開啟地圖 //public bool openInventory = false; //開啟背包 //解鎖能力 public bool unlockWallJump; public bool unlockDash; public bool unlockAirJump; public bool unlockSideSpell = false; public static PlayerController Instance; //防止多個重複的玩家腳本出現 private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); } else { Instance = this; } DontDestroyOnLoad(gameObject); } void Start() { pState = GetComponent<PlayerStateList>(); rb = GetComponent<Rigidbody2D>(); sr = GetComponent<SpriteRenderer>(); anim = GetComponent<Animator>(); gravity = rb.gravityScale; Health = maxHealth; //Save之後不再需要 Mana = maxMana; healthImage.fillAmount = health / maxHealth; healthEffect.fillAmount = health / maxHealth; healthText.text = health.ToString() + "/" + maxHealth.ToString(); manaImage.fillAmount = Mana; if (Health == 0) { pState.alive = false; GameManager.Instance.RespawnPlayer(); } SaveData.Instance.LoadPlayerData(); } private void OnDrawGizmos() { Gizmos.color = Color.red; Gizmos.DrawWireCube(SideAttackTransform.position, SideAttackArea); Gizmos.DrawWireCube(UpAttackTransform.position, UpAttackArea); Gizmos.DrawWireCube(DownAttackTransform.position, DownAttackArea); //Gizmos.DrawWireCube(wallCheck.position, ) } void Update() { if (GameManager.Instance.isPaused) return; //RestoreTimeScale(); if (pState.cutscene) return; if (pState.alive) { GetInputs(); //ToggleMap(); //ToggleInventory(); } UpdateJumpVariables(); //RestoreTimeScale(); UpdateCameraYDampForPlayerFall(); if (pState.alive) { Heal(); } if (pState.dashing || pState.healing) return; if (pState.alive) { if (!isWallJumping) { Flip(); Move(); Jump(); } if (unlockWallJump) { WallSlide(); WallJump(); } if (unlockDash) { StartDash(); } Attack(); CastSpell(); } FlashWhileInvincible(); } private void FixedUpdate() { if (pState.cutscene) return; if (pState.dashing || pState.healing) return; Recoil(); } void GetInputs() //獲取輸入 { if (GameManager.Instance.isPaused || GameManager.isStopped) return; xAxis = Input.GetAxisRaw("Horizontal"); yAxis = Input.GetAxisRaw("Vertical"); attack = Input.GetButtonDown("Attack"); if (Input.GetButton("Cast/Heal")) { castOrHealtimer += Time.deltaTime; } } void Flip() //翻轉 { if(xAxis < 0) { transform.localScale = new Vector2(-Mathf.Abs(transform.localScale.x), transform.localScale.y); pState.lookingRight = false; } else if(xAxis >0) { transform.localScale = new Vector2(Mathf.Abs(transform.localScale.x), transform.localScale.y); pState.lookingRight = true; } } private void Move() //移動 { if (pState.healing) rb.velocity = new Vector2(0, 0); //如果玩家正在治療則無法移動 rb.velocity = new Vector2(walkSpeed * xAxis ,rb.velocity.y); anim.SetBool("Walk", rb.velocity.x !=0 && Grounded()); } void UpdateCameraYDampForPlayerFall() { //if falling past a certain speed threshold if (rb.velocity.y < playerFallSpeedTheshold && !CameraManager.Instance.isLerpingYDamp && !CameraManager.Instance.hasLerpingYDamping) { StartCoroutine(CameraManager.Instance.LerpYDamping(true)); } //if standing stil or moveing up if(rb.velocity.y >= 0 && !CameraManager.Instance.isLerpingYDamp && CameraManager.Instance.hasLerpingYDamping) { //reset camera funtion CameraManager.Instance.hasLerpingYDamping = false; StartCoroutine(CameraManager.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("Dash"); rb.gravityScale = 0; int _dir = pState.lookingRight ? 1 : -1; rb.velocity = new Vector2(_dir * dashSpeed, 0); yield return new WaitForSeconds(dashTime); rb.gravityScale = gravity; pState.dashing = false; yield return new WaitForSeconds(dashCooldown); canDash = true; } public IEnumerator WalkIntoNewScene(Vector2 _exitDir, float _delay) //過場動畫 { pState.invincible = true; if(_exitDir.y > 0) //如果退出位置為向上 { rb.velocity = jumpForce * _exitDir; } //如果退出位置為水平移動 if(_exitDir.x != 0) { xAxis = _exitDir.x > 0 ? 1 : -1; Move(); } Flip(); yield return new WaitForSeconds(_delay); pState.invincible = false; pState.cutscene = false; } void Attack() //攻擊 { timeSinceAttack += Time.deltaTime; if(attack && timeSinceAttack >= timeBetweenAttack) { timeSinceAttack = 0; anim.SetTrigger("Attack"); if(yAxis == 0 || yAxis < 0 && Grounded()) //如果玩家在地面上且沒有按住"上",正常攻擊 { int _recoilLeftOrRight = pState.lookingRight ? 1 : -1; Hit(SideAttackTransform, SideAttackArea, ref pState.recoilingX, Vector2.right * _recoilLeftOrRight, recilXSpeed); Instantiate(slashEffect, SideAttackTransform); } else if(yAxis > 0) //如果按住"上",上攻擊 { Hit(UpAttackTransform, UpAttackArea, ref pState.recoilingY, Vector2.up, recilYSpeed); SlashEffectAtAngle(slashEffect, 90, UpAttackTransform); } else if (yAxis < 0 && !Grounded()) //如果不在地面且按住"下",下攻擊 { Hit(DownAttackTransform, DownAttackArea, ref pState.recoilingY, Vector2.down, recilYSpeed); SlashEffectAtAngle(slashEffect, -90, DownAttackTransform); } } } private void Hit(Transform _attackTransfrom, Vector2 _attackArea, ref bool _recoilBool,Vector2 _recoilDir , float _recoilStrength) //給予傷害 { Collider2D[] objectsToHit = Physics2D.OverlapBoxAll(_attackTransfrom.position, _attackArea, 0, attackableLayer); List<Enemy> hitEnemise = new List<Enemy>(); if(objectsToHit.Length > 0) { //Debug.Log("Hit"); _recoilBool = true; } for(int i = 0; i < objectsToHit.Length; i++) { if(objectsToHit[i].GetComponent<Enemy>() != null) { objectsToHit[i].GetComponent<Enemy>().EnemyHit(damage, _recoilDir , _recoilStrength) ; /* Enemy e = objectsToHit[i].GetComponent<Enemy>(); if(e && !hitEnemise.Contains(e)) { e.EnemyHit(damage, (transform.position - objectsToHit[i].transform.position).normalized, _recoilStrength); hitEnemise.Add(e); } */ if (objectsToHit[i].CompareTag("Monster")) //打到怪物增加魔力 { 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 Vector2(transform.localScale.x, transform.localScale.y); } void Recoil() //反衝 { if(pState.recoilingX) { if (pState.lookingRight) { rb.velocity = new Vector2(-recilXSpeed, 0); } else { rb.velocity = new Vector2(recilXSpeed, 0); } } if(pState.recoilingY) { rb.gravityScale = 0; if (yAxis < 0) { rb.velocity = new Vector2(rb.velocity.x, recilYSpeed); } else { rb.velocity = new Vector2(rb.velocity.x, -recilYSpeed); } airJumpCounter = 0; } else { rb.gravityScale = gravity; } //Stop recoil 停止反衝 if(pState.recoilingX && stepsXRecoiled < recilXSteps) { stepsXRecoiled++; } else { StopRecoilX(); } if (pState.recoilingY && stepsYRecoiled < recilYSteps) { 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) //玩家受傷 { if(pState.alive) { Health -= Mathf.RoundToInt(_damage); if(Health <= 0) { Health = 0; StartCoroutine(Death()); } else { StartCoroutine(StopTakingDamage()); healthImage.fillAmount = health / maxHealth; //血量圖片 healthText.text = health.ToString() + "/" + maxHealth.ToString(); //血量文字 if (updateCoroutine != null) { StopCoroutine(updateCoroutine); } updateCoroutine = StartCoroutine(UpdateHealthEffect()); } } } private IEnumerator UpdateHealthEffect() //血量緩降協程 { float effectLength = healthEffect.fillAmount - healthImage.fillAmount; float elapsedTime = 0f; while (elapsedTime < effectTime && effectLength != 0) { elapsedTime += Time.deltaTime; healthEffect.fillAmount = Mathf.Lerp (healthImage.fillAmount + effectLength, healthImage.fillAmount, elapsedTime / effectTime); yield return null; } healthEffect.fillAmount = healthImage.fillAmount; } public IEnumerator StopTakingDamage() //無敵偵 { pState.invincible = true; GameObject _hitEffect = Instantiate(hitEffect, transform.position, Quaternion.identity); Destroy(_hitEffect, 1.5f); anim.SetTrigger("Hurt"); yield return new WaitForSeconds(1f); pState.invincible = false; } IEnumerator Flash() { sr.enabled = !sr.enabled; canFlash = false; yield return new WaitForSeconds(0.2f); canFlash = true; } void FlashWhileInvincible() { //將渲染改為"PingPongLerp" 如果玩家是無敵狀態則將其從白色改為黑色 否則將玩家設定為白色 //sr.material.color = pState.invincible ? Color.Lerp(Color.white, Color.black, Mathf.PingPong(Time.time * hitFlashSpeed, 1.0f)) : Color.white; if(pState.invincible && !pState.cutscene) { if (Time.timeScale > 0.2f && canFlash) { StartCoroutine(Flash()); } } else { sr.enabled = true; } } /* void RestoreTimeScale() //受傷暫停時間 { if(restoreTime) //檢查是否為真 { if(Time.timeScale < 1) //如果小於1 時間速度加快到達1 { Time.timeScale += Time.unscaledDeltaTime * restoreTimeSpeed; } else //如果超過1 時間為1 { 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) { yield return new WaitForSecondsRealtime(_delay); restoreTime = true; } IEnumerator Death() { pState.alive = false; Time.timeScale = 1f; //避免受傷暫停時間導致出錯 //可在此呼叫粒子效果 anim.SetTrigger("Death"); rb.constraints = RigidbodyConstraints2D.FreezePosition; rb.constraints = RigidbodyConstraints2D.FreezeRotation; GetComponent<BoxCollider2D>().enabled = false; yield return new WaitForSeconds(0.9f); StartCoroutine(UIManager.Instance.ActiveDeathScreen()); } public void Respawned() { if(!pState.alive) { rb.constraints = RigidbodyConstraints2D.None; rb.constraints = RigidbodyConstraints2D.FreezeRotation; GetComponent<BoxCollider2D>().enabled = true; pState.alive = true; Health = maxHealth; anim.Play("PlayerIdle"); } } public float Health //血量 { get { return health; } set { if(health != value) { health = Mathf.Clamp(value, 0, maxHealth); } } } void Heal() { if(Input.GetButton("Cast/Heal") && castOrHealtimer > 0.5f && health < maxHealth && Mana > 0 && Grounded() && !pState.dashing) { pState.healing = true; anim.SetBool("Heal", true); //補血 healTimer += Time.deltaTime; if(healTimer >= timeToheal) { health += 5; //恢復血量 healTimer = 0; //血量計時器 healthImage.fillAmount = health / maxHealth; //血量圖片 healthEffect.fillAmount = health / maxHealth; //血量效果圖片 healthText.text = health.ToString() + "/" + maxHealth.ToString(); //血量文字 } Mana -= Time.deltaTime * manaDrainSpeed;//魔力消耗 } else { pState.healing = false; anim.SetBool("Heal", false); healTimer = 0; } } public float Mana { get { return mana; } set { if(mana != value) { mana = Mathf.Clamp(value, 0, maxMana); manaImage.fillAmount = Mana; } } } void CastSpell() { if(Input.GetButtonUp("Cast/Heal") && castOrHealtimer <= 0.5f && timeSinceCast >= timeBetweenCast && Mana > manaSpellCost) { pState.casting = true; timeSinceCast = 0; StartCoroutine(CastCoroutine()); } else { timeSinceCast += Time.deltaTime; } if(!Input.GetButton("Cast/Heal")) { castOrHealtimer = 0; } } IEnumerator CastCoroutine() { //橫向技能 if((yAxis == 0 || (yAxis < 0 && Grounded())) && unlockSideSpell ) //沒有垂直輸入時播放 目前無需要按住上下才能施放的技能 { anim.SetBool("Cast", true); yield return new WaitForSeconds(0.1f); //此為等待動畫到達正確播放時間 GameObject _fireBall = Instantiate(sideSpellFireball, SideAttackTransform.position, Quaternion.identity); //實例化該物件 //Flip火球 if(pState.lookingRight) { _fireBall.transform.eulerAngles = Vector3.zero; } else { _fireBall.transform.eulerAngles = new Vector2(_fireBall.transform.eulerAngles.x, 180); //如果不是面向右邊翻轉180度 } pState.recoilingX = true; //給予玩家反衝 Mana -= manaSpellCost; //魔力減少 yield return new WaitForSeconds(0.3f); //此為動畫撥放完整時間 } anim.SetBool("Cast", 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 (jumpBufferCounter > 0 && coyoteTimeCounter > 0 && !pState.jumping) { rb.velocity = new Vector2(rb.velocity.x, jumpForce); //Vector3 pState.jumping = true; } //二段跳 if (!Grounded() && airJumpCounter < maxAirJumps && Input.GetButtonDown("Jump") && unlockAirJump && !isWallSliding) { pState.jumping = true; airJumpCounter++; rb.velocity = new Vector2(rb.velocity.x, jumpForce); } /* if(rb.velocity.y > 0 && pState.jumping) { jumpCounter += Time.deltaTime; if (jumpCounter > jumpTime) isJumping = false; rb.velocity += vecGravity * jumpMulitiplier * Time.deltaTime; } if(Input.GetButtonUp("Jump")) { isJumping = false; } if (rb.velocity.y < 0) { rb.velocity -= vecGravity * fallMultiplier * Time.deltaTime; } */ if(rb.velocity.y < 0) { rb.velocity += Vector2.up * Physics2D.gravity.y * (fallMultiplier - 1) * Time.fixedDeltaTime; pState.jumping = false; } else if(Input.GetButtonUp("Jump") && rb.velocity.y > 0) { rb.velocity += Vector2.up * Physics2D.gravity.y * (lowJumpMulitiplier - 1) * Time.fixedDeltaTime; pState.jumping = false; } anim.SetBool("Jump", !Grounded()); } void UpdateJumpVariables() { if(Grounded()) { pState.jumping = false; coyoteTimeCounter = coyoteTime; airJumpCounter = 0; } else { coyoteTimeCounter -= Time.deltaTime; } if(Input.GetButtonDown("Jump") ) { jumpBufferCounter = jumpBufferFrames; } else { jumpBufferCounter--; } } private bool Walled() { return Physics2D.OverlapCircle(wallCheck.position, 0.2f, wallLayer); } void WallSlide() { if(Walled() && !Grounded() && xAxis !=0) { isWallSliding = true; rb.velocity = new Vector2(rb.velocity.x, Mathf.Clamp(rb.velocity.y, -wallSlidingSpeed, float.MaxValue)); anim.SetBool("Slide", true); transform.eulerAngles = new Vector2(transform.eulerAngles.x, 0); } else { isWallSliding = false; anim.SetBool("Slide", false); } } void WallJump() { if(isWallSliding) { isWallJumping = false; wallJumpingDirection = !pState.lookingRight ? 1 : -1; CancelInvoke(nameof(StopWallJumping)); } if(Input.GetButtonDown("Jump") && isWallSliding) { anim.SetBool("WallJump", true); isWallJumping = true; rb.velocity = new Vector2(wallJumpingDirection * wallJumpPower.x, wallJumpPower.y); dashed = false; airJumpCounter = 0; pState.lookingRight = !pState.lookingRight; transform.eulerAngles = new Vector2(transform.eulerAngles.x, 180); Invoke(nameof(StopWallJumping), wallJumpingDuration); } } void StopWallJumping() { anim.SetBool("WallJump", false); isWallJumping = false; rb.velocity = new Vector2(rb.velocity.x, 0); transform.eulerAngles = new Vector2(transform.eulerAngles.x, 0); } } </code>
November 17, 2024 at 7:04 pm #16378::I got the same problem but my game still run and the animation still continue. It just in my game the player cannot take input anymore. I can even attack or doing anything. If you have the answer from someone later, please let me know it.
November 20, 2024 at 11:34 am #16450::Let’s try and fix the NullReferenceException that appears when you resume first:
Is the issue this line?
public void Pause(bool b) { if(b) { //儲存我們將還原到的時間刻度。 if (lastTimeScale < 0) { lastTimeScale = Time.timeScale; } Time.timeScale = 0f; } else { if(isStopped) { Time.timeScale = lastTimeScale; lastTimeScale = -1; } } //pauseMenu.Fade(fadeTime, b); isPaused = b; }
November 20, 2024 at 11:09 pm #16468::yes, once this line of code is enabled and you click to continue the game or exit, an error will occur. If this line of code is not enabled, and you click to continue the game, the player cannot move and can only flip left and right.
November 21, 2024 at 3:18 pm #16475November 21, 2024 at 7:15 pm #16478::View post on imgur.com
The button to exit the game to the main menu does not have a variable specified because UnPaused disappears.
November 22, 2024 at 10:51 pm #16489::Can you check whether Pause Menu remains assigned when the game is running?
Unless I’m missing something, the only possible way this error occurs is when the Pause Menu is null and you try to run the code.
November 22, 2024 at 11:55 pm #16494November 23, 2024 at 1:39 pm #16497::If your Pause Menu is assigned when the game is not playing, but you are getting a NullReferenceException when playing the game, it is possible that the Pause Menu variable becomes unassigned during gameplay.
To check if this is the case, select the Game Manager GameObject during the game. Recreate the error and check whether the Pause Menu is assigned.
November 23, 2024 at 2:19 pm #16501::I tried reproducing the NullReferenceException, I made sure I had the PauseMenu assigned to the GameManager
November 24, 2024 at 11:19 am #16509::Could it be that you are not calling
UnpauseGame()
in the resume button? Or there might be an issue that is causing isStopped to remain false try to add some print code to the code that is managing the StopGameCoroutineNovember 24, 2024 at 10:34 pm #16535::I found that my Fade UI.cs didn’t work for some reason. I typed the code again and a different situation happened. My
Puase()
did not havepauseMenu.Fade(fadeTime, b);
, you can use and cancel the pause function normally, but the PauseMenu UI will not appear. without any error reportingIn the case of
pauseMenu.Fade(fadeTime, b);
, a new error occurred<code>Time.timeScale is out of range. The value cannot be less than 0.0 UnityEngine.StackTraceUtility:ExtractStackTrace () GameManager:Pause (bool) (at Assets/Script/GameManager.cs:147) UnityEngine.EventSystems.EventSystem:Update ()</code>
<code>NullReferenceException: Object reference not set to an instance of an object GameManager.Pause (System.Boolean b) (at Assets/Script/GameManager.cs:151)</code>
If you look at the teaching article, you should delete the
pauseMenu.Fade(fadeTime, b);
line of code, but it is not clearly marked in the article, so I am not sure, so my current problem is that when I press the pause button After that, the pause menu does not appear. I think it is because there is no CallpauseMenu.Fade
anywhere. And I tested changing the allocation in PauseMenu in GameManager to a null value, but this did not affect the pause function of the game, and there was no error report, so is it because there is no place to call the pause menu?November 24, 2024 at 11:37 pm #16536::Okay, I think it is a problem with Unity itself. After I tried a lot of modifications, nothing worked, so I deleted the entire GameManager.Pause area assigned to Resume and QuitToMainMenu in PauseMenu and reset it (press – )
View post on imgur.com
you can use the pause function normally without NullReferenceException errors. But since no call pause menu appears, I made the following modificationspublic void Pause(bool b) { if(b) { pauseMenu.Fade(fadeTime, b); // Save the timescale we will restore to. if (lastTimeScale < 0) lastTimeScale = Time.timeScale; Time.timeScale = 0f; } else { if(!isStopped) { pauseMenu.Fade(fadeTime, b); Time.timeScale = lastTimeScale; lastTimeScale = -1; } } pauseMenu.Fade(fadeTime, b); isPaused = b; }
Hope this helps anyone who has encountered these errors
I don’t know why, but my green and red markers never show up.
-
-
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: