using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using DG.Tweening; using UnityEngine.SceneManagement; /* 箭对象 */ public class Arrow : MonoBehaviour { //飞行时间统计 [NonSerialized] public float flyTime = 0; //标识—是否击中了什么 [NonSerialized] public bool isHit = false; //箭的初速度(私用) [NonSerialized] public float mySpeed = 0; //箭的初速度(公用) public static float speed = GameMgr.RealSizeToGameSize(60); //箭射出时从弓传过来的参数 #region //手臂弓 [NonSerialized] public ArmBow armBow; //射出时的起始坐标 [NonSerialized] public Vector3 shootOutPosition; //绝对射线 [NonSerialized] public RaycastHit absoluteRay; //默认层级的射线对象 [NonSerialized] public RaycastHit absoluteRayDef; //制作误差偏移角(强行制造误差,为了增加游戏难度,该偏移角的大小根据难度而定) [NonSerialized] public float offsetAngle; //误差偏移后的欧拉角 [NonSerialized] public Vector3 finalAngleAfterOffset; #endregion //射线击中的靶子 TargetBody rayHitTargetBody; //能够完美击中射线点 bool canPerfectHit; //能否使用侧面镜头 [NonSerialized] public bool canUseSideCamera; [NonSerialized] public bool canUserLockCamera; [NonSerialized] public ArrowCamera arrowCameraComp; public System.Action onDoNextShoot; public static HashSet arrowSet = new HashSet(); private bool isInGameScene = false; private float currentSlowFactor = 1; void Awake() { arrowSet.Add(this); GameMgr.ins.gameMode.PauseTimeCounting(this); //箭模型平时属于ArmBow层,因为ArmBow层在飞行镜头中不被渲染,所以箭出去后要切换layer this.transform.Find("Head/_hunse_jian").gameObject.layer = 0; if (SceneManager.GetActiveScene().name == "Game" && GlobalData.MyDeviceMode == DeviceMode.Archery) { //经典射箭 isInGameScene = true; currentSlowFactor = ArmBow.ins.slowFactor; } else { isInGameScene = false; } } void OnDestroy() { arrowSet.Remove(this); if (GameMgr.ins) GameMgr.ins.gameMode.ResumeTimeCounting(this); } void Start() { //speed = 23; mySpeed = speed; Billboard.ins?.SetArrowSpeed(speed); if (GameAssistUI.ins && GlobalData.MyDeviceMode == DeviceMode.Archery) { mySpeed *= GameAssistUI.ins.shootScaleValue; Billboard.ins?.SetArrowSpeedScale(GameAssistUI.ins.shootScaleValue); } Debug.Log($"Speed:{speed},mySpeed:{mySpeed},shootScaleValue :{GameAssistUI.ins.shootScaleValue}"); Billboard.ins?.ShowSpeed(); if (GlobalData.pkMatchType == PKMatchType.OnlinePK) { if (GameMgr.gameType == 9) { ((PKGameMode_OnlinePK)GameMgr.ins.gameMode).shootSpeedWillSend = Billboard.ins.GetShootSpeedText(); } } if (absoluteRay.transform) { //记录射线有没有击中靶子 rayHitTargetBody = absoluteRay.transform.GetComponent(); //把瞄准点画成红圈,渲染在靶子上 if (rayHitTargetBody) { //如果是目标是靶子,就是启动靶子摄像头 canUserLockCamera = true; //string typeStr = GlobalData.MyDeviceMode == DeviceMode.Archery? "RedCircle":"BulletCircle"; Transform redCircle = rayHitTargetBody.transform.Find("RedCircle"); redCircle.gameObject.SetActive(true); redCircle.transform.position = -redCircle.transform.forward * 0.001f + absoluteRay.point; //渲染弹坑 if (GlobalData.MyDeviceMode == DeviceMode.Gun) { Transform crater = transform.Find("Crater"); crater.rotation = redCircle.rotation; } } } //新的靶子场景不用这个SetUpBeforFly if (isInGameScene) { this.InitFlyLogic_Parabola(); } else { SetUpBeforFly(); } if (GlobalData.MyDeviceMode == DeviceMode.Archery) { Transform cameraTF = this.transform.Find("Camera"); cameraTF.gameObject.SetActive(true); arrowCameraComp = cameraTF.gameObject.AddComponent(); arrowCameraComp.arrow = this; } this.activeEffectTrail(true); } /**在箭飞行前初始化&如果瞄准射线的落点在靶子上时,才调用该函数 */ void SetUpBeforFly() { //基础角 float baseAngleX = FormatAngleX(this.transform.eulerAngles.x); //最大可调整的角度差 float maxDeltaAngleX = 5; //最终角 float finalAngleX = baseAngleX; //额外偏移误差角 float offsetAngleX = FormatAngleX(this.finalAngleAfterOffset.x) - baseAngleX; float offsetAngleY = FormatAngleY(this.finalAngleAfterOffset.y - this.transform.eulerAngles.y); if (DebugArrowOffsetAngle.ins) { offsetAngleX *= DebugArrowOffsetAngle.ins.GetOffsetScaleValue(); offsetAngleY *= DebugArrowOffsetAngle.ins.GetOffsetScaleValue(); } else { offsetAngleX *= this.armBow.shootOffsetAngleScale; offsetAngleY *= this.armBow.shootOffsetAngleScale; } DebugArrowOffsetAngle2.ins?.SetOffsetAngles(offsetAngleX, offsetAngleY); if (absoluteRay.transform) { bool plusOffsetAngleY = false; Debug.Log($"射出位置:{shootOutPosition} 目标位置:{absoluteRay.point} 名字: {absoluteRay.collider.name}"); //看能否通过调整发射角让箭落在靶子的瞄准点上 CalculateParabolaAngle(absoluteRay.point); //瞄准的是不是Target层 bool isTargetLayer = IsTargetLayer(absoluteRay.transform.gameObject); //绝对发射角无解 if (!hasParabolaAngle) { if (isTargetLayer) AimLoadChecker.ins?.ShowOutTip(); } else { //来到这里,证明发射角有解,该解姑且叫它绝对角 float absoluteAngleX = parabolaAngleInRadian / Mathf.PI * 180; //客户要求绝对角跟原本角度相差不能超过 maxDeltaAngleX float deltaAngleX = absoluteAngleX - baseAngleX; //如果绝对角跟原本角度相差不超过maxDeltaAngleX if (Mathf.Abs(deltaAngleX) < maxDeltaAngleX) { finalAngleX = Mathf.Clamp(absoluteAngleX + offsetAngleX, -89, 89); plusOffsetAngleY = true; if (Math.Abs(offsetAngle) < 0.001) { canPerfectHit = true; } if (rayHitTargetBody && Mathf.RoundToInt(rayHitTargetBody.GetDistance()) >= 50 && UnityEngine.Random.value < 0.5) { canUseSideCamera = true; } } else { finalAngleX = Mathf.Clamp(baseAngleX + maxDeltaAngleX, -89, 89); if (isTargetLayer) AimLoadChecker.ins?.ShowOutTip(); } } finalPoint = absoluteRay.point; if (plusOffsetAngleY) { Vector3 myPos = this.transform.position; Vector3 pointer = finalPoint - myPos; pointer = Quaternion.AngleAxis(offsetAngleY, Vector3.up) * pointer; finalPoint = myPos + pointer; this.transform.LookAt(finalPoint); } } else { //以前默认的计算 finalPoint = this.transform.position + this.transform.forward * 100; } parabolaAngleInRadian = finalAngleX / 180 * Mathf.PI; } /* 因为Unity引擎的规则,向上时359~270度,向下是0~90度 为了方便数学运算,换算成 向上时0~90度,向下是0~-90度 */ float FormatAngleX(float value) { if (value > 180) value = 360 - value; else value = -value; return value; } /**UnityY轴旋转只有0°~360°,两个Y轴旋转相减求得的夹角时会出现>180的情况,该函数就是为了解决这问题 */ float FormatAngleY(float value) { if (Mathf.Abs(value) > 180) { if (value < 0) { return 360f + value; } if (value > 0) { return value - 360f; } } return value; } bool IsTargetLayer(GameObject gameObject) { return (1 << gameObject.layer) == LayerMask.GetMask("Target"); } public Transform Head() { return this.transform.Find("Head"); } /**发射角有无解 */ bool hasParabolaAngle = false; /**发射角弧度解 */ float parabolaAngleInRadian = 0; /** 求弓箭发射到指定坐标所需要的角度。 已知初速度大小V,重力g,起始坐标(a1,a2),目标坐标(b1,b2)。 解: 1、列出关系式 Δx = b1 - a1; Δy = b2 - a2; Vx = V * cos(angle) Vy = V * sin(angle) Vy * t + 1/2 * g * t^2 = Δy Vx * t = Δx t = Δx / Vx 2、推导过程 (V * sin(angle)) * Δx / (V * cos(angle)) + 1/2 * g * Δx^2 / (V^2*cos(angle)^2) = Δy tan(angle) * Δx + 1/2 * g * Δx^2 / (V^2*cos(angle)^2) = Δy tan(angle) * Δx + 1/2 * g * (Δx^2 / V^2) * (1 + tan(angle)^2) = Δy 3、根据求根公式得出结论 a = 1/2 * g * Δx^2 / V^2 b = Δx c = a - Δy d = tan(angle) = (-b ± (b^2 - 4*a*c)^0.5) / (2*a) angle = atan(d) 有无根的判别式,有根则b^2-4*a*c>=0 */ void CalculateParabolaAngle(Vector3 destination) { float deltaX = Vector2.Distance( new Vector2(destination.x, destination.z), new Vector2(this.transform.position.x, this.transform.position.z) ); float deltaY = destination.y - this.transform.position.y; float a = 0.5f * Physics.gravity.y * Mathf.Pow(deltaX, 2) / Mathf.Pow(this.mySpeed, 2); float b = deltaX; float c = a - deltaY; hasParabolaAngle = Mathf.Pow(b, 2) - 4 * a * c >= 0; //Debug.Log("是否有弧度解:" + hasParabolaAngle); if (hasParabolaAngle) { float res1 = (-b + Mathf.Pow(Mathf.Pow(b, 2) - 4 * a * c, 0.5f)) / (2 * a); float res2 = (-b - Mathf.Pow(Mathf.Pow(b, 2) - 4 * a * c, 0.5f)) / (2 * a); //parabolaAngleInRadian = Mathf.Min(Mathf.Atan(res1), Mathf.Atan(res2)); // 两个解分别代表低抛角和高抛角 float angle1 = Mathf.Atan(res1); // 弧度 float angle2 = Mathf.Atan(res2); // 这里决定是取低角度还是高角度 parabolaAngleInRadian = Mathf.Min(angle1, angle2); //parabolaAngleInRadian = Mathf.Max(angle1, angle2); // 高抛解 } } void FixedUpdate() { if (!isHit && flyTime >= 0) { flyTime += Time.deltaTime; if (flyTime > 14) { Debug.Log("flyTime:"+ flyTime); FlyTimeOut(); } // this.UpdateRotate(); } } public ArrowSync.SyncData outputSyncData; void Update() { if (isInGameScene) UpdateFlyLogic_Parabola(); else UpdateFlyLogic(); if (GlobalData.pkMatchType == PKMatchType.OnlinePK) { if (outputSyncData == null) outputSyncData = new ArrowSync.SyncData(); outputSyncData.SetData(this); } } IEnumerator delayFlyTimeOut() { yield return new WaitForSeconds(0.8f);//gun_shoot 音频长度 FlyTimeOut(); } void FlyTimeOut() { if (arrowCameraComp != null) { var targetLock = arrowCameraComp.arrowCameraTemplate as ArrowCameraTemplate_targetLock; if (targetLock != null && !targetLock.IsDeleting) { // 删除弓箭相机,因为Lock目标靶的情况下 会脱离弓箭 Destroy(arrowCameraComp.gameObject); } } Destroy(gameObject); GameMgr.ins.gameMode.HitTarget(0); AudioMgr.ins.PlayCheer(false); nextShoot(); } float logicFlyTime = 0; Vector3 finalPoint; //飞行速度可在过程中变化,比如快到目标时突然加速 public AnimationCurve speedCurve = AnimationCurve.Linear(0, 1, 1, 1.5f); /**飞行帧逻辑(运动学) */ void UpdateFlyLogic() { if (isHit) return; logicFlyTime += Time.deltaTime; Vector3 destination = finalPoint; float vx = Mathf.Cos(parabolaAngleInRadian) * mySpeed; float t = logicFlyTime; float vy = Mathf.Sin(parabolaAngleInRadian) * mySpeed + Physics.gravity.y * t; float dy = Mathf.Sin(parabolaAngleInRadian) * mySpeed * t + 0.5f * Physics.gravity.y * Mathf.Pow(t, 2); float dx = vx * t; Vector3 nextPosition = new Vector3(destination.x - shootOutPosition.x, 0, destination.z - shootOutPosition.z); nextPosition = nextPosition.normalized * dx; nextPosition.y = dy; nextPosition = shootOutPosition + nextPosition; Vector3 oldPosition = this.transform.position; this.transform.position = nextPosition; Vector3 eulerAngles = this.transform.eulerAngles; float angleX = Mathf.Atan(vy / vx) / Mathf.PI * 180; eulerAngles.x = -angleX; this.transform.eulerAngles = eulerAngles; float deltaDistance = Vector3.Distance(oldPosition, nextPosition); Ray ray = new Ray(oldPosition, nextPosition - oldPosition); RaycastHit raycastHit; bool raycastResult = Physics.Raycast(ray, out raycastHit, deltaDistance); if (raycastResult) { this.transform.position = raycastHit.point; if (ArrowTraceDebug.ins) ArrowTraceDebug.ins.OnArrowUpdate(this); OnHitAnyInFlyLogic(raycastHit); } else { if (ArrowTraceDebug.ins) ArrowTraceDebug.ins.OnArrowUpdate(this); } } #region 新飞行帧逻辑 (非运动学) /** 经典射击里面的 新飞行帧逻辑(非运动学) */ public float minFlyDuration = 0.2f; // 最短飞行时间,受slowFactor影响 public float maxFlyDuration = 2.0f; // 最长飞行时间,受slowFactor影响 public float maxAirTime = 3f; // 最大允许飞行时间,超过这个时间即加上重力 public float minHeight = 0.3f; // 最近时的最小弧度 public float maxHeightFactor = 1f; // 最远时的最大弧度倍数 public float flyDuration; // 自动计算后的飞行时长 public float maxHeight; // 自动计算后的抛物线高度 private float flyTimer = 0f; public float arrowLength = 1.7f; // 箭长度 private bool isFalling = false; /// /// 初始化飞行参数(在发射时调用一次) /// public void InitFlyLogic_Parabola() { //获取终点 if (absoluteRay.transform) { if (Math.Abs(offsetAngle) < 0.001) { canPerfectHit = true; } if (rayHitTargetBody && Mathf.RoundToInt(rayHitTargetBody.GetDistance()) >= 50 && UnityEngine.Random.value < 0.5) { canUseSideCamera = true; } finalPoint = absoluteRay.point; } else { if (absoluteRayDef.transform) { finalPoint = absoluteRayDef.point; } else { //如果没有给个以前默认的计算 finalPoint = this.transform.position + this.transform.forward * 100; } } // 目标距离 float dist = Vector3.Distance(shootOutPosition, finalPoint); // 自动计算飞行时长 //flyDuration = Mathf.Clamp(dist / mySpeed, minFlyDuration, maxFlyDuration); //Debug.Log("dist / mySpeed:" + (dist / mySpeed)); // 超长飞行时间 → 把终点调整到靶子前面,让箭自然落地 float flyTime = dist / mySpeed; if (flyTime <= maxFlyDuration) { // 正常情况 flyDuration = flyTime; } else { // 超过最大飞行时间 → 缩短水平距离 flyDuration = maxFlyDuration; // 缩短比例 = maxFlyDuration / flyTime float ratio = maxFlyDuration / flyTime; // 新终点 = 起点 + ratio * (原终点 - 起点) this.finalPoint = shootOutPosition + (finalPoint - shootOutPosition) * ratio; this.isFalling = true; // currentSlowFactor = ArmBow.ins.notSlowFactor; //这里飞不到的时候给个false,反正弓箭最后跳回瞄准点 //OnHitAnyInFlyLogic 之后 canPerfectHit = false; //给个加速卡提示? AimLoadChecker.ins?.ShowOutTip(); } // 自动计算抛物线弧度(远的高一点,近的低一点) maxHeight = Mathf.Lerp(minHeight, maxHeightFactor, dist / 70f); // 假设70米是最大距离 flyTimer = 0f; isHit = false; // 起始位置 transform.position = shootOutPosition; } /// /// 带抛物线的飞行逻辑 /// void UpdateFlyLogic_Parabola() { if (isHit) return; // 时间推进(整体慢速因子) flyTimer += Time.deltaTime * currentSlowFactor; // 线性进度 float rawProgress = Mathf.Clamp01(flyTimer / flyDuration); // 曲线控制速度分布(0~1) float curveValue = ArmBow.ins.customCurveArrow.Evaluate(rawProgress); // 最终进度 = 曲线值(保证能跑到 1) float progress = Mathf.Clamp01(curveValue); // --- 水平位置插值 --- Vector3 horizontalPos = Vector3.Lerp(shootOutPosition, finalPoint, progress); // --- 抛物线高度 --- float parabola = 4 * progress * (1 - progress); // 标准0~1抛物线 float heightOffset = parabola * maxHeight; Vector3 nextPos = new Vector3( horizontalPos.x, Mathf.Lerp(shootOutPosition.y, finalPoint.y, progress) + heightOffset, horizontalPos.z ); // 移动 Vector3 oldPos = transform.position; // --- 最终位置 --- if (this.isFalling && progress == 1) { //这个状态到了,加个持续飞行,知道碰撞到停止? // 当前状态:到达缩短终点,但需要持续飞行 Vector3 forwardMove = transform.forward * mySpeed *2 * Time.deltaTime * currentSlowFactor; nextPos = transform.position + forwardMove; } transform.position = nextPos; // 箭头朝向(指向移动方向) Vector3 moveDir = (nextPos - oldPos).normalized; if (moveDir.sqrMagnitude > 0.0001f) transform.forward = moveDir; // 箭尖位置(保证碰撞从箭尖检测) Vector3 tipOffset = transform.forward * arrowLength * 0.5f; Vector3 oldTip = oldPos + tipOffset; Vector3 newTip = nextPos + tipOffset; // 轨迹调试线 Debug.DrawLine(shootOutPosition, nextPos, Color.cyan, 2f); Debug.DrawLine(oldTip, newTip, Color.green, 0.1f); // 碰撞检测 if (Physics.Linecast(oldTip, newTip, out RaycastHit hit)) { transform.position = hit.point - tipOffset; // 调整箭模型,使箭尖贴在命中点 Debug.DrawLine(oldTip, hit.point, Color.red, 2f); if (ArrowTraceDebug.ins) ArrowTraceDebug.ins.OnArrowUpdate(this); OnHitAnyInFlyLogic(hit); this.isFalling = false; } else { if (ArrowTraceDebug.ins) ArrowTraceDebug.ins.OnArrowUpdate(this); } } #endregion public class HitType { public static int None = 0; public static int TargetInRing = 1; //击中了靶子,且在得分环内 public static int TargetOutRing = 2; //击中了靶子,但不在得分环内 public static int NotTarget = 4; //击中非目标对象 public static int Animal = 5; //击中动物 } [NonSerialized] public int hitType = HitType.None; [NonSerialized] public Transform raycastHitTransform; //射线击中的目标变换 [NonSerialized] public string[] hitTargetAnimalInfo; //飞行逻辑中检测到碰撞 void OnHitAnyInFlyLogic(RaycastHit raycastHit) { string targetName = raycastHit.transform.gameObject.name; this.Head().position = raycastHit.point; //命中靶子的情况下不移动这个parent,不然弓箭位置旋转会被影响 //其他情况照常 if (targetName != "TargetBody") { this.transform.SetParent(raycastHit.transform.parent); } this.raycastHitTransform = raycastHit.transform; if (targetName == "TargetBody") { Vector3 hitPoint = raycastHit.point; if (rayHitTargetBody && canPerfectHit) { hitPoint = absoluteRay.point; this.Head().position = hitPoint; } if (GlobalData.MyDeviceMode == DeviceMode.Archery) this.arrowCameraComp.arrowCameraTemplate?.beforeHit(); else nextShootFromType();//子弹直接下一轮 raycastHit.transform.GetComponent().Hit(this, hitPoint); } else if (targetName.StartsWith("TargetAnimalPart")) { hitType = HitType.Animal; Vector3 hitPoint = raycastHit.point; string partName = targetName.Split(new char[] { '_' })[1]; if (GlobalData.MyDeviceMode == DeviceMode.Archery) this.arrowCameraComp.arrowCameraTemplate?.beforeHit(); else nextShootFromType();//子弹直接下一轮 TargetAnimal targetAnimal = raycastHit.transform.GetComponentInParent(); targetAnimal.OnHit(this, hitPoint, partName); //箭击中的音效 AudioMgr.ins.PlayArrowEnter(); //记录击中的部位和动物ID hitTargetAnimalInfo = new string[] { targetAnimal.GetOnlineID().ToString(), targetAnimal.targetAnimalParts.IndexOf(raycastHitTransform).ToString(), SyncDataUtil.Vec3ToStr(transform.localPosition), SyncDataUtil.QuatToStr(transform.localRotation), SyncDataUtil.Vec3ToStr(transform.position), SyncDataUtil.QuatToStr(transform.rotation) }; } else if (raycastHit.transform.GetComponent()) { //撞到空气墙当作超时处理 if (GlobalData.MyDeviceMode == DeviceMode.Archery) FlyTimeOut(); else StartCoroutine(delayFlyTimeOut()); } else { if (GlobalData.MyDeviceMode == DeviceMode.Archery) this.arrowCameraComp.arrowCameraTemplate?.beforeHit(); else nextShootFromType();//子弹直接下一轮 Hit(); GameMgr.ins.gameMode.HitTarget(0); //击中其它东西时的音效 hitType = HitType.NotTarget; if (UnityEngine.SceneManagement.SceneManager.GetActiveScene().name == "Game") { AudioMgr.ins.PlayCheer(false); } else { if (CommonConfig.StandaloneModeOrPlatformB) { AudioMgr.ins.PlayCheer(false); } else { AudioMgr.ins.PlayArrowEnter(); } } } } public void Hit() { isHit = true; if (GameDebug.ins) GameDebug.ins.ShowRes(absoluteRay.point, this.Head().position); //控制箭的特效显示 this.activeEffectCyclone(false); this.activeEffectBomb(true); this.activeEffectTrail(false); //最新一箭击中后会发光标记 ArrowLightSick.RecoveryAll(); this.GetComponentInChildren().Hit(); } //进入下一轮射击 [NonSerialized] public bool hasDoneNextShoot = false; public void nextShootFromType() { // Debug.Log("nextShootFromType:" + GlobalDataTemp.pkMatchType); if (GlobalDataTemp.pkMatchType == PKMatchType.LocalPK || GlobalDataTemp.pkMatchType == PKMatchType.OnlinePK) { StartCoroutine(delayNectShoot()); } else { nextShoot(); } } IEnumerator delayNectShoot() { yield return new WaitForSeconds(1.0f); nextShoot(); } public void nextShoot() { if (hasDoneNextShoot) return; hasDoneNextShoot = true; GameMgr.ins.gameMode.ResumeTimeCounting(this); onDoNextShoot?.Invoke(); try { //把瞄准点画成红圈,渲染在靶子上(取消) if (rayHitTargetBody) { //string typeStr = GlobalData.MyDeviceMode == DeviceMode.Archery ? "RedCircle" : "BulletCircle"; Transform redCircle = rayHitTargetBody.transform.Find("RedCircle"); redCircle.gameObject.SetActive(false); } //最新一箭击中后会发光标记(取消) ArrowLightSick.RecoveryAll(); } catch (System.Exception e) { Debug.LogError(e.Message + "\n" + e.StackTrace); } if (!GameMgr.ins.gameMode.DoNextShoot()) return; this.armBow.readyShoot(); } //---------箭矢旋转-------- Vector3 rotateV3 = new Vector3(0, 0, 1400); void UpdateRotate() { this.Head().Rotate(rotateV3 * Time.deltaTime, Space.Self); } //---------箭矢特效--------- public void activeEffectCyclone(bool value) { this.transform.Find("Head/EF_kuosanquan").gameObject.SetActive(value); if (!value) return; ParticleSystemRenderer ps = this.transform.Find("Head/EF_kuosanquan/kuosan").GetComponent(); ParticleSystemRenderer ps1 = this.transform.Find("Head/EF_kuosanquan/kuosan (1)").GetComponent(); DOTween.To(() => ps.minParticleSize, value => { ps.minParticleSize = value; ps.maxParticleSize = value; }, 0.4f, 0.6f); DOTween.To(() => ps1.minParticleSize, value => { ps1.minParticleSize = value; ps1.maxParticleSize = value; }, 0.8f, 0.6f); } void activeEffectBomb(bool value) { this.transform.Find("Head/EF_baodian").gameObject.SetActive(value); } void activeEffectTrail(bool value) { if (GlobalData.MyDeviceMode == DeviceMode.Archery) { Transform guadiantuowei = this.transform.Find("guadiantuowei"); if (guadiantuowei == null) { this.transform.Find("EF_tuowei").gameObject.SetActive(value); this.transform.Find("EF_tuowei/Trail").GetComponent().time = 1.6f / mySpeed; } else { guadiantuowei.gameObject.SetActive(value); } } else { StartCoroutine(showTrail(value)); } } IEnumerator showTrail(bool value) { this.transform.Find("EF_tuowei_bullet").gameObject.SetActive(value); //this.transform.Find("EF_tuowei_bullet/Trail").GetComponent().time = 0; yield return new WaitForEndOfFrame(); this.transform.Find("EF_tuowei_bullet/GB_flame_FX_1").gameObject.SetActive(true); //this.transform.Find("EF_tuowei_bullet/Trail").GetComponent().time = 0.02f; //this.transform.Find("EF_tuowei_bullet/Trail").GetComponent().time = 1.6f / mySpeed; } }