SR render

This commit is contained in:
shenjack 2023-01-19 22:29:43 +08:00
parent b247352d4b
commit 886c1eb029
6 changed files with 168 additions and 50 deletions

View File

@ -22,9 +22,10 @@ game_version = Version("0.6.4.2") # 游戏版本
build_version = Version("0.1.0.0") # 编译文件版本(与游戏本体无关) build_version = Version("0.1.0.0") # 编译文件版本(与游戏本体无关)
__version__ = game_version __version__ = game_version
long_version: int = 6 long_version: int = 7
""" """
long_version: 一个用于标记内部协议的整数 long_version: 一个用于标记内部协议的整数
7: DR_option 添加 std_font_size
6: 事实证明, 不如直接用int 6: 事实证明, 不如直接用int
5: 添加 build_version 信息,用于标记编译文件版本, 5: 添加 build_version 信息,用于标记编译文件版本,
游戏版本改为四位数终于有一个可以让我随便刷的版本号位数了 游戏版本改为四位数终于有一个可以让我随便刷的版本号位数了
@ -54,7 +55,11 @@ class _DR_option(Options):
crash_report_test: bool = True crash_report_test: bool = True
# window option # window option
gui_scale: float = 1.0 # default 1 2 -> 2x 3 -> 3x gui_scale: float = 1.0 # default 1.0 2.0 -> 2x 3 -> 3x
@property
def std_font_size(self) -> int:
return round(11 * self.gui_scale)
class _DR_runtime(Options): class _DR_runtime(Options):

View File

@ -25,6 +25,7 @@ class BaseScreen(EventDispatcher):
def __init__(self, main_window: "ClientWindow"): def __init__(self, main_window: "ClientWindow"):
super().__init__() super().__init__()
self.focus = False
self.window_pointer = main_window self.window_pointer = main_window
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:

View File

@ -4,6 +4,7 @@
# All rights reserved # All rights reserved
# ------------------------------- # -------------------------------
import math
from typing import Dict, Union, List, Optional from typing import Dict, Union, List, Optional
from dataclasses import dataclass from dataclasses import dataclass
@ -21,13 +22,14 @@ class SR1PartData:
y: float y: float
id: int id: int
type: str type: str
active: bool
angle: float angle: float
angle_v: float angle_v: float
editor_angle: int editor_angle: int
flip_x: bool flip_x: bool
flip_y: bool flip_y: bool
explode: bool explode: bool
textures: str textures: Optional[str]
connections: Optional[List[int]] = None connections: Optional[List[int]] = None
@ -115,11 +117,44 @@ class SR1PartTexture:
'lander-1': 'LanderLegPreview'} 'lander-1': 'LanderLegPreview'}
@classmethod @classmethod
def get_sprite_from_type(cls, name: str) -> Union[None, str]: def get_textures_from_type(cls, name: str) -> Union[None, str]:
if name not in cls.part_type_sprite: if name not in cls.part_type_sprite:
return None return None
return cls.part_type_sprite[name] return cls.part_type_sprite[name]
class SR1Rotation(Options):
radian_angle_map: Dict[float, float] = {
0.0: 0,
1.570796: 270,
3.141593: 180,
4.712389: 90
}
@classmethod
def get_rotation(cls, radian: float) -> float:
if radian in cls.radian_angle_map:
return cls.radian_angle_map[radian]
else:
return (radian / math.pi) * 180
def xml_bool(bool_like: Union[str, int, bool, None]) -> bool:
if bool_like is None:
return False
if isinstance(bool_like, bool):
return bool_like
if isinstance(bool_like, int):
if bool_like == 0:
return False
else:
return True
if bool_like == '0':
return False
if bool_like.lower() == 'false':
return False
return True
# #
# #
# from xml.etree.ElementTree import Element, ElementTree # from xml.etree.ElementTree import Element, ElementTree

View File

