科技AI + 连续攻击移动的ui效果

This commit is contained in:
daixiawu 2025-06-24 00:34:20 +08:00
parent aaad6994a6
commit b836ea4dbd
9 changed files with 223 additions and 113 deletions

View File

@ -233,7 +233,7 @@ MonoBehaviour:
Sprite: {fileID: 0}
VarientSprite: 0
ResourceName: "\u7ED3\u754C\u5854"
Exp: 0
Exp: 1
SpriteList: []
- Resource: 0
Sprite: {fileID: 0}
@ -257,7 +257,7 @@ MonoBehaviour:
Sprite: {fileID: 0}
VarientSprite: 0
ResourceName: "\u77FF\u77F3"
Exp: 2
Exp: 0
SpriteList: []
- Resource: 4
Sprite: {fileID: 0}
@ -275,7 +275,7 @@ MonoBehaviour:
Sprite: {fileID: 0}
VarientSprite: 0
ResourceName: "\u5E84\u7A3C"
Exp: 2
Exp: 0
SpriteList: []
- Resource: 7
Sprite: {fileID: 0}

View File

@ -233,7 +233,7 @@ MonoBehaviour:
Sprite: {fileID: 0}
VarientSprite: 0
ResourceName: 813
Exp: 0
Exp: 1
SpriteList: []
- Resource: 0
Sprite: {fileID: 0}
@ -257,7 +257,7 @@ MonoBehaviour:
Sprite: {fileID: 0}
VarientSprite: 0
ResourceName: 873
Exp: 2
Exp: 0
SpriteList: []
- Resource: 4
Sprite: {fileID: 0}
@ -275,7 +275,7 @@ MonoBehaviour:
Sprite: {fileID: 0}
VarientSprite: 0
ResourceName: 876
Exp: 2
Exp: 0
SpriteList: []
- Resource: 7
Sprite: {fileID: 0}

View File

@ -107871,7 +107871,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 4c0a91e9c554c694791fbd5691165798, type: 3}
m_Name:
m_EditorClassIdentifier:
NoAI: 1
NoAI: 0
FullSight: 1
AIActionTime: 0.5
AIAllTech: 1

View File

@ -48,16 +48,6 @@ namespace Logic
unitRenderer.SetAttackHighlight(false);
}
}
public void CheckDoUnitContinuousAction()
{
/*if (mapRenderer.CheckUnitAllMoveTargetHighlight(A.pos, A.unitId))
{
map.ClearHightlightInfo();
OnTileClicked(tilePosition); //连续行动
}
else map.ClearHightlightInfo();*/
}
public void OnTileClicked(MapData mapData,GridData gridData)
{
@ -96,7 +86,6 @@ namespace Logic
CancelAllHighlight();
Main.UnitLogic.Attack(_main.MapData, _main.MapRenderer.SelectUnitData, unitData);
_main.UIManager.BottomInfoUI.UIBottomInfoStatus = false;
CheckDoUnitContinuousAction();
}
//如果是选择角色
else if (!roGrid.IsSelectHighlight)
@ -128,9 +117,19 @@ namespace Logic
else if (roGrid.IsMoveHighlight)
{
CancelAllHighlight();
_main.UIManager.BottomInfoUI.UIBottomInfoStatus = false;
Main.UnitLogic.MoveTo(_main.MapData, _main.MapRenderer.SelectUnitData, gridData,MoveType.ActiveMove);
CheckDoUnitContinuousAction();
_main.UIManager.BottomInfoUI.UIBottomInfoStatus = false;
if (_main.MapRenderer.CheckUnitHasMoveAttackTarget(_main.MapRenderer.SelectUnitData.Id))
{
Timer.Instance.TimerRegister(this, () =>
{
OnTileClicked(mapData,gridData);
},Table.Instance.AnimDataAssets.MoveAnimTime);
}
}
//如果是地块点击
else if (!roGrid.IsSelectHighlight)

View File

