Initial Commit

This commit is contained in:
2026-03-16 14:38:46 +02:00
commit b8f7327a21
2327 changed files with 253610 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1c5580cd97b1c0e4db11f0e60860e945
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,254 @@
using System;
using FlappyBird.Core;
using UnityEngine;
using Zenject;
namespace FlappyBird.Bird
{
/// <summary>
/// Pure service class that owns all bird physics logic.
/// No MonoBehaviour ? follows AI_RULES.md architecture rule #1.
/// Registered via Zenject and driven by BirdView.
/// </summary>
public class BirdController : IBirdController, IDisposable
{
private readonly BirdSettings _settings;
private readonly IGameManager _gameManager;
private readonly IDeathManager _deathManager;
private BirdView _view;
private bool _inputEnabled;
private float _rotationVelocity;
private Vector3 _startPosition;
[Inject]
public BirdController(BirdSettings settings, IGameManager gameManager, IDeathManager deathManager)
{
_settings = settings;
_gameManager = gameManager;
_deathManager = deathManager;
_gameManager.OnGameStateChanged += HandleGameStateChanged;
_deathManager.OnDied += HandleDied;
}
public void Bind(BirdView view)
{
_view = view;
_startPosition = _view.transform.position;
ApplyState(_gameManager.CurrentState);
}
/// <summary>
/// Spawns bird in a frozen state waiting for first input.
/// </summary>
public void Initialize()
{
if (_view == null)
{
_inputEnabled = false;
_rotationVelocity = 0f;
return;
}
_inputEnabled = false;
_rotationVelocity = 0f;
_view.transform.position = _startPosition;
_view.Rigidbody.constraints = RigidbodyConstraints2D.None;
_view.Rigidbody.gravityScale = 0f;
_view.Rigidbody.linearVelocity = Vector2.zero;
_view.Rigidbody.angularVelocity = 0f;
_view.transform.rotation = Quaternion.identity;
}
/// <summary>
/// Applies upward impulse and un-freezes physics on first tap.
/// </summary>
public void Jump()
{
if (_view == null)
{
return;
}
if (_gameManager.CurrentState == GameState.GameOver)
{
RestartRun();
return;
}
if (_deathManager.IsDead)
{
return;
}
if (_gameManager.CurrentState == GameState.Menu)
{
_gameManager.StartGame();
}
JumpInternal();
}
public void HandleCollision()
{
_deathManager.Die();
}
public void SetInputEnabled(bool enabled)
{
_inputEnabled = enabled;
if (_view == null)
{
return;
}
if (!enabled)
{
_view.Rigidbody.gravityScale = 0f;
_view.Rigidbody.linearVelocity = Vector2.zero;
_view.Rigidbody.angularVelocity = 0f;
return;
}
_view.Rigidbody.gravityScale = _settings.activeGravityScale;
}
/// <summary>
/// Clamps fall velocity and rotates the bird based on vertical speed.
/// Called from BirdView.FixedUpdate ? contains NO input polling.
/// </summary>
public void FixedTick()
{
if (!_inputEnabled || _view == null)
{
return;
}
ApplyGravityScale();
ClampRiseVelocity();
ClampFallVelocity();
ApplyRotation();
}
public void Dispose()
{
_gameManager.OnGameStateChanged -= HandleGameStateChanged;
_deathManager.OnDied -= HandleDied;
}
private void HandleGameStateChanged(GameState state)
{
ApplyState(state);
}
private void HandleDied()
{
if (_view == null)
{
_inputEnabled = false;
_rotationVelocity = 0f;
return;
}
_inputEnabled = false;
_rotationVelocity = 0f;
_view.Rigidbody.constraints = RigidbodyConstraints2D.None;
_view.Rigidbody.gravityScale = _settings.activeGravityScale * _settings.fallGravityMultiplier;
_view.Rigidbody.linearVelocity = new Vector2(
_view.Rigidbody.linearVelocity.x,
Mathf.Min(_view.Rigidbody.linearVelocity.y, 0f));
_view.Rigidbody.angularVelocity = -Mathf.Abs(_settings.rotationSpeed);
}
private void ApplyState(GameState state)
{
if (state == GameState.Menu)
{
Initialize();
return;
}
if (state == GameState.GameOver && !_deathManager.IsDead)
{
SetInputEnabled(false);
}
}
private void RestartRun()
{
_gameManager.ReturnToMenu();
_gameManager.StartGame();
JumpInternal();
}
private void JumpInternal()
{
if (!_inputEnabled)
{
SetInputEnabled(true);
}
Vector2 velocity = _view.Rigidbody.linearVelocity;
float carriedFallVelocity = Mathf.Min(velocity.y, 0f) * _settings.fallVelocityCarryover;
_view.Rigidbody.gravityScale = _settings.activeGravityScale * _settings.riseGravityMultiplier;
_view.Rigidbody.linearVelocity = new Vector2(velocity.x, _settings.jumpStartVelocity + carriedFallVelocity);
_view.Rigidbody.AddForce(Vector2.up * _settings.jumpForce, ForceMode2D.Impulse);
ClampRiseVelocity();
}
private void ApplyGravityScale()
{
float verticalVelocity = _view.Rigidbody.linearVelocity.y;
float gravityMultiplier = verticalVelocity >= 0f
? _settings.riseGravityMultiplier
: _settings.fallGravityMultiplier;
_view.Rigidbody.gravityScale = _settings.activeGravityScale * gravityMultiplier;
}
private void ClampRiseVelocity()
{
Vector2 velocity = _view.Rigidbody.linearVelocity;
if (velocity.y > _settings.maxRiseVelocity)
{
_view.Rigidbody.linearVelocity = new Vector2(velocity.x, _settings.maxRiseVelocity);
}
}
private void ClampFallVelocity()
{
Vector2 velocity = _view.Rigidbody.linearVelocity;
if (velocity.y < _settings.maxFallVelocity)
{
_view.Rigidbody.linearVelocity = new Vector2(velocity.x, _settings.maxFallVelocity);
}
}
private void ApplyRotation()
{
float verticalVelocity = _view.Rigidbody.linearVelocity.y;
float targetAngle = verticalVelocity >= 0f
? Mathf.Lerp(0f, _settings.maxUpAngle, Mathf.InverseLerp(0f, _settings.upAngleVelocity, verticalVelocity))
: Mathf.Lerp(0f, _settings.maxDownAngle, Mathf.InverseLerp(0f, Mathf.Abs(_settings.downAngleVelocity), Mathf.Abs(verticalVelocity)));
float currentAngle = _view.transform.eulerAngles.z;
if (currentAngle > 180f)
{
currentAngle -= 360f;
}
float smoothTime = Mathf.Max(0.02f, 180f / Mathf.Max(1f, _settings.rotationSpeed) * 0.1f);
float newAngle = Mathf.SmoothDampAngle(
currentAngle,
targetAngle,
ref _rotationVelocity,
smoothTime,
Mathf.Max(1f, _settings.rotationSpeed),
Time.fixedDeltaTime);
_view.transform.rotation = Quaternion.Euler(0f, 0f, newAngle);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b5b67107af99c5a4f900c6901da708db
@@ -0,0 +1,55 @@
using UnityEngine;
namespace FlappyBird.Bird
{
/// <summary>
/// Configurable physics and rotation settings for the bird.
/// Create via Assets > Create > FlappyBird > Bird Settings.
/// </summary>
[CreateAssetMenu(fileName = "BirdSettings", menuName = "FlappyBird/Bird Settings")]
public class BirdSettings : ScriptableObject
{
[Header("Jump")]
[Tooltip("Upward force applied on each tap.")]
public float jumpForce = 5f;
[Tooltip("Vertical velocity applied before jump force. Useful for snappier flaps.")]
public float jumpStartVelocity = 0f;
[Tooltip("Maximum upward speed after a jump.")]
public float maxRiseVelocity = 8f;
[Tooltip("How much current falling speed is preserved when jumping. 0 = reset fall fully.")]
[Range(0f, 1f)]
public float fallVelocityCarryover = 0f;
[Header("Movement")]
[Tooltip("Gravity scale while the bird is active.")]
public float activeGravityScale = 1f;
[Tooltip("Extra gravity applied while the bird is falling.")]
public float fallGravityMultiplier = 1.5f;
[Tooltip("Extra gravity applied while the bird is rising.")]
public float riseGravityMultiplier = 1f;
[Tooltip("Terminal fall velocity clamp (negative value).")]
public float maxFallVelocity = -10f;
[Header("Rotation")]
[Tooltip("Maximum rotation angle when moving upward (degrees).")]
public float maxUpAngle = 30f;
[Tooltip("Maximum rotation angle when falling (degrees, negative = nose-down).")]
public float maxDownAngle = -90f;
[Tooltip("Vertical speed used to reach max upward angle.")]
public float upAngleVelocity = 5f;
[Tooltip("Vertical speed used to reach max downward angle.")]
public float downAngleVelocity = -10f;
[Tooltip("Degrees per second for rotation lerp towards target angle.")]
public float rotationSpeed = 360f;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 25db2a2dd51cc6f4fadb316f81d7b382
+103
View File
@@ -0,0 +1,103 @@
using FlappyBird.Pipes;
using UnityEngine;
using Zenject;
using FlappyBird.Ground;
namespace FlappyBird.Bird
{
/// <summary>
/// MonoBehaviour facade for the Bird prefab.
/// Responsibilities (AI_RULES.md rule #1):
/// - Holds component references
/// - Forwards Unity lifecycle events to BirdController
/// - Detects input in Update only ? all logic lives in BirdController
/// </summary>
[RequireComponent(typeof(Rigidbody2D))]
public class BirdView : MonoBehaviour
{
public Rigidbody2D Rigidbody { get; private set; }
private IBirdController _controller;
[Inject]
public void Construct(IBirdController controller)
{
_controller = controller;
}
private void Awake()
{
Rigidbody = GetComponent<Rigidbody2D>();
}
private void Start()
{
_controller.Bind(this);
}
/// <summary>
/// Input detection only ? no logic.
/// Delegates to IBirdController on tap/click/space.
/// </summary>
private void Update()
{
if (_controller == null)
{
return;
}
if (Input.GetMouseButtonDown(0) || Input.GetKeyDown(KeyCode.Space))
{
_controller.Jump();
}
}
/// <summary>
/// Physics-tick delegation ? no input here.
/// </summary>
private void FixedUpdate()
{
if (_controller == null)
{
return;
}
_controller.FixedTick();
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (_controller == null || collision == null)
{
return;
}
if (!IsPipeCollision(collision.collider) && !IsGroundCollision(collision.collider))
{
return;
}
_controller.HandleCollision();
}
private void OnTriggerEnter2D(Collider2D other)
{
if (_controller == null || other == null || !IsGroundCollision(other))
{
return;
}
_controller.HandleCollision();
}
private static bool IsPipeCollision(Collider2D collider)
{
return collider.GetComponentInParent<PipeView>() != null;
}
private static bool IsGroundCollision(Collider2D collider)
{
return collider.GetComponentInParent<GroundView>() != null;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 128f85bdbe34ee14992703c62de302d0
@@ -0,0 +1,30 @@
namespace FlappyBird.Bird
{
/// <summary>
/// Contract for the bird physics controller service.
/// Separates logic from the MonoBehaviour view layer.
/// </summary>
public interface IBirdController
{
/// <summary>Attaches the spawned bird view to the controller.</summary>
void Bind(BirdView view);
/// <summary>Initializes the bird at the given spawn position and freezes physics.</summary>
void Initialize();
/// <summary>Applies an upward impulse and enables physics simulation.</summary>
void Jump();
/// <summary>Reports a lethal collision from the view layer.</summary>
void HandleCollision();
/// <summary>
/// Enables or disables input processing.
/// Pass false to freeze the bird before the first tap.
/// </summary>
void SetInputEnabled(bool enabled);
/// <summary>Called every fixed-update tick to apply rotation.</summary>
void FixedTick();
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 1713e69de72bd3843ac048392b2483b9
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2418ea2830a9ae84cb75389256ea2025
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,23 @@
using UnityEngine;
namespace FlappyBird.Ground
{
[CreateAssetMenu(fileName = "GroundSettings", menuName = "FlappyBird/Ground Settings")]
public class GroundSettings : ScriptableObject
{
[Tooltip("World X position where the first ground segment starts.")]
public float spawnX = -5f;
[Tooltip("World X position where ground segments are returned to the pool.")]
public float despawnX = -15f;
[Tooltip("The exact width of the ground sprite/collider to ensure seamless looping.")]
public float groundWidth = 10f;
[Tooltip("World Y position where the ground is placed.")]
public float yPosition = -4f;
[Tooltip("Number of ground segments to create up front.")]
public int initialPoolSize = 3;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2646352a67506b34181e2563502f05fb
@@ -0,0 +1,127 @@
using FlappyBird.Core;
using FlappyBird.Ground;
using FlappyBird.Pipes;
using System;
using System.Collections.Generic;
using UnityEngine;
using Zenject;
namespace FlappyBird.Environment
{
public class GroundSpawner : IInitializable, ITickable, IDisposable
{
private readonly IObjectPoolManager _objectPoolManager;
private readonly IGameManager _gameManager;
private readonly PipeSettings _pipeSettings;
private readonly GroundSettings _groundSettings;
private readonly GameObject _groundPrefab;
private readonly List<GroundView> _activeGrounds = new List<GroundView>();
private bool _isRunning;
[Inject]
public GroundSpawner(
IObjectPoolManager objectPoolManager,
IGameManager gameManager,
PipeSettings pipeSettings,
GroundSettings groundSettings,
[Inject(Id = "GroundPrefab")] GameObject groundPrefab)
{
_objectPoolManager = objectPoolManager;
_gameManager = gameManager;
_pipeSettings = pipeSettings;
_groundSettings = groundSettings;
_groundPrefab = groundPrefab;
}
/// <summary>
/// Initializes the ground system by registering the object pool and
/// spawning initial segments. They will remain stationary until the game starts.
/// </summary>
public void Initialize()
{
_objectPoolManager.RegisterPool<GroundView>(_groundPrefab, _groundSettings.initialPoolSize);
_gameManager.OnGameStateChanged += HandleGameStateChanged;
// Pre-spawn segments so the floor is visible in the Menu state
float currentX = _groundSettings.spawnX;
for (int i = 0; i < Mathf.Max(1, _groundSettings.initialPoolSize); i++)
{
SpawnGround(currentX);
currentX += _groundSettings.groundWidth;
}
// Sets initial movement state based on current GameManager state (Menu = stopped)
ApplyState(_gameManager.CurrentState);
}
public void Dispose()
{
_gameManager.OnGameStateChanged -= HandleGameStateChanged;
}
public void Tick()
{
if (!_isRunning)
{
return;
}
MoveActiveGrounds();
}
private void HandleGameStateChanged(GameState state)
{
ApplyState(state);
}
private void ApplyState(GameState state)
{
// Ground only moves during active gameplay
_isRunning = state == GameState.Playing;
}
private void MoveActiveGrounds()
{
float step = _pipeSettings.moveSpeed * Time.deltaTime;
float rightmostX = GetRightmostGroundX();
for (int i = _activeGrounds.Count - 1; i >= 0; i--)
{
GroundView groundView = _activeGrounds[i];
groundView.MoveLeft(step);
if (groundView.XPosition > _groundSettings.despawnX)
{
continue;
}
_activeGrounds.RemoveAt(i);
_objectPoolManager.Return(groundView);
// Re-spawn at the end of the chain
SpawnGround(rightmostX + _groundSettings.groundWidth - step);
rightmostX += _groundSettings.groundWidth;
}
}
private float GetRightmostGroundX()
{
float maxX = float.MinValue;
foreach (var ground in _activeGrounds)
{
if (ground.XPosition > maxX) maxX = ground.XPosition;
}
return maxX == float.MinValue ? _groundSettings.spawnX : maxX;
}
private void SpawnGround(float xPosition)
{
GroundView groundView = _objectPoolManager.Get<GroundView>();
groundView.SetPosition(new Vector3(xPosition, _groundSettings.yPosition, 0f));
_activeGrounds.Add(groundView);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 9b7f0d695b68afb4bbc3556af67e7c7b
@@ -0,0 +1,37 @@
using FlappyBird.Core;
using FlappyBird.Pipes;
using UnityEngine;
using Zenject;
namespace FlappyBird.Ground
{
public class GroundView : MonoBehaviour, Core.IPoolable
{
private Transform _cachedTransform;
public float XPosition => _cachedTransform.position.x;
private void Awake()
{
_cachedTransform = transform;
}
public void SetPosition(Vector3 position)
{
_cachedTransform.position = position;
}
public void MoveLeft(float distance)
{
_cachedTransform.position += Vector3.left * distance;
}
public void OnSpawned()
{
}
public void OnDespawned()
{
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 0d496ba852926f14cb1a0cbbda11884f
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: eb893dec691073e4b86b89c57a58400f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,37 @@
using FlappyBird.Core;
using UnityEngine;
namespace FlappyBird.Pipes
{
public class PipeScoreTrigger : MonoBehaviour
{
private IScoreManager _scoreManager;
private bool _canScore;
public void Initialize(IScoreManager scoreManager)
{
_scoreManager = scoreManager;
}
public void ResetTrigger()
{
_canScore = true;
}
private void OnTriggerEnter2D(Collider2D other)
{
if (!_canScore || _scoreManager == null)
{
return;
}
if (!other.CompareTag("Player"))
{
return;
}
_canScore = false;
_scoreManager.AddScore(1);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 5e53bfbf6adf1dd4b8b2b230469171a9
@@ -0,0 +1,41 @@
using UnityEngine;
namespace FlappyBird.Pipes
{
[CreateAssetMenu(fileName = "PipeSettings", menuName = "FlappyBird/Pipe Settings")]
public class PipeSettings : ScriptableObject
{
[Header("Spawn")]
[Tooltip("Seconds between pipe spawns.")]
public float spawnInterval = 1.5f;
[Tooltip("World X position where new pipe pairs appear.")]
public float spawnX = 10f;
[Tooltip("World X position where pipe pairs are returned to the pool.")]
public float despawnX = -10f;
[Tooltip("Number of pipe pairs to create up front.")]
public int initialPoolSize = 4;
[Tooltip("Spawn the first pipe immediately when gameplay starts.")]
public bool spawnImmediately = false;
[Header("Movement")]
[Tooltip("Horizontal speed for active pipe pairs.")]
public float moveSpeed = 2.5f;
[Header("Gap")]
[Tooltip("Distance between the top and bottom pipes.")]
public float gapSize = 4f;
[Tooltip("Minimum vertical center position for the pipe gap.")]
public float minGapCenterY = -1.5f;
[Tooltip("Maximum vertical center position for the pipe gap.")]
public float maxGapCenterY = 2.5f;
[Tooltip("Width of the scoring trigger collider.")]
public float scoreTriggerWidth = 1f;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c5ca25bedc7c8714b87c9d32fbc5946b
@@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using FlappyBird.Core;
using UnityEngine;
using Zenject;
namespace FlappyBird.Pipes
{
public class PipeSpawner : IInitializable, ITickable, IDisposable
{
private readonly IObjectPoolManager _objectPoolManager;
private readonly IGameManager _gameManager;
private readonly PipeSettings _settings;
private readonly GameObject _pipePrefab;
private readonly List<PipeView> _activePipes = new List<PipeView>();
private bool _isRunning;
private float _spawnTimer;
[Inject]
public PipeSpawner(
IObjectPoolManager objectPoolManager,
IGameManager gameManager,
PipeSettings settings,
[Inject(Id = "PipePrefab")] GameObject pipePrefab)
{
_objectPoolManager = objectPoolManager;
_gameManager = gameManager;
_settings = settings;
_pipePrefab = pipePrefab;
}
/// <summary>
/// Initializes the spawner by registering the Pipe pool, subscribing to game state changes,
/// and setting the initial spawn timer based on settings.
/// </summary>
public void Initialize()
{
// Register the pool with the new generic ObjectPoolManager
_objectPoolManager.RegisterPool<PipeView>(_pipePrefab, _settings.initialPoolSize);
_gameManager.OnGameStateChanged += HandleGameStateChanged;
ApplyState(_gameManager.CurrentState);
}
public void Dispose()
{
_gameManager.OnGameStateChanged -= HandleGameStateChanged;
}
public void Tick()
{
if (!_isRunning)
{
return;
}
MoveActivePipes();
UpdateSpawnTimer();
}
private void HandleGameStateChanged(GameState state)
{
ApplyState(state);
}
private void ApplyState(GameState state)
{
if (state == GameState.Playing)
{
_isRunning = true;
_spawnTimer = _settings.spawnImmediately ? 0f : _settings.spawnInterval;
return;
}
_isRunning = false;
if (state == GameState.Menu)
{
DespawnAllActivePipes();
}
}
private void UpdateSpawnTimer()
{
_spawnTimer -= Time.deltaTime;
if (_spawnTimer > 0f)
{
return;
}
SpawnPipe();
_spawnTimer += Mathf.Max(0.01f, _settings.spawnInterval);
}
private void SpawnPipe()
{
PipeView pipeView = _objectPoolManager.Get<PipeView>();
// Randomize the vertical position of the gap
float gapCenterY = UnityEngine.Random.Range(_settings.minGapCenterY, _settings.maxGapCenterY);
pipeView.ConfigureGap(gapCenterY, _settings.gapSize, _settings.scoreTriggerWidth);
pipeView.SetWorldPosition(new Vector3(_settings.spawnX, 0f, 0f));
_activePipes.Add(pipeView);
}
private void MoveActivePipes()
{
float step = _settings.moveSpeed * Time.deltaTime;
for (int i = _activePipes.Count - 1; i >= 0; i--)
{
PipeView pipeView = _activePipes[i];
pipeView.MoveLeft(step);
// Despawn if the pipe has moved past the despawn threshold
if (pipeView.XPosition > _settings.despawnX)
{
continue;
}
_activePipes.RemoveAt(i);
_objectPoolManager.Return(pipeView);
}
}
private void DespawnAllActivePipes()
{
for (int i = _activePipes.Count - 1; i >= 0; i--)
{
_objectPoolManager.Return(_activePipes[i]);
}
_activePipes.Clear();
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 62dc8cef99ffb954b838a53c73cb8a0c
+110
View File
@@ -0,0 +1,110 @@
using FlappyBird.Core;
using UnityEngine;
using Zenject;
namespace FlappyBird.Pipes
{
public class PipeView : MonoBehaviour, global::FlappyBird.Core.IPoolable
{
private const string UpperPipeName = "Pipe_Upper";
private const string LowerPipeName = "Pipe_Lower";
private const string ScoreTriggerName = "ScoreTrigger";
private Transform _cachedTransform;
private Transform _upperPipe;
private Transform _lowerPipe;
private Transform _scoreTrigger;
private SpriteRenderer _upperRenderer;
private SpriteRenderer _lowerRenderer;
private BoxCollider2D _scoreTriggerCollider;
private PipeScoreTrigger _pipeScoreTrigger;
private FlappyBird.Core.IScoreManager _scoreManager;
public float XPosition => _cachedTransform.position.x;
[Inject]
public void Construct(FlappyBird.Core.IScoreManager scoreManager)
{
_scoreManager = scoreManager;
}
private void Awake()
{
_cachedTransform = transform;
_upperPipe = _cachedTransform.Find(UpperPipeName);
_lowerPipe = _cachedTransform.Find(LowerPipeName);
_scoreTrigger = _cachedTransform.Find(ScoreTriggerName);
if (_upperPipe != null)
{
_upperRenderer = _upperPipe.GetComponent<SpriteRenderer>();
}
if (_lowerPipe != null)
{
_lowerRenderer = _lowerPipe.GetComponent<SpriteRenderer>();
}
if (_scoreTrigger != null)
{
_scoreTriggerCollider = _scoreTrigger.GetComponent<BoxCollider2D>();
_pipeScoreTrigger = _scoreTrigger.GetComponent<PipeScoreTrigger>();
if (_pipeScoreTrigger == null)
{
_pipeScoreTrigger = _scoreTrigger.gameObject.AddComponent<PipeScoreTrigger>();
}
_pipeScoreTrigger.Initialize(_scoreManager);
}
}
public void SetWorldPosition(Vector3 position)
{
_cachedTransform.position = position;
}
public void MoveLeft(float distance)
{
_cachedTransform.position += Vector3.left * distance;
}
public void ConfigureGap(float gapCenterY, float gapSize, float scoreTriggerWidth)
{
if (_upperPipe == null || _lowerPipe == null || _upperRenderer == null || _lowerRenderer == null)
{
return;
}
float upperHalfHeight = _upperRenderer.size.y * 0.5f;
float lowerHalfHeight = _lowerRenderer.size.y * 0.5f;
float gapHalf = gapSize * 0.5f;
_upperPipe.localPosition = new Vector3(0f, gapCenterY + gapHalf + upperHalfHeight, 0f);
_lowerPipe.localPosition = new Vector3(0f, gapCenterY - gapHalf - lowerHalfHeight, 0f);
if (_scoreTrigger != null)
{
_scoreTrigger.localPosition = new Vector3(0f, gapCenterY, 0f);
}
if (_scoreTriggerCollider != null)
{
_scoreTriggerCollider.size = new Vector2(scoreTriggerWidth, gapSize);
_scoreTriggerCollider.offset = Vector2.zero;
}
}
public void OnSpawned()
{
if (_pipeScoreTrigger != null)
{
_pipeScoreTrigger.ResetTrigger();
}
}
public void OnDespawned()
{
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 12677c2e66077cc4f89582de3982d3e7