fix: edit a level (#66)

Co-authored-by: Djelal BOUDJI <djelal@gmail.com>
Co-authored-by: Théo LUDWIG <contact@theoludwig.fr>
This commit is contained in:
M VINCENT PETT 2025-05-16 20:03:37 +02:00 committed by GitHub
parent 0b32ce7036
commit 728c57c66c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 209 additions and 66 deletions

View File

@ -1,7 +1,4 @@
using System.IO;
using UnityEngine; using UnityEngine;
using UnityEngine.SceneManagement;
public class NormalGameMode : IGameMode public class NormalGameMode : IGameMode
{ {
@ -18,7 +15,6 @@ public class NormalGameMode : IGameMode
if (player.IsColliding && Input.GetKey(JumpKey) && !isRotating) if (player.IsColliding && Input.GetKey(JumpKey) && !isRotating)
{ {
Debug.Log("Player is Jumping");
Jump(player); Jump(player);
} }

View File

@ -51,6 +51,10 @@ public class LevelEditor : MonoBehaviour
HandleBlockDeletion(); HandleBlockDeletion();
} }
void OnDestroy()
{
ClearEditor();
}
#region UI #region UI
void LoadPrefabs() void LoadPrefabs()
@ -176,7 +180,6 @@ public class LevelEditor : MonoBehaviour
foreach (var h in hits) foreach (var h in hits)
{ {
if (h == col) continue; if (h == col) continue;
if (h.CompareTag("Ground")) continue;
if (h.transform.IsChildOf(currentBlock.transform)) continue; if (h.transform.IsChildOf(currentBlock.transform)) continue;
return false; return false;
} }
@ -198,7 +201,7 @@ public class LevelEditor : MonoBehaviour
currentBlock = sel; currentBlock = sel;
isPlacingBlock = true; isPlacingBlock = true;
currentScale = currentBlock.transform.localScale; currentScale = currentBlock.transform.localScale;
Debug.Log($"🟢 Sélection : {sel.name}"); Debug.Log($"Sélection : {sel.name}");
} }
} }
void PlaceBlock() void PlaceBlock()
@ -211,7 +214,7 @@ public class LevelEditor : MonoBehaviour
// 1) Bloquer si on perçoit un spike de même type dans la direction de snap // 1) Bloquer si on perçoit un spike de même type dans la direction de snap
if (IsBlockedBySameTypeInSnapDirection()) if (IsBlockedBySameTypeInSnapDirection())
{ {
Debug.LogError("Impossible de poser un spike sur un autre spike !"); Debug.LogError("Impossible de poser un spike sur un autre spike !");
Destroy(currentBlock); Destroy(currentBlock);
} }
else else
@ -219,7 +222,7 @@ public class LevelEditor : MonoBehaviour
// 2) On snap dans la direction (down/left/up/right), et on détruit si aucun support // 2) On snap dans la direction (down/left/up/right), et on détruit si aucun support
if (!SnapSpikeByRotation()) if (!SnapSpikeByRotation())
{ {
Debug.LogError("Impossible de poser un spike dans le vide !"); Debug.LogError("Impossible de poser un spike dans le vide !");
Destroy(currentBlock); Destroy(currentBlock);
} }
else else
@ -370,7 +373,7 @@ public class LevelEditor : MonoBehaviour
else if (dir == Vector2.right) p.x = bestHit.point.x - hw; else if (dir == Vector2.right) p.x = bestHit.point.x - hw;
currentBlock.transform.position = new Vector3(p.x, p.y, -1f); currentBlock.transform.position = new Vector3(p.x, p.y, -1f);
Debug.Log($"📌 Spike snapé {dir} sur « {bestHit.collider.name} » à {currentBlock.transform.position}"); Debug.Log($"Spike snapé {dir} sur « {bestHit.collider.name} » à {currentBlock.transform.position}");
return true; return true;
} }
@ -403,7 +406,7 @@ public class LevelEditor : MonoBehaviour
? ResizeAxis.Horizontal ? ResizeAxis.Horizontal
: ResizeAxis.Vertical; : ResizeAxis.Vertical;
isResizing = true; isResizing = true;
Debug.Log($"🧰 Début redim {tgt.name} (axe {currentResizeAxis})"); Debug.Log($"Début redim {tgt.name} (axe {currentResizeAxis})");
} }
void PerformResizing() void PerformResizing()
@ -419,14 +422,14 @@ public class LevelEditor : MonoBehaviour
if (IsOverlapping(resizingTarget)) if (IsOverlapping(resizingTarget))
{ {
resizingTarget.transform.localScale = originalScale; resizingTarget.transform.localScale = originalScale;
Debug.Log("Redim annulé : collision"); Debug.Log("Redim annulé : collision");
} }
if (Input.GetMouseButtonUp(0)) if (Input.GetMouseButtonUp(0))
{ {
isResizing = false; isResizing = false;
resizingTarget = null; resizingTarget = null;
currentResizeAxis = ResizeAxis.None; currentResizeAxis = ResizeAxis.None;
Debug.Log("Fin redim"); Debug.Log("Fin redim");
} }
} }
@ -452,7 +455,7 @@ public class LevelEditor : MonoBehaviour
toD = toD.transform.parent.gameObject; toD = toD.transform.parent.gameObject;
if (toD == currentBlock) { currentBlock = null; isPlacingBlock = false; } if (toD == currentBlock) { currentBlock = null; isPlacingBlock = false; }
Destroy(toD); Destroy(toD);
Debug.Log($"🗑️ Supprimé {toD.name}"); Debug.Log($"Supprimé {toD.name}");
} }
} }
@ -470,45 +473,88 @@ public class LevelEditor : MonoBehaviour
var col = currentBlock.GetComponent<Collider2D>(); var col = currentBlock.GetComponent<Collider2D>();
var b = col.bounds; var b = col.bounds;
float snapDistance = 1f; float snapDistance = 1f;
float verticalEps = 0.05f; // petite marge pour exclure trop hauts ou trop bas float verticalEps = 0.1f;
// Taille et positions des deux zones de recherche latérales // === SNAP HORIZONTAL (droite)
Vector2 boxSize = new Vector2(snapDistance, b.size.y - verticalEps * 2f); Vector2 hBoxSize = new Vector2(snapDistance, b.size.y - verticalEps * 2f);
// à droite
Vector2 rightCenter = new Vector2(b.max.x + snapDistance / 2f, b.center.y); Vector2 rightCenter = new Vector2(b.max.x + snapDistance / 2f, b.center.y);
// à gauche var hits = Physics2D.OverlapBoxAll(rightCenter, hBoxSize, 0f);
Vector2 leftCenter = new Vector2(b.min.x - snapDistance / 2f, b.center.y);
// Cherche à droite
var hits = Physics2D.OverlapBoxAll(rightCenter, boxSize, 0f);
foreach (var h in hits) foreach (var h in hits)
{ {
if (h != null && h.gameObject != currentBlock && !h.isTrigger) if (IsInvalidSnapTarget(h)) continue;
{
float newX = h.bounds.min.x - b.extents.x; float newX = h.bounds.min.x - b.extents.x;
currentBlock.transform.position = new Vector3(newX, currentBlock.transform.position.y, -1f); currentBlock.transform.position = new Vector3(newX, currentBlock.transform.position.y, -1f);
Debug.Log($"↔️ Snap horizontal à droite contre {h.name}"); Debug.Log($"Snap horizontal à droite contre {h.name}");
return;
}
// === SNAP HORIZONTAL (gauche)
Vector2 leftCenter = new Vector2(b.min.x - snapDistance / 2f, b.center.y);
hits = Physics2D.OverlapBoxAll(leftCenter, hBoxSize, 0f);
foreach (var h in hits)
{
if (IsInvalidSnapTarget(h)) continue;
float newX = h.bounds.max.x + b.extents.x;
currentBlock.transform.position = new Vector3(newX, currentBlock.transform.position.y, -1f);
Debug.Log($"Snap horizontal à gauche contre {h.name}");
return;
}
// === SNAP VERTICAL (dessous)
Vector2 downBoxSize = new Vector2(b.size.x - 0.1f, snapDistance);
Vector2 downCenter = new Vector2(b.center.x, b.min.y - snapDistance / 2f);
hits = Physics2D.OverlapBoxAll(downCenter, downBoxSize, 0f);
foreach (var h in hits)
{
if (IsInvalidSnapTarget(h)) continue;
float newY = h.bounds.max.y + b.extents.y;
currentBlock.transform.position = new Vector3(currentBlock.transform.position.x, newY, -1f);
Debug.Log($"Snap vertical (bas) contre {h.name}");
return;
}
// === SNAP VERTICAL (au-dessus)
Vector2 upCenter = new Vector2(b.center.x, b.max.y + snapDistance / 2f);
hits = Physics2D.OverlapBoxAll(upCenter, downBoxSize, 0f);
foreach (var h in hits)
{
if (IsInvalidSnapTarget(h)) continue;
float newY = h.bounds.min.y - b.extents.y;
currentBlock.transform.position = new Vector3(currentBlock.transform.position.x, newY, -1f);
Debug.Log($"Snap vertical (haut) contre {h.name}");
return; return;
} }
} }
// Cherche à gauche bool IsInvalidSnapTarget(Collider2D h)
hits = Physics2D.OverlapBoxAll(leftCenter, boxSize, 0f);
foreach (var h in hits)
{ {
if (h != null && h.gameObject != currentBlock && !h.isTrigger) if (h == null || h.gameObject == currentBlock || h.isTrigger) return true;
var t = h.transform;
if (t.parent != null && t.parent.name.Contains("ObstacleBlock"))
{ {
float newX = h.bounds.max.x + b.extents.x; if (t.name.Contains("ObstacleKiller") || t.name.Contains("ObstacleSafer"))
currentBlock.transform.position = new Vector3(newX, currentBlock.transform.position.y, -1f); return true;
Debug.Log($"↔️ Snap horizontal à gauche contre {h.name}");
return;
} }
return false;
} }
// Filtrage des enfants parasites
bool IsChildOfObstacleBlock(Collider2D col)
{
var t = col.transform;
if (t.parent == null) return false;
bool isNamedObstacleChild = t.name.Contains("ObstacleSafer") || t.name.Contains("ObstacleKiller");
bool parentIsBlock = t.parent.name.Contains("ObstacleBlock");
return isNamedObstacleChild && parentIsBlock;
} }
void HandleBlockRotation() void HandleBlockRotation()
{ {
currentBlock.transform.Rotate(0, 0, -90f); currentBlock.transform.Rotate(0, 0, -90f);
Debug.Log("🔄 Rotation 90°!"); Debug.Log("Rotation 90°!");
} }
void InstantiateAndPrepare(GameObject prefab, Vector3? scaleOverride = null) void InstantiateAndPrepare(GameObject prefab, Vector3? scaleOverride = null)
@ -523,8 +569,25 @@ public class LevelEditor : MonoBehaviour
public void Save() public void Save()
{ {
// TODO
} }
#endregion #endregion
public void ClearEditor()
{
if (persistentBlockContainer == null) return;
foreach (Transform child in persistentBlockContainer)
{
Destroy(child.gameObject);
}
Debug.Log("Éditeur vidé.");
currentBlock = null;
isPlacingBlock = false;
currentPage = 0;
ClearCurrentButtons();
GenerateButtons();
}
} }

View File

@ -13,7 +13,12 @@ public class LevelLoader : MonoBehaviour
private GameObject GetPrefab(string type) private GameObject GetPrefab(string type)
{ {
return Resources.Load<GameObject>("Prefabs/" + type); var prefab = Resources.Load<GameObject>("Prefabs/" + type);
if (prefab == null)
{
Debug.LogError($"Prefab introuvable pour : {type}");
}
return prefab;
} }
private void LoadAudio() private void LoadAudio()
@ -28,29 +33,95 @@ public class LevelLoader : MonoBehaviour
{ {
musicSource.volume = 1f; musicSource.volume = 1f;
} }
musicSource.Play(); musicSource.Play();
} }
private void LoadElements() private void LoadElements()
{ {
Level current = levelsLoader.levelCurrent; Level current = levelsLoader.levelCurrent;
foreach (var element in current.elements) foreach (var element in current.elements)
{ {
GameObject prefab = GetPrefab(element.type); GameObject prefab = GetPrefab(element.type);
GameObject instance = Instantiate(prefab, new Vector3(element.x, element.y, 0), Quaternion.identity); if (prefab == null) continue;
if (prefab.CompareTag("Kill")) GameObject instance = Instantiate(
prefab,
new Vector3(element.x, element.y, 0),
Quaternion.identity
);
if (editMode)
{ {
Instantiate(Resources.Load<GameObject>("AICollider"), new Vector3(element.x - 1, element.y, 0), Quaternion.identity); foreach (Transform child in instance.transform)
{
if (child.name.Contains("ObstacleKiller"))
{
var col = child.GetComponent<BoxCollider2D>();
if (col != null && col.size.y > 2f) // Trop grand
{
Debug.LogWarning($"⚠️ Collider {child.name} trop grand, réduction appliquée.");
col.size = new Vector2(col.size.x, 1f);
col.offset = new Vector2(col.offset.x, -2f); // Ajuste selon ton design
}
}
}
} }
// En mode jeu/test uniquement → ajout du AICollider
if (!editMode)
{
if (prefab.CompareTag("Kill"))
{
Instantiate(
Resources.Load<GameObject>("AICollider"),
new Vector3(element.x - 1, element.y, 0),
Quaternion.identity
);
}
}
// Appliquer l'échelle personnalisée
Vector3 originalScale = instance.transform.localScale; Vector3 originalScale = instance.transform.localScale;
float newScaleX = element.scaleX > 0 ? element.scaleX : originalScale.x; float newScaleX = element.scaleX > 0 ? element.scaleX : originalScale.x;
float newScaleY = element.scaleY > 0 ? element.scaleY : originalScale.y; float newScaleY = element.scaleY > 0 ? element.scaleY : originalScale.y;
instance.transform.localScale = new Vector3(newScaleX, newScaleY, originalScale.z); instance.transform.localScale = new Vector3(newScaleX, newScaleY, originalScale.z);
} }
// Sol uniquement en mode jeu
if (!editMode)
{
GameObject groundPrefab = GetPrefab("Ground");
if (groundPrefab != null)
{
GameObject groundInstance = Instantiate(
groundPrefab,
new Vector3(current.LastX / 2, groundY, 0),
Quaternion.identity
);
groundInstance.transform.localScale = new Vector3(current.LastX / 5f * 2, 1, 1);
}
}
// Mur de fin toujours placé
GameObject winWall = GetPrefab("WinnerWall");
if (winWall != null)
{
Instantiate(
winWall,
new Vector3(current.LastX, 0, 0),
Quaternion.Euler(0, 0, 90)
);
}
}
private void Awake()
{
levelsLoader = GameObject.FindGameObjectWithTag("LevelsLoader").GetComponent<LevelsLoader>();
Level current = levelsLoader.levelCurrent;
createMode = PlayerPrefs.GetInt("CreateMode", 0) == 1;
if (!editMode) if (!editMode)
{ {
GameObject groundPrefab = GetPrefab("Ground"); GameObject groundPrefab = GetPrefab("Ground");
@ -63,10 +134,12 @@ public class LevelLoader : MonoBehaviour
public void Start() public void Start()
{ {
createMode = PlayerPrefs.GetInt("CreateMode", 0) == 1;
if (!createMode) if (!createMode)
{ {
levelsLoader = GameObject.FindGameObjectWithTag("LevelsLoader").GetComponent<LevelsLoader>(); levelsLoader = GameObject
.FindGameObjectWithTag("LevelsLoader")
.GetComponent<LevelsLoader>();
levelsLoader.IncreaseTotalAttempts(); levelsLoader.IncreaseTotalAttempts();
LoadElements(); LoadElements();
@ -79,8 +152,7 @@ public class LevelLoader : MonoBehaviour
{ {
if (!editMode) if (!editMode)
{ {
Level current = levelsLoader.levelCurrent; progressionText.text = levelsLoader.levelCurrent.ProgressionPercent + "%";
progressionText.text = current.ProgressionPercent + "%";
} }
} }
} }

View File

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 588b3f9ce673f764b8a3a81842ed9f46
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -91,22 +91,25 @@ public class Player : MonoBehaviour
{ {
CurrentGameMode?.OnCollisionEnter(this, collision); CurrentGameMode?.OnCollisionEnter(this, collision);
if (collision.gameObject.CompareTag("Kill")) if (editMode && (collision.gameObject.CompareTag("Kill") || collision.gameObject.CompareTag("Win")))
{
if (editMode)
{ {
GameObject spawn = new GameObject("AutoSpawnPoint"); GameObject spawn = new GameObject("AutoSpawnPoint");
spawn.transform.position = new Vector3(-16, -3, 0f); spawn.transform.position = new Vector3(-16, -3, 0f);
transform.position = spawn.transform.position; transform.position = spawn.transform.position;
RigidBody.linearVelocity = Vector2.zero; RigidBody.linearVelocity = Vector2.zero;
SpeedMultiplier = 1f; SpeedMultiplier = 1f;
CurrentGameMode = new NormalGameMode();
SpriteRenderer.sprite = Resources.Load<Sprite>("Shapes/BaseSquare");
return;
} }
else
if (collision.gameObject.CompareTag("Kill"))
{ {
sfxSource.clip = Resources.Load<AudioClip>(Path.Combine("Sounds", "death")); sfxSource.clip = Resources.Load<AudioClip>(Path.Combine("Sounds", "death"));
sfxSource.Play(); sfxSource.Play();
StartCoroutine(LevelHomeButton.PlaySoundAndLoadScene(sfxSource, SceneManager.GetActiveScene().name)); StartCoroutine(LevelHomeButton.PlaySoundAndLoadScene(sfxSource, SceneManager.GetActiveScene().name));
}
} }
if (collision.gameObject.CompareTag("Win")) if (collision.gameObject.CompareTag("Win"))

View File

@ -76,6 +76,8 @@ public class TestManager : MonoBehaviour
} }
currentPlayer.transform.position = spawnPoint.position; currentPlayer.transform.position = spawnPoint.position;
currentPlayer.transform.rotation = Quaternion.Euler(0f, 0f, 0f);
currentPlayer.RigidBody.freezeRotation = true;
currentPlayer.RigidBody.linearVelocity = Vector2.zero; currentPlayer.RigidBody.linearVelocity = Vector2.zero;
currentPlayer.SpeedMultiplier = 1f; currentPlayer.SpeedMultiplier = 1f;
// currentPlayer.SpriteRenderer.sprite = Resources.Load<Sprite>("Shapes/BaseSquare"); // currentPlayer.SpriteRenderer.sprite = Resources.Load<Sprite>("Shapes/BaseSquare");
@ -93,7 +95,7 @@ public class TestManager : MonoBehaviour
currentPlayer.SpriteRenderer.enabled = true; currentPlayer.SpriteRenderer.enabled = true;
if (currentPlayer.Particle != null) if (currentPlayer.Particle != null)
currentPlayer.Particle.Play(); // Démarrer la particule currentPlayer.Particle.Play(); // Démarrer la particule
Debug.Log("[TestManager] Test du niveau démarré !"); Debug.Log("[TestManager] Test du niveau démarré !");
} }
@ -110,7 +112,7 @@ public class TestManager : MonoBehaviour
currentPlayer.SpeedMultiplier = 0f; currentPlayer.SpeedMultiplier = 0f;
if (currentPlayer.Particle != null) if (currentPlayer.Particle != null)
currentPlayer.Particle.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear); // Arrêter proprement currentPlayer.Particle.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear); // Arrêter proprement
if (currentPlayer.SpriteRenderer != null) if (currentPlayer.SpriteRenderer != null)
currentPlayer.SpriteRenderer.enabled = false; currentPlayer.SpriteRenderer.enabled = false;