如何在ggez中调整窗口大小且不造成绘制元素变形
ggez 调整窗口大小无变形实现方案
你当前调用的graphics::set_drawable_size并不是用来修改窗口尺寸的接口,它的作用是修改内部逻辑绘制区域的大小。直接调用它但不同步调整物理窗口、更新投影矩阵的话,必然会出现两个异常:
- 设置的逻辑绘制区域宽高比和窗口实际物理宽高比不匹配,图形被拉伸,圆形变椭圆
- 绘制区域尺寸和窗口尺寸不匹配,清屏操作无法覆盖整个窗口范围,旧帧内容残留形成残影
正确实现方式
根据你的需求选对应方案即可,都不会出现图形变形:
场景1:代码主动修改窗口大小
不要用set_drawable_size改窗口尺寸,直接调用winit窗口提供的接口修改物理窗口大小,ggez会自动同步更新投影矩阵,不会出现比例异常:
use ggez::winit::dpi::LogicalSize; // 示例:将窗口调整为800*600尺寸 ctx.gfx.window().set_inner_size(LogicalSize::new(800.0, 600.0));
调用后不需要手动执行任何和drawable size相关的操作,ggez内部会自动对齐逻辑绘制尺寸和窗口物理尺寸。
场景2:允许用户拖拽窗口边缘自由调整大小
- 初始化窗口时先开启可调整大小的配置:
use ggez::conf; let window_mode = conf::WindowMode::default().resizable(true); let (mut ctx, event_loop) = ggez::ContextBuilder::new("your_game", "your_name") .window_mode(window_mode) .build()?; - 在事件处理的
resize_event回调中同步更新屏幕坐标配置,可按需固定逻辑宽高比,从根源避免拉伸:fn resize_event(&mut self, ctx: &mut Context, width: f32, height: f32) -> GameResult { // 示例固定16:9逻辑宽高比,多余区域自动留边,不需要可以直接用传入的width/height作为绘制尺寸 const TARGET_RATIO: f32 = 16.0 / 9.0; let current_ratio = width / height; let (draw_w, draw_h) = if current_ratio > TARGET_RATIO { (height * TARGET_RATIO, height) } else { (width, width / TARGET_RATIO) }; // 设置绘制区域居中,适配当前窗口尺寸 let screen_area = graphics::Rect::new( (width - draw_w) / 2.0, (height - draw_h) / 2.0, draw_w, draw_h ); graphics::set_screen_coordinates(ctx, screen_area)?; Ok(()) } - 每帧绘制的第一步执行全画布清屏,彻底解决残影问题:
fn draw(&mut self, ctx: &mut Context) -> GameResult { // 用你需要的背景色清空整个帧缓冲,示例用黑色 let mut canvas = graphics::Canvas::from_frame(ctx, graphics::Color::BLACK); // 后续正常写你的绘制逻辑即可,比如绘制白色圆形 let circle = graphics::Mesh::new_circle( ctx, graphics::DrawMode::fill(), glam::Vec2::new(400.0, 300.0), 100.0, 2.0, graphics::Color::WHITE, )?; canvas.draw(&circle, glam::Vec2::ZERO); canvas.finish(ctx)?; Ok(()) }
注意点
- 非特殊渲染需求不要主动调用
set_drawable_size,这个接口是给多渲染目标、离屏渲染这类场景用的,常规窗口大小调整用上面的方法就行 - 只要保证逻辑绘制区域的宽高比和你设计的一致,就不会出现圆形变椭圆的拉伸问题
- 每帧开头统一清屏是解决残影的最直接方式,不要依赖缓冲区默认的覆盖逻辑
内容的提问来源于stack exchange,提问作者Samuel
相关产品推荐
相关产品推荐

