Initial commit

This commit is contained in:
IM23a-spirgif
2025-03-28 20:10:45 +01:00
commit 8162e2b331
781 changed files with 240333 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/contentModel.xml
/modules.xml
/projectSettingsUpdater.xml
/.idea.Scripts.iml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>
@@ -0,0 +1,38 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<Languages>
<language minSize="67" name="JavaScript" />
<language minSize="47" name="Python" />
</Languages>
</inspection_tool>
<inspection_tool class="PyCallingNonCallableInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="pytest" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="PyPep8Inspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="E501" />
</list>
</option>
</inspection_tool>
<inspection_tool class="PyStatementEffectInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredIdentifiers">
<list>
<option value="tuple.append" />
<option value="str.get_width" />
<option value="rental.return_date" />
</list>
</option>
</inspection_tool>
</profile>
</component>
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings" defaultProject="true" />
</project>
+61
View File
@@ -0,0 +1,61 @@
using UnityEngine;
public class Bullet : MonoBehaviour
{
public float speed = 5f;
private int damage = 1;
private Transform target;
public GameObject shatterEffectPrefab;
private Vector2 lastKnownDirection;
private bool hasTarget = true; // Track if bullet has an active target
public void SetTarget(GameObject enemy)
{
if (enemy != null)
{
target = enemy.transform;
lastKnownDirection = (target.position - transform.position).normalized; // Store initial direction
}
}
public void SetDamage(int newDamage)
{
damage = newDamage;
}
void Update()
{
if (hasTarget && target != null)
{
lastKnownDirection = (target.position - transform.position).normalized; // Keep updating direction
}
else
{
hasTarget = false; // The target is gone, continue in last direction
}
// Move bullet in the last known direction
transform.position += (Vector3)lastKnownDirection * speed * Time.deltaTime;
// Destroy bullet after 5 seconds to prevent infinite movement
Destroy(gameObject, 5f);
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Enemy"))
{
other.GetComponent<EnemyHealth>().TakeDamage(damage);
ShatterEffect();
Destroy(gameObject);
}
}
void ShatterEffect()
{
if (shatterEffectPrefab != null)
{
Instantiate(shatterEffectPrefab, transform.position, Quaternion.identity);
}
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: bc2f4c13183a4fe4aa62f7a8d5d0ee0c
+32
View File
@@ -0,0 +1,32 @@
using UnityEngine;
public class Enemy : MonoBehaviour
{
public float speed = 1.5f;
private Transform tower;
private EnemyHealth enemyHealth;
void Start()
{
GameObject towerObject = GameObject.FindGameObjectWithTag("Tower");
if (towerObject != null)
{
tower = towerObject.transform;
}
enemyHealth = GetComponent<EnemyHealth>();
}
void Update()
{
if (tower == null || enemyHealth == null) return;
transform.position = Vector2.MoveTowards(transform.position, tower.position, speed * Time.deltaTime);
if (Vector2.Distance(transform.position, tower.position) < 0.5f)
{
int damageDealt = enemyHealth.GetCurrentHealth();
tower.GetComponent<TowerHealth>().TakeDamage(damageDealt);
enemyHealth.NotifySpawner();
Destroy(gameObject);
}
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6c389f0cb0e33bc4ca63795950c6d7e5
+55
View File
@@ -0,0 +1,55 @@
using UnityEngine;
public class EnemyHealth : MonoBehaviour
{
public int health = 1;
public GameObject shatterEffectPrefab;
public EnemySpawner spawner;
public void TakeDamage(int damage)
{
health -= damage;
if (health <= 0)
{
NotifySpawner();
ShatterEffect();
NotifyUpgradeManager();
Destroy(gameObject);
}
}
public int GetCurrentHealth()
{
return health;
}
public void SetHealth(int newHealth)
{
health = newHealth;
}
public void NotifySpawner()
{
if (spawner != null)
{
spawner.EnemyDefeated();
}
}
void NotifyUpgradeManager()
{
UpgradeManager upgradeManager = FindObjectOfType<UpgradeManager>();
if (upgradeManager != null)
{
upgradeManager.EnemyKilled();
}
}
void ShatterEffect()
{
if (shatterEffectPrefab != null)
{
Instantiate(shatterEffectPrefab, transform.position, Quaternion.identity);
}
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 01e7936326d532c439716d83853feb09
+108
View File
@@ -0,0 +1,108 @@
using System.Collections;
using UnityEngine;
using TMPro;
using UnityEngine.UI;
public class EnemySpawner : MonoBehaviour
{
public GameObject enemyPrefab;
public Transform tower;
public TextMeshProUGUI waveText;
public Button startWaveButton;
public float spawnRadius = 10f;
private int currentWave = 1;
private bool isSpawning = false;
private int enemiesRemaining = 0;
private int baseEnemyCount = 5;
private int enemyCountIncrease = 3;
private float enemySpeed = 1.5f;
private int enemyBaseHP = 1;
private float speedIncreaseRate = 0.2f;
private int hpIncreaseRate = 1;
private float maxEnemySpeed = 6f;
private int maxEnemyHP = 20;
public void StartWave()
{
if (!isSpawning)
{
currentWave++;
UpdateWaveText();
enemiesRemaining = baseEnemyCount + (currentWave) * enemyCountIncrease;
startWaveButton.gameObject.SetActive(false);
StartCoroutine(SpawnWave());
}
}
IEnumerator SpawnWave()
{
isSpawning = true;
int enemiesToSpawn = baseEnemyCount + (currentWave) * enemyCountIncrease;
for (int i = 0; i < enemiesToSpawn; i++)
{
SpawnEnemy();
yield return new WaitForSeconds(1f);
}
isSpawning = false;
}
void SpawnEnemy()
{
Vector2 spawnPos = GetRandomSpawnPosition();
GameObject newEnemy = Instantiate(enemyPrefab, spawnPos, Quaternion.identity);
Enemy enemyComponent = newEnemy.GetComponent<Enemy>();
if (enemyComponent != null)
{
// Lower speed scale to 0.1f
float waveSpeedBonus = currentWave * 0.1f;
enemyComponent.speed = Mathf.Min(maxEnemySpeed, enemySpeed + waveSpeedBonus);
}
EnemyHealth enemyHealth = newEnemy.GetComponent<EnemyHealth>();
if (enemyHealth != null)
{
// For HP: baseHP + wave * 0.3 (round it)
int scaledHP = enemyBaseHP + Mathf.RoundToInt(currentWave * 0.3f);
scaledHP = Mathf.Min(maxEnemyHP, scaledHP);
enemyHealth.SetHealth(scaledHP);
enemyHealth.spawner = this;
}
}
Vector2 GetRandomSpawnPosition()
{
float angle = Random.Range(0, 360) * Mathf.Deg2Rad;
return (Vector2)tower.position + new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * spawnRadius;
}
void UpdateWaveText()
{
if (waveText != null)
{
waveText.text = "Wave: " + currentWave;
}
}
public void EnemyDefeated()
{
enemiesRemaining--;
if (enemiesRemaining <= 0)
{
startWaveButton.gameObject.SetActive(true);
// If wave is multiple of 5, show special upgrade choice
if (currentWave % 5 == 0)
{
UpgradeManager upgradeManager = FindObjectOfType<UpgradeManager>();
if (upgradeManager != null)
{
upgradeManager.ShowSpecialUpgradeChoice();
}
}
}
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 26b80a73eb730cb4fb4e079d98235253
+17
View File
@@ -0,0 +1,17 @@
using UnityEngine;
using UnityEngine.SceneManagement;
public class MainMenu : MonoBehaviour
{
public void StartGame()
{
Debug.Log("Game Start!");
SceneManager.LoadScene("SampleScene");
}
public void QuitGame()
{
Debug.Log("Game Quit!");
Application.Quit();
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 04438cc05d1d0f643a51f117e8f28494
+25
View File
@@ -0,0 +1,25 @@
using UnityEngine;
public class SpecialChoiceCard : MonoBehaviour
{
public UpgradeManager upgradeManager;
void OnMouseDown()
{
// Identify which UpgradeType this card has
UpgradeCard card = GetComponent<UpgradeCard>();
if (card != null)
{
upgradeManager.UnlockSpecialUpgrade(card.upgradeType);
}
// Destroy all other special cards
SpecialChoiceCard[] allChoices = FindObjectsOfType<SpecialChoiceCard>();
foreach (var choice in allChoices)
{
Destroy(choice.gameObject);
}
Time.timeScale = 1f; // Resume game
}
}
+3
View File
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2d508f3bec6b44ffb7914c41f743fcdd
timeCreated: 1742472511
+63
View File
@@ -0,0 +1,63 @@
using System.Collections.Generic;
using UnityEngine;
public class Tower : MonoBehaviour
{
public static Tower Instance;
public GameObject bulletPrefab;
public int damage = 1;
public float fireRate = 1f;
public float attackRange = 5f;
private float nextFireTime;
private GameObject currentTarget;
public int extraBullets = 0; // For "Shoot +1 bullet"
public bool homingEnabled = false;
public bool ricochetEnabled = false;
public bool pierceEnabled = false;
void Update()
{
if (Time.time >= nextFireTime)
{
RetargetAndFire();
nextFireTime = Time.time + fireRate;
}
}
void RetargetAndFire()
{
currentTarget = GetNearestEnemy();
if (currentTarget != null)
{
FireAtTarget(currentTarget);
}
}
void FireAtTarget(GameObject target)
{
GameObject bullet = Instantiate(bulletPrefab, transform.position, Quaternion.identity);
bullet.GetComponent<Bullet>().SetDamage(damage);
bullet.GetComponent<Bullet>().SetTarget(target);
}
GameObject GetNearestEnemy()
{
float minDist = float.MaxValue;
GameObject nearestEnemy = null;
foreach (GameObject enemy in GameObject.FindGameObjectsWithTag("Enemy"))
{
float dist = Vector2.Distance(transform.position, enemy.transform.position);
if (dist < minDist && dist <= attackRange)
{
minDist = dist;
nearestEnemy = enemy;
}
}
return nearestEnemy;
}
public void OnEnemyDestroyed()
{
RetargetAndFire();
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 50b372157104f4b408854b557144ecd6
+71
View File
@@ -0,0 +1,71 @@
using System.Collections; // Required for IEnumerator
using UnityEngine;
using TMPro;
public class TowerHealth : MonoBehaviour
{
public int maxHealth = 100;
private int currentHealth;
private SpriteRenderer spriteRenderer;
public GameObject floatingTextPrefab;
void Start()
{
currentHealth = maxHealth;
spriteRenderer = GetComponent<SpriteRenderer>();
UpdateColor();
}
public void TakeDamage(int damage)
{
currentHealth -= damage;
if (currentHealth < 0) currentHealth = 0;
ShowFloatingDamage(damage);
UpdateColor();
if (currentHealth <= 0)
{
Debug.Log("Tower Destroyed! Game Over.");
}
}
void UpdateColor()
{
float healthPercentage = (float)currentHealth / maxHealth;
spriteRenderer.color = Color.Lerp(Color.red, Color.green, healthPercentage);
}
void ShowFloatingDamage(int damage)
{
if (floatingTextPrefab != null)
{
GameObject damageTextInstance = Instantiate(
floatingTextPrefab,
transform.position + new Vector3(0, 1.5f, 0),
Quaternion.identity
);
TextMeshPro tmp = damageTextInstance.GetComponent<TextMeshPro>();
tmp.text = "-" + damage.ToString();
StartCoroutine(FadeAndMoveText(damageTextInstance));
Destroy(damageTextInstance, 1.5f);
}
}
IEnumerator FadeAndMoveText(GameObject textObj)
{
float duration = 1f;
float elapsed = 0f;
Vector3 startPos = textObj.transform.position;
Vector3 targetPos = startPos + new Vector3(0, 1f, 0); // Move up
TextMeshPro tmp = textObj.GetComponent<TextMeshPro>();
Color startColor = tmp.color;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
float t = elapsed / duration;
textObj.transform.position = Vector3.Lerp(startPos, targetPos, t);
tmp.color = new Color(startColor.r, startColor.g, startColor.b, 1 - t);
yield return null;
}
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 50fa3b33053ba8c45ba59e06db0e2c5f
+70
View File
@@ -0,0 +1,70 @@
using TMPro;
using UnityEngine;
public class UpgradeCard : MonoBehaviour
{
public enum UpgradeType
{
FireRate = 0,
Range = 1,
Damage = 2,
MultiShot= 3,
Homing = 4,
Ricochet = 5,
}
public UpgradeType upgradeType;
public int upgradeLevel = 1;
public int upgradeCost;
public UpgradeManager upgradeManager;
public TextMeshProUGUI upgradeText;
public TextMeshProUGUI upgradeDescription;
public TextMeshProUGUI upgradeCostText;
void Start()
{
// If not assigned in Inspector, try to find them by name
if (upgradeText == null)
upgradeText = transform.Find("Canvas/UpgradeText").GetComponent<TextMeshProUGUI>();
if (upgradeDescription == null)
upgradeDescription = transform.Find("Canvas/UpgradeDescription").GetComponent<TextMeshProUGUI>();
if (upgradeCostText == null)
upgradeCostText = transform.Find("Canvas/UpgradeCost").GetComponent<TextMeshProUGUI>();
// Update all UI fields
UpdateUpgradeUI();
}
void OnMouseDown()
{
// When clicked, attempt to buy/upgrade
upgradeManager.TryUpgrade(upgradeType);
}
public void UpdateUpgradeUI()
{
if (upgradeText != null)
{
string roman = ConvertToRoman(upgradeLevel);
upgradeText.text = $"{upgradeType} {roman}";
}
// 2) Description is presumably already set in the prefab,
// but if you want to set it dynamically, you can do so here.
// e.g.: upgradeDescription.text = "This does X, Y, Z..."
// 3) Cost text, e.g. "Cost: 5 Bits"
if (upgradeCostText != null)
{
upgradeCostText.text = $"Cost: {upgradeCost} Bits";
}
}
string ConvertToRoman(int number)
{
string[] numerals = { "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X" };
return number > 10 ? number.ToString() : numerals[number - 1];
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 821918f62c41f564fa08120e9bb6db3a
+240
View File
@@ -0,0 +1,240 @@
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class UpgradeManager : MonoBehaviour
{
public GameObject[] normalUpgradePrefabs; // FireRate, Range, Damage
public GameObject[] specialUpgradePrefabs; // MultiShot, Homing, Ricochet, etc.
[HideInInspector] public List<UpgradeCard.UpgradeType> unlockedUpgrades = new List<UpgradeCard.UpgradeType>();
// Stores which special upgrades have been permanently unlocked.
// UI placeholder to spawn the 3 choices
public Transform specialUpgradeSpawnParent; // A center-of-screen parent or coordinates
public float specialUpgradeSpacing = 2f;
public Tower tower;
private Dictionary<UpgradeCard.UpgradeType, int> upgradeLevels = new Dictionary<UpgradeCard.UpgradeType, int>();
private Dictionary<UpgradeCard.UpgradeType, int> upgradeCosts = new Dictionary<UpgradeCard.UpgradeType, int>();
public int bits = 5;
public TextMeshProUGUI bitsText;
void Start()
{
InitializeUpgrades();
UpdateBitsText();
Debug.Log($"specialUpgradePrefabs length: {specialUpgradePrefabs.Length}");
}
public void EnemyKilled()
{
bits++;
UpdateBitsText();
}
void InitializeUpgrades()
{
float spacing = 2.5f;
float startX = -((normalUpgradePrefabs.Length - 1) * spacing / 2f);
foreach (UpgradeCard.UpgradeType type in System.Enum.GetValues(typeof(UpgradeCard.UpgradeType)))
{
if (!upgradeLevels.ContainsKey(type))
upgradeLevels[type] = 0;
if (!upgradeCosts.ContainsKey(type))
upgradeCosts[type] = GetBaseCost(type);
}
for (int i = 0; i < normalUpgradePrefabs.Length; i++)
{
UpgradeCard.UpgradeType upgradeType = (UpgradeCard.UpgradeType)i;
SpawnUpgradeCard(upgradeType, startX + (i * spacing));
}
}
void SpawnUpgradeCard(UpgradeCard.UpgradeType upgradeType, float xPos)
{
Vector3 spawnPosition = new Vector3(xPos, -4f, 0);
// Ensure dictionaries have entries for this type
if (!upgradeLevels.ContainsKey(upgradeType))
upgradeLevels[upgradeType] = 0;
if (!upgradeCosts.ContainsKey(upgradeType))
upgradeCosts[upgradeType] = GetBaseCost(upgradeType);
// Determine this upgrade's current level
int currentLevel = upgradeLevels[upgradeType] + 1;
// 1) Choose which prefab array to use
GameObject prefab;
if ((int)upgradeType < normalUpgradePrefabs.Length)
{
// It's one of FireRate=0, Range=1, Damage=2
prefab = normalUpgradePrefabs[(int)upgradeType];
}
else
{
// It's a special upgrade, e.g. MultiShot, Homing, Ricochet
int specialIndex = (int)upgradeType - normalUpgradePrefabs.Length;
// e.g. MultiShot=3 => specialIndex=0
// Homing=4 => specialIndex=1, etc.
prefab = specialUpgradePrefabs[specialIndex];
}
// 2) Instantiate the correct prefab
GameObject upgradeCardObj = Instantiate(prefab, spawnPosition, Quaternion.identity);
// 3) Set up the UpgradeCard component
UpgradeCard upgradeCard = upgradeCardObj.GetComponentInChildren<UpgradeCard>();
if (upgradeCard != null)
{
upgradeCard.upgradeType = upgradeType;
upgradeCard.upgradeLevel = currentLevel;
upgradeCard.upgradeCost = upgradeCosts[upgradeType];
upgradeCard.upgradeManager = this;
upgradeCard.upgradeText = upgradeCardObj.GetComponentInChildren<TextMeshProUGUI>(true);
upgradeCard.UpdateUpgradeUI();
}
}
public int GetBaseCost(UpgradeCard.UpgradeType type)
{
switch (type)
{
case UpgradeCard.UpgradeType.FireRate: return 3;
case UpgradeCard.UpgradeType.Range: return 2;
case UpgradeCard.UpgradeType.Damage: return 5;
default: return 1;
}
}
public void TryUpgrade(UpgradeCard.UpgradeType type)
{
if (bits >= upgradeCosts[type])
{
bits -= upgradeCosts[type];
upgradeLevels[type]++;
upgradeCosts[type] += (int)(upgradeCosts[type] * 0.5f);
ApplyUpgrade(type);
UpdateBitsText();
}
}
void ApplyUpgrade(UpgradeCard.UpgradeType type)
{
switch (type)
{
case UpgradeCard.UpgradeType.FireRate:
tower.fireRate *= 0.9f;
break;
case UpgradeCard.UpgradeType.Range:
tower.attackRange += 2.5f;
break;
case UpgradeCard.UpgradeType.Damage:
tower.damage += 1;
break;
case UpgradeCard.UpgradeType.Ricochet:
tower.ricochetEnabled = true;
break;
case UpgradeCard.UpgradeType.Homing:
tower.homingEnabled = true;
break;
case UpgradeCard.UpgradeType.MultiShot:
tower.extraBullets += 1;
break;
}
UpdateUpgradeCards();
}
void UpdateUpgradeCards()
{
UpgradeCard[] allCards = FindObjectsOfType<UpgradeCard>();
foreach (UpgradeCard card in allCards)
{
card.upgradeLevel = upgradeLevels[card.upgradeType] + 1;
card.upgradeCost = upgradeCosts[card.upgradeType];
card.UpdateUpgradeUI();
}
}
void UpdateBitsText()
{
if (bitsText != null)
{
bitsText.text = "Bits: " + bits;
}
}
public void ShowSpecialUpgradeChoice()
{
Debug.Log(specialUpgradeSpawnParent.name);
Debug.Log("Showing Upgrades...");
List<GameObject> available = new List<GameObject>(specialUpgradePrefabs);
// Shuffle the list to randomize order (Fisher-Yates)
for (int i = 0; i < available.Count; i++)
{
int rand = Random.Range(i, available.Count);
var temp = available[i];
available[i] = available[rand];
available[rand] = temp;
}
// Determine how many upgrades we can actually show
int numToShow = Mathf.Min(3, available.Count);
// If there are no upgrades left, resume game and return
if (numToShow == 0)
{
Debug.Log("No special upgrades available!");
Time.timeScale = 1f;
return;
}
// Instantiate the first 'numToShow' from the shuffled list
float startX = -specialUpgradeSpacing;
for (int i = 0; i < numToShow; i++)
{
float xPos = startX + i * specialUpgradeSpacing;
Vector3 spawnPos = specialUpgradeSpawnParent.position + new Vector3(xPos, 0, 0);
GameObject specialCardObj =
Instantiate(available[i], spawnPos, Quaternion.identity, specialUpgradeSpawnParent);
Debug.Log("Spawned special card: " + specialCardObj, specialCardObj);
// Add your click-to-choose script
SpecialChoiceCard choiceCard = specialCardObj.AddComponent<SpecialChoiceCard>();
choiceCard.upgradeManager = this;
}
}
public void UnlockSpecialUpgrade(UpgradeCard.UpgradeType type)
{
Debug.Log("Card chosen! Applying upgrade...");
// 1) Add this special upgrade type to the dictionary if not present
if (!upgradeLevels.ContainsKey(type))
{
upgradeLevels[type] = 0;
upgradeCosts[type] = GetBaseCost(type);
}
// 2) Add to the “unlockedUpgrades” list if you want to track them
if (!unlockedUpgrades.Contains(type))
unlockedUpgrades.Add(type);
// 3) Instantiate it at the bottom row with the other upgrades
float newXPos = CalculateNextUpgradeXPos(); // A method to place new card to the right
SpawnUpgradeCard(type, newXPos);
}
// Example method to find a new X position for the newly unlocked card
float CalculateNextUpgradeXPos()
{
// If you originally placed normal upgrades from -X to +X,
// find how many total upgrades are unlocked, and space them out.
// For simplicity:
int totalUnlocked = FindObjectsOfType<UpgradeCard>().Length;
float spacing = 2.5f;
return -((totalUnlocked - 1) * spacing / 2f) + (totalUnlocked - 1) * spacing;
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 57719a5f7440e814890b2e4abbaca663