@ -78,6 +78,45 @@ namespace Logic
return true;
}
//返回unit2是否能反击unit1
public bool CanCounter(MapData mapData, UnitData unit1, UnitData unit2)
{
if (!mapData.GetPlayerDataByUnitId(unit1.Id, out var player1)) return false;
if (!mapData.GetPlayerDataByUnitId(unit2.Id, out var player2)) return false;
if (!mapData.GetGridDataByUnitId(unit1.Id, out var grid1)) return false;
if (!mapData.GetGridDataByUnitId(unit2.Id, out var grid2)) return false;
// 计算攻击伤害
int dmg1 = Table.Instance.CalcDamage(mapData, unit1, unit2);
//设置unit1 attackendermark和相关参数
//判断对方能否反击的参数
bool canCounter;
canCounter = true;
//是否限制敌方反击
if (unit1.IsLimitTargetCounterAttack(mapData))
canCounter = false;
//敌方是否被限制反击
if (unit2.IsLimitSelfCounterAttack(mapData))
canCounter = false;
//确认对方是否有我的视野,没有的话无法反击
if (!player2.Sight.CheckIsInSight(grid1.Id))
canCounter = false;
//如果对方攻击范围无法覆盖我,则无法反击
if (Table.Instance.CalcDistance(new Vector2Int(grid1.Pos.X,grid1.Pos.Y),new Vector2Int(grid2.Pos.X,grid2.Pos.Y))
> unit2.GetAttackRange())
canCounter = false;
if (dmg1 >= unit2.Health) //如果伤害直接够杀死对方
canCounter = false;
return canCounter;
}
public bool Attack(MapData mapData, UnitData unit1, UnitData unit2)
{
if (!mapData.GetPlayerDataByUnitId(unit1.Id, out var player1)) return false;
@ -118,55 +157,44 @@ namespace Logic
unit1.AttackRenderMarkNeedBack = true;
//判断对方能否反击的参数
bool canCounter;
canCounter = true;
//是否限制敌方反击
if (unit1.IsLimitTargetCounterAttack(mapData))
canCounter = false;
//敌方是否被限制反击
if (unit2.IsLimitSelfCounterAttack(mapData))
canCounter = false;
//确认对方是否有我的视野,没有的话无法反击
if (!player2.Sight.CheckIsInSight(grid1.Id))
canCounter = false;
//如果对方攻击范围无法覆盖我,则无法反击
if (Table.Instance.CalcDistance(new Vector2Int(grid1.Pos.X,grid1.Pos.Y),new Vector2Int(grid2.Pos.X,grid2.Pos.Y))
> unit2.GetAttackRange())
canCounter = false;
bool canCounter = CanCounter(mapData,unit1,unit2);
//一组参数用来处理攻击时长的
//第一次攻击到对方的时刻
float attackWait = Table.Instance.AnimDataAssets.GetAttackTimeByAttackRange(unit1.GetAttackRange());
float counterAttackWait = attackWait + Table.Instance.AnimDataAssets.GetAttackTimeByAttackRange(unit2.GetAttackRange())
+ Table.Instance.AnimDataAssets.BetweenAttackCounterAnimTime +
Table.Instance.AnimDataAssets.GetAttackReturnTimeByAttackRange(unit1.GetAttackRange());;
//第一次攻击到对方后返回的时刻
float attackBack = attackWait + Table.Instance.AnimDataAssets.GetAttackReturnTimeByAttackRange(unit1.GetAttackRange());
float counterAttackStart = attackBack+ Table.Instance.AnimDataAssets.BetweenAttackCounterAnimTime;
//第一次攻击到对方后返回+对方反击打到我的时刻
float counterAttackWait = attackBack + Table.Instance.AnimDataAssets.GetAttackTimeByAttackRange(unit2.GetAttackRange())
+ Table.Instance.AnimDataAssets.BetweenAttackCounterAnimTime;
//第一次攻击到对方后返回+对方反击打到我+对方返回的时刻
float counterAttackBack = counterAttackWait +
Table.Instance.AnimDataAssets.GetAttackReturnTimeByAttackRange(
unit2.GetAttackRange());
if (dmg1 >= unit2.Health) //如果伤害直接够杀死对方
{
//关闭unit2的counter rendermark
canCounter = false;
//处理PERSIST技能
unit1.OnKill(unit2,mapData);
unit2.Health -= dmg1;
//如果是近战且目标位置不是山或者有山的科技,且目标不在水里.
//攻击+移动:如果是近战且目标位置不是山或者有山的科技,且目标不在水里
if (unit1.GetAttackRange() == 1 && grid2.Terrain == TerrainType.Land
&& (grid2.Feature != TerrainFeature.Mountain || player1.TechTree.CheckIfHasTech(TechType.Climbing)))
{
//立刻死亡并播放受伤动画,必须先处理死亡再处理移动不然gridToUnit的dict会出错
Main.UnitLogic.Die(mapData, unit2);
unit1.AttackRenderMark = false;
//移动过去并更新视野
MoveTo(mapData, unit1, grid2,MoveType.AttackMove);
//关闭unit1的rendermakr中 攻击完需要移动回来的开关
//unit1.AttackRenderMarkNeedBack = false;
//杀死对方并移动过去的时刻触发的事件
Timer.Instance.TimerRegister(unit2, () =>
{
var main = GameObject.Find("Main").GetComponent<Main>();
@ -177,7 +205,17 @@ namespace Logic
grid2.SetGridVFXRenderMark(t);
grid2.VFXRenderMarkDie = true;
},0.1f);
//处理玩家攻击后自动再次点击unit从而连续处理移动或者攻击的情况for MapInteraction
if (player1.Id == mapData.PlayerMap.SelfPlayerId &&
main.MapRenderer.CheckUnitHasMoveAttackTarget(unit1.Id))
{
main.MapInteractionLogic.OnTileClicked(mapData,grid2);
}
//权力奇观记录
player1.TotalKill++;
//如果没有STATIC技能那么就可以增加小兵经验
if (!Table.Instance.QueryUnitHasSkill(unit1.UnitType, SkillType.STATIC)) unit1.Exp++;
},Table.Instance.AnimDataAssets.MoveAnimTime);
@ -198,12 +236,41 @@ namespace Logic
main.MapRenderer.ROGridMap[grid2.Id].SetBounceAnim();
},attackWait);
//处理攻击到对方的时刻
Timer.Instance.TimerRegister(unit2, () =>
{
Main.UnitLogic.Die(mapData, unit2);
unit2.RenderMark = true;
grid2.VFXRenderMarkHurt = true;
var t = new GridVFXRenderMark(GridVFXType.Damage);
t.Damage = dmg1;
grid2.SetGridVFXRenderMark(t);
grid2.VFXRenderMarkDie = true;
var main = GameObject.Find("Main").GetComponent<Main>();
main.MapRenderer.ROGridMap[grid2.Id].SetBounceAnim();
},attackWait);
//处理已经攻击返回的时刻
Timer.Instance.TimerRegister(this, () =>
{
var main = GameObject.Find("Main").GetComponent<Main>();
//处理玩家攻击后自动再次点击unit从而连续处理移动或者攻击的情况for MapInteraction
if (player1.Id == mapData.PlayerMap.SelfPlayerId &&
main.MapRenderer.CheckUnitHasMoveAttackTarget(unit1.Id))
{
Debug.Log("!!!!!");
main.MapInteractionLogic.OnTileClicked(mapData,grid2);
}
//权力奇观记录
player1.TotalKill++;
//如果没有STATIC技能那么就可以增加小兵经验
if (!Table.Instance.QueryUnitHasSkill(unit1.UnitType, SkillType.STATIC)) unit1.Exp++;
},attackBack);
}
//权力奇观记录
player1.TotalKill++;
//如果没有STATIC技能那么就可以增加小兵经验
if (!Table.Instance.QueryUnitHasSkill(unit1.UnitType, SkillType.STATIC)) unit1.Exp++;
}
//如果杀不死对方
else
@ -225,11 +292,12 @@ namespace Logic
},
Table.Instance.AnimDataAssets.AttackAnimTime);
//处理反击情况设置unit2的rendermark
unit2.AttackRenderMark = canCounter;
if (canCounter)
{
unit2.AttackRenderMarkWaitTime = 0.4f;
unit2.AttackRenderMarkWaitTime = counterAttackStart;
unit2.AttackRenderMarkAttackAnimType = unit2.GetAttackRange() switch
{
1 => AttackAnimType.Melee, 2 => AttackAnimType.Arrow, 3 => AttackAnimType.Bomb,
@ -279,6 +347,18 @@ namespace Logic
}
}
//在全部攻击结束的时刻触发的事件
Timer.Instance.TimerRegister(this, () =>
{
var main = GameObject.Find("Main").GetComponent<Main>();
//处理玩家攻击后自动再次点击unit从而连续处理移动或者攻击的情况for MapInteraction
if (player1.Id == mapData.PlayerMap.SelfPlayerId && unit1.Alive &&
main.MapRenderer.CheckUnitHasMoveAttackTarget(unit1.Id))
{
main.MapInteractionLogic.OnTileClicked(mapData,grid1);
}
},
counterAttackBack);
}
//处理 ESCAPE 和 SPLASH 技能 OnAttack
unit1.OnAttack(mapData, unit2, dmg1);

