Some more airplane game doodles
This commit is contained in:
@@ -199,7 +199,7 @@ namespace Minigames.Airplane.Core
|
||||
HandleLanding();
|
||||
yield break;
|
||||
}
|
||||
|
||||
|
||||
yield return null; // Update every frame, not just fixed update
|
||||
}
|
||||
}
|
||||
@@ -209,7 +209,7 @@ namespace Minigames.Airplane.Core
|
||||
#region Collision Detection
|
||||
|
||||
/// <summary>
|
||||
/// Detect trigger collisions with targets
|
||||
/// Detect trigger collisions with targets and ground
|
||||
/// </summary>
|
||||
private void OnTriggerEnter2D(Collider2D other)
|
||||
{
|
||||
@@ -227,6 +227,15 @@ namespace Minigames.Airplane.Core
|
||||
|
||||
// Land after hitting target
|
||||
HandleLanding();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if it's ground (by layer)
|
||||
var settings = GameManager.GetSettingsObject<AppleHills.Core.Settings.IAirplaneSettings>();
|
||||
if (settings != null && other.gameObject.layer == settings.GroundLayer)
|
||||
{
|
||||
if (showDebugLogs) Logging.Debug($"[AirplaneController] Hit ground at Y={transform.position.y:F2}");
|
||||
HandleLanding();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using AppleHills.Core.Settings;
|
||||
using Core;
|
||||
using Core.Lifecycle;
|
||||
using Minigames.Airplane.Data;
|
||||
using Minigames.Airplane.UI;
|
||||
using UnityEngine;
|
||||
using Random = UnityEngine.Random;
|
||||
@@ -25,6 +26,18 @@ namespace Minigames.Airplane.Core
|
||||
|
||||
[Tooltip("Prefab to spawn for this target")]
|
||||
public GameObject prefab;
|
||||
|
||||
[Tooltip("How to position this target vertically")]
|
||||
public SpawnPositionMode spawnPositionMode = SpawnPositionMode.SnapToGround;
|
||||
|
||||
[Tooltip("Y position to use (SpecifiedY mode)")]
|
||||
public float specifiedY = 0f;
|
||||
|
||||
[Tooltip("Min Y for random range (RandomRange mode)")]
|
||||
public float randomYMin = -5f;
|
||||
|
||||
[Tooltip("Max Y for random range (RandomRange mode)")]
|
||||
public float randomYMax = 5f;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -35,11 +48,11 @@ namespace Minigames.Airplane.Core
|
||||
[Tooltip("Dictionary of target prefabs (key = target name)")]
|
||||
[SerializeField] private TargetPrefabEntry[] targetPrefabs;
|
||||
|
||||
[Tooltip("Array of positive object prefabs")]
|
||||
[SerializeField] private GameObject[] positiveObjectPrefabs;
|
||||
[Tooltip("Array of positive object prefabs with spawn configuration")]
|
||||
[SerializeField] private PrefabSpawnEntry[] positiveObjectPrefabs;
|
||||
|
||||
[Tooltip("Array of negative object prefabs")]
|
||||
[SerializeField] private GameObject[] negativeObjectPrefabs;
|
||||
[Tooltip("Array of negative object prefabs with spawn configuration")]
|
||||
[SerializeField] private PrefabSpawnEntry[] negativeObjectPrefabs;
|
||||
|
||||
[Tooltip("Array of ground tile prefabs")]
|
||||
[SerializeField] private GameObject[] groundTilePrefabs;
|
||||
@@ -52,6 +65,10 @@ namespace Minigames.Airplane.Core
|
||||
[Tooltip("Launch controller (provides launch anchor position for distance calculation)")]
|
||||
[SerializeField] private AirplaneLaunchController launchController;
|
||||
|
||||
[Header("Spawn Threshold")]
|
||||
[Tooltip("Transform marker in scene where dynamic spawning begins (uses X position). If null, uses fallback from settings.")]
|
||||
[SerializeField] private Transform dynamicSpawnThresholdMarker;
|
||||
|
||||
[Header("Spawn Parents")]
|
||||
[Tooltip("Parent transform for spawned objects (optional, for organization)")]
|
||||
[SerializeField] private Transform spawnedObjectsParent;
|
||||
@@ -77,6 +94,7 @@ namespace Minigames.Airplane.Core
|
||||
private Sprite _targetIconSprite;
|
||||
private GameObject _spawnedTarget;
|
||||
private GameObject _targetPrefabToSpawn;
|
||||
private TargetPrefabEntry _currentTargetEntry;
|
||||
private bool _hasSpawnedTarget;
|
||||
|
||||
// Plane tracking
|
||||
@@ -84,8 +102,8 @@ namespace Minigames.Airplane.Core
|
||||
private bool _isSpawningActive;
|
||||
private bool _hasPassedThreshold;
|
||||
|
||||
// Spawning timers
|
||||
private float _nextObjectSpawnTime;
|
||||
// Spawning positions (distance-based)
|
||||
private float _nextObjectSpawnX;
|
||||
private float _nextGroundSpawnX;
|
||||
|
||||
// Spawn statistics (for weighted ratio adjustment)
|
||||
@@ -97,7 +115,7 @@ namespace Minigames.Airplane.Core
|
||||
private bool _isRetryAttempt;
|
||||
|
||||
// Cached dictionaries
|
||||
private Dictionary<string, GameObject> _targetPrefabDict;
|
||||
private Dictionary<string, TargetPrefabEntry> _targetPrefabDict;
|
||||
private IAirplaneSettings _settings;
|
||||
|
||||
#endregion
|
||||
@@ -148,7 +166,9 @@ namespace Minigames.Airplane.Core
|
||||
}
|
||||
|
||||
// Check if plane has crossed threshold
|
||||
if (!_hasPassedThreshold && planeX >= _settings.DynamicSpawnThreshold)
|
||||
float threshold = dynamicSpawnThresholdMarker.position.x;
|
||||
|
||||
if (!_hasPassedThreshold && planeX >= threshold)
|
||||
{
|
||||
_hasPassedThreshold = true;
|
||||
InitializeDynamicSpawning();
|
||||
@@ -167,11 +187,11 @@ namespace Minigames.Airplane.Core
|
||||
|
||||
if (shouldSpawnNewContent)
|
||||
{
|
||||
// Spawn objects at intervals
|
||||
if (Time.time >= _nextObjectSpawnTime)
|
||||
// Spawn objects when plane reaches spawn position
|
||||
if (planeX >= _nextObjectSpawnX)
|
||||
{
|
||||
SpawnRandomObject();
|
||||
ScheduleNextObjectSpawn();
|
||||
ScheduleNextObjectSpawn(planeX);
|
||||
}
|
||||
|
||||
// Spawn ground tiles ahead of plane
|
||||
@@ -229,13 +249,15 @@ namespace Minigames.Airplane.Core
|
||||
}
|
||||
}
|
||||
|
||||
// Find target prefab and extract icon WITHOUT spawning
|
||||
if (!_targetPrefabDict.TryGetValue(_currentTargetKey, out _targetPrefabToSpawn))
|
||||
// Find target entry and extract icon WITHOUT spawning
|
||||
if (!_targetPrefabDict.TryGetValue(_currentTargetKey, out _currentTargetEntry))
|
||||
{
|
||||
Logging.Error($"[SpawnManager] Target prefab not found for key '{_currentTargetKey}'!");
|
||||
return;
|
||||
}
|
||||
|
||||
_targetPrefabToSpawn = _currentTargetEntry.prefab;
|
||||
|
||||
// Extract icon from prefab (doesn't need to be instantiated)
|
||||
ExtractTargetIconFromPrefab(_targetPrefabToSpawn);
|
||||
|
||||
@@ -400,7 +422,7 @@ namespace Minigames.Airplane.Core
|
||||
/// </summary>
|
||||
private void BuildTargetDictionary()
|
||||
{
|
||||
_targetPrefabDict = new Dictionary<string, GameObject>();
|
||||
_targetPrefabDict = new Dictionary<string, TargetPrefabEntry>();
|
||||
|
||||
if (targetPrefabs == null || targetPrefabs.Length == 0)
|
||||
{
|
||||
@@ -428,7 +450,7 @@ namespace Minigames.Airplane.Core
|
||||
continue;
|
||||
}
|
||||
|
||||
_targetPrefabDict[entry.targetKey] = entry.prefab;
|
||||
_targetPrefabDict[entry.targetKey] = entry;
|
||||
}
|
||||
|
||||
if (showDebugLogs)
|
||||
@@ -482,24 +504,28 @@ namespace Minigames.Airplane.Core
|
||||
/// </summary>
|
||||
private void SpawnTarget()
|
||||
{
|
||||
if (!_targetPrefabDict.TryGetValue(_currentTargetKey, out GameObject targetPrefab))
|
||||
if (_currentTargetEntry == null || _currentTargetEntry.prefab == null)
|
||||
{
|
||||
Logging.Error($"[SpawnManager] Target prefab not found for key '{_currentTargetKey}'!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Spawn target at initial position
|
||||
_spawnedTarget = Instantiate(targetPrefab, _targetSpawnPosition, Quaternion.identity);
|
||||
_spawnedTarget = Instantiate(_currentTargetEntry.prefab, _targetSpawnPosition, Quaternion.identity);
|
||||
|
||||
if (spawnedObjectsParent != null)
|
||||
{
|
||||
_spawnedTarget.transform.SetParent(spawnedObjectsParent);
|
||||
}
|
||||
|
||||
// Snap target to ground
|
||||
SnapObjectToGround(_spawnedTarget, _targetSpawnPosition.x);
|
||||
// Position target using configured spawn mode
|
||||
PositionObject(_spawnedTarget, _targetSpawnPosition.x,
|
||||
_currentTargetEntry.spawnPositionMode,
|
||||
_currentTargetEntry.specifiedY,
|
||||
_currentTargetEntry.randomYMin,
|
||||
_currentTargetEntry.randomYMax);
|
||||
|
||||
// Update target spawn position to actual snapped position
|
||||
// Update target spawn position to actual positioned location
|
||||
_targetSpawnPosition = _spawnedTarget.transform.position;
|
||||
|
||||
// Extract sprite for UI icon
|
||||
@@ -591,11 +617,16 @@ namespace Minigames.Airplane.Core
|
||||
/// </summary>
|
||||
private void InitializeDynamicSpawning()
|
||||
{
|
||||
ScheduleNextObjectSpawn();
|
||||
|
||||
if (showDebugLogs)
|
||||
// Schedule first spawn trigger from current plane position
|
||||
// Actual spawning will happen at look-ahead distance
|
||||
if (_planeTransform != null)
|
||||
{
|
||||
Logging.Debug("[SpawnManager] Dynamic spawning initialized");
|
||||
ScheduleNextObjectSpawn(_planeTransform.position.x);
|
||||
|
||||
if (showDebugLogs)
|
||||
{
|
||||
Logging.Debug($"[SpawnManager] Dynamic spawning initialized, first spawn trigger at planeX={_nextObjectSpawnX:F2}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -608,24 +639,32 @@ namespace Minigames.Airplane.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Schedule the next object spawn based on random interval.
|
||||
/// Schedule the next object spawn based on random distance from current position.
|
||||
/// </summary>
|
||||
private void ScheduleNextObjectSpawn()
|
||||
/// <param name="currentX">Current X position (usually plane's X or last spawn X)</param>
|
||||
private void ScheduleNextObjectSpawn(float currentX)
|
||||
{
|
||||
float interval = Random.Range((float)_settings.ObjectSpawnMinInterval, (float)_settings.ObjectSpawnMaxInterval);
|
||||
_nextObjectSpawnTime = Time.time + interval;
|
||||
float spawnDistance = Random.Range(_settings.ObjectSpawnMinDistance, _settings.ObjectSpawnMaxDistance);
|
||||
_nextObjectSpawnX = currentX + spawnDistance;
|
||||
|
||||
if (showDebugLogs)
|
||||
{
|
||||
Logging.Debug($"[SpawnManager] Next object scheduled at X={_nextObjectSpawnX:F2} (distance: {spawnDistance:F2} from {currentX:F2})");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawn a random positive or negative object.
|
||||
/// Uses weighted randomness to maintain target ratio.
|
||||
/// Avoids spawning near target position to prevent obscuring it.
|
||||
/// Objects spawn at look-ahead distance to ensure they're off-screen.
|
||||
/// </summary>
|
||||
private void SpawnRandomObject()
|
||||
{
|
||||
if (_planeTransform == null) return;
|
||||
|
||||
// Calculate spawn X position ahead of plane
|
||||
// Spawn at look-ahead distance from plane's current position
|
||||
// This ensures objects always spawn off-screen
|
||||
float spawnX = _planeTransform.position.x + _settings.SpawnDistanceAhead;
|
||||
|
||||
// Check if spawn position is too close to target (avoid obscuring it)
|
||||
@@ -634,24 +673,26 @@ namespace Minigames.Airplane.Core
|
||||
|
||||
if (distanceToTarget < targetClearanceZone)
|
||||
{
|
||||
// Too close to target, skip this spawn
|
||||
// Too close to target, skip this spawn and schedule the next one
|
||||
if (showDebugLogs)
|
||||
{
|
||||
Logging.Debug($"[SpawnManager] Skipped object spawn at X={spawnX:F2} (too close to target at X={_targetSpawnPosition.x:F2})");
|
||||
}
|
||||
// Schedule next spawn further ahead
|
||||
ScheduleNextObjectSpawn(spawnX);
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine if spawning positive or negative based on weighted ratio
|
||||
bool spawnPositive = ShouldSpawnPositive();
|
||||
|
||||
GameObject prefabToSpawn = null;
|
||||
PrefabSpawnEntry entryToSpawn = null;
|
||||
|
||||
if (spawnPositive)
|
||||
{
|
||||
if (positiveObjectPrefabs != null && positiveObjectPrefabs.Length > 0)
|
||||
{
|
||||
prefabToSpawn = positiveObjectPrefabs[UnityEngine.Random.Range(0, positiveObjectPrefabs.Length)];
|
||||
entryToSpawn = positiveObjectPrefabs[UnityEngine.Random.Range(0, positiveObjectPrefabs.Length)];
|
||||
_positiveSpawnCount++;
|
||||
}
|
||||
}
|
||||
@@ -659,25 +700,36 @@ namespace Minigames.Airplane.Core
|
||||
{
|
||||
if (negativeObjectPrefabs != null && negativeObjectPrefabs.Length > 0)
|
||||
{
|
||||
prefabToSpawn = negativeObjectPrefabs[UnityEngine.Random.Range(0, negativeObjectPrefabs.Length)];
|
||||
entryToSpawn = negativeObjectPrefabs[UnityEngine.Random.Range(0, negativeObjectPrefabs.Length)];
|
||||
_negativeSpawnCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (prefabToSpawn == null) return;
|
||||
if (entryToSpawn == null || entryToSpawn.prefab == null) return;
|
||||
|
||||
|
||||
// Spawn object at temporary position
|
||||
Vector3 tempPosition = new Vector3(spawnX, 0f, 0f);
|
||||
GameObject spawnedObject = Instantiate(prefabToSpawn, tempPosition, Quaternion.identity);
|
||||
GameObject spawnedObject = Instantiate(entryToSpawn.prefab, tempPosition, Quaternion.identity);
|
||||
|
||||
if (spawnedObjectsParent != null)
|
||||
{
|
||||
spawnedObject.transform.SetParent(spawnedObjectsParent);
|
||||
}
|
||||
|
||||
// Snap to ground
|
||||
SnapObjectToGround(spawnedObject, spawnX);
|
||||
// Position object using entry's spawn configuration
|
||||
PositionObject(spawnedObject, spawnX,
|
||||
entryToSpawn.spawnPositionMode,
|
||||
entryToSpawn.specifiedY,
|
||||
entryToSpawn.randomYMin,
|
||||
entryToSpawn.randomYMax);
|
||||
|
||||
// Initialize components that need post-spawn setup
|
||||
var initializable = spawnedObject.GetComponent<Interactive.ISpawnInitializable>();
|
||||
if (initializable != null)
|
||||
{
|
||||
initializable.Initialize();
|
||||
}
|
||||
|
||||
if (showDebugLogs)
|
||||
{
|
||||
@@ -749,7 +801,57 @@ namespace Minigames.Airplane.Core
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ground Snapping
|
||||
#region Object Positioning
|
||||
|
||||
/// <summary>
|
||||
/// Position an object based on the specified spawn mode.
|
||||
/// </summary>
|
||||
/// <param name="obj">Object to position</param>
|
||||
/// <param name="xPosition">X position for the object</param>
|
||||
/// <param name="mode">Spawn position mode to use</param>
|
||||
/// <param name="specifiedY">Y value for SpecifiedY mode</param>
|
||||
/// <param name="randomYMin">Min Y for RandomRange mode</param>
|
||||
/// <param name="randomYMax">Max Y for RandomRange mode</param>
|
||||
private void PositionObject(GameObject obj, float xPosition, SpawnPositionMode mode,
|
||||
float specifiedY, float randomYMin, float randomYMax)
|
||||
{
|
||||
if (obj == null) return;
|
||||
|
||||
float targetY;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case SpawnPositionMode.SnapToGround:
|
||||
targetY = SnapToGround(obj, xPosition);
|
||||
break;
|
||||
|
||||
case SpawnPositionMode.SpecifiedY:
|
||||
targetY = specifiedY;
|
||||
if (showDebugLogs)
|
||||
{
|
||||
Logging.Debug($"[SpawnManager] Positioned object at specified Y={targetY:F2}");
|
||||
}
|
||||
break;
|
||||
|
||||
case SpawnPositionMode.RandomRange:
|
||||
targetY = Random.Range(randomYMin, randomYMax);
|
||||
if (showDebugLogs)
|
||||
{
|
||||
Logging.Debug($"[SpawnManager] Positioned object at random Y={targetY:F2} (range: {randomYMin:F2} to {randomYMax:F2})");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Logging.Error($"[SpawnManager] Unknown spawn position mode: {mode}");
|
||||
targetY = 0f;
|
||||
break;
|
||||
}
|
||||
|
||||
// Apply position
|
||||
Vector3 newPosition = obj.transform.position;
|
||||
newPosition.y = targetY;
|
||||
obj.transform.position = newPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Snap an object to the ground using raycast.
|
||||
@@ -757,10 +859,9 @@ namespace Minigames.Airplane.Core
|
||||
/// </summary>
|
||||
/// <param name="obj">Object to snap to ground</param>
|
||||
/// <param name="xPosition">X position to raycast from</param>
|
||||
private void SnapObjectToGround(GameObject obj, float xPosition)
|
||||
/// <returns>The Y position where object was snapped</returns>
|
||||
private float SnapToGround(GameObject obj, float xPosition)
|
||||
{
|
||||
if (obj == null) return;
|
||||
|
||||
// Start raycast from high Y position
|
||||
Vector2 rayOrigin = new Vector2(xPosition, 0.0f);
|
||||
|
||||
@@ -800,10 +901,7 @@ namespace Minigames.Airplane.Core
|
||||
Logging.Warning($"[SpawnManager] No ground found at X={xPosition}, using default Y={targetY}");
|
||||
}
|
||||
|
||||
// Apply position
|
||||
Vector3 newPosition = obj.transform.position;
|
||||
newPosition.y = targetY;
|
||||
obj.transform.position = newPosition;
|
||||
return targetY;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
29
Assets/Scripts/Minigames/Airplane/Data/PrefabSpawnEntry.cs
Normal file
29
Assets/Scripts/Minigames/Airplane/Data/PrefabSpawnEntry.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Minigames.Airplane.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a prefab with spawn position configuration.
|
||||
/// Used for positive/negative objects in the airplane minigame.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class PrefabSpawnEntry
|
||||
{
|
||||
[Tooltip("Prefab to spawn")]
|
||||
public GameObject prefab;
|
||||
|
||||
[Tooltip("How to position this object vertically")]
|
||||
public SpawnPositionMode spawnPositionMode = SpawnPositionMode.SnapToGround;
|
||||
|
||||
[Tooltip("Y position to use (SpecifiedY mode)")]
|
||||
public float specifiedY;
|
||||
|
||||
[Tooltip("Min Y for random range (RandomRange mode)")]
|
||||
public float randomYMin = -5f;
|
||||
|
||||
[Tooltip("Max Y for random range (RandomRange mode)")]
|
||||
public float randomYMax = 5f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0290f7749fa4d2bb5cc8830a371705f
|
||||
timeCreated: 1765189597
|
||||
24
Assets/Scripts/Minigames/Airplane/Data/SpawnPositionMode.cs
Normal file
24
Assets/Scripts/Minigames/Airplane/Data/SpawnPositionMode.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace Minigames.Airplane.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines how spawned objects are positioned vertically.
|
||||
/// </summary>
|
||||
public enum SpawnPositionMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Raycast down to find ground and snap object's bottom to ground surface.
|
||||
/// </summary>
|
||||
SnapToGround,
|
||||
|
||||
/// <summary>
|
||||
/// Spawn at a specific Y coordinate.
|
||||
/// </summary>
|
||||
SpecifiedY,
|
||||
|
||||
/// <summary>
|
||||
/// Spawn at a random Y coordinate within a specified range.
|
||||
/// </summary>
|
||||
RandomRange
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97deb3f78d56408e83dc9f766418a5a2
|
||||
timeCreated: 1765189120
|
||||
@@ -3,11 +3,13 @@
|
||||
namespace Minigames.Airplane.Interactive
|
||||
{
|
||||
/// <summary>
|
||||
/// Gravity well that pulls airplanes toward its center.
|
||||
/// Gravity well that pulls airplanes toward its Y position (vertical only).
|
||||
/// Plane below the well gets pulled UP, plane above gets pulled DOWN.
|
||||
/// Does NOT affect horizontal (X) velocity - plane maintains forward momentum.
|
||||
/// Creates challenging "danger zones" that players must avoid or escape from.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Collider2D))]
|
||||
public class AirplaneGravityWell : MonoBehaviour
|
||||
public class AirplaneGravityWell : MonoBehaviour, ISpawnInitializable
|
||||
{
|
||||
[Header("Gravity Configuration")]
|
||||
[SerializeField] private float pullStrength = 5f;
|
||||
@@ -37,7 +39,14 @@ namespace Minigames.Airplane.Interactive
|
||||
{
|
||||
collider.isTrigger = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by spawn manager after object is positioned.
|
||||
/// Caches the final Y position for pull calculations.
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
{
|
||||
centerPosition = transform.position;
|
||||
}
|
||||
|
||||
@@ -59,31 +68,31 @@ namespace Minigames.Airplane.Interactive
|
||||
var rb = other.GetComponent<Rigidbody2D>();
|
||||
if (rb == null) return;
|
||||
|
||||
// Calculate direction and distance to center
|
||||
// Calculate VERTICAL distance only (Y-axis only)
|
||||
Vector2 airplanePos = rb.position;
|
||||
Vector2 toCenter = centerPosition - airplanePos;
|
||||
float distance = toCenter.magnitude;
|
||||
float yDistance = centerPosition.y - airplanePos.y;
|
||||
float absYDistance = Mathf.Abs(yDistance);
|
||||
|
||||
// Prevent division by zero
|
||||
if (distance < minPullDistance)
|
||||
if (absYDistance < minPullDistance)
|
||||
{
|
||||
distance = minPullDistance;
|
||||
absYDistance = minPullDistance;
|
||||
}
|
||||
|
||||
// Calculate pull force
|
||||
// Calculate pull force based on vertical distance
|
||||
float forceMagnitude;
|
||||
|
||||
if (useInverseSquare)
|
||||
{
|
||||
// Realistic gravity-like force (inverse square law)
|
||||
forceMagnitude = pullStrength / (distance * distance);
|
||||
forceMagnitude = pullStrength / (absYDistance * absYDistance);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Linear falloff based on distance
|
||||
// Linear falloff based on vertical distance
|
||||
var collider = GetComponent<Collider2D>();
|
||||
float maxDistance = collider != null ? collider.bounds.extents.magnitude : 5f;
|
||||
float normalizedDistance = Mathf.Clamp01(distance / maxDistance);
|
||||
float maxDistance = collider != null ? collider.bounds.extents.y * 2f : 5f; // Use height, not diagonal
|
||||
float normalizedDistance = Mathf.Clamp01(absYDistance / maxDistance);
|
||||
float falloff = pullFalloff.Evaluate(1f - normalizedDistance);
|
||||
forceMagnitude = pullStrength * falloff;
|
||||
}
|
||||
@@ -91,13 +100,16 @@ namespace Minigames.Airplane.Interactive
|
||||
// Clamp force
|
||||
forceMagnitude = Mathf.Min(forceMagnitude, maxPullForce);
|
||||
|
||||
// Apply force toward center
|
||||
Vector2 pullForce = toCenter.normalized * forceMagnitude;
|
||||
// Apply force ONLY in Y direction, toward the well's Y position
|
||||
// If plane is below (yDistance > 0), pull up (+Y)
|
||||
// If plane is above (yDistance < 0), pull down (-Y)
|
||||
float yForceDirection = yDistance > 0 ? 1f : -1f;
|
||||
Vector2 pullForce = new Vector2(0f, yForceDirection * forceMagnitude);
|
||||
rb.AddForce(pullForce, ForceMode2D.Force);
|
||||
|
||||
if (showDebugLogs && Time.frameCount % 30 == 0) // Log every 30 frames
|
||||
{
|
||||
Debug.Log($"[AirplaneGravityWell] Pulling {other.name}: force={forceMagnitude:F2}, distance={distance:F2}");
|
||||
Debug.Log($"[AirplaneGravityWell] Pulling {other.name}: force={forceMagnitude:F2} {(yForceDirection > 0 ? "UP" : "DOWN")}, Y-distance={absYDistance:F2}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Minigames.Airplane.Interactive
|
||||
if (rb != null)
|
||||
{
|
||||
Vector2 force = isWorldSpace ? windForce : transform.TransformDirection(windForce);
|
||||
rb.AddForce(force * Time.fixedDeltaTime, ForceMode2D.Force);
|
||||
rb.AddForce(force, ForceMode2D.Force);
|
||||
|
||||
if (showDebugLogs)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace Minigames.Airplane.Interactive
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for objects that need initialization after being spawned and positioned.
|
||||
/// The spawn manager will call Initialize() after setting the object's position.
|
||||
/// </summary>
|
||||
public interface ISpawnInitializable
|
||||
{
|
||||
/// <summary>
|
||||
/// Called by the spawn manager after the object has been instantiated and positioned.
|
||||
/// Use this to cache position-dependent state instead of using Awake().
|
||||
/// </summary>
|
||||
void Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a4d1d877662847eeae0daa9fd4fa4788
|
||||
timeCreated: 1765193296
|
||||
@@ -75,8 +75,8 @@ namespace Minigames.Airplane.Settings
|
||||
[SerializeField] private float evaluationDuration = 1f;
|
||||
|
||||
[Header("Spawn System")]
|
||||
[Tooltip("X position where dynamic spawning begins")]
|
||||
[SerializeField] private float dynamicSpawnThreshold = 10f;
|
||||
[Tooltip("Transform marker in scene where dynamic spawning begins (uses X position). If null, uses fallback distance.")]
|
||||
[SerializeField] private Transform dynamicSpawnThresholdMarker;
|
||||
|
||||
[Tooltip("Minimum random distance for target spawn")]
|
||||
[SerializeField] private float targetMinDistance = 30f;
|
||||
@@ -84,11 +84,11 @@ namespace Minigames.Airplane.Settings
|
||||
[Tooltip("Maximum random distance for target spawn")]
|
||||
[SerializeField] private float targetMaxDistance = 50f;
|
||||
|
||||
[Tooltip("Minimum time interval between object spawns (seconds)")]
|
||||
[SerializeField] private float objectSpawnMinInterval = 1f;
|
||||
[Tooltip("Minimum distance between spawned objects (units)")]
|
||||
[SerializeField] private float objectSpawnMinDistance = 5f;
|
||||
|
||||
[Tooltip("Maximum time interval between object spawns (seconds)")]
|
||||
[SerializeField] private float objectSpawnMaxInterval = 3f;
|
||||
[Tooltip("Maximum distance between spawned objects (units)")]
|
||||
[SerializeField] private float objectSpawnMaxDistance = 20f;
|
||||
|
||||
[Tooltip("Ratio of positive to negative objects (0 = all negative, 1 = all positive)")]
|
||||
[Range(0f, 1f)]
|
||||
@@ -138,11 +138,10 @@ namespace Minigames.Airplane.Settings
|
||||
public float IntroDuration => introDuration;
|
||||
public float PersonIntroDuration => personIntroDuration;
|
||||
public float EvaluationDuration => evaluationDuration;
|
||||
public float DynamicSpawnThreshold => dynamicSpawnThreshold;
|
||||
public float TargetMinDistance => targetMinDistance;
|
||||
public float TargetMaxDistance => targetMaxDistance;
|
||||
public float ObjectSpawnMinInterval => objectSpawnMinInterval;
|
||||
public float ObjectSpawnMaxInterval => objectSpawnMaxInterval;
|
||||
public float ObjectSpawnMinDistance => objectSpawnMinDistance;
|
||||
public float ObjectSpawnMaxDistance => objectSpawnMaxDistance;
|
||||
public float PositiveNegativeRatio => positiveNegativeRatio;
|
||||
public float SpawnDistanceAhead => spawnDistanceAhead;
|
||||
public float GroundSpawnInterval => groundSpawnInterval;
|
||||
|
||||
Reference in New Issue
Block a user