Forum begins after the advertisement:
[Part 8] Missing Video Content, Common Issues & Fixes
Home › Forums › Video Game Tutorial Series › Creating a Metroidvania in Unity › [Part 8] Missing Video Content, Common Issues & Fixes
- This topic has 0 replies, 1 voice, and was last updated 3 months, 3 weeks ago by
Joseph Tang.
-
AuthorPosts
-
May 14, 2024 at 2:44 pm #14653
Joseph Tang
Moderator::This is a supplementary post written for Part 8 of our Metroidvania series, and it aims to address 1 thing:
- Missing information in the video.
For convenience, below are the links to the article and the video:
- Article Link: https://blog.terresquall.com/2023/08/creating-a-metroidvania-like-hollow-knight-part-8/
- Video Link: https://youtu.be/o5jUkjoUYGc
Table of Contents
Missing Information in the Video
- Extra Mana orbs don’t gain Mana while the Mana State is halfMana
- Non-functional WallJump/WallJump flips my player permanently
- Collectibles do not disappear when picking them up until after the pickup canvas has disappeared
- MaxHealth is not saved and does not appear correctly on start
Common Issues
Missing Information in the Video
1. Extra Mana orbs don’t gain Mana while the Mana State is halfMana
void Hit(Transform _attackTransform, Vector2 _attackArea, ref bool _recoilBool, Vector2 _recoilDir, float _recoilStrength) { ... for(int i = 0; i < objectsToHit.Length; i++) { if (objectsToHit[i].GetComponent
() != null) { objectsToHit[i].GetComponent ().EnemyHit (damage, _recoilDir, _recoilStrength); if (objectsToHit[i].CompareTag("Enemy")) { if (Mana < 1)if(!halfMana && Mana < 1 || (halfMana && Mana < 0.5)) { Mana += manaGain; } else { manaOrbsHandler.UpdateMana(manaGain * 3); } } } } }
2. Non-functional WallJump/WallJump flips my player permanently
We’ve updated this since encountering another issue with an updated Flip() function in Part 6
- Remove the if statement in the PlayerController.cs
WallJump()
method. - Then add a line to reverse the playerstate bool of
lookingRight
- We’ll then create a new float in the method called
jumpDirection
which we’ll set to either 0 or 180 depending on thepState.lookingRight
. - Next we’ll set the eulerAngles to
Vector2(transform.eulerAngles.x, jumpDirection)
. - Finally, reset the eulerAngles in
StopWallJumping()
.
void WallJump() { 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; pState.lookingRight = !pState.lookingRight; float jumpDirection = pState.lookingRight ? 0 : 180; transform.eulerAngles = new Vector2(transform.eulerAngles.x, jumpDirection);
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; transform.eulerAngles = new Vector2(transform.eulerAngles.x, 0); }Explanation of issue: Euler Angles
When using “int _yRotation = pState.lookingRight ? 0 : 180;” in “WallJump()”, it sets the “eulerAngles.y” to either [0] or [180], depending on the Player’s “lookingRight” bool. The idea is to have the player bounce off the wall and face away from the wall when they do so. The issue with this code is that “eulerAngles” aren’t affected by normal forms of rotation, like movement keys in game or the Transform Rotation in the inspector, as can be seen in here:Rather, they have to be reset by code back to a [0] value / normal to have your Player matching up correctly with your intended actions. Thus, we reset the “eulerAngles.y” to 0 when we write the “StopWallJumping()” code, where the player bounces away from the wall and stays that way for the set “wallJumpingDuration” before being free to move.
Understanding how Euler Angles work, we can remove the differentiated “pState.lookingRight” code from “WallJump()” as they do not affect the “eulerAngles” as intended. Thus, allowing both directions of wall jumping to bounce off the wall.
3. Collectibles do not disappear when picking them up until after the pickup canvas has disappeared
- Add “gameObject.GetComponent[SpriteRenderer]().enabled = false;” into all Collectibles’ scripts with ShowUI()
IEnumerator ShowUI() { GameObject _particles = Instantiate(particles, transform.position, Quaternion.identity); Destroy(_particles, 0.5f); yield return new WaitForSeconds(0.5f); gameObject.GetComponent[SpriteRenderer]().enabled = false; canvasUI.SetActive(true); yield return new WaitForSeconds(4f); PlayerController.Instance.unlockedWallJump = true; canvasUI.SetActive(false); Destroy(gameObject); }
4. MaxHealth is not saved and does not appear correctly on start
[This issue is caused by a missing piece of code]
- Add new
public int
variables to SaveData.cs forplayerMaxHealth
andplayerHeartShards
. Then follow the same steps as other player data inSavePlayerData()
andLoadPlayerData()
. - Then, go to HeartController.cs and change
InstantiateHeartContainers()
to apublic void
. - Move all the code from
Start()
intoInstantiateHeartContatiners()
, putting the code withUpdateHeartsHUD()
to the bottom of the method and the rest above. - Finally, call
InstantiateHeartContainers()
usingFindObjectOfType
to findHeartController
.
SaveData.cs
[System.Serializable] public struct SaveData { public static SaveData Instance; //player stuff public int playerHealth; public int playerMaxHealth; public int playerHeartShards; ... #region Player stuff public void SavePlayerData() { using(BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.player.data"))) { playerHealth = PlayerController.Instance.Health; writer.Write(playerHealth); playerMaxHealth = PlayerController.Instance.maxHealth; writer.Write(playerMaxHealth); playerHeartShards = PlayerController.Instance.heartShards; writer.Write(playerHeartShards); playerMana = PlayerController.Instance.Mana; writer.Write(playerMana); ... } Debug.Log("saved player data"); } public void LoadPlayerData() { if(File.Exists(Application.persistentDataPath + "/save.player.data")) { using(BinaryReader reader = new BinaryReader(File.OpenRead(Application.persistentDataPath + "/save.player.data"))) { playerHealth = reader.ReadInt32(); playerMaxHealth = reader.ReadInt32(); playerHeartShards = reader.ReadInt32(); playerMana = reader.ReadSingle(); playerHalfMana = reader.ReadBoolean(); ... lastScene = reader.ReadString(); SceneManager.LoadScene(lastScene); PlayerController.Instance.transform.position = playerPosition; PlayerController.Instance.halfMana = playerHalfMana; PlayerController.Instance.Health = playerHealth; PlayerController.Instance.maxHealth = playerMaxHealth; PlayerController.Instance.heartShards = playerHeartShards; PlayerController.Instance.Mana = playerMana; ... } Debug.Log("load player data"); Debug.Log(playerHalfMana); } } else { ... PlayerController.Instance.heartShards = 0; } #endregion }
HeartController.cs
public void Start() { heartContainers = new GameObject[PlayerController.Instance.maxTotalHealth]; heartFills = new Image[PlayerController.Instance.maxTotalHealth]; InstantiateHeartContainers(); PlayerController.Instance.onHealthChangedCallback += UpdateHeartsHUD; UpdateHeartsHUD(); }public void InstantiateHeartContainers() { heartContainers = new GameObject[PlayerController.Instance.maxHealth]; heartFills = new Image[PlayerController.Instance.maxHealth]; for (int i = 0; i < PlayerController.Instance.maxTotalHealth; i++) { GameObject temp = Instantiate(heartContainerPrefab); temp.transform.SetParent(heartsParent, false); heartContainers[i] = temp; heartFills[i] = temp.transform.Find("HeartFill").GetComponent(); } PlayerController.Instance.onHealthChangedCallback += UpdateHeartsHUD; UpdateHeartsHUD(); } PlayerController.cs
// Start is called before the first frame update void Start() { ... SaveData.Instance.LoadPlayerData(); if (halfMana) { UIManager.Instance.SwitchMana(UIManager.ManaState.HalfMana); } else { UIManager.Instance.SwitchMana(UIManager.ManaState.FullMana); } FindObjectOfType[HeartController]().InstantiateHeartContainers(); ... }
4.5. Alternative Method.
[We’ve found a simpler method for this issue after the change in SaveData.cs]
- In HeartController.cs
Start()
, change theheartContainers
andheartFills
to reference themaxTotalHealth
instead ofmaxHealth
. - in InstantiateHeartContainers(), we’ll also reference the maxTotalHealth.
- Finally, in our PlayerController.cs
Start()
, invoke theonHealthChangedCallback
to ensure that when we load our player data, our maxHealth is also reset correctly on Start().
HeartController.cs
void Start() { heartContainers = new GameObject[PlayerController.Instance.
maxHealthmaxTotalHealth]; heartFills = new Image[PlayerController.Instance.maxHealthmaxTotalHealth]; ... } void InstantiateHeartContainers() { for(int i = 0; i < PlayerController.Instance.maxHealthmaxTotalHealth; i++) { GameObject temp = Instantiate(heartContainerPrefab); temp.transform.SetParent(heartsParent, false); heartContainers[i] = temp; heartFills[i] = temp.transform.Find("HeartFill").GetComponent(); } } PlayerController.cs
void Start() { ... SaveData.Instance.LoadPlayerData(); ... onHealthChangedCallback.Invoke(); if (Health == 0) { pState.alive = false; GameManager.Instance.RespawnPlayer(); } }
Common Issues
5. Player is sent flying upwards after walljumping.
[This issue is caused by Wall Jumping Power Y being too high of a value]
Demonstration:
- 1. You can reduce the Wall Jumping Power to a lower more manageable value.
Lower Wall Jumping Power Y value.
- 2. Set the player’s Rigidbody2D’s Y velocity to [0] in
StopWallJumping()
void StopWallJumping() { isWallJumping = false; rb.velocity = new Vector2(rb.velocity.x, 0); transform.eulerAngles = new Vector2(transform.eulerAngles.x, 0); }
That will be all for Part 8.
Hopefully this can help you on any issues you may have. However, if you find that your issues weren’t addressed or is a unique circumstance, you can submit a forum post to go into detail on your problem for further assistance. -
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: