您的位置:首页 > 编程语言 > Python开发

用VR观看LoL,这到底是如何实现的

2016-11-20 20:52 621 查看
前段时间,网上出现了一段使用HTC vive观看LoL的视频,相关链接在这里。这是由一位名叫Fire-Proof的大神开发完成的,并且大神公布了源代码。作为一名程序员,对牛掰的代码自然充满了好奇,用VR观看LoL,这到底是如何实现的呢?

查看代码发现,程序完全使用Python开发(再次感受到Python的强大),包含的代码并不多,一共也就10几个文件。



首先查看main.py。在最下方定义了
start_app
方法

def start_app():
app = QApplication(sys.argv)
tray_icon = MainDialog()
tray_icon.show()
app.exec_()


程序创建了一个名为MainDialog的对话框,让我们看一下MainDialog中有些什么(代码有删减)

class MainDialog(QDialog, Ui_MainDialog):
def __init__(self, parent=None):
super(MainDialog, self).__init__(parent)
self.spectate = None
self.spectate_started = False
self.setupUi(self)
self.lol_watcher = ProcessWatcher(b"League of Legends")
self.lol_watcher.running.connect(self.lol_watcher_update, Qt.QueuedConnection)
self.lol_watcher.start()
self.vorpx_watcher = ProcessWatcher(b"vorpControl")
self.vorpx_watcher.running.connect(self.vorpx_watcher_update,Qt.QueuedConnection)
self.vorpx_watcher.start()
self.pushButtonStart.clicked.connect(self.toggle_spectate)
def toggle_spectate(self):
if self.spectate_started:
self.pushButtonStart.setText("Start")
self.stop_spectate()
self.spectate_started = False
else:
self.pushButtonStart.setText("Stop")
self.start_spectate()
self.spectate_started = True
def start_spectate(self):
if self.spectate is None:
self.spectate = SpectateThread()
self.spectate.error.connect(self.spectate_error, Qt.QueuedConnection)
self.spectate.start()


对话框创建了一个start按钮,并将该按钮的点击事件绑定到了
toggle_spectate
方法上,该方法会调用
start_spectate
方法,并最终通过
SpectateThread
创建了一个线程。继续查看
SpectateThread
(代码有删减)

class SpectateThread(QThread):
def __init__(self):
super(SpectateThread, self).__init__()
self.spectate = None
def run(self, *args, **kwargs):
self.spectate = VRSpectate()
self.spectate.run()


看来玄机在
VRSpectate
方法中。在查看
VRSpectate
之前,注意到
MainDailog
中开启了两个线程来检测”League of Legends”进程和”vorpControl”进程。前一个很好理解,观看LoL自然需要”League of Legends”进程,那这个”vorpControl”进程又是什么鬼呢?这是vorpX软件的运行进程,用来将非VR游戏的画面转换成VR模式并同步到头显设备中。看到这里是不是有种被欺骗的感觉,通过VR观看LoL是通过软件来实现的?答案确实如此,Python程序并不负责转换LoL的画面到VR设备上,它只是不停的检测VR头显和控制器的状态,并以此调整LoL的画面,这样就可以通过转动头部或触发控制器来以不同的视角观看LoL画面(比如站立时鸟瞰整个战场,蹲下来观看局部战场)。

VRSepecate
run
方法中,会不停的调用
_next_frame
方法来更新画面

def _next_frame(self):
self.scale = self.z_offset
controller_frame = self.vr.controller_frame()
# Camera movement
if len(controller_frame) == 2:
if all(controller_frame.button_pressed("trigger")):
self.yaw_offset += self.prev_controller_frame.relative_rotation - controller_frame.relative_rotation
self.z_offset += (self.prev_controller_frame.relative_distance - controller_frame.relative_distance) * self.z_offset * 2
self._move_offset(self.prev_controller_frame.position(), controller_frame.position())
elif any(controller_frame.button_pressed("trigger")):
active_controller = [i for i, x in enumerate(controller_frame.button_pressed("trigger")) if x][0]
self._move_offset(self.prev_controller_frame.position(active_controller), controller_frame.position(active_controller))
if len(controller_frame) == 1:
if any(controller_frame.button_pressed("trigger")):
self._move_offset(self.prev_controller_frame.position(), controller_frame.position(), update_z=True)

self.prev_controller_frame = controller_frame
pos = self.vr.hmd.position()
self.lol.yaw = pos.yaw + self.yaw_offset
self.lol.pitch = pos.pitch*-1
self.lol.x = (pos.x*self.scale) + self.x_offset
self.lol.y = (pos.y*self.scale) + self.y_offset
self.lol.z = (pos.z*self.scale) + self.z_offset


该方法会首先来检测VR控制器是否触发,并获取VR头显的位置信息,根据这些数据来更新LoL的画面信息。其中访问VR设备是通过调用openvr库来实现的,并在OpenVR.py中做了进一步的封装。那么更新LoL画面如何实现呢,答案是修改LoL进程的内存。

在LeagueOfLegends.py中,封装了修改LoL进程内存的方法,例如执行self.lol.x = 520,实际上执行的是下面的代码

@x.setter
def x(self, val):
self._x.write(val, type="float")


而更底层的实现是通过memorpy目录中的相关代码来实现的。那么如何知道应该修改哪些内存位置呢(毕竟直接修改内存一个不慎可能进程就挂了)。涉及到画面信息的内存位置都是通过一个
cam_base_address
进行偏移得到的,作者给出了
cam_base_address
的搜索方法:

HOW TO: Find base addr

1) Move screen to lowest y coord

2) Scan for float = 520

3) Move screen to highest y coord

4) Scan for float = 14765

5) Select the address that will updated the camera pos when changed

6) Right click this address and click “find out what accesses this address”

7) Select an element and press more information

8) Copy the value which is listed at the end of the string “The value of the pointer needed to find this …”

9) Search for this this value as an 8-bit hex value

10) Now search for the first address found

11) You should now have found a “green” address, select this and double click the address

12) You should now be able to see a string similar to “League of Legends.exe”+13D4280 <– base addr

看起来作者是通过某些内存查看软件(例如IDA)来进行分析得到的内存地址(在此膜拜大神)。

了解了这些,基本就明白程序的技术原理了,剩下的部分感兴趣的同学可以自行查看源码,当然有条件可以上设备体验一番。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python 源代码 VR LoL
相关文章推荐