feat(gameplay): 调整单位属性和纹理设置并优化主角移动系统

- 修改多个纹理资源的最大尺寸从2048降至512,并添加PlayableAds平台设置
- 调整第一、二章多个单位的移动速度、攻击力和攻击持续时间参数
- 重命名精锐士兵预制体名称,移除"新"字后缀
- 降低精锐士兵的攻击力和攻击持续时间

- 为主角战斗组件添加detectRange字段,实现寻敌和攻击范围分离
- 重构主角移动系统,从点击移动改为自动巡逻模式,增加栓绳机制防止远离基地
- 实现巡逻和追击状态切换,在发现敌人时停止巡逻进行攻击
-
This commit is contained in:
huadexuan committed 2026-09-14 18:36:50 +08:00
1 parent 769aea30f3
commit 8d43961f67
28 files changed
+443 -125

No files matched your search

+42 -6
View File
@@ -7,6 +7,9 @@ public class MainCharacterCombat : MonoBehaviour
[Min(0f)]
public float attackRange = 8f;
[Min(0f)]
public float detectRange = 10f;
[Min(0f)]
public float attackPower = 50f;
@@ -86,13 +89,39 @@ public class MainCharacterCombat : MonoBehaviour
return;
}
if (movement == null || movement.IsMoving || animator == null)
if (animator == null)
{
return;
}
// 巡逻中也能发现敌人;发现后停止巡逻,没有敌人则恢复巡逻
target = FindNearestEnemy();
if (target == null || attackCooldown > 0f)
if (target == null)
{
if (movement != null)
{
movement.ClearChaseTarget();
movement.SetPatrolEnabled(true);
}
return;
}
if (movement != null)
{
movement.SetPatrolEnabled(false);
// 寻敌范围内但超出攻击范围:主动走近敌人再攻击(受基地栓绳限制)
float targetDistance = GetHorizontalDistance(target.transform.position);
if (targetDistance > attackRange)
{
movement.SetChaseTarget(target.transform.position);
return;
}
movement.ClearChaseTarget();
}
if (attackCooldown > 0f)
{
return;
}
@@ -165,7 +194,7 @@ public class MainCharacterCombat : MonoBehaviour
if (movement != null)
{
movement.StopMovement();
movement.SetPatrolEnabled(false);
}
if (attacking)
@@ -334,8 +363,7 @@ public class MainCharacterCombat : MonoBehaviour
private bool IsInsideMovementBounds(Vector3 position)
{
return movement == null || (position.x >= movement.xLimits.x && position.x <= movement.xLimits.y
&& position.z >= movement.zLimits.x && position.z <= movement.zLimits.y);
return movement == null || movement.IsWithinLeash(position);
}
private void CheckSummonFinished()
@@ -466,10 +494,18 @@ public class MainCharacterCombat : MonoBehaviour
animator.SetInteger("speed", 0);
}
private float GetHorizontalDistance(Vector3 position)
{
Vector3 offset = position - transform.position;
offset.y = 0f;
return offset.magnitude;
}
private GameObject FindNearestEnemy()
{
// 索敌范围使用较小的 detectRange,主角不主动远离基地寻找敌人
GameObject nearest = null;
float nearestDistance = attackRange * attackRange;
float nearestDistance = detectRange * detectRange;
foreach (GameObject enemy in GameObject.FindGameObjectsWithTag("Enemy"))
{
+169 -37
View File
@@ -1,49 +1,91 @@
using UnityEngine;
using UnityEngine.EventSystems;
/// <summary>
/// 主角自动巡逻移动:不再支持点击地面移动(避免与拖动屏幕、建筑放置的输入冲突)。
/// 没有敌人时在己方主建筑周围小范围来回走动,所有移动被限制在基地附近(栓绳范围)。
/// 战斗组件发现敌人后调用 SetPatrolEnabled(false) 停止巡逻进行攻击。
/// </summary>
public class MainCharacterDebugMovement : MonoBehaviour
{
[Header("电脑调试移动")]
[Header("巡逻移动")]
[Min(0f)]
public float moveSpeed = 4f;
[Min(0f)]
public float clickStopDistance = 0.08f;
public float patrolStopDistance = 0.15f;
[Header("移动范围")]
public bool limitToBattlePlatform = true;
public Vector2 xLimits = new Vector2(-7.5f, 7.5f);
public Vector2 zLimits = new Vector2(-14f, 14f);
[Min(0f)]
public float patrolPause = 1.5f;
[Header("巡逻区域(以主建筑为锚点)")]
[Min(0f)]
public float patrolRadius = 5f;
[Min(0f)]
public float leashRadius = 8f;
public string friendlyBuildingTag = "Friend";
private Animator animator;
private Vector3 clickTarget;
private bool hasClickTarget;
private Transform baseAnchor;
private Vector3 fallbackAnchor;
private Vector3 patrolTarget;
private Vector3 chaseTarget;
private bool hasPatrolTarget;
private bool hasChaseTarget;
private float pauseTimer;
private bool patrolEnabled = true;
public bool IsMoving => hasClickTarget;
public bool IsMoving { get; private set; }
private Vector3 AnchorPosition
{
get
{
Vector3 position = baseAnchor != null ? baseAnchor.position : fallbackAnchor;
position.y = transform.position.y;
return position;
}
}
private void Start()
{
animator = GetComponent<Animator>();
clickTarget = transform.position;
fallbackAnchor = transform.position;
baseAnchor = FindMainBase();
SetWalking(false);
}
private void Update()
{
HandleClickTarget();
if (!hasClickTarget)
if (!patrolEnabled)
{
UpdateChase();
return;
}
if (pauseTimer > 0f)
{
pauseTimer -= Time.deltaTime;
IsMoving = false;
SetWalking(false);
return;
}
Vector3 toTarget = clickTarget - transform.position;
if (!hasPatrolTarget)
{
patrolTarget = GetNextPatrolPoint();
hasPatrolTarget = true;
}
Vector3 toTarget = patrolTarget - transform.position;
toTarget.y = 0f;
if (toTarget.magnitude <= clickStopDistance)
if (toTarget.magnitude <= patrolStopDistance)
{
hasClickTarget = false;
hasPatrolTarget = false;
pauseTimer = patrolPause;
IsMoving = false;
SetWalking(false);
return;
}
@@ -51,32 +93,122 @@ public class MainCharacterDebugMovement : MonoBehaviour
Move(toTarget.normalized);
}
private void HandleClickTarget()
private void UpdateChase()
{
if (!Input.GetMouseButtonDown(0) || Camera.main == null)
if (!hasChaseTarget)
{
IsMoving = false;
SetWalking(false);
return;
}
Vector3 toTarget = chaseTarget - transform.position;
toTarget.y = 0f;
if (toTarget.magnitude <= patrolStopDistance)
{
IsMoving = false;
SetWalking(false);
return;
}
// 追击同样受栓绳限制,不会完全离开基地
Move(toTarget.normalized);
}
private Transform FindMainBase()
{
Transform firstBuilding = null;
foreach (GameObject candidate in GameObject.FindGameObjectsWithTag(friendlyBuildingTag))
{
if (candidate == null || candidate.transform.root == transform.root)
{
continue;
}
if (candidate.GetComponentInParent<BuildingHealth>() == null)
{
continue;
}
// 主建筑按名称优先匹配;找不到时退回第一个己方建筑
if (candidate.name.Contains("主要") || candidate.name.Contains("基地"))
{
return candidate.transform;
}
if (firstBuilding == null)
{
firstBuilding = candidate.transform;
}
}
return firstBuilding;
}
private Vector3 GetNextPatrolPoint()
{
Vector2 offset = Random.insideUnitCircle * patrolRadius;
return ClampToLeash(AnchorPosition + new Vector3(offset.x, 0f, offset.y));
}
private Vector3 ClampToLeash(Vector3 position)
{
Vector3 offset = position - AnchorPosition;
offset.y = 0f;
if (offset.magnitude > leashRadius)
{
offset = offset.normalized * leashRadius;
}
return AnchorPosition + offset;
}
public bool IsWithinLeash(Vector3 position)
{
Vector3 offset = position - AnchorPosition;
offset.y = 0f;
return offset.magnitude <= leashRadius;
}
public void SetPatrolEnabled(bool enabled)
{
if (patrolEnabled == enabled)
{
return;
}
if (EventSystem.current != null && EventSystem.current.IsPointerOverGameObject())
patrolEnabled = enabled;
if (!enabled)
{
return;
hasPatrolTarget = false;
pauseTimer = 0f;
IsMoving = false;
SetWalking(false);
}
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (!Physics.Raycast(ray, out RaycastHit hit))
else
{
return;
hasChaseTarget = false;
}
}
clickTarget = hit.point;
clickTarget.y = transform.position.y;
hasClickTarget = true;
public void SetChaseTarget(Vector3 position)
{
chaseTarget = position;
chaseTarget.y = transform.position.y;
hasChaseTarget = true;
}
public void ClearChaseTarget()
{
hasChaseTarget = false;
}
public void StopMovement()
{
hasClickTarget = false;
hasPatrolTarget = false;
hasChaseTarget = false;
IsMoving = false;
SetWalking(false);
}
@@ -85,19 +217,19 @@ public class MainCharacterDebugMovement : MonoBehaviour
direction.y = 0f;
if (direction.sqrMagnitude <= 0.001f)
{
IsMoving = false;
SetWalking(false);
return;
}
IsMoving = true;
SetWalking(true);
transform.position += direction * moveSpeed * Time.deltaTime;
if (limitToBattlePlatform)
{
Vector3 position = transform.position;
position.x = Mathf.Clamp(position.x, xLimits.x, xLimits.y);
position.z = Mathf.Clamp(position.z, zLimits.x, zLimits.y);
transform.position = position;
}
// 栓绳:任何情况下都不能完全离开基地
Vector3 clamped = ClampToLeash(transform.position);
clamped.y = transform.position.y;
transform.position = clamped;
transform.rotation = Quaternion.LookRotation(direction, Vector3.up);
}
@@ -109,4 +241,4 @@ public class MainCharacterDebugMovement : MonoBehaviour
animator.SetInteger("speed", walking ? 1 : 0);
}
}
}
}