@ -300,10 +300,26 @@ class ClientWindow(Window):
def on_deactivate(self): def on_deactivate(self):
... ...
@_call_screen_before
def on_move(self, x, y):
...
@_call_screen_after @_call_screen_after
def on_mouse_motion(self, x, y, dx, dy) -> None: def on_mouse_motion(self, x, y, dx, dy) -> None:
pass pass
@_call_screen_after
def on_mouse_scroll(self, x, y, scroll_x, scroll_y):
...
@_call_screen_after
def on_mouse_enter(self, x, y):
...
@_call_screen_after
def on_mouse_leave(self, x, y):
...
@_call_screen_after @_call_screen_after
def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers) -> None: def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers) -> None:
pass pass

View File

@ -6,25 +6,58 @@
import math import math
from xml.etree import ElementTree from xml.etree import ElementTree
from typing import List, TYPE_CHECKING, Union, Dict from xml.etree.ElementTree import Element
from typing import List, TYPE_CHECKING, Union, Dict, Optional
# third party package # third party package
from defusedxml.ElementTree import parse from defusedxml.ElementTree import parse
# pyglet # pyglet
from pyglet.graphics import Batch, Group from pyglet.text import Label
from pyglet.sprite import Sprite from pyglet.sprite import Sprite
from pyglet.graphics import Batch, Group
# Difficult Rocket # Difficult Rocket
from Difficult_Rocket import DR_option from Difficult_Rocket import DR_option
from Difficult_Rocket.command.line import CommandText from Difficult_Rocket.api.types import Fonts
# from Difficult_Rocket.command.line import CommandText
from Difficult_Rocket.client.screen import BaseScreen from Difficult_Rocket.client.screen import BaseScreen
from Difficult_Rocket.api.types.SR1 import SR1Textures, SR1PartTexture, SR1PartData from Difficult_Rocket.api.types.SR1 import SR1Textures, SR1PartTexture, SR1PartData, SR1Rotation, xml_bool
if TYPE_CHECKING: if TYPE_CHECKING:
from Difficult_Rocket.client import ClientWindow from Difficult_Rocket.client import ClientWindow
def get_part_data_from_xml(part_xml: Element) -> Optional[SR1PartData]:
if part_xml.tag != 'Part':
return None
# print(f"tag: {part.tag} attrib: {part.attrib}")
part_id = int(part_xml.attrib.get('id'))
part_type = part_xml.attrib.get('partType')
part_x = float(part_xml.attrib.get('x'))
part_y = float(part_xml.attrib.get('y'))
part_activate = xml_bool(part_xml.attrib.get('activated'))
part_angle = float(part_xml.attrib.get('angle'))
part_angle_v = float(part_xml.attrib.get('angleV'))
part_editor_angle = int(part_xml.attrib.get('editorAngle'))
part_flip_x = xml_bool(part_xml.attrib.get('flippedX'))
part_flip_y = xml_bool(part_xml.attrib.get('flippedY'))
part_explode = xml_bool(part_xml.attrib.get('exploded'))
if part_type not in SR1PartTexture.part_type_sprite:
part_textures = None
else:
part_textures = SR1PartTexture.get_textures_from_type(part_type)
print(f'id: {part_id:<4} type: {part_type:<10} x: {part_x} y: {part_y} activated: {part_activate} '
f'angle: {part_angle} angle_v: {part_angle_v} editor_angle: {part_editor_angle} '
f'flip_x: {part_flip_x} flip_y: {part_flip_y} explode: {part_explode} '
f'textures: {SR1PartTexture.get_textures_from_type(part_type)}')
part_data = SR1PartData(x=part_x, y=part_y, id=part_id, type=part_type,
active=part_activate, angle=part_angle, angle_v=part_angle_v,
editor_angle=part_editor_angle, flip_x=part_flip_x,
flip_y=part_flip_y, explode=part_explode, textures=part_textures)
return part_data
class SR1ShipRender(BaseScreen): class SR1ShipRender(BaseScreen):
"""用于渲染 sr1 船的类""" """用于渲染 sr1 船的类"""
@ -32,81 +65,109 @@ class SR1ShipRender(BaseScreen):
main_window: "ClientWindow", main_window: "ClientWindow",
scale: float): scale: float):
super().__init__(main_window) super().__init__(main_window)
self.rendered = False
self.scale = scale self.scale = scale
self.focus = True
self.dx = 0
self.dy = 0
self.textures: Union[SR1Textures, None] = None self.textures: Union[SR1Textures, None] = None
self.xml_doc = parse('configs/dock1.xml') self.xml_doc: ElementTree = parse('configs/dock1.xml')
self.xml_root: ElementTree.Element = self.xml_doc.getroot() self.xml_root: ElementTree.Element = self.xml_doc.getroot()
self.part_batch = Batch() self.part_batch = Batch()
self.part_group = Group() self.part_group = Group()
self.part_data = {} self.debug_label = Label(x=20, y=main_window.height - 20, font_size=DR_option.std_font_size,
text='SR1 render!', font_name=Fonts.微软等宽无线,
width=main_window.width - 20, height=20,
anchor_x='left', anchor_y='top',
batch=self.part_batch)
self.part_data: Dict[int, SR1PartData] = {}
self.parts_sprite: Dict[int, Sprite] = {} self.parts_sprite: Dict[int, Sprite] = {}
def load_xml(self, file_path: str) -> bool:
try:
cache_doc = parse(file_path)
self.xml_doc = cache_doc
self.xml_root = self.xml_doc.getroot()
return True
except:
return False
def load_textures(self): def load_textures(self):
self.textures = SR1Textures() self.textures = SR1Textures()
def render_ship(self): def render_ship(self):
if self.textures is None: if self.textures is None:
self.load_textures() self.load_textures()
self.part_data: Dict[int, SR1PartData] = {}
self.parts_sprite: Dict[int, Sprite] = {}
parts = self.xml_root.find('Parts') parts = self.xml_root.find('Parts')
for part in parts: for part_xml in parts:
if part.tag != 'Part': if part_xml.tag != 'Part':
continue # 如果不是部件,则跳过 continue # 如果不是部件,则跳过
# print(f"tag: {part.tag} attrib: {part.attrib}") # print(f"tag: {part.tag} attrib: {part.attrib}")
part_render = True part_render = True
part_id = int(part.attrib.get('id')) part = get_part_data_from_xml(part_xml)
part_type = part.attrib.get('partType') if part.id in self.part_data:
part_x = float(part.attrib.get('x')) print(f'hey! warning! id{part.id}')
part_y = float(part.attrib.get('y')) self.part_data[part.id] = part
part_activate = not not (part.attrib.get('activated') or 0)
part_angle = float(part.attrib.get('angle'))
part_angle_v = float(part.attrib.get('angleV'))
part_editor_angle = int(part.attrib.get('editorAngle'))
part_flip_x = not not (part.attrib.get('flippedX') or 0)
part_flip_y = not not (part.attrib.get('flippedY') or 0)
part_explode = not not (part.attrib.get('exploded') or 0)
if part_type not in SR1PartTexture.part_type_sprite:
part_render = False
print('Textures None found!')
part_textures = None
else:
part_textures = SR1PartTexture.get_sprite_from_type(part_type)
print(f'id: {part_id:<4} type: {part_type:<10} x: {part_x} y: {part_y} activated: {part_activate} '
f'angle: {part_angle} angle_v: {part_angle_v} editor_angle: {part_editor_angle} '
f'flip_x: {part_flip_x} flip_y: {part_flip_y} explode: {part_explode} '
f'textures: {SR1PartTexture.get_sprite_from_type(part_type)}')
if part_id in self.part_data:
print(f'hey! warning! id{part_id}')
part_data = SR1PartData(x=part_x, y=part_y, id=part_id, type=part_type,
angle=part_angle, angle_v=part_angle_v,
editor_angle=part_editor_angle, flip_x=part_flip_x,
flip_y=part_flip_y, explode=part_explode, textures=part_textures)
self.part_data[part_id] = part_data
# 下面就是调用 pyglet 去渲染的部分 # 下面就是调用 pyglet 去渲染的部分
render_scale = DR_option.gui_scale # 这个是 DR 的缩放比例 可以调节的( render_scale = DR_option.gui_scale # 这个是 DR 的缩放比例 可以调节的(
# 主要是 Windows 下有一个缩放系数嘛,我待会试试这玩意能不能获取(估计得 ctypes # 主要是 Windows 下有一个缩放系数嘛,我待会试试这玩意能不能获取(估计得 ctypes
# 在不缩放的情况下XML的1个单位长度对应60个像素 # 在不缩放的情况下XML的1个单位长度对应60个像素
render_x = part_x * render_scale * 60 + self.window_pointer.width / 2 render_x = part.x * render_scale * self.scale * 60 + self.window_pointer.width / 2 + self.dx
render_y = part_y * render_scale * 60 + self.window_pointer.height / 2 render_y = part.y * render_scale * self.scale * 60 + self.window_pointer.height / 2 + self.dy
# 你就这里改吧 # 你就这里改吧
cache_sprite = Sprite(img=self.textures.get_texture(part_data.textures), cache_sprite = Sprite(img=self.textures.get_texture(part.textures),
x=render_x, y=render_y, x=render_x, y=render_y,
batch=self.part_batch, group=self.part_group) batch=self.part_batch, group=self.part_group)
# 你得帮我换算一下 XML 里的 x y 和这里的屏幕像素的关系OK # 你得帮我换算一下 XML 里的 x y 和这里的屏幕像素的关系OK
# 旋转啥的不是大问题, 我找你要那个渲染代码就是要 x y 的换算逻辑 # 旋转啥的不是大问题, 我找你要那个渲染代码就是要 x y 的换算逻辑
if part_flip_x: cache_sprite.rotation = SR1Rotation.get_rotation(part.angle)
cache_sprite.scale_x = -cache_sprite.scale_x # 就是直接取反缩放,应该没问题····吧?(待会试试就知道了 if part.flip_x:
if part_flip_y: cache_sprite.scale_x = -1 # 就是直接取反缩放,应该没问题····吧?(待会试试就知道了
cache_sprite.scale_y = -cache_sprite.scale_y if part.flip_y:
cache_sprite.scale_y = -1
cache_sprite.scale = self.scale * DR_option.gui_scale
cache_sprite.x = cache_sprite.x - cache_sprite.scale_x / 2 cache_sprite.x = cache_sprite.x - cache_sprite.scale_x / 2
cache_sprite.y = cache_sprite.y - cache_sprite.scale_y / 2 cache_sprite.y = cache_sprite.y - cache_sprite.scale_y / 2
cache_sprite.rotation = part_data.angle / math.pi * 180
if not part_render: # 如果不渲染(渲染有毛病) if not part_render: # 如果不渲染(渲染有毛病)
self.parts_sprite[part_id].visible = False self.parts_sprite[part.id].visible = False
self.parts_sprite[part.id] = cache_sprite
self.rendered = True
self.parts_sprite[part_id] = cache_sprite def update_parts(self) -> bool:
if not self.rendered:
return False
for part_id in self.part_data:
# x y scale
self.parts_sprite[part_id].x = self.part_data[part_id].x * DR_option.gui_scale * self.scale * 60 + self.window_pointer.width / 2 + self.dx
self.parts_sprite[part_id].y = self.part_data[part_id].y * DR_option.gui_scale * self.scale * 60 + self.window_pointer.height / 2 + self.dy
self.parts_sprite[part_id].scale = self.scale * DR_option.gui_scale
def on_draw(self): def on_draw(self):
self.part_batch.draw() self.part_batch.draw()
def on_resize(self, width: int, height: int):
if not self.rendered:
return
self.update_parts()
def on_mouse_scroll(self, x: int, y: int, scroll_x: int, scroll_y: int):
if not self.rendered:
return
if not (self.scale + scroll_y * 0.1 == 0.0):
self.scale += scroll_y * 0.1
self.update_parts()
def on_mouse_drag(self, x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int):
if not self.focus:
return
self.dx += dx
self.dy += dy
self.update_parts()
def on_file_drop(self, x: int, y: int, paths: List[str]): def on_file_drop(self, x: int, y: int, paths: List[str]):
for path in paths:
if self.load_xml(path):
break
self.render_ship() self.render_ship()

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB