egui glfw input behaving strangely - rust

I am trying to integrate egui into a custom engine. In particular I am dealing with the input handling, like mouse and keyboard.
I used this repo as a reference, but its using opengl and I am using vulkan through ash.
To get things working I merely copied the event handling function into my won project:
pub struct DebugGui
{
pub ctx : egui::Context,
textures : Vec::<GpuDataDescriptor>,
raw_input : egui::RawInput,
pub pointer_pos: Pos2,
pub modifiers: egui::Modifiers,
shader : ShaderHandle,
}
impl DebugGui
{
pub fn init<RenderContext>(
render_context : &mut RenderContext,
window_size : (u32, u32)
) -> DebugGui
where RenderContext : GpuResourceCreation
{
let ctx = egui::Context::default();
let (width, height) = window_size;
let raw_input = egui::RawInput
{
screen_rect : Some(egui::Rect::from_points(&[
Default::default(),
[width as f32, height as f32].into(),
])),
pixels_per_point : Some(1.0),
..Default::default()
};
DebugGui
{
ctx,
shader : render_context.add_shader_from_memory(
&vec!["debug_ui.vert".to_string(), "debug_ui.frag".to_string()],
&vec![IMGUI_VERT_SHADER.to_string(), IMGUI_FRAG_SHADER.to_string()]),
textures : Vec::new(),
raw_input,
pointer_pos : Pos2::default(),
modifiers : egui::Modifiers::default(),
}
}
pub fn handle_event(&mut self, event: &glfw::WindowEvent)
{
match event {
FramebufferSize(width, height) => {
self.raw_input.screen_rect = Some(Rect::from_min_size(
Pos2::new(0f32, 0f32),
egui::vec2(*width as f32, *height as f32)
/ self.raw_input.pixels_per_point.unwrap(),
))
}
MouseButton (mouse_btn, glfw::Action::Press, _) =>
self.raw_input.events.push(egui::Event::PointerButton {
pos: self.pointer_pos,
button: match mouse_btn {
glfw::MouseButtonLeft => egui::PointerButton::Primary,
glfw::MouseButtonRight => egui::PointerButton::Secondary,
glfw::MouseButtonMiddle => egui::PointerButton::Middle,
_ => unreachable!(),
},
pressed: true,
modifiers: self.modifiers,
}),
MouseButton (mouse_btn, glfw::Action::Release, _) =>
self.raw_input.events.push(egui::Event::PointerButton {
pos: self.pointer_pos,
button: match mouse_btn {
glfw::MouseButtonLeft => egui::PointerButton::Primary,
glfw::MouseButtonRight => egui::PointerButton::Secondary,
glfw::MouseButtonMiddle => egui::PointerButton::Middle,
_ => unreachable!(),
},
pressed: false,
modifiers: self.modifiers,
}),
CursorPos(x, y) => {
self.pointer_pos = pos2(
*x as f32 / self.raw_input.pixels_per_point.unwrap(),
*y as f32 / self.raw_input.pixels_per_point.unwrap(),
);
self
.raw_input
.events
.push(egui::Event::PointerMoved(self.pointer_pos))
}
Key(keycode, _scancode, glfw::Action::Release, keymod) => {
use glfw::Modifiers as Mod;
if let Some(key) = translate_virtual_key_code(*keycode) {
self.modifiers = Modifiers {
alt: (*keymod & Mod::Alt == Mod::Alt),
ctrl: (*keymod & Mod::Control == Mod::Control),
shift: (*keymod & Mod::Shift == Mod::Shift),
// TODO: GLFW doesn't seem to support the mac command key
// mac_cmd: keymod & Mod::LGUIMOD == Mod::LGUIMOD,
command: (*keymod & Mod::Control == Mod::Control),
..Default::default()
};
self.raw_input.events.push(Event::Key {
key,
pressed: false,
modifiers: self.modifiers,
});
}
}
Key(keycode, _scancode, glfw::Action::Press | glfw::Action::Repeat, keymod) => {
use glfw::Modifiers as Mod;
if let Some(key) = translate_virtual_key_code(*keycode) {
self.modifiers = Modifiers {
alt: (*keymod & Mod::Alt == Mod::Alt),
ctrl: (*keymod & Mod::Control == Mod::Control),
shift: (*keymod & Mod::Shift == Mod::Shift),
command: (*keymod & Mod::Control == Mod::Control),
..Default::default()
};
if self.modifiers.command && key == egui::Key::X {
self.raw_input.events.push(egui::Event::Cut);
} else if self.modifiers.command && key == egui::Key::C {
self.raw_input.events.push(egui::Event::Copy);
} else if self.modifiers.command && key == egui::Key::V {
todo!()
} else {
self.raw_input.events.push(Event::Key {
key,
pressed: true,
modifiers: self.modifiers,
});
}
}
}
Char(c) => {
self.raw_input.events.push(Event::Text(c.to_string()));
}
Scroll (x, y) => {
self.raw_input.events.push(egui::Event::Scroll(vec2(*x as f32, *y as f32)));
}
_ => {}
}
}
And I am calling it inside the glfw event loop:
for (_, event) in glfw::flush_messages(&self.events)
{
debug_gui.handle_event(&event);
}
It's very glitchy for some reason.
With this basic example, if I hover my mouse over the arrow it flickers like crazy, I cannot resize the quad of the widget and if I click on the click me button it will call the body a few hundred times before stopping.
It seems part of the issue is that you need to reset the raw input every frame, like this:
pub fn handle_event(&mut self, event: &glfw::WindowEvent)
{
let (width, height) = self.window_size;
self.raw_input = egui::RawInput
{
screen_rect : Some(egui::Rect::from_points(&[
Default::default(),
[width as f32, height as f32].into(),
])),
pixels_per_point : Some(1.0),
..Default::default()
};
}
But for some reason i cannot resize teh widget I made.

Related

What is the alternative of downcasting a trait object in this situation?

I have a Companion trait that encompasses a base trait for Components such as Health. I store a list of Companion using trait objects because all companions must at least implement the Companion trait. However not all companions will use the subtype Health trait.
Now the heal command only accepts a list of Health traits, so I need to filter out, remap and downcast all the base Companion traits so that it supports the Health traits.
I understand this is bad design. How can I implement the same behavior without having to downcast the trait objects to a specific component? Note: I cannot have a large struct which includes all the subtypes into one type.
Here's the code I have so far:
type CompanionId = Uuid;
fn main() {
let mut companion_storage: HashMap<CompanionId, Box<dyn Companion>> = HashMap::new();
let companion_id: CompanionId = Uuid::new_v4();
companion_storage.insert(
companion_id,
Box::new(Cheetah {
...
}),
);
let mut player = Player {
...,
companions: Vec::new(),
};
player.companions.push(companion_id);
'GameLoop: loop {
let input = poll_input().trim().to_lowercase();
match input.as_str() {
// TODO: Extract healing component here.
"heal" => heal_command(companion_id, companion_storage.into_iter().filter(|(companion_id, companion)| {
// QUESTION: How do I filter out Companions without the Health trait here so they can automatically be downcasted and mapped?
}).collect()),
"q" => {
break 'GameLoop;
}
"s" => {
status_command(&player, &companion_storage); // SAME PROBLEM HERE
}
_ => println!("Unknown command"),
}
}
}
struct Player {
id: u8,
name: String,
companions: Vec<CompanionId>,
}
trait Companion {
...
}
trait Health: Companion {
...
}
trait Status: Health {}
struct Cheetah {
id: CompanionId,
name: String,
current_health: f32,
max_health: f32,
}
impl Companion for Cheetah {
...
}
impl Health for Cheetah {
...
}
fn heal_command(
companion_id: CompanionId,
companion_storage: &mut HashMap<CompanionId, Box<dyn Health>>,
) {
let companion = companion_storage.get_mut(&companion_id).unwrap();
companion.heal_max();
println!("Healed to max.");
}
fn status_command(player: &Player, companion_storage: &mut HashMap<CompanionId, Box<dyn Status>>) {
println!("Status for {}: ", player.name);
println!("===============================");
print!("Companions: ");
for companion_id in &player.companions {
let companion = companion_storage.get(companion_id).unwrap();
print!(
"{} [{}/{}], ",
companion.name(),
companion.health(),
companion.max_health()
);
}
println!();
println!("===============================");
}
Is this code a better alternative?
type CompanionId = Uuid;
fn main() {
let mut companion_storage: HashMap<CompanionId, Companion> = HashMap::new();
let companion_id: CompanionId = Uuid::new_v4();
companion_storage.insert(
companion_id,
Companion {
id: companion_id,
name: "Cheetah".to_string(),
health: Some(Box::new(RegularHealth {
current_health: 50.0,
max_health: 50.0,
})),
},
);
let mut player = Player {
id: 0,
name: "FyiaR".to_string(),
companions: Vec::new(),
};
player.companions.push(companion_id);
'GameLoop: loop {
let input = poll_input().trim().to_lowercase();
match input.as_str() {
// TODO: Extract healing component here.
"heal" => {
let companion = companion_storage.get_mut(&companion_id).unwrap();
match companion.health_mut() {
None => {
println!("The selected companion doesn't have health associated with it.");
}
Some(health) => {
heal_command(health);
println!("{} was healed to max.", companion.name);
}
}
}
"q" => {
break 'GameLoop;
}
"s" => {
status_command(&player, &companion_storage); // SAME PROBLEM HERE
}
_ => println!("Unknown command"),
}
}
}
struct Player {
id: u8,
name: String,
companions: Vec<CompanionId>,
}
struct Companion {
id: CompanionId,
name: String,
health: Option<Box<dyn Health>>,
}
struct RegularHealth {
current_health: f32,
max_health: f32,
}
trait Health {
...
}
impl Companion {
fn health_mut(&mut self) -> Option<&mut dyn Health> {
match self.health.as_mut() {
None => None,
Some(health) => Some(health.as_mut()),
}
}
fn health(&self) -> Option<&dyn Health> {
match self.health.as_ref() {
None => None,
Some(health) => Some(health.as_ref()),
}
}
}
impl Health for RegularHealth {
...
}
fn heal_command(health: &mut dyn Health) {
health.heal_max();
}
fn status_command(player: &Player, companion_storage: &HashMap<CompanionId, Companion>) {
println!("Status for {}: ", player.name);
println!("===============================");
print!("Companions: ");
for companion_id in &player.companions {
let companion = companion_storage.get(companion_id).unwrap();
match companion.health.as_ref() {
None => {}
Some(health) => {
print!(
"{} [{}/{}], ",
companion.name,
health.health(),
health.max_health()
);
}
}
}
println!();
println!("===============================");
}

