UnityCommon/Role/Attack.cs
2025-01-09 10:35:40 +08:00

540 lines
20 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
using Debug = UnityEngine.Debug;
using DG.Tweening;
public class Attack : MonoBehaviour
{
[Header("子弹预制体")] public GameObject bulletPrefab;
[Header("子弹数据")] public BulletData bulletData;
[Header("角色对象")] public Role role;
//[Header("攻击范围")] public float attackScope;
[Header("攻击类型")] public DamageType damageTyp = DamageType.noAttributeDamage;
[Header("攻击CD时间")] public float attackCooldown = 1f; // 攻击冷却时间
private float lastAttackTime = 0f; // 上次攻击的时间
[HideInInspector]
public List<GameObject> bulltes = new List<GameObject>();
[Header("角色动画控制器")] public Animator animator;
//[Header("火焰动画控制器")] public Animator fireAni;
[Header("火焰动画控制器")] public List<Animator> fireAnis;
[Header("子弹起始点")] public Transform BulletStartPos;
[Header("攻击速度")] public float AttackSpeed = 1f;
public Vector2 direction;
[Header("子弹速度加成")] public float roleBulletSpeedAdd = 0f;
//[Header("子弹长度")] public float scaleFactor;
[Header("子弹数量")] public int BulletNumber = 1;
[Header("攻击碰撞体")] public CircleCollider2D attackCollider;
[Header("矩形攻击碰撞体")] public BoxCollider2D attackColliderBox;
[Header("攻击范围图片")] public SpriteRenderer attackRangeSprite;
[Header("攻击持续时间")] public float AttackStayTime;//
[Header("攻击目标")] public GameObject Target;
[Header("攻击范围显示脚本")] public CharacterClick characterClick;
[Header("攻击随机角度范围")] public float Angle=30;
[Header("子弹分裂个数")] public int splitNum = 2;
[Header("分裂子弹伤害")] public float SplitAttack = 10;
[Header("子弹围绕范围")] public float RunRange = 3f;
[Header("是否造成额外伤害")] public bool haveAddDamage = false;
[Header("额外伤害类型")] public DamageType AdddamageType=DamageType.magicDamage;
[Header("子弹爆炸范围")] public float BoomRange = 1;
public bool isAttack = true;
public bool flag = false;
[HideInInspector] public float timer = 0;
public async void Start()
{
bulletData = new BulletData();
bulletData.BulletScattering = 1;
//animator.SetFloat("AttackSpeed", AttackSpeed);
attackCooldown = transform.parent.GetComponent<enemy>().AttackCD;
SetAttackRange(); // 设置攻击范围
while (isAttack)
{
if (attackCollider || attackColliderBox)
{
// 检查是否可以进行攻击(基于冷却时间)
if (Time.time - lastAttackTime >= attackCooldown)
{
if (attackCollider)
{
Collider2D[] colliders = Physics2D.OverlapCircleAll(attackCollider.transform.position, attackCollider.radius);
GetAllColliderInAttackRange(colliders);
}
else if (attackColliderBox)
{
// 获取碰撞体的尺寸
Vector2 boxSize = attackColliderBox.size;
// 只检测右边一半,修改尺寸
boxSize.x /= 2f;
// 获取碰撞体的中心位置
Vector3 colliderCenter = attackColliderBox.transform.position;
// 计算右半部分的中心位置,确保中心点在矩形的右半部分
Vector3 rightCenter = new Vector3(colliderCenter.x + attackColliderBox.size.x / 4f, colliderCenter.y, colliderCenter.z);
// 使用OverlapBoxAll检测右半部分区域
Collider2D[] colliders = Physics2D.OverlapBoxAll(rightCenter, boxSize, 0f);
GetAllColliderInAttackRange(colliders);
}
lastAttackTime = Time.time; // 更新上次攻击时间
}
await Task.Delay(100); // 延迟,避免过于频繁地检测
}
else
{
return;
}
}
}
private void Update()
{
if (flag)
{
timer += Time.deltaTime;
if (timer > AttackStayTime)
{
animator.SetInteger("State", 0);
isAttack = true;
timer = 0;
flag = false;
}
}
//Debug.Log(role.name+"攻击范围的图片大小"+attackRangeSprite.transform.localScale + "color" + attackRangeSprite.color+ "color.a" + attackRangeSprite.color.a + "是否显示"+ attackRangeSprite.enabled);
}
public void GetAllColliderInAttackRange(Collider2D[] colliders)
{
foreach (Collider2D collider in colliders)
{
Role targetRole = collider.GetComponent<Role>();
if (targetRole && targetRole.camp != role.camp)
{
role.animationHighlight = 1;
if (animator != null)
{
if (bulletPrefab.GetComponent<Bullet>().myBulletType != BulletType.Spraying)
{
direction = (targetRole.transform.position - BulletStartPos.position).normalized;
Target = targetRole.gameObject;
animator.SetTrigger("Attack");
}
else if (bulletPrefab.GetComponent<Bullet>().myBulletType == BulletType.Spraying|| bulletPrefab.GetComponent<Bullet>().myBulletType==BulletType.XuLi)
{
animator.SetInteger("State", 1);
flag = true;
}
lastAttackTime = Time.time; // 更新上次攻击时间
}
break; // 只攻击一个目标
}
else
{
if (animator != null)
{
//animator.SetInteger("State", 0);
}
else
{
role.animationHighlight = 0;
}
}
}
}
public void Pointattack()
{
if (bulletPrefab == null)
{
Debug.LogError("子弹预制体为空");
return;
}
for (int i = 0; i < BulletNumber; i++)
{
// 改变子弹方向,根据角色自身的攻击范围来计算
GameObject BulletGamobj = GameObject.Instantiate(bulletPrefab, this.transform.root);
BulletGamobj.GetComponent<Bullet>().role = role;
BulletGamobj.GetComponent<Bullet>().attackObj = this;
BulletGamobj.GetComponent<Bullet>().bulletData.BulletSpeed *= (1 + roleBulletSpeedAdd);
BulletGamobj.GetComponent<Bullet>().Target = Target;
BulletGamobj.transform.up = direction;
BulletGamobj.transform.position = BulletStartPos.position;
bulltes.Add(BulletGamobj);
}
}
public void Fireattack()
{
if (bulletPrefab == null)
{
Debug.LogError("子弹预制体为空");
return;
}
// 检查子弹类型是否为 Spraying
if (bulletPrefab.GetComponent<Bullet>().myBulletType == BulletType.Spraying)
{
// 根据 BulletNumber 生成不同数量和旋转角度的子弹
switch (BulletNumber)
{
case 1:
// 生成一个不旋转的子弹
GenerateBullet(0f, BulletStartPos.position, new Vector2(0, 0));
break;
case 3:
// 生成三个子弹一个不旋转一个向上旋转30度一个向下旋转30度
GenerateBullet(-35f, BulletStartPos.position, new Vector2(0, -0.25f));
GenerateBullet(0f, BulletStartPos.position, new Vector2(0, 0));
GenerateBullet(35f, BulletStartPos.position, new Vector2(0, 0.25f));
break;
default:
// 可选:处理其他 BulletNumber 值的情况
Debug.LogWarning($"BulletNumber 为 {BulletNumber} 时未定义的生成逻辑。");
break;
}
}
}
private void GenerateBullet(float angle, Vector2 startPos, Vector2 changePos)
{
// 实例化子弹对象并设置父物体为 transform.root
GameObject bulletGameObj = GameObject.Instantiate(bulletPrefab, transform.root);
// 获取子弹的原始宽度
float originalWidth = bulletGameObj.GetComponentInChildren<SpriteRenderer>().bounds.size.x;
// 保存原始位置,以便在攻击范围变化时恢复
Vector2 originalPosition = bulletGameObj.transform.position;
// 设置子弹的缩放
if (role.AttackRange / 2 > 1)
{
float offsetX = 0;
bulletGameObj.transform.localScale = new Vector2(role.AttackRange / 2, role.AttackRange / 3);
// 计算新的子弹宽度
float newWidth = bulletGameObj.GetComponentInChildren<SpriteRenderer>().bounds.size.x;
//// 根据宽度变化计算偏移量
//if (newWidth > originalWidth)
//{
// // 如果新宽度大于原始宽度,计算向右的偏移
// offsetX = (originalWidth * (role.AttackRange / 2)) / 4f + 0.15f;
//}
//else if (newWidth < originalWidth)
//{
// // 如果新宽度小于原始宽度,计算向左的偏移
// offsetX = -((originalWidth * (role.AttackRange / 2)) / 4f +0.15f);
//}
offsetX = (newWidth - originalWidth) / 2;
if (offsetX > 0)
{
offsetX += 0.15f;
}
// 调整子弹的位置,确保左边界不变
bulletGameObj.transform.position = new Vector2(originalPosition.x + offsetX, originalPosition.y);
}
// 添加位置微调
bulletGameObj.transform.position = new Vector2(bulletGameObj.transform.position.x + 1.75f, bulletGameObj.transform.position.y + 0.2f);
// 获取动画组件(如果需要的话)
fireAnis.Add(bulletGameObj.GetComponentInChildren<Animator>());
// 设置子弹的初始属性
bulletGameObj.GetComponent<Bullet>().role = role;
bulletGameObj.GetComponent<Bullet>().attackObj = this;
// 计算子弹相对于旋转中心的偏移
Vector2 direction = bulletGameObj.transform.position - (Vector3)startPos;
// 旋转偏移
direction = Quaternion.Euler(0, 0, angle) * direction;
// 更新子弹的位置
bulletGameObj.transform.position = startPos + direction;
// 设置子弹的旋转角度
bulletGameObj.transform.rotation = Quaternion.Euler(0, 0, angle);
// 根据变化调整位置例如“changePos”可能需要与原始位置结合使用
bulletGameObj.transform.position = bulletGameObj.transform.position + (Vector3)changePos;
// 将生成的子弹添加到子弹列表中
bulltes.Add(bulletGameObj);
}
public void EndFire()
{
foreach (Animator ani in fireAnis)
{
ani.SetInteger("State", 1);
}
fireAnis.Clear();
//Debug.Log("停止火焰");
}
private void OnDisable()
{
bulltes.Clear();
}
public void SetAttackRange()
{
if (attackCollider&& role.AttackRange !=0)
{
// 设置碰撞体的半径
attackCollider.radius = role.AttackRange+2f;
// 计算缩放因子,使得图片的直径与碰撞体的直径匹配
characterClick.OrSizeX = (role.AttackRange+2f) * 2f / characterClick.StartSizeX; // * 2f 因为 radius 是半径,图片的尺寸通常是直径
characterClick.OrSizeY = characterClick.OrSizeX;
}
else if(attackColliderBox)
{
Vector2 boxSize = attackColliderBox.size;
// 假设攻击范围决定了矩形的长宽比
// attackRange * 2 可以增加攻击范围的矩形尺寸,调整比例或常数以适应你的需要
boxSize.x = role.AttackRange* 2f + 2; // 设置矩形宽度
//boxSize.y = role.AttackRange/3; // 设置矩形高度
// 更新矩形碰撞体的尺寸
attackColliderBox.size = boxSize;
characterClick.OrSizeX = boxSize.x / characterClick.StartSizeX; // * 2f 因为 radius 是半径,图片的尺寸通常是直径
characterClick.OrSizeY = boxSize.y * 2 / characterClick.StartSizeY;
float offSetX = (characterClick.StartSizeX - characterClick.OrSizeX) / 2;
if (role.AttackRange==3)
{
offSetX += 0.6f;
}
attackRangeSprite.transform.position = new Vector2(attackRangeSprite.transform.position.x - offSetX, attackRangeSprite.transform.position.y);
//characterClick.OrSizeY = 1;
Debug.Log("修改图片宽度");
}
}
public void RandPointattack()//在一个角度随机发射子弹 仓鼠攻击方式
{
if (bulletPrefab == null)
{
Debug.LogError("子弹预制体为空");
return;
}
for (int i = 0; i < BulletNumber; i++)
{
// 改变子弹方向, 根据角色自身的攻击范围来计算
GameObject BulletGamobj = GameObject.Instantiate(bulletPrefab, this.transform.root);
BulletGamobj.GetComponent<Bullet>().role = role;
BulletGamobj.GetComponent<Bullet>().attackObj = this;
BulletGamobj.GetComponent<Bullet>().bulletData.BulletSpeed *= (1 + roleBulletSpeedAdd);
BulletGamobj.GetComponent<Bullet>().Target = Target;
// 计算一个随机偏移角度,在-15度到+15度之间
float randomAngle = Random.Range(-Angle/2, Angle/2);
Debug.Log("土角度"+Angle);
// 将原始方向转换为角度
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
// 加上随机偏移角度
angle += randomAngle;
// 将角度转换回方向
direction = new Vector2(Mathf.Cos(angle * Mathf.Deg2Rad), Mathf.Sin(angle * Mathf.Deg2Rad));
// 设置子弹的方向
BulletGamobj.transform.up = direction;
// 设置子弹的起始位置
BulletGamobj.transform.position = BulletStartPos.position;
// 将子弹添加到列表中
bulltes.Add(BulletGamobj);
}
}
public void ShanXingAttack() // 扇形散射
{
if (bulletPrefab == null)
{
Debug.LogError("子弹预制体为空");
return;
}
float halfAngle = Angle / 2f; // 半个散射角度范围
float angleStep = Angle / (BulletNumber - 1); // 每个子弹之间的角度间隔
for (int i = 0; i < BulletNumber; i++)
{
// 创建子弹实例
GameObject BulletGamobj = GameObject.Instantiate(bulletPrefab, this.transform.root);
Bullet bulletScript = BulletGamobj.GetComponent<Bullet>();
// 设置子弹的基本信息
bulletScript.role = role;
bulletScript.attackObj = this;
bulletScript.bulletData.BulletSpeed *= (1 + roleBulletSpeedAdd); // 根据角色的加成调整子弹速度
bulletScript.Target = Target;
// 计算当前子弹的发射角度
float currentAngle = -halfAngle + angleStep * i; // 当前子弹的偏移角度
// 使用偏移角度调整子弹的发射方向
// 只在z轴上进行旋转2D场景
if (BulletNumber==1)
{
BulletGamobj.transform.up = direction;
}
else
{
Vector3 scatterDirection = Quaternion.Euler(0, 0, currentAngle) * direction; // 只旋转z轴
// 设置子弹的方向和位置
BulletGamobj.transform.up = scatterDirection;
}
BulletGamobj.transform.position = BulletStartPos.position;
// 将子弹加入子弹列表
bulltes.Add(BulletGamobj);
}
}
private int lastBulletNumber = 0; // 记录上一次的 BulletNumber
private float lastbulletLengthAdd = 0; // 记录上一次的 BulletNumber
public float bulletLengthAdd = 0f; // 子弹的长度属性
public void RunRangeattack() // 生成在周围
{
if (bulletPrefab == null)
{
// Debug.LogError("子弹预制体为空");
return;
}
// 只有在 BulletNumber 变化时,才重新生成子弹
if (BulletNumber != lastBulletNumber||lastbulletLengthAdd!=bulletLengthAdd) // 检查 BulletNumber 和 bulletLength 是否变化
{
// 销毁现有的所有子弹
if (bulltes.Count > 0)
{
foreach (var bullet in bulltes)
{
Destroy(bullet); // 销毁子弹对象
}
bulltes.Clear(); // 清空子弹列表
}
// 计算子弹发射的圆周角度间隔
float angleStep = 360f / BulletNumber; // 计算每个子弹之间的角度间隔
// 生成新的子弹
for (int i = 0; i < BulletNumber; i++)
{
// 创建子弹实例
GameObject BulletGamobj = GameObject.Instantiate(bulletPrefab, this.transform.root);
// 获取Bullet组件
RunRangeBullet bulletScript = BulletGamobj.GetComponent<RunRangeBullet>();
// 设置子弹的基本信息
bulletScript.role = role;
bulletScript.attackObj = this;
bulletScript.bulletData.BulletSpeed *= (1 + roleBulletSpeedAdd); // 根据角色的加成调整子弹速度
bulletScript.Target = Target;
// 将新子弹加入子弹列表
bulltes.Add(BulletGamobj);
// 计算当前子弹的发射角度(基于子弹的索引)
float currentAngle = angleStep * i; // 当前子弹的发射角度
// 计算子弹的发射位置,使用极坐标来计算圆周上的位置
float x = Mathf.Cos(Mathf.Deg2Rad * currentAngle) * RunRange;
float y = Mathf.Sin(Mathf.Deg2Rad * currentAngle) * RunRange;
Vector3 position = new Vector3(x, y, 0) + BulletStartPos.position; // 加上起始位置
// 计算子弹的发射方向,使用极坐标转换为单位向量
Vector3 scatterDirection = new Vector3(Mathf.Cos(Mathf.Deg2Rad * currentAngle), Mathf.Sin(Mathf.Deg2Rad * currentAngle), 0);
// 设置子弹的朝向和位置
BulletGamobj.transform.up = scatterDirection; // 让子弹的"up"方向指向计算出的角度
BulletGamobj.transform.position = position; // 更新子弹的位置
if (lastbulletLengthAdd != bulletLengthAdd)
{
// 根据 bulletLength 设置子弹的长度
BulletGamobj.transform.localScale = new Vector3(BulletGamobj.transform.localScale.x + bulletLengthAdd, BulletGamobj.transform.localScale.y, BulletGamobj.transform.localScale.z);
}
}
// 更新记录的 BulletNumber 和 bulletLength
lastBulletNumber = BulletNumber;
lastbulletLengthAdd = bulletLengthAdd;
}
}
}