View File

@ -833,7 +833,6 @@ namespace TH1Renderer
if (DieVFXAnim != null)
{
_isPlayingDieHintVFX = true;
Debug.Log("!!!!");
animancer.Play(DieHintVFXAnim);
}

View File

@ -318,7 +318,58 @@ namespace TH1Renderer
ROGridMap[gridId].RenderUpdateCityBuilding(cityId);
}
public void SetUnitAllMoveAttackTargetHighlight(uint uid) //渲染所有可移动位置的高亮,其中可以攻击的位置要标红,如果是自己人或者敌人在移动范围内但是不在攻击范围内,则不能高亮
public bool SetUnitAllMoveAttackTargetHighlight(uint uid) //渲染所有可移动位置的高亮,其中可以攻击的位置要标红,如果是自己人或者敌人在移动范围内但是不在攻击范围内,则不能高亮
{
bool ret = false;
Main.UnitLogic.CalcUnitMoveInfo(_main.MapData, uid);
_main.MapData.UnitMap.GetUnitDataByUnitId(uid, out var unitData);
_main.MapData.GetGridDataByUnitId(uid, out var gridData);
_main.MapData.GetPlayerDataByUnitId(uid, out var playerData);
//unitLogic.DebugOutputMoveInfo();
int r = Mathf.Max((Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unitData.UnitType,unitData.GiantType,out var info)?info.MoveRange:0) * 2,
Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unitData.UnitType,unitData.GiantType,out var info2)?info2.AttackRange:0);
var targetGridDataList = _main.MapData.GridMap.GetAroundGridDataSet(r,r,gridData);
foreach(var targetGridData in targetGridDataList)
{
//如果不在视野 跳过
if (!playerData.Sight.CheckIsInSight(targetGridData.Id)) continue;
var sig = Main.UnitLogic.CheckUnitCanMoveOrAttack(_main.MapData, unitData, targetGridData);
//如果是移动目标且unit的MP>0
if ((sig == MoveAttackType.Move || sig == MoveAttackType.MoveToPort ||
sig == MoveAttackType.MoveAshore) && unitData.MP > 0)
{
ret = true;
ROGridMap[targetGridData.Id].SetMoveHighlight(true);
}
//如果是攻击目标且unit的AP>0
if (sig == MoveAttackType.Attack && unitData.AP > 0)
{
ret = true;
if (!_main.MapData.GetUnitDataByGid(targetGridData.Id, out var unitDataB))
continue;
//如果可以杀死,做一个提示
if (Table.Instance.CalcDamage(_main.MapData, unitData, unitDataB) >= unitDataB.Health)
{
targetGridData.VFXRenderMarkDieHintStart = true;
//0代表这是杀死提示
targetGridData.VFXRenderMarkDieHintStartParam = 0;
}
else if(Main.UnitLogic.CanCounter(_main.MapData, unitData, unitDataB) &&
Table.Instance.CalcDamage(_main.MapData, unitData, unitDataB,true) >= unitData.Health)
{
targetGridData.VFXRenderMarkDieHintStart = true;
//1代表这是被杀死提示
targetGridData.VFXRenderMarkDieHintStartParam = 1;
}
ROUnitMap[unitDataB.Id].SetAttackHighlight(true);
}
}
return ret;
}
//返回一个Unit是否存在移动或者攻击目标
public bool CheckUnitHasMoveAttackTarget(uint uid)
{
Main.UnitLogic.CalcUnitMoveInfo(_main.MapData, uid);
_main.MapData.UnitMap.GetUnitDataByUnitId(uid, out var unitData);
@ -334,33 +385,15 @@ namespace TH1Renderer
if (!playerData.Sight.CheckIsInSight(targetGridData.Id)) continue;
var sig = Main.UnitLogic.CheckUnitCanMoveOrAttack(_main.MapData, unitData, targetGridData);
//如果是移动目标且unit的MP>0
if ((sig == MoveAttackType.Move || sig == MoveAttackType.MoveToPort || sig == MoveAttackType.MoveAshore) && unitData.MP > 0)
ROGridMap[targetGridData.Id].SetMoveHighlight(true);
if ((sig == MoveAttackType.Move || sig == MoveAttackType.MoveToPort ||
sig == MoveAttackType.MoveAshore) && unitData.MP > 0)
return true;
//如果是攻击目标且unit的AP>0
if (sig == MoveAttackType.Attack && unitData.AP > 0)
{
if (!_main.MapData.GetUnitDataByGid(targetGridData.Id, out var unitDataB))
continue;
//Debug.Log(Table.Instance.CalcDamage(_main.MapData, unitData, unitDataB));
//Debug.Log(Table.Instance.CalcDamage(_main.MapData, unitDataB, unitData,true));
//如果可以杀死,做一个提示
if (Table.Instance.CalcDamage(_main.MapData, unitData, unitDataB) >= unitDataB.Health)
{
targetGridData.VFXRenderMarkDieHintStart = true;
//0代表这是杀死提示
targetGridData.VFXRenderMarkDieHintStartParam = 0;
}
else if(Table.Instance.CalcDamage(_main.MapData, unitData, unitDataB,true) >= unitData.Health)
{
targetGridData.VFXRenderMarkDieHintStart = true;
//1代表这是被杀死提示
targetGridData.VFXRenderMarkDieHintStartParam = 1;
}
ROUnitMap[unitDataB.Id].SetAttackHighlight(true);
}
return true;
}
return false;
}
// 创建临时探索者并向视野最少的方向移动