Value of a field attribute on a proc macro

I have this struct:
pub struct Thing {
pub some_field: i32,
#[my_attr = some_value]
pub field_attr: String
}
How can I recover the data on the right side of the equals? I can recover perfectly fine the left side.
pub fn new(name: &Ident, raw_helper_attributes: &[Attribute], ty: &Type) -> syn::Result<Self> {
// Getting the name of attributes put in front of struct fields
let helper_attributes = raw_helper_attributes
.iter()
.map(|attribute| {
attribute
.path
.segments
.iter()
.map( |segment| {
&segment.ident
})
.collect::<Vec<_>>()
})
.flatten()
.collect::<Vec<_>>();
let attribute_type = if helper_attributes.len() == 1 {
let helper_attribute = helper_attributes[0];
Some(EntityFieldAnnotation::try_from(helper_attribute)?)
} else if helper_attributes.len() > 1 {
return Err(
syn::Error::new_spanned(
name,
"Field has more than one attribute"
)
);
} else { None };
Ok(
Self {
name: name.clone(),
field_type: ty.clone(),
attribute: attribute_type,
}
)
}
For short, I ommited the rest of the code of the macro for summarize.
Use Attribute::parse_meta():
let (path, value) = match attribute.parse_meta().unwrap() {
syn::Meta::NameValue(syn::MetaNameValue {
path,
lit: syn::Lit::Str(s),
..
}) => (path, s.value()),
_ => panic!("malformed attribute syntax"),
};

