Forum begins after the advertisement:
[Part 7] Shade problem
Home › Forums › Video Game Tutorial Series › Creating a Metroidvania in Unity › [Part 7] Shade problem
- This topic has 14 replies, 2 voices, and was last updated 5 months ago by Hoang Nguyen.
-
AuthorPosts
-
July 13, 2024 at 11:49 pm #15278::
1.My shade show up when start game like an enemy, how can i hide it when the game start 2.when i clear all enemy in the first map then come to second map, when i come back to first map, the enemy is still there
July 14, 2024 at 4:45 pm #15279::Check two things first:
- Your scene does not already include a shade.
- Simply delete the shade from the scene.
- Your mana state is not halfMana.
- If you’re at halfMana, it means you died. Thus, you need to kill the shade to reset your mana state and save the game again.
Regardless, try killing the shade and then saving the game. If it doesn’t work, can you take a short video of the problem for us to take a look? Include the scene, the SaveData and Shade scripts and the shade being there in the game itself.
July 15, 2024 at 1:17 pm #15281::when i respawn, the shade js stood there and i can’t interact with it and when i kill all enemy in scene_1 then come to scene_2 and comeback, all enem is alive
View post on imgur.com
July 15, 2024 at 1:18 pm #15282::using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; using UnityEngine.SceneManagement; using Unity.Mathematics;
[System.Serializable] public struct SaveData { public static SaveData Instance;
//map stuff public HashSet<string> sceneNames; //bench stuff public string benchSceneName; public Vector2 benchPos; //player stuff public int playerHealth; public float playerMana; public bool playerHalfMana; public Vector2 playerPos; public string lastScene; //enemy stuff //Shade stuff public Vector2 shadePos; public string sceneWithShade; public Quaternion shadeRot; public void Initialize() { if(!File.Exists(Application.persistentDataPath + "/save.bench.data"))//if file doesnt exist , create new file { BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.bench.data")); } if(!File.Exists(Application.persistentDataPath + "/save.player.data"))//if file doesnt exist , create new file { BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.player.data")); } if(!File.Exists(Application.persistentDataPath + "/save.shade.data"))//if file doesnt exist , create new file { BinaryWriter writer = new BinaryWriter(File.Create(Application.persistentDataPath + "/save.shade.data")); } if (sceneNames == null) { sceneNames = new HashSet<string>(); } } public void SaveBench() { using(BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.bench.data"))) { writer.Write(benchSceneName); writer.Write(benchPos.x); writer.Write(benchPos.y); } } public void LoadBench() { if(File.Exists(Application.persistentDataPath + "/save.bench.data")) { using(BinaryReader reader = new BinaryReader(File.OpenRead(Application.persistentDataPath + "/save.bench.data"))) { benchSceneName = reader.ReadString(); benchPos.x = reader.ReadSingle(); benchPos.y = reader.ReadSingle(); } } } public void SavePlayerData() { using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(Application.persistentDataPath + "/save.player.data"))) { playerHealth = Player.Instance.Health; writer.Write(playerHealth); playerMana = Player.Instance.Mana; writer.Write(playerMana); playerHalfMana = Player.Instance.halfMana; writer.Write(playerHalfMana); playerPos = Player.Instance.transform.position; writer.Write(playerPos.x); writer.Write(playerPos.y); lastScene = SceneManager.GetActiveScene().name; writer.Write(lastScene); } } 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(); playerHalfMana = reader.ReadBoolean(); playerMana = reader.ReadSingle(); playerPos.x = reader.ReadSingle(); playerPos.y = reader.ReadSingle(); lastScene = reader.ReadString(); SceneManager.LoadScene(lastScene); Player.Instance.transform.position = playerPos; Player.Instance.Health = playerHealth; Player.Instance.halfMana = playerHalfMana; Player.Instance.Mana = playerMana; } } else { Debug.Log("file doesn't exist"); Player.Instance.halfMana = false; Player.Instance.Health = Player.Instance.maxHealth; Player.Instance.Mana = 0.5f; } } 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.x); writer.Write(shadeRot.y); writer.Write(shadeRot.z); writer.Write(shadeRot.w); } } public void LoadShadeData() { if (File.Exists(Application.persistentDataPath + "/save.shade.data")) { 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 exist"); } } public void DeletePlayerData() { string path = Application.persistentDataPath + "/save.player.data"; if (File.Exists(path)) { File.Delete(path); Debug.Log("Player save data deleted."); } } public void DeleteShadeData() { string path = Application.persistentDataPath + "/save.shade.data"; if (File.Exists(path)) { File.Delete(path); Debug.Log("Shade save data deleted."); } } public void DeleteAllSaveData() { //DeleteBenchData(); DeletePlayerData(); DeleteShadeData(); }
}
July 15, 2024 at 1:18 pm #15283::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; private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject ); } else { Instance = this; } SaveData.Instance.SaveShadeData(); } protected override void Start() { base.Start(); ChangeState(EnemyStates.Shade_Idle); } protected override void Update() { base.Update(); if (!Player.Instance.pState.alive) { ChangeState(EnemyStates.Shade_Idle); } } protected override void UpdateEnemyStates() { float _dist = Vector2.Distance(transform.position, Player.Instance.transform.position); switch (GetCurrentEnemyState) { case EnemyStates.Shade_Idle: //rb.velocity = new Vector2(0, 0); if (_dist < chaseDistance) { ChangeState(EnemyStates.Shade_Chase); } break; case EnemyStates.Shade_Chase: rb.MovePosition(Vector2.MoveTowards(transform.position, Player.Instance.transform.position, Time.deltaTime * speed)); FlipShade(); if (_dist > chaseDistance) { ChangeState(EnemyStates.Shade_Idle); } break; case EnemyStates.Shade_Stunned: timer += Time.deltaTime; if (timer > stunDuration) { ChangeState(EnemyStates.Shade_Idle); timer = 0; } break; case EnemyStates.Shade_Death: Death(Random.Range(1, 3)); break; } } public override void EnemyHit(float _dameDone, Vector2 _hitDirection, float _hitForce) { base.EnemyHit(_dameDone, _hitDirection, _hitForce); if (health > 0) { ChangeState(EnemyStates.Shade_Stunned); } else { ChangeState(EnemyStates.Shade_Death); } } protected override void Death(float _destroyTime) { rb.gravityScale = 12; base.Death(_destroyTime); } protected override void ChangeCurrentAnimation() { if(GetCurrentEnemyState == EnemyStates.Shade_Idle) { anim.Play("Idle"); } anim.SetBool("Walking", GetCurrentEnemyState == EnemyStates.Shade_Chase); if (GetCurrentEnemyState == EnemyStates.Shade_Death) { Player.Instance.RestoreMana(); SaveData.Instance.SavePlayerData(); anim.SetTrigger("Death"); Destroy(gameObject, 0.5f); } } protected override void Attack() { anim.SetTrigger("Attacking"); Player.Instance.TakeDamage(damage); } void FlipShade() { sr.flipX = Player.Instance.transform.position.x < transform.position.x; }
}
July 15, 2024 at 1:19 pm #15284::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] Bench bench; public static GameManager Instance { get; private set; }
public GameObject shade; private void Awake() { SaveData.Instance.Initialize(); if (Instance != null && Instance != this) { Destroy(gameObject); } else { Instance = this; } if(Player.Instance != null) { if (Player.Instance.halfMana) { SaveData.Instance.LoadShadeData(); if(SaveData.Instance.sceneWithShade ==SceneManager.GetActiveScene().name || SaveData.Instance.sceneWithShade == "") { Instantiate(shade, SaveData.Instance.shadePos, SaveData.Instance.shadeRot); } } } SaveScene(); DontDestroyOnLoad(gameObject); bench = FindObjectOfType<Bench>(); /* used for del all the temp save*/ //SaveData.Instance.DeleteAllSaveData(); } private void Update() { if (Input.GetKeyDown(KeyCode.O)) { SaveData.Instance.SavePlayerData(); } } public void SaveScene() { string currentSceneName = SceneManager.GetActiveScene().name; SaveData.Instance.sceneNames.Add(currentSceneName); } public void RespawnPlayer() { SaveData.Instance.LoadBench(); if(SaveData.Instance.benchSceneName != null)// load the bench's scene if exist { SceneManager.LoadScene(SaveData.Instance.benchSceneName); } if(SaveData.Instance.benchPos != null)// set the respawn to the bench's pos { respawnPoint = SaveData.Instance.benchPos; } else { respawnPoint = platFormingRespawnPoint; } Player.Instance.transform.position = respawnPoint; StartCoroutine(UIManager.Instance.DeactiveDeathSceen()); Player.Instance.Respawned(); }
}
July 15, 2024 at 2:48 pm #15287::Taking a quick look at your code, & testing the same code you used on my end, everything seems to work. I believe the issue lies within the Unity settings setup.
So let’s take a look at two outstanding issues:
- The Shade cannot be hit or touched despite having a non-trigger box collider. [And I assume it has an attack animation which it does not perform]
For this, I could only wager there’s something on with your Layer Collision Matrix. Although, on further thought it wouldn’t make sense considering the other enemies should be on the same layer.
Regardless, could you show this to me. Edit > Project Settings > Physics2D > Layer Collision Matrix. And can you also show me what your player Layer and Tag is.
- The Shade cannot move, despite it’s states changing.
The code works on my end, but this doesn’t make sense why it doesn’t on yours. Furthermore, we can also see the Shade states changing as it goes to idle and back to chasing when you are close enough to it. We can also see that it turns around to see you, so the
rb.velocity
line is functioning to allowFlipShade()
.I would like you to check this by moving the
rb.velocity
code in chase state to theUpdate()
method. This will check if the Rigidbody2D can be manipulated or not, and if it can, which variable is the issue in moving the Shade.protected override void Update() { base.Update(); if (!Player.Instance.pState.alive) { ChangeState(EnemyStates.Shade_Idle); } rb.MovePosition(Vector2.MoveTowards(transform.position, Player.Instance.transform.position, Time.deltaTime * speed)); } protected override void UpdateEnemyStates() { float _dist = Vector2.Distance(transform.position, Player.Instance.transform.position); switch (GetCurrentEnemyState) { ... case EnemyStates.Shade_Chase: //rb.MovePosition(Vector2.MoveTowards(transform.position, Player.Instance.transform.position, Time.deltaTime * speed)); FlipShade(); if (_dist > chaseDistance) { ChangeState(EnemyStates.Shade_Idle); } break; ... } }
If the Shade is still not moving, then swap the target parameter
Player.Instance.transform.position
withnew Vector2(100, 0)
. This should force the shade to run to the right until it reaches the coordinates. If this works, then something is wrong with the Player’s position.
This is one confusing situation so we can only try to see what isn’t the problem.
If none of these are consequential, i’d suggest you try removing and readding fresh, unchanged, RigidBody2D & Box Collider 2D components to the Shade to see if it does anything.
By the way, the enemies respawning after you killed them is not a problem, it’s a feature/aspect that remains unchanged. They respawn because we did not code saved data for regular enemies in scenes, like in Hollow Knight, where enemies will respawn as you re-enter the room.
July 15, 2024 at 10:14 pm #15289::I try removing and readding fresh, unchanged, RigidBody2D & Box Collider 2D components to the Shade and it work, my shade is working normaly. Thank you sm! and i have another question, when my player comeback from scene_2 and interact with bench but not respawn in bench (it work fine in scene_1)
View post on imgur.com
July 16, 2024 at 3:00 pm #15291::Here’s my thought process on what is causing this issue:
The only reason you would spawn from a transition portal is due to the GameManager’s string variable
transitionedFromScene
.This is used by SceneTransition.cs to find which scene you came from to put you at the right position if you enter a scene with multiple instances of SceneTransition.cs. This will function as it’s in the
Start()
function, meaning it will activate when you enter a scene as the game object is then created and called on start of the scene.The string variable
transitionedFromScene
is also not refreshed when respawning, so it will stay the same as the scene you came from. Thus, when you respawn, you will respawn at the bench but you reloaded the scene, causing the SceneTransition.cs to take the string from GameManager.cs and respawn you at the transition point.
So what we will need to do is to clear the
transitionedFromScene
variable or replace it with the current scene.We should be able to do this using the
RespawnPlayer()
method, which is called on pressing the respawn button.At the top of the method, just put
transitionedFromScene = SceneManager.GetActiveScene().name;
to change the scene to the current scene everytime you choose to respawn.This should cause no issue as the
transitionedFromScene
will always be changed anyways before you leave a scene, upon interaction with a SceneTransition.csJuly 17, 2024 at 9:31 pm #15299::im adding transitionedFromScene = SceneManager.GetActiveScene().name; at top of RespawnPlayer() funcion but it only work when player die and respawn in scene_1, player respawn at the transition of scene_1 when player die in scene_2
July 17, 2024 at 9:36 pm #15300::Do you have a bench in Scene 2 that you have interacted with? Because if not, you will always respawn at Scene 1’s Bench since it’s the only bench you have interacted with before.
The bench is the save point and where you respawn*
July 17, 2024 at 9:56 pm #15301::i dont have bench in scene_2, what i mean is when i die from scene_2, player respawn at the transittion of scene_1 but not the bench
July 17, 2024 at 9:57 pm #15302July 17, 2024 at 11:41 pm #15303::Ah sorry, I understand why. The reason this happens is because the code i gave you would take the current scene the player died in and set it as the string value. this doesn’t mean it will set the string value to your respawn.
So let’s try this instead:
public void RespawnPlayer() {
transitionedFromScene = SceneManager.GetActiveScene().name;SaveData.Instance.LoadBench(); if (SaveData.Instance.benchSceneName != null) //load the bench's scene if it exists. { transitionedFromScene = SaveData.Instance.benchSceneName; SceneManager.LoadScene(SaveData.Instance.benchSceneName); } ... }This should get the bench scene that you need to teleport to directly if it exists when respawning.
July 18, 2024 at 7:34 pm #15323 -
AuthorPosts
- You must be logged in to reply to this topic.
Advertisement below: