first part for phy simluation #34

Merged
shenjackyuanjie merged 19 commits from dr_game/phy_simluation into main 2023-06-22 02:11:47 +08:00
25 changed files with 296 additions and 294 deletions

View File

@ -15,7 +15,7 @@ import traceback
from pathlib import Path
from decimal import Decimal
from typing import Callable, Dict, List, TYPE_CHECKING
from typing import Callable, Dict, List, TYPE_CHECKING, Optional
# third function
import rtoml
@ -23,6 +23,7 @@ import pyglet
# from pyglet import gl
# from pyglet.gl import glClearColor
# from pyglet.libs.win32 import _user32
from pyglet.graphics import Group, Batch
from pyglet.window import Window
from pyglet.window import key, mouse
@ -37,10 +38,10 @@ from Difficult_Rocket.utils.translate import tr
from Difficult_Rocket.runtime import DR_runtime
from Difficult_Rocket.api.screen import BaseScreen
from Difficult_Rocket.utils.thread import new_thread
from Difficult_Rocket.client.screen import DRDEBUGScreen
from Difficult_Rocket.client.fps.fps_log import FpsLogger
from Difficult_Rocket.client.guis.widgets import InputBox
from Difficult_Rocket.exception.language import LanguageNotFound
from Difficult_Rocket.client.screen import DRScreen, DRDEBUGScreen
logger = logging.getLogger('client')
@ -122,7 +123,36 @@ def pyglet_load_fonts_folder(folder) -> None:
pyglet_load_fonts_folder(os.path.join(folder, obj))
def _call_back(call_back: Callable) -> Callable:
"""
>>> def call_back():
>>> pass
>>> @_call_back(call_back)
>>> def on_draw(self):
>>> pass
用于在调用窗口函数后调用指定函数 的装饰器
:param call_back: 需要调用的函数
:return: 包装后的函数
"""
def wrapper(func):
@functools.wraps(func)
def warp(self: "ClientWindow", *args, **kwargs):
result = func(self, *args, **kwargs)
call_back(self)
return result
return warp
return wrapper
def _call_screen_after(func: Callable) -> Callable:
"""
>>> @_call_screen_after
>>> def on_draw(self):
>>> pass
用于在调用窗口函数后调用子窗口函数 的装饰器
:param func: 需要包装的函数
:return: 包装后的函数
"""
@functools.wraps(func)
def warped(self: "ClientWindow", *args, **kwargs):
result = func(self, *args, **kwargs)
@ -141,6 +171,14 @@ def _call_screen_after(func: Callable) -> Callable:
def _call_screen_before(func: Callable) -> Callable:
"""
>>> @_call_screen_before
>>> def on_draw(self):
>>> pass
用于在调用窗口函数前调用子窗口函数 的装饰器
:param func: 需要包装的函数
:return: 包装后的函数
"""
@functools.wraps(func)
def warped(self: "ClientWindow", *args, **kwargs):
for title, a_screen in self.screen_list.items():
@ -185,8 +223,9 @@ class ClientWindow(Window):
self.SPF = Decimal('1') / self.FPS
self.fps_log = FpsLogger(stable_fps=int(self.FPS))
# batch
self.part_batch = pyglet.graphics.Batch()
self.label_batch = pyglet.graphics.Batch()
self.part_batch = Batch()
self.label_batch = Batch()
self.main_group = Group(0)
# frame
self.frame = pyglet.gui.Frame(self, order=20)
self.M_frame = pyglet.gui.MovableFrame(self, modifier=key.LCTRL)
@ -194,9 +233,10 @@ class ClientWindow(Window):
# setup
self.setup()
# 命令显示
self.command_group = pyglet.graphics.Group(0)
self.command_batch = Batch()
self.command_group = Group(1, parent=self.main_group)
self.input_box = InputBox(x=50, y=30, width=300,
batch=self.label_batch, text='') # 实例化
batch=self.command_batch, text='') # 实例化
self.input_box.push_handlers(self)
self.input_box.set_handler('on_commit', self.on_input)
self.set_handlers(self.input_box)
@ -215,9 +255,7 @@ class ClientWindow(Window):
def setup(self):
self.set_icon(pyglet.image.load('./textures/icon.png'))
self.load_fonts()
# TODO 读取配置文件,加载不同的屏幕,解耦
self.screen_list['DR_debug'] = DRDEBUGScreen(self)
self.screen_list['DR_main'] = DRScreen(self)
self.game.dispatch_event('on_client_start', game=self.game, client=self)
def load_fonts(self) -> None:
@ -264,6 +302,10 @@ class ClientWindow(Window):
now_FPS = pyglet.clock.get_frequency()
self.fps_log.update_tick(now_FPS, decimal_tick)
def on_command_draw(self):
self.command_batch.draw()
@_call_back(on_command_draw)
@_call_screen_after
def on_draw(self, *dt):
while command := self.game.console.get_command():
@ -306,10 +348,15 @@ class ClientWindow(Window):
self.on_command(command_text)
self.input_box.value = ''
def new_command(self):
self.game.console.new_command()
@_call_back(new_command)
@_call_screen_after
def on_command(self, command: line.CommandText):
print(command.find('/'))
command.text = command.text.rstrip('\n')
self.logger.info(tr().window.command.text().format(command))
command.find('/')
if command.find('stop'):
# self.dispatch_event('on_exit')
print("command stop!")
@ -335,8 +382,9 @@ class ClientWindow(Window):
except LanguageNotFound:
self.logger.info(tr().language_available().format(os.listdir('./configs/lang')))
self.save_info()
# self.command_tree.parse(command.plain_command)
elif command.find('mods'):
for mod in self.game.mod_module:
self.logger.info(f"mod: {mod.name} id: {mod.mod_id} version: {mod.version}")
@_call_screen_after
def on_message(self, message: line.CommandText):

View File

@ -7,24 +7,17 @@
import typing
from pyglet.text import Label
from pyglet.graphics import Batch, Group
from pyglet.clock import get_frequency
from pyglet.graphics import Batch, Group
# Difficult Rocket function
from Difficult_Rocket.api.types import Fonts
# from Difficult_Rocket.utils import translate
from Difficult_Rocket.api.screen import BaseScreen
# from Difficult_Rocket.command.tree import CommandTree
if typing.TYPE_CHECKING:
from Difficult_Rocket.client import ClientWindow
class DRScreen(BaseScreen):
def __init__(self, main_window: "ClientWindow"):
super().__init__(main_window)
class DRDEBUGScreen(BaseScreen):
def __init__(self, main_window: "ClientWindow"):
super().__init__(main_window)
@ -55,4 +48,3 @@ class DRDEBUGScreen(BaseScreen):
def on_draw(self, *dt, window: "ClientWindow"):
self.main_batch.draw()
# print(self.window_pointer.try_if_runs)

View File

@ -44,9 +44,12 @@ class CommandText:
i += 1
def find(self, text: str) -> bool:
startswith = self.text.startswith(text)
if startswith:
find = self.text.find(text)
if find != -1:
self.text = self.text[find + len(text):]
if not len(text) == len(self.text):
self.text = self.text[find + len(text):] if not self.text[find+len(text)] == ' ' else self.text[find + len(text) + 1:]
return True
return False

View File

@ -65,6 +65,9 @@ class Console(Options):
def get_command(self) -> Optional[str]:
return self.caches.pop(0) if self.caches else None
def new_command(self) -> None:
return None
class Game(Options):
name = 'MainGame'

View File

@ -16,20 +16,19 @@ import sys
import time
import math
import json
import rtoml
import logging
import configparser
from typing import Union
from pathlib import Path
from typing import Union, Optional
from xml.etree import ElementTree
import rtoml
from defusedxml.ElementTree import parse
from Difficult_Rocket.exception.unsupport import NoMoreJson5
# logger
tools_logger = logging.getLogger('part-tools')
tools_logger = logging.getLogger('tools')
"""
file configs
"""
@ -39,10 +38,12 @@ file_error = {FileNotFoundError: 'no {filetype} file was founded!:\n file name:
Exception: 'get some {error_type} when read {filetype} file {filename}! \n file type: {} \n file name: {} \n stack: {stack}'}
def load_file(file_name: str,
stack: Union[str, list, dict, None] = None,
raise_error: bool = True,
encoding: str = 'utf-8') -> Union[dict, ElementTree.ElementTree]:
def load_file(file_name: Union[str, Path],
stack: Optional[Union[str, list, dict]] = None,
raise_error: Optional[bool] = True,
encoding: Optional[str] = 'utf-8') -> Union[dict, ElementTree.ElementTree]:
if isinstance(file_name, Path):
file_name = str(file_name)
f_type = file_name[file_name.rfind('.') + 1:] # 从最后一个.到末尾 (截取文件格式)
get_file = NotImplementedError('解析失败,请检查文件类型/文件内容/文件是否存在!')
try:

View File