What is an acceptable approach to dragging sprites with Bevy 0.4?

While trying out Bevy, I had the need for dragging and dropping sprites.
Unfortunately, this does not seem to come ready made or I did not find it in the documentation.
What would be the most idiomatic way to achieve this goal?
What I have tried so far is in my answer, but I'll gladly accept another solution that is better/faster/more idiomatic.
I'm not experienced enough to know what's idiomatic unfortunately, however, here's an overview of how I've implemented sprite dragging in my application, and it feels like a good way to me:
I have a "cursor location" entity with a transform component (and a Cursor component for identification) that I update in a system each frame to the location of the cursor.
Every draggable object has a Hoverable and Draggable component. I iterate over those objects in one system each where I add/remove a Hovered and Dragged component to the entities to indicate if they are hovered or dragged.
I have a system that checks if an object is getting dropped, and if so gives it a Dropped component.
I have a system that runs when an entity gets the 'Dragged' component (using the Added<C> filter), which sets the objects parent to the "cursor location" entity.
And another system for when an entity gets the 'Dropped' component, which clears the parent.
To me, having many systems with small areas of responsibility feels good. I would be interested to hear opposing views as I lack experience.
There are of course many things I've left out in this overview, so here's my code for reference. There are some oddities and unnecessary code for a minimal example since this is adapted from my actual code:
#![allow(clippy::type_complexity)]
use bevy::{prelude::*, render::camera::Camera};
fn main() {
App::build()
.init_resource::<State>()
.add_resource(WindowDescriptor {
title: "Bevy".to_string(),
width: 1024.0,
height: 768.0,
vsync: true,
..Default::default()
})
.add_plugins(DefaultPlugins)
.add_plugin(MyPlugin)
.run();
}
pub struct MyPlugin;
impl Plugin for MyPlugin {
fn build(&self, app: &mut AppBuilder) {
app.add_startup_system(setup.system())
.add_system_to_stage(stage::PRE_UPDATE, cursor_state.system())
.add_system_to_stage(stage::UPDATE, cursor_transform.system())
.add_system_to_stage(stage::UPDATE, draggable.system())
.add_system_to_stage(stage::UPDATE, hoverable.system())
.add_system_to_stage(stage::POST_UPDATE, drag.system())
.add_system_to_stage(stage::POST_UPDATE, drop.system())
.add_system_to_stage(stage::POST_UPDATE, material.system());
}
}
const SPRITE_SIZE: f32 = 55.0;
fn setup(
commands: &mut Commands,
asset_server: Res<AssetServer>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
let bevy_texture = asset_server.load("sprites/bevy-icon.png");
commands
.spawn(Camera2dBundle::default())
.spawn(())
.with(CursorState::default())
.spawn((Transform::default(), GlobalTransform::default(), Cursor));
for _ in 0..4 {
commands
.spawn(SpriteBundle {
material: materials.add(bevy_texture.clone().into()),
sprite: Sprite::new(Vec2::new(SPRITE_SIZE, SPRITE_SIZE)),
..Default::default()
})
.with(Hoverable)
.with(Draggable);
}
}
#[derive(Default)]
struct CursorState {
cursor_world: Vec2,
cursor_moved: bool,
}
struct Cursor;
struct Draggable;
struct Dragged;
struct Dropped;
struct Hoverable;
struct Hovered;
fn cursor_state(
mut state: ResMut<State>,
e_cursor_moved: Res<Events<CursorMoved>>,
windows: Res<Windows>,
mut q_cursor_state: Query<&mut CursorState>,
q_camera: Query<&Transform, With<Camera>>,
) {
let event_cursor_screen = state.er_cursor_moved.latest(&e_cursor_moved);
for mut cursor_state in q_cursor_state.iter_mut() {
if let Some(event_cursor_screen) = event_cursor_screen {
let window = windows.get_primary().unwrap();
let cam_transform = q_camera.iter().last().unwrap();
cursor_state.cursor_world =
cursor_to_world(window, cam_transform, event_cursor_screen.position);
cursor_state.cursor_moved = true;
} else {
cursor_state.cursor_moved = false;
}
}
}
fn cursor_transform(
commands: &mut Commands,
q_cursor_state: Query<&CursorState>,
mut q_cursor: Query<(Entity, &mut Transform), With<Cursor>>,
) {
let cursor_state = q_cursor_state.iter().next().unwrap();
for (cursor_e, mut transform) in q_cursor.iter_mut() {
transform.translation.x = cursor_state.cursor_world.x;
transform.translation.y = cursor_state.cursor_world.y;
commands.remove_one::<Parent>(cursor_e);
}
}
fn hoverable(
commands: &mut Commands,
q_cursor_state: Query<&CursorState>,
q_hoverable: Query<(Entity, &Transform, &Sprite), (With<Hoverable>, Without<Dragged>)>,
) {
let cursor_state = q_cursor_state.iter().next().unwrap();
if cursor_state.cursor_moved {
for (entity, transform, sprite) in q_hoverable.iter() {
let half_width = sprite.size.x / 2.0;
let half_height = sprite.size.y / 2.0;
if transform.translation.x - half_width < cursor_state.cursor_world.x
&& transform.translation.x + half_width > cursor_state.cursor_world.x
&& transform.translation.y - half_height < cursor_state.cursor_world.y
&& transform.translation.y + half_height > cursor_state.cursor_world.y
{
commands.insert_one(entity, Hovered);
} else {
commands.remove_one::<Hovered>(entity);
}
}
}
}
fn material(
mut materials: ResMut<Assets<ColorMaterial>>,
q_hoverable: Query<
(&Handle<ColorMaterial>, Option<&Hovered>, Option<&Dragged>),
With<Hoverable>,
>,
) {
let mut first = true;
for (material, hovered, dragged) in q_hoverable.iter() {
let (red, green, alpha) = if dragged.is_some() {
(0.0, 1.0, 1.0)
} else if first && hovered.is_some() {
first = false;
(1.0, 0.0, 1.0)
} else if hovered.is_some() {
(1.0, 1.0, 0.5)
} else {
(1.0, 1.0, 1.0)
};
materials.get_mut(material).unwrap().color.set_r(red);
materials.get_mut(material).unwrap().color.set_g(green);
materials.get_mut(material).unwrap().color.set_a(alpha);
}
}
fn cursor_to_world(window: &Window, cam_transform: &Transform, cursor_pos: Vec2) -> Vec2 {
// get the size of the window
let size = Vec2::new(window.width() as f32, window.height() as f32);
// the default orthographic projection is in pixels from the center;
// just undo the translation
let screen_pos = cursor_pos - size / 2.0;
// apply the camera transform
let out = cam_transform.compute_matrix() * screen_pos.extend(0.0).extend(1.0);
Vec2::new(out.x, out.y)
}
fn draggable(
commands: &mut Commands,
i_mouse_button: Res<Input<MouseButton>>,
q_pressed: Query<Entity, (With<Hovered>, With<Draggable>)>,
q_released: Query<Entity, With<Dragged>>,
) {
if i_mouse_button.just_pressed(MouseButton::Left) {
if let Some(entity) = q_pressed.iter().next() {
commands.insert_one(entity, Dragged);
}
} else if i_mouse_button.just_released(MouseButton::Left) {
for entity in q_released.iter() {
commands.remove_one::<Dragged>(entity);
commands.insert_one(entity, Dropped);
}
}
}
fn drag(
commands: &mut Commands,
mut q_dragged: Query<(Entity, &mut Transform, &GlobalTransform), Added<Dragged>>,
q_cursor: Query<(Entity, &GlobalTransform), With<Cursor>>,
) {
if let Some((cursor_e, cursor_transform)) = q_cursor.iter().next() {
for (entity, mut transform, global_transform) in q_dragged.iter_mut() {
let global_pos = global_transform.translation - cursor_transform.translation;
commands.insert_one(entity, Parent(cursor_e));
transform.translation.x = global_pos.x;
transform.translation.y = global_pos.y;
}
}
}
fn drop(
commands: &mut Commands,
mut q_dropped: Query<(Entity, &mut Transform, &GlobalTransform), Added<Dropped>>,
) {
for (entity, mut transform, global_transform) in q_dropped.iter_mut() {
let global_pos = global_transform.translation;
transform.translation.x = global_pos.x;
transform.translation.y = global_pos.y;
commands.remove_one::<Parent>(entity);
commands.remove_one::<Dropped>(entity);
}
}
#[derive(Default)]
struct State {
er_cursor_moved: EventReader<CursorMoved>,
}
This code is for bevy 0.4.
This is the solution I came up with. Complete example
main.rs
use bevy::prelude::*;
use bevy::render::pass::ClearColor;
use bevy::window::CursorMoved;
const SPRITE_SIZE: f32 = 55.0;
fn main() {
App::build()
.add_resource(WindowDescriptor {
width: 1000.0,
height: 1000.0,
resizable: false,
title: "Bevy: drag sprite".to_string(),
..Default::default()
})
.add_resource(Msaa { samples: 4 })
.add_resource(ClearColor(Color::rgb(0.9, 0.9, 0.9)))
.add_plugins(DefaultPlugins)
.add_startup_system(setup.system())
.add_system(sprite_system.system())
.add_system(bevy::input::system::exit_on_esc_system.system())
.run();
}
fn setup(
commands: &mut Commands,
asset_server: Res<AssetServer>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
commands.spawn(Camera2dBundle::default());
// show sprite in the middle of the screen
let bevy_texture = asset_server.load("sprites/bevy-icon.png");
commands.spawn(SpriteBundle {
sprite: Sprite::new(Vec2::new(SPRITE_SIZE, SPRITE_SIZE)),
material: materials.add(bevy_texture.clone().into()),
..Default::default()
});
}
#[derive(Default)]
struct State {
cursor_moved_event_reader: EventReader<CursorMoved>,
// store current cursor/mouse position
cursor_pos: Vec2,
// store entity ID and the difference between sprite center and mouse click location
sprite: Option<(Entity, Vec3)>,
}
fn sprite_system(
mut state: Local<State>,
windows: Res<Windows>,
mouse_button_input: Res<Input<MouseButton>>,
cursor_moved_events: Res<Events<CursorMoved>>,
mut sprites: Query<(Entity, &Sprite)>,
mut transforms: Query<&mut Transform>,
) {
let window = windows.get_primary().unwrap();
let half_window = Vec2::new(window.width() / 2.0, window.height() / 2.0);
// if cursor has moved, transform to graphics coordinates and store in state.curser_pos
if let Some(cursor_event) = state.cursor_moved_event_reader.latest(&cursor_moved_events) {
state.cursor_pos = cursor_event.position - half_window;
state.cursor_pos.x = state.cursor_pos.x;
};
// stop dragging if mouse button was released
if mouse_button_input.just_released(MouseButton::Left) {
state.sprite = None;
return;
}
// set new sprite position, if mouse button is pressed and a sprite was clicked on
// take previous click difference into account, to avoid sprite jumps on first move
if mouse_button_input.pressed(MouseButton::Left) && state.sprite.is_some() {
let sprite = state.sprite.unwrap();
let mut sprite_pos = transforms.get_mut(sprite.0).unwrap();
trace!("Sprite position old: {:?}", sprite_pos.translation);
sprite_pos.translation.x = state.cursor_pos.x + sprite.1.x;
sprite_pos.translation.y = state.cursor_pos.y + sprite.1.y;
trace!("Sprite position new: {:?}", sprite_pos.translation);
// position clamping was left out intentionally
}
// store sprite ID and mouse distance from sprite center, if sprite was clicked
if mouse_button_input.just_pressed(MouseButton::Left) {
for (entity, sprite) in sprites.iter_mut() {
let sprite_pos = transforms.get_mut(entity).unwrap().translation;
let diff = cursor_to_sprite_diff(&state.cursor_pos, &sprite_pos);
// sprite is a circle, so check distance from center < sprite radius
if diff.length() < (sprite.size.x / 2.0) {
state.sprite = Some((entity, diff));
}
}
}
}
fn cursor_to_sprite_diff(cursor_pos: &Vec2, sprite_pos: &Vec3) -> Vec3 {
Vec3::new(
sprite_pos.x - cursor_pos.x,
sprite_pos.y - cursor_pos.y,
0.0,
)
}
Cargo.toml
[package]
name = "bevy-drag-sprite"
version = "0.1.0"
authors = ["Me"]
edition = "2018"
[dependencies]
bevy = "0.4"

