Python을 기반으로 개발을 진행하였습니다.
1. 자기장 데이터 가져오기
자기장을 맵위에 그리기 위해서는 자기장의 반지름 정보와 중심이 되는 점(x,y)의 위치를 알아야 합니다.
자기장의 반지름의 크기와 중심의 위치(x,y)는 LogGameStatePeriodic 아래에 있습니다.
자기장의 위치를 전부 출력 해보면 자기장이 위치가 아주 다양하게 나타나 맵전체를 가리는 자기장이 그려집니다.
이는 자기장의 위치가 담고 있는 정보가 다르기 때문입니다.
좀더 자세히 설명하자면 LogGameStatePeriodic에 isgame라는 정보가 있습니다. isgame의 값에 따라 자기장이 가지는 의미는 다음과 같습니다.
아래의 설명으로 보아 isgame을 자기장의 단계로 보아도 무방하기 때문에 단계별로 나타나는 자기장을 맵위에 그려줍니다.
isGame = 0 -> Before lift off
isGame = 0.1 -> On airplane
isGame = 0.5 -> When there’s no ‘zone’ on map(before game starts)
isGame = 1.0 -> First safezone and bluezone appear
isGame = 1.5 -> First bluezone shrinks
isGame = 2.0 -> Second bluezone appears
isGame = 2.5 -> Second bluezone shrinks
...
이는 아래의 공식 홈페이지에서 확인 하실 수 있습니다.
https://documentation.pubg.com/en/telemetry-objects.html
Telemetry Objects — pubg 1.0 documentation
Telemetry Objects The following is a list of every telemetry object with basic schemas to show the data that they contain. Data dictionaries and enums can be found here. BlueZoneCustomOptions The blueZoneCustomOptions string contains an array of config obj
documentation.pubg.com
위의 정보를 바탕으로 1,2,3과 같이 소수점이 없는 부분이 safezone이 나타나는 것을 의미합니다.
그렇다면 이를 이용해 isgame의 값이 int인 것들의 반지름 크기, 중심 좌표의 정보를 추출합니다.
이렇게 가져온 데이터를 확인해보면 여전히 자기장의 위치 정보가 많이 있다는것을 확인 할 수있습니다. 이는 자기장이 멈춰 있을 때 중복되어 기록되는 현상 때문입니다.
중복되어 나타나는 정보는 제거를 하지 않아도 같은 위치에 한번 더 그리는 것이기 때문에 출력되는 결과는 다르지 않습니다.
하지만 코드를 연습할 겸 중복을 제거한 후 제거한 결과를 확인 해보겠습니다.
우선, 중복을 제거 하기 전 safety_Zone_Radius_list에 들어있는 값들입니다.
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 203699.6875, 'location_x': 537049.875, 'location_y': 190871.28125, 'isgame': 2}
{'safetyZoneRadius': 203699.6875, 'location_x': 537049.875, 'location_y': 190871.28125, 'isgame': 2}
{'safetyZoneRadius': 203699.6875, 'location_x': 537049.875, 'location_y': 190871.28125, 'isgame': 2}
{'safetyZoneRadius': 203699.6875, 'location_x': 537049.875, 'location_y': 190871.28125, 'isgame': 2}
{'safetyZoneRadius': 203699.6875, 'location_x': 537049.875, 'location_y': 190871.28125, 'isgame': 2}
{'safetyZoneRadius': 203699.6875, 'location_x': 537049.875, 'location_y': 190871.28125, 'isgame': 2}
{'safetyZoneRadius': 203699.6875, 'location_x': 537049.875, 'location_y': 190871.28125, 'isgame': 2}
{'safetyZoneRadius': 203699.6875, 'location_x': 537049.875, 'location_y': 190871.28125, 'isgame': 2}
{'safetyZoneRadius': 203699.6875, 'location_x': 537049.875, 'location_y': 190871.28125, 'isgame': 2}
{'safetyZoneRadius': 112034.828125, 'location_x': 605318.6875, 'location_y': 247584.703125, 'isgame': 3}
{'safetyZoneRadius': 112034.828125, 'location_x': 605318.6875, 'location_y': 247584.703125, 'isgame': 3}
{'safetyZoneRadius': 112034.828125, 'location_x': 605318.6875, 'location_y': 247584.703125, 'isgame': 3}
{'safetyZoneRadius': 112034.828125, 'location_x': 605318.6875, 'location_y': 247584.703125, 'isgame': 3}
{'safetyZoneRadius': 112034.828125, 'location_x': 605318.6875, 'location_y': 247584.703125, 'isgame': 3}
{'safetyZoneRadius': 112034.828125, 'location_x': 605318.6875, 'location_y': 247584.703125, 'isgame': 3}
{'safetyZoneRadius': 67220.8984375, 'location_x': 631240.6875, 'location_y': 255418.328125, 'isgame': 4}
{'safetyZoneRadius': 67220.8984375, 'location_x': 631240.6875, 'location_y': 255418.328125, 'isgame': 4}
{'safetyZoneRadius': 67220.8984375, 'location_x': 631240.6875, 'location_y': 255418.328125, 'isgame': 4}
{'safetyZoneRadius': 67220.8984375, 'location_x': 631240.6875, 'location_y': 255418.328125, 'isgame': 4}
{'safetyZoneRadius': 67220.8984375, 'location_x': 631240.6875, 'location_y': 255418.328125, 'isgame': 4}
{'safetyZoneRadius': 67220.8984375, 'location_x': 631240.6875, 'location_y': 255418.328125, 'isgame': 4}
{'safetyZoneRadius': 40332.5390625, 'location_x': 643579.6875, 'location_y': 232269.46875, 'isgame': 5}
{'safetyZoneRadius': 40332.5390625, 'location_x': 643579.6875, 'location_y': 232269.46875, 'isgame': 5}
{'safetyZoneRadius': 40332.5390625, 'location_x': 643579.6875, 'location_y': 232269.46875, 'isgame': 5}
{'safetyZoneRadius': 40332.5390625, 'location_x': 643579.6875, 'location_y': 232269.46875, 'isgame': 5}
{'safetyZoneRadius': 40332.5390625, 'location_x': 643579.6875, 'location_y': 232269.46875, 'isgame': 5}
{'safetyZoneRadius': 40332.5390625, 'location_x': 643579.6875, 'location_y': 232269.46875, 'isgame': 5}
{'safetyZoneRadius': 26216.150390625, 'location_x': 643874.375, 'location_y': 220229.109375, 'isgame': 6}
{'safetyZoneRadius': 26216.150390625, 'location_x': 643874.375, 'location_y': 220229.109375, 'isgame': 6}
{'safetyZoneRadius': 26216.150390625, 'location_x': 643874.375, 'location_y': 220229.109375, 'isgame': 6}
{'safetyZoneRadius': 26216.150390625, 'location_x': 643874.375, 'location_y': 220229.109375, 'isgame': 6}
{'safetyZoneRadius': 26216.150390625, 'location_x': 643874.375, 'location_y': 220229.109375, 'isgame': 6}
{'safetyZoneRadius': 26216.150390625, 'location_x': 643874.375, 'location_y': 220229.109375, 'isgame': 6}
{'safetyZoneRadius': 17040.498046875, 'location_x': 646938.375, 'location_y': 214197.90625, 'isgame': 7}
{'safetyZoneRadius': 17040.498046875, 'location_x': 646938.375, 'location_y': 214197.90625, 'isgame': 7}
{'safetyZoneRadius': 17040.498046875, 'location_x': 646938.375, 'location_y': 214197.90625, 'isgame': 7}
{'safetyZoneRadius': 17040.498046875, 'location_x': 646938.375, 'location_y': 214197.90625, 'isgame': 7}
{'safetyZoneRadius': 17040.498046875, 'location_x': 646938.375, 'location_y': 214197.90625, 'isgame': 7}
{'safetyZoneRadius': 17040.498046875, 'location_x': 646938.375, 'location_y': 214197.90625, 'isgame': 7}
{'safetyZoneRadius': 11076.3232421875, 'location_x': 643173.5, 'location_y': 210807, 'isgame': 8}
단계는 총 8단계이지만 중복된 값들이 아주 많이 보입니다.
converted_list = [tuple(item.items()) for item in safety_Zone_Radius_list]
unique_list = list(set(converted_list))
unique_list_of_dicts = [dict(item) for item in unique_list]
unique_list_of_dicts = sorted(unique_list_of_dicts, key=lambda item: item["safetyZoneRadius"],reverse=True)
1. converted_list = [tuple(item.items()) for item in safety_Zone_Radius_list]
이 부분에서 각 딕셔너리를 튜플의 리스트로 변환합니다. 딕셔너리의 items() 메서드는 키-값 쌍을 튜플 형태로 반환합니다. 리스트 컴프리헨션을 사용하여 각 딕셔너리의 키-값 쌍을 튜플로 변환하고 새로운 리스트 converted_list에 저장합니다.
2. unique_list = list(set(converted_list))
set() 함수를 사용하여 converted_list의 중복 항목을 제거합니다. set()은 중복된 값을 허용하지 않기 때문에 각 튜플이 고유한 값만 포함하게 됩니다. 다시 list() 함수를 사용하여 중복이 제거된 튜플의 리스트로 변환해 unique_list에 저장합니다.
3. unique_list_of_dicts = [dict(item) for item in unique_list]
중복이 제거된 튜플의 리스트 unique_list를 다시 딕셔너리의 리스트로 변환합니다. 리스트 컴프리헨션을 사용하여 각 튜플을 딕셔너리로 변환하고 새로운 리스트 unique_list_of_dicts에 저장합니다.
4. unique_list_of_dicts = sorted(unique_list_of_dicts, key=lambda item: item["safetyZoneRadius"], reverse=True)
sorted() 함수를 사용하여 unique_list_of_dicts를 safetyZoneRadius 키 값에 따라 내림차순으로 정렬합니다. key 매개변수는 각 딕셔너리의 "safetyZoneRadius" 값을 사용하도록 설정됩니다. reverse=True를 설정하여 내림차순 정렬을 수행합니다.
중복을 제거한 unique_list_of_dicts 리스트의 값들은 아래와 같습니다.
중복이 제거되거 8단계가 남은것을 확인 할 수 있습니다.
{'safetyZoneRadius': 581999.125, 'location_x': 408000, 'location_y': 408000, 'isgame': 1}
{'safetyZoneRadius': 203699.6875, 'location_x': 537049.875, 'location_y': 190871.28125, 'isgame': 2}
{'safetyZoneRadius': 112034.828125, 'location_x': 605318.6875, 'location_y': 247584.703125, 'isgame': 3}
{'safetyZoneRadius': 67220.8984375, 'location_x': 631240.6875, 'location_y': 255418.328125, 'isgame': 4}
{'safetyZoneRadius': 40332.5390625, 'location_x': 643579.6875, 'location_y': 232269.46875, 'isgame': 5}
{'safetyZoneRadius': 26216.150390625, 'location_x': 643874.375, 'location_y': 220229.109375, 'isgame': 6}
{'safetyZoneRadius': 17040.498046875, 'location_x': 646938.375, 'location_y': 214197.90625, 'isgame': 7}
{'safetyZoneRadius': 11076.3232421875, 'location_x': 643173.5, 'location_y': 210807, 'isgame': 8}
2. 원 그리기
원을 그리는 방법은 아주 간단합니다. 8단계에 걸쳐 원이 생성되기 때문에 8개의 원을 그리면 됩니다.
cv.circle() 함수를 사용해서 간단하게 원을 그릴 수 있습니다. cv.circle() 함수의 매개변수는 다음과 같습니다.
cv2.circle(img, center, radius, color, thickness=None, lineType=None, shift=None)
- img: 원을 그릴 이미지입니다. Numpy 배열 형태의 이미지 객체입니다.
- center: 원의 중심 좌표(x, y)를 나타내는 정수 형태의 튜플입니다.
- radius: 원의 반지름을 나타내는 정수입니다.
- color: 원의 색을 나타내는 정수 형태의 튜플입니다. (R, G, B) 혹은 (B, G, R) 형태로 적용되며, 일반적으로 OpenCV에서는 (B, G, R) 순서를 따릅니다.
- thickness (선택): 원의 선 두께를 나타내는 정수입니다. 음수 값을 주면 원이 채워집니다. 기본값은 1입니다.
- lineType (선택): 그려지는 원의 선 유형을 정의합니다. cv2.LINE_8, cv2.LINE_4, cv2.LINE_AA 등의 값을 설정할 수 있습니다. 기본값은 cv2.LINE_8입니다.
- shift (선택): 좌표 값의 소수점 이하 비트 수를 나타냅니다. 기본값은 0입니다.
x,y좌표와 반지름의 크기 역시 지도의 비율에 맟추기 위해 0.01을 곱한 값으로 설정합니다.
map_image = cv2.imread(map_image_path)
for Radius_lists in unique_list_of_dicts:
x = Radius_lists['location_x'] * scale_factor
y = Radius_lists['location_y'] * scale_factor
radius = Radius_lists['safetyZoneRadius'] * scale_factor
cv2.circle(map_image, (int(x), int(y)), int(radius), (255, 255, 255), 50)
8개의 원을 그린 후 [게임데이터 분석#4-1]의 코드와[게임데이터 분석 #6]의 코드에 잘 조합하여 실행 하게 되면 결과는 아래와 같습니다.
작은 원 안으로 점점 진입하는 것을 보아 제대로 나온것 같습니다.
3. 이슈
위의 결과가 나오기 전에 원을 그리고 불필요한 코드를 제거 하는 과정에서 이동경로가 누적되어 지도에 나타나는 오류가 발생했습니다.
위의 문제는 이동경로가 들어가는 리스트를 초기화 하지 않고 전역변수로 두었기 때문에 값이 계속해서 누적되어 들어가는 바람에 나타난 문제 였습니다.
해서 전역변수로 두었던 변수를 매판 마다 초기화 시켜 새로운 데이터가 들어가게 구현하여 오류를 수정 할 수있었습니다.
'게임 데이터 분석' 카테고리의 다른 글
[게임 데이터 분석 #1] LOL API 발급 및 데이터 불러오기 (0) | 2023.12.03 |
---|---|
[게임 데이터 분석 #8] opencv를 활용해 비행기 이동 경로, 낙하 경로 등 출력하기 (0) | 2023.07.06 |
[게임 데이터 분석 #6] Mysql 설치, 연동 및 DB에서 데이터 가져오기 (0) | 2023.07.01 |
[게임 데이터 분석 #5] MongoDB를 사용한 데이터 저장 (1) | 2023.06.30 |
[게임 데이터 분석 #4-1] TELEMETRY API를 활용해 판별 MAP 이미지 위에 이동 경로 그리기 (1) | 2023.06.25 |