Merge branch 'develop' of github.com:boudji-ludwig-pett/cnam-geometry-dash into map-editor-design

This commit is contained in:
Vincent PETT
2025-03-31 17:27:56 +02:00
79 changed files with 7276 additions and 3159 deletions

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 86a7976eac8daf648837e934393ac7ba
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
using UnityEngine;
public interface IGameMode
{
void Update(Player player);
void OnCollisionEnter(Player player, Collision2D collision);
void OnCollisionExit(Player player, Collision2D collision);
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 68e961c0b929a2a4d9e92b2f2bf9e197

View File

@ -0,0 +1,78 @@
using UnityEngine;
using UnityEngine.SceneManagement;
public class NormalGameMode : IGameMode
{
private const float HorizontalSpeed = 8.6f;
private const float JumpForce = 26.6581f;
private const KeyCode JumpKey = KeyCode.Space;
public void Update(Player player)
{
player.RigidBody.linearVelocity = new Vector2(HorizontalSpeed, player.RigidBody.linearVelocity.y);
if (player.HasStarted && player.IsColliding && Input.GetKey(JumpKey) && player.CanJump)
{
Jump(player);
}
if (!IsJumping(player))
{
AlignRotation(player);
player.Particle.gameObject.SetActive(true);
}
else
{
player.Particle.gameObject.SetActive(false);
player.Transform.Rotate(Vector3.back * 360 * Time.deltaTime);
}
UpdateParticlePositionAndRotation(player);
}
private void Jump(Player player)
{
player.RigidBody.linearVelocity = new Vector2(player.RigidBody.linearVelocity.x, 0);
player.RigidBody.AddForce(Vector2.up * JumpForce, ForceMode2D.Impulse);
player.LevelsLoader.IncreaseTotalJumps();
}
private bool IsJumping(Player player)
{
return !player.IsColliding;
}
private void AlignRotation(Player player)
{
Vector3 rotation = player.Transform.rotation.eulerAngles;
rotation.z = Mathf.Round(rotation.z / 90) * 90;
player.Transform.rotation = Quaternion.Euler(rotation);
}
private void UpdateParticlePositionAndRotation(Player player)
{
player.Particle.transform.position = player.Transform.position + new Vector3(-0.19f, -0.64f, -10);
player.Particle.transform.rotation = Quaternion.Euler(0, 0, 150.464f);
}
public void OnCollisionEnter(Player player, Collision2D collision)
{
player.IsColliding = true;
player.CanJump = true;
if (collision.gameObject.CompareTag("Kill"))
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
if (collision.gameObject.CompareTag("Win"))
{
SceneManager.LoadScene("SelectLevelScene");
}
}
public void OnCollisionExit(Player player, Collision2D collision)
{
player.IsColliding = false;
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 222f727842e308847a028d3fce55d364

View File

@ -0,0 +1,67 @@
using UnityEngine;
using UnityEngine.SceneManagement;
public class ShipGameMode : IGameMode
{
private const float HorizontalSpeed = 8.6f;
private const float JumpForce = 26.6581f;
private const KeyCode JumpKey = KeyCode.Space;
private const float UpperAngle = 45f;
private const float LowerAngle = -45f;
private const float RotationLerpSpeed = 5f;
public void Update(Player player)
{
player.RigidBody.linearVelocity = new Vector2(HorizontalSpeed, player.RigidBody.linearVelocity.y);
if (player.HasStarted && Input.GetKey(JumpKey))
{
Jump(player);
}
float targetAngle = Input.GetKey(JumpKey) ? UpperAngle : LowerAngle;
float currentAngle = player.Transform.rotation.eulerAngles.z;
if (currentAngle > 180f)
currentAngle -= 360f;
float newAngle = Mathf.Lerp(currentAngle, targetAngle, RotationLerpSpeed * Time.deltaTime);
player.Transform.rotation = Quaternion.Euler(0, 0, newAngle);
UpdateParticlePositionAndRotation(player);
}
private void UpdateParticlePositionAndRotation(Player player)
{
player.Particle.transform.position = player.Transform.position + new Vector3(-0.19f, -0.64f, -10);
player.Particle.transform.rotation = Quaternion.Euler(0, 0, 150.464f);
}
private void Jump(Player player)
{
player.RigidBody.linearVelocity = new Vector2(player.RigidBody.linearVelocity.x, 0);
player.RigidBody.AddForce(Vector2.up * JumpForce, ForceMode2D.Impulse);
player.LevelsLoader.IncreaseTotalJumps();
}
public void OnCollisionEnter(Player player, Collision2D collision)
{
if (collision.gameObject.CompareTag("Kill"))
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
return;
}
if (collision.gameObject.CompareTag("Win"))
{
SceneManager.LoadScene("HomeScene");
return;
}
float currentAngle = player.Transform.rotation.eulerAngles.z;
float shortestAngle = Mathf.DeltaAngle(currentAngle, 0);
player.Transform.rotation = Quaternion.RotateTowards(player.Transform.rotation, Quaternion.Euler(0, 0, 0), Mathf.Abs(shortestAngle));
}
public void OnCollisionExit(Player player, Collision2D collision)
{
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 28b5f8f9214141740af5157d6b421677

View File

@ -0,0 +1,128 @@
using UnityEngine;
using System.IO;
using System.Collections;
using SimpleFileBrowser;
using UnityEngine.SceneManagement;
using TMPro;
public class JSONImporter : MonoBehaviour
{
public TMP_Text statusText;
private void Awake()
{
if (statusText == null)
{
GameObject statusObj = GameObject.Find("StatusText");
if (statusObj != null)
{
statusText = statusObj.GetComponent<TMP_Text>();
if (statusText != null)
{
Debug.Log("✅ StatusText found and assigned automatically!");
}
else
{
Debug.LogError("⚠️ 'StatusText' was found but does not have a TMP_Text component!");
}
}
else
{
Debug.LogError("⚠️ No GameObject named 'StatusText' found in the scene. Please create a TextMeshPro element and name it 'StatusText'.");
}
}
}
private void Start()
{
if (statusText != null)
{
statusText.text = "Ready to import...";
statusText.color = Color.white;
}
else
{
Debug.LogError("statusText is not assigned!");
}
}
public void ImportJSON()
{
Debug.Log("Button clicked, starting import...");
if (statusText != null)
{
statusText.text = "Importing...";
statusText.color = Color.yellow;
}
StartCoroutine(ShowFileBrowser());
}
private IEnumerator ShowFileBrowser()
{
yield return FileBrowser.WaitForLoadDialog(FileBrowser.PickMode.Files, false, null, null, "Select JSON File", "Load");
if (FileBrowser.Success)
{
string sourcePath = FileBrowser.Result[0];
if (Path.GetExtension(sourcePath).ToLower() != ".json")
{
UpdateStatus("Invalid file. Please select a JSON file.", Color.red);
yield break;
}
string fileName = Path.GetFileName(sourcePath);
string destinationPath = Path.Combine(Application.dataPath, "Resources/Levels", fileName);
bool success = false;
try
{
File.Copy(sourcePath, destinationPath, true);
success = true;
}
catch (IOException e)
{
Debug.LogError("Error copying file: " + e.Message);
}
if (success)
{
UpdateStatus("Import successful!", Color.green);
}
else
{
UpdateStatus("Import error.", Color.red);
}
#if UNITY_EDITOR
UnityEditor.AssetDatabase.Refresh();
#endif
LevelsLoader loader = Object.FindAnyObjectByType<LevelsLoader>();
if (loader != null)
{
loader.RefreshLevels();
}
}
else
{
UpdateStatus("No file selected.", Color.red);
}
yield return null;
}
private void UpdateStatus(string message, Color color)
{
if (statusText != null)
{
statusText.text = message;
statusText.color = color;
statusText.gameObject.SetActive(false);
statusText.gameObject.SetActive(true);
Canvas.ForceUpdateCanvases();
}
else
{
Debug.LogError("statusText is NULL!");
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: baf97ea8555b6214299a38be9fe1724f

View File

@ -1,16 +1,48 @@
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
[System.Serializable]
public class LevelElement
{
public string type;
public float x;
public float y;
public float scaleX = -1;
public float scaleY = -1;
}
[System.Serializable]
public class Level
{
public static readonly int LAST_X = 15;
public string JsonName { get; set; }
public int TotalJumps { get; set; }
public int TotalAttempts { get; set; }
public int ProgressionPercent { get; set; }
public int ProgressionPercentMax { get; set; }
public string name;
public string musicName;
public int totalJumps;
public int totalAttempts;
public int order;
public List<LevelElement> elements;
public float LastX
{
get
{
LevelElement lastElement = elements[^1];
float lastX = LAST_X;
if (lastElement != null)
{
lastX += lastElement.x;
}
return lastX;
}
}
public static Level CreateFromJSON(string jsonString)
{
return JsonUtility.FromJson<Level>(jsonString);

View File

@ -1,31 +1,165 @@
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
public class LevelEditor : MonoBehaviour
{
public Transform mapParent; // Parent pour organiser les objets
private GameObject currentBlock; // Block en cours de placement
public Image blockButtonImage; // Image du bouton pour récupérer le sprite
[Header("Placement")]
public Transform mapParent;
private GameObject currentBlock;
private bool isPlacingBlock = false;
private Vector3 currentScale = new Vector3(1f, 1f, 1);
private float scaleStep = 0.1f;
private bool isPlacingBlock = false; // Indique si un block est en mode placement
private float scaleStep = 0.1f; // Incrément de redimensionnement avec la molette
private Vector3 currentScale = new Vector3(1f, 1f, 1); // Échelle actuelle appliquée au block
[Header("UI")]
public Transform blockGroupContainer;
public Transform portalGroupContainer;
public GameObject buttonPrefabTemplate;
private bool showingBlocks = true;
private int currentPage = 0;
private const int buttonsPerPage = 8;
private List<GameObject> blockPrefabs = new();
private List<GameObject> portalPrefabs = new();
private List<GameObject> currentButtons = new();
private GameObject resizingTarget = null;
private bool isResizing = false;
private Vector3 originalMousePos;
private Vector3 originalScale;
private enum ResizeAxis { None, Horizontal, Vertical }
private ResizeAxis currentResizeAxis = ResizeAxis.None;
void Start()
{
LoadPrefabs();
GenerateButtons();
}
void LoadPrefabs()
{
blockPrefabs.AddRange(Resources.LoadAll<GameObject>("Prefabs/Block"));
blockPrefabs.AddRange(Resources.LoadAll<GameObject>("Prefabs/Spike"));
portalPrefabs.AddRange(Resources.LoadAll<GameObject>("Prefabs/Portals"));
}
void GenerateButtons()
{
ClearCurrentButtons();
List<GameObject> source = showingBlocks ? blockPrefabs : portalPrefabs;
Transform container = showingBlocks ? blockGroupContainer : portalGroupContainer;
if (container == null || buttonPrefabTemplate == null)
{
Debug.LogError("UI Container ou prefab de bouton manquant.");
return;
}
int start = currentPage * buttonsPerPage;
int end = Mathf.Min(start + buttonsPerPage, source.Count);
for (int i = start; i < end; i++)
{
GameObject button = Instantiate(buttonPrefabTemplate, container);
button.SetActive(true);
Transform canvas = button.transform.Find("Canvas");
Transform bg = canvas?.Find("BlankSquare");
Transform icon = canvas?.Find("PrefabIcon");
if (bg == null || icon == null)
{
Destroy(button);
continue;
}
float xOffset = -375f + (i - start) * 125f;
bg.GetComponent<RectTransform>().anchoredPosition = new Vector2(xOffset, bg.GetComponent<RectTransform>().anchoredPosition.y);
icon.GetComponent<RectTransform>().anchoredPosition = new Vector2(xOffset, icon.GetComponent<RectTransform>().anchoredPosition.y);
Image bgImage = bg.GetComponent<Image>();
Image iconImage = icon.GetComponent<Image>();
bgImage.sprite = Resources.Load<Sprite>("InGame/ButtonSkin/BlankSquare");
iconImage.sprite = source[i].GetComponent<SpriteRenderer>()?.sprite;
string prefabName = source[i].name.ToLower();
if (prefabName.Contains("smallspike") || prefabName.Contains("smallobstacle"))
icon.GetComponent<RectTransform>().sizeDelta = new Vector2(50, 25);
else
icon.GetComponent<RectTransform>().sizeDelta = new Vector2(50, 50);
GameObject prefab = source[i];
button.GetComponent<Button>().onClick.AddListener(() => SelectPrefab(prefab));
currentButtons.Add(button);
}
}
void ClearCurrentButtons()
{
foreach (var button in currentButtons)
Destroy(button);
currentButtons.Clear();
}
public void ToggleButtonGroup()
{
showingBlocks = !showingBlocks;
currentPage = 0;
GenerateButtons();
}
public void NextPage()
{
int maxPage = Mathf.CeilToInt((showingBlocks ? blockPrefabs.Count : portalPrefabs.Count) / (float)buttonsPerPage);
if (currentPage < maxPage - 1)
{
currentPage++;
GenerateButtons();
}
}
public void PreviousPage()
{
if (currentPage > 0)
{
currentPage--;
GenerateButtons();
}
}
void SelectPrefab(GameObject prefab)
{
if (isPlacingBlock) return;
string name = prefab.name.ToLower();
if (name.Contains("portal"))
currentScale = new Vector3(0.5f, 0.5f, 1);
else if (name.Contains("small"))
currentScale = new Vector3(0.15f, 0.07f, 1);
else if (name.Contains("spike"))
currentScale = new Vector3(0.15f, 0.15f, 1);
else if (name.Contains("block"))
currentScale = new Vector3(0.2f, 0.2f, 1);
else
currentScale = new Vector3(1f, 1f, 1);
InstantiateAndPrepare(prefab, currentScale);
}
// Update is called once per frame
void Update()
{
// Déplacement du block sous la souris
if (isPlacingBlock && currentBlock != null)
{
Vector2 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
currentBlock.transform.position = new Vector3(Mathf.Round(mousePos.x), Mathf.Round(mousePos.y), -1); // Aligne sur une grille
currentBlock.transform.position = new Vector3(Mathf.Round(mousePos.x), Mathf.Round(mousePos.y), -1);
// Redimensionnement avec la molette sauf pour les portails
if (currentBlock.name != "ShipPortal" && currentBlock.name != "CubePortal")
if (!currentBlock.name.ToLower().Contains("portal"))
{
float scroll = Input.GetAxis("Mouse ScrollWheel");
if (scroll != 0)
@ -36,13 +170,15 @@ public class LevelEditor : MonoBehaviour
}
}
// Placer définitivement le block quand on clique
if (Input.GetMouseButtonDown(0))
{
// Vérifie les collisions avec d'autres objets
Collider2D[] overlaps = Physics2D.OverlapBoxAll(currentBlock.transform.position, currentBlock.GetComponent<Collider2D>().bounds.size, 0f);
Collider2D[] overlaps = Physics2D.OverlapBoxAll(
currentBlock.transform.position,
currentBlock.GetComponent<Collider2D>().bounds.size,
0f
);
if (overlaps.Length > 1) // >1 car le bloc en cours a déjà un collider
if (overlaps.Length > 1)
{
Debug.Log("Placement annulé : un objet est déjà présent à cet endroit.");
return;
@ -51,192 +187,81 @@ public class LevelEditor : MonoBehaviour
PlaceBlock();
}
}
}
public void AddBlock()
{
if (isPlacingBlock) return;
GameObject newBlock = new GameObject("Block");
SpriteRenderer spriteRenderer = newBlock.AddComponent<SpriteRenderer>();
// Sélection pour redimensionnement
if (Input.GetMouseButtonDown(0) && !isPlacingBlock)
{
Vector2 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Collider2D hit = Physics2D.OverlapPoint(mousePos);
if (blockButtonImage != null && blockButtonImage.sprite != null)
{
spriteRenderer.sprite = blockButtonImage.sprite;
}
else
{
spriteRenderer.sprite = Resources.Load<Sprite>("InGame/BlockSkin/RegularBlock01");
Debug.LogError("L'image du bouton de block n'est pas assignée !");
if (hit != null)
{
resizingTarget = hit.gameObject;
originalMousePos = mousePos;
originalScale = resizingTarget.transform.localScale;
Vector2 localClick = mousePos - (Vector2)resizingTarget.transform.position;
float ratio = resizingTarget.GetComponent<Collider2D>().bounds.size.x /
resizingTarget.GetComponent<Collider2D>().bounds.size.y;
currentResizeAxis = Mathf.Abs(localClick.x) > Mathf.Abs(localClick.y * ratio)
? ResizeAxis.Horizontal
: ResizeAxis.Vertical;
isResizing = true;
}
}
BoxCollider2D collider = newBlock.AddComponent<BoxCollider2D>();
collider.offset = Vector2.zero;
collider.size = spriteRenderer.sprite.bounds.size;
newBlock.transform.position = new Vector2(0, 0);
currentScale = new Vector3(1f, 1f, 1);
newBlock.transform.localScale = currentScale;
if (mapParent != null)
// Étirement en cours
if (isResizing && resizingTarget != null)
{
newBlock.transform.SetParent(mapParent);
}
Vector3 currentMousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector3 delta = currentMousePos - originalMousePos;
currentBlock = newBlock;
isPlacingBlock = true;
if (currentResizeAxis == ResizeAxis.Horizontal)
{
float newScaleX = Mathf.Max(0.1f, originalScale.x + delta.x);
resizingTarget.transform.localScale = new Vector3(newScaleX, originalScale.y, 1);
}
else if (currentResizeAxis == ResizeAxis.Vertical)
{
float newScaleY = Mathf.Max(0.1f, originalScale.y + delta.y);
resizingTarget.transform.localScale = new Vector3(originalScale.x, newScaleY, 1);
}
if (Input.GetMouseButtonUp(0))
{
isResizing = false;
resizingTarget = null;
currentResizeAxis = ResizeAxis.None;
}
}
}
private void PlaceBlock()
void PlaceBlock()
{
isPlacingBlock = false;
currentBlock = null;
}
public void AddSpike()
void InstantiateAndPrepare(GameObject prefab, Vector3? scaleOverride = null)
{
if (isPlacingBlock) return;
GameObject obj = Instantiate(prefab);
obj.transform.position = new Vector3(0, 0, -1);
obj.transform.localScale = scaleOverride ?? currentScale;
GameObject newSpike = new GameObject("Spike");
SpriteRenderer spriteRenderer = newSpike.AddComponent<SpriteRenderer>();
spriteRenderer.sprite = Resources.Load<Sprite>("InGame/SpikeSkin/BlueSpike");
if (spriteRenderer.sprite == null)
{
Debug.LogError("Le sprite de la plateforme est introuvable. Vérifiez le chemin Resources/InGame/SpikeSkin/BlueSpike");
}
BoxCollider2D collider = newSpike.AddComponent<BoxCollider2D>();
collider.offset = Vector2.zero;
collider.size = spriteRenderer.sprite.bounds.size;
newSpike.transform.position = new Vector2(0, 0);
currentScale = new Vector3(1f, 1f, 1);
newSpike.transform.localScale = currentScale;
try { obj.tag = prefab.name; }
catch { Debug.LogWarning($"Le tag '{prefab.name}' n'existe pas. Ajoutez-le dans Project Settings > Tags."); }
if (mapParent != null)
{
newSpike.transform.SetParent(mapParent);
}
obj.transform.SetParent(mapParent);
currentBlock = newSpike;
isPlacingBlock = true;
}
public void AddSmallSpike()
{
if (isPlacingBlock) return;
GameObject newSmallSpike = new GameObject("SmallSpike");
SpriteRenderer spriteRenderer = newSmallSpike.AddComponent<SpriteRenderer>();
spriteRenderer.sprite = Resources.Load<Sprite>("InGame/SmallSpikeSkin/BaseSmallSpike");
if (spriteRenderer.sprite == null)
{
Debug.LogError("Le sprite de la plateforme est introuvable. Vérifiez le chemin Resources/InGame/SpikeSkin/BlueSpike");
}
BoxCollider2D collider = newSmallSpike.AddComponent<BoxCollider2D>();
collider.offset = Vector2.zero;
collider.size = spriteRenderer.sprite.bounds.size;
newSmallSpike.transform.position = new Vector2(0, 0);
currentScale = new Vector3(0.25f, 0.25f, 1);
newSmallSpike.transform.localScale = currentScale;
if (mapParent != null)
{
newSmallSpike.transform.SetParent(mapParent);
}
currentBlock = newSmallSpike;
isPlacingBlock = true;
}
public void AddPlatform()
{
if (isPlacingBlock) return;
GameObject newPlatform = new GameObject("Platform");
SpriteRenderer spriteRenderer = newPlatform.AddComponent<SpriteRenderer>();
spriteRenderer.sprite = Resources.Load<Sprite>("InGame/PlateformSkin/RegularPlatform01");
if (spriteRenderer.sprite == null)
{
Debug.LogError("Le sprite de la plateforme est introuvable. Vérifiez le chemin Resources/InGame/Platform/Platform01.png");
}
BoxCollider2D collider = newPlatform.AddComponent<BoxCollider2D>();
collider.offset = Vector2.zero;
collider.size = spriteRenderer.sprite.bounds.size;
newPlatform.transform.position = new Vector2(0, 0);
currentScale = new Vector3(1f, 1f, 1);
newPlatform.transform.localScale = currentScale;
if (mapParent != null)
{
newPlatform.transform.SetParent(mapParent);
}
currentBlock = newPlatform;
isPlacingBlock = true;
}
public void AddShipPortal()
{
if (isPlacingBlock) return;
GameObject newShipPortal = new GameObject("ShipPortal");
SpriteRenderer spriteRenderer = newShipPortal.AddComponent<SpriteRenderer>();
spriteRenderer.sprite = Resources.Load<Sprite>("InGame/PortalsSkin/ShipPortalLabelled");
if (spriteRenderer.sprite == null)
{
Debug.LogError("Le sprite de la plateforme est introuvable. Vérifiez le chemin Resources/InGame/PortalSkin/ShipPortalLabelled");
}
BoxCollider2D collider = newShipPortal.AddComponent<BoxCollider2D>();
collider.offset = Vector2.zero;
collider.size = spriteRenderer.sprite.bounds.size;
newShipPortal.transform.position = new Vector2(0, 0);
newShipPortal.transform.localScale = new Vector3(0.5f, 0.5f, 1);
if (mapParent != null)
{
newShipPortal.transform.SetParent(mapParent);
}
currentBlock = newShipPortal;
isPlacingBlock = true;
}
public void AddCubePortal()
{
if (isPlacingBlock) return;
GameObject newCubePortal = new GameObject("CubePortal");
SpriteRenderer spriteRenderer = newCubePortal.AddComponent<SpriteRenderer>();
spriteRenderer.sprite = Resources.Load<Sprite>("InGame/PortalsSkin/CubePortalLabelled");
if (spriteRenderer.sprite == null)
{
Debug.LogError("Le sprite de la plateforme est introuvable. Vérifiez le chemin Resources/InGame/PortalSkin/ShipPortalLabelled");
}
BoxCollider2D collider = newCubePortal.AddComponent<BoxCollider2D>();
collider.offset = Vector2.zero;
collider.size = spriteRenderer.sprite.bounds.size;
newCubePortal.transform.position = new Vector2(0, 0);
newCubePortal.transform.localScale = new Vector3(0.5f, 0.5f, 1);
if (mapParent != null)
{
newCubePortal.transform.SetParent(mapParent);
}
currentBlock = newCubePortal;
currentBlock = obj;
isPlacingBlock = true;
}
public void Save()
{
//TODO
// TODO : Implémenter la sauvegarde du niveau
}
}

View File

@ -0,0 +1,53 @@
using UnityEngine;
using UnityEngine.UI;
using System.IO;
public class LevelLoader : MonoBehaviour
{
public LevelsLoader levelsLoader;
public AudioSource audioSource;
public Text progressionText;
private GameObject GetPrefab(string type)
{
return Resources.Load<GameObject>("Prefabs/" + type);
}
private void LoadAudio()
{
audioSource.clip = Resources.Load<AudioClip>(Path.Combine("Musics", levelsLoader.levelCurrent.musicName));
audioSource.Play();
}
private void LoadElements()
{
Level current = levelsLoader.levelCurrent;
foreach (var element in current.elements)
{
GameObject prefab = GetPrefab(element.type);
GameObject instance = Instantiate(prefab, new Vector3(element.x, element.y, 0), Quaternion.identity);
Vector3 originalScale = instance.transform.localScale;
float newScaleX = element.scaleX > 0 ? element.scaleX : originalScale.x;
float newScaleY = element.scaleY > 0 ? element.scaleY : originalScale.y;
instance.transform.localScale = new Vector3(newScaleX, newScaleY, originalScale.z);
}
Instantiate(GetPrefab("WinnerWall"), new Vector3(current.LastX, 0, 0), Quaternion.Euler(0, 0, 90));
}
public void Start()
{
levelsLoader = GameObject.FindGameObjectWithTag("LevelsLoader").GetComponent<LevelsLoader>();
levelsLoader.IncreaseTotalAttempts();
LoadAudio();
LoadElements();
}
public void Update()
{
Level current = levelsLoader.levelCurrent;
progressionText.text = current.ProgressionPercent + "%";
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 017ea60a517f31bf3af976010911be25

View File

@ -0,0 +1,16 @@
using UnityEngine;
[System.Serializable]
public class LevelStat
{
public string JsonName { get; set; }
public int totalJumps;
public int totalAttempts;
public int progressionPercent;
public static LevelStat CreateFromJSON(string jsonString)
{
return JsonUtility.FromJson<LevelStat>(jsonString);
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: be0a07689666b367aaf138fa766075c8

View File

@ -1,6 +1,7 @@
using UnityEngine;
using System.Collections.Generic;
using System.IO;
using System;
public class LevelsLoader : MonoBehaviour
{
@ -17,10 +18,37 @@ public class LevelsLoader : MonoBehaviour
private void LoadAllLevels()
{
TextAsset[] levelFiles = Resources.LoadAll<TextAsset>("Levels");
TextAsset[] levelStatsFiles = Resources.LoadAll<TextAsset>("LevelsStats");
Dictionary<string, LevelStat> levelStatsMap = new();
foreach (TextAsset jsonTextFileStats in levelStatsFiles)
{
LevelStat levelStat = LevelStat.CreateFromJSON(jsonTextFileStats.text);
levelStat.JsonName = jsonTextFileStats.name;
levelStatsMap[levelStat.JsonName] = levelStat;
}
foreach (TextAsset jsonTextFile in levelFiles)
{
Level level = Level.CreateFromJSON(jsonTextFile.text);
level.JsonName = jsonTextFile.name;
level.TotalAttempts = 0;
level.TotalJumps = 0;
level.ProgressionPercent = 0;
level.ProgressionPercentMax = 0;
if (levelStatsMap.TryGetValue(level.JsonName, out LevelStat levelStat))
{
level.TotalAttempts = levelStat.totalAttempts;
level.TotalJumps = levelStat.totalJumps;
level.ProgressionPercentMax = levelStat.progressionPercent;
}
else
{
levelStat = new LevelStat { JsonName = level.JsonName, totalJumps = 0, totalAttempts = 0 };
levelStatsMap[level.JsonName] = levelStat;
}
levels.Add(level);
}
levels.Sort((x, y) => x.order.CompareTo(y.order));
@ -28,8 +56,18 @@ public class LevelsLoader : MonoBehaviour
private void SaveLevelCurrent()
{
string json = JsonUtility.ToJson(levelCurrent, true) + "\n";
File.WriteAllText(Path.Combine(Application.dataPath, "Resources", "Levels", levelCurrent.JsonName + ".json"), json);
string levelJson = JsonUtility.ToJson(levelCurrent, true) + "\n";
File.WriteAllText(Path.Combine(Application.dataPath, "Resources", "Levels", levelCurrent.JsonName + ".json"), levelJson);
LevelStat levelStat = new()
{
JsonName = levelCurrent.JsonName,
totalJumps = levelCurrent.TotalJumps,
totalAttempts = levelCurrent.TotalAttempts,
progressionPercent = levelCurrent.ProgressionPercentMax,
};
string levelStatJson = JsonUtility.ToJson(levelStat, true) + "\n";
File.WriteAllText(Path.Combine(Application.dataPath, "Resources", "LevelsStats", levelCurrent.JsonName + ".json"), levelStatJson);
}
public void NextLevel()
@ -46,13 +84,39 @@ public class LevelsLoader : MonoBehaviour
public void IncreaseTotalJumps()
{
levelCurrent.totalJumps += 1;
levelCurrent.TotalJumps += 1;
SaveLevelCurrent();
}
public void IncreaseTotalAttempts()
{
levelCurrent.totalAttempts += 1;
levelCurrent.TotalAttempts += 1;
SaveLevelCurrent();
}
public int CalculateCurrentProgressionPercent(Vector3 playerPosition)
{
float lastX = levelCurrent.LastX;
GameObject winnerWallPrefab = Resources.Load<GameObject>("Prefabs/WinnerWall");
float winnerWallWidth = winnerWallPrefab.GetComponent<Renderer>().bounds.size.x;
float marginError = 0.5f;
float totalDistance = lastX - (winnerWallWidth / 2) - marginError;
float rawPercentage = (playerPosition.x / totalDistance) * 100;
int clampedPercentage = Mathf.Clamp(Mathf.RoundToInt(rawPercentage), 0, 100);
levelCurrent.ProgressionPercent = clampedPercentage;
levelCurrent.ProgressionPercentMax = Math.Max(levelCurrent.ProgressionPercentMax, levelCurrent.ProgressionPercent);
SaveLevelCurrent();
return clampedPercentage;
}
public void RefreshLevels()
{
levels.Clear();
LoadAllLevels();
if (levels.Count > 0)
levelCurrent = levels[0];
}
}

View File

@ -0,0 +1,25 @@
using UnityEngine;
using UnityEngine.UI;
public class LevelProgression : MonoBehaviour
{
public Text levelProgressionText;
public LevelsLoader levelsLoader;
private string GetText()
{
return "Progression Max: " + levelsLoader.levelCurrent.ProgressionPercentMax + "%";
}
public void Start()
{
levelsLoader = GameObject.FindGameObjectWithTag("LevelsLoader").GetComponent<LevelsLoader>();
levelProgressionText.text = GetText();
}
public void Update()
{
levelProgressionText.text = GetText();
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f53ed5a09d2704999b0f112382f62309

View File

@ -0,0 +1,28 @@
using System;
using UnityEngine;
using UnityEngine.UI;
public class LevelTotalAttempts : MonoBehaviour
{
public Text levelTotalAttemptsText;
public LevelsLoader levelsLoader;
private string GetText()
{
int number = levelsLoader.levelCurrent.TotalAttempts;
FormattableString message = $"{number:N0}";
return "Total Attempts: " + FormattableString.Invariant(message);
}
public void Start()
{
levelsLoader = GameObject.FindGameObjectWithTag("LevelsLoader").GetComponent<LevelsLoader>();
levelTotalAttemptsText.text = GetText();
}
public void Update()
{
levelTotalAttemptsText.text = GetText();
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c2e1b06afdba22e2e801e20c56a99a30

View File

@ -0,0 +1,28 @@
using System;
using UnityEngine;
using UnityEngine.UI;
public class LevelTotalJumps : MonoBehaviour
{
public Text levelTotalJumpsText;
public LevelsLoader levelsLoader;
private string GetText()
{
int number = levelsLoader.levelCurrent.TotalJumps;
FormattableString message = $"{number:N0}";
return "Total Jumps: " + FormattableString.Invariant(message);
}
public void Start()
{
levelsLoader = GameObject.FindGameObjectWithTag("LevelsLoader").GetComponent<LevelsLoader>();
levelTotalJumpsText.text = GetText();
}
public void Update()
{
levelTotalJumpsText.text = GetText();
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 90096cf0341a3ac178de4db506d4205c

View File

@ -8,6 +8,11 @@ public class MainMenu : MonoBehaviour
SceneManager.LoadSceneAsync("SelectLevelScene");
}
public void OpenImport()
{
SceneManager.LoadSceneAsync("ImportScene");
}
public void OpenSettings()
{
// SceneManager.LoadSceneAsync(?);

View File

@ -0,0 +1,41 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PageScript : MonoBehaviour
{
public List<GameObject> buttons; // À assigner dans linspector
public int visibleCount = 4;
private int currentIndex = 0;
public void ShowNext()
{
if (currentIndex + visibleCount < buttons.Count)
{
currentIndex++;
UpdateVisibility();
}
}
public void ShowPrevious()
{
if (currentIndex > 0)
{
currentIndex--;
UpdateVisibility();
}
}
void Start()
{
UpdateVisibility();
}
void UpdateVisibility()
{
for (int i = 0; i < buttons.Count; i++)
{
buttons[i].SetActive(i >= currentIndex && i < currentIndex + visibleCount);
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: cffba6c21bfdaae43b201d3e4a16c7cd

View File

@ -0,0 +1,33 @@
using UnityEngine;
using UnityEngine.SceneManagement;
public class PauseMenu : MonoBehaviour
{
public GameObject pauseMenu;
public GameObject pauseButton;
public LevelLoader levelLoader;
public void Pause()
{
Time.timeScale = 0;
levelLoader.audioSource.Pause();
pauseMenu.SetActive(true);
pauseButton.SetActive(false);
}
public void Home()
{
Time.timeScale = 1;
SceneManager.LoadScene("HomeScene");
}
public void Resume()
{
Time.timeScale = 1;
levelLoader.audioSource.Play();
pauseMenu.SetActive(false);
pauseButton.SetActive(true);
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: fe007f250f006dae5b98ae5c5a87113b

View File

@ -1,113 +1,76 @@
using UnityEngine;
using UnityEngine.SceneManagement;
using System.IO;
public class Player : MonoBehaviour
{
public Rigidbody2D rigidBody;
public GameObject playerObject;
public ParticleSystem particle;
public LevelsLoader levelsLoader;
public Rigidbody2D RigidBody { get; private set; }
public Transform Transform { get; private set; }
public ParticleSystem Particle { get; private set; }
public LevelsLoader LevelsLoader { get; private set; }
public SpriteRenderer SpriteRenderer { get; private set; }
public bool IsColliding { get; set; } = true;
public bool HasStarted { get; private set; } = false;
public bool CanJump { get; set; } = true;
public bool isColliding = true;
public AudioSource audioSource;
private bool hasStarted = false;
public IGameMode CurrentGameMode { get; set; }
private bool canJump = true;
public void Awake()
{
RigidBody = GetComponent<Rigidbody2D>();
Transform = transform;
Particle = GetComponentInChildren<ParticleSystem>();
SpriteRenderer = GetComponentInChildren<SpriteRenderer>();
LevelsLoader = GameObject.FindGameObjectWithTag("LevelsLoader").GetComponent<LevelsLoader>();
}
public void Start()
{
levelsLoader = GameObject.FindGameObjectWithTag("LevelsLoader").GetComponent<LevelsLoader>();
levelsLoader.IncreaseTotalAttempts();
audioSource.clip = Resources.Load<AudioClip>(Path.Combine("Musics", levelsLoader.levelCurrent.musicName));
audioSource.Play();
var mainModule = particle.main;
var mainModule = Particle.main;
mainModule.simulationSpace = ParticleSystemSimulationSpace.World;
particle.transform.parent = null;
Particle.transform.parent = null;
Invoke(nameof(EnableInput), 0.1f);
CurrentGameMode = new NormalGameMode();
}
private void EnableInput()
{
hasStarted = true;
HasStarted = true;
}
public void Update()
{
rigidBody.linearVelocity = new Vector2(8.6f, rigidBody.linearVelocity.y);
if (hasStarted && isColliding && Input.GetKey(KeyCode.Space) && canJump)
{
Jump();
}
if (!IsJumping())
{
AlignRotation();
particle.gameObject.SetActive(true);
}
else
{
particle.gameObject.SetActive(false);
transform.Rotate(Vector3.back * 360 * Time.deltaTime);
}
UpdateParticlePositionAndRotation();
UpdateParticleSystemSpeed();
}
private void Jump()
{
rigidBody.linearVelocity = new Vector2(rigidBody.linearVelocity.x, 0);
rigidBody.AddForce(Vector2.up * 26.6581f, ForceMode2D.Impulse);
levelsLoader.IncreaseTotalJumps();
}
private bool IsJumping()
{
return !isColliding;
}
private void AlignRotation()
{
Vector3 rotation = transform.rotation.eulerAngles;
rotation.z = Mathf.Round(rotation.z / 90) * 90;
transform.rotation = Quaternion.Euler(rotation);
}
private void UpdateParticlePositionAndRotation()
{
particle.transform.position = transform.position + new Vector3(-0.19f, -0.64f, -10);
particle.transform.rotation = Quaternion.Euler(0, 0, 150.464f);
}
private void UpdateParticleSystemSpeed()
{
var velocityOverLifetime = particle.velocityOverLifetime;
velocityOverLifetime.x = rigidBody.linearVelocity.x;
CurrentGameMode.Update(this);
LevelsLoader.CalculateCurrentProgressionPercent(transform.position);
}
public void OnCollisionEnter2D(Collision2D collision)
{
isColliding = true;
canJump = true;
if (collision.gameObject.CompareTag("Kill"))
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
if (collision.gameObject.CompareTag("Win"))
{
SceneManager.LoadScene("HomeScene");
}
CurrentGameMode.OnCollisionEnter(this, collision);
}
public void OnCollisionExit2D(Collision2D collision)
{
isColliding = false;
CurrentGameMode.OnCollisionExit(this, collision);
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("ShipPortal"))
{
SpriteRenderer.sprite = Resources.Load<Sprite>("Shapes/Ship");
ChangeGameMode(new ShipGameMode());
}
else if (collision.CompareTag("CubePortal"))
{
SpriteRenderer.sprite = Resources.Load<Sprite>("Shapes/BaseSquare");
ChangeGameMode(new NormalGameMode());
}
}
public void ChangeGameMode(IGameMode newMode)
{
CurrentGameMode = newMode;
}
}

View File

@ -3,10 +3,23 @@ using UnityEngine;
public class PlayerCamera : MonoBehaviour
{
public GameObject playerObject;
public const float MIN_Y_FOLLOW = 2.0f;
private float initialY;
public void Update()
private void Start()
{
transform.position = new Vector3(playerObject.transform.position.x, transform.position.y, transform.position.z);
// transform.position = new Vector3(player.transform.position.x, player.transform.position.y, transform.position.z);
initialY = transform.position.y;
}
private void Update()
{
float targetY = initialY;
if (playerObject.transform.position.y > MIN_Y_FOLLOW)
{
targetY = playerObject.transform.position.y;
}
transform.position = new Vector3(playerObject.transform.position.x, targetY, transform.position.z);
}
}