Хоть этот код не совершенен, вертолет неплохо летает, наклоняется и даже стреляет.
Важные ньюансы:
Для вертолета нужно создать физическую модель в которой будут четыре тела с названиями: helicopter (основное тело в которое пойдут меши корпуса вертолета), stabilizator (невидимое тело, которое должно располагаться над вертолетом, под ним будет висеть вертолет на специальном сочленении, у самого тела следует выключить параметр "Контактировать"), mainPropeller (тело с мешем основного винта), backPropeller (тело с мешем заднего винта). Также нужно три сочленения: два HingeJoint с именами suspensionJoint, соединяющий тело helicopter с телом mainPropeller и rotationJoint, соединяющий тело backPropeller с телом helicopter. Третье сочленение - UniversalJoint с именем stabilizeJoint соединяет тело stabilizator с телом helicopter. У этого сочленения должны быть включены ограничители на обеих осях. Направление одной оси - X, другой - Y. А также значения ограничителей должны стоять на 0. Еще нужны два мотора GearedMotor. Один с именем suspensionMotor должен крепиться на сочленении suspensionJoint, второй мотор с именем rotationMotor должен крепиться на сочленении rotationJoint.
Образец вертолета прикрепляю
А теперь по коду. Это код класса Helicopter. Создать новый файл Helicopter.cs нужно в папке ProjectEntities
using System; using System.Collections.Generic; using System.Text; using System.ComponentModel; using System.Drawing.Design; using Engine; using Engine.EntitySystem; using Engine.MapSystem; using Engine.MathEx; using Engine.PhysicsSystem; using Engine.Renderer; using Engine.SoundSystem; using Engine.Utils; using ProjectCommon; using System.IO; using Engine.FileSystem; namespace ProjectEntities { public class HelicopterType : UnitType { [FieldSerialize] private float maxSpeedUp = 40f; [FieldSerialize] private float speedRotations = 1f; [FieldSerialize] private float maxSpeedMove = 80f; [FieldSerialize] private float maxForce = 85000f; [FieldSerialize] private float force = 50000f; [FieldSerialize] Range optimalAttackDistanceRange; [FieldSerialize] Degree towerTurnSpeed = 90; [FieldSerialize] string soundOn; [FieldSerialize] string soundOff; [FieldSerialize] string soundGearUp; [FieldSerialize] string soundGearDown; [FieldSerialize] Range gunRotationAngleRange = new Range(-80, 20); [FieldSerialize] List<Gear> gears = new List<Gear>(); /////////////////////////////////////////// public class Gear { [FieldSerialize] int number; [FieldSerialize] Range speedRange; [FieldSerialize] string soundMotor; [FieldSerialize] [DefaultValue(typeof(Range), "1 1.2")] Range soundMotorPitchRange = new Range(1, 1.2f); // [DefaultValue(0)] public int Number { get { return number; } set { number = value; } } [DefaultValue(typeof(Range), "0 0")] public Range SpeedRange { get { return speedRange; } set { speedRange = value; } } [Editor(typeof(EditorSoundUITypeEditor), typeof(UITypeEditor))] public string SoundMotor { get { return soundMotor; } set { soundMotor = value; } } [DefaultValue(typeof(Range), "1 1.2")] public Range SoundMotorPitchRange { get { return soundMotorPitchRange; } set { soundMotorPitchRange = value; } } public override string ToString() { return string.Format("Gear {0}", number); } } /////////////////////////////////////////// public float MaxSpeedUp { get { return maxSpeedUp; } set { maxSpeedUp = value; } } public float SpeedRotations { get { return speedRotations; } set { speedRotations = value; } } public float MaxSpeedMove { get { return maxSpeedMove; } set { maxSpeedMove = value; } } public float MaxForce { get { return maxForce; } set { maxForce = value; } } public float Force { get { return force; } set { force = value; } } [Description("In degrees.")] [DefaultValue(typeof(Range), "-80 20")] public Range GunRotationAngleRange { get { return gunRotationAngleRange; } set { gunRotationAngleRange = value; } } [DefaultValue(typeof(Range), "0 0")] public Range OptimalAttackDistanceRange { get { return optimalAttackDistanceRange; } set { optimalAttackDistanceRange = value; } } [Description("Degrees per second.")] [DefaultValue(typeof(Degree), "90")] public Degree TowerTurnSpeed { get { return towerTurnSpeed; } set { towerTurnSpeed = value; } } [Editor(typeof(EditorSoundUITypeEditor), typeof(UITypeEditor))] public string SoundOn { get { return soundOn; } set { soundOn = value; } } [Editor(typeof(EditorSoundUITypeEditor), typeof(UITypeEditor))] public string SoundOff { get { return soundOff; } set { soundOff = value; } } public List<Gear> Gears { get { return gears; } } [Editor(typeof(EditorSoundUITypeEditor), typeof(UITypeEditor))] public string SoundGearUp { get { return soundGearUp; } set { soundGearUp = value; } } [Editor(typeof(EditorSoundUITypeEditor), typeof(UITypeEditor))] public string SoundGearDown { get { return soundGearDown; } set { soundGearDown = value; } } } public class Helicopter : Unit { private HelicopterType _type = null; public new HelicopterType Type { get { return _type; } } /////////////////////////////////////////// class Track { public float speed; public float server_sentSpeed; } /////////////////////////////////////////// Body helicopterBody; //Это тело самого вертолета Body stabilizatorBody; //Это тело стабилизатора. Небольшой куб, который надо расположить в редакторе физики над //вертолетом, затем соединить его с телом вертолета при помощи сочленения UniversalJoint. Стабилизатор будет выравнивать //тело вертолета, когда оно будет наклоняться. Тело стабилизатора должно быть над центром вертолета, чтобы тело вертолета //висело ровно и не наклонялось само. float Mass, suspensionForce; GearedMotor suspensionMotor, rotationMotor; HingeJoint suspensionJoint, rotationJoint; UniversalJoint stabilizeJoint; bool isWorking = false; bool firstTick = true; float RL = 0; float FB1 = 0; float FB2 = 0; float RwLw1 = 0; float RwLw2 = 0; Body towerBody; Vec3 towerBodyLocalPosition; MapObjectAttachedMapObject mainGunAttachedObject; Gun mainGun; Vec3 mainGunOffsetPosition; SphereDir towerLocalDirection; SphereDir needTowerLocalDirection; SphereDir server_sentTowerLocalDirection; bool motorOn; string currentMotorSoundName; VirtualChannel motorSoundChannel; HelicopterType.Gear currentGear; Track leftTrack = new Track(); Track rightTrack = new Track(); protected override void OnPostCreate(bool loaded) { base.OnPostCreate(loaded); if (EngineApp.Instance.ApplicationType == EngineApp.ApplicationTypes.Simulation) GetPhysics(); { if (EngineApp.Instance.ApplicationType != EngineApp.ApplicationTypes.ResourceEditor) { if (PhysicsModel == null) { Log.Error("Не добавлена физика вертолету. Класс Helicopter, экземпляр: " + Name); return; } helicopterBody = PhysicsModel.GetBody("helicopter"); if (helicopterBody == null) { Log.Error("Тело 'helicopter' не найдено. Либо оно отсутствует, либо задано неправильное имя. Класс Helicopter, экземпляр: " + Name); return; } towerBody = PhysicsModel.GetBody("tower"); stabilizatorBody = PhysicsModel.GetBody("stabilizator"); if (stabilizatorBody == null) { Log.Error("Тело 'stabilizator' не найдено. Либо оно отсутствует, либо задано неправильное имя. Класс Helicopter, экземпляр: " + Name); return; } stabilizeJoint = PhysicsModel.GetJoint("stabilizeJoint") as UniversalJoint; if (stabilizeJoint == null) { Log.Error("Сочленение 'stabilizeJoint' не найдено. Либо оно отсутствует, либо задано неправильное имя, либо сочленение не является UniversalJoint. Класс Helicopter, экземпляр: " + Name); return; } } //mainGun foreach (MapObjectAttachedObject attachedObject in AttachedObjects) { MapObjectAttachedMapObject attachedMapObject = attachedObject as MapObjectAttachedMapObject; if (attachedMapObject == null) continue; mainGun = attachedMapObject.MapObject as Gun; if (mainGun != null) { mainGunAttachedObject = attachedMapObject; mainGunOffsetPosition = attachedMapObject.PositionOffset; break; } } //towerBodyLocalPosition if (towerBody != null) towerBodyLocalPosition = PhysicsModel.ModelDeclaration.GetBody(towerBody.Name).Position; //initialize currentGear currentGear = Type.Gears.Find(delegate(HelicopterType.Gear gear) { return gear.Number == 0; }); //disable contacts between chassisBody and towerBody if (helicopterBody != null && towerBody != null) { foreach (Shape shape1 in helicopterBody.Shapes) { foreach (Shape shape2 in towerBody.Shapes) { PhysicsWorld.Instance.SetShapePairFlags(shape1, shape2, ShapePairFlags.DisableContacts); } } } } } protected override void OnDestroy() { if (motorSoundChannel != null) { motorSoundChannel.Stop(); motorSoundChannel = null; } base.OnDestroy(); } protected override void OnRender(Camera camera) { //not very true update in the OnRender. //it is here because need update after all Ticks and before update attached objects. UpdateTowerTransform(); base.OnRender(camera); } void TickTowerTurn() { //update direction if (towerLocalDirection != needTowerLocalDirection) { Radian turnSpeed = Type.TowerTurnSpeed; SphereDir needDirection = needTowerLocalDirection; SphereDir direction = towerLocalDirection; //update horizontal direction float diffHorizontalAngle = needDirection.Horizontal - direction.Horizontal; while (diffHorizontalAngle < -MathFunctions.PI) diffHorizontalAngle += MathFunctions.PI * 2; while (diffHorizontalAngle > MathFunctions.PI) diffHorizontalAngle -= MathFunctions.PI * 2; if (diffHorizontalAngle > 0) { if (direction.Horizontal > needDirection.Horizontal) direction.Horizontal -= MathFunctions.PI * 2; direction.Horizontal += turnSpeed * TickDelta; if (direction.Horizontal > needDirection.Horizontal) direction.Horizontal = needDirection.Horizontal; } else { if (direction.Horizontal < needDirection.Horizontal) direction.Horizontal += MathFunctions.PI * 2; direction.Horizontal -= turnSpeed * TickDelta; if (direction.Horizontal < needDirection.Horizontal) direction.Horizontal = needDirection.Horizontal; } //update vertical direction if (direction.Vertical < needDirection.Vertical) { direction.Vertical += turnSpeed * TickDelta; if (direction.Vertical > needDirection.Vertical) direction.Vertical = needDirection.Vertical; } else { direction.Vertical -= turnSpeed * TickDelta; if (direction.Vertical < needDirection.Vertical) direction.Vertical = needDirection.Vertical; } if (direction.Equals(needTowerLocalDirection, .001f)) towerLocalDirection = direction; towerLocalDirection = direction; } } private void GetPhysics() { //Будьте внимательны при вводе имен тел, сочленений и моторов в редакторе ресурсов при создании физики. //Обязательно соблюдайте регистр. По умолчанию имена принято писать с маленькой буквы helicopterBody = PhysicsModel.GetBody("helicopter"); if (helicopterBody == null) Log.Error("{0} - Тело 'helicopter' не найдено. Либо оно отсутствует, либо задано неправильное имя. Класс Helicopter, экземпляр: " + Name, this); stabilizatorBody = PhysicsModel.GetBody("stabilizator"); if (stabilizatorBody == null) Log.Error("{0} - Тело 'stabilizator' не найдено. Либо оно отсутствует, либо задано неправильное имя. Класс Helicopter, экземпляр: " + Name, this); rotationJoint = PhysicsModel.GetJoint("rotationJoint") as HingeJoint; rotationMotor = PhysicsModel.GetMotor("rotationMotor") as GearedMotor; suspensionJoint = PhysicsModel.GetJoint("suspensionJoint") as HingeJoint; suspensionMotor = PhysicsModel.GetMotor("suspensionMotor") as GearedMotor; if (suspensionMotor == null) Log.Error("{0} - Мотор 'stabilizationMotor' не найден. Либо он отсутствует, либо задано неправильное имя, либо мотор не является GearedMotor. Класс Helicopter, экземпляр: " + Name, this); if (suspensionJoint == null) Log.Error("{0} - Сочлинение 'suspensionJoint' не найдено. Либо оно отсутствует, либо задано неправильное имя, либо сочленение не является HingeJoint. Класс Helicopter, экземпляр: " + Name, this); if (rotationJoint == null) Log.Error("{0} - Сочлинение 'rotationJoint' не найдено. Либо оно отсутствует, либо задано неправильное имя, либо сочленение не является HingeJoint. Класс Helicopter, экземпляр: " + Name, this); if (rotationMotor == null) Log.Error("{0} - Мотор 'rotationMotor' не найден. Либо он отсутствует, либо задано неправильное имя, либо мотор не является GearedMotor. Класс Helicopter, экземпляр: " + Name, this); foreach (Body b in PhysicsModel.Bodies) Mass += b.Mass; suspensionForce = (-PhysicsWorld.Instance.MainScene.Gravity.Z * Mass) + Type.Force; } private void Starting() { if (suspensionJoint.Broken) return; if (Intellect == null) { isWorking = false; suspensionMotor.Throttle = 0; rotationMotor.Throttle = 0; } else if (!isWorking && suspensionMotor.Throttle < 1) { suspensionMotor.Throttle += (TickDelta / 7); rotationMotor.Throttle += (TickDelta / 7); } else isWorking = true; } private void MoveUpDown() { Vec3 ForceVec = new Vec3(0, 0, suspensionForce); if (Intellect.IsControlKeyPressed(GameControlKeys.Jump) && GetSpeed().Z < Type.MaxSpeedUp) stabilizatorBody.AddForce(ForceType.GlobalAtLocalPos, TickDelta, ForceVec, Vec3.Zero); else if (Intellect.IsControlKeyPressed(GameControlKeys.Run) && GetSpeed().Z > -Type.MaxSpeedUp) stabilizatorBody.AddForce(ForceType.GlobalAtLocalPos, TickDelta, -ForceVec, Vec3.Zero); } private void MoveForwardBackward() { if (Intellect.IsControlKeyPressed(GameControlKeys.Forward)) { if (GetSpeed().X < Type.MaxSpeedMove) stabilizatorBody.AddForce(ForceType.LocalAtLocalPos, TickDelta, new Vec3(Type.Force, 0, 0), Vec3.Zero); } else if (Intellect.IsControlKeyPressed(GameControlKeys.Backward)) { if (GetSpeed().X < Type.MaxSpeedMove) stabilizatorBody.AddForce(ForceType.LocalAtLocalPos, TickDelta, new Vec3(-Type.Force, 0, 0), Vec3.Zero); } } private void MoveLeftRight() { if (Intellect.IsControlKeyPressed(GameControlKeys.Rightward)) { if (GetSpeed().X < Type.MaxSpeedMove) stabilizatorBody.AddForce(ForceType.LocalAtLocalPos, TickDelta, new Vec3(0, -Type.Force, 0), Vec3.Zero); } else if (Intellect.IsControlKeyPressed(GameControlKeys.Leftward)) if (GetSpeed().X < Type.MaxSpeedMove) stabilizatorBody.AddForce(ForceType.LocalAtLocalPos, TickDelta, new Vec3(0, Type.Force, 0), Vec3.Zero); } private void Rotate() { //Наклоны тела работают не совсем и не всегда корректно, нуждаются в доработке //Тело наклоняется посредством изменения значения ограничителя в UniversalJoint, которое по умолчанию 0 //и не дает вертолету наклоняться if (rotationJoint.Broken) return; if (RL < 20) RL += Intellect.GetControlKeyStrength(GameControlKeys.Left); //сила, с которой тело вертолета разворачивается if (RL > -20) RL -= Intellect.GetControlKeyStrength(GameControlKeys.Right); if (FB1 < 30) FB1 += Intellect.GetControlKeyStrength(GameControlKeys.Backward) * 2; if (FB1 > 0 && FB1 < 30) stabilizeJoint.Axis2.LimitHigh = FB1; //Наклоняет тело назад до 30 градусов if (FB2 > -30) FB2 -= Intellect.GetControlKeyStrength(GameControlKeys.Forward) * 2; if (FB2 < 0 && FB2 > -30) stabilizeJoint.Axis2.LimitLow = FB2; //Наклоняет тело вперед до 30 градусов if (RwLw1 < 30) RwLw1 += Intellect.GetControlKeyStrength(GameControlKeys.Leftward) * 2; if (RwLw1 > 0 && RwLw1 < 30) stabilizeJoint.Axis1.LimitHigh = RwLw1; //Наклоняет тело влево до 30 градусов if (RwLw2 > -30) RwLw2 -= Intellect.GetControlKeyStrength(GameControlKeys.Rightward) * 2; if (RwLw2 < 0 && RwLw2 > -30) stabilizeJoint.Axis1.LimitLow = RwLw2; //Наклоняет тело вправо до 30 градусов stabilizatorBody.AngularVelocity = new Vec3(0, 0, RL * Type.SpeedRotations); //применение силы к телу для поворота //по оси Z. Обратите внимание, что поворачиваться по оси Z должно не тело вертолета, а тело стабилизатора, которое //плавно тянет за собой тело вертолета } private void Stabilization() { if (rotationJoint.Broken) { if (!suspensionJoint.Broken && suspensionMotor.Throttle != 0) stabilizatorBody.AngularVelocity = new Vec3(0, 0, 3); } else stabilizatorBody.AddForce(ForceType.GlobalAtLocalPos, TickDelta, -PhysicsWorld.Instance.MainScene.Gravity * Mass, Vec3.Zero); //Автоматическое выравнивание вертолета if (RL > 0) RL -= 0.1f; if (RL < 0) RL += 0.1f; if (FB1 > 0) FB1 -= 1f; if (FB2 < 0) FB2 += 1f; if (RwLw1 > 0) RwLw1 -= 1f; if (RwLw2 < 0) RwLw2 += 1f; } //not ideal true private Vec3 GetSpeed() { Vec3 v = Vec3.Zero; Vec3 linearVelocity = stabilizatorBody.LinearVelocity; Vec3 angularVelocity = stabilizatorBody.AngularVelocity; //optimization if (linearVelocity.Equals(Vec3.Zero, .2f) && angularVelocity.Equals(Vec3.Zero, .2f)) return v; Vec3 localLinearVelocity = linearVelocity * stabilizatorBody.Rotation.GetInverse(); v.X = localLinearVelocity.X + Math.Abs(angularVelocity.X) * 2; v.Y = localLinearVelocity.Y + Math.Abs(angularVelocity.Y) * 2; v.Z = localLinearVelocity.Z + Math.Abs(angularVelocity.Z) * 2; return v; } protected override void OnTick() { base.OnTick(); bool lastMotorOn = motorOn; motorOn = Intellect != null && Intellect.IsActive(); if (motorOn != lastMotorOn) { if (motorOn) SoundPlay3D(Type.SoundOn, .7f, true); } if (motorSoundChannel != null && !motorOn) { motorSoundChannel.Pause = true; if (motorOn != lastMotorOn) { if (!motorOn) SoundPlay3D(Type.SoundOff, .7f, true); } } else if (motorSoundChannel != null && Intellect != null && isWorking == true) motorSoundChannel.Pause = false; TickTowerTurn(); firstTick = false; Starting(); if (!isWorking) return; Stabilization(); if (!suspensionJoint.Broken) { MoveUpDown(); MoveForwardBackward(); MoveLeftRight(); Rotate(); } else isWorking = false; TickMotorSound(); TickCurrentGear(); if (Intellect != null) { if (Intellect.IsControlKeyPressed(GameControlKeys.Fire1)) if (GunsTryFire(false)) if (Intellect.IsControlKeyPressed(GameControlKeys.Fire2)) GunsTryFire(true); } { //send tower local direction to clients if (EntitySystemWorld.Instance.IsServer()) Server_TickSendTowerLocalDirection(); //!!!!!should use for disabled renderer if (EntitySystemWorld.Instance.IsDedicatedServer()) UpdateTowerTransform(); } } void Print(string Text) { //Метод для проверки работы кода. //Чтобы его задействовать, впишите в нужный вам метод: Print("какой-то текст" + переменная, например Health); //В игре в вызванной командной строке при помощи тильды "~" будет отображаться ваш текст if (EngineConsole.Instance == null) return; EngineConsole.Instance.Print(Text); } void TickMotorSound() { string needSoundName = null; if (currentGear != null) needSoundName = currentGear.SoundMotor; if (needSoundName != currentMotorSoundName) { //change motor sound currentMotorSoundName = needSoundName; if (!string.IsNullOrEmpty(needSoundName)) { Sound sound = SoundWorld.Instance.SoundCreate( RelativePathUtils.ConvertToFullPath(Path.GetDirectoryName(Type.FilePath), needSoundName), SoundMode.Mode3D | SoundMode.Loop); if (sound != null) { motorSoundChannel = SoundWorld.Instance.SoundPlay( sound, EngineApp.Instance.DefaultSoundChannelGroup, .3f, true); motorSoundChannel.Position = Position; switch (Type.SoundRolloffMode) { case DynamicType.SoundRolloffModes.Logarithmic: motorSoundChannel.SetLogarithmicRolloff(Type.SoundMinDistance, Type.SoundMaxDistance, Type.SoundRolloffLogarithmicFactor); break; case DynamicType.SoundRolloffModes.Linear: motorSoundChannel.SetLinearRolloff(Type.SoundMinDistance, Type.SoundMaxDistance); break; } motorSoundChannel.Pause = false; } } } //update motor channel position and pitch if (motorSoundChannel != null) { Range speedRangeAbs = currentGear.SpeedRange; if (speedRangeAbs.Minimum < 0 && speedRangeAbs.Maximum < 0) speedRangeAbs = new Range(-speedRangeAbs.Maximum, -speedRangeAbs.Minimum); Range pitchRange = currentGear.SoundMotorPitchRange; float speedCoef = 0; MathFunctions.Clamp(ref speedCoef, 0, 1); //update channel motorSoundChannel.Pitch = pitchRange.Minimum + speedCoef * pitchRange.Size(); motorSoundChannel.Position = Position; } } protected override void Client_OnTick() { base.Client_OnTick(); TickCurrentGear(); TickMotorSound(); firstTick = false; } void TickCurrentGear() { //currently gears used only for sounds if (currentGear == null) return; if (motorOn) { float speed = Math.Max(leftTrack.speed, rightTrack.speed); HelicopterType.Gear newGear = null; if (speed < currentGear.SpeedRange.Minimum || speed > currentGear.SpeedRange.Maximum) { //find new gear newGear = Type.Gears.Find(delegate(HelicopterType.Gear gear) { return speed >= gear.SpeedRange.Minimum && speed <= gear.SpeedRange.Maximum; }); } if (newGear != null && currentGear != newGear) { //change gear HelicopterType.Gear oldGear = currentGear; OnGearChange(oldGear, newGear); currentGear = newGear; } } else { if (currentGear.Number != 0) { currentGear = Type.Gears.Find(delegate(HelicopterType.Gear gear) { return gear.Number == 0; }); } } } void OnGearChange(HelicopterType.Gear oldGear, HelicopterType.Gear newGear) { if (!firstTick && Health != 0) { bool up = Math.Abs(newGear.Number) > Math.Abs(oldGear.Number); string soundName = up ? Type.SoundGearUp : Type.SoundGearDown; SoundPlay3D(soundName, .7f, true); } } [Browsable(false)] public Gun MainGun { get { return mainGun; } } void UpdateTowerTransform() { if (towerBody == null || helicopterBody == null || mainGunAttachedObject == null) return; Radian horizontalAngle = towerLocalDirection.Horizontal; Radian verticalAngle = towerLocalDirection.Vertical; Range gunRotationRange = Type.GunRotationAngleRange * MathFunctions.PI / 180.0f; if (verticalAngle < gunRotationRange.Minimum) verticalAngle = gunRotationRange.Minimum; if (verticalAngle > gunRotationRange.Maximum) verticalAngle = gunRotationRange.Maximum; //update tower body towerBody.Position = GetInterpolatedPosition() + GetInterpolatedRotation() * towerBodyLocalPosition; towerBody.Rotation = GetInterpolatedRotation() * new Angles(0, 0, -horizontalAngle.InDegrees()).ToQuat(); towerBody.Sleeping = true; //update gun vertical rotation Quat verticalRotation = new Angles(0, verticalAngle.InDegrees(), 0).ToQuat(); mainGunAttachedObject.RotationOffset = verticalRotation; } bool GunsTryFire(bool alternative) { bool fire = false; foreach (MapObjectAttachedObject attachedObject in AttachedObjects) { MapObjectAttachedMapObject attachedMapObject = attachedObject as MapObjectAttachedMapObject; if (attachedMapObject == null) continue; Gun gun = attachedMapObject.MapObject as Gun; if (gun != null) { if (gun.TryFire(alternative)) fire = true; } } return fire; } public void SetMomentaryTurnToPosition(Vec3 pos) { if (towerBody == null) return; Vec3 direction = pos - towerBody.Position; towerLocalDirection = SphereDir.FromVector(Rotation.GetInverse() * direction); needTowerLocalDirection = towerLocalDirection; } public void SetNeedTurnToPosition(Vec3 pos) { if (towerBody == null) return; if (Type.TowerTurnSpeed != 0) { Vec3 direction = pos - towerBody.Position; needTowerLocalDirection = SphereDir.FromVector(Rotation.GetInverse() * direction); } else SetMomentaryTurnToPosition(pos); } protected override void Server_OnClientConnectedAfterPostCreate( RemoteEntityWorld remoteEntityWorld) { base.Server_OnClientConnectedAfterPostCreate(remoteEntityWorld); RemoteEntityWorld[] worlds = new RemoteEntityWorld[] { remoteEntityWorld }; Server_SendTowerLocalDirectionToClients(worlds); } void Server_TickSendTowerLocalDirection() { float epsilon = new Degree(.5f).InRadians(); if (!towerLocalDirection.Equals(server_sentTowerLocalDirection, epsilon)) { Server_SendTowerLocalDirectionToClients(EntitySystemWorld.Instance.RemoteEntityWorlds); server_sentTowerLocalDirection = towerLocalDirection; } } void Server_SendTowerLocalDirectionToClients(IList<RemoteEntityWorld> remoteEntityWorlds) { SendDataWriter writer = BeginNetworkMessage(remoteEntityWorlds, typeof(Helicopter), (ushort)NetworkMessages.TowerLocalDirectionToClient); writer.Write(towerLocalDirection); EndNetworkMessage(); } [NetworkReceive(NetworkDirections.ToClient, (ushort)NetworkMessages.TowerLocalDirectionToClient)] void Client_ReceiveTowerLocalDirection(RemoteEntityWorld sender, ReceiveDataReader reader) { SphereDir value = reader.ReadSphereDir(); if (!reader.Complete()) return; towerLocalDirection = value; } /////////////////////////////////////////// enum NetworkMessages { TowerLocalDirectionToClient, TracksSpeedToClient, } /////////////////////////////////////////// } }
Теперь нужно подправить несколько других классов
Класс GameControlKeys. Добавить две новые клавиши. Движение в правый бок и в левый бок. Назначение клавиши Use поменять
[DefaultKeyboardMouseValue( EKeys.F )] Use, [DefaultKeyboardMouseValue(EKeys.E)] Rightward, [DefaultKeyboardMouseValue(EKeys.Q)] Leftward,
Класс PlayerIntellect. Найдите метод void ServerOrSingle_RestoreMainControlledUnit() и в нем Tank specific
if( ControlledObject != null ) { //Tank specific if( ControlledObject is Tank || ControlledObject is Car )
Добавьте в условие этого метода ControlledObject is Helicopter
if( ControlledObject != null ) { //Tank specific if( ControlledObject is Tank || ControlledObject is Car || ControlledObject is Helicopter )
В классе ActionGameWindow, найдите метод void UpdateHUD(). В нем под Tank specific добавьте строки с Helicopter
//Tank specific Tank tank = playerUnit as Tank; if( tank != null ) weapon = tank.MainGun; //Helicopter specific Helicopter heli = playerUnit as Helicopter; if (heli != null) weapon = heli.MainGun;
То же самое напишите в этом же классе, в методе void DrawTarget( GuiRenderer renderer )
А ниже в этом методе, под строкой Tank specific DrawTankGunTarget(renderer) добавьте аналогичные строки про Helicopter
//Tank specific DrawTankGunTarget( renderer ); //Helicopter specific DrawHeliGunTarget(renderer);
Вроде ничего не забыл. Спрашивайте, если что-то не понятно