using CommonAI.RTS; using CommonLang.Vector; using CommonAI.Zone; using CommonAI.Zone.Helper; using CommonLang; using System; using System.Collections.Generic; using System.Linq; using System.Text; using CommonLang.Log; namespace CommonAI.ZoneClient { public class ZoneSpell : ZoneObject { public readonly SpellTemplate Info; public override string Name { get { return null; } } public override string DisplayName { get { return Info.Name; } } public override float X { get { return mLocalPos.X; } } public override float Y { get { return mLocalPos.Y; } } public override float Z { get { return mLocal_Z; } } public override float RadiusSize { get { return mDisplaySize; } } public float RotationX { get; internal set; } public ZoneObject Sender { get; internal set; } public ZoneUnit Launcher { get; internal set; } public ZoneUnit Target { get; internal set; } public Vector2 TargetPos { get; internal set; } public Vector2 StartPos { get { return mStartPos; } } public float LaunchHeight { get { return mStartHeight; } set { this.mStartHeight = value; this.mLocal_Z = value; } } public float BodySize { get { return mDisplaySize; } } public float Distance { get { return mDisplayDistance; } } public int PassTimeMS { get { return mPassTimeMS; } } private Vector2 mLocalPos = new Vector2(); private float mLocal_Z = 0; private float mSizeLimit, mDisplaySize; private float mDistanceLimit, mDisplayDistance; private Vector2 mStartPos = new Vector2(); private float mStartDirection; private float mStartHeight; private int mPassTimeMS; private float mSpeed; //private float mMoveDistance; private int mCurveMissileIndex = -1; private PopupKeyFrames mKeyFrames; private TimeInterval mHitIntervalTicker; private AddSpellEvent mAddEvent; public System.Action OnTargetChanged = null; private static HashMap hashCurveMissileCnt = new HashMap(); private string strCurveMissileIndexKey; public ZoneSpell(SpellTemplate info, SyncSpellInfo syn, ZoneLayer parent, AddSpellEvent add) : base(syn.ObjectID, parent) { this.Info = info; RotationX = 0; this.mStartPos.SetX(syn.x); this.mStartPos.SetY(syn.y); this.mPos.SetX(syn.x); this.mPos.SetY(syn.y); this.mLocalPos.SetX(syn.x); this.mLocalPos.SetY(syn.y); this.mDirection = this.mStartDirection = syn.direction; this.mDistanceLimit = this.mDisplayDistance = info.Distance; this.mSizeLimit = this.mDisplaySize = info.BodySize; this.mPassTimeMS = 0; this.mAddEvent = add; if (add != null && add.IsHeight) { this.mLocal_Z = add.height; } this.mKeyFrames = new PopupKeyFrames(); this.mKeyFrames.AddRange(info.KeyFrames); this.mHitIntervalTicker = new TimeInterval(info.HitIntervalMS); this.mHitIntervalTicker.Tag = info.HitIntervalKeyFrame; this.mSpeed = info.MSpeedSEC; //this.mMoveDistance = 0; if (syn.HasSpeed) { this.mSpeed = syn.CurSpeed; } if(info.MType == SpellTemplate.MotionType.CurveMissile) { int cnt; strCurveMissileIndexKey = add.sender_unit_id.ToString() + ":" + info.TemplateID.ToString(); if (hashCurveMissileCnt.TryGetValue(strCurveMissileIndexKey, out cnt)) { hashCurveMissileCnt[strCurveMissileIndexKey] = ++cnt; } else { hashCurveMissileCnt.Add(strCurveMissileIndexKey, 1); cnt = 1; } mCurveMissileIndex = cnt-1; } } protected override void Disposing() { if (!string.IsNullOrEmpty(strCurveMissileIndexKey)) { int cnt; if (hashCurveMissileCnt.TryGetValue(strCurveMissileIndexKey, out cnt)) { if (cnt <= 1) { hashCurveMissileCnt.Remove(strCurveMissileIndexKey); } else { hashCurveMissileCnt[strCurveMissileIndexKey] = cnt - 1; } } } base.Disposing(); } protected internal override void OnAdded() { base.OnAdded(); float radius = 0; if (mAddEvent != null) { if (mAddEvent.LaunchData.FromUnitBody && Sender != null && (Sender is ZoneUnit)) { radius = (Sender as ZoneUnit).Info.LaunchSpellRadius; } else { radius = mAddEvent.LaunchData.LaunchSpellRadius; } } if (!Info.IsLaunchSpellEventSyncPos) { switch (Info.MType) { case SpellTemplate.MotionType.SelectTarget: if (Target != null) { mStartPos.SetX(Target.X); mStartPos.SetY(Target.Y); mLocal_Z = Target.Z; mStartDirection = Target.ServerDirection; } MathVector.movePolar(mStartPos, mStartDirection, radius); break; case SpellTemplate.MotionType.SelectLauncher: if (Launcher != null) { mStartPos.SetX(Launcher.X); mStartPos.SetY(Launcher.Y); mLocal_Z = Launcher.Z; mStartDirection = Launcher.ServerDirection; } MathVector.movePolar(mStartPos, mStartDirection, radius); break; case SpellTemplate.MotionType.Chain: if (Sender != null) { mStartPos.SetX(Sender.X); mStartPos.SetY(Sender.Y); mLocal_Z = Sender.Z; } break; case SpellTemplate.MotionType.AOE_Binding: case SpellTemplate.MotionType.Binding: if (Sender != null) { mStartPos.SetX(Sender.X); mStartPos.SetY(Sender.Y); mLocal_Z = Sender.Z; } break; case SpellTemplate.MotionType.AOE_BindingTarget: case SpellTemplate.MotionType.BindingTarget: if (Target != null) { mStartPos.SetX(Target.X); mStartPos.SetY(Target.Y); mLocal_Z = Target.Z; } break; } this.mPos.SetX(this.mStartPos.X); this.mPos.SetY(this.mStartPos.Y); this.mLocalPos.SetX(this.mStartPos.X); this.mLocalPos.SetY(this.mStartPos.Y); this.mDirection = this.mStartDirection; } if (Info.TargetEffect != null) { if (Target != null) { Parent.PreQueueEvent(new UnitEffectEvent(Target.ObjectID, Info.TargetEffect)); } else if (TargetPos != null) { Parent.PreQueueEvent(new AddEffectEvent(0,TargetPos.X, TargetPos.Y, mDirection, Info.TargetEffect)); } } } public override void ForceSyncPos(float x, float y) { this.mPos.SetX(x); this.mPos.SetY(y); this.mLocalPos.SetX(x); this.mLocalPos.SetY(y); } internal protected override void DoEvent(ObjectEvent e) { if (e is SpellLockTargetEvent) { doSpellLockTargetEvent(e as SpellLockTargetEvent); } else if (e is ObjectForceSyncPosEvent) { var oe = e as ObjectForceSyncPosEvent; this.ForceSyncPos(oe.X, oe.Y); this.Direction = oe.Direction; } } private void doSpellLockTargetEvent(SpellLockTargetEvent e) { if (Parent == null) { log.Error("doSpellLockTargetEvent -> Parent == null"); } if (Info == null) { log.Error("doSpellLockTargetEvent -> Info == null"); } if (e == null) { log.Error("doSpellLockTargetEvent -> e == null"); } this.Target = Parent.GetUnit(e.target_obj_id); if(OnTargetChanged != null) { OnTargetChanged(); } switch (Info.MType) { case SpellTemplate.MotionType.SeekerSelectTarget: if (Target != null) { this.mLocalPos.SetX(Target.X); this.mLocalPos.SetY(Target.Y); this.mLocal_Z = Target.Z; } break; } } internal protected override void Update() { int intervalMS = Parent.CurrentIntervalMS; mPassTimeMS += intervalMS; //法术位置更新交给战斗服 /*if (Parent.ActorSyncMode != SyncMode.ForceByServer && (Info.MaxMoveDistance == 0 || mMoveDistance < Info.MaxMoveDistance)) { updateMotion(intervalMS); }*/ if (!this.Parent.IsSyncZ && Info.HeightUpdate) { var prez = this.mLocal_Z; updateZPos(); //根据z向高度的变化,调整法术x轴Rotation float difz = mLocal_Z - prez; float speed_distance = MoveHelper.GetDistance(intervalMS, mSpeed); RotationX = (float)Math.Atan2(difz, speed_distance); } /*switch (Info.MType) { case SpellTemplate.MotionType.AOE: case SpellTemplate.MotionType.AOE_Binding: case SpellTemplate.MotionType.AOE_BindingTarget: updateAOE(intervalMS); break; } switch (Info.BodyShape) { case SpellTemplate.Shape.StripRayTouchEnd: updateRayTouchEnd(); break; case SpellTemplate.Shape.LineToStart: case SpellTemplate.Shape.LineToTarget: updateLineToTarget(); break; } if (Info.IsMoveable && (Info.MaxMoveDistance == 0 || mMoveDistance < Info.MaxMoveDistance)) { MoveHelper.UpdateSpeed(intervalMS, ref mSpeed, Info.MSpeedAdd, Info.MSpeedAcc); MoveHelper.UpdateMoveDistance(intervalMS, ref mMoveDistance, mSpeed); } updateKeyFrames();*/ } public override void SyncPos(ref SyncPosEvent.UnitPos pos) { base.SyncPos(ref pos); if (Parent.ActorSyncMode == SyncMode.ForceByServer) { mLocalPos.SetX(pos.X); mLocalPos.SetY(pos.Y); if (Parent.IsSyncZ) { this.Z = pos.Z; } } } //----------------------------------------------------------------------- #region _UpdateMotions_ float foxfireRoundAngle = 0f; //float foxfireSeekWait = 0f; private Logger log = LoggerFactory.GetLogger("ZoneClientSpell"); /// /// 更新移动行为 /// private void updateMotion(int intervalMS) { float speed_distance = MoveHelper.GetDistance(intervalMS, mSpeed); try { switch (Info.MType) { case SpellTemplate.MotionType.SelectLauncher: case SpellTemplate.MotionType.SelectTarget: break; case SpellTemplate.MotionType.Cannon: case SpellTemplate.MotionType.Straight: MathVector.movePolar(mLocalPos, mStartDirection, speed_distance); break; case SpellTemplate.MotionType.StraightAndStop: if (PassTimeMS < Info.BoomerangFlyTime) { MathVector.movePolar(mLocalPos, mStartDirection, speed_distance); } //else if (PassTimeMS > Info.BoomerangFlyTime + Info.BoomerangHangtime) //{ // PreFlyTo(Sender.X, Sender.Y, mSpeed * 1.8f, intervalMS); //} break; case SpellTemplate.MotionType.Boomerang1: if (PassTimeMS < Info.BoomerangFlyTime) { MathVector.movePolar(mLocalPos, mStartDirection, speed_distance); } else if (Sender != null && PassTimeMS > Info.BoomerangFlyTime + Info.BoomerangHangtime) { PreFlyTo(Sender.X, Sender.Y, mSpeed * 1.4f, intervalMS); } break; case SpellTemplate.MotionType.Boomerang2: if (PassTimeMS < Info.BoomerangFlyTime) { MathVector.movePolar(mLocalPos, mStartDirection, speed_distance); } else if (PassTimeMS > Info.BoomerangFlyTime + Info.BoomerangHangtime) { PreFlyTo(mStartPos.X, mStartPos.Y, mSpeed * 1.4f, intervalMS); } break; case SpellTemplate.MotionType.Immovability: case SpellTemplate.MotionType.AOE: mLocalPos.SetX(mPos.X); mLocalPos.SetY(mPos.Y); break; case SpellTemplate.MotionType.AOE_Binding: case SpellTemplate.MotionType.Binding: if (Sender != null) { updateBinding(Sender.X, Sender.Y, Sender.Direction, intervalMS); } else { adjustPos(speed_distance); } break; case SpellTemplate.MotionType.AOE_BindingTarget: case SpellTemplate.MotionType.BindingTarget: if (Target != null) { updateBinding(Target.X, Target.Y, Target.Direction, intervalMS); } else { adjustPos(speed_distance); } break; case SpellTemplate.MotionType.Missile: case SpellTemplate.MotionType.MissileAttackRoute: if (Target != null) { if (Info.RotateSpeedSEC == 0) { PreFaceTo(Target.X, Target.Y); } PreFlyTo(Target.X, Target.Y, mSpeed, intervalMS); } else { MathVector.movePolar(mLocalPos, mStartDirection, speed_distance); } break; case SpellTemplate.MotionType.SeekerMissile: if (Target != null) { if (Info.SeekingTurningAngleSEC != 0) { FlyToTargetTunning(Target.X, Target.Y, mSpeed, Info.SeekingTurningAngleSEC, intervalMS); } else { PreFaceTo(Target.X, Target.Y); PreFlyTo(Target.X, Target.Y, mSpeed, intervalMS); } } else { MathVector.movePolar(mLocalPos, mStartDirection, speed_distance); } break; case SpellTemplate.MotionType.SeekerSelectTarget: break; case SpellTemplate.MotionType.Chain: if (Sender != null && Sender.IsEnable && Target != null && Target.IsActive) { mLocalPos.SetX(Sender.X); mLocalPos.SetY(Sender.Y); PreFaceTo(Target.X, Target.Y); } break; case SpellTemplate.MotionType.CurveMissile: IPosBase targetPos = Target == null ? (IPosBase)TargetPos : (IPosBase)Target; if(targetPos == null) { PreFlyTo(mStartDirection, mSpeed, intervalMS); } else if (Info.SeekingTurningAngleSEC != 0) { FlyToTargetTunning(targetPos.X, targetPos.Y, mSpeed, Info.SeekingTurningAngleSEC, intervalMS); } else { PreFaceTo(targetPos.X, targetPos.Y); PreFlyTo(targetPos.X, targetPos.Y, mSpeed, intervalMS); } break; case SpellTemplate.MotionType.Foxfire: if (Target == null) { var mLaunchData = mAddEvent.LaunchData; foxfireRoundAngle = mStartDirection + PassTimeMS * 0.001f * 3; var x = (float)(Math.Cos(foxfireRoundAngle + 0.3f) * mLaunchData.LaunchSpellRadius); var y = (float)(Math.Sin(foxfireRoundAngle + 0.3f) * mLaunchData.LaunchSpellRadius); if (Sender != null) { x = Sender.X + x; y = Sender.Y + y; } else { x = mStartPos.X + x; y = mStartPos.Y + y; } PreFlyTo(x, y, mSpeed, intervalMS); if (Info.RotateSpeedSEC == 0) { PreFaceTo(foxfireRoundAngle + (float)Math.PI * 0.25f); } //foxfireSeekWait += intervalMS; } else { if (Info.SeekingTurningAngleSEC != 0) { FlyToTargetTunning(Target.X, Target.Y, mSpeed, Info.SeekingTurningAngleSEC, intervalMS); } else { PreFaceTo(Target.X, Target.Y); PreFlyTo(Target.X, Target.Y, mSpeed, intervalMS); } } break; } if (Info.BodyShape == SpellTemplate.Shape.LineToTarget) { if (Target != null) { PreFaceTo(Target.X, Target.Y); } } else if (Info.BodyShape == SpellTemplate.Shape.LineToStart) { PreFaceTo(StartPos.X, StartPos.Y); } else if (Info.RotateSpeedSEC != 0 && Info.MType != SpellTemplate.MotionType.SeekerMissile) { this.mDirection += MoveHelper.GetDistance(intervalMS, Info.RotateSpeedSEC); } } catch (Exception e) { log.Error(e.Message + "\n" + "Info.MType = " + Info.MType + " Info.BodyShape = " + Info.BodyShape); } } private void updateZPos() { switch (Info.MType) { case SpellTemplate.MotionType.Immovability: case SpellTemplate.MotionType.SelectTarget: case SpellTemplate.MotionType.SelectLauncher: case SpellTemplate.MotionType.AOE: updateFloatingZ(mStartHeight); break; case SpellTemplate.MotionType.Straight: case SpellTemplate.MotionType.Boomerang1: case SpellTemplate.MotionType.Boomerang2: case SpellTemplate.MotionType.StraightAndStop: updateFloatingZ(mStartHeight); break; case SpellTemplate.MotionType.AOE_Binding: case SpellTemplate.MotionType.Binding: if (Sender != null) { this.mLocal_Z = Sender.Z; } break; case SpellTemplate.MotionType.AOE_BindingTarget: case SpellTemplate.MotionType.BindingTarget: if (Target != null) { this.mLocal_Z = Target.Z; } break; case SpellTemplate.MotionType.Cannon: if (TargetPos != null) { updateTargetZ(TargetPos, 0); } break; case SpellTemplate.MotionType.Missile: case SpellTemplate.MotionType.SeekerMissile: case SpellTemplate.MotionType.MissileAttackRoute: if (Target != null) { updateTargetZ(new TVector2(Target.X, Target.Y), Target.Z + Target.Info.BodyHeight * 0.6f); } break; case SpellTemplate.MotionType.CurveMissile: if (Target != null) { updata3DParabola(new TVector2(Target.X, Target.Y)); } break; } } private void updata3DParabola(IVector2 targetPos) { TVector2 nowPos = new TVector2(mLocalPos.X, mLocalPos.Y); TVector2 vcStart = new TVector2(mStartPos.X, mStartPos.Y); TVector2 vcEnd = new TVector2(targetPos.X, targetPos.Y); TVector2 v1 = nowPos - vcStart; TVector2 v2 = vcEnd - vcStart; float a = (float)Math.Atan2(v1.Y, v1.X); float b = (float)Math.Atan2(v2.Y, v2.X); if (float.Equals(a, b) || float.Equals(Math.PI, Math.Abs(a - b))) { mLocal_Z = Target.Z; } else { float dis = nowPos.DistanceTo(vcStart) * (float)Math.Abs(Math.Sin(a - b)); mLocal_Z = this.mStartHeight + dis *1.6f* (float)Math.Sin(Math.PI / 6 * mCurveMissileIndex); //ClientLog.LogWarning(">z:{0}, index:{1}, ({2},{3})->({4},{5})->({6},{7})", mLocal_Z, mCurveMissileIndex, mStartPos.X, mStartPos.Y, TargetPos.X, targetPos.Y, nowPos.X, nowPos.Y); } } /// /// 计算有固定目标Z坐标 /// /// /// private void updateTargetZ(IVector2 targetPos, float targetZ) { float fdis = MathVector.getDistance(mStartPos, targetPos); float odis = MathVector.getDistance(mStartPos, mLocalPos); float pct = CMath.getInRange((fdis == 0 ? 0 : (odis / fdis)), 0, 1);//生命周期百分比// float dz = CMath.getMiddleValue(pct, mStartHeight, targetZ);//和目标高度差// float ph = 0;//抛物线相对高度// if (Info.ParabolaHeight != 0) { //计算抛物线// ph = (float)Math.Sin(pct * CMath.PI_F) * Info.ParabolaHeight; } this.mLocal_Z = dz + ph; } private void updateFloatingZ(float targetZ) { float pct = (float)mPassTimeMS / (float)Info.LifeTimeMS;//生命周期百分比// float dz = CMath.getMiddleValue(pct, mStartHeight, targetZ);//和目标高度差// float ph = 0;//抛物线相对高度// if (Info.ParabolaHeight != 0) { //计算抛物线// ph = (float)Math.Sin(pct * CMath.PI_F) * Info.ParabolaHeight; } this.mLocal_Z = dz + ph; } private void updateBinding(float x, float y, float direction, int intervalMS) { if (Info.IsBindingDirection) { mDirection = direction; } if (Info.IsBindingOrbit) { float dadd = Info.OrbitDistance; float ox = (float)Math.Cos(Direction) * dadd; float oy = (float)Math.Sin(Direction) * dadd; mLocalPos.SetX(x + ox); mLocalPos.SetY(y + oy); } else { mLocalPos.SetX(x); mLocalPos.SetY(y); } } #endregion //--------------------------------------------------------------------------------------------------- #region _UpdateShape_ private void updateAOE(int intervalMS) { switch (Info.BodyShape) { case SpellTemplate.Shape.LineToTarget: case SpellTemplate.Shape.LineToStart: case SpellTemplate.Shape.Strip: case SpellTemplate.Shape.StripRay: case SpellTemplate.Shape.StripRayTouchEnd: case SpellTemplate.Shape.RectStrip: case SpellTemplate.Shape.RectStripRay: updateAoeMotion(intervalMS, Info.Distance, ref mDistanceLimit); mDisplayDistance = mDistanceLimit; break; default: updateAoeMotion(intervalMS, Info.BodySize, ref mSizeLimit); mDisplaySize = mSizeLimit; break; } } private void updateAoeMotion(int intervalMS, float base_value, ref float value) { switch (Info.AOEMType) { case SpellTemplate.AoeMotionType.Sine: value = (float)Math.Sin(CMath.PI_F * mPassTimeMS / (float)Info.LifeTimeMS) * base_value; break; case SpellTemplate.AoeMotionType.Linear: default: value += MoveHelper.GetDistance(intervalMS, mSpeed); break; } } private void updateRayTouchEnd() { if (Launcher != null) { float d_width = Info.RectWide / 2f; Vector2 p1 = new Vector2(X, Y); MathVector.movePolar(p1, this.Direction, this.mDistanceLimit); using (var list = ListObjectPool.AllocAutoRelease()) { Parent.ForEachNearObjectsRect(this.X, this.Y, p1.X, p1.Y, (ZoneObject u, ref bool cancel) => { if ((u is ZoneUnit)) { ZoneUnit zu = u as ZoneUnit; if (Parent.IsAttackable(Launcher, zu, Info.ExpectTarget)) { if ((CMath.intersectLineRound(this.X, this.Y, p1.X, p1.Y, zu.X, zu.Y, d_width + zu.BodyHitSize))) { list.Add(u as ZoneUnit); } } } }); if (list.Count > 0) { list.Sort(new ObjectBodySorterNearest(this.X, this.Y, 0)); this.mDisplayDistance = CMath.getDistance(this.X, this.Y, list[0].X, list[0].Y); this.mDisplayDistance = Math.Min(mDisplayDistance, this.mDistanceLimit); } else { this.mDisplayDistance = this.mDistanceLimit; } } } } private void updateLineToTarget() { if (Info.BodyShape == SpellTemplate.Shape.LineToTarget) { if (Target != null) { this.mDisplayDistance = CMath.getDistance(this.X, this.Y, Target.X, Target.Y); this.mDisplayDistance = Math.Min(mDisplayDistance, this.mDistanceLimit); PreFaceTo(Target.X, Target.Y); } else { this.mDisplayDistance = 0; } } else if (Info.BodyShape == SpellTemplate.Shape.LineToStart) { this.mDisplayDistance = CMath.getDistance(this.X, this.Y, mStartPos.X, mStartPos.Y); this.mDisplayDistance = Math.Min(mDisplayDistance, this.mDistanceLimit); PreFaceTo(mStartPos.X, mStartPos.Y); } else { this.mDisplayDistance = 0; } } #endregion //--------------------------------------------------------------------------------------------------- #region _UpdateKeyFrames_ /// /// 更新范围检测以及关键帧 /// private void updateKeyFrames() { switch (Info.MType) { case SpellTemplate.MotionType.Missile: case SpellTemplate.MotionType.SeekerMissile: case SpellTemplate.MotionType.Cannon: case SpellTemplate.MotionType.Chain: case SpellTemplate.MotionType.CurveMissile: break; default: if (Info.BodyShape == SpellTemplate.Shape.LineToTarget || Info.BodyShape == SpellTemplate.Shape.LineToStart) { } else { updateKeyFramesRanged(); } break; } } private void updateKeyFramesRanged() { using (var kfs = ListObjectPool.AllocAutoRelease()) { // int kfs_count = mKeyFrames.PopKeyFrames(PassTimeMS, kfs); bool is_interval_test = mHitIntervalTicker.Update(Parent.CurrentIntervalMS); //if (kfs_count > 0) //{ // for (int i = 0; i < kfs.Count; i++) // { // if (kfs[i].Effect != null) // { // Parent.PreQueueEvent(new UnitEffectEvent(ObjectID, kfs[i].Effect)); // } // } //} if (Info.HitOnExplosion) { } else if (Info.HitIntervalMS == 0) { } else if (is_interval_test) { if (Info.HitIntervalKeyFrame != null && Info.HitIntervalKeyFrame.Effect != null) { Parent.PreQueueEvent(new UnitEffectEvent(ObjectID, Info.HitIntervalKeyFrame.Effect)); } } } } #endregion //--------------------------------------------------------------------------------------------------- private void adjustPos(float min_distance) { float fdistance = MathVector.getDistance(mLocalPos, mPos); if (fdistance < min_distance) { MathVector.moveTo(mLocalPos, mPos.X, mPos.Y, min_distance); } else { MathVector.moveTo(mLocalPos, mPos.X, mPos.Y, fdistance / 2f); } } private void PreFaceTo(float angle) { this.mDirection = angle; } private void PreFaceTo(float x, float y) { if (this.X == x && this.Y == y) { return; } this.mDirection = (float)(Math.Atan2(y - this.Y, x - this.X)); } private void PreTurnTo(float add) { this.mDirection += add; } private void PreFlyTo(float x, float y, float speedSEC, int intervalMS) { float distance = MoveHelper.GetDistance(intervalMS, speedSEC); MathVector.moveTo(mLocalPos, x, y, distance); } private void PreFlyTo(float direction, float speedSEC, int intervalMS) { float distance = MoveHelper.GetDistance(intervalMS, speedSEC); float dx = (float)(Math.Cos(direction) * distance); float dy = (float)(Math.Sin(direction) * distance); mLocalPos.AddX(dx); mLocalPos.AddY(dy); } public void FlyToTargetTunning(float x, float y, float speedSEC, float tunningSpeedSEC, int intervalMS) { MoveHelper.MoveToTargetTunning(ref mLocalPos, ref mDirection, x, y, speedSEC, tunningSpeedSEC, intervalMS); } } }