How do I copy/clone a struct that derives neither?

I'm trying to do some game programming with Piston, but i'm struggling with opengl_graphics::Texture, since it does not derive Copy or Clone.
extern crate piston_window;
extern crate piston;
extern crate graphics;
extern crate opengl_graphics;
use opengl_graphics::Texture as Tex;
use piston_window::*;
use std::path::Path;
use opengl_graphics::GlGraphics;
#[derive(PartialEq)]
enum ObjectType {
Blocking,
Passing,
}
struct Object {
sprite: Tex,
obj_type: ObjectType,
position: Position,
}
struct Game {
gl: GlGraphics,
images: Vec<Object>,
player: Player,
}
struct Player {
sprite: Tex,
position: Position,
}
struct Position {
x: i32,
y: i32,
}
impl Game {
fn render(&mut self, args: &RenderArgs) {
let iter = self.images.iter();
let player = &self.player;
self.gl.draw(args.viewport(), |c, g| {
clear([1.0, 1.0, 1.0, 1.0], g);
for img in iter {
let pos = img.get_position();
let transform = c.transform.trans(((pos.x * 64)) as f64, ((pos.y * 64)) as f64);
image(img.get_sprite(), transform, g);
}
image(player.get_sprite(),
c.transform.trans((player.get_position().x * 64) as f64,
(player.get_position().y * 64) as f64),
g);
});
}
fn update(&mut self, args: &UpdateArgs) {}
}
The main game loop:
fn main() {
let (width, height) = (64*10, 64*10);
let opengl = OpenGL::V3_2;
let mut window: PistonWindow =
WindowSettings::new("piston", (width, height))
.exit_on_esc(true)
.opengl(opengl)
.build()
.unwrap();
window.hide();
println!("Loading...");
let mut player = Player { sprite: Tex::from_path(&Path::new(
"./assets/player_n.png")).unwrap(),
position: Position { x: 3, y: 3 },
};
let mut game = Game {
gl: GlGraphics::new(opengl),
images: Vec::new(),
player: player,
};
for i in 0..10 {
for j in 0..10 {
if i == 0 || i == 9 || j == 0 || j == 9 {
let obj = Object { sprite: Tex::from_path(&Path::new(
"./assets/wall.png")).unwrap(),
obj_type: ObjectType::Blocking,
position: Position { x: i, y: j },
};
game.images.push(obj);
} else {
let obj = Object { sprite: Tex::from_path(&Path::new(
"./assets/floor.png")).unwrap(),
obj_type: ObjectType::Passing,
position: Position { x: i, y: j },
};
game.images.push(obj);
}
}
}
window.show();
while let Some(e) = window.next() {
if let Some(Button::Keyboard(key)) = e.press_args() {
let mut pos = game.player.position.clone();
let mut spr: Option<Tex> = None;
match key {
Key::Up => { pos.y -= 1; spr = Some(Tex::from_path(&Path::new(
"./assets/player_n.png")).unwrap()); },
Key::Down => { pos.y += 1; spr = Some(Tex::from_path(&Path::new(
"./assets/player_s.png")).unwrap()); },
Key::Left => { pos.x -= 1; spr = Some(Tex::from_path(&Path::new(
"./assets/player_w.png")).unwrap()); },
Key::Right => { pos.x += 1; spr = Some(Tex::from_path(&Path::new(
"./assets/player_e.png")).unwrap()); },
_ => (),
}
for elem in game.images.iter() {
if pos.x == elem.position.x && pos.y == elem.position.y && elem.obj_type == ObjectType::Passing {
game.player.position = pos;
game.player.sprite = spr.clone().unwrap();
}
}
}
if let Some(r) = e.render_args() {
game.render(&r);
}
if let Some(u) = e.update_args() {
game.update(&u);
}
}
}
Produces the error:
error: no method named `clone` found for type `std::option::Option<opengl_graphics::Texture>` in the current scope
--> src/main.rs:159:46
159 | game.player.sprite = spr.clone().unwrap();
| ^^^^^
|
= note: the method `clone` exists but the following trait bounds were not satisfied: `opengl_graphics::Texture : std::clone::Clone`
I understand why I get this error, since opengl_graphics::Texture doesn't derive Copy I can't clone Option<opengl_texture>. What workaround is there for this?
I tried messing around with references but that didn't work.
How do I copy/clone a struct that derives neither?
You don't. The only thing you can do is take some kind of reference to it.
In this case, it is a very good thing that the library has chosen to not implement Clone or Copy. If you were able to clone the structure, you'd be allocating a lot of memory frequently and needlessly. Instead, the library has forced you to think about when you allocate that memory. One of the solutions is to load all the textures at application startup and reference them:
Change your structures to hold references:
#[derive(PartialEq)]
enum ObjectType {
Blocking,
Passing,
}
struct Object<'a> {
sprite: &'a Tex,
obj_type: ObjectType,
position: Position,
}
struct Game<'a> {
gl: GlGraphics,
images: Vec<Object<'a>>,
player: Player<'a>,
}
struct Player<'a> {
sprite: &'a Tex,
position: Position,
}
#[derive(Copy, Clone, PartialEq)]
struct Position {
x: i32,
y: i32,
}
struct Textures {
player_n: Tex,
player_s: Tex,
player_e: Tex,
player_w: Tex,
wall: Tex,
floor: Tex,
}
Load the textures early on in main. Note that there's no need to use Path explicitly, as it takes AsRef<Path>:
let textures = Textures {
player_n: Tex::from_path("./assets/player_n.png").unwrap(),
player_s: Tex::from_path("./assets/player_s.png").unwrap(),
player_e: Tex::from_path("./assets/player_e.png").unwrap(),
player_w: Tex::from_path("./assets/player_w.png").unwrap(),
wall: Tex::from_path("./assets/wall.png").unwrap(),
floor: Tex::from_path("./assets/floor.png").unwrap()
};
Then pass references to those textures:
match key {
Key::Up => {
pos.y -= 1;
spr = Some(&textures.player_n)
}
Key::Down => {
pos.y += 1;
spr = Some(&textures.player_s)
}
Key::Left => {
pos.x -= 1;
spr = Some(&textures.player_w)
}
Key::Right => {
pos.x += 1;
spr = Some(&textures.player_e)
}
_ => (),
}
for elem in game.images.iter() {
if pos == elem.position && elem.obj_type == ObjectType::Passing {
game.player.position = pos;
if let Some(spr) = spr {
game.player.sprite = spr;
}
}
}
Note that this also consolidates the places that errors can occur. There's no longer an unwrap inside the guts of the loop.
I was unable to get your code to finish compiling as the code is not complete but this should help get started:
error: no method named `render` found for type `Game<'_>` in the current scope
--> src/main.rs:122:18
|
122 | game.render(&r);
| ^^^^^^
|
= help: items from traits can only be used if the trait is implemented and in scope; the following trait defines an item `render`, perhaps you need to implement it:
= help: candidate #1: `piston_window::RenderEvent`
error: no method named `update` found for type `Game<'_>` in the current scope
--> src/main.rs:125:18
|
125 | game.update(&u);
| ^^^^^^
|
= help: items from traits can only be used if the trait is implemented and in scope; the following traits define an item `update`, perhaps you need to implement one of them:
= help: candidate #1: `piston_window::UpdateEvent`
= help: candidate #2: `piston_window::<unnamed>::UpdateTexture`
= help: candidate #3: `deflate::checksum::RollingChecksum`
= help: candidate #4: `cocoa::appkit::NSOpenGLContext`
= help: candidate #5: `cocoa::appkit::NSOpenGLContext`
In some cases, you may be able to wrap your types in an Rc or Arc and clone that. Cloning a Rc/Arc only increments a reference counter, regardless of the implementation of Clone for the underlying type (or the absence of such an implementation).

