-
Notifications
You must be signed in to change notification settings - Fork 0
/
game_action.py
312 lines (280 loc) · 13 KB
/
game_action.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
import os
import time
import threading
import numpy as np
from collections import deque
from hero.naima import Naima
from game_control import GameControl
from utils.action_utils import calculate_bottom_center, calculate_box_center, calculate_distance, find_closest_or_second_closest_box_to_point, calculate_iou, find_close_box_to_point, find_farthest_box_to_box, calculate_point_to_box_angle, calculate_point_to_gate_angle, calculate_angle, is_image_almost_black, calculate_point_to_box_angle, find_farthest_box_to_box
from utils.cv2_matcher import CV2Matcher
class GameAction:
def __init__(self, ctrl: GameControl, queue):
self.queue = queue
self.ctrl = ctrl
self.stop_event = True
self.reset_event = False
self.control_attack = Naima(ctrl)
self.room_num = -1
self.buwanjia = [8, 10, 10, 11, 9, ] + [10] * 20
self.matcher = CV2Matcher(os.path.join(os.path.dirname(
os.path.abspath(__file__)), "./template.json"))
self.special_command = 'update_room'
self.use_diamond = False # 用黑砖来判断hero的位置
self.diamond_to_hero_offset = None # 黑砖到hero的偏移
self.last_closest_door = None # 上一帧最近的门以防止走错
self.timers = {
'each_time': 0,
'refresh_time': 0,
'door_time': 0,
}
self.thread_run = True
self.thread = threading.Thread(target=self.control) # 创建线程,并指定目标函数
self.thread.daemon = True # 设置为守护线程(可选)
self.thread.start()
def reset_timers(self):
for name in self.timers:
self.timers[name] = 0
def get_time(self, name):
return time.time() - self.timers[name]
def update_time(self, name):
self.timers[name] = time.time()
def reset(self):
self.thread_run = False
time.sleep(0.1)
self.room_num = -1
self.reset_timers()
self.special_command = 'update_room'
self.thread_run = True
self.thread = threading.Thread(target=self.control) # 创建线程,并指定目标函数
self.thread.daemon = True # 设置为守护线程(可选)
self.thread.start()
def control(self):
self.buwanjia_control()
def street_control(self, image, boxs):
pass
def buwanjia_control(self):
last_room_pos = []
hero_track = deque()
hero_track.appendleft([0, 0])
while self.thread_run:
# 10s 防卡死
if self.get_time('refresh_time') > 10:
self.ctrl.reset()
self.update_time('refresh_time')
# 等待数据
if self.stop_event:
time.sleep(0.001)
self.ctrl.reset()
continue
if self.queue.empty():
time.sleep(0.001)
continue
# 等待过图黑屏
image, boxs = self.queue.get()
if is_image_almost_black(image):
if self.get_time('door_time') > 1.2:
print("过图")
last_room_pos = hero_track[0]
hero_track = deque()
hero_track.appendleft(
[1-last_room_pos[0], 1-last_room_pos[1]])
self.ctrl.reset()
self.update_time('door_time')
self.special_command = 'update_room'
else:
continue
# 预处理检测结果
hero = boxs[boxs[:, 5] == 6][:, :4]
gate = boxs[boxs[:, 5] == self.buwanjia[self.room_num]][:, :4]
arrow = boxs[boxs[:, 5] == 5][:, :4]
equipment = [[detection[0], detection[1] + (detection[3] - detection[1]), detection[2], detection[3] + (detection[3] - detection[1]), detection[4], detection[5]]
for detection in boxs if detection[5] == 4 and detection[4] > 0.3]
monster = boxs[boxs[:, 5] <= 2][:, :4]
card = boxs[boxs[:, 5] == 3][:, :4]
pet = boxs[boxs[:, 5] == 12][:, :4]
diamond = boxs[boxs[:, 5] == 13][:, :4]
# 更新房间,目标门
angle = 0
outprint = ''
if self.special_command == 'update_room':
if len(hero) > 0: # 记录房间号
self.room_num += 1
self.special_command = None
print("房间号:", self.room_num)
print("目标", self.buwanjia[self.room_num])
else:
continue
# 计算英雄位置
self.calculate_hero_pos(hero_track, hero, diamond)
self.last_closest_door = None # 重写门逻辑
# 过滤和英雄/宠物IoU大于0.8的monster(误检)
filter_monster = [hero, pet]
delete_index = []
for obj in filter_monster:
if len(obj) == 0 or len(monster) == 0:
continue
for i in range(len(monster)):
if calculate_iou(monster[i], obj[0]) > 0.8:
delete_index.append(i)
break
monster = np.delete(monster, delete_index, axis=0)
# 过图逻辑
if len(card) >= 8:
time.sleep(1+np.random.rand())
self.ctrl.click(0.25*image.shape[0], 0.25*image.shape[1])
self.special_command = 'finish'
time.sleep(2.)
elif len(monster) > 0:
outprint = '有怪物'
angle = self.control_attack.control(
hero_track[0], image, boxs, self.room_num)
elif len(equipment) > 0:
outprint = '有材料'
if len(gate) > 0:
close_gate, distance = find_close_box_to_point(
gate, hero_track[0]) # 找到最近的门
target_item, distance = find_farthest_box_to_box(
equipment, close_gate) # 找到离最近门最远的材料
angle = calculate_point_to_box_angle(
hero_track[0], target_item)
else:
target_item, distance = find_close_box_to_point(
equipment, hero_track[0]) # 找到最近的材料
angle = calculate_point_to_box_angle(
hero_track[0], target_item)
self.ctrl.attack(False)
self.ctrl.move(angle)
elif len(gate) > 0 and not (self.room_num == 6 and len(arrow) > 3):
outprint = '有门'
close_gate, distance = find_close_box_to_point(
gate, hero_track[0])
if self.room_num == 4 and distance < 0.4:
angle = calculate_point_to_gate_angle( # 左门位置偏高
hero_track[0], close_gate)
self.ctrl.attack(False)
self.ctrl.move(angle)
else:
angle = calculate_point_to_box_angle(
hero_track[0], close_gate)
self.ctrl.attack(False)
self.ctrl.move(angle)
elif len(arrow) > 0 and self.room_num != 4:
outprint = '有箭头'
close_arrow, distance = find_closest_or_second_closest_box_to_point(
arrow, hero_track[0])
angle = calculate_point_to_box_angle(
hero_track[0], close_arrow)
self.ctrl.attack(False)
self.ctrl.move(angle)
elif self.special_command == 'finish':
if self.find_and_click(image, "repair", check_until_disappear=False):
self.special_command = 'repair_ok'
time.sleep(1.+np.random.normal(0, 0.3))
elif self.find_and_click(image, "retry", check_until_disappear=False):
self.special_command = 'retry_ok'
time.sleep(1.5+np.random.normal(0, 0.3))
elif self.special_command == 'repair_ok':
self.find_and_click(image, "repair_ok")
self.special_command = 'repair_cancel'
elif self.special_command == 'repair_cancel':
self.find_and_click(image, "repair_cancel",
check_until_disappear=False)
self.special_command = 'finish'
elif self.special_command == 'retry_ok':
self.find_and_click(image, "ok")
self.ctrl.reset()
self.room_num = 0
hero_track = deque()
hero_track.appendleft([0, 0])
self.special_command = None
else:
outprint = "无目标"
if self.room_num == 4:
angle = calculate_angle(
hero_track[0], [0.25, 0.6])
else:
angle = calculate_angle(
hero_track[0], [0.5, 0.75])
self.ctrl.move(angle)
self.ctrl.attack(False)
interval = self.get_time('each_time')
self.update_time('each_time')
print(
f"\r当前进度:{outprint},角度{angle},位置{hero_track[0]}, 耗时{interval},特定命令:{self.special_command}", end="")
def find(self, image, target):
# 找2次
for _ in range(2):
target_box = self.matcher.match(image, target)
if target_box is not None:
break
time.sleep(0.2)
if target_box is None:
return None
return calculate_box_center(target_box)
def find_and_click(self, image, target, random_r=4, check_until_disappear=True):
target_point = self.find(image, target)
if target_point is None:
return False
self.ctrl.click(target_point[0], target_point[1], random_r)
if not check_until_disappear:
return True
# 确认消失
for _ in range(4):
time.sleep(0.5)
target_box = self.matcher.match(image, target)
if target_box is None:
return True
self.ctrl.click(target_point[0], target_point[1], random_r)
return False
def calculate_hero_pos(self, hero_track, hero_boxs, diamond_boxs):
if self.use_diamond:
# 当有一个hero和一个diamond的时候更新offset
if len(hero_boxs) == 1 and len(diamond_boxs) == 1:
diamond_center = calculate_bottom_center(diamond_boxs[0])
hero_center = calculate_bottom_center(hero_boxs[0])
if self.diamond_to_hero_offset is None:
self.diamond_to_hero_offset = [
hero_center[0] - diamond_center[0], hero_center[1] - diamond_center[1]]
else:
self.diamond_to_hero_offset = [
0.9 * self.diamond_to_hero_offset[0] +
0.1 * (hero_center[0] - diamond_center[0]),
0.9 * self.diamond_to_hero_offset[1] +
0.1 * (hero_center[1] - diamond_center[1])]
if len(diamond_boxs) == 0:
pass
elif len(diamond_boxs) == 1:
diamond_center = calculate_bottom_center(diamond_boxs[0])
hero_center = (diamond_center[0] + self.diamond_to_hero_offset[0],
diamond_center[1] + self.diamond_to_hero_offset[1])
hero_track.appendleft(hero_center)
elif len(diamond_boxs) > 1:
ori_diamond_center = (hero_track[0]-self.diamond_to_hero_offset[0],
hero_track[0]-self.diamond_to_hero_offset[1])
for box in diamond_boxs:
box_center = calculate_bottom_center(box)
if calculate_distance(box_center, ori_diamond_center) < 0.1:
hero_track.appendleft(box)
return
hero_track.appendleft(hero_track[0])
else:
if len(hero_boxs) == 0:
pass
elif len(hero_boxs) == 1:
hero_track.appendleft(calculate_bottom_center(hero_boxs[0]))
elif len(hero_boxs) > 1:
for box in hero_boxs:
box_center = calculate_bottom_center(box)
if calculate_distance(box_center, hero_track[0]) < 0.1:
hero_track.appendleft(box)
return
hero_track.appendleft(hero_track[0])
def move_to(self, cur_pos, target_pos, distance_pre_second=0.35):
# distance_pre_second速度越大,相对的位移时间越短,实际位移距离越短
distance = calculate_distance(cur_pos, target_pos)
angle = calculate_angle(cur_pos, target_pos)
self.ctrl.move(angle)
time.sleep(max(distance/distance_pre_second,
0.1+np.random.uniform(0, 0.05)))
self.ctrl.move(0)
return angle