It's common to feel overwhelmed when looking at someone else's Unity project, especially if you're new to Unity. Let me help you understand your options and the best approach.
First, let's understand what some common files in a Unity project are:
.unity files: These are scene files.prefab files: Reusable game objects with components.cs files: C# scripts that control behavior.meta files: Unity's internal tracking files (don't delete these).asset files: Various Unity assets like animations, materials, etc..anim files: Animation files.controller files: Animation controllersYou have two main options:
I recommend a hybrid approach where you start fresh but reference the existing project:
Create these folders in your Assets directory:
Assets/
├── Animations/
├── Audio/
├── Materials/
├── Prefabs/
├── Scenes/
├── Scripts/
├── Sprites/
└── UI/Open the GitHub project in another Unity instance and examine:
Here's how to create an eye-catching main menu:
Set up the Canvas:
Add a background:
Create a title:
Add decorative elements:
Create buttons:
Add panels for Instructions and Credits:
Add background music:
When examining the GitHub project, focus on understanding:
Look for these key components:
I recommend the hybrid approach: start with a fresh project and selectively implement components from the GitHub project that you understand. This will give you a better learning experience and ensure you understand how your game works.
Would you like me to explain any specific part of the code or Unity concepts in more detail? Or would you like guidance on implementing a specific feature from the GitHub project?
every portion of code should be under its title not all of them in one section can you change that please\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage[margin=1in]{geometry}
\usepackage{listings}
\usepackage{xcolor}
\usepackage{titlesec}
\usepackage{graphicx}
\usepackage{float}
\usepackage{placeins} % Added for FloatBarrier
% Compact section spacing
\titlespacing*{\section}{0pt}{1ex}{0.5ex}
\titlespacing*{\subsection}{0pt}{0.5ex}{0.3ex}
% Colors for code
\definecolor{dkgreen}{rgb}{0,0.5,0}
\definecolor{gray}{rgb}{0.5,0.5,0.5}
\definecolor{mauve}{rgb}{0.58,0,0.82}
\lstset{
language=C++,
basicstyle=\scriptsize\ttfamily, % Changed from footnotesize to scriptsize for more compact code
keywordstyle=\color{blue},
commentstyle=\color{dkgreen},
stringstyle=\color{mauve},
numbers=none,
numberstyle=\tiny\color{gray},
breaklines=true,
breakatwhitespace=true,
tabsize=2,
frame=single,
captionpos=b
}
\title{\textbf{Sunnyland Game Documentation}}
\author{Réalisé par Sinda Sahmim, Abderrahim Dhehibi, Mahdi Baya, Mariam Mabrouk}
\date{} % Removed the date
\begin{document}
\maketitle
\section{Game Summary}
Sunnyland is a 2D platformer where players navigate levels, collect gems, and avoid or defeat enemies like frogs and eagles. The game includes smooth jumping mechanics, enemy AI, and collectibles.
% Screenshots section - arranged in a 2x1 layout
\begin{figure}[ht]
\centering
\begin{minipage}{0.48\textwidth}
\centering
\includegraphics[width=\linewidth]{sunny.png}
\caption{Main gameplay scene}
\end{minipage}
\hfill
\begin{minipage}{0.48\textwidth}
\centering
\includegraphics[width=\linewidth]{tileset.png}
\caption{Level design with platforms}
\end{minipage}
\end{figure}
\section{Gameplay Overview}
Players control a character capable of running, jumping, and defeating enemies by jumping on them. Enemy types include:
\begin{itemize}
\item \textbf{Frogs}: Jump between platforms.
\item \textbf{Eagles}: Shoot projectiles at the player.
\end{itemize}
Players collect gems across levels while avoiding pitfalls and enemy attacks.
\FloatBarrier % Ensures all floats are processed before continuing
\section{Key Code Components}
The game is built with modular scripts handling movement, enemy behavior, and level transitions.
\subsection{PlayerController.cs}
Manages player movement, jumping, collisions, and animations.
\begin{lstlisting}[float=h, caption=Player Movement]
private void Movement() {
float hDirection = Input.GetAxis("Horizontal");
if (state != State.hurt) {
if (hDirection != 0) {
rb.velocity = new Vector2(hDirection * speed, rb.velocity.y);
transform.localScale = new Vector2(Mathf.Sign(hDirection), 1);
}
}
if (Input.GetButtonDown("Jump") && IsGrounded()) {
Jump();
}
}
\end{lstlisting}
\subsection{Frog.cs}
Defines patrol and jumping behavior for frog enemies.
\begin{lstlisting}[float=h, caption=Frog Movement]
private void Move() {
if (facingLeft && position.x > leftCap) {
rb.velocity = new Vector2(-jumpLength, jumpHeight);
} else if (!facingLeft && position.x < rightCap) {
rb.velocity = new Vector2(jumpLength, jumpHeight);
} else {
facingLeft = !facingLeft;
}
}
\end{lstlisting}
\subsection{Eagle.cs & EagleShot.cs}
Controls enemy shooting and projectile behavior.
\begin{lstlisting}[float=h, caption=Eagle Shooting]
private void Shoot() {
if (Vector2.Distance(player.position, firePoint.position) < shootRange) {
Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);
}
}
\end{lstlisting}
\begin{lstlisting}[float=h, caption=Projectile Movement]
IEnumerator Start() {
rb = GetComponent<Rigidbody2D>();
var direction = (player.position - transform.position).normalized;
rb.velocity = direction * speed;
yield return new WaitForSeconds(5f);
Destroy(gameObject);
}
\end{lstlisting}
\section{Conclusion}
Sunnyland demonstrates essential platformer mechanics, including player movement, AI enemies, and collectibles. The modular codebase allows easy extensions for future enhancements.
\end{document}
I'll reorganize the code examples with each portion under its own title to make it easier to understand and reference.
First, let's understand what some common files in a Unity project are:
.unity files: These are scene files.prefab files: Reusable game objects with components.cs files: C# scripts that control behavior.meta files: Unity's internal tracking files (don't delete these).asset files: Various Unity assets like animations, materials, etc..anim files: Animation files.controller files: Animation controllersI recommend a hybrid approach where you start fresh but reference the existing project:
Create these folders in your Assets directory:
Assets/
├── Animations/
├── Audio/
├── Materials/
├── Prefabs/
├── Scenes/
├── Scripts/
├── Sprites/
└── UI/This script handles player movement, jumping, animations, and interactions:
using UnityEngine;
public class PlayerController : MonoBehaviour
{
// Movement variables
[SerializeField] private float moveSpeed = 5f;
[SerializeField] private float jumpForce = 10f;
// Ground check
[SerializeField] private Transform groundCheck;
[SerializeField] private float groundCheckRadius = 0.2f;
[SerializeField] private LayerMask groundLayer;
// Components
private Rigidbody2D rb;
private Animator animator;
private SpriteRenderer spriteRenderer;
private bool isGrounded;
private bool isJumping;
// Animation parameter names
private readonly string WALK_ANIMATION = "walk";
private readonly string JUMP_ANIMATION = "jump";
private readonly string HURT_ANIMATION = "hurt";
[Header("Visual Feedback")]
[SerializeField] private Color hurtColor = Color.red;
[SerializeField] private ParticleSystem jumpParticles;
[Header("Audio")]
[SerializeField] private AudioClip jumpSound;
[SerializeField] private AudioClip collectSound;
[SerializeField] private AudioClip hurtSound;
private AudioSource audioSource;
private Color originalColor;
// References to other game systems
private GameManager gameManager;
void Start()
{
// Get components
rb = GetComponent<Rigidbody2D>();
animator = GetComponent<Animator>();
spriteRenderer = GetComponent<SpriteRenderer>();
audioSource = GetComponent<AudioSource>();
gameManager = FindObjectOfType<GameManager>();
// Validate components
if (rb == null) Debug.LogError("Rigidbody2D not found on player!");
if (animator == null) Debug.LogError("Animator not found on player!");
if (spriteRenderer == null) Debug.LogError("SpriteRenderer not found on player!");
if (groundCheck == null) Debug.LogError("Ground check transform not assigned!");
if (spriteRenderer != null)
{
originalColor = spriteRenderer.color;
}
}
void Update()
{
// Check if player is grounded
isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundLayer);
// Handle horizontal movement
float horizontalInput = Input.GetAxis("Horizontal");
rb.velocity = new Vector2(horizontalInput * moveSpeed, rb.velocity.y);
// Flip sprite based on direction
if (horizontalInput > 0)
spriteRenderer.flipX = false;
else if (horizontalInput < 0)
spriteRenderer.flipX = true;
// Handle jumping
if (Input.GetButtonDown("Jump") && isGrounded)
{
rb.velocity = new Vector2(rb.velocity.x, jumpForce);
isJumping = true;
}
// Update animations
UpdateAnimations(horizontalInput);
}
void UpdateAnimations(float horizontalInput)
{
// Walking animation
animator.SetBool(WALK_ANIMATION, Mathf.Abs(horizontalInput) > 0.1f && isGrounded);
// Jumping animation
animator.SetBool(JUMP_ANIMATION, !isGrounded);
// Reset jumping flag when landing
if (isGrounded && isJumping)
isJumping = false;
}
private void Jump()
{
rb.velocity = new Vector2(rb.velocity.x, jumpForce);
// Play jump animation
if (animator != null)
{
animator.SetTrigger("Jump");
}
// Play jump particles
if (jumpParticles != null)
{
jumpParticles.Play();
}
// Play jump sound
PlaySound(jumpSound);
}
private void OnTriggerEnter2D(Collider2D other)
{
// Check if the player collects a coin
if (other.CompareTag("Coin"))
{
// Increase score
gameManager.AddScore(1);
// Play collect sound
PlaySound(collectSound);
// Destroy the coin
Destroy(other.gameObject);
}
// Check if the player hits a hazard
if (other.CompareTag("Hazard"))
{
TakeDamage();
}
}
private void TakeDamage()
{
// Visual feedback - flash red
if (spriteRenderer != null)
{
StartCoroutine(FlashColor());
}
// Play hurt sound
PlaySound(hurtSound);
// Notify game manager
gameManager.PlayerHit();
}
private System.Collections.IEnumerator FlashColor()
{
spriteRenderer.color = hurtColor;
yield return new WaitForSeconds(0.2f);
spriteRenderer.color = originalColor;
}
private void PlaySound(AudioClip clip)
{
if (audioSource != null && clip != null)
{
audioSource.PlayOneShot(clip);
}
}
// Called when player gets hurt
public void GetHurt()
{
animator.SetTrigger(HURT_ANIMATION);
// Add more hurt logic here (knockback, health reduction, etc.)
}
// Visualize ground check in editor
void OnDrawGizmosSelected()
{
if (groundCheck == null) return;
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(groundCheck.position, groundCheckRadius);
}
}This script makes the camera smoothly follow the player with a look-ahead effect:
using UnityEngine;
public class CameraFollow : MonoBehaviour
{
[SerializeField] private Transform target;
[SerializeField] private float smoothSpeed = 0.125f;
[SerializeField] private Vector3 offset = new Vector3(0, 1, -10);
[SerializeField] private float lookAheadFactor = 3f;
[SerializeField] private float lookAheadReturnSpeed = 0.5f;
[SerializeField] private float lookAheadMoveThreshold = 0.1f;
private float currentLookAheadX = 0f;
private float targetLookAheadX = 0f;
private Vector3 lastTargetPosition;
private Vector3 currentVelocity;
void Start()
{
if (target == null)
{
Debug.LogError("Camera target not assigned!");
return;
}
lastTargetPosition = target.position;
}
void LateUpdate()
{
if (target == null) return;
// Calculate look-ahead factor
float directionX = Mathf.Sign(target.position.x - lastTargetPosition.x);
float moveX = Mathf.Abs(target.position.x - lastTargetPosition.x);
if (moveX > lookAheadMoveThreshold)
{
targetLookAheadX = lookAheadFactor * directionX;
}
else
{
targetLookAheadX = Mathf.MoveTowards(targetLookAheadX, 0, lookAheadReturnSpeed * Time.deltaTime);
}
currentLookAheadX = Mathf.SmoothDamp(currentLookAheadX, targetLookAheadX, ref currentVelocity.x, smoothSpeed);
// Calculate desired position
Vector3 lookAheadPos = Vector3.right * currentLookAheadX;
Vector3 desiredPosition = target.position + offset + lookAheadPos;
// Only follow in X and Y, keep Z constant
desiredPosition = new Vector3(desiredPosition.x, desiredPosition.y, transform.position.z);
// Smoothly move camera
transform.position = Vector3.SmoothDamp(transform.position, desiredPosition, ref currentVelocity, smoothSpeed);
lastTargetPosition = target.position;
}
}This script handles collectible items like coins or gems:
using UnityEngine;
public class Collectible : MonoBehaviour
{
[SerializeField] private int scoreValue = 1;
[SerializeField] private GameObject collectEffect;
[SerializeField] private AudioClip collectSound;
[SerializeField] private float bobSpeed = 2f;
[SerializeField] private float bobHeight = 0.2f;
private Vector3 startPosition;
private GameManager gameManager;
void Start()
{
startPosition = transform.position;
gameManager = FindObjectOfType<GameManager>();
if (gameManager == null)
Debug.LogWarning("GameManager not found in scene!");
}
void Update()
{
// Make the collectible bob up and down
float newY = startPosition.y + Mathf.Sin(Time.time * bobSpeed) * bobHeight;
transform.position = new Vector3(transform.position.x, newY, transform.position.z);
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
Collect();
}
}
void Collect()
{
// Add score
if (gameManager != null)
gameManager.AddScore(scoreValue);
// Play sound
if (collectSound != null)
AudioSource.PlayClipAtPoint(collectSound, transform.position);
// Spawn effect
if (collectEffect != null)
Instantiate(collectEffect, transform.position, Quaternion.identity);
// Destroy the collectible
Destroy(gameObject);
}
}This script manages game state, score, health, and UI:
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using TMPro;
public class GameManager : MonoBehaviour
{
// Singleton instance
public static GameManager Instance { get; private set; }
[Header("UI References")]
[SerializeField] private TextMeshProUGUI scoreText;
[SerializeField] private TextMeshProUGUI healthText;
[SerializeField] private GameObject gameOverPanel;
[Header("Player Settings")]
[SerializeField] private int maxHealth = 3;
[Header("Game Settings")]
[SerializeField] private string mainMenuScene = "MainMenu";
[SerializeField] private float gameOverDelay = 1f;
// Game state
private int currentScore = 0;
private int currentHealth;
private bool isGameOver = false;
void Awake()
{
// Singleton pattern
if (Instance == null)
{
Instance = this;
}
else
{
Destroy(gameObject);
return;
}
}
void Start()
{
// Initialize game
currentHealth = maxHealth;
isGameOver = false;
// Hide game over panel
if (gameOverPanel != null)
gameOverPanel.SetActive(false);
// Update UI
UpdateScoreUI();
UpdateHealthUI();
}
public void AddScore(int points)
{
if (isGameOver) return;
currentScore += points;
UpdateScoreUI();
}
public void TakeDamage(int damage = 1)
{
if (isGameOver) return;
currentHealth -= damage;
UpdateHealthUI();
// Check for game over
if (currentHealth <= 0)
{
GameOver();
}
}
private void UpdateScoreUI()
{
if (scoreText != null)
scoreText.text = "Score: " + currentScore;
}
private void UpdateHealthUI()
{
if (healthText != null)
healthText.text = "Health: " + currentHealth;
}
private void GameOver()
{
isGameOver = true;
// Show game over panel after delay
Invoke("ShowGameOverPanel", gameOverDelay);
}
private void ShowGameOverPanel()
{
if (gameOverPanel != null)
gameOverPanel.SetActive(true);
}
public void RestartGame()
{
// Reload current scene
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
public void ReturnToMainMenu()
{
SceneManager.LoadScene(mainMenuScene);
}
}This script controls basic enemy movement and player interaction:
using UnityEngine;
public class SimpleEnemy : MonoBehaviour
{
[SerializeField] private float moveSpeed = 2f;
[SerializeField] private Transform leftPoint;
[SerializeField] private Transform rightPoint;
[SerializeField] private int damageAmount = 1;
private Rigidbody2D rb;
private SpriteRenderer spriteRenderer;
private Animator animator;
private bool movingRight = true;
private Vector2 currentTarget;
void Start()
{
rb = GetComponent<Rigidbody2D>();
spriteRenderer = GetComponent<SpriteRenderer>();
animator = GetComponent<Animator>();
// Validate components
if (rb == null) Debug.LogError("Rigidbody2D not found on enemy!");
if (spriteRenderer == null) Debug.LogError("SpriteRenderer not found on enemy!");
// Validate patrol points
if (leftPoint == null || rightPoint == null)
{
Debug.LogError("Patrol points not set for enemy!");
enabled = false;
return;
}
// Set initial target
currentTarget = rightPoint.position;
}
void Update()
{
// Move towards current target
Vector2 direction = (currentTarget - rb.position).normalized;
rb.velocity = new Vector2(direction.x * moveSpeed, rb.velocity.y);
// Flip sprite based on movement direction
if (rb.velocity.x > 0.1f)
spriteRenderer.flipX = false;
else if (rb.velocity.x < -0.1f)
spriteRenderer.flipX = true;
// Check if reached target
float distanceToTarget = Vector2.Distance(rb.position, currentTarget);
if (distanceToTarget < 0.1f)
{
// Switch target
currentTarget = movingRight ? leftPoint.position : rightPoint.position;
movingRight = !movingRight;
}
}
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Player"))
{
// Get player controller
PlayerController player = collision.gameObject.GetComponent<PlayerController>();
if (player != null)
{
// Hurt player
player.GetHurt();
// Reduce player health
GameManager gameManager = GameManager.Instance;
if (gameManager != null)
{
gameManager.TakeDamage(damageAmount);
}
}
}
}
// Visualize patrol path in editor
void OnDrawGizmosSelected()
{
if (leftPoint != null && rightPoint != null)
{
Gizmos.color = Color.cyan;
Gizmos.DrawLine(leftPoint.position, rightPoint.position);
Gizmos.DrawWireSphere(leftPoint.position, 0.3f);
Gizmos.DrawWireSphere(rightPoint.position, 0.3f);
}
}
}This script creates an eye-catching main menu with animations and transitions:
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using TMPro;
public class MainMenuController : MonoBehaviour
{
[Header("Menu Panels")]
[SerializeField] private GameObject mainPanel;
[SerializeField] private GameObject instructionsPanel;
[SerializeField] private GameObject creditsPanel;
[Header("Button References")]
[SerializeField] private Button playButton;
[SerializeField] private Button instructionsButton;
[SerializeField] private Button creditsButton;
[SerializeField] private Button quitButton;
[SerializeField] private Button backFromInstructionsButton;
[SerializeField] private Button backFromCreditsButton;
[Header("Animation")]
[SerializeField] private RectTransform titleRect;
[SerializeField] private float pulsateSpeed = 1.5f;
[SerializeField] private float pulsateAmount = 0.1f;
[Header("Game Settings")]
[SerializeField] private string gameSceneName = "GameScene";
private Vector3 titleOriginalScale;
void Start()
{
// Store original title scale
if (titleRect != null)
titleOriginalScale = titleRect.localScale;
// Show main panel, hide others
ShowMainPanel();
// Set up button listeners
SetupButtons();
}
void Update()
{
// Animate title
if (titleRect != null)
{
float pulse = 1f + Mathf.Sin(Time.time * pulsateSpeed) * pulsateAmount;
titleRect.localScale = titleOriginalScale * pulse;
}
}
private void SetupButtons()
{
// Main menu buttons
if (playButton != null)
playButton.onClick.AddListener(OnPlayClicked);
if (instructionsButton != null)
instructionsButton.onClick.AddListener(OnInstructionsClicked);
if (creditsButton != null)
creditsButton.onClick.AddListener(OnCreditsClicked);
if (quitButton != null)
quitButton.onClick.AddListener(OnQuitClicked);
// Back buttons
if (backFromInstructionsButton != null)
backFromInstructionsButton.onClick.AddListener(ShowMainPanel);
if (backFromCreditsButton != null)
backFromCreditsButton.onClick.AddListener(ShowMainPanel);
}
private void OnPlayClicked()
{
SceneManager.LoadScene(gameSceneName);
}
private void OnInstructionsClicked()
{
ShowPanel(instructionsPanel);
}
private void OnCreditsClicked()
{
ShowPanel(creditsPanel);
}
private void OnQuitClicked()
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
}
private void ShowMainPanel()
{
ShowPanel(mainPanel);
}
private void ShowPanel(GameObject panelToShow)
{
// Hide all panels
if (mainPanel != null) mainPanel.SetActive(false);
if (instructionsPanel != null) instructionsPanel.SetActive(false);
if (creditsPanel != null) creditsPanel.SetActive(false);
// Show the selected panel
if (panelToShow != null)
panelToShow.SetActive(true);
}
}Here's a step-by-step guide to create a visually appealing main menu:
Style them to match the Sunny Land aesthetic:
When examining the GitHub project, focus on understanding:
Look for these key components:
Would you like me to explain any specific part of the code or Unity concepts in more detail? Or would you like guidance on implementing a specific feature from the GitHub project?