@ -14,6 +14,7 @@ gitee: @shenjackyuanjie
import os
import inspect
from pathlib import Path
from dataclasses import dataclass
from typing import Union, Tuple, Any, List, Dict, Hashable, Optional
@ -153,15 +154,16 @@ class Tr:
GOOD
"""
def __init__(self, language: str = None, config: Optional[TranslateConfig] = None):
def __init__(self, language: str = None, config: Optional[TranslateConfig] = None, lang_path: Optional[Path] = None):
"""
诶嘿我抄的MCDR
:param language: Tr 所使用的的语言
:param config: 配置
"""
self.language_name = language if language is not None else DR_runtime.language
self.translates: Dict[str, Union[str, Dict]] = tools.load_file(f'configs/lang/{self.language_name}.toml')
self.default_translate: Dict = tools.load_file(f'configs/lang/{DR_status.default_language}.toml')
self.language_path = lang_path if lang_path is not None else Path('configs/lang')
self.translates: Dict[str, Union[str, Dict]] = tools.load_file(self.language_path / f'{self.language_name}.toml')
self.default_translate: Dict = tools.load_file(f'{self.language_path}/{DR_status.default_language}.toml')
self.default_config = config.set('source', self) if config is not None else TranslateConfig(source=self)
self.translates_cache = Translates(value=self.translates, config=self.default_config.copy())
@ -185,11 +187,11 @@ class Tr:
if lang == ' ' or lang == '':
raise LanguageNotFound('Can not be empty')
lang = lang or self.language_name
if not os.path.exists(f'./configs/lang/{lang}.toml'):
print(f"lang: {os.path.exists(f'./configs/lang/{lang}.toml')} language = {lang} {self.language_name=}")
if not os.path.exists(f'{self.language_path}/{lang}.toml'):
print(f"lang: {os.path.exists(f'{self.language_path}/{lang}.toml')} language = {lang} {self.language_name=}")
raise LanguageNotFound(lang)
self.translates: Dict[str, Union[str, Dict]] = tools.load_file(f'configs/lang/{lang}.toml')
self.default_translate: Dict = tools.load_file(f'configs/lang/{DR_runtime.default_language}.toml')
self.translates: Dict[str, Union[str, Dict]] = tools.load_file(f'{self.language_path}/{lang}.toml')
self.default_translate: Dict = tools.load_file(f'{self.language_path}/{DR_runtime.default_language}.toml')
self.translates_cache = Translates(value=self.translates, config=self.default_config.copy())
self.language_name = lang
DR_runtime.language = self.language_name

View File

@ -75,14 +75,3 @@ input = "console"
window = "window"
command = "in game commands"
require_DR_rs = "require DR_rs module"
[client.sr1_render]
setup.start = "SR1 Renderer start loading"
setup.use_time = "SR1 Renderer loading has used: {} second"
xml.loading = "Loading XML file: {}"
xml.load_done = "XML file loaded"
xml.load_time = "XML file loading has used: {} second"
ship.load = "Loading ship: {}"
ship.load_time = "Ship loading has used: {} second"
ship.info = "Ship info:\n- Parts: {}\n- Weight: {}"
ship.render.done = "Ship render done"

View File

@ -75,15 +75,3 @@ input = "控制台"
window = "窗口"
command = "游戏内命令行"
require_DR_rs = "需要 DR_rs 模块"
[client.sr1_render]
setup.start = "SR1 渲染器开始载入"
setup.use_time = "SR1 渲染器载入消耗时间: {} 秒"
xml.loading = "正在加载XML文件: {}"
xml.load_done = "XML 文件加载完成"
xml.load_time = "XML 文件加载消耗时间: {} 秒"
ship.load = "正在加载飞船: {}"
ship.load_time = "飞船加载消耗时间: {} 秒"
#ship.info = "飞船信息:\n- 部件数量: {}\n- 部件重量: {}\n- 文件大小: {}"
ship.info = "飞船信息:\n- 部件数量: {}\n- 部件重量: {}"
ship.render.done = "飞船渲染完成"

View File

@ -7,8 +7,8 @@ fonts_folder = "libs/fonts"
[window]
style = "None"
width = 1021
height = 1078
width = 1041
height = 1050
visible = true
gui_scale = 1
caption = "Difficult Rocket v{DR_version}"

View File

@ -3,7 +3,24 @@
- 最新版本号
- DR game: 0.2.0.0
- DR rs: 0.2.10.1
- DR rs: 0.2.11.0
## DR game 0.1.2.0
### 修改
- 现在渲染飞船的时候不会再显示那个白色框了
- 改为一个彩色的框
- Now, the ship will not be displayed in the white box
- Change to a colored box
## DR rs 0.2.11.0
### 添加
- `Python::data::PySR1Ship`
- `get_part_box(&self, part_id: i64) -> Option<(f64, f64), (f64, f64)>`
- 用于获取对应 id 的实际碰撞箱
## DR game 0.2.0.0

View File

@ -11,6 +11,21 @@
- issue #33 (https://github.com/shenjackyuanjie/Difficult-Rocket/issues/33)
- 修复了实际上并不会加载 `.otf` 格式的字体文件的问题
### 修改
- 现在输入命令之后不会输出一个 `True`/`False` 了
- (实际上是用来检测命令是不是用 `/` 开头的)
- Now, the command will not output a `True`/`False`
- (Actually used to detect whether the command starts with `/`)
- `CommandText`
- `find`
- 现在会先用 `str.startswith` 检测是否以要求的字符串开头
- 实际上就是丐版 `re.match`
- 并且会在匹配上之后 如果匹配内容后面第一个字符是空格 则会截取掉空格
- Now, it will first use `str.startswith` to detect whether it starts with the required string
- Actually a poor version of `re.match`
- And after matching, if the first character after the matching content is a space, the space will be intercepted
## DR sdk 0.8.3.0
### 删除

View File

@ -121,9 +121,11 @@ if TYPE_CHECKING:
@property
def img_pos(self) -> Tuple[int, int, int, int]: ...
""" -x -y +x +y 左下右上 """
def get_part_box(self, part_id: int) -> Optional[Tuple[Tuple[int, int], Tuple[int, int]]]: ...
class Console_rs:
def __init__(self) -> None: ...
def start(self) -> None: ...
def stop(self) -> bool: ...
def get_command(self) -> Optional[str]: ...
def new_command(self) -> bool: ...

View File

@ -13,9 +13,9 @@ dependencies = [
[[package]]
name = "arrayvec"
version = "0.7.2"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "autocfg"
@ -84,14 +84,14 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
version = "0.9.14"
version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"memoffset 0.9.0",
"scopeguard",
]
@ -107,9 +107,9 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
version = "0.8.15"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
@ -167,9 +167,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.18"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
[[package]]
name = "matrixmultiply"
@ -190,6 +190,15 @@ dependencies = [
"autocfg",
]
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "nalgebra"
version = "0.32.2"
@ -348,7 +357,7 @@ dependencies = [
"cfg-if",
"indoc",
"libc",
"memoffset",
"memoffset 0.8.0",
"parking_lot",
"pyo3-build-config",
"pyo3-ffi",

View File

@ -12,7 +12,7 @@ package_path = 'Difficult_Rocket_rs'
setup(
name='Difficult_Rocket_rs',
version="0.2.10.1",
version="0.2.11.0",
author='shenjackyuanjie',
author_email='3695888@qq.com',
rust_extensions=[RustExtension(target="Difficult_Rocket_rs.Difficult_Rocket_rs",

View File

@ -12,6 +12,7 @@ mod python;
mod render;
mod simulator;
mod sr1_data;
mod translate;
mod types;
use pyo3::prelude::*;
@ -26,7 +27,7 @@ enum LoadState {
}
#[pyfunction]
fn get_version_str() -> String { "0.2.10.1".to_string() }
fn get_version_str() -> String { "0.2.11.0".to_string() }
#[pyfunction]
fn test_call(py_obj: &PyAny) -> PyResult<bool> {
@ -42,7 +43,6 @@ fn test_call(py_obj: &PyAny) -> PyResult<bool> {
fn module_init(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(get_version_str, m)?)?;
m.add_function(wrap_pyfunction!(test_call, m)?)?;
m.add_function(wrap_pyfunction!(simulator::simulation, m)?)?;
m.add_function(wrap_pyfunction!(sr1_data::part_list::read_part_list_py, m)?)?;
m.add_function(wrap_pyfunction!(sr1_data::ship::py_raw_ship_from_file, m)?)?;
m.add_class::<render::camera::CameraRs>()?;

View File

@ -12,6 +12,7 @@
/// 插件加载
///
#[allow(unused)]
pub mod plugin_trait {
pub struct ModInfo {
pub name: String,

View File

@ -12,6 +12,7 @@ pub mod data {
use pyo3::prelude::*;
use crate::sr1_data::part_list::RawPartList;
use crate::types::math::{Point2D, Rotatable};
use crate::types::sr1::{get_max_box, SR1PartData, SR1PartListTrait};
use crate::types::sr1::{SR1PartList, SR1PartType, SR1Ship};
@ -142,96 +143,35 @@ pub mod data {
}
parts
}
fn get_part_box(&self, part_id: i64) -> Option<((f64, f64), (f64, f64))> {
let part_data = self.ship.parts.iter().find(|&x| x.id == part_id);
if let Some(part_data) = part_data {
let part_type = self.part_list.get_part_type(part_data.part_type_id.clone()).unwrap();
// rotate
let radius = part_data.angle;
let ((x1, y1), (x2, y2)) = part_type.get_box();
let mut p1 = Point2D::new(x1, y1);
let mut p2 = Point2D::new(x2, y2);
p1.rotate_radius_mut(radius);
p2.rotate_radius_mut(radius);
// transform
p1.add_mut(part_data.x * 2.0, part_data.y * 2.0);
p2.add_mut(part_data.x * 2.0, part_data.y * 2.0);
return Some(((p1.x, p1.y), (p2.x, p2.y)));
}
None
}
}
}
pub mod translate {
use pyo3::prelude::*;
use pyo3::types::PyDict;
#[pyclass]
#[pyo3(name = "TranslateConfig_rs")]
#[pyo3(text_signature = "(language, raise_error = False, replace_normal = False, add_error = False, is_result = False, keep_get = False)")]
pub struct PyTranslateConfig {
pub raise_error: bool,
pub replace_normal: bool,
pub add_error: bool,
pub is_result: bool,
pub keep_get: bool,
pub language: String,
}
#[pymethods]
impl PyTranslateConfig {
#[new]
fn new(py_: Python, raise_error: bool, replace_normal: bool, language: Option<String>) -> Self {
let dr_runtime = PyModule::import(py_, "Difficult_Rocket").unwrap().get_item("DR_runtime").unwrap();
let default_language = dr_runtime.get_item("language").unwrap().extract::<String>().unwrap();
Self {
raise_error,
replace_normal,
add_error: false,
is_result: false,
keep_get: false,
language: language.unwrap_or(default_language),
}
}
// fn set(&self, py_: Python, item: String, value: BoolString) -> &Self {
// match item.as_str() {
// "raise_error" => self,
// _ => self,
// }
// }
}
#[pyclass]
pub struct PyTranslate {
pub data: Py<PyAny>,
pub get_list: Vec<(String, bool)>,
pub config: PyTranslateConfig,
}
#[pymethods]
impl PyTranslate {
#[new]
fn py_new(py_: Python, data: &PyAny) -> Self {
let _ = data.is_instance_of::<PyDict>();
Self {
data: data.into_py(py_),
get_list: Vec::new(),
config: PyTranslateConfig::new(py_, false, false, None),
}
}
}
}
pub mod physics {
use pyo3::prelude::*;
use crate::simulator::interface::PhysicsSpace;
#[pyclass]
#[pyo3(name = "PhysicsSpace_rs")]
pub struct PyPhysicsSpace {
pub space: PhysicsSpace,
}
#[pymethods]
impl PyPhysicsSpace {
#[new]
fn new(gravity: (f64, f64)) -> Self {
Self {
space: PhysicsSpace::new(gravity),
}
}
fn tick_space(&mut self) { self.space.tick_space() }
}
use crate::translate;
}
pub mod console {
use pyo3::prelude::*;
use std::io::{self, Write};
#[pyclass]
#[pyo3(name = "Console_rs")]
@ -255,7 +195,7 @@ pub mod console {
let (stop_sender, stop_receiver) = std::sync::mpsc::channel();
let (keyboard_input_sender, keyboard_input_receiver) = std::sync::mpsc::channel();
std::thread::spawn(move || {
let std_in = std::io::stdin();
let std_in = io::stdin();
loop {
if let Ok(()) = stop_receiver.try_recv() {
break;
@ -265,9 +205,10 @@ pub mod console {
if !input.is_empty() {
keyboard_input_sender.send(input).unwrap();
}
print!(">>");
}
});
print!("rs>");
io::stdout().flush().unwrap();
self.stop_sender = Some(stop_sender);
self.keyboard_input_receiver = Some(keyboard_input_receiver);
}
@ -280,6 +221,12 @@ pub mod console {
false
}
fn new_command(&self) -> bool {
print!("rs>");
io::stdout().flush().unwrap();
true
}
fn get_command(&self) -> Option<String> {
// 获取输入
if let Some(receiver) = &self.keyboard_input_receiver {

View File

@ -9,6 +9,7 @@
use pyo3::prelude::*;
use rapier2d_f64::prelude::*;
#[allow(unused)]
#[pyfunction]
#[pyo3(name = "simulation")]
pub fn simulation() -> () {
@ -63,73 +64,8 @@ pub fn simulation() -> () {
}
}
pub mod interface {
#[allow(unused)]
pub mod python {
use pyo3::prelude::*;
use rapier2d_f64::prelude::*;
pub struct PhysicsSpace {
pub rigid_body_set: RigidBodySet,
pub collider_set: ColliderSet,
pub gravity: Vector<f64>,
pub integration_parameters: IntegrationParameters,
pub physics_pipeline: PhysicsPipeline,
pub island_manager: IslandManager,
pub broad_phase: BroadPhase,
pub narrow_phase: NarrowPhase,
pub impulse_joint_set: ImpulseJointSet,
pub multibody_joint_set: MultibodyJointSet,
pub ccd_solver: CCDSolver,
pub physics_hooks: (),
pub event_handler: (),
}
impl PhysicsSpace {
pub fn new(gravity: (f64, f64)) -> Self {
let rigid_body_set = RigidBodySet::new();
let collider_set = ColliderSet::new();
let gravity = vector![gravity.0, gravity.1];
let integration_parameters = IntegrationParameters::default();
let physics_pipeline = PhysicsPipeline::new();
let island_manager = IslandManager::new();
let broad_phase = BroadPhase::new();
let narrow_phase = NarrowPhase::new();
let impulse_joint_set = ImpulseJointSet::new();
let multibody_joint_set = MultibodyJointSet::new();
let ccd_solver = CCDSolver::new();
let physics_hooks = ();
let event_handler = ();
Self {
rigid_body_set,
collider_set,
gravity,
integration_parameters,
physics_pipeline,
island_manager,
broad_phase,
narrow_phase,
impulse_joint_set,
multibody_joint_set,
ccd_solver,
physics_hooks,
event_handler,
}
}
pub fn tick_space(&mut self) {
self.physics_pipeline.step(
&self.gravity,
&self.integration_parameters,
&mut self.island_manager,
&mut self.broad_phase,
&mut self.narrow_phase,
&mut self.rigid_body_set,
&mut self.collider_set,
&mut self.impulse_joint_set,
&mut self.multibody_joint_set,
&mut self.ccd_solver,
None,
&self.physics_hooks,
&self.event_handler,
);
}
}
}

View File

@ -0,0 +1,16 @@
/*
* -------------------------------
* Difficult Rocket
* Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
* All rights reserved
* -------------------------------
*/
use std::collections::HashMap;
type TranslateMapper = HashMap<String, HashMap<String, HashMap<String, String>>>;
pub struct Translater {
pub data: TranslateMapper,
pub language: String,
}

View File

@ -197,9 +197,13 @@ pub mod sr1 {
}
impl SR1PartType {
pub fn get_box(&self) -> (f64, f64, f64, f64) {
let x = self.width as f64 / 2.0;
(-x, 0.0, x, self.height as f64)
pub fn get_box(&self) -> ((f64, f64), (f64, f64)) {
// -x, -y, x, y
// 居中
(
(-(self.width as f64 / 2.0), -(self.height as f64 / 2.0)),
(self.width as f64 / 2.0, self.height as f64 / 2.0),
)
}
}
@ -777,14 +781,14 @@ pub mod sr1 {
let mut max_box = (0_f64, 0_f64, 0_f64, 0_f64);
for part in parts.iter() {
let part_type = part_list.get_part_type(part.part_type_id.clone()).unwrap();
let (x1, y1, x2, y2) = part_type.get_box();
let ((x1, y1), (x2, y2)) = part_type.get_box();
// rotate
let mut p1 = Point2D::new(x1, y1);
let mut p2 = Point2D::new(x2, y2);
p1.rotate_radius_mut(part.angle);
p2.rotate_radius_mut(part.angle);
let p1 = p1.add(part.x, part.y);
let p2 = p2.add(part.x, part.y);
let p1 = p1.add(part.x * 2.0, part.y * 2.0);
let p2 = p2.add(part.x * 2.0, part.y * 2.0);
let (x1, y1, x2, y2) = (p1.x, p1.y, p2.x, p2.y);
// get max box
max_box.0 = max_box.0.min(x1).min(part.x);
@ -835,6 +839,12 @@ pub mod math {
#[inline]
pub fn add(&self, x: f64, y: f64) -> Self { Point2D::new(self.x + x, self.y + y) }
#[inline]
pub fn add_mut(&mut self, x: f64, y: f64) -> () {
self.x += x;
self.y += y;
}
}
impl Rotatable for Point2D {

View File

@ -17,7 +17,7 @@ from Difficult_Rocket.api.mod import ModInfo
from Difficult_Rocket.client import ClientWindow
from Difficult_Rocket.api.types import Options, Version
DR_rust_version = Version("0.2.10.1") # DR_mod 的 Rust 编写部分的兼容版本
DR_rust_version = Version("0.2.11.0") # DR_mod 的 Rust 编写部分的兼容版本
logger = logging.getLogger('client.dr_game')

View File

@ -22,3 +22,6 @@ class RustConsole(Console):
def get_command(self) -> str:
return self.console.get_command()
def new_command(self) -> None:
self.console.new_command()

View File

@ -0,0 +1,12 @@
[mod.info]
setup.start = "SR1 Renderer start loading"
setup.use_time = "SR1 Renderer loading has used: {} second"
[sr1.ship]
xml.loading = "Loading XML file: {}"
xml.load_done = "XML file loaded"
xml.load_time = "XML file loading has used: {} second"
ship.load = "Loading ship: {}"
ship.load_time = "Ship loading has used: {} second"
ship.info = "Ship info:\n- Parts: {}\n- Weight: {}"
ship.render.done = "Ship render done"

View File

@ -0,0 +1,13 @@
[mod.info]
setup.start = "SR1 渲染器开始载入"
setup.use_time = "SR1 渲染器载入消耗时间: {} 秒"
[sr1.ship]
xml.loading = "正在加载XML文件: {}"
xml.load_done = "XML 文件加载完成"
xml.load_time = "XML 文件加载消耗时间: {} 秒"
ship.load = "正在加载飞船: {}"
ship.load_time = "飞船加载消耗时间: {} 秒"
#ship.info = "飞船信息:\n- 部件数量: {}\n- 部件重量: {}\n- 文件大小: {}"
ship.info = "飞船信息:\n- 部件数量: {}\n- 部件重量: {}"
ship.render.done = "飞船渲染完成"

View File

@ -9,26 +9,24 @@ import time
import random
import logging
import traceback
# from xml.etree import ElementTree
from pathlib import Path
from xml.etree.ElementTree import Element
from typing import List, TYPE_CHECKING, Union, Dict, Optional, Generator
# third party package
from defusedxml.ElementTree import parse
# pyglet
from pyglet.math import Vec4
from pyglet.text import Label
from pyglet.shapes import Line, Rectangle
from pyglet.sprite import Sprite
# from pyglet.image import Texture
from pyglet.graphics import Batch, Group
from pyglet.shapes import Line, Rectangle
from . import DR_mod_runtime
# Difficult Rocket
from Difficult_Rocket import DR_status
from Difficult_Rocket.utils.translate import tr
from Difficult_Rocket.utils.translate import Tr
from Difficult_Rocket.api.types import Fonts, Options
from Difficult_Rocket.command.line import CommandText
from Difficult_Rocket.client.screen import BaseScreen
@ -41,6 +39,7 @@ if DR_mod_runtime.use_DR_rust:
from .Difficult_Rocket_rs import CenterCamera_rs, SR1PartList_rs, SR1Ship_rs
logger = logging.getLogger('client.dr_game_sr1_ship')
sr_tr = Tr(lang_path=Path('./mods/dr_game/lang'))
def get_sr1_part(part_xml: Element) -> Optional[SR1PartData]:
@ -88,7 +87,7 @@ class SR1ShipRender(BaseScreen):
def __init__(self,
main_window: "ClientWindow"):
super().__init__(main_window)
logger.info(tr().client.sr1_render.setup.start())
logger.info(sr_tr().mod.info.setup.start())
load_start_time = time.time_ns()
self.rendered = False
self.focus = True
@ -124,15 +123,16 @@ class SR1ShipRender(BaseScreen):
self.part_box_batch = Batch()
self.part_batch = Batch()
self.part_group = Group()
self.debug_label = Label(x=20, y=main_window.height - 20, font_size=DR_status.std_font_size,
self.debug_label = Label(x=20, y=main_window.height - 100, font_size=DR_status.std_font_size,
text='SR1 render!', font_name=Fonts.微软等宽无线,
width=main_window.width - 20, height=20,
anchor_x='left', anchor_y='top')
self.part_data: Dict[int, SR1PartData] = {}
self.parts_sprite: Dict[int, Sprite] = {}
self.part_box_dict: Dict[int, Rectangle] = {}
self.part_line_box: Dict[int, List[Line]] = {}
load_end_time = time.time_ns()
logger.info(tr().client.sr1_render.setup.use_time().format(
logger.info(sr_tr().mod.info.setup.use_time().format(
(load_end_time - load_start_time) / 1000000000))
if DR_mod_runtime.use_DR_rust:
self.camera_rs = CenterCamera_rs(main_window,
@ -143,7 +143,7 @@ class SR1ShipRender(BaseScreen):
def load_xml(self, file_path: str) -> bool:
try:
start_time = time.time_ns()
logger.info(tr().client.sr1_render.xml.loading().format(file_path))
logger.info(sr_tr().sr1.ship.xml.loading().format(file_path))
cache_doc = parse(file_path)
self.xml_doc = cache_doc
self.xml_root = self.xml_doc.getroot()
@ -155,8 +155,8 @@ class SR1ShipRender(BaseScreen):
print(self.rust_ship.img_pos)
except Exception:
traceback.print_exc()
logger.info(tr().client.sr1_render.xml.load_done())
logger.info(tr().client.sr1_render.xml.load_time().format(
logger.info(sr_tr().sr1.ship.xml.load_done())
logger.info(sr_tr().sr1.ship.xml.load_time().format(
(time.time_ns() - start_time) / 1000000000))
return True
except Exception as e:
@ -191,19 +191,26 @@ class SR1ShipRender(BaseScreen):
cache_sprite.y = cache_sprite.y - cache_sprite.scale_y / 2
self.parts_sprite[part.id] = cache_sprite
part_width = 100
part_height = 100
if DR_mod_runtime.use_DR_rust:
part_type = self.part_list_rs.get_part_type(part.p_type)
if part_type is not None:
part_width = part_type.width * 15
part_height = part_type.height * 15
part_box = Rectangle(x=render_x, y=render_y,
width=part_width, height=part_height,
batch=self.part_box_batch, group=self.part_group)
part_box.rotation = SR1Rotation.get_rotation(part.angle)
part_box.opacity = 50
self.part_box_dict[part.id] = part_box
part_debug_box = self.rust_ship.get_part_box(part.id)
if part_debug_box:
# 线框
part_line_box = []
width = 4
color = (random.randrange(0, 255), random.randrange(0, 255), random.randrange(0, 255), random.randrange(100, 200))
part_line_box.append(Line(x=part_debug_box[0][0] * 30, y=part_debug_box[0][1] * 30,
x2=part_debug_box[0][0] * 30, y2=part_debug_box[1][1] * 30,
batch=self.part_box_batch, width=width, color=color))
part_line_box.append(Line(x=part_debug_box[0][0] * 30, y=part_debug_box[1][1] * 30,
x2=part_debug_box[1][0] * 30, y2=part_debug_box[1][1] * 30,
batch=self.part_box_batch, width=width, color=color))
part_line_box.append(Line(x=part_debug_box[1][0] * 30, y=part_debug_box[1][1] * 30,
x2=part_debug_box[1][0] * 30, y2=part_debug_box[0][1] * 30,
batch=self.part_box_batch, width=width, color=color))
part_line_box.append(Line(x=part_debug_box[1][0] * 30, y=part_debug_box[0][1] * 30,
x2=part_debug_box[0][0] * 30, y2=part_debug_box[0][1] * 30,
batch=self.part_box_batch, width=width, color=color))
self.part_line_box[part.id] = part_line_box
# if not part_render: # 如果不渲染(渲染有毛病)
# self.parts_sprite[part.id].visible = False
count += 1
@ -216,10 +223,11 @@ class SR1ShipRender(BaseScreen):
def render_ship(self):
if self.textures is None:
self.load_textures()
logger.info(tr().client.sr1_render.ship.load().format(self.xml_name))
logger.info(sr_tr().sr1.ship.ship.load().format(self.xml_name))
start_time = time.perf_counter_ns()
self.part_data: Dict[int, SR1PartData] = {}
self.parts_sprite: Dict[int, Sprite] = {}
self.part_line_box = {}
self.camera_rs.zoom = 1.0
if DR_mod_runtime.use_DR_rust:
self.camera_rs.dx = 0
@ -243,10 +251,10 @@ class SR1ShipRender(BaseScreen):
if DR_mod_runtime.use_DR_rust:
for part in self.part_data:
full_mass += self.part_list_rs.get_part_type(self.part_data[part].p_type).mass * 500
logger.info(tr().client.sr1_render.ship.load_time().format(
logger.info(sr_tr().sr1.ship.ship.load_time().format(
(time.perf_counter_ns() - start_time) / 1000000000))
logger.info(tr().client.sr1_render.ship.info().format(
len(self.part_data), f'{full_mass}kg' if DR_mod_runtime.use_DR_rust else tr().game.require_DR_rs()))
logger.info(sr_tr().sr1.ship.ship.info().format(
len(self.part_data), f'{full_mass}kg' if DR_mod_runtime.use_DR_rust else sr_tr().game.require_DR_rs()))
self.rendered = True
def update_parts(self) -> bool:
@ -269,7 +277,7 @@ class SR1ShipRender(BaseScreen):
next(self.gen_draw)
except GeneratorExit:
self.drawing = False
logger.info(tr().client.sr1_render.ship.render.done())
logger.info(sr_tr().sr1.ship.ship.render.done())
if self.need_update_parts:
self.update_parts()
@ -292,6 +300,7 @@ class SR1ShipRender(BaseScreen):
self.debug_mouse_delta_line.draw()
def on_resize(self, width: int, height: int, window: "ClientWindow"):
self.debug_label.y = height - 100
if not self.rendered:
return
self.debug_line.x = width / 2
@ -348,22 +357,26 @@ class SR1ShipRender(BaseScreen):
self.need_draw = True
print('应该渲染飞船的')
elif command.find('debug'):
print('sr ?')
if command.find('delta'):
SR1ShipRender_Option.debug_d_pos = not SR1ShipRender_Option.debug_mouse_d_pos
self.debug_line.visible = SR1ShipRender_Option.debug_d_pos
self.debug_d_pos_label.visible = SR1ShipRender_Option.debug_d_pos
# print('sr1 delta')
# SR1ShipRender_Option.debug_d_pos = not SR1ShipRender_Option.debug_mouse_d_pos
self.debug_line.visible = not self.debug_line.visible
self.debug_d_pos_label.visible = not self.debug_d_pos_label.visible
SR1ShipRender_Option.debug_d_pos = self.debug_line.visible
logger.debug('sr1 delta')
elif command.find('mouse'):
if command.find('delta'):
SR1ShipRender_Option.debug_mouse_pos = not SR1ShipRender_Option.debug_mouse_pos
self.debug_mouse_line.visible = SR1ShipRender_Option.debug_mouse_pos
self.debug_mouse_label.visible = SR1ShipRender_Option.debug_mouse_pos
# print('sr1 mouse delta')
logger.debug(f'sr1 mouse delta {SR1ShipRender_Option.debug_mouse_pos}')
else:
SR1ShipRender_Option.debug_mouse_d_pos = not SR1ShipRender_Option.debug_mouse_d_pos
self.debug_mouse_delta_line.visible = SR1ShipRender_Option.debug_mouse_d_pos
# print('sr1 mouse')
self.debug_mouse_delta_line.visible = not self.debug_mouse_delta_line.visible
SR1ShipRender_Option.debug_mouse_d_pos = self.debug_mouse_delta_line.visible
logger.debug(f'sr1 mouse {SR1ShipRender_Option.debug_mouse_d_pos}')
elif command.find('ship'):
if self.rendered:
for index, sprite in self.parts_sprite.items():
sprite.visible = not sprite.visible
elif command.find('get_buf'):
@ -390,7 +403,6 @@ class SR1ShipRender(BaseScreen):
return
img_box = self.rust_ship.img_pos
img_size = (img_box[2] - img_box[0] + 1000, img_box[3] - img_box[1] + 1000)
# img_center = (abs(img_box[0]), abs(img_box[1]))
# 中心点是左上角坐标
img_center = (abs(img_box[0]), abs(img_box[3]))
print(f"img_box: {img_box} img_size: {img_size} img_center: {img_center}")
@ -400,23 +412,6 @@ class SR1ShipRender(BaseScreen):
traceback.print_exc()
print('PIL not found')
return
min_x = 0
min_y = 0
max_x = 0
max_y = 0
for part, sprite in self.parts_sprite.items():
sprite_img = sprite.image
print(f"sprite_img: {sprite_img} {sprite_img.width} {sprite_img.height}")
# 碰撞箱是居中的
# -x, -y, +x, +y
part_data = self.part_data[part]
bound_box = [-sprite_img.width / 2 + part_data.x, -sprite_img.height / 2 + part_data.y,
sprite_img.width / 2 + part_data.x, sprite_img.height / 2 + part_data.y]
min_x = min(min_x, bound_box[0])
min_y = min(min_y, bound_box[1])
max_x = max(max_x, bound_box[2])
max_y = max(max_y, bound_box[3])
print(f"min_x: {min_x} min_y: {min_y} max_x: {max_x} max_y: {max_y}")
img = Image.new('RGBA', img_size)
for part, sprite in self.parts_sprite.items():
sprite_img = sprite.image