View File

@ -226,34 +226,23 @@ namespace TH1Renderer
//Debug.Log($"Unit {_unitId} grid changed: {curGridData.Id}->{_gridData.Id}, needMove={_needMove}, RenderMark={_unitData.RenderMark}");
}
//处理attack bounce 和move 动画三者冲突必须用if else 独立处理
if (_needBounce)
//处理attack bounce 和move 动画优先move然后attack 最后再bounce 三者冲突必须用if else 独立处理
if (_needMove)
{
if (_bounceWaitTime > 0){
_bounceWaitTime -= Time.deltaTime;
}
else
if (_isBounceDown)
//Debug.Log($"Moving from {moveStartPos} to {moveEndPos}" );
moveTime += Time.deltaTime / moveFullTime;
_ROUnit.transform.position = Vector3.Lerp(moveStartPos, moveEndPos, moveTime);
if (moveTime >= 1f)
{
bounceTime += Time.deltaTime / bounceDownFullTime;
_ROUnit.transform.position = Vector3.Lerp(bounceUpPos, bounceDownPos, bounceTime);
if (bounceTime >= 1f)
{
_isBounceDown = false;
bounceTime = 0f;
}
}
else
{
bounceTime += Time.deltaTime / bounceUpFullTime;
_ROUnit.transform.position = Vector3.Lerp(bounceDownPos, bounceUpPos, bounceTime);
if (bounceTime >= 1f)
_needBounce = false;
//Debug.Log($"Unit {_unitId} move completed. In sight: {_mapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(_gridData.Id)}");
_needMove = false;
Vector2Int tt = Table.Instance.WorldToGrid(_ROUnit.transform.position, "isUnit");
if (!_mapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(_gridData.Id))
_ROUnit.SetActive(false);
}
}
else
if (_needAttack)
{
@ -306,23 +295,33 @@ namespace TH1Renderer
}
}
else if (_needMove)
else
if (_needBounce)
{
//Debug.Log($"Moving from {moveStartPos} to {moveEndPos}" );
moveTime += Time.deltaTime / moveFullTime;
_ROUnit.transform.position = Vector3.Lerp(moveStartPos, moveEndPos, moveTime);
if (moveTime >= 1f)
if (_bounceWaitTime > 0){
_bounceWaitTime -= Time.deltaTime;
}
else
if (_isBounceDown)
{
//Debug.Log($"Unit {_unitId} move completed. In sight: {_mapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(_gridData.Id)}");
_needMove = false;
Vector2Int tt = Table.Instance.WorldToGrid(_ROUnit.transform.position, "isUnit");
if (!_mapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(_gridData.Id))
_ROUnit.SetActive(false);
bounceTime += Time.deltaTime / bounceDownFullTime;
_ROUnit.transform.position = Vector3.Lerp(bounceUpPos, bounceDownPos, bounceTime);
if (bounceTime >= 1f)
{
_isBounceDown = false;
bounceTime = 0f;
}
}
else
{
bounceTime += Time.deltaTime / bounceUpFullTime;
_ROUnit.transform.position = Vector3.Lerp(bounceDownPos, bounceUpPos, bounceTime);
if (bounceTime >= 1f)
_needBounce = false;
}
}
}
public void RenderUpdateUnitAll()

Binary file not shown.