docs(clipboard): 添加剪贴板功能相关图片资源

添加了两个剪贴板功能的截图文件,用于文档说明和功能展示。
```
This commit is contained in:
huadexuan committed 2026-09-09 23:59:15 +08:00
1 parent 47f01ecd2e
commit f93d69d8ee
77 files changed
+5482 -307

No files matched your search

+52
View File
@@ -0,0 +1,52 @@
using System.Collections.Generic;
using UnityEngine;
public class CastleMageSkill : UnitSkill
{
[Header("小型魔爆参数")]
[Min(0.1f)]
public float explosionRadius = 1.8f;
[Min(0f)]
public float explosionDamage = 85f;
public override void ReleaseSkill()
{
GameObject target = unit.Target;
if (target == null)
{
return;
}
string opponentTag = unit.OpponentTag;
if (string.IsNullOrEmpty(opponentTag))
{
return;
}
Vector3 center = target.transform.position;
float radiusSquared = explosionRadius * explosionRadius;
HashSet<UnitBase> hitUnits = new HashSet<UnitBase>();
foreach (GameObject opponent in GameObject.FindGameObjectsWithTag(opponentTag))
{
if (opponent == null || opponent.transform.root == unit.transform.root)
{
continue;
}
Vector3 offset = opponent.transform.position - center;
offset.y = 0f;
if (offset.sqrMagnitude > radiusSquared)
{
continue;
}
UnitBase targetUnit = opponent.GetComponentInParent<UnitBase>();
if (targetUnit != null && hitUnits.Add(targetUnit))
{
targetUnit.ReceiveDamage(explosionDamage, unit.gameObject);
}
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: Cn4bsnyuVn6kevm8A3QZW4Oh8KCqLYuAMcb2daUizH+Zn45nRrCY42U=
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+59
View File
@@ -0,0 +1,59 @@
using UnityEngine;
[CreateAssetMenu(fileName = "NewCharacterData", menuName = "召唤之战/角色数据")]
public class CharacterData : ScriptableObject
{
[Header("基础信息")]
public string characterId = "unknown";
public string characterName = "未命名角色";
[TextArea(2, 4)]
public string description = "";
[Header("UI 显示")]
public Sprite cardIcon;
[Header("召唤配置")]
public GameObject unitPrefab;
[Min(0)]
public int manaCost = 1;
[Min(0f)]
public float cooldownTime = 1f;
[Header("基础属性(1级)")]
[Min(0f)]
public float baseMaxHealth = 100f;
[Min(0f)]
public float baseAttackPower = 10f;
[Min(0f)]
public float baseMoveSpeed = 3f;
[Header("升级成长")]
[Min(0f)]
public float healthPerLevel = 10f;
[Min(0f)]
public float attackPerLevel = 5f;
[Min(0f)]
public float moveSpeedPerLevel = 0f;
public float GetMaxHealth(int level)
{
return baseMaxHealth + (level - 1) * healthPerLevel;
}
public float GetAttackPower(int level)
{
return baseAttackPower + (level - 1) * attackPerLevel;
}
public float GetMoveSpeed(int level)
{
return baseMoveSpeed + (level - 1) * moveSpeedPerLevel;
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: CX0ZvCmrUiknanzzrRw7tS6o4/Rp0TGlIvuj81f1G/6JxI0yhleTFqk=
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+48 -11
View File
@@ -24,8 +24,7 @@ public class MainCharacterCombat : MonoBehaviour
public Transform attackPos;
[Header("召唤单位")]
public GameObject humanSoldierPrefab;
public GameObject archerPrefab;
public CharacterData[] summonableCharacters = new CharacterData[5];
public float summonDistance = 1.5f;
public float summonSideOffset = 0.8f;
public float summonSearchStep = 0.8f;
@@ -48,6 +47,8 @@ public class MainCharacterCombat : MonoBehaviour
private bool attacking;
private bool attackEffectTriggered;
private GameObject pendingSummonPrefab;
private CharacterData pendingSummonCharacterData;
private PlayerCharacterData pendingSummonPlayerData;
private float summonElapsed;
private bool summonSpawned;
private bool summoning;
@@ -136,16 +137,32 @@ public class MainCharacterCombat : MonoBehaviour
return false;
}
GameObject prefab = slot == 1
? humanSoldierPrefab
: slot == 2
? archerPrefab
: null;
if (prefab == null)
int index = slot - 1;
if (index < 0 || index >= summonableCharacters.Length)
{
return false;
}
CharacterData characterData = summonableCharacters[index];
if (characterData == null || characterData.unitPrefab == null)
{
return false;
}
if (ManaSystem.Instance == null || !ManaSystem.Instance.TryConsumeMana(characterData.manaCost))
{
return false;
}
PlayerCharacterData playerData = PlayerDataManager.Instance != null
? PlayerDataManager.Instance.GetDeckCharacter(index)
: null;
if (playerData == null)
{
playerData = new PlayerCharacterData(characterData.characterId, 1, 0);
}
if (movement != null)
{
movement.StopMovement();
@@ -156,11 +173,11 @@ public class MainCharacterCombat : MonoBehaviour
FinishAttack();
}
BeginSummon(prefab);
BeginSummon(characterData.unitPrefab, characterData, playerData);
return true;
}
private void BeginSummon(GameObject prefab)
private void BeginSummon(GameObject prefab, CharacterData characterData, PlayerCharacterData playerData)
{
if (prefab == null)
{
@@ -168,6 +185,8 @@ public class MainCharacterCombat : MonoBehaviour
}
pendingSummonPrefab = prefab;
pendingSummonCharacterData = characterData;
pendingSummonPlayerData = playerData;
summonElapsed = 0f;
summonSpawned = false;
summoning = true;
@@ -201,9 +220,27 @@ public class MainCharacterCombat : MonoBehaviour
spawnPosition,
Quaternion.LookRotation(direction, Vector3.up));
summoned.tag = "Friend";
UnitBase unitBase = summoned.GetComponent<UnitBase>();
if (unitBase != null && pendingSummonCharacterData != null && pendingSummonPlayerData != null)
{
float finalHealth = pendingSummonPlayerData.GetMaxHealth(pendingSummonCharacterData);
float finalAttack = pendingSummonPlayerData.GetAttackPower(pendingSummonCharacterData);
float finalSpeed = pendingSummonPlayerData.GetMoveSpeed(pendingSummonCharacterData);
unitBase.InitializeFromPlayerData(finalHealth, finalAttack, finalSpeed);
Debug.Log($"主角召唤单位:{summoned.name} Lv.{pendingSummonPlayerData.level} HP={finalHealth} ATK={finalAttack} position={spawnPosition}");
}
else
{
Debug.Log("主角召唤单位:" + summoned.name + " position=" + spawnPosition);
}
summonSpawned = true;
pendingSummonPrefab = null;
Debug.Log("主角召唤单位:" + summoned.name + " position=" + spawnPosition);
pendingSummonCharacterData = null;
pendingSummonPlayerData = null;
}
private bool TryFindSummonPosition(Vector3 direction, out Vector3 position)
+93
View File
@@ -0,0 +1,93 @@
using UnityEngine;
using UnityEngine.UI;
public class ManaSystem : MonoBehaviour
{
public static ManaSystem Instance { get; private set; }
[Header("能量配置")]
[Min(1)]
public int maxMana = 10;
[Min(0f)]
public float manaRegenRate = 1f;
[Header("UI 引用")]
public Image[] manaBeads;
private float currentMana;
private float manaRegenTimer;
public float CurrentMana => currentMana;
public int CurrentManaInt => Mathf.FloorToInt(currentMana);
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
}
private void Start()
{
currentMana = maxMana;
UpdateManaUI();
}
private void Update()
{
if (currentMana < maxMana)
{
manaRegenTimer += Time.deltaTime;
if (manaRegenTimer >= manaRegenRate)
{
manaRegenTimer = 0f;
currentMana = Mathf.Min(currentMana + 1f, maxMana);
UpdateManaUI();
}
}
}
public bool TryConsumeMana(int cost)
{
if (CurrentManaInt < cost)
{
return false;
}
currentMana -= cost;
if (currentMana < 0f)
{
currentMana = 0f;
}
UpdateManaUI();
return true;
}
public void AddMana(int amount)
{
currentMana = Mathf.Min(currentMana + amount, maxMana);
UpdateManaUI();
}
private void UpdateManaUI()
{
if (manaBeads == null || manaBeads.Length == 0)
{
return;
}
int fullBeads = CurrentManaInt;
for (int i = 0; i < manaBeads.Length; i++)
{
if (manaBeads[i] != null)
{
manaBeads[i].enabled = i < fullBeads;
}
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: CX1N43j8An3F2SS5xfnnw0MEMGM1LHRi9Q5D/8G0X7pg6HMvFNxXEQw=
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+34
View File
@@ -0,0 +1,34 @@
using UnityEngine;
[System.Serializable]
public class PlayerCharacterData
{
public string characterId;
public int level = 1;
public int starLevel = 0;
public PlayerCharacterData(string id, int lvl = 1, int star = 0)
{
characterId = id;
level = lvl;
starLevel = star;
}
public float GetMaxHealth(CharacterData baseData)
{
if (baseData == null) return 100f;
return baseData.GetMaxHealth(level);
}
public float GetAttackPower(CharacterData baseData)
{
if (baseData == null) return 10f;
return baseData.GetAttackPower(level);
}
public float GetMoveSpeed(CharacterData baseData)
{
if (baseData == null) return 3f;
return baseData.GetMoveSpeed(level);
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: Xi9KvC3+B30tX+wHmYFe+bhJC4+O7cS3uMRPBwUDjR3vJnL0eZUuTnI=
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+60
View File
@@ -0,0 +1,60 @@
using System.Collections.Generic;
using UnityEngine;
public class PlayerDataManager : MonoBehaviour
{
public static PlayerDataManager Instance { get; private set; }
[Header("玩家拥有的角色(临时写死)")]
public List<PlayerCharacterData> ownedCharacters = new List<PlayerCharacterData>();
[Header("当前卡组(临时写死)")]
public PlayerCharacterData[] currentDeck = new PlayerCharacterData[5];
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
InitializeTempData();
}
private void InitializeTempData()
{
ownedCharacters.Clear();
ownedCharacters.Add(new PlayerCharacterData("human_soldier", 1, 0));
ownedCharacters.Add(new PlayerCharacterData("archer", 1, 0));
currentDeck[0] = ownedCharacters[0];
currentDeck[1] = ownedCharacters[1];
}
public PlayerCharacterData GetCharacterData(string characterId)
{
return ownedCharacters.Find(c => c.characterId == characterId);
}
public PlayerCharacterData GetDeckCharacter(int slot)
{
if (slot < 0 || slot >= currentDeck.Length)
{
return null;
}
return currentDeck[slot];
}
public void UpgradeCharacter(string characterId)
{
var character = GetCharacterData(characterId);
if (character != null)
{
character.level++;
Debug.Log($"角色 {characterId} 升级到 {character.level} 级");
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: XXgYs3/+BXwaGUgnfPJroomJsvMXkT0jY2SiC+sLTmtNIlLz1jWA7qg=
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+116 -2
View File
@@ -4,17 +4,36 @@ using UnityEngine.UI;
[RequireComponent(typeof(Button))]
public class SummonCardButton : MonoBehaviour
{
[Header("角色数据")]
public CharacterData characterData;
[Header("卡槽配置")]
[Min(1)]
public int summonSlot = 1;
[Header("UI 引用")]
public Image cooldownMask;
public Text cooldownText;
public Image cardIcon;
public Text characterNameText;
public Text manaCostText;
private Button button;
private MainCharacterCombat combat;
private float cooldownTimer;
private bool isOnCooldown;
private void Awake()
{
button = GetComponent<Button>();
combat = FindObjectOfType<MainCharacterCombat>();
button.onClick.AddListener(OnClick);
isOnCooldown = false;
cooldownTimer = 0f;
UpdateCardUI();
UpdateCooldownUI();
}
private void OnDestroy()
@@ -25,16 +44,111 @@ public class SummonCardButton : MonoBehaviour
}
}
private void Update()
{
if (isOnCooldown)
{
cooldownTimer -= Time.deltaTime;
if (cooldownTimer <= 0f)
{
isOnCooldown = false;
cooldownTimer = 0f;
}
UpdateCooldownUI();
}
UpdateButtonState();
}
public void OnClick()
{
if (isOnCooldown || characterData == null)
{
return;
}
if (combat == null)
{
combat = FindObjectOfType<MainCharacterCombat>();
}
if (combat != null)
if (combat != null && combat.RequestSummon(summonSlot))
{
combat.RequestSummon(summonSlot);
StartCooldown();
}
}
private void StartCooldown()
{
if (characterData != null)
{
isOnCooldown = true;
cooldownTimer = characterData.cooldownTime;
UpdateCooldownUI();
}
}
private void UpdateCooldownUI()
{
if (cooldownMask != null)
{
cooldownMask.fillAmount = isOnCooldown ? (cooldownTimer / GetCooldownTime()) : 0f;
cooldownMask.enabled = isOnCooldown;
}
if (cooldownText != null)
{
cooldownText.text = isOnCooldown ? Mathf.CeilToInt(cooldownTimer).ToString() : "";
cooldownText.enabled = isOnCooldown;
}
}
private void UpdateButtonState()
{
if (button == null)
{
return;
}
button.interactable = !isOnCooldown && CanAfford();
}
private bool CanAfford()
{
if (ManaSystem.Instance == null || characterData == null)
{
return false;
}
return ManaSystem.Instance.CurrentManaInt >= characterData.manaCost;
}
private float GetCooldownTime()
{
return characterData != null ? characterData.cooldownTime : 5f;
}
private void UpdateCardUI()
{
if (characterData == null)
{
return;
}
if (cardIcon != null && characterData.cardIcon != null)
{
cardIcon.sprite = characterData.cardIcon;
}
if (characterNameText != null)
{
characterNameText.text = characterData.characterName;
}
if (manaCostText != null)
{
manaCostText.text = characterData.manaCost.ToString();
}
}
}
+17
View File
@@ -125,6 +125,23 @@ public class UnitBase : MonoBehaviour
// Start is called before the first frame update
public void InitializeFromPlayerData(float health, float attack, float moveSpeed)
{
attackPower = attack;
speed = moveSpeed;
if (worldHealthBar == null)
{
worldHealthBar = GetComponent<WorldHealthBar>();
}
if (worldHealthBar != null)
{
worldHealthBar.maxHealth = health;
worldHealthBar.currentHealth = health;
}
}
void Start()
{
//初始化
+115
View File
@@ -0,0 +1,115 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WaveSpawner : MonoBehaviour
{
[System.Serializable]
public class Wave
{
public string waveName = "第一波";
public GameObject[] enemyPrefabs;
public Vector3[] spawnPositions;
public float spawnDelay = 0.5f;
}
[Header("波次配置")]
public Wave[] waves;
[Header("触发设置")]
public float triggerZPosition = 20f;
public bool autoStart = false;
private int currentWave = 0;
private bool waveStarted = false;
private bool waveCompleted = false;
private List<GameObject> activeEnemies = new List<GameObject>();
private Transform player;
private void Start()
{
player = GameObject.FindGameObjectWithTag("Player")?.transform;
if (player == null)
{
player = FindObjectOfType<MainCharacterCombat>()?.transform;
}
if (autoStart)
{
StartWave();
}
}
private void Update()
{
if (!waveStarted && !waveCompleted && player != null)
{
if (player.position.z >= triggerZPosition)
{
StartWave();
}
}
if (waveStarted && !waveCompleted)
{
CleanupDestroyedEnemies();
if (activeEnemies.Count == 0)
{
CompleteWave();
}
}
}
private void StartWave()
{
if (currentWave >= waves.Length)
{
return;
}
waveStarted = true;
StartCoroutine(SpawnWave(waves[currentWave]));
}
private IEnumerator SpawnWave(Wave wave)
{
Debug.Log($"开始生成:{wave.waveName}");
int count = Mathf.Min(wave.enemyPrefabs.Length, wave.spawnPositions.Length);
for (int i = 0; i < count; i++)
{
if (wave.enemyPrefabs[i] != null)
{
Vector3 spawnPos = transform.position + wave.spawnPositions[i];
GameObject enemy = Instantiate(wave.enemyPrefabs[i], spawnPos, Quaternion.identity);
enemy.tag = "Enemy";
activeEnemies.Add(enemy);
Debug.Log($"生成敌人:{enemy.name} 位置={spawnPos}");
yield return new WaitForSeconds(wave.spawnDelay);
}
}
}
private void CleanupDestroyedEnemies()
{
activeEnemies.RemoveAll(enemy => enemy == null);
}
private void CompleteWave()
{
waveCompleted = true;
Debug.Log($"完成:{waves[currentWave].waveName}");
}
public void NextWave()
{
currentWave++;
waveStarted = false;
waveCompleted = false;
activeEnemies.Clear();
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: XS4f4S7/WijE42v1mXpkmgvnNyaw2MphlMrkNdzMukMLev/BbsLYREs=
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: