├── src
├── environments
│ ├── dogfightEnv
│ │ ├── models
│ │ │ ├── Flare.py
│ │ │ └── __init__.py
│ │ ├── evade.bat
│ │ ├── dogfight.bat
│ │ ├── initialization
│ │ │ ├── __init__.py
│ │ │ ├── ActionSpace.py
│ │ │ ├── TakeOff.py
│ │ │ └── ObservationSpace.py
│ │ ├── __init__.py
│ │ └── dogfightEnv.py
│ ├── dfEnv
│ │ ├── __init__.py
│ │ └── dfEnv.py
│ └── jsbsimEnv
│ │ ├── __init__.py
│ │ ├── jsbsimFdm.py
│ │ └── jsbsimEnv.py
└── models
│ ├── sac_jsbsim.py
│ ├── sac_df.py
│ └── evade_sb3.py
├── images
├── host.png
├── logo.png
├── heading.png
├── fgattribute.png
├── roll_attitude.png
├── pitch_attitude.png
└── coordinate_system.png
├── requirements.txt
├── example
├── launch.py
├── ego_oppo_plane.py
└── maneuvering.py
├── .gitignore
├── README.md
├── doc
├── properties_of_harfang_df.md
├── QuickStart_zh.md
└── QuickStart_en.md
├── df2.py
├── tutorial.ipynb
└── bak
├── Missions.bak
├── Missions_evade.bak
└── Missions_dogfight.bak
/src/environments/dogfightEnv/models/Flare.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/environments/dogfightEnv/models/__init__.py:
--------------------------------------------------------------------------------
1 | from .Flare import *
--------------------------------------------------------------------------------
/images/host.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrwangyou/DBRL/HEAD/images/host.png
--------------------------------------------------------------------------------
/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrwangyou/DBRL/HEAD/images/logo.png
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | numpy==1.23.4
2 | gym==0.21.0
3 | stable-baselines3==1.6.2
4 |
--------------------------------------------------------------------------------
/images/heading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrwangyou/DBRL/HEAD/images/heading.png
--------------------------------------------------------------------------------
/images/fgattribute.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrwangyou/DBRL/HEAD/images/fgattribute.png
--------------------------------------------------------------------------------
/images/roll_attitude.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrwangyou/DBRL/HEAD/images/roll_attitude.png
--------------------------------------------------------------------------------
/images/pitch_attitude.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrwangyou/DBRL/HEAD/images/pitch_attitude.png
--------------------------------------------------------------------------------
/images/coordinate_system.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrwangyou/DBRL/HEAD/images/coordinate_system.png
--------------------------------------------------------------------------------
/src/environments/dogfightEnv/evade.bat:
--------------------------------------------------------------------------------
1 | copy ..\..\..\bak\Missions_evade.bak .\dogfight_sandbox_hg2\source\Missions.py
2 |
3 | echo Done.
4 |
--------------------------------------------------------------------------------
/src/environments/dogfightEnv/dogfight.bat:
--------------------------------------------------------------------------------
1 | copy ..\..\..\bak\Missions_dogfight.bak .\dogfight_sandbox_hg2\source\Missions.py
2 |
3 | echo Done.
4 |
--------------------------------------------------------------------------------
/src/environments/dogfightEnv/initialization/__init__.py:
--------------------------------------------------------------------------------
1 | from .ActionSpace import *
2 | from .ObservationSpace import *
3 | from .TakeOff import *
4 |
--------------------------------------------------------------------------------
/src/environments/dfEnv/__init__.py:
--------------------------------------------------------------------------------
1 | try:
2 | from src.environments.dfEnv.dfEnv import DfEnv
3 | except:
4 | from gym.envs.dfEnv.dfEnv import DfEnv
5 |
--------------------------------------------------------------------------------
/src/environments/dogfightEnv/__init__.py:
--------------------------------------------------------------------------------
1 | try:
2 | from .dogfightEnv import DogfightEnv
3 | except:
4 | from gym.envs.dogfightEnv.dogfightEnv import DogfightEnv
5 |
--------------------------------------------------------------------------------
/src/environments/jsbsimEnv/__init__.py:
--------------------------------------------------------------------------------
1 | try:
2 | from src.environments.jsbsimEnv.jsbsimEnv import JsbsimEnv
3 | from src.environments.jsbsimEnv.jsbsimFdm import JsbsimFdm
4 | except:
5 | from gym.envs.jsbsimEnv.jsbsimEnv import JsbsimEnv
6 | from gym.envs.jsbsimEnv.jsbsimFdm import JsbsimFdm
7 |
8 |
--------------------------------------------------------------------------------
/src/environments/dogfightEnv/initialization/ActionSpace.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from gym.spaces import Box
3 |
4 | def action_space(
5 | pitch_enable=True,
6 | roll_enable=True,
7 | yaw_enable=True,
8 | flaps_enable=True,
9 | throttle_enable=False,
10 | flare_enable=False,
11 | ):
12 | action_infimum = np.array([])
13 | action_supermum = np.array([])
14 | action_type = []
15 |
16 | if pitch_enable: # 俯仰角
17 | action_infimum = np.append(action_infimum, -1)
18 | action_supermum = np.append(action_supermum, 1)
19 | action_type = action_type + ['_df.set_plane_pitch']
20 | if roll_enable: # 滚转角
21 | action_infimum = np.append(action_infimum, -1)
22 | action_supermum = np.append(action_supermum, 1)
23 | action_type = action_type + ['_df.set_plane_roll']
24 | if yaw_enable: # 偏航角
25 | action_infimum = np.append(action_infimum, -1)
26 | action_supermum = np.append(action_supermum, 1)
27 | action_type = action_type + ['_df.set_plane_yaw']
28 | if flaps_enable: # 襟翼
29 | action_infimum = np.append(action_infimum, 0)
30 | action_supermum = np.append(action_supermum, 1)
31 | action_type = action_type + ['_df.set_plane_flaps']
32 | if throttle_enable: # 油门
33 | action_infimum = np.append(action_infimum, 0)
34 | action_supermum = np.append(action_supermum, 1)
35 | action_type = action_type + ['_df.set_plane_thrust']
36 | if flare_enable: # 干扰弹
37 | action_infimum = np.append(action_infimum, 0)
38 | action_supermum = np.append(action_supermum, 1)
39 | action_type = action_type + ['Flare']
40 |
41 | action = Box(
42 | low=action_infimum,
43 | high=action_supermum,
44 | dtype=float
45 | )
46 |
47 | return action, action_type
48 |
--------------------------------------------------------------------------------
/src/environments/dogfightEnv/initialization/TakeOff.py:
--------------------------------------------------------------------------------
1 |
2 | def start_on_carrier(
3 | df,
4 | planeID,
5 | enemyID,
6 | missile_slot,
7 | missileID,
8 | ):
9 | t = 0
10 | while t < 1:
11 | plane_state = df.get_plane_state(enemyID)
12 | df.update_scene()
13 | t = plane_state["thrust_level"]
14 |
15 | df.activate_post_combustion(enemyID)
16 | df.activate_post_combustion(planeID)
17 |
18 | df.set_plane_pitch(enemyID, -0.5)
19 | df.set_plane_pitch(planeID, -0.5)
20 |
21 | p = 0
22 | while p < 15:
23 | plane_state = df.get_plane_state(enemyID)
24 | df.update_scene()
25 | p = plane_state["pitch_attitude"]
26 |
27 | df.stabilize_plane(enemyID)
28 | df.stabilize_plane(planeID)
29 |
30 | df.retract_gear(enemyID)
31 | df.retract_gear(planeID)
32 |
33 | s = 0
34 | while s < 1000:
35 | plane_state = df.get_plane_state(enemyID)
36 | df.update_scene()
37 | s = plane_state["altitude"]
38 |
39 | df.set_plane_yaw(planeID, 1)
40 |
41 | missiles = df.get_machine_missiles_list(enemyID)
42 | missileID = missiles[missile_slot]
43 |
44 | df.fire_missile(enemyID, missile_slot)
45 |
46 | df.set_missile_target(missileID, planeID)
47 | df.set_missile_life_delay(missileID, 30)
48 |
49 |
50 | def start_in_sky(
51 | df,
52 | planeID,
53 | enemyID,
54 | missile_slot,
55 | missileID,
56 | ):
57 | t = 0
58 | while t < 1:
59 | plane_state = df.get_plane_state(planeID)
60 | df.update_scene()
61 | t = plane_state["thrust_level"]
62 | df.activate_post_combustion(planeID)
63 |
64 | df.fire_missile(enemyID, missile_slot)
65 |
66 | df.set_missile_target(missileID, planeID)
67 | df.set_missile_life_delay(missileID, 10)
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/example/launch.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import time
3 | import argparse
4 |
5 | sys.path.append('.')
6 |
7 | from src.environments.dogfightEnv.dogfight_sandbox_hg2.network_client_example import dogfight_client as df
8 |
9 | def parse_args():
10 | parser = argparse.ArgumentParser(description='TBD')
11 | parser.add_argument('--host', default='10.184.0.0', help='specifies Harfang host id')
12 | parser.add_argument('--port', default='50888', help='specifies Harfang port id')
13 | args = parser.parse_args()
14 | return args
15 |
16 | args = parse_args()
17 |
18 | df.connect(args.host, int(args.port))
19 |
20 | planes = df.get_planes_list()
21 |
22 | for i in planes:
23 | df.reset_machine(i)
24 |
25 | df.set_plane_thrust(planes[1], 1)
26 |
27 | df.set_client_update_mode(True)
28 |
29 | # Thrust
30 | t = 0
31 | while t < 1:
32 | time.sleep(1/60)
33 | plane_state = df.get_plane_state(planes[1])
34 | df.update_scene()
35 | t = plane_state["thrust_level"]
36 |
37 | df.activate_post_combustion(planes[1])
38 |
39 | df.set_plane_pitch(planes[1], -0.5)
40 |
41 | # Pitch
42 | p = 0
43 | while p < 15:
44 | time.sleep(1/60)
45 | plane_state = df.get_plane_state(planes[1])
46 | df.update_scene()
47 | p = plane_state["pitch_attitude"]
48 |
49 | df.stabilize_plane(planes[1])
50 |
51 | df.retract_gear(planes[1])
52 |
53 | s = 0
54 | while s < 100:
55 | time.sleep(1/60)
56 | plane_state = df.get_plane_state(planes[1])
57 | df.update_scene()
58 | s = plane_state["altitude"]
59 |
60 | df.set_plane_pitch(planes[1], 0.1)
61 |
62 | p = 15
63 | while p > 0:
64 | time.sleep(1/60)
65 | plane_state = df.get_plane_state(planes[1])
66 | df.update_scene()
67 | p = plane_state["pitch_attitude"]
68 |
69 | df.stabilize_plane(planes[1])
70 |
71 | while True:
72 | time.sleep(1/60)
73 | df.update_scene()
74 | # Print anything you want to see here while controlling the plane with your keyboard
75 | # print('')
76 |
--------------------------------------------------------------------------------
/src/environments/dogfightEnv/initialization/ObservationSpace.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from gym.spaces import Box
3 |
4 | def observation_space(
5 | ego_plane_position=True,
6 | ego_plane_attitude=True,
7 | oppo_plane_position=False,
8 | oppo_plane_attitude=False,
9 | missile_position=True,
10 | missile_attitude=True,
11 | missile_relative_azimuth=False,
12 | ):
13 | observation_infimum = np.array([])
14 | observation_supermum = np.array([])
15 | observation_type = []
16 |
17 | if ego_plane_position: # 我方飞机位置
18 | observation_infimum = np.append(observation_infimum, [-300, -300, -1])
19 | observation_supermum = np.append(observation_supermum, [300, 300, 200])
20 | if ego_plane_attitude: # 我方飞机姿态
21 | observation_infimum = np.append(observation_infimum, [0, -360, -360])
22 | observation_supermum = np.append(observation_supermum, [360, 360, 360])
23 | if oppo_plane_position: # 敌方飞机位置
24 | observation_infimum = np.append(observation_infimum, [-300, -300, -1])
25 | observation_supermum = np.append(observation_supermum, [300, 300, 200])
26 | if oppo_plane_attitude: # 敌方飞机姿态
27 | observation_infimum = np.append(observation_infimum, [0, -360, -360])
28 | observation_supermum = np.append(observation_supermum, [360, 360, 360])
29 | if missile_position: # 导弹位置
30 | observation_infimum = np.append(observation_infimum, [-300, -300, -1])
31 | observation_supermum = np.append(observation_supermum, [300, 300, 200])
32 | if missile_attitude: # 导弹姿态
33 | observation_infimum = np.append(observation_infimum, [-315, -315, -315])
34 | observation_supermum = np.append(observation_supermum, [315, 315, 315])
35 | if missile_relative_azimuth: # 导弹相对方位角
36 | observation_infimum = np.append(observation_infimum, [-1, -1, -1])
37 | observation_supermum = np.append(observation_supermum, [1, 1, 1])
38 |
39 | observation = Box(
40 | low=observation_infimum,
41 | high=observation_supermum,
42 | dtype=np.float64
43 | )
44 |
45 | return observation
46 |
--------------------------------------------------------------------------------
/src/models/sac_jsbsim.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import os
3 | import time
4 |
5 | import gym
6 | import numpy as np
7 | from stable_baselines3 import SAC
8 | from stable_baselines3.common.noise import (NormalActionNoise,
9 | OrnsteinUhlenbeckActionNoise)
10 |
11 |
12 | def parse_args():
13 | parser = argparse.ArgumentParser(description='TBD')
14 | # parser.add_argument('--modelPath', default='/data/wnn_data/bestModel/', metavar='str', help='specifies the pre-trained model')
15 | parser.add_argument('--playSpeed', default=0, metavar='double', help='specifies to run in real world time')
16 | parser.add_argument('--trainPolicy', default='Level', metavar='string', help='specifies the opposite plane\'s strategy of dogfighting')
17 | parser.add_argument('--train', action='store_true', help='specifies the running mode of DBRL')
18 | parser.add_argument('--test', action='store_true', help='specifies the running mode of DBRL')
19 | parser.add_argument('--timesteps', default=10000000, metavar='int', help='specifies the training timesteps. Only works when --train is specified')
20 | args = parser.parse_args()
21 | return args
22 |
23 | args = parse_args()
24 |
25 | env = gym.make(
26 | "DBRLJsbsim-v0",
27 | policy2=args.trainPolicy
28 | )
29 |
30 | n_actions = env.action_space.shape[-1]
31 | action_noise = NormalActionNoise(mean=np.zeros(n_actions), sigma=0.1 * np.ones(n_actions))
32 |
33 | model = SAC(
34 | "MlpPolicy",
35 | env,
36 | verbose=1,
37 | action_noise=action_noise,
38 | )
39 |
40 | path = r'./log/'
41 | if not os.path.exists(path):
42 | os.mkdir(path)
43 |
44 | if args.train:
45 | # try:
46 | # model.set_parameters("./log/sac_jsbsim")
47 | # except:
48 | # pass
49 | model.learn(total_timesteps=int(args.timesteps), log_interval=1)
50 | model.save("./log/sac_jsbsim")
51 |
52 | if args.test:
53 | model = SAC.load("./log/sac_jsbsim")
54 |
55 | win = 0
56 | episode = 0
57 |
58 | obs = env.reset()
59 | while True:
60 | action, _states = model.predict(obs)
61 | obs, rewards, dones, info = env.step(action)
62 | env.render()
63 | if dones == True:
64 | f = open('./log/sac_jsbsim_record.txt', 'a')
65 | if rewards == 50:
66 | win += 1
67 | episode += 1
68 | f.write("{} / {}\n".format(win, episode))
69 | print("Done! episode: {}\tacc: {}".format(episode, win / episode))
70 | time.sleep(2)
71 | env.reset()
72 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 |
131 | # Customized
132 | bestModel/
133 | src/environments/dogfightEnv/dogfight_sandbox_hg2/
134 | log/
135 | test/
136 | .vscode/
137 | tmp/
138 | catalog/
139 |
--------------------------------------------------------------------------------
/example/ego_oppo_plane.py:
--------------------------------------------------------------------------------
1 | import os
2 | import time
3 |
4 | import gym
5 | import numpy as np
6 | from stable_baselines3 import SAC
7 | from stable_baselines3.common.noise import (NormalActionNoise,
8 | OrnsteinUhlenbeckActionNoise)
9 |
10 |
11 |
12 | env = gym.make(
13 | "DBRLDogfight-v0",
14 | host='192.168.239.1',
15 | port='50888',
16 | rendering=True,
17 | )
18 |
19 | n_actions = env.action_space.shape[-1]
20 | action_noise = NormalActionNoise(mean=np.zeros(n_actions), sigma=0.1 * np.ones(n_actions))
21 |
22 | model = SAC(
23 | "MlpPolicy",
24 | env,
25 | verbose=1,
26 | action_noise=action_noise,
27 | )
28 |
29 | path = r'./log/'
30 | if not os.path.exists(path):
31 | os.mkdir(path)
32 |
33 |
34 | model = SAC.load("./log/sac_df2")
35 |
36 | win = 0
37 | episode = 0
38 |
39 |
40 | env.setProperty('plane', 0) # TFX
41 | env.setProperty('enemy', 3) #
42 | env.setProperty('missile', 0) # Mica
43 | obs = env.reset()
44 | times = 0
45 | sett = 0
46 | while True:
47 | if times == 1 and sett == 0:
48 | env.setProperty('plane', 0) # TFX
49 | env.setProperty('enemy', 3) #
50 | env.setProperty('missile', 1) # Meteor
51 | obs = env.reset()
52 | sett = 1
53 | if times == 2 and sett == 0:
54 | env.setProperty('plane', 1) # F-16
55 | env.setProperty('enemy', 3) #
56 | env.setProperty('missile', 0) # Mica
57 | obs = env.reset()
58 | sett = 1
59 | if times == 3 and sett == 0:
60 | env.setProperty('plane', 1) # F-16
61 | env.setProperty('enemy', 3) #
62 | env.setProperty('missile', 1) # Meteor
63 | obs = env.reset()
64 | sett = 1
65 | if times == 4 and sett == 0:
66 | env.setProperty('plane', 2) # Eurofighter Typhoon
67 | env.setProperty('enemy', 1) #
68 | env.setProperty('missile', 0) # AIM
69 | obs = env.reset()
70 | sett = 1
71 | if times == 5 and sett == 0:
72 | env.setProperty('plane', 3) # Rafale
73 | env.setProperty('enemy', 1) #
74 | env.setProperty('missile', 2) # Karaok
75 | obs = env.reset()
76 | sett = 1
77 | if times == 6:
78 | break
79 |
80 |
81 | action, _states = model.predict(obs)
82 | obs, rewards, dones, info = env.step(action)
83 | env.render()
84 | if dones == True:
85 | if rewards == 50:
86 | win += 1
87 | episode += 1
88 | print("Done! episode: {}\tacc: {}".format(episode, win / episode))
89 | time.sleep(2)
90 | env.reset()
91 | times = times + 1
92 | sett = 0
93 |
--------------------------------------------------------------------------------
/src/models/sac_df.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import os
3 | import time
4 |
5 | import gym
6 | import numpy as np
7 | from stable_baselines3 import SAC
8 | from stable_baselines3.common.noise import (NormalActionNoise,
9 | OrnsteinUhlenbeckActionNoise)
10 |
11 |
12 | def parse_args():
13 | parser = argparse.ArgumentParser(description='TBD')
14 | parser.add_argument('--host', default='10.184.0.0', metavar='str', help='specifies Harfang host id')
15 | parser.add_argument('--port', default='50888', metavar='str', help='specifies Harfang port id')
16 | parser.add_argument('--planeSlot', default=1, metavar='int', help='specifies the ego plane')
17 | parser.add_argument('--enemySlot', default=3, metavar='int', help='specifies the enemy plane')
18 | parser.add_argument('--playSpeed', default=0, metavar='double', help='specifies to run in real world time')
19 | parser.add_argument('--train', action='store_true', help='specifies the running mode of DBRL')
20 | parser.add_argument('--test', action='store_true', help='specifies the running mode of DBRL')
21 | parser.add_argument('--timesteps', default=10000000, metavar='double', help='specifies the training timesteps. Only works when --train is specified')
22 | # parser.add_argument('--modelPath', default=None, metavar='str', help='specifies the pre-trained model. Only works when --train is specified')
23 | parser.add_argument('--record', action='store_true', help='specifies whether to record the evaluating result of DBRL. Only works when --test is specified')
24 | args = parser.parse_args()
25 | return args
26 |
27 | args = parse_args()
28 |
29 | env = gym.make(
30 | "DBRLDf-v0",
31 | host=args.host,
32 | port=args.port,
33 | plane_slot=args.planeSlot,
34 | enemy_slot=args.enemySlot,
35 | rendering=True if args.playSpeed else False
36 | )
37 |
38 | n_actions = env.action_space.shape[-1]
39 | action_noise = NormalActionNoise(mean=np.zeros(n_actions), sigma=0.1 * np.ones(n_actions))
40 |
41 | model = SAC(
42 | "MlpPolicy",
43 | env,
44 | verbose=1,
45 | action_noise=action_noise,
46 | )
47 |
48 | path = r'./log/'
49 | if not os.path.exists(path):
50 | os.mkdir(path)
51 |
52 | if args.train:
53 | try:
54 | model = SAC.load("./log/sac_df", env)
55 | model.load_replay_buffer("./log/sac_df_rb")
56 | except:
57 | pass
58 | model.learn(total_timesteps=10000000, log_interval=1)
59 | model.save("./log/sac_df")
60 | model.save_replay_buffer("./log/sac_df_rb")
61 |
62 | if args.test:
63 | model = SAC.load("./log/sac_df")
64 |
65 | win = 0
66 | episode = 0
67 |
68 | if args.record:
69 | f = open('./log/sac_df_record.txt', 'r')
70 | for line in f:
71 | pass
72 | win = int(line.split()[0])
73 | episode = int(line.split()[2])
74 |
75 | f.close()
76 |
77 | obs = env.reset()
78 | while True:
79 | action, _states = model.predict(obs)
80 | obs, rewards, dones, info = env.step(action)
81 | env.render()
82 | if dones == True:
83 | if rewards == 50:
84 | win += 1
85 | episode += 1
86 | if args.record:
87 | f = open('./log/sac_df_record.txt', 'a')
88 | f.write("{} / {}\n".format(win, episode))
89 | f.close()
90 | print("Done! episode: {}\tacc: {}".format(episode, win / episode))
91 | time.sleep(2)
92 | env.reset()
93 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
DBRL: A Gym Dogfighting Simulation Benchmark for Reinforcement Learning Research
3 |
4 |
5 |