Error with trait bound on gfx_core::Resources

I have a problem with this code:
extern crate piston_window;
extern crate find_folder;
extern crate gfx_graphics;
extern crate gfx;
extern crate gfx_device_gl;
use piston_window::*;
use gfx_device_gl::{Resources, Output, CommandBuffer};
use gfx_graphics::GfxGraphics;
struct Object {
x: f64,
y: f64,
sprite: Option<Texture<Resources>>,
}
impl Object {
fn new() -> Object {
Object {
x: 0.0,
y: 0.0,
sprite: None,
}
}
fn mov(&mut self, x: f64, y: f64) {
self.x = x;
self.y = y;
}
fn render(&self,
g: &mut GfxGraphics<Resources, CommandBuffer<Resources>, Output>,
view: math::Matrix2d) {
let square = rectangle::square(0.0, 0.0, 100.0);
let red = [1.0, 0.0, 0.0, 1.0];
match self.sprite {
None => {
rectangle(red,
square,
view.trans(self.x, self.y).trans(-50.0, -50.0),
g);
}
Some(ref sprite) => {
image(sprite, view.trans(self.x, self.y).trans(-50.0, -50.0), g);
}
}
}
fn setSprite(&mut self, sprite: Texture<Resources>) {
self.sprite = Some(sprite);
}
}
struct Game {
position: f64,
one: bool,
two: bool,
three: bool,
four: bool,
five: bool,
six: bool,
seven: bool,
eight: bool,
nine: bool,
player: Object,
}
impl Game {
fn new() -> Game {
Game {
rotation: 0.0,
player: Object::new(),
one: false,
two: false,
three: false,
four: false,
five: false,
six: false,
seven: false,
eight: false,
nine: false,
}
}
fn on_load(&mut self, w: &PistonWindow) {
let assets = find_folder::Search::ParentsThenKids(3, 3) //Cerca la cartella assets
.for_folder("assets").unwrap();
let tris = assets.join("x.png");
let tris = Texture::from_path(&mut *w.factory, &tris, Flip::None, &TextureSettings::new())
.unwrap();
self.player.setSprite(tris);
}
fn on_update(&mut self, upd: UpdateArgs) {
self.position += 3.0 * upd.dt;
if self.one {
self.player.mov(100.0, 100.0);
}
if self.two {
self.player.mov(100.0, 200.0);
}
if self.three {
self.player.mov(100.0, 300.0);
}
if self.four {
self.player.mov(200.0, 100.0);
}
if self.five {
self.player.mov(300.0, 100.0);
}
if self.six {
self.player.mov(200.0, 200.0);
}
if self.seven {
self.player.mov(200.0, 300.0);
}
if self.eight {
self.player.mov(300.0, 200.0);
}
if self.nine {
self.player.mov(300.0, 300.0);
}
}
fn on_draw(&mut self, ren: RenderArgs, e: PistonWindow) {
e.draw_2d(|c, g| {
clear([0.0, 0.0, 0.0], g);
let center = c.transf((ren.width / 2) as f64, (ren.eight / 2) as f64);
self.player.render(g, center);
});
}
fn on_input(&mut self, inp: Input) {
match inp {
Input::Press(but) => {
match but {
Button::Keyboard(Key::D1) => {
self.one = true;
}
Button::Keyboard(Key::D2) => {
self.two = true;
}
Button::Keyboard(Key::D3) => {
self.three = true;
}
Button::Keyboard(Key::D4) => {
self.four = true;
}
Button::Keyboard(Key::D5) => {
self.five = true;
}
Button::Keyboard(Key::D6) => {
self.six = true;
}
Button::Keyboard(Key::D7) => {
self.seven = true;
}
Button::Keyboard(Key::D8) => {
self.eight = true;
}
Button::Keyboard(Key::D9) => {
self.nine = true;
}
_ => {}
}
}
}
}
}
fn main() {
let title = "Tris"; //Titolo della finestra
let mut window: PistonWindow = WindowSettings::new(title, [800, 600]) //Crea la finestra
.exit_on_esc(true)
.build()
.unwrap_or_else(|e| { panic!("Failed to build PistonWindow: {}", e) });
let mut game = Game::new();
game.on_load(&window);
let assets = find_folder::Search::ParentsThenKids(3, 3) //Cerca la cartella assets
.for_folder("assets").unwrap();
let tris = assets.join("tris.png");
let tris = Texture::from_path(&mut window.factory,
&tris,
Flip::None,
&TextureSettings::new())
.unwrap();
let x = assets.join("x.png");
let x = Texture::from_path(&mut window.factory, &x, Flip::None, &TextureSettings::new())
.unwrap();
let o = assets.join("o.png");
let o = Texture::from_path(&mut window.factory, &o, Flip::None, &TextureSettings::new())
.unwrap();
while let Some(e) = window.next() {
// Inizia il ciclo e disegna la roba sotto
window.draw_2d(&e, |c, g| {
clear([0.0, 0.0, 0.0, 0.0], g);
image(&tris, c.transform, g);
});
match e.event {
Some(Event::Update(upd)) => {
game.on_update(upd);
}
Some(Event::Render(ren)) => {
game.on_draw(ren, e);
}
Some(Event::Input(inp)) => {
game.on_input(inp);
}
_ => {}
}
}
}
(Playground)
When I try to compile it, it says the error:
the trait bound `gfx_device_gl::Resources: gfx_core::Resources` is not
satisfied [E0277]
src/main.rs:14 sprite: Option<Texture<Resources>>,
And the second error:
error: the trait bound `gfx_device_gl::Resources: gfx_core::Resources`
is not satisfied [E0277]
src/main.rs:39 fn setSprite(&mut self, sprite: Texture<Resources>) {
src/main.rs:40 self.sprite = Some(sprite);
src/main.rs:41 }
How should I fix it? I looked online but found nothing about it.
I was getting an identical compile error on very similar code and setting
gfx_device_gl = "=0.9.0"
in Cargo.toml caused it to stop happening for me. Previously, I was using 0.11.0.
After changing this, I get other (I believe separate/independent) compilation errors, but perhaps this solves your predicament.

Resources