Loading...
Vietnam Geography App
Loading...
Vietnam Geography App
Hands-on Unity 2D development: sprites, animations, physics, UI systems. Tạo complete 2D platformer từ concept đến deployment.
180 phút
Trung bình
5+ Use Cases
Tạo complete 2D platformer với character movement, collectibles, enemies, và level progression
# Unity 2D Platformer Development: "Crystal Runner" ## 1. Project Setup & Architecture ### Project Structure: ``` Assets/ ├── Scripts/ │ ├── Player/ │ ├── Enemies/ │ ├── Managers/ │ ├── UI/ │ └── Utils/ ├── Sprites/ │ ├── Characters/ │ ├── Environment/ │ ├── UI/ │ └── Effects/ ├── Animations/ ├── Audio/ ├── Scenes/ └── Prefabs/ ``` ### Core Systems Architecture: - **GameManager:** Scene transitions, game state, save/load - **PlayerController:** Input handling, movement, animations - **CameraController:** Following, boundaries, smooth transitions - **LevelManager:** Objectives, completion, progression - **AudioManager:** Music, SFX, dynamic mixing - **UIManager:** Menus, HUD, responsive layouts ## 2. Player Controller Implementation ### Movement System (C#): ```csharp using UnityEngine; public class PlayerController : MonoBehaviour { [Header("Movement Settings")] public float moveSpeed = 8f; public float jumpForce = 16f; public float coyoteTime = 0.2f; public float jumpBufferTime = 0.2f; [Header("Physics")] public LayerMask groundLayerMask; public Transform groundCheck; public float groundCheckRadius = 0.2f; // Private variables private Rigidbody2D rb; private Animator animator; private SpriteRenderer spriteRenderer; private AudioSource audioSource; private float horizontalInput; private bool jumpInput; private bool isGrounded; private float coyoteTimeCounter; private float jumpBufferCounter; // Animation hashes for performance private int speedHash; private int jumpHash; private int groundedHash; void Start() { rb = GetComponent<Rigidbody2D>(); animator = GetComponent<Animator>(); spriteRenderer = GetComponent<SpriteRenderer>(); audioSource = GetComponent<AudioSource>(); // Cache animator parameter hashes speedHash = Animator.StringToHash("Speed"); jumpHash = Animator.StringToHash("Jump"); groundedHash = Animator.StringToHash("Grounded"); } void Update() { HandleInput(); CheckGrounded(); HandleCoyoteTime(); HandleJumpBuffer(); HandleMovement(); HandleJump(); UpdateAnimations(); } void HandleInput() { horizontalInput = Input.GetAxisRaw("Horizontal"); if (Input.GetKeyDown(KeyCode.Space) || Input.GetKeyDown(KeyCode.W)) { jumpInput = true; jumpBufferCounter = jumpBufferTime; } else { jumpInput = false; } } void CheckGrounded() { bool wasGrounded = isGrounded; isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundLayerMask); // Landing sound effect if (!wasGrounded && isGrounded) { PlaySound("Land"); } } void HandleCoyoteTime() { if (isGrounded) { coyoteTimeCounter = coyoteTime; } else { coyoteTimeCounter -= Time.deltaTime; } } void HandleJumpBuffer() { if (jumpBufferCounter > 0) { jumpBufferCounter -= Time.deltaTime; } } void HandleMovement() { // Horizontal movement với smooth acceleration float targetVelocity = horizontalInput * moveSpeed; rb.velocity = new Vector2( Mathf.MoveTowards(rb.velocity.x, targetVelocity, moveSpeed * 2 * Time.deltaTime), rb.velocity.y ); // Sprite flipping if (horizontalInput != 0) { spriteRenderer.flipX = horizontalInput < 0; } } void HandleJump() { // Jump with coyote time và buffer if (jumpBufferCounter > 0 && coyoteTimeCounter > 0) { rb.velocity = new Vector2(rb.velocity.x, jumpForce); jumpBufferCounter = 0; coyoteTimeCounter = 0; PlaySound("Jump"); // Particle effect PlayJumpEffect(); } // Variable jump height if (Input.GetKeyUp(KeyCode.Space) && rb.velocity.y > 0) { rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * 0.5f); } } void UpdateAnimations() { animator.SetFloat(speedHash, Mathf.Abs(rb.velocity.x)); animator.SetBool(jumpHash, !isGrounded && rb.velocity.y > 0.1f); animator.SetBool(groundedHash, isGrounded); } void PlaySound(string soundName) { // Implementation would load và play audio clip AudioManager.Instance.PlaySFX(soundName); } void PlayJumpEffect() { // Instantiate particle effect EffectManager.Instance.PlayEffect("JumpDust", groundCheck.position); } void OnDrawGizmosSelected() { // Visualize ground check trong Scene view if (groundCheck != null) { Gizmos.color = isGrounded ? Color.green : Color.red; Gizmos.DrawWireSphere(groundCheck.position, groundCheckRadius); } } } ``` ## 3. Camera System Implementation ### Dynamic Camera Controller: ```csharp public class CameraController : MonoBehaviour { [Header("Target Settings")] public Transform target; public Vector3 offset = new Vector3(0, 2, -10); [Header("Follow Settings")] public float smoothTime = 0.3f; public bool lookAhead = true; public float lookAheadDistance = 3f; public float lookAheadSmoothTime = 1f; [Header("Boundaries")] public Vector2 minBounds; public Vector2 maxBounds; private Vector3 velocity = Vector3.zero; private float lookAheadPos; private float lookAheadVelocity; private PlayerController playerController; void Start() { playerController = target.GetComponent<PlayerController>(); } void LateUpdate() { if (target == null) return; Vector3 targetPosition = target.position + offset; // Look ahead system if (lookAhead) { float playerInputX = Input.GetAxisRaw("Horizontal"); float targetLookAhead = playerInputX * lookAheadDistance; lookAheadPos = Mathf.SmoothDamp( lookAheadPos, targetLookAhead, ref lookAheadVelocity, lookAheadSmoothTime ); targetPosition.x += lookAheadPos; } // Apply boundary constraints targetPosition.x = Mathf.Clamp(targetPosition.x, minBounds.x, maxBounds.x); targetPosition.y = Mathf.Clamp(targetPosition.y, minBounds.y, maxBounds.y); // Smooth camera movement transform.position = Vector3.SmoothDamp( transform.position, targetPosition, ref velocity, smoothTime ); } } ``` ## 4. Game Systems Implementation ### Collectible System: ```csharp public class Crystal : MonoBehaviour { [Header("Settings")] public int pointValue = 10; public AudioClip collectSound; public GameObject collectEffect; private bool isCollected = false; void Start() { // Idle animation StartCoroutine(FloatAnimation()); } void OnTriggerEnter2D(Collider2D other) { if (other.CompareTag("Player") && !isCollected) { CollectCrystal(); } } void CollectCrystal() { isCollected = true; // Update game manager GameManager.Instance.AddScore(pointValue); GameManager.Instance.CollectCrystal(); // Visual và audio feedback if (collectEffect != null) { Instantiate(collectEffect, transform.position, Quaternion.identity); } AudioManager.Instance.PlaySFX("Crystal"); // Animate collection StartCoroutine(CollectAnimation()); } IEnumerator FloatAnimation() { Vector3 startPos = transform.position; while (!isCollected) { float newY = startPos.y + Mathf.Sin(Time.time * 2f) * 0.3f; transform.position = new Vector3(startPos.x, newY, startPos.z); transform.Rotate(0, 0, 90 * Time.deltaTime); yield return null; } } IEnumerator CollectAnimation() { float timer = 0f; Vector3 startPos = transform.position; Vector3 targetPos = startPos + Vector3.up * 2f; while (timer < 0.5f) { timer += Time.deltaTime; float progress = timer / 0.5f; transform.position = Vector3.Lerp(startPos, targetPos, progress); transform.localScale = Vector3.Lerp(Vector3.one, Vector3.zero, progress); yield return null; } Destroy(gameObject); } } ``` ## 5. Level Design & Progression ### Level Structure: - **Tutorial Level:** Basic movement và jumping - **Level 1-3:** Core mechanics introduction - **Level 4-6:** Platforming challenges - **Level 7-9:** Enemy encounters - **Level 10:** Boss fight và story conclusion ### Design Principles: 1. **Introduce:** New mechanic trong safe environment 2. **Develop:** Practice mechanic với increasing complexity 3. **Twist:** Combine với previous mechanics 4. **Conclude:** Master-level challenge ### Level Completion Metrics: - **Primary Objective:** Reach the end portal - **Secondary Objectives:** Collect all crystals, finish under time limit - **Star Rating System:** 1-3 stars based on completion criteria ## 6. Audio & Polish ### Audio Implementation: - **Adaptive Music:** Intensity changes based on player state - **Spatial Audio:** 3D positioned sound effects - **Audio Mixing:** Dynamic volume balancing - **Accessibility:** Visual representations của audio cues ### Juice & Polish Elements: - **Screen Shake:** Landing impacts, damage effects - **Particle Systems:** Dust clouds, sparkles, explosions - **UI Animations:** Smooth transitions, bouncy buttons - **Responsive Feedback:** Every action has immediate response ## 7. Deployment & Publishing ### Build Settings: - **Target Platform:** PC (Windows, Mac, Linux) - **Resolution:** 1920x1080 với scaling support - **Quality Settings:** Optimized for various hardware - **Input Support:** Keyboard và gamepad ### Publishing Checklist: - [ ] All levels tested và balanced - [ ] Audio levels properly mixed - [ ] Performance optimized (60+ FPS) - [ ] Save system implemented - [ ] Settings menu functional - [ ] Credits và legal info included ### Distribution Options: - **Steam:** Premium release ($9.99-$14.99) - **Itch.io:** Indie-friendly platform - **Game Jolt:** Free với optional donations - **Mobile Ports:** iOS/Android versions
Playable 2D platformer với multiple levels, polished gameplay, và proper game feel
Team Cherry
Create atmospheric 2D Metroidvania với limited budget và small team (3 people)
Focused on exceptional art direction, tight controls, và atmospheric audio. Used hand-drawn animations và carefully crafted world design.
3M+ copies sold, 97% positive Steam reviews, considered modern classic trong Metroidvania genre