6 |
7 | [Reference Manual](doc/QuickStart_en.md) | [简体中文使用手册](doc/QuickStart_zh.md)
8 |
9 |
10 |
11 | ## Introduction
12 |
13 | DBRL is an air combat simulation benchmark based on JSBSim and Dogfight 2.
14 |
15 | We adopt JSBSim as flight dynamic model to build our dogfight environment, and Dogfight 2 to simulate the situation when the plane is confronted with a missile.
16 |
17 | If you are new to air combat simulation or reinforcement learning algorithm, we advise you to learn [DBRL tutorial](tutorial.ipynb) first.
18 |
19 | ## Gym Parameters
20 |
21 | ```python
22 | # JSBSim
23 | observation_space = Box(
24 | low=np.array([
25 | -360, # Latitude
26 | -360, # Longitude
27 | 0, # Height above sea level
28 | -360, # Yaw
29 | -360, # Pitch
30 | -360, # Roll
31 | ] * 2),
32 | high=np.array([
33 | 360,
34 | 360,
35 | 60000,
36 | 360,
37 | 360,
38 | 360,
39 | ] * 2)
40 | )
41 |
42 | action_space = Box(
43 | low=np.array([
44 | -1, # Aileron
45 | -1, # Elevator
46 | -1, # Rudder
47 | 0, # Throttle
48 | 0, # Flap
49 | 0, # Speed brake
50 | 0, # Spoiler
51 | ]),
52 | high=np.array([
53 | 1,
54 | 1,
55 | 1,
56 | 1,
57 | 1,
58 | 1,
59 | 1,
60 | ]),
61 | )
62 |
63 |
64 | # Dogfight 2
65 | if ego_plane_position:
66 | observation_infimum = np.append(observation_infimum, [-300, -300, -1])
67 | observation_supermum = np.append(observation_supermum, [300, 300, 200])
68 | if ego_plane_attitude:
69 | observation_infimum = np.append(observation_infimum, [0, -360, -360])
70 | observation_supermum = np.append(observation_supermum, [360, 360, 360])
71 | if oppo_plane_position:
72 | observation_infimum = np.append(observation_infimum, [-300, -300, -1])
73 | observation_supermum = np.append(observation_supermum, [300, 300, 200])
74 | if oppo_plane_attitude:
75 | observation_infimum = np.append(observation_infimum, [0, -360, -360])
76 | observation_supermum = np.append(observation_supermum, [360, 360, 360])
77 | if missile_position:
78 | observation_infimum = np.append(observation_infimum, [-300, -300, -1])
79 | observation_supermum = np.append(observation_supermum, [300, 300, 200])
80 | if missile_attitude:
81 | observation_infimum = np.append(observation_infimum, [-315, -315, -315])
82 | observation_supermum = np.append(observation_supermum, [315, 315, 315])
83 | if missile_relative_azimuth:
84 | observation_infimum = np.append(observation_infimum, [-1, -1, -1])
85 | observation_supermum = np.append(observation_supermum, [1, 1, 1])
86 | observation_space = Box(
87 | low=observation_infimum,
88 | high=observation_supermum,
89 | )
90 |
91 | if pitch_enable:
92 | action_infimum = np.append(action_infimum, -1)
93 | action_supermum = np.append(action_supermum, 1)
94 | if roll_enable:
95 | action_infimum = np.append(action_infimum, -1)
96 | action_supermum = np.append(action_supermum, 1)
97 | if yaw_enable:
98 | action_infimum = np.append(action_infimum, -1)
99 | action_supermum = np.append(action_supermum, 1)
100 | if flaps_enable:
101 | action_infimum = np.append(action_infimum, 0)
102 | action_supermum = np.append(action_supermum, 1)
103 | if throttle_enable:
104 | action_infimum = np.append(action_infimum, 0)
105 | action_supermum = np.append(action_supermum, 1)
106 | if flare_enable:
107 | action_infimum = np.append(action_infimum, 0)
108 | action_supermum = np.append(action_supermum, 1)
109 | action_space = Box(
110 | low=action_infimum,
111 | high=action_supermum,
112 | )
113 |
114 | ```
115 |
116 |
117 | ## Welcome PR
118 |
119 | If you find any mistakes while using, or have any suggestions and advices, please point it out in Github Issues. We're also looking forward to your contributions, such as your air combat reinforcement learning models, to this dogfight benchmark.
120 |
121 |
--------------------------------------------------------------------------------
/src/models/evade_sb3.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import os
3 | import time
4 |
5 | import gym
6 | import numpy as np
7 | from stable_baselines3 import DDPG, TD3, SAC
8 | from stable_baselines3.common.noise import (NormalActionNoise,
9 | OrnsteinUhlenbeckActionNoise)
10 |
11 |
12 | def parse_args():
13 | parser = argparse.ArgumentParser(description='TBD')
14 | parser.add_argument('--host', default='10.184.0.0', help='specifies Harfang host id')
15 | parser.add_argument('--port', default='50888', help='specifies Harfang port id')
16 | parser.add_argument('--planeSlot', default=1, help='specifies the ego plane')
17 | parser.add_argument('--enemySlot', default=3, help='specifies the enemy plane')
18 | parser.add_argument('--missileSlot', default=1, help='specifies the missile')
19 | parser.add_argument('--playSpeed', type=int, default=0, help='specifies to run in real world time')
20 | parser.add_argument('--train', action='store_true', help='specifies the running mode of DBRL')
21 | parser.add_argument('--test', action='store_true', help='specifies the running mode of DBRL')
22 | parser.add_argument('--timesteps', type=int, default=10000000, help='specifies the training timesteps. Only works when --train is specified')
23 | parser.add_argument('--model', default='SAC', help='specifies the DRL model used in algorithm training. Only works when --train or --test is specified')
24 | # parser.add_argument('--modelPath', default=None, help='specifies the pre-trained model. Only works when --train is specified')
25 | parser.add_argument('--recordResult', action='store_true', help='specifies whether to record the evaluating result of DBRL. Only works when --test is specified')
26 | parser.add_argument('--recordStatus', type=int, default=0, help='specifies the frequency of aircraft status recording during test flight. Only works when --test is specified')
27 | parser.add_argument('--flareEnable', action='store_true', help='specifies whether to activate the flare countermeasure')
28 | args = parser.parse_args()
29 | return args
30 |
31 | args = parse_args()
32 |
33 | env = gym.make(
34 | "DBRLDogfight-v0",
35 | host=args.host,
36 | port=args.port,
37 | plane_slot=args.planeSlot,
38 | enemy_slot=args.enemySlot,
39 | missile_slot=args.missileSlot,
40 | rendering=True if args.playSpeed else False,
41 | record_status=args.recordStatus,
42 | flare_enable=args.flareEnable,
43 | )
44 |
45 | n_actions = env.action_space.shape[-1]
46 | action_noise = NormalActionNoise(mean=np.zeros(n_actions), sigma=0.1 * np.ones(n_actions))
47 |
48 | method = {
49 | 'DDPG': 'ddpg',
50 | 'TD3': 'td3',
51 | 'SAC': 'sac',
52 | }
53 |
54 | assert args.model in method, 'Model {} not defined'.format(args.model)
55 |
56 | msg = "evade_{}_{}".format(args.model, args.timesteps)
57 |
58 | model = eval(args.model)(
59 | "MlpPolicy",
60 | env,
61 | verbose=1,
62 | action_noise=action_noise,
63 | )
64 |
65 | path = r'./log/'
66 | if not os.path.exists(path):
67 | os.mkdir(path)
68 |
69 | if args.train:
70 | # try:
71 | # model = eval(args.model).load("./log/{}_evade".format(method[args.model]), env)
72 | # model.load_replay_buffer("./log/{}_evade_rb".format(method[args.model]))
73 | # except:
74 | # pass
75 | model.learn(total_timesteps=args.timesteps, log_interval=1)
76 | model.save("./log/{}".format(msg))
77 | model.save_replay_buffer("./log/{}_rb".format(msg))
78 |
79 | if args.test:
80 | model = eval(args.model).load("./log/{}".format(msg))
81 |
82 | if args.recordResult:
83 | if os.path.exists('./log/{}_recordResult.txt'.format(msg)):
84 | f = open('./log/{}_recordResult.txt'.format(msg), 'r')
85 | for line in f:
86 | pass
87 | win = int(line.split()[0])
88 | episode = int(line.split()[2])
89 | f.close()
90 | else:
91 | win = 0
92 | episode = 0
93 |
94 | obs = env.reset()
95 | while True:
96 | action, _states = model.predict(obs)
97 | obs, rewards, dones, info = env.step(action)
98 | env.render()
99 | if dones == True:
100 | if rewards == 50:
101 | win += 1
102 | episode += 1
103 | if args.recordResult:
104 | f = open('./log/{}_recordResult.txt'.format(msg), 'a')
105 | f.write("{} / {}\n".format(win, episode))
106 | f.close()
107 | print("Episode: {}\tacc: {}".format(episode, win / episode))
108 | time.sleep(2)
109 | env.reset()
110 |
--------------------------------------------------------------------------------
/doc/properties_of_harfang_df.md:
--------------------------------------------------------------------------------
1 | ## df.get_plane_state(self.planeID)
2 |
3 | Return a `dict`
4 |
5 | Example:
6 |
7 | {
8 | 'timestamp': 103981,
9 | 'timestep': 0.016666666666666666,
10 | 'position': [
11 | 7.842685222625732,
12 | 19.442346572875977,
13 | 104.7667007446289
14 | ],
15 | 'Euler_angles': [
16 | 0.0,
17 | -0.23409990966320038,
18 | 0.0
19 | ],
20 | 'easy_steering': True,
21 | 'health_level': 1,
22 | 'destroyed': False,
23 | 'wreck': False,
24 | 'crashed': False,
25 | 'active': True,
26 | 'type': 'AICRAFT',
27 | 'nationality': 1,
28 | 'thrust_level': 1,
29 | 'brake_level': 0,
30 | 'flaps_level': 0,
31 | 'horizontal_speed': 56.026527404785156,
32 | 'vertical_speed': -0.5931200385093689,
33 | 'linear_speed': 56.029666900634766,
34 | 'move_vector': [
35 | -3.7959580421447754,
36 | -0.5931200385093689,
37 | 55.897789001464844
38 | ],
39 | 'linear_acceleration': 1.3036510467529325,
40 | 'altitude': 19.442346572875977,
41 | 'heading': 346.5870820059287,
42 | 'pitch_attitude': 0.0,
43 | 'roll_attitude': 0.0,
44 | 'post_combustion': False,
45 | 'user_pitch_level': 0.0,
46 | 'user_roll_level': 0.0,
47 | 'user_yaw_level': -1.0,
48 | 'gear': True,
49 | 'ia': False,
50 | 'autopilot': False,
51 | 'autopilot_heading': 0,
52 | 'autopilot_speed': -1,
53 | 'autopilot_altitude': 500,
54 | 'target_id': '',
55 | 'target_locked': False
56 | }
57 |
58 | ## df.get_missile_state(self.missileID)
59 |
60 | Return a `dict`
61 |
62 | Example:
63 |
64 | {
65 | 'timestamp': 485000,
66 | 'timestep': 0.016666666666666666,
67 | 'type': 'MISSILE',
68 | 'position': [
69 | -11877.857421875,
70 | 1046.38427734375,
71 | 462.67791748046875
72 | ],
73 | 'Euler_angles': [
74 | -0.01395590603351593,
75 | 1.195150375366211,
76 | 0.046285953372716904
77 | ],
78 | 'move_vector': [
79 | 453.8008117675781,
80 | 8.936182975769043,
81 | 167.3896942138672
82 | ],
83 | 'destroyed': False,
84 | 'wreck': False,
85 | 'crashed': False,
86 | 'active': True,
87 | 'nationality': 2,
88 | 'altitude': 1046.38427734375,
89 | 'heading': 68.4770805117828,
90 | 'pitch_attitude': 0.8042992707191573,
91 | 'roll_attitude': 2.652989167383663,
92 | 'horizontal_speed': 483.68841552734375,
93 | 'vertical_speed': 8.936182975769043,
94 | 'linear_speed': 483.7709655761719,
95 | 'target_id': 'ally_2',
96 | 'life_delay': 30,
97 | 'life_time': 4.133333333333324
98 | }
99 |
100 | ## df.get_plane_state(self.planeID)['timestamp']
101 |
102 | Returns an `int`, recording the timestamp elapsed since the simulation was started.
103 |
104 | ## df.get_plane_state(self.planeID)['position']
105 |
106 | Return a `list[3]=[x, z, y]`, which is the position of plane *planeID*. The coordinate system is shown in the following figure.
107 |
108 | 
109 |
110 | ## df.get_plane_state(self.planeID)['pitch_attitude']
111 |
112 | Return a `float` (degree). The value is shown in the following figure.
113 |
114 | 
115 |
116 | ## df.get_missile_state(self.missileID)['pitch_attitude']
117 |
118 | Same to `df.get_plane_state(self.planeID)['pitch_attitude']` except changing each $0 \degree$ into approximately $\pm 75 \degree$. Besides, it seems nonlinear.
119 |
120 | ## df.get_plane_state(self.planeID)['roll_attitude']
121 |
122 | Return a `float` (degree). The value is shown in the following figure. **This is not a bijection.**
123 |
124 | 
125 |
126 | ## df.get_missile_state(self.missileID)['roll_attitude']
127 |
128 | Same to `df.get_plane_state(self.planeID)['roll_attitude']` except changing each $0 \degree$ into approximately $\pm 75 \degree$. Besides, it seems nonlinear.
129 |
130 | ## df.get_plane_state(self.planeID)['heading']
131 |
132 | Return a `float` (degree). The value is shown in the following figure.
133 |
134 | 
135 |
136 | ## df.get_missile_state(self.missileID)['heading']
137 |
138 | Same to `df.get_plane_state(self.planeID)['heading']`
139 |
140 | ## df.get_plane_state(self.planeID)['horizontal_speed']
141 |
142 | Return a `float`. This value is always positive.
143 |
144 | ## df.get_plane_state(self.planeID)['vertical_speed']
145 |
146 | Return a `float`.
147 |
148 | ## df.get_plane_state(self.planeID)['linear_speed']
149 |
150 | Return a `float`. This value equals to the geometric mean of 'horizontal_speed' and 'vertical_speed'.
151 |
152 |
153 |
154 |
155 |
156 |
--------------------------------------------------------------------------------
/example/maneuvering.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import time
3 | import argparse
4 |
5 | sys.path.append('.')
6 |
7 | from src.environments.dogfightEnv.dogfight_sandbox_hg2.network_client_example import dogfight_client as df
8 |
9 | def parse_args():
10 | parser = argparse.ArgumentParser(description='TBD')
11 | parser.add_argument('--host', default='10.184.0.0', help='specifies Harfang host id')
12 | parser.add_argument('--port', default='50888', help='specifies Harfang port id')
13 | args = parser.parse_args()
14 | return args
15 |
16 | args = parse_args()
17 |
18 | df.connect(args.host, int(args.port))
19 |
20 | planes = df.get_planes_list()
21 |
22 | for i in planes:
23 | df.reset_machine(i)
24 |
25 | df.set_plane_thrust(planes[1], 1)
26 |
27 | df.set_client_update_mode(True)
28 |
29 | # Thrust
30 | t = 0
31 | while t < 1:
32 | df.display_2DText([0.4, 0.7], "Thrust", 0.1, [1, 0.5, 0, 1])
33 | time.sleep(1/60)
34 | plane_state = df.get_plane_state(planes[1])
35 | df.update_scene()
36 | t = plane_state["thrust_level"]
37 |
38 | df.activate_post_combustion(planes[1])
39 |
40 | df.set_plane_pitch(planes[1], -0.5)
41 |
42 | # Pitch
43 | p = 0
44 | while p < 15:
45 | df.display_2DText([0.4, 0.7], "Pitch", 0.1, [1, 0.5, 0, 1])
46 | time.sleep(1/60)
47 | plane_state = df.get_plane_state(planes[1])
48 | df.update_scene()
49 | p = plane_state["pitch_attitude"]
50 |
51 | df.stabilize_plane(planes[1])
52 |
53 | df.retract_gear(planes[1])
54 |
55 |
56 | s = 0
57 | while s < 500:
58 | time.sleep(1/60)
59 | plane_state = df.get_plane_state(planes[1])
60 | df.update_scene()
61 | s = plane_state["altitude"]
62 |
63 | plane_id = planes[1]
64 |
65 | missiles = df.get_machine_missiles_list(plane_id)
66 |
67 | missile_slot = 0
68 | missile_id = missiles[missile_slot]
69 |
70 |
71 | df.fire_missile(plane_id, missile_slot)
72 |
73 | df.set_machine_custom_physics_mode(missile_id, True)
74 |
75 | df.set_missile_life_delay(missile_id, 10)
76 |
77 | df.update_scene()
78 |
79 |
80 | missile_state = df.get_missile_state(missile_id)
81 | x, y, z = missile_state["position"][0], missile_state["position"][1], missile_state["position"][2]
82 | y_b = y
83 | z_b = z
84 | v_0 = df.get_plane_state(planes[1])['linear_speed']
85 | y_0 = df.get_plane_state(planes[1])['vertical_speed']
86 |
87 | df.set_renderless_mode(False)
88 |
89 |
90 | missile_matrix = [1, 0, 0,
91 | 0, 1, 0,
92 | 0, 0, 1,
93 | x, y, z]
94 |
95 | # Linear displacement vector in m.s-1
96 | missile_speed_vector = [1, 1, 1]
97 |
98 | frame_time_step = 1/60
99 |
100 | # Custom missile movements
101 | t = 0
102 | while not missile_state["wreck"]:
103 | df.display_2DText([0.4, 0.7], "Flare", 0.1, [1, 0.5, 0, 1])
104 | time.sleep(1/60)
105 | missile_state = df.get_missile_state(missile_id)
106 | missile_matrix[9] = x
107 | missile_matrix[10] = y
108 | missile_matrix[11] = z
109 |
110 | df.update_machine_kinetics(missile_id, missile_matrix, missile_speed_vector)
111 | df.update_scene()
112 | x = x
113 | y = y_b + y_0 * t - 0.5 * 10 * t * t
114 | z = z_b + v_0 * t
115 |
116 |
117 | # Compute speed vector, used by missile engine smoke
118 | missile_speed_vector = [(x-missile_matrix[9]) / frame_time_step, (y - missile_matrix[10]) / frame_time_step, (z - missile_matrix[11]) / frame_time_step]
119 |
120 | t += frame_time_step
121 |
122 |
123 | # Custom physics off
124 | df.set_machine_custom_physics_mode(missile_id, False)
125 |
126 | df.set_plane_roll(planes[1], .5)
127 |
128 | s = 0
129 | while s < 120:
130 | df.display_2DText([0.4, 0.7], "Roll", 0.1, [1, 0.5, 0, 1])
131 | time.sleep(1/60)
132 | df.update_scene()
133 | s = s + 1
134 |
135 | df.set_plane_roll(planes[1], -.5)
136 |
137 | s = 0
138 | while s < 120:
139 | df.display_2DText([0.4, 0.7], "Roll", 0.1, [1, 0.5, 0, 1])
140 | time.sleep(1/60)
141 | df.update_scene()
142 | s = s + 1
143 |
144 |
145 | df.stabilize_plane(planes[1])
146 |
147 | s = 0
148 | while s < 120:
149 | time.sleep(1/60)
150 | df.update_scene()
151 | s = s + 1
152 |
153 | df.set_plane_yaw(planes[1], .5)
154 | s = 0
155 | while s < 120:
156 | df.display_2DText([0.4, 0.7], "Yaw", 0.1, [1, 0.5, 0, 1])
157 | time.sleep(1/60)
158 | df.update_scene()
159 | s = s + 1
160 |
161 | df.set_plane_yaw(planes[1], -.5)
162 | s = 0
163 | while s < 120:
164 | df.display_2DText([0.4, 0.7], "Yaw", 0.1, [1, 0.5, 0, 1])
165 | time.sleep(1/60)
166 | df.update_scene()
167 | s = s + 1
168 |
169 | df.stabilize_plane(planes[1])
170 |
171 | while s < 120:
172 | time.sleep(1/60)
173 | df.update_scene()
174 | s = s + 1
175 |
176 | df.set_plane_flaps(planes[1], 1)
177 | s = 0
178 | while s < 120:
179 | df.display_2DText([0.4, 0.7], "Flaps", 0.1, [1, 0.5, 0, 1])
180 | time.sleep(1/60)
181 | df.update_scene()
182 | s = s + 1
183 |
184 |
185 | df.set_plane_flaps(planes[1], -1)
186 | s = 0
187 | while s < 120:
188 | df.display_2DText([0.4, 0.7], "Flaps", 0.1, [1, 0.5, 0, 1])
189 | time.sleep(1/60)
190 | df.update_scene()
191 | s = s + 1
192 |
193 |
194 | df.set_plane_brake(planes[1], 1)
195 | df.set_plane_thrust(planes[1], 0)
196 | s = 0
197 | while s < 120:
198 | df.display_2DText([0.4, 0.7], "Speed brake", 0.1, [1, 0.5, 0, 1])
199 | time.sleep(1/60)
200 | df.update_scene()
201 | s = s + 1
202 |
203 | df.deploy_gear(planes[1])
204 |
205 |
206 | # Client update mode OFF
207 | df.set_client_update_mode(False)
208 |
209 | # Disconnect from the Dogfight server
210 | df.disconnect()
211 |
212 |
213 |
--------------------------------------------------------------------------------
/df2.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import os
3 | import time
4 |
5 | import gym
6 | import numpy as np
7 | from stable_baselines3 import DDPG, TD3, SAC
8 | from stable_baselines3.common.noise import (NormalActionNoise,
9 | OrnsteinUhlenbeckActionNoise)
10 |
11 |
12 | def parse_args():
13 | parser = argparse.ArgumentParser(description='DBRL is an air combat simulation benchmark based on JSBSim and Dogfight 2. For more information, please visit https://github.com/mrwangyou/DBRL')
14 | parser.add_argument('--task', choices=['evade', 'dogfight'], required=True, help='specifies the simulation task')
15 | parser.add_argument('--host', default='10.184.0.0', help='specifies Harfang host id')
16 | parser.add_argument('--port', default='50888', help='specifies Harfang port id')
17 | parser.add_argument('--plane-slot', type=int, default=1, help='specifies the ego plane')
18 | parser.add_argument('--enemy-slot', type=int, default=3, help='specifies the enemy plane')
19 | parser.add_argument('--missile-slot', type=int, default=1, help='specifies the missile to escape from')
20 | parser.add_argument('--realtime', action='store_true', help='specifies to run in real world time while training. Only works when --trian is specified')
21 | parser.add_argument('--train', action='store_true', help='specifies the running mode of DBRL')
22 | parser.add_argument('--test', action='store_true', help='specifies the running mode of DBRL')
23 | parser.add_argument('--timesteps', type=int, default=10000000, help='specifies the training timesteps')
24 | parser.add_argument('--model', choices=['DDPG', 'TD3', 'SAC'], default='SAC', help='specifies the DRL model used in algorithm training')
25 | parser.add_argument('--initial-state', choices=['air', 'carrier'], default='carrier', help='')
26 | # parser.add_argument('--modelPath', default=None, help='specifies the pre-trained model. Only works when --train is specified')
27 | parser.add_argument('--record-result', action='store_true', help='specifies to record the evaluating result of DBRL. Only works when --test is specified')
28 | parser.add_argument('--record-status', type=int, default=0, help='specifies the recording period for aircraft status recording during test flights. Only works when --test is specified')
29 | parser.add_argument('--throttle-enable', action='store_true', help='specifies whether to enable the throttle control')
30 | parser.add_argument('--flare-enable', action='store_true', help='specifies whether to enable the decoy flare')
31 | parser.add_argument('--ego-pose-enable', action='store_true', help='')
32 | parser.add_argument('--oppo-pose-enable', action='store_true', help='')
33 | parser.add_argument('--missile-pose-enable', action='store_true', help='')
34 | parser.add_argument('--missile-relative-azimuth-enable', action='store_true', help='')
35 | args = parser.parse_args()
36 | return args
37 |
38 | args = parse_args()
39 |
40 | msg = "{}_{}_{}".format(args.task, args.model, args.timesteps)
41 | print(msg)
42 |
43 | env = gym.make(
44 | "DBRLDogfight-v0" if args.task == 'evade' else "DBRLDf-v0",
45 | host=args.host,
46 | port=args.port,
47 | plane_slot=args.plane_slot,
48 | enemy_slot=args.enemy_slot,
49 | missile_slot=args.missile_slot,
50 | # rendering=args.realtime,
51 | rendering=False,
52 | record_status=args.record_status,
53 | initial_state=args.initial_state,
54 | throttle_enable=args.throttle_enable,
55 | flare_enable=args.flare_enable,
56 | ego_pose_enable=True,
57 | oppo_pose_enable=False,
58 | missile_pose_enable=True,
59 | missile_relative_azimuth_enable=True,
60 | msg=msg
61 | )
62 |
63 | n_actions = env.action_space.shape[-1]
64 | action_noise = NormalActionNoise(
65 | mean=np.zeros(n_actions),
66 | sigma=0.1 * np.ones(n_actions)
67 | )
68 |
69 | model = eval(args.model)(
70 | "MlpPolicy",
71 | env,
72 | verbose=1,
73 | action_noise=action_noise,
74 | )
75 |
76 | path = r'./log/'
77 | if not os.path.exists(path):
78 | os.mkdir(path)
79 |
80 | if args.train:
81 | # try:
82 | # model = eval(args.model).load("./log/{}_evade".format(method[args.model]), env)
83 | # model.load_replay_buffer("./log/{}_evade_rb".format(method[args.model]))
84 | # except:
85 | # pass
86 | model.learn(total_timesteps=args.timesteps, log_interval=1)
87 | model.save("./log/{}".format(msg))
88 | model.save_replay_buffer("./log/{}_rb".format(msg))
89 |
90 | if args.test:
91 | model = eval(args.model).load("./log/{}".format(msg))
92 |
93 | if args.record_result and os.path.exists('./log/{}_record_result.txt'.format(msg)):
94 | f = open('./log/{}_record_result.txt'.format(msg), 'r')
95 | for line in f:
96 | pass
97 | win = int(line.split()[0])
98 | episodes = int(line.split()[2])
99 | f.close()
100 | else:
101 | win = 0
102 | episodes = 0
103 |
104 | obs = env.reset()
105 | while True:
106 | action, _states = model.predict(obs)
107 | obs, rewards, dones, info = env.step(action)
108 | env.render()
109 | if dones == True:
110 | if rewards == 50:
111 | win += 1
112 | episodes += 1
113 | if args.record_result:
114 | f = open('./log/{}_record_result.txt'.format(msg), 'a')
115 | f.write("{} / {}\n".format(win, episodes))
116 | f.close()
117 | print("Episodes: {}\tacc: {}".format(episodes, win / episodes))
118 | time.sleep(2)
119 | env.reset()
120 |
--------------------------------------------------------------------------------
/src/environments/jsbsimEnv/jsbsimFdm.py:
--------------------------------------------------------------------------------
1 | import time
2 |
3 | import jsbsim
4 |
5 |
6 | class JsbsimFdm():
7 |
8 | def __init__(
9 | self,
10 | fdm_id = 1,
11 | fdm_aircraft = 'f16',
12 | fdm_ic_v = 500, # Calibrated Velocity (knots) https://skybrary.aero/articles/calibrated-airspeed-cas
13 | fdm_ic_lat = 0, # Latitude (degree) 纬度 recommend .01
14 | fdm_ic_long = 0, # Longitude (degree) 经度 recommend .003
15 | fdm_ic_h = 20005.5, # Height above sea level (feet) 海拔
16 | fdm_ic_psi = 0, # Yaw 偏航角,绕Z轴,JSBSim将按照这里列出的顺序先后进行欧拉角计算
17 | fdm_ic_theta = 0, # Pitch 俯仰角,绕Y轴
18 | fdm_ic_phi = 0, # Roll 翻滚角,绕X轴
19 |
20 | fdm_hp = 1,
21 | fdm_fgfs = False, # Visualization
22 | flight_mode = 1, # 0 for flight test
23 | ) -> None:
24 |
25 | # FDM Initialization 空气动力学模型初始化
26 | self.fdm = jsbsim.FGFDMExec(None)
27 |
28 | # Aircraft Loading 加载飞机模型
29 | self.fdm.load_model(fdm_aircraft)
30 |
31 | # FlightGear Visualization 可视化
32 | if fdm_fgfs is True:
33 | self.fdm.set_output_directive('./data_output/flightgear{}.xml'.format(fdm_id))
34 |
35 | # Velocity Initialization 速度初始化
36 | self.fdm['ic/vc-kts'] = fdm_ic_v
37 |
38 | # Position Initialization 位置初始化
39 | self.fdm["ic/lat-gc-deg"] = fdm_ic_lat
40 | self.fdm["ic/long-gc-deg"] = fdm_ic_long
41 | self.fdm["ic/h-sl-ft"] = fdm_ic_h
42 |
43 | # Attitude Initialization 姿态初始化
44 | self.fdm["ic/psi-true-deg"] = fdm_ic_psi
45 | self.fdm["ic/theta-deg"] = fdm_ic_theta
46 | self.fdm["ic/phi-deg"] = fdm_ic_phi
47 |
48 | ##########################
49 | ## Model Initialization ##
50 | ## 模型初始化 ##
51 | self.fdm.run_ic() ##
52 | ##########################
53 |
54 | # Turning on the Engine 启动引擎
55 | self.fdm["propulsion/starter_cmd"] = 1
56 |
57 | # Refueling 无限燃料
58 | if flight_mode == 0:
59 | self.fdm["propulsion/refuel"] = 1
60 |
61 | # First but not Initial 第一帧设置
62 | self.fdm.run()
63 | self.fdm["propulsion/active_engine"] = True
64 | self.fdm["propulsion/set-running"] = -1
65 |
66 | # Number of Frames 帧数
67 | self.nof = 1
68 |
69 | # Setting HP 生命值
70 | self.fdm_hp = fdm_hp
71 |
72 | # Getting parameters 记录参数
73 | self.param = {
74 | 'fdm_id' : fdm_id,
75 | 'fdm_aircraft' : fdm_aircraft,
76 | 'fdm_ic_v' : fdm_ic_v,
77 | 'fdm_ic_lat' : fdm_ic_lat,
78 | 'fdm_ic_long' : fdm_ic_long,
79 | 'fdm_ic_h' : fdm_ic_h,
80 | 'fdm_ic_psi' : fdm_ic_psi,
81 | 'fdm_ic_theta' : fdm_ic_theta,
82 | 'fdm_ic_phi' : fdm_ic_phi,
83 | 'fdm_hp' : fdm_hp,
84 | 'fdm_fgfs' : fdm_fgfs,
85 | 'flight_mode' : flight_mode,
86 | }
87 |
88 | def getProperty(
89 | self,
90 | prop,
91 | ) -> list:
92 | if prop == 'position':
93 | prop = [
94 | "position/lat-gc-deg", # Latitude 纬度
95 | "position/long-gc-deg", # Longitude 经度
96 | "position/h-sl-ft", # Altitude above sea level(feet) 海拔(英尺)
97 | ]
98 | elif prop == 'positionEci': # Earth-centered inertial(feet) 地心惯性坐标系(英尺),与Ecef相比相对静止
99 | prop = [
100 | "position/eci-x-ft", # 指向春分点
101 | "position/eci-y-ft", # 左手系决定
102 | "position/eci-z-ft", # 指向北极点
103 | ]
104 | elif prop == 'positionEcef': # Earth-centered, Earth-fixed coordinate system(feet) 地心地固坐标系(英尺) https://zhuanlan.zhihu.com/p/360744867
105 | prop = [
106 | "position/ecef-x-ft", # 指向经纬度为0的点(本初子午线与赤道的交点)
107 | "position/ecef-y-ft", # 左手系决定
108 | "position/ecef-z-ft", # 指向北极点
109 | ]
110 | elif prop == 'attitudeRad': # Attitude(Rad) 姿态(弧度)
111 | prop = [
112 | "attitude/psi-rad", # Yaw 偏航角
113 | "attitude/theta-rad", # Pitch 俯仰角
114 | "attitude/phi-rad", # Roll 翻滚角
115 | ]
116 | elif prop == 'attitudeDeg': # Attitude(Deg) 姿态(角度)
117 | prop = [
118 | "attitude/psi-deg", # Yaw 偏航角
119 | "attitude/theta-deg", # Pitch 俯仰角
120 | "attitude/phi-deg", # Roll 翻滚角
121 | ]
122 | elif prop == 'pose': # Pose(deg) 位姿(角度)
123 | prop = [
124 | "position/lat-gc-deg",
125 | "position/long-gc-deg",
126 | "position/h-sl-ft",
127 | "attitude/psi-deg",
128 | "attitude/theta-deg",
129 | "attitude/phi-deg",
130 | ]
131 | elif prop == 'velocity': # Velocity(fps) 速度(英尺每秒)
132 | prop = [
133 | "velocities/v-north-fps",
134 | "velocities/v-east-fps",
135 | "velocities/v-down-fps",
136 | ]
137 | elif prop == 'acceleration': # Acceleration 加速度
138 | raise NotImplementedError('Hasn\'t finished yet!')
139 | prop = [
140 |
141 | ]
142 | elif prop == 'all': # nominal 'all'
143 | prop = [
144 | "position/lat-gc-deg",
145 | "position/long-gc-deg",
146 | "position/h-sl-ft",
147 | "attitude/psi-deg",
148 | "attitude/theta-deg",
149 | "attitude/phi-deg",
150 | "velocities/v-north-fps",
151 | "velocities/v-east-fps",
152 | "velocities/v-down-fps",
153 | ]
154 | else:
155 | return self.fdm[prop]
156 |
157 | return [
158 | self.fdm[item] for item in prop
159 | ]
160 |
161 | def sendAction(
162 | self,
163 | action, # List of size [num_of_action]
164 | actionType=None,
165 | ):
166 | if actionType is None:
167 | action_space = [
168 | "fcs/aileron-cmd-norm", # 副翼
169 | "fcs/elevator-cmd-norm", # 升降舵
170 | "fcs/rudder-cmd-norm", # 方向舵
171 | "fcs/throttle-cmd-norm", # 油门
172 | "fcs/flap-cmd-norm", # 襟翼
173 | "fcs/speedbrake-cmd-norm", # 减速板
174 | "fcs/spoiler-cmd-norm", # 扰流片
175 | ]
176 | for i in range(len(action_space)):
177 | self.fdm[action_space[i]] = action[i]
178 | else:
179 | self.fdm[actionType] = action
180 |
181 | def damage(self, value):
182 | self.fdm_hp = self.fdm_hp - value
183 |
184 | def step(self, playSpeed=0):
185 | self.nof = self.nof + 1
186 | self.fdm.run()
187 |
188 | if playSpeed != 0:
189 | time.sleep(self.fdm.get_delta_t() / playSpeed)
190 |
191 | def terminate(self):
192 | if self.fdm_hp <= 0:
193 | return 1
194 | else:
195 | return 0
196 |
--------------------------------------------------------------------------------
/doc/QuickStart_zh.md:
--------------------------------------------------------------------------------
1 |
2 |
DBRL: A Gym Dogfighting Simulation Benchmark for Reinforcement Learning Research
3 |
4 |
5 |
6 |
7 | ## 快速入门
8 |
9 |
10 | 准备工作
11 |
12 | DBRL基于空气动力学软件JSBSim与Dogfight 2搭建空战仿真平台。
13 |
14 | 如需使用基于JSBSim搭建的视距内缠斗仿真环境,可以运行如下代码安装Python-JSBSim:
15 |
16 | ```bash
17 | pip install jsbsim
18 | ```
19 |
20 | 安装的JSBSim可能不包括[JSBSim Github页面](https://github.com/JSBSim-Team/jsbsim)中的全部文件,建议您将如上链接中的文件拷贝进`JSBSim`源文件地址中。
21 |
22 | 这里只提供Dogfight 2环境在Windows系统下运行的示例,如果需要在Linux系统中运行Harfang3D Dog-Fight,可以下载Linux版Harfang。如需使用基于Dogfight 2搭建的导弹躲避仿真环境,可以在Windows PowerShell中运行如下代码进行软件下载与配置:
23 |
24 | ```bash
25 | wget -Uri https://github.com/harfang3d/dogfight-sandbox-hg2/releases/download/1.0.2/dogfight-sandbox-hg2-win64.7z -OutFile "df2.7z"
26 |
27 | 7z x -odf2/ df2.7z
28 |
29 | mv ./df2/dogfight-sandbox-hg2/ {DBRL}/src/environments/dogfightEnv/dogfight_sandbox_hg2/
30 |
31 | del df2/, df2.7z
32 | ```
33 | 其中,`{DBRL}`需要替换为本项目所在的路径,在命令行中使用7z指令需要将您的7zip安装位置添加至path系统变量,您同样可以手动进行文件下载,解压缩,重命名与移动操作。
34 |
35 |
36 |
37 |
38 | JSBSim的可视化功能基于FlightGear实现,如需在使用JSBSim的过程中将空战的飞行过程可视化显示,请在FlightGear官网中安装FlightGear软件。如需对缠斗中的两架飞机分别进行可视化,请在`{JSBSim}/data_output/`下复制两份`flightgear.xml`文件,分别命名为`flightgear{1/2}.xml`,并将`flightgear2.xml`第18行中的`5550`修改为`5551`。上述操作中的`{JSBSim}`为Python JSBSim库所在的路径;如需查看`JSBSim`源文件地址,可以运行如下代码:
39 |
40 | ```python
41 | import jsbsim
42 |
43 | print(jsbsim.get_default_root_dir())
44 | ```
45 |
46 | 在运行需要可视化的接战仿真中,请在FlightGear启动时使用如下参数:
47 |
48 | ```bash
49 | --fdm=null --native-fdm=socket,in,60,,5550,udp
50 | ```
51 | > 如果您的系统语言非英语,且出现了FlightGear在操纵键盘后卡住的情况,您可以尝试使用其他输入法来解决这个问题。详情可见[论坛](https://forum.flightgear.org/viewtopic.php?f=22&t=40704)。
52 |
53 | 在运行同时对两架飞机可视化的仿真中,请分别使用
54 | ```bash
55 | --fdm=null --native-fdm=socket,in,60,,5550,udp --multiplay=out,10,127.0.0.1,5000 --multiplay=in,10,127.0.0.1,5001 --callsign=Test1
56 | ```
57 | 与
58 | ```bash
59 | --fdm=null --native-fdm=socket,in,60,,5550,udp --multiplay=out,10,127.0.0.1,5001 --multiplay=in,10,127.0.0.1,5000 --callsign=Test2
60 | ```
61 | 参数。
62 |
63 | 如需使用本项目中的强化学习模型`src/models/*.py`,请将本项目文件夹置于JSBSim目录下,即:
64 |
65 | ```
66 | JSBSim/
67 | ├── DBRL/
68 | │ ├── doc/
69 | │ ├── log/
70 | │ ├── src/
71 | │ ├── test/
72 | │ └── ...
73 | ├── aircraft/
74 | ├── scripts/
75 | └── ...
76 | ```
77 |
78 |
79 |
80 |
81 |
82 | 使用方法
83 |
84 | DBRL提供了OpenAI Gym格式的强化学习环境,可以通过Gym库进行调用。如需使用`gym.make`调用环境,请在`Gym/envs`下复制`src/environments/*Env/`文件夹,并在`Gym/envs/__init__.py`中添加如下代码:
85 |
86 | ```python
87 | register(
88 | id="DBRL{Jsbsim/Dogfight}-v0",
89 | entry_point="gym.envs.{jsbsim/dogfight}Env:{Jsbsim/Dogfight}Env",
90 | )
91 | ```
92 |
93 | 如需查看`gym`源文件地址,可以运行如下代码:
94 |
95 | ```bash
96 | pip show gym
97 | ```
98 |
99 | 调用环境时可以采用如下代码:
100 |
101 | ```python
102 | import gym
103 |
104 | env = gym.make('DBRL{Jsbsim/Dogfight}-v0')
105 | ```
106 |
107 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 | 环境特征
122 |
123 | DBRL-JSBSim提供了一个基于强化学习框架Gym的智能空战仿真环境,它的动作空间为:
124 |
125 | ```python
126 | gym.spaces.Box(
127 | low=np.array([-1, -1, -1, 0, 0, 0, 0]),
128 | high=np.array([1, 1, 1, 1, 1, 1, 1])
129 | )
130 | ```
131 | 其中六个维度分别表示对副翼(Aileron)、升降舵(Elevator)、方向舵(Rudder)、油门(Throttle)、襟翼(Flap)、减速板(Speed brake)、扰流片(Spoiler)的操控。
132 |
133 | 状态空间为:
134 |
135 | ```python
136 | gym.spaces.Box(
137 | low=np.array([-360, -360, 0, -360, -360, -360] * 2),
138 | high=np.array([360, 360, 60000, 360, 360, 360] * 2)
139 | )
140 | ```
141 | 其中六个维度分别表示飞机的纬度(Latitude)、经度(Longitude)、海拔(Height above sea level)、偏航角(Yaw)、俯仰角(Pitch)、滚转角(Roll),单位为度(Degree)与英尺(Feet)。
142 |
143 | 作为一场接战环境的初始化,`class JSBSimEnv()`接受两个`class JSBSimFdm()`作为输入变量:
144 |
145 | ```python
146 | class JsbsimEnv(Env):
147 |
148 | def __init__(
149 | self,
150 | fdm1=Fdm(fdm_id=1),
151 | fdm2=Fdm(fdm_id=2),
152 | ) -> None:
153 | ```
154 |
155 | JSBSimFdm类储存在`DBRL/src/environments/jsbsimEnv/jsbsimFdm.py`中,它的构造函数接受如下参数作为输入:
156 |
157 | ```python
158 | class JsbsimFdm():
159 |
160 | def __init__(
161 | self,
162 | fdm_id = 1,
163 | fdm_aircraft = 'f16',
164 | fdm_ic_v = 500,
165 | fdm_ic_lat = 0,
166 | fdm_ic_long = 0,
167 | fdm_ic_h = 20005.5,
168 | fdm_ic_psi = 0,
169 | fdm_ic_theta = 0,
170 | fdm_ic_phi = 0,
171 | fdm_hp = 1,
172 | fdm_fgfs = False,
173 | flight_mode = 1
174 | ) -> None:
175 | ```
176 | 其中:`fdm_id`表示接战中飞机的编号,需要分别设置为`1`与`2`;`fdm_aircraft`表示接战中飞机的型号;`fdm_ic_*`表示飞机的初始化特征;`fdm_ic_v`表示飞机的初始校正空速,单位为节(knot);`fdm_ic_lat`表示飞机的初始纬度;`fdm_ic_long`表示飞机的初始经度;`fdm_ic_h`表示飞机的初始海拔高度,单位为英尺;`fdm_ic_psi`、`fdm_ic_theta`、`fdm_ic_phi`分别表示飞机的偏航角,俯仰角与滚转角;`fdm_hp`表示飞机的初始血量;`fdm_fgfs`表示飞机是否需要在FlightGear上进行可视化;`flight_mode`表示飞行模式(暂无效果)。
177 |
178 | > JSBSim将按照此处列出的三个欧拉角的先后顺序进行飞机姿态的计算,即,先设定飞机的偏航角,再在此基础上,依照参数设定飞机的俯仰角与滚转角。
179 |
180 | DBRL提供的回报函数为当前帧对敌机造成的血量损失大小与敌机对自身造成伤害的差值,但也同样可以用JSBSimFdm类的`getProperty()`函数获得一些其他特征作为不同强化学习模型的回报函数。
181 |
182 | ```python
183 | def getProperty(
184 | self,
185 | prop,
186 | ) -> list:
187 | ```
188 |
189 | ---
190 | ---
191 |
192 |
193 | DBRL-Dogfight提供了一个基于强化学习框架Gym的战斗机机动躲避智能决策仿真环境,作为环境的初始状态,智能体需要操控的飞机被另一架飞机发射的“流星”空空导弹锁定,智能体需要采取机动操纵躲避导弹的威胁。
194 |
195 | Dogfight 2环境提供可选的动作空间包括:
196 |
197 | ```python
198 | if pitch_enable:
199 | action_infimum = np.append(action_infimum, -1)
200 | action_supermum = np.append(action_supermum, 1)
201 | if roll_enable:
202 | action_infimum = np.append(action_infimum, -1)
203 | action_supermum = np.append(action_supermum, 1)
204 | if yaw_enable:
205 | action_infimum = np.append(action_infimum, -1)
206 | action_supermum = np.append(action_supermum, 1)
207 | if flaps_enable:
208 | action_infimum = np.append(action_infimum, 0)
209 | action_supermum = np.append(action_supermum, 1)
210 | if throttle_enable:
211 | action_infimum = np.append(action_infimum, 0)
212 | action_supermum = np.append(action_supermum, 1)
213 | if flare_enable:
214 | action_infimum = np.append(action_infimum, 0)
215 | action_supermum = np.append(action_supermum, 1)
216 | action_space = Box(
217 | low=action_infimum,
218 | high=action_supermum,
219 | )
220 | ```
221 | 其中六个维度分别表示对俯仰角(Pitch)、滚转角(Roll)、偏航角(Yaw)、襟翼(Flaps)、油门(Throttle)、干扰弹(Decoy flare)的操控。
222 |
223 | 可选状态空间包括:
224 |
225 | ```python
226 | if ego_plane_position:
227 | observation_infimum = np.append(observation_infimum, [-300, -300, -1])
228 | observation_supermum = np.append(observation_supermum, [300, 300, 200])
229 | if ego_plane_attitude:
230 | observation_infimum = np.append(observation_infimum, [0, -360, -360])
231 | observation_supermum = np.append(observation_supermum, [360, 360, 360])
232 | if oppo_plane_position:
233 | observation_infimum = np.append(observation_infimum, [-300, -300, -1])
234 | observation_supermum = np.append(observation_supermum, [300, 300, 200])
235 | if oppo_plane_attitude:
236 | observation_infimum = np.append(observation_infimum, [0, -360, -360])
237 | observation_supermum = np.append(observation_supermum, [360, 360, 360])
238 | if missile_position:
239 | observation_infimum = np.append(observation_infimum, [-300, -300, -1])
240 | observation_supermum = np.append(observation_supermum, [300, 300, 200])
241 | if missile_attitude:
242 | observation_infimum = np.append(observation_infimum, [-315, -315, -315])
243 | observation_supermum = np.append(observation_supermum, [315, 315, 315])
244 | if missile_relative_azimuth:
245 | observation_infimum = np.append(observation_infimum, [-1, -1, -1])
246 | observation_supermum = np.append(observation_supermum, [1, 1, 1])
247 | observation_space = Box(
248 | low=observation_infimum,
249 | high=observation_supermum,
250 | )
251 | ```
252 | 可选飞机的X坐标(÷100)、Y坐标(÷100)、Z坐标(÷50)、偏航角、俯仰角(×4)、滚转角(×4),导弹的X坐标(÷100)、Y坐标(÷100)、Z坐标(÷50)、偏航角(×100)、俯仰角(×100)、滚转角(×100),导弹相对于飞机的方位角等。
253 |
254 | DogfightEnv的初始化需要与Dogfight 2软件进行连接,接受如下参数作为输入变量:
255 |
256 | ```python
257 | class DogfightEnv(Env):
258 |
259 | def __init__(
260 | self,
261 | host='10.184.0.0',
262 | port='50888',
263 | plane_slot=1,
264 | enemy_slot=3,
265 | missile_slot=1,
266 | rendering=False,
267 | record_status=0,
268 | initial_state='carrier',
269 | throttle_enable=False,
270 | flare_enable=False,
271 | ego_pose_enable=True,
272 | oppo_pose_enable=False,
273 | missile_pose_enable=True,
274 | missile_relative_azimuth_enable=False,
275 | msg=None,
276 | ) -> None:
277 | ```
278 |
279 | 在使用DogfightEnv环境前,需要先启动Dogfight 2软件,选择Network Mode,并将软件左上角显示的Host与Port作为参数传进环境中。
280 |
281 |
282 |
283 |
284 |
285 |
294 |
295 |
296 | ## 欢迎交流
297 |
298 | 如果您在使用过程中发现了任何错误,或者有任何需求与改进的建议,欢迎在Github Issues中提出,谢谢!
299 |
--------------------------------------------------------------------------------
/tutorial.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "### Tutorial of dogfight simulation algorithm training and testing"
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": [
14 | "#### Clone GitHub repository and install dependencies."
15 | ]
16 | },
17 | {
18 | "cell_type": "code",
19 | "execution_count": null,
20 | "metadata": {
21 | "vscode": {
22 | "languageId": "powershell"
23 | }
24 | },
25 | "outputs": [],
26 | "source": [
27 | "pip install jsbsim"
28 | ]
29 | },
30 | {
31 | "cell_type": "code",
32 | "execution_count": 1,
33 | "metadata": {},
34 | "outputs": [
35 | {
36 | "name": "stdout",
37 | "output_type": "stream",
38 | "text": [
39 | "c:\\Users\\mrwan\\.conda\\envs\\jsbsim\\share\\JSBSim\n"
40 | ]
41 | }
42 | ],
43 | "source": [
44 | "import jsbsim\n",
45 | "\n",
46 | "print(jsbsim.get_default_root_dir())"
47 | ]
48 | },
49 | {
50 | "cell_type": "code",
51 | "execution_count": null,
52 | "metadata": {
53 | "vscode": {
54 | "languageId": "powershell"
55 | }
56 | },
57 | "outputs": [],
58 | "source": [
59 | "cd c:/Users/mrwan/.conda/envs/jsbsim/share\n",
60 | "\n",
61 | "git clone git@github.com:JSBSim-Team/jsbsim.git\n",
62 | "\n",
63 | "cd jsbsim\n",
64 | "\n",
65 | "# Skip the next line of code if you've already cloned DBRL in Dogfight2 environment\n",
66 | "git clone https://github.com/mrwangyou/DBRL.git\n",
67 | "\n",
68 | "cd DBRL"
69 | ]
70 | },
71 | {
72 | "cell_type": "markdown",
73 | "metadata": {},
74 | "source": [
75 | "#### Download FlightGear"
76 | ]
77 | },
78 | {
79 | "cell_type": "markdown",
80 | "metadata": {},
81 | "source": [
82 | "If you are using Windows, please download [FlightGear for Windows](https://sourceforge.net/projects/flightgear/files/release-2020.3/FlightGear-2020.3.13.exe/download). If you are using Linux x86 systems, download [AppImage binary release](https://sourceforge.net/projects/flightgear/files/release-2020.3/FlightGear-2020.3.13-x86_64.AppImage/download).\n",
83 | "\n",
84 | "Run two FlightGear separately with the attributes below\n",
85 | "\n",
86 | "```bash\n",
87 | "--fdm=null --native-fdm=socket,in,60,,5550,udp --multiplay=out,10,127.0.0.1,5000 --multiplay=in,10,127.0.0.1,5001 --callsign=Test1\n",
88 | "\n",
89 | "--fdm=null --native-fdm=socket,in,60,,5550,udp --multiplay=out,10,127.0.0.1,5001 --multiplay=in,10,127.0.0.1,5000 --callsign=Test2\n",
90 | "```\n",
91 | "\n",
92 | ""
93 | ]
94 | },
95 | {
96 | "cell_type": "markdown",
97 | "metadata": {},
98 | "source": [
99 | "#### Register JSBSimEnv in Gym"
100 | ]
101 | },
102 | {
103 | "cell_type": "code",
104 | "execution_count": 2,
105 | "metadata": {
106 | "vscode": {
107 | "languageId": "powershell"
108 | }
109 | },
110 | "outputs": [
111 | {
112 | "name": "stdout",
113 | "output_type": "stream",
114 | "text": [
115 | "Name: gym\n",
116 | "Version: 0.21.0\n",
117 | "Summary: Gym: A universal API for reinforcement learning environments.\n",
118 | "Home-page: https://github.com/openai/gym\n",
119 | "Author: OpenAI\n",
120 | "Author-email: jkterry@umd.edu\n",
121 | "License: \n",
122 | "Location: c:\\users\\mrwan\\.conda\\envs\\jsbsim\\lib\\site-packages\n",
123 | "Requires: cloudpickle, numpy\n",
124 | "Required-by: stable-baselines3\n",
125 | "Note: you may need to restart the kernel to use updated packages.\n"
126 | ]
127 | }
128 | ],
129 | "source": [
130 | "pip show gym"
131 | ]
132 | },
133 | {
134 | "cell_type": "code",
135 | "execution_count": null,
136 | "metadata": {
137 | "vscode": {
138 | "languageId": "powershell"
139 | }
140 | },
141 | "outputs": [],
142 | "source": [
143 | "# Replace the path with your own Gym directory\n",
144 | "cp ./src/environments/jsbsimEnv/ c:/users/mrwan/.conda/envs/jsbsim/lib/site-packages/gym/envs/ -r"
145 | ]
146 | },
147 | {
148 | "cell_type": "markdown",
149 | "metadata": {},
150 | "source": [
151 | "Add the following code to `{GYM}/envs/__init__.py`.\n",
152 | "\n",
153 | "```python\n",
154 | "register(\n",
155 | " id=\"DBRLJsbsim-v0\",\n",
156 | " entry_point=\"gym.envs.jsbsimEnv:JsbsimEnv\",\n",
157 | ")\n",
158 | "```"
159 | ]
160 | },
161 | {
162 | "cell_type": "markdown",
163 | "metadata": {},
164 | "source": [
165 | "#### Start to train or test the dogfighting algorithm"
166 | ]
167 | },
168 | {
169 | "cell_type": "code",
170 | "execution_count": null,
171 | "metadata": {
172 | "vscode": {
173 | "languageId": "powershell"
174 | }
175 | },
176 | "outputs": [],
177 | "source": [
178 | "python ./src/models/sac_jsbsim.py --train --test --timesteps 1000000"
179 | ]
180 | },
181 | {
182 | "cell_type": "markdown",
183 | "metadata": {},
184 | "source": [
185 | "---"
186 | ]
187 | },
188 | {
189 | "cell_type": "markdown",
190 | "metadata": {},
191 | "source": [
192 | "### Tutorial of missile-evading algorithm training and testing"
193 | ]
194 | },
195 | {
196 | "cell_type": "markdown",
197 | "metadata": {},
198 | "source": [
199 | "#### Install dependencies."
200 | ]
201 | },
202 | {
203 | "cell_type": "code",
204 | "execution_count": null,
205 | "metadata": {
206 | "vscode": {
207 | "languageId": "powershell"
208 | }
209 | },
210 | "outputs": [],
211 | "source": [
212 | "cd jsbsim\n",
213 | "\n",
214 | "# Skip the next line of code if you've already cloned DBRL in JSBSim environment\n",
215 | "git clone https://github.com/mrwangyou/DBRL.git\n",
216 | "\n",
217 | "cd DBRL\n",
218 | "\n",
219 | "wget -Uri https://github.com/harfang3d/dogfight-sandbox-hg2/releases/download/1.0.2/dogfight-sandbox-hg2-win64.7z -OutFile \"df2.7z\"\n",
220 | "7z x -odf2/ df2.7z\n",
221 | "mv ./df2/dogfight-sandbox-hg2/ ./src/environments/dogfightEnv/dogfight_sandbox_hg2/\n",
222 | "del df2/, df2.7z"
223 | ]
224 | },
225 | {
226 | "cell_type": "markdown",
227 | "metadata": {},
228 | "source": [
229 | "#### Register DogfightEnv in Gym."
230 | ]
231 | },
232 | {
233 | "cell_type": "code",
234 | "execution_count": 3,
235 | "metadata": {
236 | "vscode": {
237 | "languageId": "powershell"
238 | }
239 | },
240 | "outputs": [
241 | {
242 | "name": "stdout",
243 | "output_type": "stream",
244 | "text": [
245 | "Name: gym\n",
246 | "Version: 0.21.0\n",
247 | "Summary: Gym: A universal API for reinforcement learning environments.\n",
248 | "Home-page: https://github.com/openai/gym\n",
249 | "Author: OpenAI\n",
250 | "Author-email: jkterry@umd.edu\n",
251 | "License: \n",
252 | "Location: c:\\users\\mrwan\\.conda\\envs\\jsbsim\\lib\\site-packages\n",
253 | "Requires: cloudpickle, numpy\n",
254 | "Required-by: stable-baselines3\n",
255 | "Note: you may need to restart the kernel to use updated packages.\n"
256 | ]
257 | }
258 | ],
259 | "source": [
260 | "pip show gym"
261 | ]
262 | },
263 | {
264 | "cell_type": "code",
265 | "execution_count": null,
266 | "metadata": {
267 | "vscode": {
268 | "languageId": "powershell"
269 | }
270 | },
271 | "outputs": [],
272 | "source": [
273 | "# Replace the path with your own Gym directory\n",
274 | "cp ./src/environments/dogfightEnv/ c:/users/mrwan/.conda/envs/jsbsim/lib/site-packages/gym/envs/ -r"
275 | ]
276 | },
277 | {
278 | "cell_type": "markdown",
279 | "metadata": {},
280 | "source": [
281 | "Add the following code to `{GYM}/envs/__init__.py`.\n",
282 | "\n",
283 | "```python\n",
284 | "register(\n",
285 | " id=\"DBRLDogfight-v0\",\n",
286 | " entry_point=\"gym.envs.dogfightEnv:DogfightEnv\",\n",
287 | ")\n",
288 | "```"
289 | ]
290 | },
291 | {
292 | "cell_type": "markdown",
293 | "metadata": {},
294 | "source": [
295 | "#### Run df2"
296 | ]
297 | },
298 | {
299 | "cell_type": "markdown",
300 | "metadata": {},
301 | "source": [
302 | "Run `evade.bat` in `./src/environments/dogfightEnv/` for missile evading mode.\n",
303 | "\n",
304 | "Run `start.bat` in `dogfight_sandbox_hg2/`, choose \"Network Mode\", and press space. The host and port IP are in the upper left corner of the screen.\n",
305 | "\n",
306 | ""
307 | ]
308 | },
309 | {
310 | "cell_type": "markdown",
311 | "metadata": {},
312 | "source": [
313 | "#### Start to train or test the evading algorithm."
314 | ]
315 | },
316 | {
317 | "cell_type": "code",
318 | "execution_count": null,
319 | "metadata": {
320 | "vscode": {
321 | "languageId": "powershell"
322 | }
323 | },
324 | "outputs": [],
325 | "source": [
326 | "# Replace the parameters with your own host and port\n",
327 | "python ./df2.py --host 192.168.239.1 --port 50888 --train --test --timesteps 50000 --model SAC --initial-state air --flare-enable --ego-pose-enable --missile-pose-enable"
328 | ]
329 | }
330 | ],
331 | "metadata": {
332 | "kernelspec": {
333 | "display_name": "Python 3.9.7 ('jsbsim')",
334 | "language": "python",
335 | "name": "python3"
336 | },
337 | "language_info": {
338 | "codemirror_mode": {
339 | "name": "ipython",
340 | "version": 3
341 | },
342 | "file_extension": ".py",
343 | "mimetype": "text/x-python",
344 | "name": "python",
345 | "nbconvert_exporter": "python",
346 | "pygments_lexer": "ipython3",
347 | "version": "3.9.16"
348 | },
349 | "orig_nbformat": 4,
350 | "vscode": {
351 | "interpreter": {
352 | "hash": "663e50e37becc1d53f39f342eec15d3c22c0771839e1c61f0c2308195e9fefc0"
353 | }
354 | }
355 | },
356 | "nbformat": 4,
357 | "nbformat_minor": 2
358 | }
359 |
--------------------------------------------------------------------------------
/src/environments/jsbsimEnv/jsbsimEnv.py:
--------------------------------------------------------------------------------
1 | import random
2 | import re
3 | import sys
4 | import time
5 |
6 | import gym
7 | import numpy as np
8 | from gym import Env
9 | from gym.spaces import Box, Discrete
10 |
11 | sys.path.append('./src/environments/jsbsimEnv')
12 | try:
13 | from gym.envs.jsbsimEnv.jsbsimFdm import JsbsimFdm as Fdm
14 | print("Gym")
15 | time.sleep(2)
16 | except:
17 | from jsbsimFdm import JsbsimFdm as Fdm
18 | print("DBRL")
19 | time.sleep(2)
20 |
21 |
22 | class JsbsimEnv(Env):
23 |
24 | def __init__(
25 | self,
26 | fdm1=Fdm(
27 | fdm_id=1,
28 | fdm_fgfs=True
29 | ),
30 | fdm2=Fdm(
31 | fdm_id=2,
32 | fdm_ic_lat=.003,
33 | fdm_ic_psi=0,
34 | fdm_fgfs=True
35 | ),
36 | policy2='Level',
37 | ) -> None:
38 | super(JsbsimEnv, self).__init__()
39 |
40 | self.fdm1 = fdm1
41 | self.fdm2 = fdm2
42 |
43 | self.param1 = self.fdm1.param
44 | self.param2 = self.fdm2.param
45 |
46 | self.policy2 = policy2
47 |
48 | self.action_space = Box(
49 | low=np.array([
50 | -1, # Aileron 副翼
51 | -1, # Elevator 升降舵
52 | -1, # Rudder 方向舵
53 | 0, # Throttle 油门
54 | 0, # Flap 襟翼
55 | 0, # Speed brake 减速板
56 | 0, # Spoiler 扰流片
57 | ]),
58 | high=np.array([
59 | 1,
60 | 1,
61 | 1,
62 | 1,
63 | 1,
64 | 1,
65 | 1,
66 | ]),
67 | )
68 |
69 | self.observation_space = Box(
70 | low=np.array([
71 | -500, # Latitude 纬度
72 | -500, # Longitude 经度
73 | -500, # Height above sea level 海拔
74 | 0, # Yaw 偏航角
75 | -180, # Pitch 俯仰角
76 | -180, # Roll 翻滚角
77 | ] * 2),
78 | high=np.array([
79 | 500,
80 | 500,
81 | 500,
82 | 360,
83 | 180,
84 | 180,
85 | ] * 2),
86 | dtype=np.float64
87 | )
88 |
89 | def getDistanceVector(self, ego):
90 | positionEci1 = self.fdm1.getProperty("positionEci") # A list of size [3]
91 | positionEci2 = self.fdm2.getProperty("positionEci") # A list of size [3]
92 | if ego == 1:
93 | return np.array(positionEci2) - np.array(positionEci1)
94 | elif ego == 2:
95 | return np.array(positionEci1) - np.array(positionEci2)
96 | else:
97 | raise Exception("Plane {} doesn\'t exist".format(ego))
98 |
99 | def getDistance(self):
100 | assert abs(np.linalg.norm(self.getDistanceVector(1)) - np.linalg.norm(self.getDistanceVector(2))) < 1e-5
101 | return np.linalg.norm(self.getDistanceVector(1))
102 |
103 | def getDamage(self, ego):
104 | attitude1 = self.fdm1.getProperty("attitudeRad") # A list of size [3]
105 | attitude2 = self.fdm2.getProperty("attitudeRad") # A list of size [3]
106 |
107 | theta_1 = np.pi / 2 - attitude1[1]
108 | psi_1 = np.pi / 2 - attitude1[0]
109 | heading_1 = np.array([
110 | np.cos(theta_1),
111 | np.sin(theta_1) * np.cos(psi_1),
112 | np.sin(theta_1) * np.sin(psi_1),
113 | ])
114 |
115 | theta_2 = np.pi / 2 - attitude2[1]
116 | psi_2 = np.pi / 2 - attitude2[0]
117 | heading_2 = np.array([
118 | np.cos(theta_2),
119 | np.sin(theta_2) * np.cos(psi_2),
120 | np.sin(theta_2) * np.sin(psi_2),
121 | ])
122 |
123 | if 500 <= self.getDistance() <= 3000:
124 |
125 | angle1 = np.arccos(
126 | np.dot(self.getDistanceVector(ego=1), heading_1) /
127 | (self.getDistance() * np.linalg.norm(heading_1))
128 | )
129 |
130 | if -1 <= angle1 / np.pi * 180 <= 1:
131 | if ego == 2:
132 | return (3000 - self.getDistance()) / 2500 / 120
133 |
134 | angle2 = np.arccos(
135 | np.dot(self.getDistanceVector(ego=2), heading_2) /
136 | (self.getDistance() * np.linalg.norm(heading_2))
137 | )
138 |
139 | if -1 <= angle2 / np.pi * 180 <= 1:
140 | if ego == 1:
141 | return (3000 - self.getDistance()) / 2500 / 120
142 |
143 | return 0
144 |
145 | def damage(self):
146 | self.fdm1.damage(self.getDamage(1))
147 | self.fdm2.damage(self.getDamage(2))
148 |
149 | def reward(self):
150 |
151 | # sparse reward
152 | if self.terminate() == 2:
153 | r = -10
154 | elif self.terminate() == 1:
155 | r = 10
156 | elif self.terminate() == -1:
157 | r = -0.001
158 | else:
159 | r = -0.001 + (self.getDamage(2) - self.getDamage(1)) * 100
160 |
161 | return r
162 |
163 | def terminate(self):
164 |
165 | if self.fdm1.getProperty('position')[2] <= 10000: # Height protection
166 | return 2
167 |
168 | if self.fdm2.getProperty('position')[2] <= 10000:
169 | return 1
170 |
171 | if self.fdm1.nof >= 2000: # Tied
172 | return -1
173 |
174 | if self.fdm1.fdm_hp <= 0 and self.fdm2.fdm_hp > 0:
175 | return 2
176 | elif self.fdm2.fdm_hp <= 0 and self.fdm1.fdm_hp > 0:
177 | return 1
178 | elif self.fdm1.fdm_hp <= 0 and self.fdm2.fdm_hp <= 0:
179 | return -1
180 | else:
181 | return 0
182 |
183 | def step(self, action):
184 |
185 | self.fdm1.sendAction(action)
186 |
187 | if self.policy2 == 'Level':
188 | self.fdm2.sendAction( # level flight
189 | action=-np.tanh((0 - self.fdm2.getProperty('attitudeDeg')[1]) * 1.5),
190 | actionType='fcs/elevator-cmd-norm'
191 | )
192 | elif self.policy2 == 'Random':
193 | self.fdm2.sendAction(
194 | self.action_space.sample()
195 | )
196 |
197 | self.damage()
198 |
199 | self.fdm1.step()
200 | self.fdm2.step()
201 |
202 | terminate = True if self.terminate() else False
203 |
204 | ob = np.array([ # ~100
205 | (self.fdm1.getProperty('position')[0] - self.fdm1.param['fdm_ic_lat']) * 1e4,
206 | (self.fdm1.getProperty('position')[1] - self.fdm1.param['fdm_ic_long']) * 1e4,
207 | (self.fdm1.getProperty('position')[2] - self.fdm1.param['fdm_ic_h']) / 10,
208 | (self.fdm1.getProperty('attitudeDeg')[0] - self.fdm1.param['fdm_ic_psi']),
209 | (self.fdm1.getProperty('attitudeDeg')[1] - self.fdm1.param['fdm_ic_theta']),
210 | (self.fdm1.getProperty('attitudeDeg')[2] - self.fdm1.param['fdm_ic_phi']),
211 | (self.fdm2.getProperty('position')[0] - self.fdm2.param['fdm_ic_lat']) * 1e4,
212 | (self.fdm2.getProperty('position')[1] - self.fdm2.param['fdm_ic_long']) * 1e4,
213 | (self.fdm2.getProperty('position')[2] - self.fdm2.param['fdm_ic_h']) / 10,
214 | (self.fdm2.getProperty('attitudeDeg')[0] - self.fdm2.param['fdm_ic_psi']),
215 | (self.fdm2.getProperty('attitudeDeg')[1] - self.fdm2.param['fdm_ic_theta']),
216 | (self.fdm2.getProperty('attitudeDeg')[2] - self.fdm2.param['fdm_ic_phi']),
217 | ])
218 |
219 | return ob, self.reward(), terminate, {}
220 |
221 | def render(self, id=0):
222 |
223 | if id == 1 or id == 0:
224 | self.fdm1.set_output_directive('./data_output/flightgear1.xml')
225 |
226 | if id == 2 or id == 0:
227 | self.fdm2.set_output_directive('./data_output/flightgear2.xml')
228 |
229 | def reset(self):
230 |
231 | self.fdm1 = Fdm(
232 | fdm_id=self.param1['fdm_id'],
233 | fdm_aircraft=self.param1['fdm_aircraft'],
234 | fdm_ic_v=self.param1['fdm_ic_v'],
235 | fdm_ic_lat=self.param1['fdm_ic_lat'],
236 | fdm_ic_long=self.param1['fdm_ic_long'],
237 | fdm_ic_h=self.param1['fdm_ic_h'],
238 | fdm_ic_psi=self.param1['fdm_ic_psi'],
239 | fdm_ic_theta=self.param1['fdm_ic_theta'],
240 | fdm_ic_phi=self.param1['fdm_ic_phi'],
241 | fdm_hp=self.param1['fdm_hp'],
242 | fdm_fgfs=self.param1['fdm_fgfs'],
243 | flight_mode=self.param1['flight_mode'],
244 | )
245 |
246 | self.fdm2 = Fdm(
247 | fdm_id=self.param2['fdm_id'],
248 | fdm_aircraft=self.param2['fdm_aircraft'],
249 | fdm_ic_v=self.param2['fdm_ic_v'],
250 | fdm_ic_lat=self.param2['fdm_ic_lat'],
251 | fdm_ic_long=self.param2['fdm_ic_long'],
252 | fdm_ic_h=self.param2['fdm_ic_h'],
253 | fdm_ic_psi=self.param2['fdm_ic_psi'],
254 | fdm_ic_theta=self.param2['fdm_ic_theta'],
255 | fdm_ic_phi=self.param2['fdm_ic_phi'],
256 | fdm_hp=self.param2['fdm_hp'],
257 | fdm_fgfs=self.param2['fdm_fgfs'],
258 | flight_mode=self.param2['flight_mode'],
259 | )
260 |
261 | ob = np.array([ # ~100
262 | (self.fdm1.getProperty('position')[0] - self.fdm1.param['fdm_ic_lat']) * 1e4,
263 | (self.fdm1.getProperty('position')[1] - self.fdm1.param['fdm_ic_long']) * 1e4,
264 | (self.fdm1.getProperty('position')[2] - self.fdm1.param['fdm_ic_h']) / 10,
265 | (self.fdm1.getProperty('attitudeDeg')[0] - self.fdm1.param['fdm_ic_psi']),
266 | (self.fdm1.getProperty('attitudeDeg')[1] - self.fdm1.param['fdm_ic_theta']),
267 | (self.fdm1.getProperty('attitudeDeg')[2] - self.fdm1.param['fdm_ic_phi']),
268 | (self.fdm2.getProperty('position')[0] - self.fdm2.param['fdm_ic_lat']) * 1e4,
269 | (self.fdm2.getProperty('position')[1] - self.fdm2.param['fdm_ic_long']) * 1e4,
270 | (self.fdm2.getProperty('position')[2] - self.fdm2.param['fdm_ic_h']) / 10,
271 | (self.fdm2.getProperty('attitudeDeg')[0] - self.fdm2.param['fdm_ic_psi']),
272 | (self.fdm2.getProperty('attitudeDeg')[1] - self.fdm2.param['fdm_ic_theta']),
273 | (self.fdm2.getProperty('attitudeDeg')[2] - self.fdm2.param['fdm_ic_phi']),
274 | ])
275 |
276 | return ob
277 |
278 |
--------------------------------------------------------------------------------
/doc/QuickStart_en.md:
--------------------------------------------------------------------------------
1 |
2 |
DBRL: A Gym Dogfighting Simulation Benchmark for Reinforcement Learning Research
3 |
4 |
5 |
6 |
7 | ## Quick Start
8 |
9 |
10 | Install
11 |
12 | DBRL is an air combat simulation benchmark based on JSBSim and Dogfight 2.
13 |
14 | If you want to run with JSBSim dogfight environment, please run the code below to install JSBSim.
15 |
16 | ```bash
17 | pip install jsbsim
18 | ```
19 |
20 | Dogfight 2 environment could only run in Windows OS. If you want to run reinforcement learning methods with Dogfight 2 missile-avoiding environment, please run the code below in Windows PowerShell.
21 |
22 | ```bash
23 | wget -Uri https://github.com/harfang3d/dogfight-sandbox-hg2/releases/download/1.0.2/dogfight-sandbox-hg2-win64.7z -OutFile "df2.7z"
24 |
25 | 7z x -odf2/ df2.7z
26 |
27 | mv ./df2/dogfight-sandbox-hg2/ {DBRL}/src/environments/dogfightEnv/dogfight_sandbox_hg2/
28 |
29 | del df2/, df2.7z
30 | ```
31 | Replace `{DBRL}` with the path of this project.
32 |
33 | > If you want to use 7z command in the command line, add your 7zip path to environment variables *Path* on Windows. Alternatively, you can also unzip the folder manually.
34 |
35 |
36 |
37 | If you want to visualize the aircraft model in FlightGear while running the FDM with the JSBSim executable, please download FlightGear following the instructions on the FlightGear website. If you need to visualize the two aircrafts in an engagement simultaneously, please copy two `{JSBSim}/data_output/flightgear.xml`, name them `flightgear{1/2}.xml`, and replace `5550` in line 18 of `flightgear2.xml` to `5551`. `{JSBSim}` represents the path of Python JSBSim. You could run the code below to get the default JSBSim path.
38 |
39 | ```python
40 | import jsbsim
41 |
42 | print(jsbsim.get_default_root_dir())
43 | ```
44 |
45 | Run FlightGear with the attributes below.
46 |
47 | ```bash
48 | --fdm=null --native-fdm=socket,in,60,,5550,udp
49 | ```
50 |
51 | If you want to visualize two aircraft at the same time, use the attributes below in two FlightGear separately.
52 |
53 | ```bash
54 | --fdm=null --native-fdm=socket,in,60,,5550,udp --multiplay=out,10,127.0.0.1,5000 --multiplay=in,10,127.0.0.1,5001 --callsign=Test1
55 | ```
56 |
57 | ```bash
58 | --fdm=null --native-fdm=socket,in,60,,5550,udp --multiplay=out,10,127.0.0.1,5001 --multiplay=in,10,127.0.0.1,5000 --callsign=Test2
59 | ```
60 |
61 | If you want to run the reinforcement learning methods in `src/models/*.py`, please put this project in `JSBSim/`.
62 |
63 | ```
64 | JSBSim/
65 | └── DBRL/
66 | ├── doc/
67 | ├── log/
68 | ├── src/
69 | ├── test/
70 | └── ...
71 | ```
72 |
73 |
74 |
75 |
76 |
77 | Adopting
78 |
79 | DBRL builds the reinforcement learning environment in OpenAI Gym form. You could use `gym.make` to build the environment with the following register.
80 |
81 | ```python
82 | register(
83 | id="DBRL{Jsbsim/Dogfight}-v0",
84 | entry_point="gym.envs.{jsbsim/dogfight}Env:{Jsbsim/Dogfight}Env",
85 | )
86 | ```
87 |
88 | Use the code below to get the path of Gym.
89 |
90 | ```bash
91 | pip show gym
92 | ```
93 |
94 | After that, you could use DBRL environments with Gym in the way like following.
95 |
96 | ```python
97 | import gym
98 |
99 | env = gym.make('DBRL{Jsbsim/Dogfight}-v0')
100 | ```
101 |
102 |
109 |
110 |
111 |
112 |
113 |
114 |
115 | Properties
116 |
117 | The action space of DBRL-JSBSim is:
118 |
119 | ```python
120 | gym.spaces.Box(
121 | low=np.array([-1, -1, -1, 0, 0, 0, 0]),
122 | high=np.array([1, 1, 1, 1, 1, 1, 1])
123 | )
124 | ```
125 | which represents the control of aileron, elevator, rudder, throttle, flap, speed brake and spoiler.
126 |
127 | The observation space is:
128 |
129 | ```python
130 | gym.spaces.Box(
131 | low=np.array([-360, -360, 0, -360, -360, -360] * 2),
132 | high=np.array([360, 360, 60000, 360, 360, 360] * 2)
133 | )
134 | ```
135 | which represents the latitude(degree), longitude(degree), height above sea level(feet), yaw(degree), pitch(degree) and roll(degree) of the plane.
136 |
137 | Two dimensions of the action space and the observation space represents the two aircraft in an engagement.
138 |
139 | JSBSimEnv class's constructor function takes two JSBSimFdm class as input.
140 |
141 | ```python
142 | class JsbsimEnv(Env):
143 |
144 | def __init__(
145 | self,
146 | fdm1=Fdm(fdm_id=1),
147 | fdm2=Fdm(fdm_id=2),
148 | ) -> None:
149 | ```
150 |
151 | You could import JSBSimFdm class from `DBRL/src/environments/jsbsimEnv/jsbsimFdm.py`. It takes the arguments below as input.
152 |
153 | ```python
154 | class JsbsimFdm():
155 |
156 | def __init__(
157 | self,
158 | fdm_id = 1,
159 | fdm_aircraft = 'f16',
160 | fdm_ic_v = 500,
161 | fdm_ic_lat = 0,
162 | fdm_ic_long = 0,
163 | fdm_ic_h = 20005.5,
164 | fdm_ic_psi = 0,
165 | fdm_ic_theta = 0,
166 | fdm_ic_phi = 0,
167 | fdm_hp = 1,
168 | fdm_fgfs = False,
169 | flight_mode = 1
170 | ) -> None:
171 | ```
172 | `fdm_id` represents the number of aircraft, which needs to be set to `1` or `2`. `fdm_aircraft` represents the aircraft used in the engagement. `fdm_ic_v` represents the initial calibrated velocity. `fdm_ic_lat` represents the initial latitude. `fdm_ic_long` represents the initial longitude. `fdm_ic_h` represents the initial height. `fdm_ic_psi`, `fdm_ic_theta` and `fdm_ic_phi` represents the initial yaw, pitch and roll angle of the aircraft. `fdm_hp` represents the initial health point. `fdm_fgfs` denotes whether the aircraft will be visualized in FlightGear.
173 |
174 | The reward function of DBRL are set to be the damage ego plane takes to the opposite minus the opposite takes to the ego. Various reward function could be customized by `getProperty` function of class JSBSimFdm.
175 |
176 | ```python
177 | def getProperty(
178 | self,
179 | prop,
180 | ) -> list:
181 | ```
182 |
183 | ---
184 | ---
185 |
186 | DBRL-Dogfight simulate the situation when the plane is confronted with a missile. The agent needs to take actions to avoid the attack of the missile.
187 |
188 | The action space of DBRL-Dogfight is:
189 |
190 | ```python
191 | if pitch_enable:
192 | action_infimum = np.append(action_infimum, -1)
193 | action_supermum = np.append(action_supermum, 1)
194 | if roll_enable:
195 | action_infimum = np.append(action_infimum, -1)
196 | action_supermum = np.append(action_supermum, 1)
197 | if yaw_enable:
198 | action_infimum = np.append(action_infimum, -1)
199 | action_supermum = np.append(action_supermum, 1)
200 | if flaps_enable:
201 | action_infimum = np.append(action_infimum, 0)
202 | action_supermum = np.append(action_supermum, 1)
203 | if throttle_enable:
204 | action_infimum = np.append(action_infimum, 0)
205 | action_supermum = np.append(action_supermum, 1)
206 | if flare_enable:
207 | action_infimum = np.append(action_infimum, 0)
208 | action_supermum = np.append(action_supermum, 1)
209 | action = Box(
210 | low=action_infimum,
211 | high=action_supermum,
212 | )
213 | ```
214 | which is the control of pitch, roll, yaw, flaps, throttle and flare.
215 |
216 | The observation space is:
217 |
218 | ```python
219 | if ego_plane_position:
220 | observation_infimum = np.append(observation_infimum, [-300, -300, -1])
221 | observation_supermum = np.append(observation_supermum, [300, 300, 200])
222 | if ego_plane_attitude:
223 | observation_infimum = np.append(observation_infimum, [0, -360, -360])
224 | observation_supermum = np.append(observation_supermum, [360, 360, 360])
225 | if oppo_plane_position:
226 | observation_infimum = np.append(observation_infimum, [-300, -300, -1])
227 | observation_supermum = np.append(observation_supermum, [300, 300, 200])
228 | if oppo_plane_attitude:
229 | observation_infimum = np.append(observation_infimum, [0, -360, -360])
230 | observation_supermum = np.append(observation_supermum, [360, 360, 360])
231 | if missile_position:
232 | observation_infimum = np.append(observation_infimum, [-300, -300, -1])
233 | observation_supermum = np.append(observation_supermum, [300, 300, 200])
234 | if missile_attitude:
235 | observation_infimum = np.append(observation_infimum, [-315, -315, -315])
236 | observation_supermum = np.append(observation_supermum, [315, 315, 315])
237 | if missile_relative_azimuth:
238 | observation_infimum = np.append(observation_infimum, [-1, -1, -1])
239 | observation_supermum = np.append(observation_supermum, [1, 1, 1])
240 | observation = Box(
241 | low=observation_infimum,
242 | high=observation_supermum,
243 | )
244 | ```
245 | which represents the X-coordinate(÷100), Y-coordinate(÷100), Z-coordinate(÷50), heading, pitch(×4) and roll(×4) of the airplane, and the X-coordinate(÷100), Y-coordinate(÷100), Z-coordinate(÷50), heading(×100), pitch(×100) and roll(×100) of the missile, as well as the azimuth of the missil relative to the airplane.
246 |
247 | DogfightEnv needs to be connected to Dogfight 2 while simulating. Its constructor function takes host and port as input.
248 |
249 | ```python
250 | class DogfightEnv(Env):
251 |
252 | def __init__(
253 | self,
254 | host='10.184.0.0',
255 | port='50888',
256 | plane_slot=1,
257 | enemy_slot=3,
258 | missile_slot=1,
259 | rendering=False,
260 | record_status=0,
261 | initial_state='carrier',
262 | throttle_enable=False,
263 | flare_enable=False,
264 | ego_pose_enable=True,
265 | oppo_pose_enable=False,
266 | missile_pose_enable=True,
267 | missile_relative_azimuth_enable=False,
268 | msg=None,
269 | ) -> None:
270 | ```
271 |
272 | You need to start the Dogfight 2 at first and choose the Network mode mission. The host and port IP are in the upper left corner of the screen.
273 |
274 |
275 |
276 |
277 |
278 |
285 |
286 |
287 | ## Welcome PR
288 |
289 | If you find any mistakes while using, or have any suggestions and advices, please point it out in Github Issues. We're looking forward to your contributions, such as your air combat reinforcement learning models, to this dogfight benchmark. If you are interesting in this project, feel free to contact `mrwangyou@stu.xjtu.edu.cn`.
290 |
--------------------------------------------------------------------------------
/src/environments/dfEnv/dfEnv.py:
--------------------------------------------------------------------------------
1 | import random
2 | import re
3 | import sys
4 | import time
5 | import warnings
6 |
7 | import gym
8 | import numpy as np
9 | from gym import Env
10 | from gym.spaces import Box, Discrete
11 |
12 | sys.path.append('./src/')
13 | sys.path.append('./src/environments/dfEnv/')
14 | sys.path.append('./src/environments/dfEnv/dogfight_sandbox_hg2/network_client_example/')
15 | sys.path.append('gym.envs.dfEnv.dogfight_sandbox_hg2.network_client_example/')
16 |
17 |
18 | try:
19 | from gym.envs.dogfightEnv.dogfight_sandbox_hg2.network_client_example import \
20 | dogfight_client as df
21 | print("Gym")
22 | time.sleep(1)
23 | except:
24 | from dogfightEnv.dogfight_sandbox_hg2.network_client_example import \
25 | dogfight_client as df
26 | print("DBRL")
27 | time.sleep(1)
28 |
29 |
30 | class DfEnv(Env):
31 |
32 | def __init__(
33 | self,
34 | host='10.184.0.0',
35 | port='50888',
36 | plane_slot=1,
37 | enemy_slot=3,
38 | rendering=False,
39 | ) -> None:
40 |
41 | self.host = host
42 | self.port = port
43 | self.nof = 0
44 | self.rendering = rendering
45 | self.plane_slot = plane_slot
46 | self.enemy_slot = enemy_slot
47 |
48 |
49 | try:
50 | planes = df.get_planes_list()
51 | except AttributeError:
52 | print('Run for the first time')
53 | df.connect(host, int(port))
54 | time.sleep(2)
55 | planes = df.get_planes_list()
56 |
57 | df.disable_log()
58 |
59 | self.planeID = planes[self.plane_slot]
60 | self.enemyID = planes[self.enemy_slot]
61 |
62 | for i in planes:
63 | df.reset_machine(i)
64 |
65 |
66 | df.set_plane_thrust(self.enemyID, 1)
67 | df.set_plane_thrust(self.planeID, 1)
68 | df.set_client_update_mode(True)
69 |
70 | df.set_renderless_mode(True)
71 |
72 | t = 0
73 | while t < 1:
74 | plane_state = df.get_plane_state(self.enemyID)
75 | df.update_scene()
76 | t = plane_state["thrust_level"]
77 |
78 | df.activate_post_combustion(self.enemyID)
79 | df.activate_post_combustion(self.planeID)
80 |
81 | df.set_plane_pitch(self.enemyID, -0.5)
82 | df.set_plane_pitch(self.planeID, -0.5)
83 |
84 | p = 0
85 | while p < 15:
86 | plane_state = df.get_plane_state(self.enemyID)
87 | df.update_scene()
88 | p = plane_state["pitch_attitude"]
89 |
90 | df.stabilize_plane(self.enemyID)
91 | df.stabilize_plane(self.planeID)
92 |
93 | df.retract_gear(self.enemyID)
94 | df.retract_gear(self.planeID)
95 |
96 | s = 0
97 | while s < 1000:
98 | plane_state = df.get_plane_state(self.enemyID)
99 | df.update_scene()
100 | s = plane_state["altitude"]
101 |
102 | df.set_target_id(self.enemyID, self.planeID)
103 | df.activate_IA(self.enemyID)
104 |
105 | self.action_space = Box( # Same as Evade mode
106 | low=np.array([
107 | 0, # Flaps 襟翼
108 | -1, # Pitch 俯仰角
109 | -1, # Roll 翻滚角
110 | -1, # Yaw 偏航角
111 | ]),
112 | high=np.array([
113 | 1,
114 | 1,
115 | 1,
116 | 1,
117 | ]),
118 | )
119 |
120 | self.observation_space = Box(
121 | low=np.array([ # simple normalized
122 | # ego
123 | -300, # x / 100
124 | -300, # y / 100
125 | -1, # z / 50
126 | 0, # heading
127 | -360, # pitch_attitude * 4
128 | -360, # roll_attitude * 4
129 | # oppo
130 | -300, # x / 100
131 | -300, # y / 100
132 | -1, # z / 50
133 | 0, # heading
134 | -360, # pitch_attitude * 4
135 | -360, # roll_attitude * 4
136 | ]),
137 | high=np.array([
138 | 300,
139 | 300,
140 | 200,
141 | 360,
142 | 360,
143 | 360,
144 | 300,
145 | 300,
146 | 200,
147 | 360,
148 | 360,
149 | 360,
150 | ]),
151 | dtype=np.float64
152 | )
153 |
154 | if self.rendering:
155 | df.set_renderless_mode(False)
156 |
157 | def getProperty(
158 | self,
159 | prop
160 | ):
161 | if prop == 'position':
162 | return [
163 | df.get_plane_state(self.planeID)['position'][0],
164 | df.get_plane_state(self.planeID)['position'][2],
165 | df.get_plane_state(self.planeID)['position'][1],
166 | ]
167 | elif prop == 'positionEnemy':
168 | return [
169 | df.get_plane_state(self.enemyID)['position'][0],
170 | df.get_plane_state(self.enemyID)['position'][2],
171 | df.get_plane_state(self.enemyID)['position'][1],
172 | ]
173 | elif prop == 'attitudeRad':
174 | return [
175 | df.get_plane_state(self.planeID)['heading'] / 180 * np.pi,
176 | df.get_plane_state(self.planeID)['pitch_attitude'] / 180 * np.pi,
177 | df.get_plane_state(self.planeID)['roll_attitude'] / 180 * np.pi,
178 | ]
179 | elif prop == 'attitudeRadEnemy':
180 | return [
181 | df.get_plane_state(self.enemyID)['heading'] / 180 * np.pi,
182 | df.get_plane_state(self.enemyID)['pitch_attitude'] / 180 * np.pi,
183 | df.get_plane_state(self.enemyID)['roll_attitude'] / 180 * np.pi,
184 | ]
185 | elif prop == 'attitudeDeg':
186 | return [
187 | df.get_plane_state(self.planeID)['heading'],
188 | df.get_plane_state(self.planeID)['pitch_attitude'],
189 | df.get_plane_state(self.planeID)['roll_attitude'],
190 | ]
191 | elif prop == 'attitudeDegEnemy':
192 | return [
193 | df.get_plane_state(self.enemyID)['heading'],
194 | df.get_plane_state(self.enemyID)['pitch_attitude'],
195 | df.get_plane_state(self.enemyID)['roll_attitude'],
196 | ]
197 | elif prop == 'pose':
198 | return [
199 | df.get_plane_state(self.planeID)['position'][0],
200 | df.get_plane_state(self.planeID)['position'][2],
201 | df.get_plane_state(self.planeID)['position'][1],
202 | df.get_plane_state(self.planeID)['heading'],
203 | df.get_plane_state(self.planeID)['pitch_attitude'],
204 | df.get_plane_state(self.planeID)['roll_attitude'],
205 | ]
206 | elif prop == 'poseEnemy':
207 | return [
208 | df.get_plane_state(self.enemyID)['position'][0],
209 | df.get_plane_state(self.enemyID)['position'][2],
210 | df.get_plane_state(self.enemyID)['position'][1],
211 | df.get_plane_state(self.enemyID)['heading'],
212 | df.get_plane_state(self.enemyID)['pitch_attitude'],
213 | df.get_plane_state(self.enemyID)['roll_attitude'],
214 | ]
215 | elif prop == 'velocity':
216 | warnings.warn('三个值为速度在欧拉角上的分量, 与JSBSim中的速度不同')
217 | return [
218 | df.get_plane_state(self.planeID)['horizontal_speed'],
219 | df.get_plane_state(self.planeID)['linear_speed'],
220 | -df.get_plane_state(self.planeID)['vertical_speed'],
221 | ]
222 | elif prop == 'velocityEnemy':
223 | warnings.warn('三个值为速度在欧拉角上的分量, 与JSBSim中的速度不同')
224 | return [
225 | df.get_plane_state(self.enemyID)['horizontal_speed'],
226 | df.get_plane_state(self.enemyID)['linear_speed'],
227 | -df.get_plane_state(self.enemyID)['vertical_speed'],
228 | ]
229 | else:
230 | raise Exception("Property {} doesn't exist!".format(prop))
231 |
232 | def getDistanceVector(self, ego):
233 |
234 | positionEci1 = self.getProperty("position")
235 | positionEci2 = self.getProperty("positionEnemy")
236 |
237 | if ego == 1:
238 | return np.array(positionEci2) - np.array(positionEci1)
239 | elif ego == 2:
240 | return np.array(positionEci1) - np.array(positionEci2)
241 | else:
242 | raise Exception("Plane {} doesn\'t exist".format(ego))
243 |
244 | def getDistance(self):
245 | plane_state = df.get_plane_state(self.planeID)
246 | enemy_state = df.get_plane_state(self.enemyID)
247 |
248 | tmp1 = ((plane_state['position'][0] - enemy_state['position'][0]) ** 2 +\
249 | (plane_state['position'][1] - enemy_state['position'][1]) ** 2 +\
250 | (plane_state['position'][2] - enemy_state['position'][2]) ** 2) ** .5
251 |
252 | tmp2 = np.linalg.norm(self.getDistanceVector(1))
253 | tmp3 = np.linalg.norm(self.getDistanceVector(2))
254 | try:
255 | assert abs(tmp1 - tmp2) < 1, [tmp1, tmp2, abs(tmp1 - tmp2), abs(tmp1 - tmp2) < 1]
256 | assert abs(tmp3 - tmp2) < 1, [tmp3, tmp2, abs(tmp3 - tmp2), abs(tmp3 - tmp2) < 1]
257 | except:
258 | warnings.warn('飞机之间距离计算误差较大!')
259 |
260 | return tmp2
261 |
262 | def getDamage(self, ego):
263 | attitude1 = self.getProperty("attitudeRad")
264 | attitude2 = self.getProperty("attitudeRadEnemy")
265 |
266 | theta_1 = np.pi / 2 - attitude1[1]
267 | psi_1 = np.pi * 2 - ((attitude1[0] - np.pi / 2) % (np.pi * 2))
268 | heading_1 = np.array([
269 | np.sin(theta_1) * np.cos(psi_1),
270 | np.sin(theta_1) * np.sin(psi_1),
271 | np.cos(theta_1)
272 | ])
273 |
274 | theta_2 = np.pi / 2 - attitude2[1]
275 | psi_2 = np.pi * 2 - (attitude2[0] - np.pi / 2) % (np.pi * 2)
276 | heading_2 = np.array([
277 | np.sin(theta_2) * np.cos(psi_2),
278 | np.sin(theta_2) * np.sin(psi_2),
279 | np.cos(theta_2)
280 | ])
281 |
282 | if 500 <= self.getDistance() <= 3000:
283 |
284 | angle1 = np.arccos(
285 | np.dot(self.getDistanceVector(ego=1), heading_1) /
286 | (self.getDistance() * np.linalg.norm(heading_1))
287 | )
288 |
289 | if -1 <= angle1 / np.pi * 180 <= 1:
290 | if ego == 2:
291 | return (3000 - self.getDistance()) / 2500 / 120
292 |
293 | angle2 = np.arccos(
294 | np.dot(self.getDistanceVector(ego=2), heading_2) /
295 | (self.getDistance() * np.linalg.norm(heading_2))
296 | )
297 |
298 | if -1 <= angle2 / np.pi * 180 <= 1:
299 | if ego == 1:
300 | return (3000 - self.getDistance()) / 2500 / 120
301 |
302 | # print("angle1 {}\tangle2 {}".format(angle1 / np.pi * 180, angle2 / np.pi * 180))
303 |
304 | return 0
305 |
306 | def damage(self, ego):
307 | if ego == 1:
308 | df.set_health(self.planeID, df.get_health(self.planeID)['health_level'] - self.getDamage(1))
309 | if ego == 2:
310 | df.set_health(self.enemyID, df.get_health(self.enemyID)['health_level'] - self.getDamage(2))
311 |
312 | def getHP(self):
313 | return df.get_health(self.planeID)['health_level']
314 |
315 | def getHPEnemy(self):
316 | return df.get_health(self.enemyID)['health_level']
317 |
318 | def terminate(self):
319 | if self.getHP() <= 0.0 and self.getHPEnemy() > 0.0: # Oppo
320 | return -1
321 | elif self.getHP() > 0.0 and self.getHPEnemy() <= 0.0: # Ego
322 | return 1
323 | elif self.getHP() <= 0.0 and self.getHPEnemy() <= 0.0: # Tie
324 | return 2
325 | else:
326 | return 0
327 |
328 | def sendAction(
329 | self,
330 | action,
331 | actionType=None,
332 | ):
333 | if actionType == None:
334 | df.set_plane_flaps(self.planeID, float(action[0]))
335 | df.set_plane_pitch(self.planeID, float(action[1]))
336 | df.set_plane_roll(self.planeID, float(action[2]))
337 | df.set_plane_yaw(self.planeID, float(action[3]))
338 | elif actionType == 'Flaps' or actionType == 'flaps':
339 | df.set_plane_flaps(self.planeID, action)
340 | elif actionType == 'Pitch' or actionType == 'pitch':
341 | df.set_plane_pitch(self.planeID, action)
342 | elif actionType == 'Roll' or actionType == 'roll':
343 | df.set_plane_roll(self.planeID, action)
344 | elif actionType == 'Yaw' or actionType == 'yaw':
345 | df.set_plane_yaw(self.planeID, action)
346 |
347 | def step(self, action):
348 |
349 | t_begin = time.time()
350 |
351 | self.sendAction(action)
352 |
353 | df.update_scene()
354 | self.nof += 1
355 |
356 | # self.damage(1)
357 | self.damage(2)
358 |
359 | terminate = self.terminate()
360 |
361 | if terminate == 1:
362 | reward = 50
363 | elif terminate == -1:
364 | reward = 0
365 | elif terminate == 2:
366 | reward = 0
367 | else:
368 | reward = 0
369 |
370 | terminate = True if terminate else False
371 |
372 | ob = np.array([ # normalized
373 | df.get_plane_state(self.planeID)['position'][0] / 100,
374 | df.get_plane_state(self.planeID)['position'][2] / 100,
375 | df.get_plane_state(self.planeID)['position'][1] / 50,
376 | df.get_plane_state(self.planeID)['heading'],
377 | df.get_plane_state(self.planeID)['pitch_attitude'] * 4,
378 | df.get_plane_state(self.planeID)['roll_attitude'] * 4,
379 |
380 | df.get_plane_state(self.enemyID)['position'][0] / 100,
381 | df.get_plane_state(self.enemyID)['position'][2] / 100,
382 | df.get_plane_state(self.enemyID)['position'][1] / 50,
383 | df.get_plane_state(self.enemyID)['heading'],
384 | df.get_plane_state(self.enemyID)['pitch_attitude'] * 4,
385 | df.get_plane_state(self.enemyID)['roll_attitude'] * 4,
386 |
387 | ])
388 |
389 | if self.rendering:
390 | time.sleep(
391 | max(0, df.get_timestep()['timestep'] - (time.time() - t_begin))
392 | )
393 |
394 | return ob, reward, terminate, {}
395 |
396 | def render(self, id=0):
397 |
398 | df.set_renderless_mode(False)
399 |
400 |
401 | def reset(
402 | self,
403 | ):
404 | self.__init__(
405 | host=self.host,
406 | port=self.port,
407 | plane_slot=self.plane_slot,
408 | enemy_slot=self.enemy_slot,
409 | rendering=self.rendering,
410 | )
411 |
412 | ob = np.array([ # normalized
413 | df.get_plane_state(self.planeID)['position'][0] / 100,
414 | df.get_plane_state(self.planeID)['position'][2] / 100,
415 | df.get_plane_state(self.planeID)['position'][1] / 50,
416 | df.get_plane_state(self.planeID)['heading'],
417 | df.get_plane_state(self.planeID)['pitch_attitude'] * 4,
418 | df.get_plane_state(self.planeID)['roll_attitude'] * 4,
419 |
420 | df.get_plane_state(self.enemyID)['position'][0] / 100,
421 | df.get_plane_state(self.enemyID)['position'][2] / 100,
422 | df.get_plane_state(self.enemyID)['position'][1] / 50,
423 | df.get_plane_state(self.enemyID)['heading'],
424 | df.get_plane_state(self.enemyID)['pitch_attitude'] * 4,
425 | df.get_plane_state(self.enemyID)['roll_attitude'] * 4,
426 | ])
427 |
428 | return ob
429 |
430 |
--------------------------------------------------------------------------------
/src/environments/dogfightEnv/dogfightEnv.py:
--------------------------------------------------------------------------------
1 | import random
2 | import os
3 | import re
4 | import sys
5 | import time
6 | import warnings
7 | import math
8 |
9 | import gym
10 | import numpy as np
11 | from gym import Env
12 | from gym.spaces import Box, Discrete
13 | from pathlib import Path
14 |
15 |
16 | if re.findall('dogfightEnv\.py$', str(Path(__file__).resolve())) and \
17 | re.findall('dogfightEnv$', str(Path(__file__).resolve().parents[0])) and \
18 | re.findall('envs$', str(Path(__file__).resolve().parents[1])) and \
19 | re.findall('gym$', str(Path(__file__).resolve().parents[2])):
20 | print("Using Gym Version")
21 | time.sleep(1)
22 |
23 | elif re.findall('dogfightEnv\.py$', str(Path(__file__).resolve())) and \
24 | re.findall('dogfightEnv$', str(Path(__file__).resolve().parents[0])) and \
25 | re.findall('environments$', str(Path(__file__).resolve().parents[1])) and \
26 | re.findall('src$', str(Path(__file__).resolve().parents[2])) and \
27 | re.findall('DBRL$', str(Path(__file__).resolve().parents[3])):
28 | print("Using DBRL Version")
29 | time.sleep(1)
30 |
31 | sys.path.append(str(Path(__file__).resolve().parents[2]) + '/envs/dogfightEnv')
32 | # You can also replace `import socket_lib` in `dogfight_sandbox_hg2\network_client_example\dogfight_client.py` with `from . import socket_lib` to achieve the same function as the following line of code
33 | sys.path.append(str(Path(__file__).resolve().parents[2]) + '/envs/dogfightEnv/dogfight_sandbox_hg2/network_client_example/')
34 |
35 | from initialization import *
36 | from models import *
37 |
38 | try:
39 | from .dogfight_sandbox_hg2.network_client_example import \
40 | dogfight_client as df
41 | print("Gym")
42 | time.sleep(1)
43 | except:
44 | from .dogfight_sandbox_hg2.network_client_example import \
45 | dogfight_client as df
46 | print("DBRL")
47 | time.sleep(1)
48 |
49 |
50 | class DogfightEnv(Env):
51 |
52 | def __init__(
53 | self,
54 | host='10.184.0.0',
55 | port='50888',
56 | plane_slot=1,
57 | enemy_slot=3,
58 | missile_slot=1,
59 | rendering=False,
60 | record_status=0,
61 | initial_state='carrier',
62 | throttle_enable=False,
63 | flare_enable=False,
64 | ego_pose_enable=True,
65 | oppo_pose_enable=False,
66 | missile_pose_enable=True,
67 | missile_relative_azimuth_enable=False,
68 | msg=None,
69 | ) -> None:
70 |
71 | self.host = host
72 | self.port = port
73 | self.nof = 0
74 | self.rendering = rendering
75 | self.plane_slot = plane_slot
76 | self.enemy_slot = enemy_slot
77 | self.missile_slot = missile_slot
78 | self.record_status = record_status
79 | self.initial_state = initial_state
80 | self.throttle_enable = throttle_enable
81 | self.flare_enable = flare_enable
82 | self.flare_active = False
83 | self.ego_pose_enable = ego_pose_enable
84 | self.oppo_pose_enable = oppo_pose_enable
85 | self.missile_pose_enable = missile_pose_enable
86 | self.missile_relative_azimuth_enable = missile_relative_azimuth_enable
87 | self.msg = msg
88 |
89 | try:
90 | planes = df.get_planes_list()
91 | except:
92 | print('Connecting...')
93 | df.connect(host, int(port))
94 | time.sleep(2)
95 | planes = df.get_planes_list()
96 |
97 | df.disable_log()
98 |
99 | for i in planes:
100 | df.reset_machine(i)
101 |
102 | self.planeID = planes[self.plane_slot]
103 | self.enemyID = planes[self.enemy_slot]
104 |
105 | missiles = df.get_machine_missiles_list(self.enemyID)
106 | self.missileID = missiles[self.missile_slot]
107 |
108 | df.set_plane_thrust(self.enemyID, 1)
109 | df.set_plane_thrust(self.planeID, 1)
110 |
111 | df.set_client_update_mode(True)
112 |
113 | df.set_renderless_mode(True)
114 |
115 | if self.initial_state == 'carrier':
116 | start_on_carrier(
117 | df=df,
118 | planeID=self.planeID,
119 | enemyID=self.enemyID,
120 | missile_slot=self.missile_slot,
121 | missileID=self.missileID,
122 | )
123 | elif self.initial_state == 'air':
124 | start_in_sky(
125 | df=df,
126 | planeID=self.planeID,
127 | enemyID=self.enemyID,
128 | missile_slot=self.missile_slot,
129 | missileID=self.missileID,
130 | )
131 | else:
132 | raise Exception('Invalid initial state {}!'.format(self.initial_state))
133 |
134 | self.action_space, self.action_type = action_space(
135 | pitch_enable=True,
136 | roll_enable=True,
137 | yaw_enable=True,
138 | flaps_enable=True,
139 | throttle_enable=throttle_enable,
140 | flare_enable=flare_enable
141 | )
142 |
143 | self.observation_space = observation_space(
144 | ego_plane_position=ego_pose_enable,
145 | ego_plane_attitude=ego_pose_enable,
146 | oppo_plane_position=oppo_pose_enable,
147 | oppo_plane_attitude=oppo_pose_enable,
148 | missile_position=missile_pose_enable,
149 | missile_attitude=missile_pose_enable,
150 | missile_relative_azimuth=missile_relative_azimuth_enable,
151 | )
152 |
153 |
154 | if self.rendering:
155 | df.set_renderless_mode(False)
156 |
157 | def getProperty(
158 | self,
159 | prop
160 | ):
161 | if prop == 'position':
162 | plane_state = df.get_plane_state(self.planeID)
163 | return [
164 | plane_state['position'][0],
165 | plane_state['position'][2],
166 | plane_state['position'][1],
167 | ]
168 | elif prop == 'positionEci':
169 | warnings.warn('Dogfight simulation environments have no global data!')
170 | plane_state = df.get_plane_state(self.planeID)
171 | return [
172 | plane_state['position'][0],
173 | plane_state['position'][2],
174 | plane_state['position'][1],
175 | ]
176 | elif prop == 'positionEcef':
177 | warnings.warn('Dogfight simulation environments have no global data!')
178 | plane_state = df.get_plane_state(self.planeID)
179 | return [
180 | plane_state['position'][0],
181 | plane_state['position'][2],
182 | plane_state['position'][1],
183 | ]
184 | elif prop == 'attitudeRad':
185 | plane_state = df.get_plane_state(self.planeID)
186 | return [
187 | plane_state['heading'] / 180 * np.pi,
188 | plane_state['pitch_attitude'] / 180 * np.pi,
189 | plane_state['roll_attitude'] / 180 * np.pi,
190 | ]
191 | elif prop == 'attitudeDeg':
192 | plane_state = df.get_plane_state(self.planeID)
193 | return [
194 | plane_state['heading'],
195 | plane_state['pitch_attitude'],
196 | plane_state['roll_attitude'],
197 | ]
198 | elif prop == 'pose':
199 | plane_state = df.get_plane_state(self.planeID)
200 | return [
201 | plane_state['position'][0],
202 | plane_state['position'][2],
203 | plane_state['position'][1],
204 | plane_state['heading'],
205 | plane_state['pitch_attitude'],
206 | plane_state['roll_attitude'],
207 | ]
208 | elif prop == 'poseNorm':
209 | plane_state = df.get_plane_state(self.planeID)
210 | return [
211 | plane_state['position'][0] / 100,
212 | plane_state['position'][2] / 100,
213 | plane_state['position'][1] / 50,
214 | plane_state['heading'],
215 | plane_state['pitch_attitude'] * 4,
216 | plane_state['roll_attitude'] * 4,
217 | ]
218 | elif prop == 'velocity':
219 | warnings.warn('三个值为速度在欧拉角上的分量, 与JSBSim中的速度不同')
220 | plane_state = df.get_plane_state(self.planeID)
221 | return [
222 | plane_state['horizontal_speed'],
223 | plane_state['linear_speed'],
224 | -plane_state['vertical_speed'],
225 | ]
226 | elif prop == 'poseMissile':
227 | missile_state = df.get_missile_state(self.missileID)
228 | return [
229 | missile_state['position'][0],
230 | missile_state['position'][2],
231 | missile_state['position'][1],
232 | missile_state['Euler_angles'][0],
233 | missile_state['Euler_angles'][1],
234 | missile_state['Euler_angles'][2],
235 | ]
236 | elif prop == 'poseMissleNorm':
237 | missile_state = df.get_missile_state(self.missileID)
238 | return [
239 | missile_state['position'][0] / 100,
240 | missile_state['position'][2] / 100,
241 | missile_state['position'][1] / 50,
242 | missile_state['Euler_angles'][0] * 100,
243 | missile_state['Euler_angles'][1] * 100,
244 | missile_state['Euler_angles'][2] * 100,
245 | ]
246 | elif prop == 'azimuthRel':
247 | plane_state = df.get_plane_state(self.planeID)
248 | missile_state = df.get_missile_state(self.missileID)
249 | azimuth = [
250 | missile_state['position'][0] - plane_state['position'][0],
251 | missile_state['position'][2] - plane_state['position'][2],
252 | missile_state['position'][1] - plane_state['position'][1],
253 | ]
254 | azimuthRel = [0, 0, 0]
255 | for idx, value in enumerate(azimuth):
256 | azimuthRel[idx] = value / np.linalg.norm(azimuth)
257 | return azimuthRel
258 |
259 | else:
260 | raise Exception("Property {} doesn't exist!".format(prop))
261 |
262 | def getDistance(self):
263 | plane_state = df.get_plane_state(self.planeID)
264 | missile_state = df.get_missile_state(self.missileID)
265 |
266 | return ((plane_state['position'][0] - missile_state['position'][0]) ** 2 +\
267 | (plane_state['position'][1] - missile_state['position'][1]) ** 2 +\
268 | (plane_state['position'][2] - missile_state['position'][2]) ** 2) ** .5
269 |
270 | def getHP(self):
271 | return df.get_health(self.planeID)['health_level']
272 |
273 | def terminate(self):
274 | if not df.get_missile_state(self.missileID)['active']:
275 | if self.getHP() >= .9:
276 | return 1
277 | else:
278 | return -1
279 | else:
280 | return 0
281 |
282 | def setProperty(
283 | self,
284 | prop,
285 | value,
286 | ):
287 | if prop == 'plane':
288 | self.plane_slot = value
289 | elif prop == 'enemy':
290 | self.enemy_slot = value
291 | elif prop == 'missile':
292 | self.missile_slot = value
293 | else:
294 | raise Exception("Property {} doesn't exist!".format(prop))
295 |
296 | def sendAction(
297 | self,
298 | action,
299 | actionType=None,
300 | ):
301 | if actionType == None:
302 | for idx, value in enumerate(self.action_type):
303 | if value[0] == '_':
304 | eval(value[1:])(self.planeID, float(action[idx]))
305 |
306 | elif value == 'Flare' and not self.flare_active:
307 | if float(action[idx]) >= 0.99:
308 | self.flare_active = True
309 | self.setFlare()
310 |
311 | elif actionType == 'Flaps' or actionType == 'flaps':
312 | df.set_plane_flaps(self.planeID, action)
313 | elif actionType == 'Pitch' or actionType == 'pitch':
314 | df.set_plane_pitch(self.planeID, action)
315 | elif actionType == 'Roll' or actionType == 'roll':
316 | df.set_plane_roll(self.planeID, action)
317 | elif actionType == 'Yaw' or actionType == 'yaw':
318 | df.set_plane_yaw(self.planeID, action)
319 |
320 | def setFlare(
321 | self,
322 | ):
323 | planes = df.get_planes_list()
324 | plane_id = planes[self.plane_slot]
325 |
326 | missiles = df.get_machine_missiles_list(plane_id)
327 |
328 | self.flare_slot = 0
329 | self.flare_id = missiles[self.flare_slot]
330 |
331 | df.fire_missile(plane_id, self.flare_slot)
332 |
333 | df.set_machine_custom_physics_mode(self.flare_id, True)
334 |
335 | df.set_missile_life_delay(self.flare_id, 5)
336 |
337 | df.update_scene()
338 |
339 |
340 | flare_state = df.get_missile_state(self.flare_id)
341 | self.x, self.y, self.z = flare_state["position"][0], flare_state["position"][1], flare_state["position"][2]
342 | self.y_init = self.y
343 | self.z_init = self.z
344 | self.v_init = df.get_plane_state(planes[self.plane_slot])['linear_speed']
345 | self.w_init = df.get_plane_state(planes[self.plane_slot])['vertical_speed']
346 |
347 | self.flare_matrix = [
348 | 1, 0, 0,
349 | 0, 1, 0,
350 | 0, 0, 1,
351 | self.x, self.y, self.z,
352 | ]
353 |
354 | # Linear displacement vector in m.s-1
355 | self.flare_speed_vector = [1, 1, 1]
356 |
357 | # Custom missile movements
358 | self.flare_active_time = 0
359 |
360 | def flare_step(
361 | self,
362 | ):
363 | frame_time_step = 1/60
364 |
365 | flare_state = df.get_missile_state(self.flare_id)
366 | if not flare_state["wreck"]:
367 | self.flare_matrix[9] = self.x
368 | self.flare_matrix[10] = self.y
369 | self.flare_matrix[11] = self.z
370 |
371 | df.update_machine_kinetics(self.flare_id, self.flare_matrix, self.flare_speed_vector)
372 | df.update_scene()
373 | self.x = self.x
374 | self.y = self.y_init + self.w_init * self.flare_active_time - 0.5 * 10 * self.flare_active_time * self.flare_active_time
375 | self.z = self.z_init + self.v_init * self.flare_active_time
376 |
377 | # Compute speed vector, used by missile engine smoke
378 | self.flare_speed_vector = [(self.x-self.flare_matrix[9]) / frame_time_step, (self.y - self.flare_matrix[10]) / frame_time_step, (self.z - self.flare_matrix[11]) / frame_time_step]
379 |
380 | self.flare_active_time += frame_time_step
381 |
382 | if df.get_missile_state(self.flare_id)['active']:
383 |
384 | if random.random() < .5:
385 | df.set_missile_target(self.missileID, self.planeID)
386 | else:
387 | df.set_missile_target(self.missileID, self.flare_id)
388 |
389 | else:
390 | df.set_missile_target(self.missileID, self.planeID)
391 |
392 | def XYtoGPS(self, x, y, ref_lat=0, ref_lon=0):
393 | CONSTANTS_RADIUS_OF_EARTH = 6378137. # meters (m)
394 | x_rad = float(x) / CONSTANTS_RADIUS_OF_EARTH
395 | y_rad = float(y) / CONSTANTS_RADIUS_OF_EARTH
396 | c = math.sqrt(x_rad * x_rad + y_rad * y_rad)
397 |
398 | ref_lat_rad = math.radians(ref_lat)
399 | ref_lon_rad = math.radians(ref_lon)
400 |
401 | ref_sin_lat = math.sin(ref_lat_rad)
402 | ref_cos_lat = math.cos(ref_lat_rad)
403 |
404 | if abs(c) > 0:
405 | sin_c = math.sin(c)
406 | cos_c = math.cos(c)
407 |
408 | lat_rad = math.asin(cos_c * ref_sin_lat + (x_rad * sin_c * ref_cos_lat) / c)
409 | lon_rad = (ref_lon_rad + math.atan2(y_rad * sin_c, c * ref_cos_lat * cos_c - x_rad * ref_sin_lat * sin_c))
410 |
411 | lat = math.degrees(lat_rad)
412 | lon = math.degrees(lon_rad)
413 |
414 | else:
415 | lat = math.degrees(ref_lat)
416 | lon = math.degrees(ref_lon)
417 |
418 | return lat, lon
419 |
420 | def getObservation(self):
421 | ob = np.array([])
422 | if self.ego_pose_enable:
423 | ob = np.append(ob, self.getProperty('poseNorm'))
424 | if self.oppo_pose_enable:
425 | pass
426 | if self.missile_pose_enable:
427 | ob = np.append(ob, self.getProperty('poseMissleNorm'))
428 | if self.missile_relative_azimuth_enable:
429 | ob = np.append(ob, self.getProperty('azimuthRel'))
430 |
431 | return ob
432 |
433 | def step(self, action):
434 |
435 | t_begin = time.time()
436 |
437 | self.sendAction(action)
438 |
439 | if self.flare_enable and self.flare_active:
440 | self.flare_step()
441 |
442 | df.update_scene()
443 | self.nof += 1
444 |
445 | if self.record_status:
446 | if self.nof == 1:
447 | path = r'./log/'
448 | if not os.path.exists(path):
449 | os.mkdir(path)
450 | file = open('./log/{}_status_record.txt'.format(self.msg), 'w')
451 | file.write('FileType=text/acmi/tacview\nFileVersion=2.1\n0,ReferenceTime=2022-10-01T00:00:00Z\n0,Title = test simple aircraft\n1000000,T=160.123456|24.8976763|0, Type=Ground+Static+Building, Name=Competition, EngagementRange=30000\n')
452 | else:
453 | file = open('./log/{}_status_record.txt'.format(self.msg), 'a')
454 | file.write('#{}\n'.format(self.nof * df.get_timestep()['timestep']))
455 | if self.nof == 1:
456 | initMsg = ',Name=F16,Type=Air+FixedWing,Coalition=Enemies,Color=Blue,Mach=0.800,ShortName=F16,RadarMode=1,RadarRange=40000,RadarHorizontalBeamwidth=60,RadarVerticalBeamwidth=60'
457 | else:
458 | initMsg = ''
459 | file.write("1,T={0[0]}|{0[1]}|{1}{2}\n".format(
460 | self.XYtoGPS(self.getProperty('position')[0], self.getProperty('position')[1]),
461 | self.getProperty('position')[2],
462 | initMsg
463 | )
464 | )
465 | file.close()
466 |
467 | terminate = self.terminate()
468 |
469 | if terminate == 1:
470 | reward = 50
471 | elif terminate == -1:
472 | reward = -50
473 | else:
474 | reward = .1
475 | if self.getHP() <= .1:
476 | reward = -1
477 |
478 | terminate = True if terminate else False
479 |
480 | # plane_state = df.get_plane_state(self.planeID)
481 | # missile_state = df.get_missile_state(self.missileID)
482 |
483 | ob = self.getObservation()
484 |
485 | if self.rendering:
486 | time.sleep(
487 | max(0, df.get_timestep()['timestep'] - (time.time() - t_begin))
488 | )
489 |
490 | return ob, reward, terminate, {}
491 |
492 | def render(self, id=0):
493 |
494 | df.set_renderless_mode(False)
495 |
496 |
497 | def reset(
498 | self,
499 | ):
500 |
501 | self.__init__(
502 | host=self.host,
503 | port=self.port,
504 | plane_slot=self.plane_slot,
505 | enemy_slot=self.enemy_slot,
506 | missile_slot=self.missile_slot,
507 | rendering=self.rendering,
508 | record_status=self.record_status,
509 | initial_state=self.initial_state,
510 | throttle_enable=self.throttle_enable,
511 | flare_enable=self.flare_enable,
512 | ego_pose_enable = self.ego_pose_enable,
513 | oppo_pose_enable = self.oppo_pose_enable,
514 | missile_pose_enable = self.missile_pose_enable,
515 | missile_relative_azimuth_enable = self.missile_relative_azimuth_enable,
516 | msg=self.msg
517 | )
518 |
519 | ob = self.getObservation()
520 |
521 | return ob
522 |
523 |
--------------------------------------------------------------------------------
/bak/Missions.bak:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2018-2021 Eric Kernin, NWNC HARFANG.
2 |
3 | import harfang as hg
4 | from Animations import *
5 | import tools
6 | from random import uniform
7 | from math import radians
8 | import json
9 | import network_server as netws
10 | from overlays import *
11 | from MachineDevice import *
12 |
13 | class Mission:
14 |
15 | def __init__(self, title, num_ennemies, num_allies, num_carriers_ennemies, num_carriers_allies, setup_players, end_test, end_phase_update):
16 | self.title = title
17 | self.ennemies = num_ennemies
18 | self.allies = num_allies
19 | self.allies_carriers = num_carriers_allies
20 | self.ennemies_carriers = num_carriers_ennemies
21 | self.setup_players_f = setup_players
22 | self.end_test_f = end_test
23 | self.update_end_phase_f = end_phase_update
24 | self.failed = False
25 | self.aborted = False
26 |
27 | def reset(self):
28 | self.failed = False
29 | self.aborted = False
30 |
31 | def setup_players(self, main):
32 | self.setup_players_f(main)
33 |
34 | def end_test(self, main):
35 | return self.end_test_f(main)
36 |
37 | def update_end_phase(self, main, dts):
38 | self.update_end_phase_f(main, dts)
39 |
40 | class Missions:
41 | beep_ref = None
42 | beep_state = None
43 | beep_source = None
44 |
45 | validation_ref = None
46 | validation_state = None
47 | validation_source = None
48 |
49 | # animations mission:
50 | am_start = False
51 | am_anim_x_prec = None
52 | am_anim_a_prec = None
53 | am_anim_x_new = None
54 | am_anim_a_new = None
55 | am_mission_id_prec = 0
56 | am_t = 0
57 |
58 | missions = []
59 | mission_id = 1
60 |
61 | @classmethod
62 | def display_mission_title(cls, main, fade_lvl, dts, yof7):
63 | mcr = 1
64 | mcg = 0.6
65 | mcb = 0.1
66 |
67 | if not cls.am_start:
68 | mx = 0.5
69 | mc = hg.Color(mcr, mcg, mcb, 1) * fade_lvl
70 | mid = cls.mission_id
71 |
72 | if cls.mission_id < len(cls.missions) - 1 and main.keyboard.Pressed(hg.K_Right):
73 | cls.am_start = True
74 | mcpt = 1
75 | xpd = 0
76 | xns = 1
77 |
78 | elif cls.mission_id > 0 and main.keyboard.Pressed(hg.K_Left):
79 | cls.am_start = True
80 | mcpt = -1
81 | xpd = 1
82 | xns = 0
83 |
84 | if cls.am_start:
85 | cls.beep_source = hg.PlayStereo(cls.beep_ref, cls.beep_state)
86 | hg.SetSourceVolume(cls.beep_source, 0.05)
87 | cls.am_mission_id_prec = cls.mission_id
88 | cls.mission_id += mcpt
89 | cls.am_t = 0
90 | cls.am_anim_x_prec = Animation(0, 0.5, 0.5, xpd)
91 | cls.am_anim_a_prec = Animation(0, 0.5, 1, 0)
92 | cls.am_anim_x_new = Animation(0, 0.2, xns, 0.5)
93 | cls.am_anim_a_new = Animation(0, 0.2, 0, 1)
94 | mid = cls.am_mission_id_prec
95 |
96 | else:
97 | cls.am_t += dts
98 | cls.am_anim_x_prec.update(cls.am_t)
99 | cls.am_anim_x_new.update(cls.am_t)
100 | cls.am_anim_a_prec.update(cls.am_t)
101 | cls.am_anim_a_new.update(cls.am_t)
102 |
103 | mx = cls.am_anim_x_new.v
104 | mc = hg.Color(mcr, mcg, mcb, cls.am_anim_a_new.v) * fade_lvl
105 |
106 | mxprec = cls.am_anim_x_prec.v
107 | mcprec = hg.Color(mcr, mcg, mcb, cls.am_anim_a_prec.v) * fade_lvl
108 |
109 | Overlays.add_text2D( Missions.missions[cls.am_mission_id_prec].title, hg.Vec2(mxprec, 671 / 900 + yof7), 0.02, mcprec, main.hud_font, hg.DTHA_Center)
110 | mid = cls.mission_id
111 |
112 | if cls.am_anim_a_new.flag_end and cls.am_anim_x_new.flag_end and cls.am_anim_x_prec.flag_end and cls.am_anim_a_prec:
113 | cls.am_start = False
114 |
115 | Overlays.add_text2D(cls.missions[mid].title, hg.Vec2(mx, 671 / 900 + yof7), 0.02, mc, main.hud_font, hg.DTHA_Center)
116 | Overlays.add_text2D( "<- choose your mission ->", hg.Vec2(0.5, 701 / 900 + yof7), 0.012, hg.Color(1, 0.9, 0.3, fade_lvl * 0.8), main.hud_font, hg.DTHA_Center)
117 |
118 | if main.keyboard.Pressed(hg.K_Space):
119 | cls.validation_source = hg.PlayStereo(cls.validation_ref, cls.validation_state)
120 | hg.SetSourceVolume(cls.validation_source, 1)
121 |
122 | @classmethod
123 | def get_current_mission(cls):
124 | return cls.missions[cls.mission_id]
125 |
126 | @classmethod
127 | def aircrafts_starts_on_carrier(cls, aircrafts, carrier, start, y_orientation, distance, liftoff_delay=0, liftoff_offset=1):
128 | p, r = carrier.get_aircraft_start_point(0)
129 | carrier_alt = p.y
130 | carrier_mat = hg.TransformationMat4(carrier.get_parent_node().GetTransform().GetPos(), carrier.get_parent_node().GetTransform().GetRot())
131 | for i, aircraft in enumerate(aircrafts):
132 | ia_ctrl = aircraft.get_device("IAControlDevice")
133 | if ia_ctrl is not None:
134 | ia_ctrl.IA_liftoff_delay = liftoff_delay
135 | aircraft.flag_landed = True
136 | liftoff_delay += liftoff_offset
137 | start += distance
138 | gear = aircraft.get_device("Gear")
139 | if gear is not None:
140 | bottom_height = gear.gear_height
141 | gear.record_start_state(True)
142 | gear.reset()
143 | else:
144 | bottom_height = aircraft.bottom_height
145 | start.y = carrier_alt + bottom_height
146 | mat = carrier_mat * hg.TransformationMat4(start, hg.Vec3(0, y_orientation, 0))
147 | aircraft.reset_matrix(hg.GetT(mat), hg.GetR(mat))
148 | aircraft.record_start_state()
149 |
150 | @classmethod
151 | def setup_aircrafts_on_carrier(cls, players, aircraft_carriers, start_time):
152 | na = len(players)
153 | nc = len(aircraft_carriers)
154 | na_row = na // (2 * nc)
155 |
156 | for i in range(nc):
157 | n0 = na_row * (2 * i)
158 | n1 = na_row * (2 * i + 1)
159 | n2 = na_row * (2 * i + 2)
160 | if na - n2 <= (na_row+1):
161 | n2 = na
162 | cls.aircrafts_starts_on_carrier(players[n0:n1], aircraft_carriers[i], hg.Vec3(10, 19.5, 80), 0, hg.Vec3(0, 0, -18), start_time + na_row * 2 * i, 2)
163 | cls.aircrafts_starts_on_carrier(players[n1:n2], aircraft_carriers[i], hg.Vec3(-10, 19.5, 62), 0, hg.Vec3(0, 0, -18), start_time + 1 + na_row * 2 * i, 2)
164 |
165 | @classmethod
166 | def aircrafts_starts_in_sky(cls, aircrafts, center: hg.Vec3, range: hg.Vec3, y_orientations_range: hg.Vec2, speed_range: hg.Vec2):
167 | for i, ac in enumerate(aircrafts):
168 | # ac.reset_matrix(hg.Vec3(uniform(center.x-range.x/2, center.x+range.x/2), uniform(center.y-range.y/2, center.y+range.y/2), uniform(center.z-range.z/2, center.z+range.z/2)), hg.Vec3(0, radians(uniform(y_orientations_range.x, y_orientations_range.y)), 0))
169 |
170 | ac.reset_matrix(hg.Vec3(uniform(center.x - range.x / 2, center.x + range.x / 2), uniform(center.y - range.y / 2, center.y + range.y / 2), uniform(center.z - range.z / 2, center.z + range.z / 2)), hg.Vec3(0, radians(uniform(y_orientations_range.x, y_orientations_range.y)), 0))
171 | gear = ac.get_device("Gear")
172 | if gear is not None:
173 | gear.record_start_state(False)
174 | gear.reset()
175 | ac.set_linear_speed(uniform(speed_range.x, speed_range.y))
176 | ac.flag_landed = False
177 | ac.record_start_state()
178 |
179 | @classmethod
180 | def setup_carriers(cls, carriers, start_pos, dist, y_orientation):
181 | for carrier in carriers:
182 | carrier.reset_matrix(start_pos, hg.Vec3(0, y_orientation, 0))
183 | start_pos += dist
184 |
185 |
186 | # ============================= Training
187 |
188 | @classmethod
189 | def mission_setup_training(cls, main):
190 |
191 | mission = cls.get_current_mission()
192 | main.create_aircraft_carriers(mission.allies_carriers, mission.ennemies_carriers)
193 | main.create_players(mission.allies, mission.ennemies)
194 |
195 | cls.setup_carriers(main.aircraft_carrier_allies, hg.Vec3(0, 0, 0), hg.Vec3(500, 0, 100), 0)
196 |
197 | n = len(main.players_allies)
198 | # if n == 1:
199 |
200 | cls.aircrafts_starts_on_carrier(main.players_allies, main.aircraft_carrier_allies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20))
201 |
202 | # cls.aircrafts_starts_in_sky(main.players_allies, hg.Vec3(1000, 2000, 0), hg.Vec3(1000, 1000, 20000), hg.Vec2(-180, 180), hg.Vec2(600 / 3.6, 800 / 3.6))
203 |
204 | # elif n > 1:
205 | # cls.aircrafts_starts_on_carrier(main.players_allies[0:n // 2], main.aircraft_carrier_allies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20))
206 | # cls.aircrafts_starts_on_carrier(main.players_allies[n // 2:n], main.aircraft_carrier_allies[0], hg.Vec3(-10, 19.5, 60), 0, hg.Vec3(0, 0, -20))
207 |
208 | fps_start_matrix = main.aircraft_carrier_allies[0].fps_start_point.GetTransform().GetWorld()
209 | main.camera_fps.GetTransform().SetWorld(fps_start_matrix)
210 |
211 | lt = []
212 | for carrier in main.aircraft_carrier_allies:
213 | lt += carrier.landing_targets
214 | for ac in main.players_allies:
215 | ac.set_landing_targets(lt)
216 | ia = ac.get_device("IAControlDevice")
217 | if ia is not None:
218 | ia.activate()
219 |
220 | # ------- Missile Launcher:
221 | main.create_missile_launchers(0, 1)
222 |
223 | plateform = main.scene.GetNode("platform.S400")
224 | ml = main.missile_launchers_ennemies[0]
225 | ml.set_platform(plateform)
226 |
227 | # --------- Views setup
228 | main.setup_views_carousel(True)
229 | main.set_view_carousel("Aircraft_ally_" + str(main.num_players_allies))
230 | main.set_track_view("back")
231 |
232 | main.user_aircraft = main.get_player_from_caroursel_id(main.views_carousel[main.views_carousel_ptr])
233 | main.user_aircraft.set_focus()
234 |
235 | """
236 | ia = main.user_aircraft.get_device("IAControlDevice")
237 | if ia is not None:
238 | ia.set_IA_landing_target(main.aircraft_carrier_allies[0].landing_targets[1])
239 | ia.deactivate()
240 | uctrl = main.user_aircraft.get_device("UserControlDevice")
241 | if uctrl is not None:
242 | uctrl.activate()
243 | """
244 | # Deactivate IA:
245 | for i, ac in enumerate(main.players_allies):
246 | ia = ac.get_device("IAControlDevice")
247 | if ia is not None:
248 | ia.deactivate()
249 |
250 | for i, ac in enumerate(main.players_ennemies):
251 | ia = ac.get_device("IAControlDevice")
252 | if ia is not None:
253 | ia.deactivate()
254 |
255 | uctrl = main.user_aircraft.get_device("UserControlDevice")
256 | if uctrl is not None:
257 | uctrl.activate()
258 |
259 | main.init_playground()
260 |
261 | # if main.num_players_allies < 4:
262 | # main.user_aircraft.set_thrust_level(1)
263 | # main.user_aircraft.activate_post_combustion()
264 |
265 | @classmethod
266 | def mission_training_end_test(cls, main):
267 | mission = cls.get_current_mission()
268 | allies_wreck = 0
269 | for ally in main.players_allies:
270 | if ally.wreck: allies_wreck += 1
271 |
272 | if main.num_players_allies == allies_wreck:
273 | mission.failed = True
274 | print("MISSION FAILED !")
275 | return True
276 |
277 | return False
278 |
279 | @classmethod
280 | def mission_training_end_phase_update(cls, main, dts):
281 | mission = cls.get_current_mission()
282 | if mission.failed:
283 | msg_title = "Mission Failed !"
284 | msg_debriefing = " You need more lessons commander..."
285 | elif mission.aborted:
286 | msg_title = "Training aborted !"
287 | msg_debriefing = "Ready for fight ?"
288 | else:
289 | msg_title = "Training successful !"
290 | msg_debriefing = "Congratulation commander !"
291 | Overlays.add_text2D(msg_title, hg.Vec2(0.5, 771 / 900 - 0.15), 0.04, hg.Color(1, 0.9, 0.3, 1), main.title_font, hg.DTHA_Center)
292 | Overlays.add_text2D(msg_debriefing, hg.Vec2(0.5, 701 / 900 - 0.15), 0.02, hg.Color(1, 0.9, 0.8, 1), main.hud_font, hg.DTHA_Center)
293 |
294 | # ============================= Basic fight
295 |
296 | @classmethod
297 | def mission_setup_players(cls, main):
298 | mission = cls.get_current_mission()
299 | main.create_aircraft_carriers(mission.allies_carriers, mission.ennemies_carriers)
300 | main.create_players(mission.allies, mission.ennemies)
301 |
302 | cls.setup_carriers(main.aircraft_carrier_allies, hg.Vec3(0, 0, 0), hg.Vec3(500, 0, 100), 0)
303 | cls.setup_carriers(main.aircraft_carrier_ennemies, hg.Vec3(-17000, 0, 0), hg.Vec3(500, 0, -150), radians(90))
304 |
305 | main.init_playground()
306 |
307 | n = len(main.players_allies)
308 | if n == 1:
309 | cls.aircrafts_starts_on_carrier(main.players_allies, main.aircraft_carrier_allies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20))
310 | elif n > 1:
311 | cls.aircrafts_starts_on_carrier(main.players_allies[0:n // 2], main.aircraft_carrier_allies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20), 2, 2)
312 | cls.aircrafts_starts_on_carrier(main.players_allies[n // 2:n], main.aircraft_carrier_allies[0], hg.Vec3(-10, 19.5, 60), 0, hg.Vec3(0, 0, -20), 3, 2)
313 |
314 | n = len(main.players_ennemies)
315 | if n < 3:
316 | cls.aircrafts_starts_in_sky(main.players_ennemies, hg.Vec3(-5000, 1000, 0), hg.Vec3(1000, 500, 2000), hg.Vec2(-180, 180), hg.Vec2(800 / 3.6, 600 / 3.6))
317 | else:
318 | cls.aircrafts_starts_in_sky(main.players_ennemies[0:2], hg.Vec3(-5000, 1000, 0), hg.Vec3(1000, 500, 2000), hg.Vec2(-180, 180), hg.Vec2(800 / 3.6, 600 / 3.6))
319 | cls.aircrafts_starts_on_carrier(main.players_ennemies[2:n], main.aircraft_carrier_ennemies[0], hg.Vec3(-10, 19.5, 60), 0, hg.Vec3(0, 0, -20), 2, 1)
320 |
321 | for i, ac in enumerate(main.players_allies):
322 | ia = ac.get_device("IAControlDevice")
323 | if ia is not None:
324 | ia.activate()
325 |
326 | for i, ac in enumerate(main.players_ennemies):
327 | ia = ac.get_device("IAControlDevice")
328 | if ia is not None:
329 | ia.activate()
330 |
331 | main.setup_views_carousel(False)
332 | main.set_view_carousel("Aircraft_ally_" + str(main.num_players_allies))
333 | main.set_track_view("back")
334 |
335 | main.user_aircraft = main.get_player_from_caroursel_id(main.views_carousel[main.views_carousel_ptr])
336 | main.user_aircraft.set_focus()
337 |
338 | if main.user_aircraft is not None:
339 | ia = main.user_aircraft.get_device("IAControlDevice")
340 | if ia is not None:
341 | ia.deactivate()
342 | uctrl = main.user_aircraft.get_device("UserControlDevice")
343 | if uctrl is not None:
344 | uctrl.activate()
345 | if main.num_players_allies < 3:
346 | main.user_aircraft.reset_thrust_level(1)
347 | main.user_aircraft.activate_post_combustion()
348 |
349 | @classmethod
350 | def mission_one_against_x_end_test(cls, main):
351 | mission = cls.get_current_mission()
352 | ennemies_wreck = 0
353 | allies_wreck = 0
354 | for ennemy in main.players_ennemies:
355 | if ennemy.wreck: ennemies_wreck += 1
356 | for ally in main.players_allies:
357 | if ally.wreck: allies_wreck += 1
358 |
359 | if main.num_players_ennemies == ennemies_wreck:
360 | mission.failed = False
361 | return True
362 | if main.num_players_allies == allies_wreck:
363 | mission.failed = True
364 | return True
365 |
366 | return False
367 |
368 | @classmethod
369 | def mission_one_against_x_end_phase_update(cls, main, dts):
370 | mission = cls.get_current_mission()
371 | if mission.failed:
372 | msg_title = "Mission Failed !"
373 | msg_debriefing = " R.I.P. Commander..."
374 | elif mission.aborted:
375 | msg_title = "Mission aborted !"
376 | msg_debriefing = "We hope you do better next time !"
377 | else:
378 | msg_title = "Mission successful !"
379 | msg_debriefing = "Congratulation commander !"
380 | Overlays.add_text2D(msg_title, hg.Vec2(0.5, 771 / 900 - 0.15), 0.04, hg.Color(1, 0.9, 0.3, 1), main.title_font, hg.DTHA_Center)
381 | Overlays.add_text2D(msg_debriefing, hg.Vec2(0.5, 701 / 900 - 0.15), 0.02, hg.Color(1, 0.9, 0.8, 1), main.hud_font, hg.DTHA_Center)
382 |
383 | # ============================ War fight
384 | @classmethod
385 | def mission_total_war_setup_players(cls, main):
386 |
387 | mission = cls.get_current_mission()
388 | main.create_aircraft_carriers(mission.allies_carriers, mission.ennemies_carriers)
389 | main.create_players(mission.allies, mission.ennemies)
390 |
391 | cls.setup_carriers(main.aircraft_carrier_allies, hg.Vec3(0, 0, 0), hg.Vec3(300, 0, 25), 0)
392 | cls.setup_carriers(main.aircraft_carrier_ennemies, hg.Vec3(-20000, 0, 0), hg.Vec3(50, 0, -350), radians(90))
393 |
394 |
395 | cls.setup_aircrafts_on_carrier(main.players_allies, main.aircraft_carrier_allies, 0)
396 |
397 | n = len(main.players_ennemies)
398 | cls.aircrafts_starts_in_sky(main.players_ennemies[0:n // 2], hg.Vec3(-8000, 1000, 0), hg.Vec3(2000, 500, 2000), hg.Vec2(-180, -175), hg.Vec2(800 / 3.6, 600 / 3.6))
399 |
400 | cls.setup_aircrafts_on_carrier(main.players_ennemies[n//2:n], main.aircraft_carrier_ennemies, 60)
401 |
402 | main.init_playground()
403 |
404 | for i, ac in enumerate(main.players_allies):
405 | ia = ac.get_device("IAControlDevice")
406 | if ia is not None:
407 | ia.activate()
408 |
409 | for i, ac in enumerate(main.players_ennemies):
410 | ia = ac.get_device("IAControlDevice")
411 | if ia is not None:
412 | ia.activate()
413 |
414 | main.setup_views_carousel()
415 | main.set_view_carousel("Aircraft_ally_" + str(main.num_players_allies))
416 | main.set_track_view("back")
417 |
418 | main.user_aircraft = main.get_player_from_caroursel_id(main.views_carousel[main.views_carousel_ptr])
419 | main.user_aircraft.set_focus()
420 |
421 | if main.user_aircraft is not None:
422 | ia = main.user_aircraft.get_device("IAControlDevice")
423 | if ia is not None:
424 | ia.deactivate()
425 | uctrl = main.user_aircraft.get_device("UserControlDevice")
426 | if uctrl is not None:
427 | uctrl.activate()
428 | if main.num_players_allies < 4:
429 | main.user_aircraft.reset_thrust_level(1)
430 | main.user_aircraft.activate_post_combustion()
431 |
432 | @classmethod
433 | def mission_war_end_test(cls, main):
434 | mission = cls.get_current_mission()
435 | if main.keyboard.Pressed(hg.K_F6):
436 | for pl in main.players_allies:
437 | pl.flag_IA_start_liftoff = True
438 | for pl in main.players_ennemies:
439 | pl.flag_IA_start_liftoff = True
440 | # Pour le moment, même test de fin de mission
441 | return cls.mission_one_against_x_end_test(main)
442 |
443 | @classmethod
444 | def mission_war_end_phase_update(cls, main, dts):
445 | mission = cls.get_current_mission()
446 | if mission.failed:
447 | msg_title = "Mission Failed !"
448 | msg_debriefing = " R.I.P. Commander..."
449 | elif mission.aborted:
450 | msg_title = "Mission aborted !"
451 | msg_debriefing = "We hope you do better next time !"
452 | else:
453 | msg_title = "Mission successful !"
454 | msg_debriefing = "Congratulation commander !"
455 |
456 | Overlays.add_text2D(msg_title, hg.Vec2(0.5, 771 / 900 - 0.15), 0.04, hg.Color(1, 0.9, 0.3, 1), main.title_font, hg.DTHA_Center)
457 | Overlays.add_text2D(msg_debriefing, hg.Vec2(0.5, 701 / 900 - 0.15), 0.02, hg.Color(1, 0.9, 0.8, 1), main.hud_font, hg.DTHA_Center)
458 |
459 | # ============================ Client / Server mode
460 | @classmethod
461 | def network_mode_setup(cls, main):
462 | mission = cls.get_current_mission()
463 | main.flag_network_mode = True
464 |
465 | file_name = "scripts/network_mission_config.json"
466 | file = hg.OpenText(file_name)
467 | if not file:
468 | print("Can't open mission configuration json file : " + file_name)
469 | else:
470 | json_script = hg.ReadString(file)
471 | hg.Close(file)
472 | if json_script != "":
473 | script_parameters = json.loads(json_script)
474 | mission.allies = script_parameters["aircrafts_allies"]
475 | mission.ennemies = script_parameters["aircrafts_ennemies"]
476 | mission.allies_carriers = script_parameters["aircraft_carriers_allies_count"]
477 | mission.ennemies_carriers = script_parameters["aircraft_carriers_enemies_count"]
478 | else:
479 | print("Mission configuration json file empty : " + file_name)
480 |
481 | main.create_aircraft_carriers(mission.allies_carriers, mission.ennemies_carriers)
482 | main.create_players(mission.allies, mission.ennemies)
483 |
484 | cls.setup_carriers(main.aircraft_carrier_allies, hg.Vec3(0, 0, 0), hg.Vec3(500, 0, 100), 0)
485 | cls.setup_carriers(main.aircraft_carrier_ennemies, hg.Vec3(-17000, 0, 0), hg.Vec3(500, 0, -150), radians(90))
486 |
487 | main.init_playground()
488 |
489 | # Sets aircrafts landed on carriers:
490 |
491 | n = len(main.players_allies)
492 | if n == 1:
493 | cls.aircrafts_starts_on_carrier(main.players_allies, main.aircraft_carrier_allies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20))
494 | elif n > 1:
495 | cls.aircrafts_starts_on_carrier(main.players_allies[0:n // 2], main.aircraft_carrier_allies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20), 2, 2)
496 | cls.aircrafts_starts_on_carrier(main.players_allies[n // 2:n], main.aircraft_carrier_allies[0], hg.Vec3(-10, 19.5, 60), 0, hg.Vec3(0, 0, -20), 3, 2)
497 |
498 | n = len(main.players_ennemies)
499 | if n == 1:
500 | cls.aircrafts_starts_on_carrier(main.players_ennemies, main.aircraft_carrier_ennemies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20))
501 | elif n > 1:
502 | cls.aircrafts_starts_on_carrier(main.players_ennemies[0:n // 2], main.aircraft_carrier_ennemies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20), 2, 2)
503 | cls.aircrafts_starts_on_carrier(main.players_ennemies[n // 2:n], main.aircraft_carrier_ennemies[0], hg.Vec3(-10, 19.5, 60), 0, hg.Vec3(0, 0, -20), 3, 2)
504 |
505 | # Deactivate IA:
506 | for i, ac in enumerate(main.players_allies):
507 | ia = ac.get_device("IAControlDevice")
508 | if ia is not None:
509 | ia.deactivate()
510 |
511 | for i, ac in enumerate(main.players_ennemies):
512 | ia = ac.get_device("IAControlDevice")
513 | if ia is not None:
514 | ia.deactivate()
515 |
516 | # ------- Missile Launcher:
517 | main.create_missile_launchers(0, 1)
518 |
519 | plateform = main.scene.GetNode("platform.S400")
520 | ml = main.missile_launchers_ennemies[0]
521 | ml.set_platform(plateform)
522 |
523 | # --------- Views setup
524 | main.setup_views_carousel(True)
525 | main.set_view_carousel("Aircraft_ally_1")# + str(main.num_players_allies))
526 | main.set_track_view("back")
527 |
528 | main.init_playground()
529 |
530 | main.user_aircraft = main.get_player_from_caroursel_id(main.views_carousel[main.views_carousel_ptr])
531 | main.user_aircraft.set_focus()
532 |
533 | uctrl = main.user_aircraft.get_device("UserControlDevice")
534 | if uctrl is not None:
535 | uctrl.activate()
536 |
537 | netws.init_server(main)
538 | netws.start_server()
539 |
540 | @classmethod
541 | def network_mode_end_test(cls, main):
542 | """
543 | mission = cls.get_current_mission()
544 |
545 | allies_wreck = 0
546 | for ally in main.players_allies:
547 | if ally.wreck:
548 | allies_wreck += 1
549 |
550 | if main.num_players_allies == allies_wreck:
551 | mission.failed = True
552 | return True
553 | """
554 | return False
555 |
556 | @classmethod
557 | def network_mode_end_phase_update(cls, main, dts):
558 | if main.flag_network_mode:
559 | main.flag_network_mode = False
560 | netws.stop_server()
561 | mission = cls.get_current_mission()
562 | if mission.failed:
563 | msg_title = "Network aborted !"
564 | msg_debriefing = " Aircraft destroyed, TAB to restart"
565 | elif mission.aborted:
566 | msg_title = "Network aborted !"
567 | msg_debriefing = "TAB to restart"
568 | else:
569 | msg_title = "Successful !"
570 | msg_debriefing = "Congratulation commander !"
571 | Overlays.add_text2D(msg_title, hg.Vec2(0.5, 771 / 900 - 0.15), 0.04, hg.Color(1, 0.9, 0.3, 1), main.title_font, hg.DTHA_Center)
572 | Overlays.add_text2D(msg_debriefing, hg.Vec2(0.5, 701 / 900 - 0.15), 0.02, hg.Color(1, 0.9, 0.8, 1), main.hud_font, hg.DTHA_Center)
573 |
574 | @classmethod
575 | def init(cls):
576 | cls.beep_ref = hg.LoadWAVSoundAsset("sfx/blip.wav")
577 | cls.beep_state = tools.create_stereo_sound_state(hg.SR_Once)
578 | cls.beep_state.volume = 0.25
579 |
580 | cls.validation_ref = hg.LoadWAVSoundAsset("sfx/blip2.wav")
581 | cls.validation_state = tools.create_stereo_sound_state(hg.SR_Once)
582 | cls.validation_state.volume = 0.5
583 |
584 | cls.missions.append(Mission("Network mode", ["Eurofighter"], ["Rafale"], 1, 1, Missions.network_mode_setup, Missions.network_mode_end_test, Missions.network_mode_end_phase_update))
585 |
586 | cls.missions.append(Mission("Training with Rafale", [], ["Rafale"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
587 | cls.missions.append(Mission("Training with Eurofighter", [], ["Eurofighter"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
588 | cls.missions.append(Mission("Training with TFX", [], ["TFX"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
589 | cls.missions.append(Mission("Training with F16", [], ["F16"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
590 | cls.missions.append(Mission("Training with Miuss", [], ["Miuss"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
591 | #cls.missions.append(Mission("Training with F14", [], ["F14"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
592 | #cls.missions.append(Mission("Training with F14 2", [], ["F14_2"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
593 |
594 | cls.missions.append(Mission("One on one", ["Rafale"], ["Eurofighter"], 1, 1, Missions.mission_setup_players, Missions.mission_one_against_x_end_test, Missions.mission_one_against_x_end_phase_update))
595 | cls.missions.append(Mission("Fight against 2 ennemies", ["Rafale"] * 2, ["Eurofighter"], 1, 1, Missions.mission_setup_players, Missions.mission_one_against_x_end_test, Missions.mission_one_against_x_end_phase_update))
596 | cls.missions.append(Mission("Fight against 3 ennemies", ["Rafale"] * 1 + ["F16"] * 2, ["Eurofighter"], 1, 1, Missions.mission_setup_players, Missions.mission_one_against_x_end_test, Missions.mission_one_against_x_end_phase_update))
597 | cls.missions.append(Mission("Fight against 4 ennemies", ["Rafale"] * 2 + ["F16"] * 2, ["TFX", "Eurofighter"], 1, 1, Missions.mission_setup_players, Missions.mission_one_against_x_end_test, Missions.mission_one_against_x_end_phase_update))
598 | cls.missions.append(Mission("Fight against 5 ennemies", ["Rafale"] * 5, ["TFX", "Eurofighter", "F16"], 1, 1, Missions.mission_setup_players, Missions.mission_one_against_x_end_test, Missions.mission_one_against_x_end_phase_update))
599 |
600 | cls.missions.append(Mission("War: 5 allies against 5 ennemies", ["Rafale"] * 3 + ["Eurofighter"] * 2, ["TFX"] * 2 + ["F16"] * 2 + ["Eurofighter"] * 1, 1, 1, Missions.mission_total_war_setup_players, Missions.mission_war_end_test, Missions.mission_war_end_phase_update))
601 | #cls.missions.append(Mission("Total War: 12 allies against 12 ennemies", ["Rafale"] * 12, ["TFX"] * 4 + ["Eurofighter"] * 4 + ["F16"] * 4 + ["Eurofighter"] * 4, 2, 2, Missions.mission_total_war_setup_players, Missions.mission_war_end_test, Missions.mission_war_end_phase_update))
602 | #cls.missions.append(Mission("Crash test: 60 allies against 60 ennemies", ["Rafale"] * 30 + ["Eurofighter"] * 30, ["TFX"] * 30 + ["Eurofighter"] * 20 + ["F16"] * 10, 5, 5, Missions.mission_total_war_setup_players, Missions.mission_war_end_test, Missions.mission_war_end_phase_update))
603 |
--------------------------------------------------------------------------------
/bak/Missions_evade.bak:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2018-2021 Eric Kernin, NWNC HARFANG.
2 |
3 | import harfang as hg
4 | from Animations import *
5 | import tools
6 | from random import uniform
7 | from math import radians
8 | import json
9 | import network_server as netws
10 | from overlays import *
11 | from MachineDevice import *
12 |
13 | class Mission:
14 |
15 | def __init__(self, title, num_ennemies, num_allies, num_carriers_ennemies, num_carriers_allies, setup_players, end_test, end_phase_update):
16 | self.title = title
17 | self.ennemies = num_ennemies
18 | self.allies = num_allies
19 | self.allies_carriers = num_carriers_allies
20 | self.ennemies_carriers = num_carriers_ennemies
21 | self.setup_players_f = setup_players
22 | self.end_test_f = end_test
23 | self.update_end_phase_f = end_phase_update
24 | self.failed = False
25 | self.aborted = False
26 |
27 | def reset(self):
28 | self.failed = False
29 | self.aborted = False
30 |
31 | def setup_players(self, main):
32 | self.setup_players_f(main)
33 |
34 | def end_test(self, main):
35 | return self.end_test_f(main)
36 |
37 | def update_end_phase(self, main, dts):
38 | self.update_end_phase_f(main, dts)
39 |
40 | class Missions:
41 | beep_ref = None
42 | beep_state = None
43 | beep_source = None
44 |
45 | validation_ref = None
46 | validation_state = None
47 | validation_source = None
48 |
49 | # animations mission:
50 | am_start = False
51 | am_anim_x_prec = None
52 | am_anim_a_prec = None
53 | am_anim_x_new = None
54 | am_anim_a_new = None
55 | am_mission_id_prec = 0
56 | am_t = 0
57 |
58 | missions = []
59 | mission_id = 1
60 |
61 | @classmethod
62 | def display_mission_title(cls, main, fade_lvl, dts, yof7):
63 | mcr = 1
64 | mcg = 0.6
65 | mcb = 0.1
66 |
67 | if not cls.am_start:
68 | mx = 0.5
69 | mc = hg.Color(mcr, mcg, mcb, 1) * fade_lvl
70 | mid = cls.mission_id
71 |
72 | if cls.mission_id < len(cls.missions) - 1 and main.keyboard.Pressed(hg.K_Right):
73 | cls.am_start = True
74 | mcpt = 1
75 | xpd = 0
76 | xns = 1
77 |
78 | elif cls.mission_id > 0 and main.keyboard.Pressed(hg.K_Left):
79 | cls.am_start = True
80 | mcpt = -1
81 | xpd = 1
82 | xns = 0
83 |
84 | if cls.am_start:
85 | cls.beep_source = hg.PlayStereo(cls.beep_ref, cls.beep_state)
86 | hg.SetSourceVolume(cls.beep_source, 0.05)
87 | cls.am_mission_id_prec = cls.mission_id
88 | cls.mission_id += mcpt
89 | cls.am_t = 0
90 | cls.am_anim_x_prec = Animation(0, 0.5, 0.5, xpd)
91 | cls.am_anim_a_prec = Animation(0, 0.5, 1, 0)
92 | cls.am_anim_x_new = Animation(0, 0.2, xns, 0.5)
93 | cls.am_anim_a_new = Animation(0, 0.2, 0, 1)
94 | mid = cls.am_mission_id_prec
95 |
96 | else:
97 | cls.am_t += dts
98 | cls.am_anim_x_prec.update(cls.am_t)
99 | cls.am_anim_x_new.update(cls.am_t)
100 | cls.am_anim_a_prec.update(cls.am_t)
101 | cls.am_anim_a_new.update(cls.am_t)
102 |
103 | mx = cls.am_anim_x_new.v
104 | mc = hg.Color(mcr, mcg, mcb, cls.am_anim_a_new.v) * fade_lvl
105 |
106 | mxprec = cls.am_anim_x_prec.v
107 | mcprec = hg.Color(mcr, mcg, mcb, cls.am_anim_a_prec.v) * fade_lvl
108 |
109 | Overlays.add_text2D( Missions.missions[cls.am_mission_id_prec].title, hg.Vec2(mxprec, 671 / 900 + yof7), 0.02, mcprec, main.hud_font, hg.DTHA_Center)
110 | mid = cls.mission_id
111 |
112 | if cls.am_anim_a_new.flag_end and cls.am_anim_x_new.flag_end and cls.am_anim_x_prec.flag_end and cls.am_anim_a_prec:
113 | cls.am_start = False
114 |
115 | Overlays.add_text2D(cls.missions[mid].title, hg.Vec2(mx, 671 / 900 + yof7), 0.02, mc, main.hud_font, hg.DTHA_Center)
116 | Overlays.add_text2D( "<- choose your mission ->", hg.Vec2(0.5, 701 / 900 + yof7), 0.012, hg.Color(1, 0.9, 0.3, fade_lvl * 0.8), main.hud_font, hg.DTHA_Center)
117 |
118 | if main.keyboard.Pressed(hg.K_Space):
119 | cls.validation_source = hg.PlayStereo(cls.validation_ref, cls.validation_state)
120 | hg.SetSourceVolume(cls.validation_source, 1)
121 |
122 | @classmethod
123 | def get_current_mission(cls):
124 | return cls.missions[cls.mission_id]
125 |
126 | @classmethod
127 | def aircrafts_starts_on_carrier(cls, aircrafts, carrier, start, y_orientation, distance, liftoff_delay=0, liftoff_offset=1):
128 | p, r = carrier.get_aircraft_start_point(0)
129 | carrier_alt = p.y
130 | carrier_mat = hg.TransformationMat4(carrier.get_parent_node().GetTransform().GetPos(), carrier.get_parent_node().GetTransform().GetRot())
131 | for i, aircraft in enumerate(aircrafts):
132 | ia_ctrl = aircraft.get_device("IAControlDevice")
133 | if ia_ctrl is not None:
134 | ia_ctrl.IA_liftoff_delay = liftoff_delay
135 | aircraft.flag_landed = True
136 | liftoff_delay += liftoff_offset
137 | start += distance
138 | gear = aircraft.get_device("Gear")
139 | if gear is not None:
140 | bottom_height = gear.gear_height
141 | gear.record_start_state(True)
142 | gear.reset()
143 | else:
144 | bottom_height = aircraft.bottom_height
145 | start.y = carrier_alt + bottom_height
146 | mat = carrier_mat * hg.TransformationMat4(start, hg.Vec3(0, y_orientation, 0))
147 | aircraft.reset_matrix(hg.GetT(mat), hg.GetR(mat))
148 | aircraft.record_start_state()
149 |
150 | @classmethod
151 | def setup_aircrafts_on_carrier(cls, players, aircraft_carriers, start_time):
152 | na = len(players)
153 | nc = len(aircraft_carriers)
154 | na_row = na // (2 * nc)
155 |
156 | for i in range(nc):
157 | n0 = na_row * (2 * i)
158 | n1 = na_row * (2 * i + 1)
159 | n2 = na_row * (2 * i + 2)
160 | if na - n2 <= (na_row+1):
161 | n2 = na
162 | cls.aircrafts_starts_on_carrier(players[n0:n1], aircraft_carriers[i], hg.Vec3(10, 19.5, 80), 0, hg.Vec3(0, 0, -18), start_time + na_row * 2 * i, 2)
163 | cls.aircrafts_starts_on_carrier(players[n1:n2], aircraft_carriers[i], hg.Vec3(-10, 19.5, 62), 0, hg.Vec3(0, 0, -18), start_time + 1 + na_row * 2 * i, 2)
164 |
165 | @classmethod
166 | def aircrafts_starts_in_sky(cls, aircrafts, center: hg.Vec3, range: hg.Vec3, y_orientations_range: hg.Vec2, speed_range: hg.Vec2):
167 | for i, ac in enumerate(aircrafts):
168 | # ac.reset_matrix(hg.Vec3(uniform(center.x-range.x/2, center.x+range.x/2), uniform(center.y-range.y/2, center.y+range.y/2), uniform(center.z-range.z/2, center.z+range.z/2)), hg.Vec3(0, radians(uniform(y_orientations_range.x, y_orientations_range.y)), 0))
169 |
170 | ac.reset_matrix(
171 | hg.Vec3(
172 | uniform(center.x - range.x / 2, center.x + range.x / 2),
173 | uniform(center.y - range.y / 2, center.y + range.y / 2),
174 | uniform(center.z - range.z / 2, center.z + range.z / 2)
175 | ),
176 | hg.Vec3(
177 | 0,
178 | radians(
179 | uniform(y_orientations_range.x, y_orientations_range.y)
180 | ),
181 | 0
182 | )
183 | )
184 | gear = ac.get_device("Gear")
185 | if gear is not None:
186 | gear.record_start_state(False)
187 | gear.reset()
188 | ac.set_linear_speed(uniform(speed_range.x, speed_range.y))
189 | ac.flag_landed = False
190 | ac.record_start_state()
191 |
192 | @classmethod
193 | def setup_carriers(cls, carriers, start_pos, dist, y_orientation):
194 | for carrier in carriers:
195 | carrier.reset_matrix(start_pos, hg.Vec3(0, y_orientation, 0))
196 | start_pos += dist
197 |
198 |
199 | # ============================= Training
200 |
201 | @classmethod
202 | def mission_setup_training(cls, main):
203 |
204 | mission = cls.get_current_mission()
205 | main.create_aircraft_carriers(mission.allies_carriers, mission.ennemies_carriers)
206 | main.create_players(mission.allies, mission.ennemies)
207 |
208 | cls.setup_carriers(main.aircraft_carrier_allies, hg.Vec3(0, 0, 0), hg.Vec3(500, 0, 100), 0)
209 |
210 | n = len(main.players_allies)
211 | # if n == 1:
212 |
213 | cls.aircrafts_starts_on_carrier(main.players_allies, main.aircraft_carrier_allies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20))
214 |
215 | # cls.aircrafts_starts_in_sky(main.players_allies, hg.Vec3(1000, 2000, 0), hg.Vec3(1000, 1000, 20000), hg.Vec2(-180, 180), hg.Vec2(600 / 3.6, 800 / 3.6))
216 |
217 | # elif n > 1:
218 | # cls.aircrafts_starts_on_carrier(main.players_allies[0:n // 2], main.aircraft_carrier_allies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20))
219 | # cls.aircrafts_starts_on_carrier(main.players_allies[n // 2:n], main.aircraft_carrier_allies[0], hg.Vec3(-10, 19.5, 60), 0, hg.Vec3(0, 0, -20))
220 |
221 | fps_start_matrix = main.aircraft_carrier_allies[0].fps_start_point.GetTransform().GetWorld()
222 | main.camera_fps.GetTransform().SetWorld(fps_start_matrix)
223 |
224 | lt = []
225 | for carrier in main.aircraft_carrier_allies:
226 | lt += carrier.landing_targets
227 | for ac in main.players_allies:
228 | ac.set_landing_targets(lt)
229 | ia = ac.get_device("IAControlDevice")
230 | if ia is not None:
231 | ia.activate()
232 |
233 | # ------- Missile Launcher:
234 | main.create_missile_launchers(0, 1)
235 |
236 | plateform = main.scene.GetNode("platform.S400")
237 | ml = main.missile_launchers_ennemies[0]
238 | ml.set_platform(plateform)
239 |
240 | # --------- Views setup
241 | main.setup_views_carousel(True)
242 | main.set_view_carousel("Aircraft_ally_" + str(main.num_players_allies))
243 | main.set_track_view("back")
244 |
245 | main.user_aircraft = main.get_player_from_caroursel_id(main.views_carousel[main.views_carousel_ptr])
246 | main.user_aircraft.set_focus()
247 |
248 | """
249 | ia = main.user_aircraft.get_device("IAControlDevice")
250 | if ia is not None:
251 | ia.set_IA_landing_target(main.aircraft_carrier_allies[0].landing_targets[1])
252 | ia.deactivate()
253 | uctrl = main.user_aircraft.get_device("UserControlDevice")
254 | if uctrl is not None:
255 | uctrl.activate()
256 | """
257 | # Deactivate IA:
258 | for i, ac in enumerate(main.players_allies):
259 | ia = ac.get_device("IAControlDevice")
260 | if ia is not None:
261 | ia.deactivate()
262 |
263 | for i, ac in enumerate(main.players_ennemies):
264 | ia = ac.get_device("IAControlDevice")
265 | if ia is not None:
266 | ia.deactivate()
267 |
268 | uctrl = main.user_aircraft.get_device("UserControlDevice")
269 | if uctrl is not None:
270 | uctrl.activate()
271 |
272 | main.init_playground()
273 |
274 | # if main.num_players_allies < 4:
275 | # main.user_aircraft.set_thrust_level(1)
276 | # main.user_aircraft.activate_post_combustion()
277 |
278 | @classmethod
279 | def mission_training_end_test(cls, main):
280 | mission = cls.get_current_mission()
281 | allies_wreck = 0
282 | for ally in main.players_allies:
283 | if ally.wreck: allies_wreck += 1
284 |
285 | if main.num_players_allies == allies_wreck:
286 | mission.failed = True
287 | print("MISSION FAILED !")
288 | return True
289 |
290 | return False
291 |
292 | @classmethod
293 | def mission_training_end_phase_update(cls, main, dts):
294 | mission = cls.get_current_mission()
295 | if mission.failed:
296 | msg_title = "Mission Failed !"
297 | msg_debriefing = " You need more lessons commander..."
298 | elif mission.aborted:
299 | msg_title = "Training aborted !"
300 | msg_debriefing = "Ready for fight ?"
301 | else:
302 | msg_title = "Training successful !"
303 | msg_debriefing = "Congratulation commander !"
304 | Overlays.add_text2D(msg_title, hg.Vec2(0.5, 771 / 900 - 0.15), 0.04, hg.Color(1, 0.9, 0.3, 1), main.title_font, hg.DTHA_Center)
305 | Overlays.add_text2D(msg_debriefing, hg.Vec2(0.5, 701 / 900 - 0.15), 0.02, hg.Color(1, 0.9, 0.8, 1), main.hud_font, hg.DTHA_Center)
306 |
307 | # ============================= Basic fight
308 |
309 | @classmethod
310 | def mission_setup_players(cls, main):
311 | mission = cls.get_current_mission()
312 | main.create_aircraft_carriers(mission.allies_carriers, mission.ennemies_carriers)
313 | main.create_players(mission.allies, mission.ennemies)
314 |
315 | cls.setup_carriers(main.aircraft_carrier_allies, hg.Vec3(0, 0, 0), hg.Vec3(500, 0, 100), 0)
316 | cls.setup_carriers(main.aircraft_carrier_ennemies, hg.Vec3(-17000, 0, 0), hg.Vec3(500, 0, -150), radians(90))
317 |
318 | main.init_playground()
319 |
320 | n = len(main.players_allies)
321 | if n == 1:
322 | cls.aircrafts_starts_on_carrier(main.players_allies, main.aircraft_carrier_allies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20))
323 | elif n > 1:
324 | cls.aircrafts_starts_on_carrier(main.players_allies[0:n // 2], main.aircraft_carrier_allies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20), 2, 2)
325 | cls.aircrafts_starts_on_carrier(main.players_allies[n // 2:n], main.aircraft_carrier_allies[0], hg.Vec3(-10, 19.5, 60), 0, hg.Vec3(0, 0, -20), 3, 2)
326 |
327 | n = len(main.players_ennemies)
328 | if n < 3:
329 | cls.aircrafts_starts_in_sky(main.players_ennemies, hg.Vec3(-5000, 1000, 0), hg.Vec3(1000, 500, 2000), hg.Vec2(-180, 180), hg.Vec2(800 / 3.6, 600 / 3.6))
330 | else:
331 | cls.aircrafts_starts_in_sky(main.players_ennemies[0:2], hg.Vec3(-5000, 1000, 0), hg.Vec3(1000, 500, 2000), hg.Vec2(-180, 180), hg.Vec2(800 / 3.6, 600 / 3.6))
332 | cls.aircrafts_starts_on_carrier(main.players_ennemies[2:n], main.aircraft_carrier_ennemies[0], hg.Vec3(-10, 19.5, 60), 0, hg.Vec3(0, 0, -20), 2, 1)
333 |
334 | for i, ac in enumerate(main.players_allies):
335 | ia = ac.get_device("IAControlDevice")
336 | if ia is not None:
337 | ia.activate()
338 |
339 | for i, ac in enumerate(main.players_ennemies):
340 | ia = ac.get_device("IAControlDevice")
341 | if ia is not None:
342 | ia.activate()
343 |
344 | main.setup_views_carousel(False)
345 | main.set_view_carousel("Aircraft_ally_" + str(main.num_players_allies))
346 | main.set_track_view("back")
347 |
348 | main.user_aircraft = main.get_player_from_caroursel_id(main.views_carousel[main.views_carousel_ptr])
349 | main.user_aircraft.set_focus()
350 |
351 | if main.user_aircraft is not None:
352 | ia = main.user_aircraft.get_device("IAControlDevice")
353 | if ia is not None:
354 | ia.deactivate()
355 | uctrl = main.user_aircraft.get_device("UserControlDevice")
356 | if uctrl is not None:
357 | uctrl.activate()
358 | if main.num_players_allies < 3:
359 | main.user_aircraft.reset_thrust_level(1)
360 | main.user_aircraft.activate_post_combustion()
361 |
362 | @classmethod
363 | def mission_one_against_x_end_test(cls, main):
364 | mission = cls.get_current_mission()
365 | ennemies_wreck = 0
366 | allies_wreck = 0
367 | for ennemy in main.players_ennemies:
368 | if ennemy.wreck: ennemies_wreck += 1
369 | for ally in main.players_allies:
370 | if ally.wreck: allies_wreck += 1
371 |
372 | if main.num_players_ennemies == ennemies_wreck:
373 | mission.failed = False
374 | return True
375 | if main.num_players_allies == allies_wreck:
376 | mission.failed = True
377 | return True
378 |
379 | return False
380 |
381 | @classmethod
382 | def mission_one_against_x_end_phase_update(cls, main, dts):
383 | mission = cls.get_current_mission()
384 | if mission.failed:
385 | msg_title = "Mission Failed !"
386 | msg_debriefing = " R.I.P. Commander..."
387 | elif mission.aborted:
388 | msg_title = "Mission aborted !"
389 | msg_debriefing = "We hope you do better next time !"
390 | else:
391 | msg_title = "Mission successful !"
392 | msg_debriefing = "Congratulation commander !"
393 | Overlays.add_text2D(msg_title, hg.Vec2(0.5, 771 / 900 - 0.15), 0.04, hg.Color(1, 0.9, 0.3, 1), main.title_font, hg.DTHA_Center)
394 | Overlays.add_text2D(msg_debriefing, hg.Vec2(0.5, 701 / 900 - 0.15), 0.02, hg.Color(1, 0.9, 0.8, 1), main.hud_font, hg.DTHA_Center)
395 |
396 | # ============================ War fight
397 | @classmethod
398 | def mission_total_war_setup_players(cls, main):
399 |
400 | mission = cls.get_current_mission()
401 | main.create_aircraft_carriers(mission.allies_carriers, mission.ennemies_carriers)
402 | main.create_players(mission.allies, mission.ennemies)
403 |
404 | cls.setup_carriers(main.aircraft_carrier_allies, hg.Vec3(0, 0, 0), hg.Vec3(300, 0, 25), 0)
405 | cls.setup_carriers(main.aircraft_carrier_ennemies, hg.Vec3(-20000, 0, 0), hg.Vec3(50, 0, -350), radians(90))
406 |
407 |
408 | cls.setup_aircrafts_on_carrier(main.players_allies, main.aircraft_carrier_allies, 0)
409 |
410 | n = len(main.players_ennemies)
411 | cls.aircrafts_starts_in_sky(main.players_ennemies[0:n // 2], hg.Vec3(-8000, 1000, 0), hg.Vec3(2000, 500, 2000), hg.Vec2(-180, -175), hg.Vec2(800 / 3.6, 600 / 3.6))
412 |
413 | cls.setup_aircrafts_on_carrier(main.players_ennemies[n//2:n], main.aircraft_carrier_ennemies, 60)
414 |
415 | main.init_playground()
416 |
417 | for i, ac in enumerate(main.players_allies):
418 | ia = ac.get_device("IAControlDevice")
419 | if ia is not None:
420 | ia.activate()
421 |
422 | for i, ac in enumerate(main.players_ennemies):
423 | ia = ac.get_device("IAControlDevice")
424 | if ia is not None:
425 | ia.activate()
426 |
427 | main.setup_views_carousel()
428 | main.set_view_carousel("Aircraft_ally_" + str(main.num_players_allies))
429 | main.set_track_view("back")
430 |
431 | main.user_aircraft = main.get_player_from_caroursel_id(main.views_carousel[main.views_carousel_ptr])
432 | main.user_aircraft.set_focus()
433 |
434 | if main.user_aircraft is not None:
435 | ia = main.user_aircraft.get_device("IAControlDevice")
436 | if ia is not None:
437 | ia.deactivate()
438 | uctrl = main.user_aircraft.get_device("UserControlDevice")
439 | if uctrl is not None:
440 | uctrl.activate()
441 | if main.num_players_allies < 4:
442 | main.user_aircraft.reset_thrust_level(1)
443 | main.user_aircraft.activate_post_combustion()
444 |
445 | @classmethod
446 | def mission_war_end_test(cls, main):
447 | mission = cls.get_current_mission()
448 | if main.keyboard.Pressed(hg.K_F6):
449 | for pl in main.players_allies:
450 | pl.flag_IA_start_liftoff = True
451 | for pl in main.players_ennemies:
452 | pl.flag_IA_start_liftoff = True
453 | # Pour le moment, même test de fin de mission
454 | return cls.mission_one_against_x_end_test(main)
455 |
456 | @classmethod
457 | def mission_war_end_phase_update(cls, main, dts):
458 | mission = cls.get_current_mission()
459 | if mission.failed:
460 | msg_title = "Mission Failed !"
461 | msg_debriefing = " R.I.P. Commander..."
462 | elif mission.aborted:
463 | msg_title = "Mission aborted !"
464 | msg_debriefing = "We hope you do better next time !"
465 | else:
466 | msg_title = "Mission successful !"
467 | msg_debriefing = "Congratulation commander !"
468 |
469 | Overlays.add_text2D(msg_title, hg.Vec2(0.5, 771 / 900 - 0.15), 0.04, hg.Color(1, 0.9, 0.3, 1), main.title_font, hg.DTHA_Center)
470 | Overlays.add_text2D(msg_debriefing, hg.Vec2(0.5, 701 / 900 - 0.15), 0.02, hg.Color(1, 0.9, 0.8, 1), main.hud_font, hg.DTHA_Center)
471 |
472 | # ============================ Client / Server mode
473 | @classmethod
474 | def network_mode_setup(cls, main):
475 | mission = cls.get_current_mission()
476 | main.flag_network_mode = True
477 |
478 | file_name = "scripts/network_mission_config.json"
479 | file = hg.OpenText(file_name)
480 | if not file:
481 | print("Can't open mission configuration json file : " + file_name)
482 | else:
483 | json_script = hg.ReadString(file)
484 | hg.Close(file)
485 | if json_script != "":
486 | script_parameters = json.loads(json_script)
487 | mission.allies = script_parameters["aircrafts_allies"]
488 | mission.ennemies = script_parameters["aircrafts_ennemies"]
489 | mission.allies_carriers = script_parameters["aircraft_carriers_allies_count"]
490 | mission.ennemies_carriers = script_parameters["aircraft_carriers_enemies_count"]
491 | else:
492 | print("Mission configuration json file empty : " + file_name)
493 |
494 | main.create_aircraft_carriers(mission.allies_carriers, mission.ennemies_carriers)
495 | main.create_players(mission.allies, mission.ennemies)
496 |
497 | cls.setup_carriers(main.aircraft_carrier_allies, hg.Vec3(0, 0, 0), hg.Vec3(500, 0, 100), 0)
498 | cls.setup_carriers(main.aircraft_carrier_ennemies, hg.Vec3(-17000, 0, 0), hg.Vec3(500, 0, -150), radians(90))
499 |
500 | main.init_playground()
501 |
502 | # Sets aircrafts landed on carriers:
503 |
504 | n = len(main.players_allies)
505 | if n == 1:
506 | cls.aircrafts_starts_on_carrier(main.players_allies, main.aircraft_carrier_allies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20))
507 | elif n > 1:
508 | cls.aircrafts_starts_on_carrier(main.players_allies[0:n // 2], main.aircraft_carrier_allies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20), 2, 2)
509 | cls.aircrafts_starts_in_sky(
510 | aircrafts=main.players_allies[n // 2:n],
511 | center=hg.Vec3(-10, 1500, 60), # (x, z, y), recommend height 1500
512 | # range=hg.Vec3(-10, 19.5, 60),
513 | range=hg.Vec3(0, 0, 0),
514 | y_orientations_range=hg.Vec2(270, 270), # heading
515 | speed_range=hg.Vec2(400, 400) # m/s, recommend 400
516 | )
517 |
518 | n = len(main.players_ennemies)
519 | if n == 1:
520 | cls.aircrafts_starts_on_carrier(main.players_ennemies, main.aircraft_carrier_ennemies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20))
521 | elif n > 1:
522 | cls.aircrafts_starts_on_carrier(main.players_ennemies[0:n // 2], main.aircraft_carrier_ennemies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20), 2, 2)
523 | cls.aircrafts_starts_in_sky(
524 | aircrafts=main.players_ennemies[n // 2:n],
525 | center=hg.Vec3(4000, 1500, 60), # (x, z, y), recommend height 1500
526 | # range=hg.Vec3(-10, 19.5, 60),
527 | range=hg.Vec3(0, 0, 0),
528 | y_orientations_range=hg.Vec2(270, 270), # heading
529 | speed_range=hg.Vec2(400, 400) # m/s, recommend 400
530 | )
531 |
532 | # Deactivate IA:
533 | for i, ac in enumerate(main.players_allies):
534 | ia = ac.get_device("IAControlDevice")
535 | if ia is not None:
536 | ia.deactivate()
537 |
538 | for i, ac in enumerate(main.players_ennemies):
539 | ia = ac.get_device("IAControlDevice")
540 | if ia is not None:
541 | ia.deactivate()
542 |
543 | # ------- Missile Launcher:
544 | main.create_missile_launchers(0, 1)
545 |
546 | plateform = main.scene.GetNode("platform.S400")
547 | ml = main.missile_launchers_ennemies[0]
548 | ml.set_platform(plateform)
549 |
550 | # --------- Views setup
551 | main.setup_views_carousel(True)
552 | main.set_view_carousel("Aircraft_ally_1")# + str(main.num_players_allies))
553 | main.set_track_view("back")
554 |
555 | main.init_playground()
556 |
557 | main.user_aircraft = main.get_player_from_caroursel_id(main.views_carousel[main.views_carousel_ptr])
558 | main.user_aircraft.set_focus()
559 |
560 | uctrl = main.user_aircraft.get_device("UserControlDevice")
561 | if uctrl is not None:
562 | uctrl.activate()
563 |
564 | netws.init_server(main)
565 | netws.start_server()
566 |
567 | @classmethod
568 | def network_mode_end_test(cls, main):
569 | """
570 | mission = cls.get_current_mission()
571 |
572 | allies_wreck = 0
573 | for ally in main.players_allies:
574 | if ally.wreck:
575 | allies_wreck += 1
576 |
577 | if main.num_players_allies == allies_wreck:
578 | mission.failed = True
579 | return True
580 | """
581 | return False
582 |
583 | @classmethod
584 | def network_mode_end_phase_update(cls, main, dts):
585 | if main.flag_network_mode:
586 | main.flag_network_mode = False
587 | netws.stop_server()
588 | mission = cls.get_current_mission()
589 | if mission.failed:
590 | msg_title = "Network aborted !"
591 | msg_debriefing = " Aircraft destroyed, TAB to restart"
592 | elif mission.aborted:
593 | msg_title = "Network aborted !"
594 | msg_debriefing = "TAB to restart"
595 | else:
596 | msg_title = "Successful !"
597 | msg_debriefing = "Congratulation commander !"
598 | Overlays.add_text2D(msg_title, hg.Vec2(0.5, 771 / 900 - 0.15), 0.04, hg.Color(1, 0.9, 0.3, 1), main.title_font, hg.DTHA_Center)
599 | Overlays.add_text2D(msg_debriefing, hg.Vec2(0.5, 701 / 900 - 0.15), 0.02, hg.Color(1, 0.9, 0.8, 1), main.hud_font, hg.DTHA_Center)
600 |
601 | @classmethod
602 | def init(cls):
603 | cls.beep_ref = hg.LoadWAVSoundAsset("sfx/blip.wav")
604 | cls.beep_state = tools.create_stereo_sound_state(hg.SR_Once)
605 | cls.beep_state.volume = 0.25
606 |
607 | cls.validation_ref = hg.LoadWAVSoundAsset("sfx/blip2.wav")
608 | cls.validation_state = tools.create_stereo_sound_state(hg.SR_Once)
609 | cls.validation_state.volume = 0.5
610 |
611 | cls.missions.append(Mission("Network mode", ["Eurofighter"], ["Rafale"], 1, 1, Missions.network_mode_setup, Missions.network_mode_end_test, Missions.network_mode_end_phase_update))
612 |
613 | cls.missions.append(Mission("Training with Rafale", [], ["Rafale"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
614 | cls.missions.append(Mission("Training with Eurofighter", [], ["Eurofighter"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
615 | cls.missions.append(Mission("Training with TFX", [], ["TFX"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
616 | cls.missions.append(Mission("Training with F16", [], ["F16"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
617 | cls.missions.append(Mission("Training with Miuss", [], ["Miuss"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
618 | #cls.missions.append(Mission("Training with F14", [], ["F14"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
619 | #cls.missions.append(Mission("Training with F14 2", [], ["F14_2"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
620 |
621 | cls.missions.append(Mission("One on one", ["Rafale"], ["Eurofighter"], 1, 1, Missions.mission_setup_players, Missions.mission_one_against_x_end_test, Missions.mission_one_against_x_end_phase_update))
622 | cls.missions.append(Mission("Fight against 2 ennemies", ["Rafale"] * 2, ["Eurofighter"], 1, 1, Missions.mission_setup_players, Missions.mission_one_against_x_end_test, Missions.mission_one_against_x_end_phase_update))
623 | cls.missions.append(Mission("Fight against 3 ennemies", ["Rafale"] * 1 + ["F16"] * 2, ["Eurofighter"], 1, 1, Missions.mission_setup_players, Missions.mission_one_against_x_end_test, Missions.mission_one_against_x_end_phase_update))
624 | cls.missions.append(Mission("Fight against 4 ennemies", ["Rafale"] * 2 + ["F16"] * 2, ["TFX", "Eurofighter"], 1, 1, Missions.mission_setup_players, Missions.mission_one_against_x_end_test, Missions.mission_one_against_x_end_phase_update))
625 | cls.missions.append(Mission("Fight against 5 ennemies", ["Rafale"] * 5, ["TFX", "Eurofighter", "F16"], 1, 1, Missions.mission_setup_players, Missions.mission_one_against_x_end_test, Missions.mission_one_against_x_end_phase_update))
626 |
627 | cls.missions.append(Mission("War: 5 allies against 5 ennemies", ["Rafale"] * 3 + ["Eurofighter"] * 2, ["TFX"] * 2 + ["F16"] * 2 + ["Eurofighter"] * 1, 1, 1, Missions.mission_total_war_setup_players, Missions.mission_war_end_test, Missions.mission_war_end_phase_update))
628 | #cls.missions.append(Mission("Total War: 12 allies against 12 ennemies", ["Rafale"] * 12, ["TFX"] * 4 + ["Eurofighter"] * 4 + ["F16"] * 4 + ["Eurofighter"] * 4, 2, 2, Missions.mission_total_war_setup_players, Missions.mission_war_end_test, Missions.mission_war_end_phase_update))
629 | #cls.missions.append(Mission("Crash test: 60 allies against 60 ennemies", ["Rafale"] * 30 + ["Eurofighter"] * 30, ["TFX"] * 30 + ["Eurofighter"] * 20 + ["F16"] * 10, 5, 5, Missions.mission_total_war_setup_players, Missions.mission_war_end_test, Missions.mission_war_end_phase_update))
630 |
--------------------------------------------------------------------------------
/bak/Missions_dogfight.bak:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2018-2021 Eric Kernin, NWNC HARFANG.
2 |
3 | import harfang as hg
4 | from Animations import *
5 | import tools
6 | from random import uniform
7 | from math import radians
8 | import json
9 | import network_server as netws
10 | from overlays import *
11 | from MachineDevice import *
12 |
13 | class Mission:
14 |
15 | def __init__(self, title, num_ennemies, num_allies, num_carriers_ennemies, num_carriers_allies, setup_players, end_test, end_phase_update):
16 | self.title = title
17 | self.ennemies = num_ennemies
18 | self.allies = num_allies
19 | self.allies_carriers = num_carriers_allies
20 | self.ennemies_carriers = num_carriers_ennemies
21 | self.setup_players_f = setup_players
22 | self.end_test_f = end_test
23 | self.update_end_phase_f = end_phase_update
24 | self.failed = False
25 | self.aborted = False
26 |
27 | def reset(self):
28 | self.failed = False
29 | self.aborted = False
30 |
31 | def setup_players(self, main):
32 | self.setup_players_f(main)
33 |
34 | def end_test(self, main):
35 | return self.end_test_f(main)
36 |
37 | def update_end_phase(self, main, dts):
38 | self.update_end_phase_f(main, dts)
39 |
40 | class Missions:
41 | beep_ref = None
42 | beep_state = None
43 | beep_source = None
44 |
45 | validation_ref = None
46 | validation_state = None
47 | validation_source = None
48 |
49 | # animations mission:
50 | am_start = False
51 | am_anim_x_prec = None
52 | am_anim_a_prec = None
53 | am_anim_x_new = None
54 | am_anim_a_new = None
55 | am_mission_id_prec = 0
56 | am_t = 0
57 |
58 | missions = []
59 | mission_id = 1
60 |
61 | @classmethod
62 | def display_mission_title(cls, main, fade_lvl, dts, yof7):
63 | mcr = 1
64 | mcg = 0.6
65 | mcb = 0.1
66 |
67 | if not cls.am_start:
68 | mx = 0.5
69 | mc = hg.Color(mcr, mcg, mcb, 1) * fade_lvl
70 | mid = cls.mission_id
71 |
72 | if cls.mission_id < len(cls.missions) - 1 and main.keyboard.Pressed(hg.K_Right):
73 | cls.am_start = True
74 | mcpt = 1
75 | xpd = 0
76 | xns = 1
77 |
78 | elif cls.mission_id > 0 and main.keyboard.Pressed(hg.K_Left):
79 | cls.am_start = True
80 | mcpt = -1
81 | xpd = 1
82 | xns = 0
83 |
84 | if cls.am_start:
85 | cls.beep_source = hg.PlayStereo(cls.beep_ref, cls.beep_state)
86 | hg.SetSourceVolume(cls.beep_source, 0.05)
87 | cls.am_mission_id_prec = cls.mission_id
88 | cls.mission_id += mcpt
89 | cls.am_t = 0
90 | cls.am_anim_x_prec = Animation(0, 0.5, 0.5, xpd)
91 | cls.am_anim_a_prec = Animation(0, 0.5, 1, 0)
92 | cls.am_anim_x_new = Animation(0, 0.2, xns, 0.5)
93 | cls.am_anim_a_new = Animation(0, 0.2, 0, 1)
94 | mid = cls.am_mission_id_prec
95 |
96 | else:
97 | cls.am_t += dts
98 | cls.am_anim_x_prec.update(cls.am_t)
99 | cls.am_anim_x_new.update(cls.am_t)
100 | cls.am_anim_a_prec.update(cls.am_t)
101 | cls.am_anim_a_new.update(cls.am_t)
102 |
103 | mx = cls.am_anim_x_new.v
104 | mc = hg.Color(mcr, mcg, mcb, cls.am_anim_a_new.v) * fade_lvl
105 |
106 | mxprec = cls.am_anim_x_prec.v
107 | mcprec = hg.Color(mcr, mcg, mcb, cls.am_anim_a_prec.v) * fade_lvl
108 |
109 | Overlays.add_text2D( Missions.missions[cls.am_mission_id_prec].title, hg.Vec2(mxprec, 671 / 900 + yof7), 0.02, mcprec, main.hud_font, hg.DTHA_Center)
110 | mid = cls.mission_id
111 |
112 | if cls.am_anim_a_new.flag_end and cls.am_anim_x_new.flag_end and cls.am_anim_x_prec.flag_end and cls.am_anim_a_prec:
113 | cls.am_start = False
114 |
115 | Overlays.add_text2D(cls.missions[mid].title, hg.Vec2(mx, 671 / 900 + yof7), 0.02, mc, main.hud_font, hg.DTHA_Center)
116 | Overlays.add_text2D( "<- choose your mission ->", hg.Vec2(0.5, 701 / 900 + yof7), 0.012, hg.Color(1, 0.9, 0.3, fade_lvl * 0.8), main.hud_font, hg.DTHA_Center)
117 |
118 | if main.keyboard.Pressed(hg.K_Space):
119 | cls.validation_source = hg.PlayStereo(cls.validation_ref, cls.validation_state)
120 | hg.SetSourceVolume(cls.validation_source, 1)
121 |
122 | @classmethod
123 | def get_current_mission(cls):
124 | return cls.missions[cls.mission_id]
125 |
126 | @classmethod
127 | def aircrafts_starts_on_carrier(cls, aircrafts, carrier, start, y_orientation, distance, liftoff_delay=0, liftoff_offset=1):
128 | p, r = carrier.get_aircraft_start_point(0)
129 | carrier_alt = p.y
130 | carrier_mat = hg.TransformationMat4(carrier.get_parent_node().GetTransform().GetPos(), carrier.get_parent_node().GetTransform().GetRot())
131 | for i, aircraft in enumerate(aircrafts):
132 | ia_ctrl = aircraft.get_device("IAControlDevice")
133 | if ia_ctrl is not None:
134 | ia_ctrl.IA_liftoff_delay = liftoff_delay
135 | aircraft.flag_landed = True
136 | liftoff_delay += liftoff_offset
137 | start += distance
138 | gear = aircraft.get_device("Gear")
139 | if gear is not None:
140 | bottom_height = gear.gear_height
141 | gear.record_start_state(True)
142 | gear.reset()
143 | else:
144 | bottom_height = aircraft.bottom_height
145 | start.y = carrier_alt + bottom_height
146 | mat = carrier_mat * hg.TransformationMat4(start, hg.Vec3(0, y_orientation, 0))
147 | aircraft.reset_matrix(hg.GetT(mat), hg.GetR(mat))
148 | aircraft.record_start_state()
149 |
150 | @classmethod
151 | def setup_aircrafts_on_carrier(cls, players, aircraft_carriers, start_time):
152 | na = len(players)
153 | nc = len(aircraft_carriers)
154 | na_row = na // (2 * nc)
155 |
156 | for i in range(nc):
157 | n0 = na_row * (2 * i)
158 | n1 = na_row * (2 * i + 1)
159 | n2 = na_row * (2 * i + 2)
160 | if na - n2 <= (na_row+1):
161 | n2 = na
162 | cls.aircrafts_starts_on_carrier(players[n0:n1], aircraft_carriers[i], hg.Vec3(10, 19.5, 80), 0, hg.Vec3(0, 0, -18), start_time + na_row * 2 * i, 2)
163 | cls.aircrafts_starts_on_carrier(players[n1:n2], aircraft_carriers[i], hg.Vec3(-10, 19.5, 62), 0, hg.Vec3(0, 0, -18), start_time + 1 + na_row * 2 * i, 2)
164 |
165 | @classmethod
166 | def aircrafts_starts_in_sky(cls, aircrafts, center: hg.Vec3, range: hg.Vec3, y_orientations_range: hg.Vec2, speed_range: hg.Vec2):
167 | for i, ac in enumerate(aircrafts):
168 | # ac.reset_matrix(hg.Vec3(uniform(center.x-range.x/2, center.x+range.x/2), uniform(center.y-range.y/2, center.y+range.y/2), uniform(center.z-range.z/2, center.z+range.z/2)), hg.Vec3(0, radians(uniform(y_orientations_range.x, y_orientations_range.y)), 0))
169 |
170 | ac.reset_matrix(
171 | hg.Vec3(
172 | uniform(center.x - range.x / 2, center.x + range.x / 2),
173 | uniform(center.y - range.y / 2, center.y + range.y / 2),
174 | uniform(center.z - range.z / 2, center.z + range.z / 2)
175 | ),
176 | hg.Vec3(
177 | 0,
178 | radians(
179 | uniform(y_orientations_range.x, y_orientations_range.y)
180 | ),
181 | 0
182 | )
183 | )
184 | gear = ac.get_device("Gear")
185 | if gear is not None:
186 | gear.record_start_state(False)
187 | gear.reset()
188 | ac.set_linear_speed(uniform(speed_range.x, speed_range.y))
189 | ac.flag_landed = False
190 | ac.record_start_state()
191 |
192 | @classmethod
193 | def setup_carriers(cls, carriers, start_pos, dist, y_orientation):
194 | for carrier in carriers:
195 | carrier.reset_matrix(start_pos, hg.Vec3(0, y_orientation, 0))
196 | start_pos += dist
197 |
198 |
199 | # ============================= Training
200 |
201 | @classmethod
202 | def mission_setup_training(cls, main):
203 |
204 | mission = cls.get_current_mission()
205 | main.create_aircraft_carriers(mission.allies_carriers, mission.ennemies_carriers)
206 | main.create_players(mission.allies, mission.ennemies)
207 |
208 | cls.setup_carriers(main.aircraft_carrier_allies, hg.Vec3(0, 0, 0), hg.Vec3(500, 0, 100), 0)
209 |
210 | n = len(main.players_allies)
211 | # if n == 1:
212 |
213 | cls.aircrafts_starts_on_carrier(main.players_allies, main.aircraft_carrier_allies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20))
214 |
215 | # cls.aircrafts_starts_in_sky(main.players_allies, hg.Vec3(1000, 2000, 0), hg.Vec3(1000, 1000, 20000), hg.Vec2(-180, 180), hg.Vec2(600 / 3.6, 800 / 3.6))
216 |
217 | # elif n > 1:
218 | # cls.aircrafts_starts_on_carrier(main.players_allies[0:n // 2], main.aircraft_carrier_allies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20))
219 | # cls.aircrafts_starts_on_carrier(main.players_allies[n // 2:n], main.aircraft_carrier_allies[0], hg.Vec3(-10, 19.5, 60), 0, hg.Vec3(0, 0, -20))
220 |
221 | fps_start_matrix = main.aircraft_carrier_allies[0].fps_start_point.GetTransform().GetWorld()
222 | main.camera_fps.GetTransform().SetWorld(fps_start_matrix)
223 |
224 | lt = []
225 | for carrier in main.aircraft_carrier_allies:
226 | lt += carrier.landing_targets
227 | for ac in main.players_allies:
228 | ac.set_landing_targets(lt)
229 | ia = ac.get_device("IAControlDevice")
230 | if ia is not None:
231 | ia.activate()
232 |
233 | # ------- Missile Launcher:
234 | main.create_missile_launchers(0, 1)
235 |
236 | plateform = main.scene.GetNode("platform.S400")
237 | ml = main.missile_launchers_ennemies[0]
238 | ml.set_platform(plateform)
239 |
240 | # --------- Views setup
241 | main.setup_views_carousel(True)
242 | main.set_view_carousel("Aircraft_ally_" + str(main.num_players_allies))
243 | main.set_track_view("back")
244 |
245 | main.user_aircraft = main.get_player_from_caroursel_id(main.views_carousel[main.views_carousel_ptr])
246 | main.user_aircraft.set_focus()
247 |
248 | """
249 | ia = main.user_aircraft.get_device("IAControlDevice")
250 | if ia is not None:
251 | ia.set_IA_landing_target(main.aircraft_carrier_allies[0].landing_targets[1])
252 | ia.deactivate()
253 | uctrl = main.user_aircraft.get_device("UserControlDevice")
254 | if uctrl is not None:
255 | uctrl.activate()
256 | """
257 | # Deactivate IA:
258 | for i, ac in enumerate(main.players_allies):
259 | ia = ac.get_device("IAControlDevice")
260 | if ia is not None:
261 | ia.deactivate()
262 |
263 | for i, ac in enumerate(main.players_ennemies):
264 | ia = ac.get_device("IAControlDevice")
265 | if ia is not None:
266 | ia.deactivate()
267 |
268 | uctrl = main.user_aircraft.get_device("UserControlDevice")
269 | if uctrl is not None:
270 | uctrl.activate()
271 |
272 | main.init_playground()
273 |
274 | # if main.num_players_allies < 4:
275 | # main.user_aircraft.set_thrust_level(1)
276 | # main.user_aircraft.activate_post_combustion()
277 |
278 | @classmethod
279 | def mission_training_end_test(cls, main):
280 | mission = cls.get_current_mission()
281 | allies_wreck = 0
282 | for ally in main.players_allies:
283 | if ally.wreck: allies_wreck += 1
284 |
285 | if main.num_players_allies == allies_wreck:
286 | mission.failed = True
287 | print("MISSION FAILED !")
288 | return True
289 |
290 | return False
291 |
292 | @classmethod
293 | def mission_training_end_phase_update(cls, main, dts):
294 | mission = cls.get_current_mission()
295 | if mission.failed:
296 | msg_title = "Mission Failed !"
297 | msg_debriefing = " You need more lessons commander..."
298 | elif mission.aborted:
299 | msg_title = "Training aborted !"
300 | msg_debriefing = "Ready for fight ?"
301 | else:
302 | msg_title = "Training successful !"
303 | msg_debriefing = "Congratulation commander !"
304 | Overlays.add_text2D(msg_title, hg.Vec2(0.5, 771 / 900 - 0.15), 0.04, hg.Color(1, 0.9, 0.3, 1), main.title_font, hg.DTHA_Center)
305 | Overlays.add_text2D(msg_debriefing, hg.Vec2(0.5, 701 / 900 - 0.15), 0.02, hg.Color(1, 0.9, 0.8, 1), main.hud_font, hg.DTHA_Center)
306 |
307 | # ============================= Basic fight
308 |
309 | @classmethod
310 | def mission_setup_players(cls, main):
311 | mission = cls.get_current_mission()
312 | main.create_aircraft_carriers(mission.allies_carriers, mission.ennemies_carriers)
313 | main.create_players(mission.allies, mission.ennemies)
314 |
315 | cls.setup_carriers(main.aircraft_carrier_allies, hg.Vec3(0, 0, 0), hg.Vec3(500, 0, 100), 0)
316 | cls.setup_carriers(main.aircraft_carrier_ennemies, hg.Vec3(-17000, 0, 0), hg.Vec3(500, 0, -150), radians(90))
317 |
318 | main.init_playground()
319 |
320 | n = len(main.players_allies)
321 | if n == 1:
322 | cls.aircrafts_starts_on_carrier(main.players_allies, main.aircraft_carrier_allies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20))
323 | elif n > 1:
324 | cls.aircrafts_starts_on_carrier(main.players_allies[0:n // 2], main.aircraft_carrier_allies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20), 2, 2)
325 | cls.aircrafts_starts_on_carrier(main.players_allies[n // 2:n], main.aircraft_carrier_allies[0], hg.Vec3(-10, 19.5, 60), 0, hg.Vec3(0, 0, -20), 3, 2)
326 |
327 | n = len(main.players_ennemies)
328 | if n < 3:
329 | cls.aircrafts_starts_in_sky(main.players_ennemies, hg.Vec3(-5000, 1000, 0), hg.Vec3(1000, 500, 2000), hg.Vec2(-180, 180), hg.Vec2(800 / 3.6, 600 / 3.6))
330 | else:
331 | cls.aircrafts_starts_in_sky(main.players_ennemies[0:2], hg.Vec3(-5000, 1000, 0), hg.Vec3(1000, 500, 2000), hg.Vec2(-180, 180), hg.Vec2(800 / 3.6, 600 / 3.6))
332 | cls.aircrafts_starts_on_carrier(main.players_ennemies[2:n], main.aircraft_carrier_ennemies[0], hg.Vec3(-10, 19.5, 60), 0, hg.Vec3(0, 0, -20), 2, 1)
333 |
334 | for i, ac in enumerate(main.players_allies):
335 | ia = ac.get_device("IAControlDevice")
336 | if ia is not None:
337 | ia.activate()
338 |
339 | for i, ac in enumerate(main.players_ennemies):
340 | ia = ac.get_device("IAControlDevice")
341 | if ia is not None:
342 | ia.activate()
343 |
344 | main.setup_views_carousel(False)
345 | main.set_view_carousel("Aircraft_ally_" + str(main.num_players_allies))
346 | main.set_track_view("back")
347 |
348 | main.user_aircraft = main.get_player_from_caroursel_id(main.views_carousel[main.views_carousel_ptr])
349 | main.user_aircraft.set_focus()
350 |
351 | if main.user_aircraft is not None:
352 | ia = main.user_aircraft.get_device("IAControlDevice")
353 | if ia is not None:
354 | ia.deactivate()
355 | uctrl = main.user_aircraft.get_device("UserControlDevice")
356 | if uctrl is not None:
357 | uctrl.activate()
358 | if main.num_players_allies < 3:
359 | main.user_aircraft.reset_thrust_level(1)
360 | main.user_aircraft.activate_post_combustion()
361 |
362 | @classmethod
363 | def mission_one_against_x_end_test(cls, main):
364 | mission = cls.get_current_mission()
365 | ennemies_wreck = 0
366 | allies_wreck = 0
367 | for ennemy in main.players_ennemies:
368 | if ennemy.wreck: ennemies_wreck += 1
369 | for ally in main.players_allies:
370 | if ally.wreck: allies_wreck += 1
371 |
372 | if main.num_players_ennemies == ennemies_wreck:
373 | mission.failed = False
374 | return True
375 | if main.num_players_allies == allies_wreck:
376 | mission.failed = True
377 | return True
378 |
379 | return False
380 |
381 | @classmethod
382 | def mission_one_against_x_end_phase_update(cls, main, dts):
383 | mission = cls.get_current_mission()
384 | if mission.failed:
385 | msg_title = "Mission Failed !"
386 | msg_debriefing = " R.I.P. Commander..."
387 | elif mission.aborted:
388 | msg_title = "Mission aborted !"
389 | msg_debriefing = "We hope you do better next time !"
390 | else:
391 | msg_title = "Mission successful !"
392 | msg_debriefing = "Congratulation commander !"
393 | Overlays.add_text2D(msg_title, hg.Vec2(0.5, 771 / 900 - 0.15), 0.04, hg.Color(1, 0.9, 0.3, 1), main.title_font, hg.DTHA_Center)
394 | Overlays.add_text2D(msg_debriefing, hg.Vec2(0.5, 701 / 900 - 0.15), 0.02, hg.Color(1, 0.9, 0.8, 1), main.hud_font, hg.DTHA_Center)
395 |
396 | # ============================ War fight
397 | @classmethod
398 | def mission_total_war_setup_players(cls, main):
399 |
400 | mission = cls.get_current_mission()
401 | main.create_aircraft_carriers(mission.allies_carriers, mission.ennemies_carriers)
402 | main.create_players(mission.allies, mission.ennemies)
403 |
404 | cls.setup_carriers(main.aircraft_carrier_allies, hg.Vec3(0, 0, 0), hg.Vec3(300, 0, 25), 0)
405 | cls.setup_carriers(main.aircraft_carrier_ennemies, hg.Vec3(-20000, 0, 0), hg.Vec3(50, 0, -350), radians(90))
406 |
407 |
408 | cls.setup_aircrafts_on_carrier(main.players_allies, main.aircraft_carrier_allies, 0)
409 |
410 | n = len(main.players_ennemies)
411 | cls.aircrafts_starts_in_sky(main.players_ennemies[0:n // 2], hg.Vec3(-8000, 1000, 0), hg.Vec3(2000, 500, 2000), hg.Vec2(-180, -175), hg.Vec2(800 / 3.6, 600 / 3.6))
412 |
413 | cls.setup_aircrafts_on_carrier(main.players_ennemies[n//2:n], main.aircraft_carrier_ennemies, 60)
414 |
415 | main.init_playground()
416 |
417 | for i, ac in enumerate(main.players_allies):
418 | ia = ac.get_device("IAControlDevice")
419 | if ia is not None:
420 | ia.activate()
421 |
422 | for i, ac in enumerate(main.players_ennemies):
423 | ia = ac.get_device("IAControlDevice")
424 | if ia is not None:
425 | ia.activate()
426 |
427 | main.setup_views_carousel()
428 | main.set_view_carousel("Aircraft_ally_" + str(main.num_players_allies))
429 | main.set_track_view("back")
430 |
431 | main.user_aircraft = main.get_player_from_caroursel_id(main.views_carousel[main.views_carousel_ptr])
432 | main.user_aircraft.set_focus()
433 |
434 | if main.user_aircraft is not None:
435 | ia = main.user_aircraft.get_device("IAControlDevice")
436 | if ia is not None:
437 | ia.deactivate()
438 | uctrl = main.user_aircraft.get_device("UserControlDevice")
439 | if uctrl is not None:
440 | uctrl.activate()
441 | if main.num_players_allies < 4:
442 | main.user_aircraft.reset_thrust_level(1)
443 | main.user_aircraft.activate_post_combustion()
444 |
445 | @classmethod
446 | def mission_war_end_test(cls, main):
447 | mission = cls.get_current_mission()
448 | if main.keyboard.Pressed(hg.K_F6):
449 | for pl in main.players_allies:
450 | pl.flag_IA_start_liftoff = True
451 | for pl in main.players_ennemies:
452 | pl.flag_IA_start_liftoff = True
453 | # Pour le moment, même test de fin de mission
454 | return cls.mission_one_against_x_end_test(main)
455 |
456 | @classmethod
457 | def mission_war_end_phase_update(cls, main, dts):
458 | mission = cls.get_current_mission()
459 | if mission.failed:
460 | msg_title = "Mission Failed !"
461 | msg_debriefing = " R.I.P. Commander..."
462 | elif mission.aborted:
463 | msg_title = "Mission aborted !"
464 | msg_debriefing = "We hope you do better next time !"
465 | else:
466 | msg_title = "Mission successful !"
467 | msg_debriefing = "Congratulation commander !"
468 |
469 | Overlays.add_text2D(msg_title, hg.Vec2(0.5, 771 / 900 - 0.15), 0.04, hg.Color(1, 0.9, 0.3, 1), main.title_font, hg.DTHA_Center)
470 | Overlays.add_text2D(msg_debriefing, hg.Vec2(0.5, 701 / 900 - 0.15), 0.02, hg.Color(1, 0.9, 0.8, 1), main.hud_font, hg.DTHA_Center)
471 |
472 | # ============================ Client / Server mode
473 | @classmethod
474 | def network_mode_setup(cls, main):
475 | mission = cls.get_current_mission()
476 | main.flag_network_mode = True
477 |
478 | file_name = "scripts/network_mission_config.json"
479 | file = hg.OpenText(file_name)
480 | if not file:
481 | print("Can't open mission configuration json file : " + file_name)
482 | else:
483 | json_script = hg.ReadString(file)
484 | hg.Close(file)
485 | if json_script != "":
486 | script_parameters = json.loads(json_script)
487 | mission.allies = script_parameters["aircrafts_allies"]
488 | mission.ennemies = script_parameters["aircrafts_ennemies"]
489 | mission.allies_carriers = script_parameters["aircraft_carriers_allies_count"]
490 | mission.ennemies_carriers = script_parameters["aircraft_carriers_enemies_count"]
491 | else:
492 | print("Mission configuration json file empty : " + file_name)
493 |
494 | main.create_aircraft_carriers(mission.allies_carriers, mission.ennemies_carriers)
495 | main.create_players(mission.allies, mission.ennemies)
496 |
497 | cls.setup_carriers(main.aircraft_carrier_allies, hg.Vec3(0, 0, 0), hg.Vec3(500, 0, 100), 0)
498 | cls.setup_carriers(main.aircraft_carrier_ennemies, hg.Vec3(-17000, 0, 0), hg.Vec3(500, 0, -150), radians(90))
499 |
500 | main.init_playground()
501 |
502 | # Sets aircrafts landed on carriers:
503 |
504 | n = len(main.players_allies)
505 | if n == 1:
506 | cls.aircrafts_starts_on_carrier(main.players_allies, main.aircraft_carrier_allies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20))
507 | elif n > 1:
508 | cls.aircrafts_starts_on_carrier(main.players_allies[0:n // 2], main.aircraft_carrier_allies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20), 2, 2)
509 | cls.aircrafts_starts_in_sky(
510 | aircrafts=main.players_allies[n // 2:n],
511 | center=hg.Vec3(-10, 1500, 60), # (x, z, y), recommend height 1500
512 | # range=hg.Vec3(-10, 19.5, 60),
513 | range=hg.Vec3(0, 0, 0),
514 | y_orientations_range=hg.Vec2(270, 270), # heading
515 | speed_range=hg.Vec2(400, 400) # m/s, recommend 400
516 | )
517 |
518 | n = len(main.players_ennemies)
519 | if n == 1:
520 | cls.aircrafts_starts_on_carrier(main.players_ennemies, main.aircraft_carrier_ennemies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20))
521 | elif n > 1:
522 | cls.aircrafts_starts_on_carrier(main.players_ennemies[0:n // 2], main.aircraft_carrier_ennemies[0], hg.Vec3(10, 19.5, 40), 0, hg.Vec3(0, 0, -20), 2, 2)
523 | cls.aircrafts_starts_in_sky(
524 | aircrafts=main.players_ennemies[n // 2:n],
525 | center=hg.Vec3(-5000, 1500, 60), # (x, z, y), recommend height 1500
526 | # range=hg.Vec3(-10, 19.5, 60),
527 | range=hg.Vec3(0, 0, 0),
528 | y_orientations_range=hg.Vec2(90, 90), # heading
529 | speed_range=hg.Vec2(400, 400) # m/s, recommend 400
530 | )
531 |
532 | # Deactivate IA:
533 | for i, ac in enumerate(main.players_allies):
534 | ia = ac.get_device("IAControlDevice")
535 | if ia is not None:
536 | ia.deactivate()
537 |
538 | for i, ac in enumerate(main.players_ennemies):
539 | ia = ac.get_device("IAControlDevice")
540 | if ia is not None:
541 | ia.deactivate()
542 |
543 | # ------- Missile Launcher:
544 | main.create_missile_launchers(0, 1)
545 |
546 | plateform = main.scene.GetNode("platform.S400")
547 | ml = main.missile_launchers_ennemies[0]
548 | ml.set_platform(plateform)
549 |
550 | # --------- Views setup
551 | main.setup_views_carousel(True)
552 | main.set_view_carousel("Aircraft_ally_1")# + str(main.num_players_allies))
553 | main.set_track_view("back")
554 |
555 | main.init_playground()
556 |
557 | main.user_aircraft = main.get_player_from_caroursel_id(main.views_carousel[main.views_carousel_ptr])
558 | main.user_aircraft.set_focus()
559 |
560 | uctrl = main.user_aircraft.get_device("UserControlDevice")
561 | if uctrl is not None:
562 | uctrl.activate()
563 |
564 | netws.init_server(main)
565 | netws.start_server()
566 |
567 | @classmethod
568 | def network_mode_end_test(cls, main):
569 | """
570 | mission = cls.get_current_mission()
571 |
572 | allies_wreck = 0
573 | for ally in main.players_allies:
574 | if ally.wreck:
575 | allies_wreck += 1
576 |
577 | if main.num_players_allies == allies_wreck:
578 | mission.failed = True
579 | return True
580 | """
581 | return False
582 |
583 | @classmethod
584 | def network_mode_end_phase_update(cls, main, dts):
585 | if main.flag_network_mode:
586 | main.flag_network_mode = False
587 | netws.stop_server()
588 | mission = cls.get_current_mission()
589 | if mission.failed:
590 | msg_title = "Network aborted !"
591 | msg_debriefing = " Aircraft destroyed, TAB to restart"
592 | elif mission.aborted:
593 | msg_title = "Network aborted !"
594 | msg_debriefing = "TAB to restart"
595 | else:
596 | msg_title = "Successful !"
597 | msg_debriefing = "Congratulation commander !"
598 | Overlays.add_text2D(msg_title, hg.Vec2(0.5, 771 / 900 - 0.15), 0.04, hg.Color(1, 0.9, 0.3, 1), main.title_font, hg.DTHA_Center)
599 | Overlays.add_text2D(msg_debriefing, hg.Vec2(0.5, 701 / 900 - 0.15), 0.02, hg.Color(1, 0.9, 0.8, 1), main.hud_font, hg.DTHA_Center)
600 |
601 | @classmethod
602 | def init(cls):
603 | cls.beep_ref = hg.LoadWAVSoundAsset("sfx/blip.wav")
604 | cls.beep_state = tools.create_stereo_sound_state(hg.SR_Once)
605 | cls.beep_state.volume = 0.25
606 |
607 | cls.validation_ref = hg.LoadWAVSoundAsset("sfx/blip2.wav")
608 | cls.validation_state = tools.create_stereo_sound_state(hg.SR_Once)
609 | cls.validation_state.volume = 0.5
610 |
611 | cls.missions.append(Mission("Network mode", ["Eurofighter"], ["Rafale"], 1, 1, Missions.network_mode_setup, Missions.network_mode_end_test, Missions.network_mode_end_phase_update))
612 |
613 | cls.missions.append(Mission("Training with Rafale", [], ["Rafale"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
614 | cls.missions.append(Mission("Training with Eurofighter", [], ["Eurofighter"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
615 | cls.missions.append(Mission("Training with TFX", [], ["TFX"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
616 | cls.missions.append(Mission("Training with F16", [], ["F16"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
617 | cls.missions.append(Mission("Training with Miuss", [], ["Miuss"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
618 | #cls.missions.append(Mission("Training with F14", [], ["F14"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
619 | #cls.missions.append(Mission("Training with F14 2", [], ["F14_2"], 0, 1, Missions.mission_setup_training, Missions.mission_training_end_test, Missions.mission_training_end_phase_update))
620 |
621 | cls.missions.append(Mission("One on one", ["Rafale"], ["Eurofighter"], 1, 1, Missions.mission_setup_players, Missions.mission_one_against_x_end_test, Missions.mission_one_against_x_end_phase_update))
622 | cls.missions.append(Mission("Fight against 2 ennemies", ["Rafale"] * 2, ["Eurofighter"], 1, 1, Missions.mission_setup_players, Missions.mission_one_against_x_end_test, Missions.mission_one_against_x_end_phase_update))
623 | cls.missions.append(Mission("Fight against 3 ennemies", ["Rafale"] * 1 + ["F16"] * 2, ["Eurofighter"], 1, 1, Missions.mission_setup_players, Missions.mission_one_against_x_end_test, Missions.mission_one_against_x_end_phase_update))
624 | cls.missions.append(Mission("Fight against 4 ennemies", ["Rafale"] * 2 + ["F16"] * 2, ["TFX", "Eurofighter"], 1, 1, Missions.mission_setup_players, Missions.mission_one_against_x_end_test, Missions.mission_one_against_x_end_phase_update))
625 | cls.missions.append(Mission("Fight against 5 ennemies", ["Rafale"] * 5, ["TFX", "Eurofighter", "F16"], 1, 1, Missions.mission_setup_players, Missions.mission_one_against_x_end_test, Missions.mission_one_against_x_end_phase_update))
626 |
627 | cls.missions.append(Mission("War: 5 allies against 5 ennemies", ["Rafale"] * 3 + ["Eurofighter"] * 2, ["TFX"] * 2 + ["F16"] * 2 + ["Eurofighter"] * 1, 1, 1, Missions.mission_total_war_setup_players, Missions.mission_war_end_test, Missions.mission_war_end_phase_update))
628 | #cls.missions.append(Mission("Total War: 12 allies against 12 ennemies", ["Rafale"] * 12, ["TFX"] * 4 + ["Eurofighter"] * 4 + ["F16"] * 4 + ["Eurofighter"] * 4, 2, 2, Missions.mission_total_war_setup_players, Missions.mission_war_end_test, Missions.mission_war_end_phase_update))
629 | #cls.missions.append(Mission("Crash test: 60 allies against 60 ennemies", ["Rafale"] * 30 + ["Eurofighter"] * 30, ["TFX"] * 30 + ["Eurofighter"] * 20 + ["F16"] * 10, 5, 5, Missions.mission_total_war_setup_players, Missions.mission_war_end_test, Missions.mission_war_end_phase_update))
630 |
--------------------------------------------------------------------------------