Delay asset hot reloading (#8503)
# Objective - Fix #5631 ## Solution - Wait 50ms (configurable) after the last modification event before reloading an asset. --- ## Changelog - `AssetPlugin::watch_for_changes` is now a `ChangeWatcher` instead of a `bool` - Fixed https://github.com/bevyengine/bevy/issues/5631 ## Migration Guide - Replace `AssetPlugin::watch_for_changes: true` with e.g. `ChangeWatcher::with_delay(Duration::from_millis(200))` --------- Co-authored-by: François <mockersf@gmail.com>
This commit is contained in:
parent
0736195a1e
commit
17f045e2a0
@ -82,10 +82,11 @@ pub struct AssetServerInternal {
|
|||||||
/// ```
|
/// ```
|
||||||
/// # use bevy_asset::*;
|
/// # use bevy_asset::*;
|
||||||
/// # use bevy_app::*;
|
/// # use bevy_app::*;
|
||||||
|
/// # use bevy_utils::Duration;
|
||||||
/// # let mut app = App::new();
|
/// # let mut app = App::new();
|
||||||
/// // The asset plugin can be configured to watch for asset changes.
|
/// // The asset plugin can be configured to watch for asset changes.
|
||||||
/// app.add_plugin(AssetPlugin {
|
/// app.add_plugin(AssetPlugin {
|
||||||
/// watch_for_changes: true,
|
/// watch_for_changes: ChangeWatcher::with_delay(Duration::from_millis(200)),
|
||||||
/// ..Default::default()
|
/// ..Default::default()
|
||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
@ -702,7 +703,7 @@ mod test {
|
|||||||
fn setup(asset_path: impl AsRef<Path>) -> AssetServer {
|
fn setup(asset_path: impl AsRef<Path>) -> AssetServer {
|
||||||
use crate::FileAssetIo;
|
use crate::FileAssetIo;
|
||||||
IoTaskPool::init(Default::default);
|
IoTaskPool::init(Default::default);
|
||||||
AssetServer::new(FileAssetIo::new(asset_path, false))
|
AssetServer::new(FileAssetIo::new(asset_path, &None))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -5,14 +5,15 @@
|
|||||||
use bevy_app::{App, Plugin, Update};
|
use bevy_app::{App, Plugin, Update};
|
||||||
use bevy_ecs::{prelude::*, system::SystemState};
|
use bevy_ecs::{prelude::*, system::SystemState};
|
||||||
use bevy_tasks::{IoTaskPool, TaskPoolBuilder};
|
use bevy_tasks::{IoTaskPool, TaskPoolBuilder};
|
||||||
use bevy_utils::HashMap;
|
use bevy_utils::{Duration, HashMap};
|
||||||
use std::{
|
use std::{
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
path::Path,
|
path::Path,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Asset, AssetEvent, AssetPlugin, AssetServer, Assets, FileAssetIo, Handle, HandleUntyped,
|
Asset, AssetEvent, AssetPlugin, AssetServer, Assets, ChangeWatcher, FileAssetIo, Handle,
|
||||||
|
HandleUntyped,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A helper [`App`] used for hot reloading internal assets, which are compiled-in to Bevy plugins.
|
/// A helper [`App`] used for hot reloading internal assets, which are compiled-in to Bevy plugins.
|
||||||
@ -72,7 +73,7 @@ impl Plugin for DebugAssetServerPlugin {
|
|||||||
let mut debug_asset_app = App::new();
|
let mut debug_asset_app = App::new();
|
||||||
debug_asset_app.add_plugin(AssetPlugin {
|
debug_asset_app.add_plugin(AssetPlugin {
|
||||||
asset_folder: "crates".to_string(),
|
asset_folder: "crates".to_string(),
|
||||||
watch_for_changes: true,
|
watch_for_changes: ChangeWatcher::with_delay(Duration::from_millis(200)),
|
||||||
});
|
});
|
||||||
app.insert_non_send_resource(DebugAssetApp(debug_asset_app));
|
app.insert_non_send_resource(DebugAssetApp(debug_asset_app));
|
||||||
app.add_systems(Update, run_debug_asset_app);
|
app.add_systems(Update, run_debug_asset_app);
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
use bevy_utils::{default, HashMap, HashSet};
|
use bevy_utils::{default, Duration, HashMap, HashSet};
|
||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
use notify::{Event, RecommendedWatcher, RecursiveMode, Result, Watcher};
|
use notify::{Event, RecommendedWatcher, RecursiveMode, Result, Watcher};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use crate::ChangeWatcher;
|
||||||
|
|
||||||
/// Watches for changes to files on the local filesystem.
|
/// Watches for changes to files on the local filesystem.
|
||||||
///
|
///
|
||||||
/// When hot-reloading is enabled, the [`AssetServer`](crate::AssetServer) uses this to reload
|
/// When hot-reloading is enabled, the [`AssetServer`](crate::AssetServer) uses this to reload
|
||||||
@ -11,10 +13,11 @@ pub struct FilesystemWatcher {
|
|||||||
pub watcher: RecommendedWatcher,
|
pub watcher: RecommendedWatcher,
|
||||||
pub receiver: Receiver<Result<Event>>,
|
pub receiver: Receiver<Result<Event>>,
|
||||||
pub path_map: HashMap<PathBuf, HashSet<PathBuf>>,
|
pub path_map: HashMap<PathBuf, HashSet<PathBuf>>,
|
||||||
|
pub delay: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FilesystemWatcher {
|
impl FilesystemWatcher {
|
||||||
fn default() -> Self {
|
pub fn new(configuration: &ChangeWatcher) -> Self {
|
||||||
let (sender, receiver) = crossbeam_channel::unbounded();
|
let (sender, receiver) = crossbeam_channel::unbounded();
|
||||||
let watcher: RecommendedWatcher = RecommendedWatcher::new(
|
let watcher: RecommendedWatcher = RecommendedWatcher::new(
|
||||||
move |res| {
|
move |res| {
|
||||||
@ -27,11 +30,10 @@ impl Default for FilesystemWatcher {
|
|||||||
watcher,
|
watcher,
|
||||||
receiver,
|
receiver,
|
||||||
path_map: default(),
|
path_map: default(),
|
||||||
|
delay: configuration.delay,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl FilesystemWatcher {
|
|
||||||
/// Watch for changes recursively at the provided path.
|
/// Watch for changes recursively at the provided path.
|
||||||
pub fn watch<P: AsRef<Path>>(&mut self, to_watch: P, to_reload: PathBuf) -> Result<()> {
|
pub fn watch<P: AsRef<Path>>(&mut self, to_watch: P, to_reload: PathBuf) -> Result<()> {
|
||||||
self.path_map
|
self.path_map
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{AssetIo, AssetIoError, Metadata};
|
use crate::{AssetIo, AssetIoError, ChangeWatcher, Metadata};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use bevy_utils::BoxedFuture;
|
use bevy_utils::BoxedFuture;
|
||||||
use std::{
|
use std::{
|
||||||
@ -59,7 +59,7 @@ impl AssetIo for AndroidAssetIo {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn watch_for_changes(&self) -> Result<(), AssetIoError> {
|
fn watch_for_changes(&self, _configuration: &ChangeWatcher) -> Result<(), AssetIoError> {
|
||||||
bevy_log::warn!("Watching for changes is not supported on Android");
|
bevy_log::warn!("Watching for changes is not supported on Android");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
#[cfg(feature = "filesystem_watcher")]
|
#[cfg(feature = "filesystem_watcher")]
|
||||||
use crate::{filesystem_watcher::FilesystemWatcher, AssetServer};
|
use crate::{filesystem_watcher::FilesystemWatcher, AssetServer};
|
||||||
use crate::{AssetIo, AssetIoError, Metadata};
|
use crate::{AssetIo, AssetIoError, ChangeWatcher, Metadata};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
#[cfg(feature = "filesystem_watcher")]
|
#[cfg(feature = "filesystem_watcher")]
|
||||||
use bevy_ecs::system::Res;
|
use bevy_ecs::system::{Local, Res};
|
||||||
use bevy_utils::BoxedFuture;
|
use bevy_utils::BoxedFuture;
|
||||||
#[cfg(feature = "filesystem_watcher")]
|
#[cfg(feature = "filesystem_watcher")]
|
||||||
use bevy_utils::{default, HashSet};
|
use bevy_utils::{default, HashMap, Instant};
|
||||||
#[cfg(feature = "filesystem_watcher")]
|
#[cfg(feature = "filesystem_watcher")]
|
||||||
use crossbeam_channel::TryRecvError;
|
use crossbeam_channel::TryRecvError;
|
||||||
use fs::File;
|
use fs::File;
|
||||||
@ -35,13 +35,13 @@ impl FileAssetIo {
|
|||||||
/// watching for changes.
|
/// watching for changes.
|
||||||
///
|
///
|
||||||
/// See `get_base_path` below.
|
/// See `get_base_path` below.
|
||||||
pub fn new<P: AsRef<Path>>(path: P, watch_for_changes: bool) -> Self {
|
pub fn new<P: AsRef<Path>>(path: P, watch_for_changes: &Option<ChangeWatcher>) -> Self {
|
||||||
let file_asset_io = FileAssetIo {
|
let file_asset_io = FileAssetIo {
|
||||||
#[cfg(feature = "filesystem_watcher")]
|
#[cfg(feature = "filesystem_watcher")]
|
||||||
filesystem_watcher: default(),
|
filesystem_watcher: default(),
|
||||||
root_path: Self::get_base_path().join(path.as_ref()),
|
root_path: Self::get_base_path().join(path.as_ref()),
|
||||||
};
|
};
|
||||||
if watch_for_changes {
|
if let Some(configuration) = watch_for_changes {
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
not(feature = "filesystem_watcher"),
|
not(feature = "filesystem_watcher"),
|
||||||
target_arch = "wasm32",
|
target_arch = "wasm32",
|
||||||
@ -52,7 +52,7 @@ impl FileAssetIo {
|
|||||||
wasm32 / android targets"
|
wasm32 / android targets"
|
||||||
);
|
);
|
||||||
#[cfg(feature = "filesystem_watcher")]
|
#[cfg(feature = "filesystem_watcher")]
|
||||||
file_asset_io.watch_for_changes().unwrap();
|
file_asset_io.watch_for_changes(configuration).unwrap();
|
||||||
}
|
}
|
||||||
file_asset_io
|
file_asset_io
|
||||||
}
|
}
|
||||||
@ -143,10 +143,10 @@ impl AssetIo for FileAssetIo {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn watch_for_changes(&self) -> Result<(), AssetIoError> {
|
fn watch_for_changes(&self, configuration: &ChangeWatcher) -> Result<(), AssetIoError> {
|
||||||
#[cfg(feature = "filesystem_watcher")]
|
#[cfg(feature = "filesystem_watcher")]
|
||||||
{
|
{
|
||||||
*self.filesystem_watcher.write() = Some(default());
|
*self.filesystem_watcher.write() = Some(FilesystemWatcher::new(configuration));
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "filesystem_watcher"))]
|
#[cfg(not(feature = "filesystem_watcher"))]
|
||||||
bevy_log::warn!("Watching for changes is not supported when the `filesystem_watcher` feature is disabled");
|
bevy_log::warn!("Watching for changes is not supported when the `filesystem_watcher` feature is disabled");
|
||||||
@ -174,7 +174,10 @@ impl AssetIo for FileAssetIo {
|
|||||||
feature = "filesystem_watcher",
|
feature = "filesystem_watcher",
|
||||||
all(not(target_arch = "wasm32"), not(target_os = "android"))
|
all(not(target_arch = "wasm32"), not(target_os = "android"))
|
||||||
))]
|
))]
|
||||||
pub fn filesystem_watcher_system(asset_server: Res<AssetServer>) {
|
pub fn filesystem_watcher_system(
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
mut changed: Local<HashMap<PathBuf, Instant>>,
|
||||||
|
) {
|
||||||
let asset_io =
|
let asset_io =
|
||||||
if let Some(asset_io) = asset_server.server.asset_io.downcast_ref::<FileAssetIo>() {
|
if let Some(asset_io) = asset_server.server.asset_io.downcast_ref::<FileAssetIo>() {
|
||||||
asset_io
|
asset_io
|
||||||
@ -182,14 +185,15 @@ pub fn filesystem_watcher_system(asset_server: Res<AssetServer>) {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let watcher = asset_io.filesystem_watcher.read();
|
let watcher = asset_io.filesystem_watcher.read();
|
||||||
|
|
||||||
if let Some(ref watcher) = *watcher {
|
if let Some(ref watcher) = *watcher {
|
||||||
let mut changed = HashSet::<&PathBuf>::default();
|
|
||||||
loop {
|
loop {
|
||||||
let event = match watcher.receiver.try_recv() {
|
let event = match watcher.receiver.try_recv() {
|
||||||
Ok(result) => result.unwrap(),
|
Ok(result) => result.unwrap(),
|
||||||
Err(TryRecvError::Empty) => break,
|
Err(TryRecvError::Empty) => break,
|
||||||
Err(TryRecvError::Disconnected) => panic!("FilesystemWatcher disconnected."),
|
Err(TryRecvError::Disconnected) => panic!("FilesystemWatcher disconnected."),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let notify::event::Event {
|
if let notify::event::Event {
|
||||||
kind: notify::event::EventKind::Modify(_),
|
kind: notify::event::EventKind::Modify(_),
|
||||||
paths,
|
paths,
|
||||||
@ -199,13 +203,22 @@ pub fn filesystem_watcher_system(asset_server: Res<AssetServer>) {
|
|||||||
for path in &paths {
|
for path in &paths {
|
||||||
let Some(set) = watcher.path_map.get(path) else {continue};
|
let Some(set) = watcher.path_map.get(path) else {continue};
|
||||||
for to_reload in set {
|
for to_reload in set {
|
||||||
if !changed.contains(to_reload) {
|
// When an asset is modified, note down the timestamp (overriding any previous modification events)
|
||||||
changed.insert(to_reload);
|
changed.insert(to_reload.to_owned(), Instant::now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload all assets whose last modification was at least 50ms ago.
|
||||||
|
//
|
||||||
|
// When changing and then saving a shader, several modification events are sent in short succession.
|
||||||
|
// Unless we wait until we are sure the shader is finished being modified (and that there will be no more events coming),
|
||||||
|
// we will sometimes get a crash when trying to reload a partially-modified shader.
|
||||||
|
for (to_reload, _) in
|
||||||
|
changed.drain_filter(|_, last_modified| last_modified.elapsed() >= watcher.delay)
|
||||||
|
{
|
||||||
let _ = asset_server.load_untracked(to_reload.as_path().into(), true);
|
let _ = asset_server.load_untracked(to_reload.as_path().into(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ use std::{
|
|||||||
};
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::ChangeWatcher;
|
||||||
|
|
||||||
/// Errors that occur while loading assets.
|
/// Errors that occur while loading assets.
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum AssetIoError {
|
pub enum AssetIoError {
|
||||||
@ -81,7 +83,7 @@ pub trait AssetIo: Downcast + Send + Sync + 'static {
|
|||||||
) -> Result<(), AssetIoError>;
|
) -> Result<(), AssetIoError>;
|
||||||
|
|
||||||
/// Enables change tracking in this asset I/O.
|
/// Enables change tracking in this asset I/O.
|
||||||
fn watch_for_changes(&self) -> Result<(), AssetIoError>;
|
fn watch_for_changes(&self, configuration: &ChangeWatcher) -> Result<(), AssetIoError>;
|
||||||
|
|
||||||
/// Returns `true` if the path is a directory.
|
/// Returns `true` if the path is a directory.
|
||||||
fn is_dir(&self, path: &Path) -> bool {
|
fn is_dir(&self, path: &Path) -> bool {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{AssetIo, AssetIoError, Metadata};
|
use crate::{AssetIo, AssetIoError, ChangeWatcher, Metadata};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use bevy_utils::BoxedFuture;
|
use bevy_utils::BoxedFuture;
|
||||||
use js_sys::Uint8Array;
|
use js_sys::Uint8Array;
|
||||||
@ -64,7 +64,7 @@ impl AssetIo for WasmAssetIo {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn watch_for_changes(&self) -> Result<(), AssetIoError> {
|
fn watch_for_changes(&self, _configuration: &ChangeWatcher) -> Result<(), AssetIoError> {
|
||||||
bevy_log::warn!("Watching for changes is not supported in WASM");
|
bevy_log::warn!("Watching for changes is not supported in WASM");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ pub use reflect::*;
|
|||||||
|
|
||||||
use bevy_app::{prelude::*, MainScheduleOrder};
|
use bevy_app::{prelude::*, MainScheduleOrder};
|
||||||
use bevy_ecs::schedule::ScheduleLabel;
|
use bevy_ecs::schedule::ScheduleLabel;
|
||||||
|
use bevy_utils::Duration;
|
||||||
|
|
||||||
/// Asset storages are updated.
|
/// Asset storages are updated.
|
||||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, ScheduleLabel)]
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, ScheduleLabel)]
|
||||||
@ -57,6 +58,30 @@ pub struct LoadAssets;
|
|||||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, ScheduleLabel)]
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, ScheduleLabel)]
|
||||||
pub struct AssetEvents;
|
pub struct AssetEvents;
|
||||||
|
|
||||||
|
/// Configuration for hot reloading assets by watching for changes.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ChangeWatcher {
|
||||||
|
/// Minimum delay after which a file change will trigger a reload.
|
||||||
|
///
|
||||||
|
/// The change watcher will wait for this duration after a file change before reloading the
|
||||||
|
/// asset. This is useful to avoid reloading an asset multiple times when it is changed
|
||||||
|
/// multiple times in a short period of time, or to avoid reloading an asset that is still
|
||||||
|
/// being written to.
|
||||||
|
///
|
||||||
|
/// If you have a slow hard drive or expect to reload large assets, you may want to increase
|
||||||
|
/// this value.
|
||||||
|
pub delay: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChangeWatcher {
|
||||||
|
/// Enable change watching with the given delay when a file is changed.
|
||||||
|
///
|
||||||
|
/// See [`Self::delay`] for more details on how this value is used.
|
||||||
|
pub fn with_delay(delay: Duration) -> Option<Self> {
|
||||||
|
Some(Self { delay })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Adds support for [`Assets`] to an App.
|
/// Adds support for [`Assets`] to an App.
|
||||||
///
|
///
|
||||||
/// Assets are typed collections with change tracking, which are added as App Resources. Examples of
|
/// Assets are typed collections with change tracking, which are added as App Resources. Examples of
|
||||||
@ -67,14 +92,14 @@ pub struct AssetPlugin {
|
|||||||
pub asset_folder: String,
|
pub asset_folder: String,
|
||||||
/// Whether to watch for changes in asset files. Requires the `filesystem_watcher` feature,
|
/// Whether to watch for changes in asset files. Requires the `filesystem_watcher` feature,
|
||||||
/// and cannot be supported on the wasm32 arch nor android os.
|
/// and cannot be supported on the wasm32 arch nor android os.
|
||||||
pub watch_for_changes: bool,
|
pub watch_for_changes: Option<ChangeWatcher>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AssetPlugin {
|
impl Default for AssetPlugin {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
asset_folder: "assets".to_string(),
|
asset_folder: "assets".to_string(),
|
||||||
watch_for_changes: false,
|
watch_for_changes: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,7 +111,7 @@ impl AssetPlugin {
|
|||||||
/// delegate to the default `AssetIo` for the platform.
|
/// delegate to the default `AssetIo` for the platform.
|
||||||
pub fn create_platform_default_asset_io(&self) -> Box<dyn AssetIo> {
|
pub fn create_platform_default_asset_io(&self) -> Box<dyn AssetIo> {
|
||||||
#[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
|
#[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
|
||||||
let source = FileAssetIo::new(&self.asset_folder, self.watch_for_changes);
|
let source = FileAssetIo::new(&self.asset_folder, &self.watch_for_changes);
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
let source = WasmAssetIo::new(&self.asset_folder);
|
let source = WasmAssetIo::new(&self.asset_folder);
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
//! It does not know anything about the asset formats, only how to talk to the underlying storage.
|
//! It does not know anything about the asset formats, only how to talk to the underlying storage.
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
asset::{AssetIo, AssetIoError, Metadata},
|
asset::{AssetIo, AssetIoError, ChangeWatcher, Metadata},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
utils::BoxedFuture,
|
utils::BoxedFuture,
|
||||||
};
|
};
|
||||||
@ -39,9 +39,9 @@ impl AssetIo for CustomAssetIo {
|
|||||||
self.0.watch_path_for_changes(to_watch, to_reload)
|
self.0.watch_path_for_changes(to_watch, to_reload)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn watch_for_changes(&self) -> Result<(), AssetIoError> {
|
fn watch_for_changes(&self, configuration: &ChangeWatcher) -> Result<(), AssetIoError> {
|
||||||
info!("watch_for_changes()");
|
info!("watch_for_changes()");
|
||||||
self.0.watch_for_changes()
|
self.0.watch_for_changes(configuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_metadata(&self, path: &Path) -> Result<Metadata, AssetIoError> {
|
fn get_metadata(&self, path: &Path) -> Result<Metadata, AssetIoError> {
|
||||||
|
@ -2,13 +2,13 @@
|
|||||||
//! running. This lets you immediately see the results of your changes without restarting the game.
|
//! running. This lets you immediately see the results of your changes without restarting the game.
|
||||||
//! This example illustrates hot reloading mesh changes.
|
//! This example illustrates hot reloading mesh changes.
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::{asset::ChangeWatcher, prelude::*, utils::Duration};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins(DefaultPlugins.set(AssetPlugin {
|
.add_plugins(DefaultPlugins.set(AssetPlugin {
|
||||||
// Tell the asset server to watch for asset changes on disk:
|
// Tell the asset server to watch for asset changes on disk:
|
||||||
watch_for_changes: true,
|
watch_for_changes: ChangeWatcher::with_delay(Duration::from_millis(200)),
|
||||||
..default()
|
..default()
|
||||||
}))
|
}))
|
||||||
.add_systems(Startup, setup)
|
.add_systems(Startup, setup)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
//! This example illustrates loading scenes from files.
|
//! This example illustrates loading scenes from files.
|
||||||
use bevy::{prelude::*, tasks::IoTaskPool, utils::Duration};
|
use bevy::{asset::ChangeWatcher, prelude::*, tasks::IoTaskPool, utils::Duration};
|
||||||
use std::{fs::File, io::Write};
|
use std::{fs::File, io::Write};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -7,7 +7,7 @@ fn main() {
|
|||||||
.add_plugins(DefaultPlugins.set(AssetPlugin {
|
.add_plugins(DefaultPlugins.set(AssetPlugin {
|
||||||
// This tells the AssetServer to watch for changes to assets.
|
// This tells the AssetServer to watch for changes to assets.
|
||||||
// It enables our scenes to automatically reload in game when we modify their files.
|
// It enables our scenes to automatically reload in game when we modify their files.
|
||||||
watch_for_changes: true,
|
watch_for_changes: ChangeWatcher::with_delay(Duration::from_millis(200)),
|
||||||
..default()
|
..default()
|
||||||
}))
|
}))
|
||||||
.register_type::<ComponentA>()
|
.register_type::<ComponentA>()
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
//! This is a fairly low level example and assumes some familiarity with rendering concepts and wgpu.
|
//! This is a fairly low level example and assumes some familiarity with rendering concepts and wgpu.
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
|
asset::ChangeWatcher,
|
||||||
core_pipeline::{
|
core_pipeline::{
|
||||||
clear_color::ClearColorConfig, core_3d,
|
clear_color::ClearColorConfig, core_3d,
|
||||||
fullscreen_vertex_shader::fullscreen_shader_vertex_state,
|
fullscreen_vertex_shader::fullscreen_shader_vertex_state,
|
||||||
@ -29,13 +30,14 @@ use bevy::{
|
|||||||
view::{ExtractedView, ViewTarget},
|
view::{ExtractedView, ViewTarget},
|
||||||
RenderApp,
|
RenderApp,
|
||||||
},
|
},
|
||||||
|
utils::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins(DefaultPlugins.set(AssetPlugin {
|
.add_plugins(DefaultPlugins.set(AssetPlugin {
|
||||||
// Hot reloading the shader works correctly
|
// Hot reloading the shader works correctly
|
||||||
watch_for_changes: true,
|
watch_for_changes: ChangeWatcher::with_delay(Duration::from_millis(200)),
|
||||||
..default()
|
..default()
|
||||||
}))
|
}))
|
||||||
.add_plugin(PostProcessPlugin)
|
.add_plugin(PostProcessPlugin)
|
||||||
|
@ -6,9 +6,11 @@
|
|||||||
//! With no arguments it will load the `FlightHelmet` glTF model from the repository assets subdirectory.
|
//! With no arguments it will load the `FlightHelmet` glTF model from the repository assets subdirectory.
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
|
asset::ChangeWatcher,
|
||||||
math::Vec3A,
|
math::Vec3A,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
render::primitives::{Aabb, Sphere},
|
render::primitives::{Aabb, Sphere},
|
||||||
|
utils::Duration,
|
||||||
window::WindowPlugin,
|
window::WindowPlugin,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -36,7 +38,7 @@ fn main() {
|
|||||||
.set(AssetPlugin {
|
.set(AssetPlugin {
|
||||||
asset_folder: std::env::var("CARGO_MANIFEST_DIR")
|
asset_folder: std::env::var("CARGO_MANIFEST_DIR")
|
||||||
.unwrap_or_else(|_| ".".to_string()),
|
.unwrap_or_else(|_| ".".to_string()),
|
||||||
watch_for_changes: true,
|
watch_for_changes: ChangeWatcher::with_delay(Duration::from_millis(200)),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.add_plugin(CameraControllerPlugin)
|
.add_plugin(CameraControllerPlugin)
|
||||||
|
Loading…
Reference in New Issue
Block a user